Mask/IMaskModifier.cs
using System.Text.Json.Serialization;

namespace Sandbox.Mask;

using System;


public enum MaskCombineOp
{
	Add,
	Subtract,
	Multiply,
	Max,
	Min,
	Lerp,
	Override
}

[JsonDerivedType( typeof( SplineMask ), "spline" )]
[JsonDerivedType( typeof( SlopeMask ),     "slope"     )]
[JsonDerivedType( typeof( HeightMask ),    "height"    )]
[JsonDerivedType( typeof( NoiseMask ),     "noise"     )]
[JsonDerivedType( typeof( DistanceMask ),  "distance"  )]
[JsonDerivedType( typeof( CurvatureMask ), "curvature" )]
[JsonDerivedType( typeof( ErosionMask ),   "erosion"   )]
[JsonDerivedType( typeof( FlowMask ),      "flow"      )]
[Serializable]
public class MaskModifier
{
	[Hide]public Terrain Terrain { get; set; }

	public virtual void Apply( MaskField field ) { }
}
/// <summary>
/// Combines a generated MaskField into an existing one using a blend operation.
/// Wrap any IMaskModifier with this to control how it stacks with others.
/// </summary>
[Serializable]
public class MaskModifierEntry
{
	[Property]
	public MaskModifier Modifier { get; set; }
	[Property] public MaskCombineOp CombineOp   { get; set; } = MaskCombineOp.Multiply;
	[Property, Range(0f, 1f)] public float Strength { get; set; } = 1f;
	[Property] public bool           Enabled    { get; set; } = true;
	
	
	/// <summary>
	/// Applies the source field onto the target using this entry's CombineOp and Strength.
	/// </summary>
	public void Blend( MaskField target, MaskField source )
	{
		if ( !Enabled ) return;

		var resampled = source.ResampleTo( target.Resolution, target.WorldSize );

		for ( int i = 0; i < target.Values.Length; i++ )
		{
			float a = target.Values[i];
			float b = resampled.Values[i] * Strength;

			target.Values[i] = CombineOp switch
			{
				MaskCombineOp.Add      => Math.Clamp( a + b, 0f, 1f ),
				MaskCombineOp.Subtract => Math.Clamp( a - b, 0f, 1f ),
				MaskCombineOp.Multiply => a * b,
				MaskCombineOp.Max      => MathF.Max( a, b ),
				MaskCombineOp.Min      => MathF.Min( a, b ),
				MaskCombineOp.Lerp     => MathX.Lerp( a, b, Strength ),
				MaskCombineOp.Override => b,
				_                      => a
			};
		}
	}
}