using System; using CeresSharp.Native; using CeresSharp.Native.SafeHandles; namespace CeresSharp.Advanced; /// /// Options for configuring a Ceres Problem. /// public sealed class ProblemOptions : IDisposable { private readonly ProblemOptionsHandle _handle; private System.Runtime.InteropServices.GCHandle? _evaluationCallbackHandle; private System.Runtime.InteropServices.GCHandle? _evaluationNativeCallbackHandle; private bool _disposed; /// /// Creates a new ProblemOptions instance with default settings. /// public ProblemOptions() { _handle = ProblemOptionsHandle.Create(); } /// /// Gets the native handle. /// internal ProblemOptionsHandle Handle => _handle; /// /// Gets or sets whether the problem owns cost functions. /// public bool CostFunctionOwnership { get => CeresNative.ceres_wrapper_problem_options_get_cost_function_ownership(_handle.DangerousGetHandle()) != 0; set => CeresNative.ceres_wrapper_problem_options_set_cost_function_ownership(_handle.DangerousGetHandle(), value ? 1 : 0); } /// /// Gets or sets whether the problem owns loss functions. /// public bool LossFunctionOwnership { get => CeresNative.ceres_wrapper_problem_options_get_loss_function_ownership(_handle.DangerousGetHandle()) != 0; set => CeresNative.ceres_wrapper_problem_options_set_loss_function_ownership(_handle.DangerousGetHandle(), value ? 1 : 0); } /// /// Gets or sets whether the problem owns manifolds. /// public bool ManifoldOwnership { get => CeresNative.ceres_wrapper_problem_options_get_manifold_ownership(_handle.DangerousGetHandle()) != 0; set => CeresNative.ceres_wrapper_problem_options_set_manifold_ownership(_handle.DangerousGetHandle(), value ? 1 : 0); } /// /// Gets or sets whether to enable fast removal of residual blocks. /// public bool EnableFastRemoval { get => CeresNative.ceres_wrapper_problem_options_get_enable_fast_removal(_handle.DangerousGetHandle()) != 0; set => CeresNative.ceres_wrapper_problem_options_set_enable_fast_removal(_handle.DangerousGetHandle(), value ? 1 : 0); } /// /// Gets or sets whether to disable all safety checks. /// public bool DisableAllSafetyChecks { get => CeresNative.ceres_wrapper_problem_options_get_disable_all_safety_checks(_handle.DangerousGetHandle()) != 0; set => CeresNative.ceres_wrapper_problem_options_set_disable_all_safety_checks(_handle.DangerousGetHandle(), value ? 1 : 0); } /// /// Sets the context for performance optimization. /// public void SetContext(Context context) { if (context == null) throw new ArgumentNullException(nameof(context)); CeresNative.ceres_wrapper_problem_options_set_context( _handle.DangerousGetHandle(), context.Handle.DangerousGetHandle()); } /// /// Sets an evaluation callback for shared computation. /// Called before evaluating cost functions to allow shared computation. /// /// The evaluation callback. public void SetEvaluationCallback(EvaluationCallback callback) { if (callback == null) throw new ArgumentNullException(nameof(callback)); // Pin the callback delegate var callbackHandle = System.Runtime.InteropServices.GCHandle.Alloc(callback); // Create native callback wrapper var nativeCallback = new CeresNative.CeresEvaluationCallback((userData, numResiduals, numParameterBlocks, parameterBlockSizesPtr) => { try { var handle = System.Runtime.InteropServices.GCHandle.FromIntPtr(userData); var csCallback = (EvaluationCallback)handle.Target!; // Parse parameter block sizes int[]? parameterBlockSizes = null; if (parameterBlockSizesPtr != IntPtr.Zero && numParameterBlocks > 0) { parameterBlockSizes = new int[numParameterBlocks]; System.Runtime.InteropServices.Marshal.Copy(parameterBlockSizesPtr, parameterBlockSizes, 0, numParameterBlocks); } // Call C# callback csCallback(numResiduals, numParameterBlocks, parameterBlockSizes); } catch { // Ignore errors in callback } }); // Pin the native callback var nativeCallbackHandle = System.Runtime.InteropServices.GCHandle.Alloc(nativeCallback); CeresNative.ceres_wrapper_problem_options_set_evaluation_callback( _handle.DangerousGetHandle(), nativeCallback, System.Runtime.InteropServices.GCHandle.ToIntPtr(callbackHandle)); // Track handles for cleanup when ProblemOptions is disposed _evaluationCallbackHandle = callbackHandle; _evaluationNativeCallbackHandle = nativeCallbackHandle; } public void Dispose() { if (!_disposed) { // Cleanup callback handles if (_evaluationCallbackHandle.HasValue && _evaluationCallbackHandle.Value.IsAllocated) _evaluationCallbackHandle.Value.Free(); if (_evaluationNativeCallbackHandle.HasValue && _evaluationNativeCallbackHandle.Value.IsAllocated) _evaluationNativeCallbackHandle.Value.Free(); _handle?.Dispose(); _disposed = true; } } }