Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion GeoSharPlusCPP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set(PROJECT_NAME GeoSharPlusCPP)
# Set the Visual Studio toolset to the latest version
if(MSVC)
set(CMAKE_GENERATOR_TOOLSET "v145" CACHE STRING "Visual Studio toolset version" FORCE)
message(STATUS "Using Visual Studio 2026 (Insider) toolset: ${CMAKE_GENERATOR_TOOLSET}")
message(STATUS "Using Visual Studio 2026 toolset: ${CMAKE_GENERATOR_TOOLSET}")
endif()

# Check for VCPKG_ROOT environment variable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "GeoSharPlusCPP/API/BridgeAPI.h"
#include "GeoSharPlusCPP/Extensions/igMesh/BridgeAPI.h"

#include <algorithm>
#include <iostream>
Expand Down
154 changes: 154 additions & 0 deletions GeoSharPlusNET/Adapters/Rhino/RhinoAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// ============================================
// Rhino Geometry Adapter
// ============================================
// This file provides conversions between Rhino.Geometry types
// and GSP.Geometry types for use with the GeoSharPlus library.
//
// To use this file, your project must reference RhinoCommon.
// ============================================

using System;
using System.Linq;
using Rhino.Geometry;

namespace GSP.Adapters.Rhino {
/// <summary>
/// Adapter for converting between Rhino.Geometry types and GSP.Geometry types.
/// </summary>
public class RhinoAdapter : GSP.Geometry.IGeometryAdapter<Point3d, global::Rhino.Geometry.Mesh> {
/// <summary>
/// Singleton instance of the adapter.
/// </summary>
public static RhinoAdapter Instance { get; } = new();

#region Point Conversions

/// <summary>
/// Converts a Rhino Point3d to a GSP Vec3.
/// </summary>
public GSP.Geometry.Vec3 PointToGSP(Point3d point) =>
new(point.X, point.Y, point.Z);

/// <summary>
/// Converts a GSP Vec3 to a Rhino Point3d.
/// </summary>
public Point3d PointFromGSP(GSP.Geometry.Vec3 point) =>
new(point.X, point.Y, point.Z);

/// <summary>
/// Converts an array of Rhino Point3d to GSP Vec3 array.
/// </summary>
public GSP.Geometry.Vec3[] PointsToGSP(Point3d[] points) =>
points.Select(p => new GSP.Geometry.Vec3(p.X, p.Y, p.Z)).ToArray();

/// <summary>
/// Converts a GSP Vec3 array to Rhino Point3d array.
/// </summary>
public Point3d[] PointsFromGSP(GSP.Geometry.Vec3[] points) =>
points.Select(p => new Point3d(p.X, p.Y, p.Z)).ToArray();

#endregion

#region Vector Conversions

/// <summary>
/// Converts a Rhino Vector3d to a GSP Vec3.
/// </summary>
public GSP.Geometry.Vec3 VectorToGSP(Vector3d vector) =>
new(vector.X, vector.Y, vector.Z);

/// <summary>
/// Converts a GSP Vec3 to a Rhino Vector3d.
/// </summary>
public Vector3d VectorFromGSP(GSP.Geometry.Vec3 vector) =>
new(vector.X, vector.Y, vector.Z);

#endregion

#region Mesh Conversions

/// <summary>
/// Converts a Rhino Mesh to a GSP Mesh.
/// </summary>
public GSP.Geometry.Mesh MeshToGSP(global::Rhino.Geometry.Mesh mesh) {
var result = new GSP.Geometry.Mesh();

// Convert vertices
result.Vertices = mesh.Vertices
.Select(v => new GSP.Geometry.Vec3(v.X, v.Y, v.Z))
.ToArray();

// Separate triangles and quads
var triangles = new System.Collections.Generic.List<(int, int, int)>();
var quads = new System.Collections.Generic.List<(int, int, int, int)>();

foreach (var face in mesh.Faces) {
if (face.IsTriangle) {
triangles.Add((face.A, face.B, face.C));
} else {
quads.Add((face.A, face.B, face.C, face.D));
}
}

result.TriangleFaces = triangles.ToArray();
result.QuadFaces = quads.ToArray();

return result;
}

/// <summary>
/// Converts a GSP Mesh to a Rhino Mesh.
/// </summary>
public global::Rhino.Geometry.Mesh MeshFromGSP(GSP.Geometry.Mesh gspMesh) {
var mesh = new global::Rhino.Geometry.Mesh();

// Add vertices
foreach (var v in gspMesh.Vertices) {
mesh.Vertices.Add(v.X, v.Y, v.Z);
}

// Add triangle faces
foreach (var (a, b, c) in gspMesh.TriangleFaces) {
mesh.Faces.AddFace(a, b, c);
}

// Add quad faces
foreach (var (a, b, c, d) in gspMesh.QuadFaces) {
mesh.Faces.AddFace(a, b, c, d);
}

// Clean up mesh
if (mesh.IsValid) {
mesh.RebuildNormals();
mesh.Compact();
}

return mesh;
}

#endregion
}

/// <summary>
/// Extension methods for converting between Rhino and GSP types.
/// </summary>
public static class RhinoExtensions {
private static readonly RhinoAdapter _adapter = RhinoAdapter.Instance;

// Point3d extensions
public static GSP.Geometry.Vec3 ToGSP(this Point3d point) => _adapter.PointToGSP(point);
public static Point3d ToRhino(this GSP.Geometry.Vec3 point) => _adapter.PointFromGSP(point);

// Point3d array extensions
public static GSP.Geometry.Vec3[] ToGSP(this Point3d[] points) => _adapter.PointsToGSP(points);
public static Point3d[] ToRhino(this GSP.Geometry.Vec3[] points) => _adapter.PointsFromGSP(points);

// Vector3d extensions
public static GSP.Geometry.Vec3 ToGSP(this Vector3d vector) => _adapter.VectorToGSP(vector);
public static Vector3d ToRhinoVector(this GSP.Geometry.Vec3 vector) => _adapter.VectorFromGSP(vector);

// Mesh extensions
public static GSP.Geometry.Mesh ToGSP(this global::Rhino.Geometry.Mesh mesh) => _adapter.MeshToGSP(mesh);
public static global::Rhino.Geometry.Mesh ToRhino(this GSP.Geometry.Mesh mesh) => _adapter.MeshFromGSP(mesh);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Google.FlatBuffers;
using Google.FlatBuffers;
using Rhino.Geometry;

namespace GSP {
namespace GSP.Adapters.Rhino {
public static class Wrapper {
#region Point3d / Vector3d Operations

Expand Down
63 changes: 63 additions & 0 deletions GeoSharPlusNET/Core/MarshalHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Runtime.InteropServices;

namespace GSP.Core {
/// <summary>
/// Cross-platform utilities for marshaling data between C# and native code.
/// Works on Windows, macOS, and Linux.
/// </summary>
public static class MarshalHelper {
/// <summary>
/// Copies data from unmanaged memory to a managed byte array and frees the unmanaged memory.
/// This method handles the common pattern of receiving data from C++ code.
/// </summary>
/// <param name="ptr">Pointer to unmanaged memory allocated by native code.</param>
/// <param name="size">Size of the data in bytes.</param>
/// <returns>A managed byte array containing the copied data, or empty array if invalid.</returns>
/// <remarks>
/// The native code should allocate memory using:
/// - Windows: CoTaskMemAlloc
/// - macOS/Linux: malloc (with appropriate .NET runtime mapping)
///
/// This method uses Marshal.FreeCoTaskMem which works cross-platform in .NET Core/.NET 5+.
/// </remarks>
public static byte[] CopyAndFree(IntPtr ptr, int size) {
if (ptr == IntPtr.Zero || size <= 0)
return Array.Empty<byte>();

try {
var buffer = new byte[size];
Marshal.Copy(ptr, buffer, 0, size);
return buffer;
}
finally {
Marshal.FreeCoTaskMem(ptr);
}
}

/// <summary>
/// Copies data from unmanaged memory without freeing it.
/// Use when you need to read data but the native side manages the memory.
/// </summary>
/// <param name="ptr">Pointer to unmanaged memory.</param>
/// <param name="size">Size of the data in bytes.</param>
/// <returns>A managed byte array containing the copied data.</returns>
public static byte[] Copy(IntPtr ptr, int size) {
if (ptr == IntPtr.Zero || size <= 0)
return Array.Empty<byte>();

var buffer = new byte[size];
Marshal.Copy(ptr, buffer, 0, size);
return buffer;
}

/// <summary>
/// Frees memory allocated by native code.
/// </summary>
/// <param name="ptr">Pointer to unmanaged memory to free.</param>
public static void Free(IntPtr ptr) {
if (ptr != IntPtr.Zero)
Marshal.FreeCoTaskMem(ptr);
}
}
}
51 changes: 51 additions & 0 deletions GeoSharPlusNET/Core/Platform.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Runtime.InteropServices;

namespace GSP.Core {
/// <summary>
/// Platform detection and native library configuration.
/// Use these constants and helpers when creating P/Invoke declarations.
/// </summary>
public static class Platform {
/// <summary>
/// Native library name for Windows.
/// </summary>
public const string WindowsLib = "GeoSharPlusCPP.dll";

/// <summary>
/// Native library name for macOS.
/// </summary>
public const string MacLib = "libGeoSharPlusCPP.dylib";

/// <summary>
/// Native library name for Linux.
/// </summary>
public const string LinuxLib = "libGeoSharPlusCPP.so";

/// <summary>
/// Returns true if running on Windows.
/// </summary>
public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

/// <summary>
/// Returns true if running on macOS.
/// </summary>
public static bool IsMac => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);

/// <summary>
/// Returns true if running on Linux.
/// </summary>
public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);

/// <summary>
/// Gets the appropriate native library name for the current platform.
/// </summary>
public static string NativeLibrary {
get {
if (IsWindows) return WindowsLib;
if (IsMac) return MacLib;
if (IsLinux) return LinuxLib;
throw new PlatformNotSupportedException("Unsupported platform");
}
}
}
}
Loading
Loading