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.FoldWindowEntityInfoVal import FoldWindowEntityInfoVal
from dips.WrappedFreehandWindowVal import WrappedFreehandWindowVal
from dips.FreehandWindowVal import FreehandWindowVal
from dips.TrendPlungeVal import TrendPlungeVal
from dips.ColorSurrogateVal import ColorSurrogateVal


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

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


def create_empty_freehand_window():
    """Create an empty FreehandWindow (for secondary window when not wrapped)."""
    window = 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 = WrappedFreehandWindowVal()
    wrapped.id = fold_id
    wrapped.primary_window = primary
    wrapped.secondary_window = secondary
    
    # Create the fold window entity info
    fold_info = FoldWindowEntityInfoVal()
    fold_info.color = ColorSurrogateVal.FromRGBA(*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)
    
    # 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


def demonstrate_get_fold_list(model):
    """Demonstrate listing all folds."""
    print("\n" + "=" * 60)
    print("GET FOLD LIST")
    print("=" * 60)
    
    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


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
    
    fold_value = fold_ref.GetValue()
    fold_id = fold_value.id
    model.DeleteFold(fold_ref)
    print(f"  ✓ DeleteFold: '{fold_id}'")


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:
        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)")
    
    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:
        fold_value = fold_ref.GetValue()
        model.DeleteFold(fold_ref)
        print(f"  ✓ Deleted: '{fold_value.id}'")


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

def main():
    """Main demonstration function."""
    print("=" * 60)
    print("Dips API - Fold Services Demo")
    print("=" * 60)
    
    # Connect to Dips
    print("\nConnecting to Dips application...")
    app = DipsApp.LaunchApp(62535)
    print("✓ Connected to Dips")
    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()