Set Demo#

Demonstration of Set services in Dips Python API.

This script demonstrates ISetServices methods including: - AddSetWindow: Create a new set window (Circular, Curved, or Freehand) - GetSetList: List all sets - DeleteSet: Remove a set

Available Set Window Types: - Circular: Defined by center (trend/plunge) and cone angle - Curved: Defined by two corner points (trend/plunge) - WrappedFreehand: Defined by polygon boundaries - Cluster: Created by automatic cluster analysis (not manually created)

Code Snippet: SetDemo.py#
"""
Demonstration of Set services in Dips Python API.

This script demonstrates ISetServices methods including:
- AddSetWindow: Create a new set window (Circular, Curved, or Freehand)
- GetSetList: List all sets
- DeleteSet: Remove a set

Available Set Window Types:
- Circular: Defined by center (trend/plunge) and cone angle
- Curved: Defined by two corner points (trend/plunge)
- WrappedFreehand: Defined by polygon boundaries
- Cluster: Created by automatic cluster analysis (not manually created)
"""

import math
from dips import DipsApp
from dips import DipsAPI_pb2

import dips.SetWindowEntityInfoVal
import dips.CircularWindowVal
import dips.CurvedWindowVal
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


# =============================================================================
# CIRCULAR SET WINDOW DEMONSTRATIONS
# =============================================================================

def demonstrate_add_circular_set_window(model):
    """Demonstrate adding a circular set window."""
    print("\n" + "=" * 60)
    print("ADD CIRCULAR SET WINDOW")
    print("=" * 60)
    
    try:
        set_window = dips.SetWindowEntityInfoVal.SetWindowEntityInfoVal()
        set_window.set_window_type = DipsAPI_pb2.eSetWindowType.Circular
        set_window.color = create_color(0, 100, 255)  # Blue
        
        # Get default statistics settings
        set_window.statistics_settings = model.GetDefaultSetStatisticsSettings()
        
        # Configure circular window
        circular = dips.CircularWindowVal.CircularWindowVal()
        circular.id = "CircularSet1"
        circular.center = create_trend_plunge(180, 45)
        circular.cone_angle = dips.AngleDataVal.AngleDataVal()
        circular.cone_angle.angle_radians = math.radians(30)
        set_window.circular_set_window = circular
        
        result = model.AddSetWindow(set_window)
        print(f"  ✓ AddSetWindow (Circular): '{circular.id}'")
        print(f"    Center: Trend=180°, Plunge=45°")
        print(f"    Cone Angle: 30°")
        return result
    except Exception as e:
        print(f"  ✗ AddSetWindow (Circular): {e}")
        return None


# =============================================================================
# CURVED SET WINDOW DEMONSTRATIONS
# =============================================================================

def demonstrate_add_curved_set_window(model):
    """Demonstrate adding a curved set window."""
    print("\n" + "=" * 60)
    print("ADD CURVED SET WINDOW")
    print("=" * 60)
    
    try:
        set_window = dips.SetWindowEntityInfoVal.SetWindowEntityInfoVal()
        set_window.set_window_type = DipsAPI_pb2.eSetWindowType.Curved
        set_window.color = create_color(255, 100, 0)  # Orange
        
        # Get default statistics settings
        set_window.statistics_settings = model.GetDefaultSetStatisticsSettings()
        
        # Configure curved window with two corner points
        curved = dips.CurvedWindowVal.CurvedWindowVal()
        curved.id = "CurvedSet1"
        curved.first_corner = create_trend_plunge(60, 30)
        curved.second_corner = create_trend_plunge(120, 60)
        set_window.curved_set_window = curved
        
        result = model.AddSetWindow(set_window)
        print(f"  ✓ AddSetWindow (Curved): '{curved.id}'")
        print(f"    First Corner: Trend=60°, Plunge=30°")
        print(f"    Second Corner: Trend=120°, Plunge=60°")
        return result
    except Exception as e:
        print(f"  ✗ AddSetWindow (Curved): {e}")
        return None


# =============================================================================
# FREEHAND SET WINDOW DEMONSTRATIONS
# =============================================================================

def demonstrate_add_freehand_set_window(model):
    """Demonstrate adding a freehand (wrapped freehand) set window."""
    print("\n" + "=" * 60)
    print("ADD FREEHAND SET WINDOW")
    print("=" * 60)
    
    try:
        set_window = dips.SetWindowEntityInfoVal.SetWindowEntityInfoVal()
        set_window.set_window_type = DipsAPI_pb2.eSetWindowType.WrappedFreehand
        set_window.color = create_color(0, 200, 100)  # Green
        
        # Get default statistics settings
        set_window.statistics_settings = model.GetDefaultSetStatisticsSettings()
        
        # Configure freehand window with polygon points
        freehand = dips.WrappedFreehandWindowVal.WrappedFreehandWindowVal()
        freehand.id = "FreehandSet1"
        
        # Primary window - define a polygon of points
        primary = dips.FreehandWindowVal.FreehandWindowVal()
        primary.is_wrapped = False
        # Create a roughly pentagonal region
        primary.polygon.append(create_trend_plunge(200, 40))
        primary.polygon.append(create_trend_plunge(220, 50))
        primary.polygon.append(create_trend_plunge(240, 45))
        primary.polygon.append(create_trend_plunge(230, 35))
        primary.polygon.append(create_trend_plunge(210, 30))
        freehand.primary_window = primary
        
        # Secondary window (can be empty or used for wrapped sets)
        secondary = dips.FreehandWindowVal.FreehandWindowVal()
        secondary.is_wrapped = False
        freehand.secondary_window = secondary
        
        set_window.wrapped_freehand_set_window = freehand
        
        result = model.AddSetWindow(set_window)
        print(f"  ✓ AddSetWindow (Freehand): '{freehand.id}'")
        print(f"    Polygon points: {len(primary.polygon)}")
        print(f"    Points: (200°/40°), (220°/50°), (240°/45°), (230°/35°), (210°/30°)")
        return result
    except Exception as e:
        print(f"  ✗ AddSetWindow (Freehand): {e}")
        return None


# =============================================================================
# GET AND DELETE DEMONSTRATIONS
# =============================================================================

def demonstrate_get_set_list(model):
    """Demonstrate listing all sets."""
    print("\n" + "=" * 60)
    print("GET SET LIST")
    print("=" * 60)
    
    try:
        sets = model.GetSetList()
        print(f"  ✓ GetSetList: {len(sets)} set(s) found")
        
        for i, set_ref in enumerate(sets):
            set_value = set_ref.GetValue()
            # SetEntityInfo has: id, color, set_window_entity_info
            # set_window_type is inside set_window_entity_info
            window_info = set_value.set_window_entity_info
            window_type = DipsAPI_pb2.eSetWindowType.Name(window_info.set_window_type)
            set_id = set_value.id
            
            print(f"    [{i+1}] ID: '{set_id}' ({window_type})")
        
        return sets
    except Exception as e:
        print(f"  ✗ GetSetList: {e}")
        return []


def demonstrate_delete_set(model, set_ref, description=""):
    """Demonstrate deleting a set."""
    if set_ref is None:
        print(f"  - DeleteSet{description}: No set to delete")
        return
    
    try:
        set_value = set_ref.GetValue()
        # SetEntityInfo has: id, color, set_window_entity_info
        window_info = set_value.set_window_entity_info
        window_type = DipsAPI_pb2.eSetWindowType.Name(window_info.set_window_type)
        set_id = set_value.id
        
        model.DeleteSet(set_ref)
        print(f"  ✓ DeleteSet: '{set_id}' ({window_type})")
    except Exception as e:
        print(f"  ✗ DeleteSet: {e}")


# =============================================================================
# MULTIPLE SETS DEMONSTRATION
# =============================================================================

def demonstrate_multiple_sets(model):
    """Demonstrate creating multiple sets of different types."""
    print("\n" + "=" * 60)
    print("MULTIPLE SET EXAMPLES")
    print("=" * 60)
    
    created_sets = []
    
    # Create circular sets at different orientations
    print("\n  Creating Circular Sets:")
    circular_examples = [
        ("Set_N", 0, 45, 25, (255, 0, 0)),     # North
        ("Set_E", 90, 60, 20, (0, 255, 0)),    # East
        ("Set_S", 180, 30, 30, (0, 0, 255)),   # South
    ]
    
    for set_id, trend, plunge, cone_angle, color_rgb in circular_examples:
        try:
            set_window = dips.SetWindowEntityInfoVal.SetWindowEntityInfoVal()
            set_window.set_window_type = DipsAPI_pb2.eSetWindowType.Circular
            set_window.color = create_color(*color_rgb)
            set_window.statistics_settings = model.GetDefaultSetStatisticsSettings()
            
            circular = dips.CircularWindowVal.CircularWindowVal()
            circular.id = set_id
            circular.center = create_trend_plunge(trend, plunge)
            circular.cone_angle = dips.AngleDataVal.AngleDataVal()
            circular.cone_angle.angle_radians = math.radians(cone_angle)
            set_window.circular_set_window = circular
            
            result = model.AddSetWindow(set_window)
            created_sets.append(result)
            print(f"    ✓ '{set_id}' (Circular: {trend}°/{plunge}°, Cone: {cone_angle}°)")
        except Exception as e:
            print(f"    ✗ '{set_id}': {e}")
    
    # Create curved sets
    print("\n  Creating Curved Sets:")
    curved_examples = [
        ("Curved_NE", 30, 20, 60, 50, (255, 128, 0)),
        ("Curved_SW", 210, 40, 250, 70, (128, 0, 255)),
    ]
    
    for set_id, t1, p1, t2, p2, color_rgb in curved_examples:
        try:
            set_window = dips.SetWindowEntityInfoVal.SetWindowEntityInfoVal()
            set_window.set_window_type = DipsAPI_pb2.eSetWindowType.Curved
            set_window.color = create_color(*color_rgb)
            set_window.statistics_settings = model.GetDefaultSetStatisticsSettings()
            
            curved = dips.CurvedWindowVal.CurvedWindowVal()
            curved.id = set_id
            curved.first_corner = create_trend_plunge(t1, p1)
            curved.second_corner = create_trend_plunge(t2, p2)
            set_window.curved_set_window = curved
            
            result = model.AddSetWindow(set_window)
            created_sets.append(result)
            print(f"    ✓ '{set_id}' (Curved: {t1}°/{p1}° to {t2}°/{p2}°)")
        except Exception as e:
            print(f"    ✗ '{set_id}': {e}")
    
    # Create freehand set
    print("\n  Creating Freehand Sets:")
    try:
        set_window = dips.SetWindowEntityInfoVal.SetWindowEntityInfoVal()
        set_window.set_window_type = DipsAPI_pb2.eSetWindowType.WrappedFreehand
        set_window.color = create_color(0, 200, 200)
        set_window.statistics_settings = model.GetDefaultSetStatisticsSettings()
        
        freehand = dips.WrappedFreehandWindowVal.WrappedFreehandWindowVal()
        freehand.id = "Freehand_Custom"
        
        primary = dips.FreehandWindowVal.FreehandWindowVal()
        primary.is_wrapped = False
        # Create a custom region
        primary.polygon.append(create_trend_plunge(300, 20))
        primary.polygon.append(create_trend_plunge(320, 35))
        primary.polygon.append(create_trend_plunge(340, 30))
        primary.polygon.append(create_trend_plunge(330, 15))
        freehand.primary_window = primary
        
        secondary = dips.FreehandWindowVal.FreehandWindowVal()
        secondary.is_wrapped = False
        freehand.secondary_window = secondary
        
        set_window.wrapped_freehand_set_window = freehand
        
        result = model.AddSetWindow(set_window)
        created_sets.append(result)
        print(f"    ✓ 'Freehand_Custom' (Freehand: 4 polygon points)")
    except Exception as e:
        print(f"    ✗ 'Freehand_Custom': {e}")
    
    return created_sets


def demonstrate_cleanup(model, sets_to_delete):
    """Clean up created sets."""
    print("\n" + "=" * 60)
    print("CLEANUP")
    print("=" * 60)
    
    for set_ref in sets_to_delete:
        demonstrate_delete_set(model, set_ref)


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

def main():
    """Main demonstration function."""
    print("=" * 60)
    print("Dips API - Set 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_set_list(model)
    
    # Add each type of set window
    circular_set = demonstrate_add_circular_set_window(model)
    curved_set = demonstrate_add_curved_set_window(model)
    freehand_set = demonstrate_add_freehand_set_window(model)
    
    # List sets after adding
    demonstrate_get_set_list(model)
    
    # Delete the sets
    print("\n" + "=" * 60)
    print("DELETE INDIVIDUAL SETS")
    print("=" * 60)
    demonstrate_delete_set(model, circular_set, " (Circular)")
    demonstrate_delete_set(model, curved_set, " (Curved)")
    demonstrate_delete_set(model, freehand_set, " (Freehand)")
    
    # Create multiple sets of all types
    created_sets = demonstrate_multiple_sets(model)
    
    # List all sets
    demonstrate_get_set_list(model)
    
    # Cleanup (commented out to keep sets visible)
    # demonstrate_cleanup(model, created_sets)
    
    # Show final state
    print("\n" + "-" * 60)
    print("FINAL STATE")
    print("-" * 60)
    demonstrate_get_set_list(model)
    
    # Show the application
    app.Show()
    
    # Summary
    print("\n" + "=" * 60)
    print("DEMONSTRATION COMPLETE")
    print("=" * 60)
    print("""
Set services demonstrated (3 methods):

  - AddSetWindow: Create a new set window
  - GetSetList: List all sets in the project
  - DeleteSet: Remove a set from the project

Set Window Types (eSetWindowType):

  1. CIRCULAR
     - Defined by: center (trend/plunge) + cone angle
     - Properties: id, center, cone_angle
     - Use case: Symmetric concentration around a pole

  2. CURVED
     - Defined by: two corner points (trend/plunge)
     - Properties: id, first_corner, second_corner
     - Use case: Elongated or asymmetric pole concentrations

  3. WRAPPED FREEHAND
     - Defined by: polygon of points (trend/plunge)
     - Properties: id, primary_window (polygon), secondary_window
     - Use case: Irregular or complex-shaped pole concentrations

  4. CLUSTER (read-only)
     - Created by automatic cluster analysis
     - Not manually created via AddSetWindow

Common Properties:
  - color: Display color for the set
  - statistics_settings: Statistical analysis settings
""")


if __name__ == "__main__":
    main()