using UnityEditor; using UnityEngine; // ─── Operation base class ───────────────────────────────────────────────────── [System.Serializable] public abstract class DeformOperation { public abstract string GetDisplayName(); public virtual void DrawParameters() { } public abstract void ApplyTo(FoldPipelineBuilder builder); public static DeformOperation Create(FoldSlot slot) => slot.opcode switch { TubeToPlaneOp.Opcode => new TubeToPlaneOp(slot), PlaneToTubeOp.Opcode => new PlaneToTubeOp(slot), PlaneToHemiOctahedronOp.Opcode => new PlaneToHemiOctahedronOp(slot), HemiOctahedronToPlaneOp.Opcode => new HemiOctahedronToPlaneOp(slot), ScaleOp.Opcode => new ScaleOp(slot), TranslateOp.Opcode => new TranslateOp(slot), PlaneToOctahedronOp.Opcode => new PlaneToOctahedronOp(slot), OctahedronToPlaneOp.Opcode => new OctahedronToPlaneOp(slot), RotateOp.Opcode => new RotateOp(slot), PointAlignOp.Opcode => new PointAlignOp(slot), AxisAlignOp.Opcode => new AxisAlignOp(slot), NormConversionOp.Opcode => new NormConversionOp(slot), SealOp.Opcode => new SealOp(slot), SineWavesOp.Opcode => new SineWavesOp(), FBMOp.Opcode => new FBMOp(), _ => null }; protected static void DrawPrsParams(ref Vector3 p, ref Vector3 r, ref Vector3 s, ref float t) { p = EditorGUILayout.Vector3Field("Origin (p)", p); r = EditorGUILayout.Vector3Field("R Axis", r); s = EditorGUILayout.Vector3Field("S Axis", s); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } protected static void DrawAlignParams(ref Vector3 po, ref Vector3 pp, ref Vector3 r, ref float t) { po = EditorGUILayout.Vector3Field("Origin Point (po)", po); pp = EditorGUILayout.Vector3Field("Target Point (pp)", pp); r = EditorGUILayout.Vector3Field("Rotation Axis (r)", r); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } } // ─── Concrete operations ────────────────────────────────────────────────────── [System.Serializable] public class TubeToPlaneOp : DeformOperation { public const int Opcode = 1; public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; public TubeToPlaneOp() { } public TubeToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } public override string GetDisplayName() => "Tube to Plane"; public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); public override void ApplyTo(FoldPipelineBuilder b) => b.TubeToPlane(p, r, s, t); } [System.Serializable] public class PlaneToTubeOp : DeformOperation { public const int Opcode = 2; public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; public PlaneToTubeOp() { } public PlaneToTubeOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } public override string GetDisplayName() => "Plane to Tube"; public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); public override void ApplyTo(FoldPipelineBuilder b) => b.PlaneToTube(p, r, s, t); } [System.Serializable] public class PlaneToHemiOctahedronOp : DeformOperation { public const int Opcode = 9; public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; public PlaneToHemiOctahedronOp() { } public PlaneToHemiOctahedronOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } public override string GetDisplayName() => "Plane to Hemi-Octahedron"; public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); public override void ApplyTo(FoldPipelineBuilder b) => b.PlaneToHemiOctahedron(p, r, s, t); } [System.Serializable] public class HemiOctahedronToPlaneOp : DeformOperation { public const int Opcode = 10; public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; public HemiOctahedronToPlaneOp() { } public HemiOctahedronToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } public override string GetDisplayName() => "Hemi-Octahedron to Plane"; public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); public override void ApplyTo(FoldPipelineBuilder b) => b.HemiOctahedronToPlane(p, r, s, t); } [System.Serializable] public class PlaneToOctahedronOp : DeformOperation { public const int Opcode = 13; public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; public PlaneToOctahedronOp() { } public PlaneToOctahedronOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } public override string GetDisplayName() => "Plane to Octahedron"; public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); public override void ApplyTo(FoldPipelineBuilder b) => b.PlaneToOctahedron(p, r, s, t); } [System.Serializable] public class OctahedronToPlaneOp : DeformOperation { public const int Opcode = 14; public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; public OctahedronToPlaneOp() { } public OctahedronToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } public override string GetDisplayName() => "Octahedron to Plane"; public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); public override void ApplyTo(FoldPipelineBuilder b) => b.OctahedronToPlane(p, r, s, t); } [System.Serializable] public class PointAlignOp : DeformOperation { public const int Opcode = 3; public Vector3 po = Vector3.zero, pp = Vector3.up, r = Vector3.right; public float t = 1f; public PointAlignOp() { } public PointAlignOp(FoldSlot slot) { po = slot.vec0; pp = slot.vec1; r = slot.vec2; t = slot.float0; } public override string GetDisplayName() => "Point Align"; public override void DrawParameters() => DrawAlignParams(ref po, ref pp, ref r, ref t); public override void ApplyTo(FoldPipelineBuilder b) => b.PointAlign(po, pp, r, t); } [System.Serializable] public class AxisAlignOp : DeformOperation { public const int Opcode = 4; public Vector3 po = Vector3.zero, pp = Vector3.up, r = Vector3.right; public float t = 1f; public AxisAlignOp() { } public AxisAlignOp(FoldSlot slot) { po = slot.vec0; pp = slot.vec1; r = slot.vec2; t = slot.float0; } public override string GetDisplayName() => "Axis Align"; public override void DrawParameters() => DrawAlignParams(ref po, ref pp, ref r, ref t); public override void ApplyTo(FoldPipelineBuilder b) => b.AxisAlign(po, pp, r, t); } [System.Serializable] public class ScaleOp : DeformOperation { public const int Opcode = 11; public Vector3 k = Vector3.one; public float t = 1f; public ScaleOp() { } public ScaleOp(FoldSlot slot) { k = slot.vec0; t = slot.float0; } public override string GetDisplayName() => "Scale"; public override void DrawParameters() { k = EditorGUILayout.Vector3Field("Scale", k); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } public override void ApplyTo(FoldPipelineBuilder b) => b.Scale(k, t); } [System.Serializable] public class TranslateOp : DeformOperation { public const int Opcode = 12; public Vector3 offset = Vector3.zero; public float t = 1f; public TranslateOp() { } public TranslateOp(FoldSlot slot) { offset = slot.vec0; t = slot.float0; } public override string GetDisplayName() => "Translate"; public override void DrawParameters() { offset = EditorGUILayout.Vector3Field("Offset", offset); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } public override void ApplyTo(FoldPipelineBuilder b) => b.Translate(offset, t); } [System.Serializable] public class RotateOp : DeformOperation { public const int Opcode = 15; public Vector3 center = Vector3.zero, axis = Vector3.up; public float angleDeg = 90f, t = 1f; public RotateOp() { } public RotateOp(FoldSlot slot) { center = slot.vec0; axis = slot.vec1; angleDeg = slot.float0 * Mathf.Rad2Deg; t = slot.float1; } public override string GetDisplayName() => "Rotate"; public override void DrawParameters() { center = EditorGUILayout.Vector3Field("Center", center); axis = EditorGUILayout.Vector3Field("Axis", axis); angleDeg = EditorGUILayout.Slider("Angle", angleDeg, 0f, 360f); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } public override void ApplyTo(FoldPipelineBuilder b) => b.Rotate(center, axis, angleDeg * Mathf.Deg2Rad, t); } [System.Serializable] public class NormConversionOp : DeformOperation { public const int Opcode = 5; public float inputK = 2f, outputK = 1f, t = 1f; public NormConversionOp() { } public NormConversionOp(FoldSlot slot) { inputK = slot.float0; outputK = slot.float1; t = slot.float2; } public override string GetDisplayName() => $"Norm Conversion (L{FormatNorm(inputK)}→L{FormatNorm(outputK)})"; string FormatNorm(float k) => float.IsPositiveInfinity(k) ? "∞" : k.ToString("F1"); public override void DrawParameters() { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Input Norm (k)", GUILayout.Width(120)); inputK = EditorGUILayout.FloatField(inputK); if (GUILayout.Button("L∞", GUILayout.Width(30))) inputK = float.PositiveInfinity; EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Output Norm (k)", GUILayout.Width(120)); outputK = EditorGUILayout.FloatField(outputK); if (GUILayout.Button("L∞", GUILayout.Width(30))) outputK = float.PositiveInfinity; EditorGUILayout.EndHorizontal(); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); EditorGUILayout.HelpBox("Common: L1=diamond, L2=sphere, L∞=cube", MessageType.Info); } public override void ApplyTo(FoldPipelineBuilder b) => b.NormConversion(inputK, outputK, t); } [System.Serializable] public class SealOp : DeformOperation { public const int Opcode = 6; public float A = 0.1f, k = 2f, st = 0.8f, t = 1f; public SealOp() { } public SealOp(FoldSlot slot) { A = slot.float0; k = slot.float1; st = slot.float2; t = slot.float3; } public override string GetDisplayName() => "Seal"; public override void DrawParameters() { A = EditorGUILayout.FloatField("Amplitude (A)", A); k = EditorGUILayout.FloatField("Smoothness (k)", k); st = EditorGUILayout.FloatField("Scale (st)", st); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } public override void ApplyTo(FoldPipelineBuilder b) => b.Seal(A, k, st, t); } [System.Serializable] public class SineWavesOp : DeformOperation { public const int Opcode = 7; public override string GetDisplayName() => "Sine Waves"; public override void DrawParameters() => EditorGUILayout.HelpBox("Uses shader-side parameters", MessageType.Info); public override void ApplyTo(FoldPipelineBuilder b) => b.SineWaves(); } [System.Serializable] public class FBMOp : DeformOperation { public const int Opcode = 8; public override string GetDisplayName() => "FBM (Fractal Brownian Motion)"; public override void DrawParameters() => EditorGUILayout.HelpBox("Uses shader-side parameters", MessageType.Info); public override void ApplyTo(FoldPipelineBuilder b) => b.FBM(); }