Chart Types Demo#

Dips API Chart Types Demo

This script demonstrates how to create and interact with all chart types available in the Dips scripting API. It creates sample traverse data and shows how to use GetDefault methods for easy settings initialization.

Chart types demonstrated: - Histogram Chart - Scatter Chart - Cumulative Chart - Qualitative/Quantitative Analysis Chart - Joint Spacing Analysis Chart - Joint Frequency Analysis Chart - RQD Analysis Chart - Kinematic Sensitivity Analysis Chart

Code Snippet: ChartTypesDemo.py#
"""
Dips API Chart Types Demo

This script demonstrates how to create and interact with all chart types
available in the Dips scripting API. It creates sample traverse data and
shows how to use GetDefault methods for easy settings initialization.

Chart types demonstrated:
- Histogram Chart
- Scatter Chart
- Cumulative Chart
- Qualitative/Quantitative Analysis Chart
- Joint Spacing Analysis Chart
- Joint Frequency Analysis Chart
- RQD Analysis Chart
- Kinematic Sensitivity Analysis Chart
"""

import math
import random

import dips
from dips import DipsApp
from dips import DipsAPI_pb2
from dips import BuiltInDataDescriptors
from dips import BuiltInDataFormatters
from dips import DiscontinuityDataVal
from dips import CustomColumnCollectionVal
from dips import FullDataFormatVal
from dips import CustomRowRawDataVal
from dips import DataFilterVal
from dips.DataDescriptorVal import DataDescriptorVal

# Settings wrappers (used in update_chart_settings for manual construction)
import dips.HistogramPlotSettingsVal
import dips.ScatterPlotSettingsVal
import dips.CumulativePlotSettingsVal
import dips.QualitativeQuantitativeAnalysisSettingsVal
import dips.JointSpacingAnalysisSettingsVal
import dips.JointFrequencyAnalysisSettingsVal
import dips.RQDAnalysisSettingsVal
import dips.KinematicSensitivitySettingsVal

# Set-related wrappers
import dips.SetWindowEntityInfoVal
import dips.CircularWindowVal
import dips.ColorSurrogateVal
import dips.TrendPlungeVal

def create_sample_traverse(model):
    """
    Create a sample linear traverse with 50 discontinuities.
    
    The traverse includes custom columns (Aperture, Quality) to demonstrate
    how chart types can analyze custom data.
    
    Args:
        model: The Dips project model
        
    Returns:
        Reference to the created traverse
    """
    print("Creating sample traverse with discontinuities...")
    
    # Use GetDefault to get a properly initialized OrientationDataSet
    traverse = model.GetDefaultOrientationDataSet()
    traverse.name = "Sample Traverse for Chart Demo"
    traverse.orientation_data_type = DipsAPI_pb2.eOrientationDataType.LinearScanline
    traverse.orientation_convention = DipsAPI_pb2.eOrientationConvention.TrendPlungeOrientation
    traverse.discontinuity_orientation_convention = DipsAPI_pb2.eOrientationConvention.TrendPlungeOrientation
    
    # Set traverse orientation (trend=20°, plunge=90°)
    traverse.orient1.angle_radians = math.radians(20)
    traverse.orient2.angle_radians = math.radians(90)
    traverse.depth = 100
    
    # Set position
    traverse.local_position.x = 0
    traverse.local_position.y = 0
    traverse.local_position.z = 0
    traverse.global_position.latitude = 0
    traverse.global_position.longitude = 0
    traverse.global_position.elevation = 0
    
    # Set units (all in meters)
    traverse.traverse_elevation_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    traverse.traverse_xyz_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    traverse.traverse_depth_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    traverse.discontinuity_distance_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    traverse.discontinuity_xyz_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    traverse.discontinuity_persistence_unit = BuiltInDataFormatters.LengthMeterDataFormatter

    # Generate 50 discontinuities with random orientations
    cumulative_distance = 0.0
    for _ in range(50):
        discontinuity = DiscontinuityDataVal.DiscontinuityDataVal()
        
        # Random orientation
        discontinuity.orientation1.angle_radians = math.radians(random.uniform(0, 360))
        discontinuity.orientation2.angle_radians = math.radians(random.uniform(0, 90))
        discontinuity.quantity = random.uniform(0.1, 2.0)
        
        # Ascending distance along traverse
        cumulative_distance += random.uniform(0.1, 2.0)
        discontinuity.distance = cumulative_distance
        
        # Custom data columns
        custom_data = CustomRowRawDataVal.CustomRowRawDataVal()
        custom_data.raw_data[0] = str(random.uniform(0.1, 50.0))  # Aperture (mm)
        custom_data.raw_data[1] = random.choice(["Good", "Fair", "Poor"])  # Quality
        discontinuity.extra_data = custom_data
        
        traverse.discontinuity_list.append(discontinuity)
    
    # Define custom column formats
    custom_columns = CustomColumnCollectionVal.CustomColumnCollectionVal()
    
    aperture_column = FullDataFormatVal.FullDataFormatVal()
    aperture_column.descriptor.data_name.data_name = "Aperture"
    aperture_column.descriptor.data_type = DipsAPI_pb2.eDataType.Length
    aperture_column.format = BuiltInDataFormatters.LengthMillimeterDataFormatter
    custom_columns.custom_columns.append(aperture_column)
    
    quality_column = FullDataFormatVal.FullDataFormatVal()
    quality_column.descriptor.data_name.data_name = "Quality"
    quality_column.descriptor.data_type = DipsAPI_pb2.eDataType.Text
    quality_column.format = BuiltInDataFormatters.TextDataFormatter
    custom_columns.custom_columns.append(quality_column)

    traverse.discontinuity_extra_columns = custom_columns

    # Add to model
    print("Adding traverse to model...")
    traverse_ref = model.AddTraverse(traverse)
    
    return traverse_ref

def create_sample_set(model):
    """
    Create a sample circular set window for Joint Spacing analysis.
    
    Args:
        model: The Dips project model
        
    Returns:
        Reference to the created set window
    """
    print("Creating sample set for Joint Spacing analysis...")
    
    set_window = dips.SetWindowEntityInfoVal.SetWindowEntityInfoVal()
    set_window.set_window_type = DipsAPI_pb2.eSetWindowType.Circular
    
    # Blue color
    color = dips.ColorSurrogateVal.ColorSurrogateVal()
    color.r, color.g, color.b, color.a = 0, 100, 255, 255
    set_window.color = color
    
    # Statistics settings
    set_window.statistics_settings = model.GetDefaultSetStatisticsSettings()
    
    # Circular window centered at trend=180°, plunge=45° with 30° cone angle
    circular = dips.CircularWindowVal.CircularWindowVal()
    circular.id = "SampleSet"
    circular.center = dips.TrendPlungeVal.TrendPlungeVal()
    circular.center.trend.angle_radians = math.radians(180)
    circular.center.plunge.angle_radians = math.radians(45)
    circular.cone_angle.angle_radians = math.radians(30)
    set_window.circular_set_window = circular
    
    set_ref = model.AddSetWindow(set_window)
    print(f"Created sample set: {set_ref}")
    
    return set_ref


def create_sample_filter(model):
    """
    Create a sample data filter that filters by Dip angle.
    
    Args:
        model: The Dips project model
        
    Returns:
        Reference to the created data filter
    """
    print("Creating sample data filter...")
    
    data_filter = DataFilterVal.DataFilterVal()
    data_filter.name = "High Dip Filter"
    data_filter.filter_string = "[Dip] > 45"
    
    filter_ref = model.AddDataFilter(data_filter)
    print(f"Created data filter: {filter_ref}")
    
    return filter_ref


def create_histogram_chart(model):
    """
    Create a histogram chart showing the distribution of Aperture values.
    
    Args:
        model: The Dips project model
        
    Returns:
        Reference to the created histogram chart
    """
    print("Creating histogram chart...")
    
    settings = model.GetDefaultHistogramPlotSettings()
    settings.selected_column.data_name.data_name = "Aperture"
    settings.selected_column.data_type = DipsAPI_pb2.eDataType.Length
    settings.num_bins = 15
    
    chart = model.CreateHistogramChartView(settings)
    print(f"Created histogram chart: {chart}")
    return chart


def create_scatter_chart(model):
    """
    Create a scatter plot chart showing Dip vs Dip Direction.
    
    Args:
        model: The Dips project model
        
    Returns:
        Reference to the created scatter chart
    """
    print("Creating scatter plot chart...")
    
    settings = model.GetDefaultScatterPlotSettings()
    settings.selected_column_x = BuiltInDataDescriptors.DipDescriptor
    settings.selected_column_y = BuiltInDataDescriptors.DipDirectionDescriptor
    
    chart = model.CreateScatterChartView(settings)
    print(f"Created scatter chart: {chart}")
    return chart


def create_cumulative_chart(model):
    """
    Create a cumulative plot chart showing the distribution of Dip values.
    
    Args:
        model: The Dips project model
        
    Returns:
        Reference to the created cumulative chart
    """
    print("Creating cumulative plot chart...")
    
    settings = model.GetDefaultCumulativePlotSettings()
    settings.selected_column = BuiltInDataDescriptors.DipDescriptor
    settings.num_bins = 10
    
    chart = model.CreateCumulativeChartView(settings)
    print(f"Created cumulative chart: {chart}")
    return chart


def create_qualitative_quantitative_chart(model):
    """
    Create a qualitative analysis chart showing Quality categories.
    
    Args:
        model: The Dips project model
        
    Returns:
        Reference to the created qualitative/quantitative chart
    """
    print("Creating qualitative/quantitative analysis chart...")
    
    settings = model.GetDefaultQualitativeQuantitativeAnalysisSettings()
    settings.analysis_type = DipsAPI_pb2.eAnalysisType.Qualitative
    settings.selected_column.data_name.data_name = "Quality"
    settings.selected_column.data_type = DipsAPI_pb2.eDataType.Text
    settings.allocated_items = ["Good", "Fair", "Poor"]
    settings.is_weighted = False
    
    chart = model.CreateQualitativeQuantitativeChartView(settings)
    print(f"Created qualitative/quantitative chart: {chart}")
    return chart


def create_joint_spacing_chart(model, set_ref, traverse_ref):
    """
    Create a joint spacing analysis chart.
    
    Args:
        model: The Dips project model
        set_ref: Reference to a set window (required)
        traverse_ref: Reference to a traverse (required)
        
    Returns:
        Reference to the created joint spacing chart
    """
    print("Creating joint spacing analysis chart...")
    
    settings = model.GetDefaultJointSpacingAnalysisSettings()
    settings.spacing_option = DipsAPI_pb2.eSpacingOption.TrueSpacing
    settings.set = set_ref
    settings.traverses.append(traverse_ref.get_model_ref())
    settings.num_intervals = 10
    settings.show_best_fit_distribution = True
    
    chart = model.CreateJointSpacingChartView(settings)
    print(f"Created joint spacing chart: {chart}")
    return chart


def create_joint_frequency_chart(model, traverse_ref):
    """
    Create a joint frequency analysis chart.
    
    Args:
        model: The Dips project model
        traverse_ref: Reference to a linear or curved traverse (required)
        
    Returns:
        Reference to the created joint frequency chart
    """
    print("Creating joint frequency analysis chart...")
    
    settings = model.GetDefaultJointFrequencyAnalysisSettings()
    settings.interval.interval_option = DipsAPI_pb2.eIntervalOption.Discrete
    settings.traverses.append(traverse_ref.get_model_ref())
    settings.is_weighted = False
    
    chart = model.CreateJointFrequencyChartView(settings)
    print(f"Created joint frequency chart: {chart}")
    return chart


def create_rqd_analysis_chart(model, traverse_ref):
    """
    Create an RQD (Rock Quality Designation) analysis chart.
    
    Args:
        model: The Dips project model
        traverse_ref: Reference to a traverse (required)
        
    Returns:
        Reference to the created RQD analysis chart
    """
    print("Creating RQD analysis chart...")
    
    settings = model.GetDefaultRQDAnalysisSettings()
    settings.interval.interval_option = DipsAPI_pb2.eIntervalOption.Discrete
    settings.traverses.append(traverse_ref.get_model_ref())
    
    chart = model.CreateRQDAnalysisChartView(settings)
    print(f"Created RQD analysis chart: {chart}")
    return chart


def create_kinematic_sensitivity_chart(model):
    """
    Create a kinematic sensitivity analysis chart for planar sliding.
    
    Configures sensitivity ranges for slope dip, dip direction, and friction angle.
    
    Args:
        model: The Dips project model
        
    Returns:
        Reference to the created kinematic sensitivity chart
    """
    print("Creating kinematic sensitivity chart...")
    
    # Get default kinematic analysis settings first, then convert to sensitivity settings
    kinematic_settings = model.GetDefaultKinematicAnalysisSettings()
    settings = model.GetDefaultKinematicSensitivitySettings(kinematic_settings)
    settings.failure_mode_option = DipsAPI_pb2.eFailureModeOption.PlanarSliding
    # At least 1 kinematic property must be applied
    settings.friction_angle.apply = True
    settings.friction_angle.current.angle_radians = math.radians(30)
    settings.friction_angle.range_from.angle_radians = math.radians(10)
    settings.friction_angle.range_to.angle_radians = math.radians(35)
    settings.friction_angle.interval.angle_radians = math.radians(1)
    
    # Chart display options
    settings.chart_type = DipsAPI_pb2.eKinematicSensitivityChartType.CartesianChart
    settings.critical = DipsAPI_pb2.eCriticalType.PercentCritical
    settings.is_weighted = False
    
    chart = model.CreateKinematicSensitivityChartView(settings.to_proto())
    print(f"Created kinematic sensitivity chart: {chart}")
    return chart


def set_active_filter_for_chart(chart, chart_name, filter_ref):
    """Set the active data filter for a single chart view."""
    try:
        chart.SetActiveDataFilter(filter_ref)
        print(f"  ✓ {chart_name}: Active filter set")
    except Exception as e:
        print(f"  ✗ {chart_name}: Error - {type(e).__name__}: {e}")


def set_active_filter_for_all_charts(charts, filter_ref):
    """
    Set the active data filter for all chart views.
    
    Args:
        charts: Dictionary mapping chart names to chart references
        filter_ref: Reference to the data filter to set as active
    """
    print("\n" + "=" * 60)
    print("SETTING ACTIVE FILTER FOR ALL CHARTS")
    print("=" * 60)
    
    if "Histogram Chart" in charts:
        set_active_filter_for_chart(charts["Histogram Chart"], "Histogram Chart", filter_ref)
    if "Scatter Chart" in charts:
        set_active_filter_for_chart(charts["Scatter Chart"], "Scatter Chart", filter_ref)
    if "Cumulative Chart" in charts:
        set_active_filter_for_chart(charts["Cumulative Chart"], "Cumulative Chart", filter_ref)
    if "Qualitative/Quantitative Chart" in charts:
        set_active_filter_for_chart(charts["Qualitative/Quantitative Chart"], "Qualitative/Quantitative Chart", filter_ref)
    if "Joint Spacing Chart" in charts:
        set_active_filter_for_chart(charts["Joint Spacing Chart"], "Joint Spacing Chart", filter_ref)
    if "Joint Frequency Chart" in charts:
        set_active_filter_for_chart(charts["Joint Frequency Chart"], "Joint Frequency Chart", filter_ref)
    if "RQD Analysis Chart" in charts:
        set_active_filter_for_chart(charts["RQD Analysis Chart"], "RQD Analysis Chart", filter_ref)
    if "Kinematic Sensitivity Chart" in charts:
        set_active_filter_for_chart(charts["Kinematic Sensitivity Chart"], "Kinematic Sensitivity Chart", filter_ref)


def set_weighted_for_applicable_charts(charts, is_weighted=True):
    """
    Set IsWeighted for all chart types that support it.
    
    Supported chart types:
    - Histogram Chart
    - Scatter Chart
    - Cumulative Chart
    - Qualitative/Quantitative Chart
    - Joint Frequency Chart
    - Kinematic Sensitivity Chart
    
    NOT supported:
    - Joint Spacing Chart
    - RQD Analysis Chart
    
    Args:
        charts: Dictionary mapping chart names to chart references
        is_weighted: Whether to enable weighting (default: True)
    """
    print("\n" + "=" * 60)
    print(f"SETTING IS_WEIGHTED = {is_weighted} FOR APPLICABLE CHARTS")
    print("=" * 60)
    
    # Histogram Chart - supports SetIsWeighted
    if "Histogram Chart" in charts:
        try:
            charts["Histogram Chart"].SetIsWeighted(is_weighted)
            print(f"  ✓ Histogram Chart: IsWeighted = {is_weighted}")
        except Exception as e:
            print(f"  ✗ Histogram Chart: Error - {e}")
    
    # Scatter Chart - supports SetIsWeighted
    if "Scatter Chart" in charts:
        try:
            charts["Scatter Chart"].SetIsWeighted(is_weighted)
            print(f"  ✓ Scatter Chart: IsWeighted = {is_weighted}")
        except Exception as e:
            print(f"  ✗ Scatter Chart: Error - {e}")
    
    # Cumulative Chart - supports SetIsWeighted
    if "Cumulative Chart" in charts:
        try:
            charts["Cumulative Chart"].SetIsWeighted(is_weighted)
            print(f"  ✓ Cumulative Chart: IsWeighted = {is_weighted}")
        except Exception as e:
            print(f"  ✗ Cumulative Chart: Error - {e}")
    
    # Qualitative/Quantitative Chart - supports SetIsWeighted
    if "Qualitative/Quantitative Chart" in charts:
        try:
            charts["Qualitative/Quantitative Chart"].SetIsWeighted(is_weighted)
            print(f"  ✓ Qualitative/Quantitative Chart: IsWeighted = {is_weighted}")
        except Exception as e:
            print(f"  ✗ Qualitative/Quantitative Chart: Error - {e}")
    
    # Joint Spacing Chart - does NOT support SetIsWeighted
    if "Joint Spacing Chart" in charts:
        print("  - Joint Spacing Chart: SetIsWeighted not supported")
    
    # Joint Frequency Chart - supports SetIsWeighted
    if "Joint Frequency Chart" in charts:
        try:
            charts["Joint Frequency Chart"].SetIsWeighted(is_weighted)
            print(f"  ✓ Joint Frequency Chart: IsWeighted = {is_weighted}")
        except Exception as e:
            print(f"  ✗ Joint Frequency Chart: Error - {e}")
    
    # RQD Analysis Chart - does NOT support SetIsWeighted
    if "RQD Analysis Chart" in charts:
        print("  - RQD Analysis Chart: SetIsWeighted not supported")
    
    # Kinematic Sensitivity Chart - supports SetIsWeighted
    if "Kinematic Sensitivity Chart" in charts:
        try:
            charts["Kinematic Sensitivity Chart"].SetIsWeighted(is_weighted)
            print(f"  ✓ Kinematic Sensitivity Chart: IsWeighted = {is_weighted}")
        except Exception as e:
            print(f"  ✗ Kinematic Sensitivity Chart: Error - {e}")


def update_chart_settings(chart, chart_name):
    """
    Update settings for a specific chart type.
    
    This demonstrates getting existing settings via GetValue(), modifying them,
    and applying the changes with Update*Settings().
    
    Args:
        chart: The chart reference to update
        chart_name: Name of the chart type for dispatch
    """
    if chart_name == "Histogram Chart":
        # Get existing settings from the chart
        settings = chart.GetValue().histogram_plot_settings
        # Modify specific properties
        settings.num_bins = 20
        result = chart.UpdateHistogramPlotSettings(settings)
        print(f"   ✓ UpdateHistogramPlotSettings() - num_bins=20")
    
    elif chart_name == "Scatter Chart":
        settings = chart.GetValue().scatter_plot_settings
        settings.selected_column_x = BuiltInDataDescriptors.DipDescriptor
        settings.selected_column_y = BuiltInDataDescriptors.DipDirectionDescriptor
        result = chart.UpdateScatterPlotSettings(settings)
        print(f"   ✓ UpdateScatterPlotSettings() - updated columns")
        
    elif chart_name == "Cumulative Chart":
        settings = chart.GetValue().cumulative_plot_settings
        settings.num_bins = 15
        result = chart.UpdateCumulativePlotSettings(settings)
        print(f"   ✓ UpdateCumulativePlotSettings() - num_bins=15")
        
    elif chart_name == "Qualitative/Quantitative Chart":
        settings = chart.GetValue().qualitative_quantitative_analysis_settings
        settings.analysis_type = DipsAPI_pb2.eAnalysisType.Quantitative
        settings.num_bins = 10
        result = chart.UpdateQualitativeQuantitativeAnalysisSettings(settings)
        print(f"   ✓ UpdateQualitativeQuantitativeAnalysisSettings() - Quantitative, num_bins=10")
        
    elif chart_name == "Joint Spacing Chart":
        settings = chart.GetValue().joint_spacing_analysis_settings
        settings.spacing_option = DipsAPI_pb2.eSpacingOption.ApparentSpacing
        settings.num_intervals = 15
        settings.show_best_fit_distribution = False
        result = chart.UpdateJointSpacingAnalysisSettings(settings)
        print(f"   ✓ UpdateJointSpacingAnalysisSettings() - ApparentSpacing, num_intervals=15")
        
    elif chart_name == "Joint Frequency Chart":
        settings = chart.GetValue().joint_frequency_analysis_settings
        settings.interval.interval_option = DipsAPI_pb2.eIntervalOption.Moving
        settings.interval.distance_interval.length = 1.0
        settings.interval.distance_interval.length_unit = BuiltInDataFormatters.LengthMeterDataFormatter
        settings.interval.distance_move_increment.length = 0.1
        settings.interval.distance_move_increment.length_unit = BuiltInDataFormatters.LengthMeterDataFormatter
        settings.num_bins = 10
        result = chart.UpdateJointFrequencyAnalysisSettings(settings)
        print(f"   ✓ UpdateJointFrequencyAnalysisSettings() - Moving interval, num_bins=10")
        
    elif chart_name == "RQD Analysis Chart":
        settings = chart.GetValue().rqd_analysis_settings
        settings.interval.interval_option = DipsAPI_pb2.eIntervalOption.Moving
        settings.interval.distance_interval.length = 1.0
        settings.interval.distance_interval.length_unit = BuiltInDataFormatters.LengthMeterDataFormatter
        settings.interval.distance_move_increment.length = 0.1
        settings.interval.distance_move_increment.length_unit = BuiltInDataFormatters.LengthMeterDataFormatter
        result = chart.UpdateRQDAnalysisSettings(settings)
        print(f"   ✓ UpdateRQDAnalysisSettings() - Moving interval")
        
    elif chart_name == "Kinematic Sensitivity Chart":
        settings = chart.GetValue().kinematic_sensitivity_settings
        settings.failure_mode_option = DipsAPI_pb2.eFailureModeOption.PlanarSliding
        settings.slope_dip.apply = True
        settings.slope_dip.current.angle_radians = math.radians(50)
        settings.slope_dip.range_from.angle_radians = math.radians(35)
        settings.slope_dip.range_to.angle_radians = math.radians(90)
        settings.slope_dip.interval.angle_radians = math.radians(5)
        settings.chart_type = DipsAPI_pb2.eKinematicSensitivityChartType.PolarChart
        result = chart.UpdateKinematicSensitivitySettings(settings)
        print(f"   ✓ UpdateKinematicSensitivitySettings() - PlanarSliding, PolarChart")
        
    else:
        print(f"   - No update defined for {chart_name}")


def update_all_chart_settings(charts):
    """
    Update settings for all chart views.
    
    This demonstrates the Update*Settings method for each chart type,
    showing how to modify chart settings after creation.
    
    Args:
        charts: Dictionary mapping chart names to chart references
    """
    print("\n" + "=" * 60)
    print("UPDATING SETTINGS FOR ALL CHARTS")
    print("=" * 60)
    
    if "Histogram Chart" in charts:
        try:
            update_chart_settings(charts["Histogram Chart"], "Histogram Chart")
        except Exception as e:
            print(f"   ✗ Histogram Chart: Error - {e}")
    
    if "Scatter Chart" in charts:
        try:
            update_chart_settings(charts["Scatter Chart"], "Scatter Chart")
        except Exception as e:
            print(f"   ✗ Scatter Chart: Error - {e}")
    
    if "Cumulative Chart" in charts:
        try:
            update_chart_settings(charts["Cumulative Chart"], "Cumulative Chart")
        except Exception as e:
            print(f"   ✗ Cumulative Chart: Error - {e}")
    
    if "Qualitative/Quantitative Chart" in charts:
        try:
            update_chart_settings(charts["Qualitative/Quantitative Chart"], "Qualitative/Quantitative Chart")
        except Exception as e:
            print(f"   ✗ Qualitative/Quantitative Chart: Error - {e}")
    
    if "Joint Spacing Chart" in charts:
        try:
            update_chart_settings(charts["Joint Spacing Chart"], "Joint Spacing Chart")
        except Exception as e:
            print(f"   ✗ Joint Spacing Chart: Error - {e}")
    
    if "Joint Frequency Chart" in charts:
        try:
            update_chart_settings(charts["Joint Frequency Chart"], "Joint Frequency Chart")
        except Exception as e:
            print(f"   ✗ Joint Frequency Chart: Error - {e}")
    
    if "RQD Analysis Chart" in charts:
        try:
            update_chart_settings(charts["RQD Analysis Chart"], "RQD Analysis Chart")
        except Exception as e:
            print(f"   ✗ RQD Analysis Chart: Error - {e}")
    
    if "Kinematic Sensitivity Chart" in charts:
        try:
            update_chart_settings(charts["Kinematic Sensitivity Chart"], "Kinematic Sensitivity Chart")
        except Exception as e:
            print(f"   ✗ Kinematic Sensitivity Chart: Error - {e}")


def close_all_charts(charts):
    """
    Close all chart views to clean up.
    
    Args:
        charts: Dictionary mapping chart names to chart references
    """
    print("\n" + "="*60)
    print("CLOSING ALL CHARTS")
    print("="*60)
    
    if "Histogram Chart" in charts:
        try:
            charts["Histogram Chart"].CloseHistogramChartView()
            print("   ✓ Closed Histogram Chart")
        except Exception as e:
            print(f"   ✗ Error closing Histogram Chart: {e}")
    
    if "Scatter Chart" in charts:
        try:
            charts["Scatter Chart"].CloseScatterChartView()
            print("   ✓ Closed Scatter Chart")
        except Exception as e:
            print(f"   ✗ Error closing Scatter Chart: {e}")
    
    if "Cumulative Chart" in charts:
        try:
            charts["Cumulative Chart"].CloseCumulativeChartView()
            print("   ✓ Closed Cumulative Chart")
        except Exception as e:
            print(f"   ✗ Error closing Cumulative Chart: {e}")
    
    if "Qualitative/Quantitative Chart" in charts:
        try:
            charts["Qualitative/Quantitative Chart"].CloseQualitativeQuantitativeChartView()
            print("   ✓ Closed Qualitative/Quantitative Chart")
        except Exception as e:
            print(f"   ✗ Error closing Qualitative/Quantitative Chart: {e}")
    
    if "Joint Spacing Chart" in charts:
        try:
            charts["Joint Spacing Chart"].CloseJointSpacingChartView()
            print("   ✓ Closed Joint Spacing Chart")
        except Exception as e:
            print(f"   ✗ Error closing Joint Spacing Chart: {e}")
    
    if "Joint Frequency Chart" in charts:
        try:
            charts["Joint Frequency Chart"].CloseJointFrequencyChartView()
            print("   ✓ Closed Joint Frequency Chart")
        except Exception as e:
            print(f"   ✗ Error closing Joint Frequency Chart: {e}")
    
    if "RQD Analysis Chart" in charts:
        try:
            charts["RQD Analysis Chart"].CloseRQDAnalysisChartView()
            print("   ✓ Closed RQD Analysis Chart")
        except Exception as e:
            print(f"   ✗ Error closing RQD Analysis Chart: {e}")
    
    if "Kinematic Sensitivity Chart" in charts:
        try:
            charts["Kinematic Sensitivity Chart"].CloseKinematicSensitivityChartView()
            print("   ✓ Closed Kinematic Sensitivity Chart")
        except Exception as e:
            print(f"   ✗ Error closing Kinematic Sensitivity Chart: {e}")


def main():
    """
    Main entry point for the chart types demonstration.
    
    Creates sample data, demonstrates all chart types, and tests their API methods.
    """
    print("=" * 60)
    print("Dips API Chart Types Demonstration")
    print("=" * 60)
    
    # Connect to Dips
    print("\nConnecting to Dips application...")
    try:
        app = DipsApp.LaunchApp(62535)
        print("Successfully 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()
    
    # Create sample data
    print("\n" + "=" * 60)
    print("CREATING SAMPLE DATA")
    print("=" * 60)
    traverse_ref = create_sample_traverse(model)
    set_ref = create_sample_set(model)
    filter_ref = create_sample_filter(model)
    
    # Create all chart types
    print("\n" + "=" * 60)
    print("CREATING CHARTS")
    print("=" * 60)
    
    charts = {}
    try:
        charts["Histogram Chart"] = create_histogram_chart(model)
        charts["Scatter Chart"] = create_scatter_chart(model)
        charts["Cumulative Chart"] = create_cumulative_chart(model)
        charts["Qualitative/Quantitative Chart"] = create_qualitative_quantitative_chart(model)
        charts["Joint Spacing Chart"] = create_joint_spacing_chart(model, set_ref, traverse_ref)
        charts["Joint Frequency Chart"] = create_joint_frequency_chart(model, traverse_ref)
        charts["RQD Analysis Chart"] = create_rqd_analysis_chart(model, traverse_ref)
        charts["Kinematic Sensitivity Chart"] = create_kinematic_sensitivity_chart(model)
        
        print(f"\nSuccessfully created {len(charts)} charts!")
    except Exception as e:
        print(f"Error creating charts: {e}")
        return
    
    # Set active filter for all charts
    set_active_filter_for_all_charts(charts, filter_ref)
    
    # Set Terzaghi weighting for applicable charts
    set_weighted_for_applicable_charts(charts, is_weighted=True)
    
    # Update settings for all charts
    update_all_chart_settings(charts)
    
    # Show application
    print("\nShowing Dips application...")
    app.Show()
    
    # List all charts
    print("\n" + "=" * 60)
    print("CHART SUMMARY")
    print("=" * 60)
    try:
        print(f"  Histogram Charts:              {len(model.GetHistogramChartViewList())}")
        print(f"  Scatter Charts:                {len(model.GetScatterChartViewList())}")
        print(f"  Cumulative Charts:             {len(model.GetCumulativeChartViewList())}")
        print(f"  Qualitative/Quantitative:      {len(model.GetQualitativeQuantitativeChartViewList())}")
        print(f"  Joint Spacing Charts:          {len(model.GetJointSpacingChartViewList())}")
        print(f"  Joint Frequency Charts:        {len(model.GetJointFrequencyChartViewList())}")
        print(f"  RQD Analysis Charts:           {len(model.GetRQDAnalysisChartViewList())}")
        print(f"  Kinematic Sensitivity Charts:  {len(model.GetKinematicSensitivityChartViewList())}")
    except Exception as e:
        print(f"Error listing charts: {e}")
    
    # Optional: Close all charts (uncomment to test)
    # input("\nPress Enter to close all charts...")
    # close_all_charts(charts)
    
    print("\n" + "=" * 60)
    print("DEMONSTRATION COMPLETE")
    print("=" * 60)
    print("\nAPI methods demonstrated:")
    print("  Data Management:")
    print("    - AddTraverse()")
    print("    - AddSetWindow()")
    print("    - AddDataFilter()")
    print("  Chart Operations:")
    print("    - Create*ChartView()")
    print("    - Get*ChartViewList()")
    print("    - GetValue()")
    print("    - SetActiveDataFilter()")
    print("    - GetActiveDataFilter()")
    print("    - SetIsWeighted() (where supported)")
    print("    - Update*Settings()")
    print("    - Close*ChartView() (commented out)")
    print("\nYou can now interact with the charts in the Dips UI.")


if __name__ == "__main__":
    main()