Fold Demo#

Demonstration of Fold services in Dips Python API.

This script demonstrates IFoldServices methods including: - AddFoldWindow: Create a new fold window - GetFoldList: List all folds - DeleteFold: Remove a fold

Code Snippet: FoldDemo.py#
"""
Demonstration of Fold services in Dips Python API.

This script demonstrates IFoldServices methods including:
- AddFoldWindow: Create a new fold window
- GetFoldList: List all folds
- DeleteFold: Remove a fold

FoldWindowEntityInfo Structure:
  - color: Display color
  - wrapped_freehand_fold_window: WrappedFreehandWindow containing:
      - id: Unique identifier
      - primary_window: FreehandWindow with polygon points
      - secondary_window: Optional FreehandWindow (for wrapped windows)
"""

import math
from dips import DipsApp
from dips import DipsAPI_pb2

import dips.FoldWindowEntityInfoVal
import dips.WrappedFreehandWindowVal
import dips.FreehandWindowVal
import dips.TrendPlungeVal
import dips.AngleDataVal
import dips.ColorSurrogateVal


# =============================================================================
# HELPER FUNCTIONS
# =============================================================================

def create_trend_plunge(trend_deg: float, plunge_deg: float):
    """Create a TrendPlunge from degrees."""
    tp = dips.TrendPlungeVal.TrendPlungeVal()
    tp.trend = dips.AngleDataVal.AngleDataVal()
    tp.trend.angle_radians = math.radians(trend_deg)
    tp.plunge = dips.AngleDataVal.AngleDataVal()
    tp.plunge.angle_radians = math.radians(plunge_deg)
    return tp


def create_color(r: int, g: int, b: int, a: int = 255):
    """Create a Color from RGBA values."""
    color = dips.ColorSurrogateVal.ColorSurrogateVal()
    color.r = r
    color.g = g
    color.b = b
    color.a = a
    return color


def create_freehand_window(polygon_points):
    """Create a FreehandWindow from a list of (trend, plunge) tuples."""
    window = dips.FreehandWindowVal.FreehandWindowVal()
    # Use append() for protobuf list fields instead of direct assignment
    for t, p in polygon_points:
        window.polygon.append(create_trend_plunge(t, p))
    window.is_wrapped = False
    return window


def create_empty_freehand_window():
    """Create an empty FreehandWindow (for secondary window when not wrapped)."""
    window = dips.FreehandWindowVal.FreehandWindowVal()
    window.is_wrapped = False
    return window


def create_fold_window(fold_id: str, color_tuple, polygon_points):
    """Create a FoldWindowEntityInfo with the given parameters."""
    # Create the freehand window with polygon points
    primary = create_freehand_window(polygon_points)
    
    # Create an empty secondary window (required by the API even if not wrapped)
    secondary = create_empty_freehand_window()
    
    # Create the wrapped freehand window
    wrapped = dips.WrappedFreehandWindowVal.WrappedFreehandWindowVal()
    wrapped.id = fold_id
    wrapped.primary_window = primary
    wrapped.secondary_window = secondary
    
    # Create the fold window entity info
    fold_info = dips.FoldWindowEntityInfoVal.FoldWindowEntityInfoVal()
    fold_info.color = create_color(*color_tuple)
    fold_info.wrapped_freehand_fold_window = wrapped
    
    return fold_info


# =============================================================================
# FOLD DEMONSTRATIONS
# =============================================================================

def demonstrate_add_fold_window(model):
    """Demonstrate adding a fold window."""
    print("\n" + "=" * 60)
    print("ADD FOLD WINDOW")
    print("=" * 60)
    
    try:
        # Define a triangular fold window region
        # Points define a polygon on the stereonet (trend/plunge pairs)
        polygon_points = [
            (40, 20),   # Point 1
            (50, 30),   # Point 2
            (45, 40),   # Point 3
            (35, 30),   # Point 4
        ]
        
        fold_info = create_fold_window(
            fold_id="DemoFold",
            color_tuple=(255, 100, 0),  # Orange
            polygon_points=polygon_points
        )
        
        result = model.AddFoldWindow(fold_info)
        print(f"  ✓ AddFoldWindow: 'DemoFold'")
        print(f"    Polygon with {len(polygon_points)} vertices")
        return result
    except Exception as e:
        print(f"  ✗ AddFoldWindow: {e}")
        return None


def demonstrate_get_fold_list(model):
    """Demonstrate listing all folds."""
    print("\n" + "=" * 60)
    print("GET FOLD LIST")
    print("=" * 60)
    
    try:
        folds = model.GetFoldList()
        print(f"  ✓ GetFoldList: {len(folds)} fold(s) found")
        
        for i, fold_ref in enumerate(folds):
            fold_value = fold_ref.GetValue()
            print(f"    [{i+1}] ID: '{fold_value.id}'")
        
        return folds
    except Exception as e:
        print(f"  ✗ GetFoldList: {e}")
        return []


def demonstrate_delete_fold(model, fold_ref):
    """Demonstrate deleting a fold."""
    print("\n" + "=" * 60)
    print("DELETE FOLD")
    print("=" * 60)
    
    if fold_ref is None:
        print("  - DeleteFold: No fold to delete")
        return
    
    try:
        fold_value = fold_ref.GetValue()
        fold_id = fold_value.id
        model.DeleteFold(fold_ref)
        print(f"  ✓ DeleteFold: '{fold_id}'")
    except Exception as e:
        print(f"  ✗ DeleteFold: {e}")


def demonstrate_multiple_folds(model):
    """Demonstrate creating multiple fold windows."""
    print("\n" + "=" * 60)
    print("MULTIPLE FOLD EXAMPLES")
    print("=" * 60)
    
    # Each fold has: (id, color_rgb, list of (trend, plunge) polygon points)
    fold_examples = [
        ("NE Fold Region", (255, 0, 0), [
            (30, 15), (60, 15), (60, 35), (30, 35)  # Rectangular region in NE
        ]),
        ("NW Fold Region", (0, 255, 0), [
            (300, 20), (330, 20), (330, 40), (300, 40)  # Rectangular region in NW
        ]),
        ("E Fold Region", (0, 0, 255), [
            (80, 10), (100, 10), (100, 30), (80, 30)  # Rectangular region in E
        ]),
    ]
    
    created_folds = []
    
    for fold_id, color_rgb, polygon_points in fold_examples:
        try:
            fold_info = create_fold_window(fold_id, color_rgb, polygon_points)
            result = model.AddFoldWindow(fold_info)
            created_folds.append(result)
            print(f"  ✓ Created: '{fold_id}' ({len(polygon_points)} vertices)")
        except Exception as e:
            print(f"  ✗ Failed to create '{fold_id}': {e}")
    
    return created_folds


def demonstrate_cleanup(model, folds_to_delete):
    """Clean up created folds."""
    print("\n" + "=" * 60)
    print("CLEANUP")
    print("=" * 60)
    
    for fold_ref in folds_to_delete:
        try:
            fold_value = fold_ref.GetValue()
            model.DeleteFold(fold_ref)
            print(f"  ✓ Deleted: '{fold_value.id}'")
        except Exception as e:
            print(f"  ✗ Delete failed: {e}")


# =============================================================================
# MAIN
# =============================================================================

def main():
    """Main demonstration function."""
    print("=" * 60)
    print("Dips API - Fold Services Demo")
    print("=" * 60)
    
    # Connect to Dips
    print("\nConnecting to Dips application...")
    try:
        app = DipsApp.LaunchApp(62535)
        print("✓ Connected to Dips")
    except Exception as e:
        print(f"✗ Failed to connect: {e}")
        print("Make sure Dips is running with scripting enabled on port 62535")
        return
    
    model = app.GetModel()
    
    # Show initial state
    print("\n" + "-" * 60)
    print("INITIAL STATE")
    print("-" * 60)
    demonstrate_get_fold_list(model)
    
    # Add a single fold
    new_fold = demonstrate_add_fold_window(model)
    
    # List folds again
    demonstrate_get_fold_list(model)
    
    # Delete the fold
    demonstrate_delete_fold(model, new_fold)
    
    # Create multiple folds
    created_folds = demonstrate_multiple_folds(model)
    
    # List all folds
    demonstrate_get_fold_list(model)
    
    # Cleanup (commented out to keep folds visible)
    # demonstrate_cleanup(model, created_folds)
    
    # Show final state
    print("\n" + "-" * 60)
    print("FINAL STATE")
    print("-" * 60)
    demonstrate_get_fold_list(model)
    
    # Show the application
    app.Show()
    
    # Summary
    print("\n" + "=" * 60)
    print("DEMONSTRATION COMPLETE")
    print("=" * 60)
    print("""
Fold services demonstrated (3 methods):

  - AddFoldWindow: Create a new fold window with polygon vertices
  - GetFoldList: List all folds in the project
  - DeleteFold: Remove a fold from the project

FoldWindowEntityInfo Structure:
  - color: Display color for the fold window
  - wrapped_freehand_fold_window: WrappedFreehandWindow containing:
      - id: Unique identifier for the fold
      - primary_window: FreehandWindow with polygon points
      - secondary_window: Optional (for windows that wrap around)

FreehandWindow Structure:
  - polygon: List of TrendPlunge points defining the window boundary
  - is_wrapped: Whether the window wraps around the stereonet edge

Note: Fold windows are defined by polygon regions on the stereonet,
similar to freehand set windows. The polygon vertices are specified
as trend/plunge pairs.
""")


if __name__ == "__main__":
    main()