Option Explicit

'=========================================================================================
' WATERJET_DXF_BATCH_EXPORT_V6_CLEANED
'
' PURPOSE
'   Production-oriented batch DXF export macro for fabrication workflows that rely on
'   cut-list-driven weldment / multi-body part export rules.
'
' WHAT THIS MACRO DOES
'   - Prompts for a folder that contains .SLDPRT and/or .SLDASM files
'   - Processes every qualifying top-level file in that folder
'   - For assemblies, recursively traverses components and subassemblies
'   - Processes each unique PART + CONFIGURATION only once per run
'   - Reads evaluated cut-list property DXF
'   - Exports only cut-list items where DXF = YES
'   - Uses PART NUMBER for the DXF file name
'   - Writes DXFs to <model folder>\WATERJET_DXF
'   - Skips duplicate DXF names instead of auto-suffixing them
'
' ORIENTATION STRATEGY
'   1) Largest planar face establishes export normal
'   2) Tab-aware gusset logic gets first priority
'   3) Shared-corner perpendicular pair logic is next
'   4) Non-intersecting virtual-corner pair logic is next
'   5) Longest linear edge fallback is last
'
' NOTES
'   - This is still a cut-list driven macro. A plain part with only file-level DXF=YES
'     and no cut-list folders is not treated as a qualifying export item.
'   - Fast mode is configured for direct part DXF export from an oriented temp part.
'   - Legacy drawing export remains in the codebase only as a manual recovery path and is not used by default.
'
'=========================================================================================

'------------------------------------------
' SolidWorks app/document globals
'------------------------------------------
Private swApp           As SldWorks.SldWorks
Private swModel         As SldWorks.ModelDoc2
Private swMathUtil      As SldWorks.MathUtility

'------------------------------------------
' Core export rules
'------------------------------------------
' OUT_FOLDER_NAME
'   Name of the per-model output folder created beside the source model.
'
' PROP_DXF / PROP_PARTNO / PROP_TRUE_VALUE
'   Exact cut-list property names and required export value.
'   These are evaluated from the cut-list custom property manager, not the file-level
'   part custom property manager.
'------------------------------------------
Private Const OUT_FOLDER_NAME               As String = "WATERJET_DXF"
Private Const PROP_DXF                      As String = "DXF"
Private Const PROP_PARTNO                   As String = "PART NUMBER"
Private Const PROP_TRUE_VALUE               As String = "YES"

Private Const EPS                           As Double = 0.0000001
Private Const PT_TOL                        As Double = 0.000001
Private Const PERP_DOT_TOL                  As Double = 0.001
Private Const SHEET_MARGIN_M                As Double = 0.05
Private Const MIN_SHEET_W_M                 As Double = 0.3
Private Const MIN_SHEET_H_M                 As Double = 0.2
Private Const TEMP_PREFIX                   As String = "WJDXF_"
Private Const TEMP_VIEW_NAME                As String = "WATERJET_EXPORT_VIEW"
Private Const PI_VAL                        As Double = 3.14159265358979
Private Const CUSTOM_DRAWING_TEMPLATE       As String = "C:\Public\Templates\Blank.drwdot"

'------------------------------------------
' Tab-aware gusset orientation toggles
'------------------------------------------
' ENABLE_TAB_AWARE_GUSSET_MODE
'   True  = Prefer a real manufacturing right-angle corner before legacy pair-length logic.
'   False = Skip the gusset-specific pass and use only the legacy orientation chain.
'
' TAB_AWARE_USER_TAB_MAX_M
'   Informational target for expected tab size. Keep this aligned with real shop parts.
'
' TAB_AWARE_ALLOWANCE_M / TAB_AWARE_AXIS_STRIP_HALF_WIDTH_M
'   Small-feature tolerance band used when deciding whether geometry still belongs to the
'   intended horizontal or vertical leg. This is what helps ignore 0.5-1.0 inch tabs.
'
' TAB_AWARE_MIN_EFFECTIVE_LEG_M
'   Minimum meaningful orthogonal leg length after tab suppression.
'
' TAB_AWARE_MIN_LEG_TO_TAB_RATIO
'   Effective leg must be at least this multiple of the tab allowance before it can win.
'
' TAB_AWARE_PREFER_LONGER_LEG_HORIZONTAL
'   True  = Longer orthogonal leg becomes X / horizontal.
'   False = First accepted orthogonal leg direction is kept as-is.
'------------------------------------------
Private Const ENABLE_TAB_AWARE_GUSSET_MODE              As Boolean = True
Private Const TAB_AWARE_USER_TAB_MAX_M                  As Double = 0.0254      ' 1.00 in
Private Const TAB_AWARE_ALLOWANCE_M                     As Double = 0.03175     ' 1.25 in
Private Const TAB_AWARE_AXIS_STRIP_HALF_WIDTH_M         As Double = 0.03175     ' 1.25 in
Private Const TAB_AWARE_MIN_EFFECTIVE_LEG_M             As Double = 0.0508      ' 2.00 in
Private Const TAB_AWARE_MIN_LEG_TO_TAB_RATIO            As Double = 2#
Private Const TAB_AWARE_PREFER_LONGER_LEG_HORIZONTAL    As Boolean = True


'------------------------------------------
' DXF export mode toggles
'------------------------------------------
' USE_FAST_MODE
'   Master performance toggle.
'   True  = Use the optimized export workflow.
'   False = Use the heavier legacy-safe workflow end-to-end.
'
' FAST_MODE_PRESERVE_LEGACY_OUTPUT
'   True  = Keep legacy-compatible orientation and output behavior while still trimming
'           avoidable overhead.
'   False = Allow fast mode to attempt non-legacy behaviors when other toggles permit it.
'
' FAST_MODE_SINGLE_TEMP_PART_SAVE
'   True  = Avoid extra temp-part save churn where practical.
'
' FAST_MODE_SKIP_TEMP_DRAWING_SAVE
'   True  = Allow DXF save without intentionally keeping a persisted temp drawing file,
'           unless the downstream workflow still requires it.
'
' FAST_MODE_MINIMIZE_EXTRA_REDRAWS
'   True  = Reduce redraw / zoom overhead during export.
'
' USE_EXPERIMENTAL_FAST_DIRECT_DXF
'   True  = Try direct ExportToDWG2 from the temp part.
'   False = Do not attempt the experimental direct path.
'
' EXPERIMENTAL_DIRECT_FALLBACK_TO_DRAWING
'   If the experimental direct path fails, fall back to the proven drawing-based method.
'
' EXPERIMENTAL_DIRECT_ALLOW_CURRENT_VIEW_FALLBACK
'   Last-resort experimental fallback that allows *Current view export.
'   Keep this False for production unless you are specifically testing it.
'
' FORCE_CLOSE_ALL_BATCH_OPENED_DOCS_AT_END
'   Final cleanup safety net for any docs opened by the batch layer.
'------------------------------------------
Private Const USE_FAST_MODE                             As Boolean = True
Private Const FAST_MODE_PRESERVE_LEGACY_OUTPUT         As Boolean = False
Private Const FAST_MODE_SINGLE_TEMP_PART_SAVE          As Boolean = True
Private Const FAST_MODE_SKIP_TEMP_DRAWING_SAVE         As Boolean = True
Private Const FAST_MODE_MINIMIZE_EXTRA_REDRAWS         As Boolean = True

' Direct part export is now the production path in fast mode.
' This uses the oriented temp part + named view + direct ExportToDWG2 workflow.
Private Const USE_EXPERIMENTAL_FAST_DIRECT_DXF         As Boolean = True
Private Const EXPERIMENTAL_DIRECT_FALLBACK_TO_DRAWING  As Boolean = False
Private Const EXPERIMENTAL_DIRECT_ALLOW_CURRENT_VIEW_FALLBACK As Boolean = False

' Force temp-part document units to IPS before direct DXF export so direct output
' matches the legacy inch-based downstream expectation.
Private Const DIRECT_TEMP_PART_FORCE_INCH_UNITS        As Boolean = True

' Skip temporary SolidWorks lock/owner files such as ~$*.SLDPRT during folder scans.
Private Const SKIP_TEMP_OWNER_FILES_IN_BATCH_SCAN      As Boolean = True

Private Const FORCE_CLOSE_ALL_BATCH_OPENED_DOCS_AT_END As Boolean = True

' ExportToDWG2 action values (compile-safe)
Private Const SW_EXPORT_TO_DWG_SELECTED_FACES   As Long = 2
Private Const SW_EXPORT_TO_DWG_ANNOTATION_VIEWS As Long = 3

'------------------------------------------
' Body type constants (compile-safe)
'------------------------------------------
Private Const BODYTYPE_SOLID   As Long = 0
Private Const BODYTYPE_SHEET   As Long = 1
Private Const BODYTYPE_WIRE    As Long = 2
Private Const BODYTYPE_MINIMUM As Long = 3
Private Const BODYTYPE_GENERAL As Long = 4
Private Const BODYTYPE_EMPTY   As Long = 5

'------------------------------------------
' Summary counters
'------------------------------------------
Private gTotalChecked              As Long
Private gTotalEligible             As Long
Private gTotalExported             As Long
Private gTotalSkipped              As Long
Private gTotalFailed               As Long
Private gTotalDuplicateOutputSkipped As Long

Private gTotalComponentInstances   As Long
Private gTotalUniquePartConfigs    As Long
Private gTotalPartDocsProcessed    As Long
Private gTotalAssemblyNodes        As Long
Private gTotalVirtualSkipped       As Long
Private gTotalSuppressedSkipped    As Long

'------------------------------------------
' Batch / run-state toggles
'------------------------------------------
' INCLUDE_SUBFOLDERS
'   True  = file-system recursion into subfolders for top-level model pickup.
'   False = scan only the exact folder the user enters.
'
' OPEN_TOPLEVEL_DOCS_READ_ONLY
'   True  = top-level models open read-only when the batch opens them.
'
' CLOSE_DOCS_OPENED_BY_BATCH
'   True  = close top-level docs opened by this macro after processing.
'
' SKIP_IF_DXF_NAME_ALREADY_EXISTS
'   True  = first DXF name wins; later identical output names are skipped.
'------------------------------------------
Private Const INCLUDE_SUBFOLDERS            As Boolean = False
Private Const OPEN_TOPLEVEL_DOCS_READ_ONLY  As Boolean = True
Private Const CLOSE_DOCS_OPENED_BY_BATCH    As Boolean = True
Private Const SKIP_IF_DXF_NAME_ALREADY_EXISTS As Boolean = True

'------------------------------------------
' Batch / run-state counters
'------------------------------------------
Private gBatchTopLevelFilesFound        As Long
Private gBatchTopLevelFilesProcessed    As Long
Private gBatchTopLevelFilesSkippedDup   As Long
Private gBatchTopLevelFilesFailed       As Long
Private gBatchTopLevelAssemblies        As Long
Private gBatchTopLevelParts             As Long

'------------------------------------------
' Batch / run-state dictionaries & logs
'------------------------------------------
Private gDictPartCfg                    As Object
Private gDictAsmCfg                     As Object
Private gDictDxfOutputPaths             As Object
Private gDictBatchOpenedDocPaths        As Object

Private gResultLines                    As Collection
Private gFailedItems                    As Collection
Private gSkippedItems                   As Collection
Private gSuccessItems                   As Collection

'------------------------------------------
' Current processing context
'------------------------------------------
Private gRunMode                        As String
Private gSelectedRootFolder             As String
Private gCurrentSourcePath              As String
Private gCurrentSourceLabel             As String
Private gCurrentRequestedConfig         As String
Private gCurrentEffectiveConfig         As String
Private gCurrentOutputDir               As String
Private gLastReportPath                 As String


'=========================================================================================
' ENTRY POINT
'=========================================================================================
Public Sub main()

    On Error GoTo EH

    Dim folderPath As String
    Dim defaultFolder As String
    Dim summary As String

    Set swApp = Application.SldWorks
    Set swModel = swApp.ActiveDoc
    Set swMathUtil = swApp.GetMathUtility

    Debug.Print String(120, "=")
    Debug.Print TimeStamp() & " | START | WATERJET_DXF_FROM_WELDMENT_CUTLIST_BATCH_FOLDER_SUPPORT"

    ResetCounters
    ResetRunState

    defaultFolder = GetDefaultFolderFromActiveModelIfAny()

    folderPath = InputBox( _
        "Paste the folder path that contains the PART and/or ASSEMBLY files for DXF export." & vbCrLf & vbCrLf & _
        "Batch behavior:" & vbCrLf & _
        "- The macro will process each .SLDPRT and .SLDASM in that folder" & vbCrLf & _
        "- Only cut-list items with DXF = YES are exported" & vbCrLf & _
        "- Output folder stays as <model folder>\" & OUT_FOLDER_NAME & vbCrLf & _
        "- File naming stays as PART NUMBER.dxf" & vbCrLf & _
        "- If the same DXF name already exists in the target output folder, that item is skipped" & vbCrLf & _
        "- If the same DXF name is encountered again later in the same batch, that item is skipped" & vbCrLf & vbCrLf & _
        "Blank input cancels the batch.", _
        "Waterjet DXF Batch Export - Folder Path", _
        defaultFolder)

    folderPath = CleanUserFolderInput(folderPath)

    If Len(folderPath) = 0 Then
        Debug.Print TimeStamp() & " | INFO  | Blank folder input. Batch cancelled by user."
        Exit Sub
    End If

    swApp.CommandInProgress = True

    gRunMode = "BATCH_FOLDER"
    gSelectedRootFolder = folderPath
    Debug.Print TimeStamp() & " | INFO  | Run mode = BATCH_FOLDER"
    If Not ProcessFolderBatch(folderPath) Then GoTo CleanExit

    summary = BuildRunSummaryText()

    Debug.Print TimeStamp() & " | DONE  | " & Replace(summary, vbCrLf, " | ")
    Debug.Print String(120, "=")

    MsgBox summary, vbInformation, "Waterjet DXF Export Summary"

CleanExit:
    On Error Resume Next
    If FORCE_CLOSE_ALL_BATCH_OPENED_DOCS_AT_END Then
        CloseAllTrackedBatchOpenedDocs
    End If
    swApp.CommandInProgress = False
    On Error GoTo 0
    Exit Sub

EH:
    Debug.Print TimeStamp() & " | ERROR | Unhandled main error: " & Err.Number & " - " & Err.Description
    MsgBox "Macro terminated with an unexpected error:" & vbCrLf & Err.Number & " - " & Err.Description, vbCritical
    Resume CleanExit
End Sub

Private Function BuildRunSummaryText() As String
    Dim s As String

    s = "Waterjet DXF export complete." & vbCrLf & vbCrLf
    s = s & "Run mode: " & gRunMode & vbCrLf
    s = s & "DXF export mode: " & GetDxfExportModeName() & vbCrLf
    s = s & "Use fast mode: " & CStr(USE_FAST_MODE) & vbCrLf
    s = s & "Fast mode preserve legacy output flag (informational only): " & CStr(FAST_MODE_PRESERVE_LEGACY_OUTPUT) & vbCrLf
    s = s & "Direct temp part force inch units: " & CStr(DIRECT_TEMP_PART_FORCE_INCH_UNITS) & vbCrLf
    s = s & "Fast mode single temp part save: " & CStr(FAST_MODE_SINGLE_TEMP_PART_SAVE) & vbCrLf
    s = s & "Fast mode skip temp drawing save: " & CStr(FAST_MODE_SKIP_TEMP_DRAWING_SAVE) & vbCrLf
    s = s & "Direct part DXF enabled: " & CStr(USE_EXPERIMENTAL_FAST_DIRECT_DXF) & vbCrLf
    s = s & "Direct part allow *Current fallback: " & CStr(EXPERIMENTAL_DIRECT_ALLOW_CURRENT_VIEW_FALLBACK) & vbCrLf
    s = s & "Skip temp owner files in batch scan: " & CStr(SKIP_TEMP_OWNER_FILES_IN_BATCH_SCAN) & vbCrLf

    If Len(gSelectedRootFolder) > 0 Then
        s = s & "Root folder: " & gSelectedRootFolder & vbCrLf
    End If

    If gRunMode = "BATCH_FOLDER" Then
        s = s & vbCrLf & _
            "Top-level model files found: " & gBatchTopLevelFilesFound & vbCrLf & _
            "Top-level model files processed: " & gBatchTopLevelFilesProcessed & vbCrLf & _
            "Top-level assemblies processed: " & gBatchTopLevelAssemblies & vbCrLf & _
            "Top-level parts processed: " & gBatchTopLevelParts & vbCrLf & _
            "Top-level duplicates skipped: " & gBatchTopLevelFilesSkippedDup & vbCrLf & _
            "Top-level open/dispatch failures: " & gBatchTopLevelFilesFailed & vbCrLf & vbCrLf
    Else
        s = s & vbCrLf & "Top-level model files processed: " & gBatchTopLevelFilesProcessed & vbCrLf & vbCrLf
    End If

    s = s & _
        "Assembly component instances seen: " & gTotalComponentInstances & vbCrLf & _
        "Assembly/subassembly nodes visited: " & gTotalAssemblyNodes & vbCrLf & _
        "Unique part+config processed: " & gTotalUniquePartConfigs & vbCrLf & _
        "Part docs processed: " & gTotalPartDocsProcessed & vbCrLf & _
        "Suppressed components skipped: " & gTotalSuppressedSkipped & vbCrLf & _
        "Virtual/unloadable components skipped: " & gTotalVirtualSkipped & vbCrLf & vbCrLf & _
        "Cut-list items checked: " & gTotalChecked & vbCrLf & _
        "Eligible (DXF=YES): " & gTotalEligible & vbCrLf & _
        "Exported: " & gTotalExported & vbCrLf & _
        "Skipped: " & gTotalSkipped & vbCrLf & _
        "Duplicate/existing DXF name skips: " & gTotalDuplicateOutputSkipped & vbCrLf & _
        "Failed: " & gTotalFailed

    If Not gFailedItems Is Nothing Then
        If gFailedItems.Count > 0 Then
            s = s & vbCrLf & vbCrLf & _
                "Failed items:" & vbCrLf & _
                BuildItemPreview(gFailedItems, 12)
        End If
    End If

    If Len(gLastReportPath) > 0 Then
        s = s & vbCrLf & vbCrLf & "Detailed report:" & vbCrLf & gLastReportPath
    End If

    BuildRunSummaryText = s
End Function

' Returns a readable label for the current export-mode combination.
Private Function GetDxfExportModeName() As String
    If USE_FAST_MODE Then
        If USE_EXPERIMENTAL_FAST_DIRECT_DXF Then
            If EXPERIMENTAL_DIRECT_FALLBACK_TO_DRAWING Then
                GetDxfExportModeName = "FAST_MODE_DIRECT_PART_EXPORT_WITH_MANUAL_DRAWING_FALLBACK"
            Else
                GetDxfExportModeName = "FAST_MODE_DIRECT_PART_EXPORT_ONLY"
            End If
        Else
            GetDxfExportModeName = "FAST_MODE_DIRECT_PART_EXPORT_DISABLED"
        End If
    Else
        GetDxfExportModeName = "LEGACY_SAFE_MODE"
    End If
End Function

' Central gatekeeper for the direct part-export path.
' User requested this method as the active production route.
Private Function ShouldAttemptFastDirectDxfExport(ByVal preserveCornerOrientation As Boolean) As Boolean
    ShouldAttemptFastDirectDxfExport = False

    If Not USE_FAST_MODE Then Exit Function
    If Not USE_EXPERIMENTAL_FAST_DIRECT_DXF Then Exit Function

    Debug.Print TimeStamp() & " | INFO  | Direct part-export path enabled. preserveCornerOrientation=" & CStr(preserveCornerOrientation)
    ShouldAttemptFastDirectDxfExport = True
End Function


'=========================================================================================
' VALIDATION / SETUP
'=========================================================================================
Private Function ValidateActiveModel(ByVal mdl As SldWorks.ModelDoc2) As Boolean
    On Error GoTo EH

    ValidateActiveModel = False

    If mdl Is Nothing Then
        MsgBox "No active SolidWorks document found.", vbExclamation
        Exit Function
    End If

    If mdl.GetType <> swDocPART And mdl.GetType <> swDocASSEMBLY Then
        MsgBox "Active document must be a saved PART or ASSEMBLY." & vbCrLf & _
               "Open a saved weldment part or an assembly and run again.", vbExclamation
        Exit Function
    End If

    If Len(Trim$(mdl.GetPathName)) = 0 Then
        MsgBox "The active document must be saved before running this macro.", vbExclamation
        Exit Function
    End If

    Debug.Print TimeStamp() & " | INFO  | Active model = " & mdl.GetTitle
    Debug.Print TimeStamp() & " | INFO  | Path         = " & mdl.GetPathName
    Debug.Print TimeStamp() & " | INFO  | Type         = " & DocTypeName(mdl.GetType)

    ValidateActiveModel = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ValidateActiveModel: " & Err.Number & " - " & Err.Description
End Function

Private Function DocTypeName(ByVal docType As Long) As String
    Select Case docType
        Case swDocPART:     DocTypeName = "PART"
        Case swDocASSEMBLY: DocTypeName = "ASSEMBLY"
        Case swDocDRAWING:  DocTypeName = "DRAWING"
        Case Else:          DocTypeName = "UNKNOWN"
    End Select
End Function

Private Sub ResetCounters()
    gTotalChecked = 0
    gTotalEligible = 0
    gTotalExported = 0
    gTotalSkipped = 0
    gTotalFailed = 0
    gTotalDuplicateOutputSkipped = 0

    gTotalComponentInstances = 0
    gTotalUniquePartConfigs = 0
    gTotalPartDocsProcessed = 0
    gTotalAssemblyNodes = 0
    gTotalVirtualSkipped = 0
    gTotalSuppressedSkipped = 0

    gBatchTopLevelFilesFound = 0
    gBatchTopLevelFilesProcessed = 0
    gBatchTopLevelFilesSkippedDup = 0
    gBatchTopLevelFilesFailed = 0
    gBatchTopLevelAssemblies = 0
    gBatchTopLevelParts = 0
End Sub

Private Sub ResetRunState()
    Set gDictPartCfg = CreateObject("Scripting.Dictionary")
    Set gDictAsmCfg = CreateObject("Scripting.Dictionary")
    Set gDictDxfOutputPaths = CreateObject("Scripting.Dictionary")
    Set gDictBatchOpenedDocPaths = CreateObject("Scripting.Dictionary")

    gDictPartCfg.CompareMode = vbTextCompare
    gDictAsmCfg.CompareMode = vbTextCompare
    gDictDxfOutputPaths.CompareMode = vbTextCompare
    gDictBatchOpenedDocPaths.CompareMode = vbTextCompare

    Set gResultLines = New Collection
    Set gFailedItems = New Collection
    Set gSkippedItems = New Collection
    Set gSuccessItems = New Collection

    gRunMode = ""
    gSelectedRootFolder = ""
    gCurrentSourcePath = ""
    gCurrentSourceLabel = ""
    gCurrentRequestedConfig = ""
    gCurrentEffectiveConfig = ""
    gCurrentOutputDir = ""
    gLastReportPath = ""
End Sub

Private Sub EnsureRunStateReady()
    If gDictPartCfg Is Nothing Then
        Set gDictPartCfg = CreateObject("Scripting.Dictionary")
        gDictPartCfg.CompareMode = vbTextCompare
    End If
    If gDictAsmCfg Is Nothing Then
        Set gDictAsmCfg = CreateObject("Scripting.Dictionary")
        gDictAsmCfg.CompareMode = vbTextCompare
    End If
    If gDictDxfOutputPaths Is Nothing Then
        Set gDictDxfOutputPaths = CreateObject("Scripting.Dictionary")
        gDictDxfOutputPaths.CompareMode = vbTextCompare
    End If
    If gDictBatchOpenedDocPaths Is Nothing Then
        Set gDictBatchOpenedDocPaths = CreateObject("Scripting.Dictionary")
        gDictBatchOpenedDocPaths.CompareMode = vbTextCompare
    End If
    If gResultLines Is Nothing Then Set gResultLines = New Collection
    If gFailedItems Is Nothing Then Set gFailedItems = New Collection
    If gSkippedItems Is Nothing Then Set gSkippedItems = New Collection
    If gSuccessItems Is Nothing Then Set gSuccessItems = New Collection
End Sub


'=========================================================================================
' RUN MODE / BATCH HELPERS
'=========================================================================================

Private Function ProcessFolderBatch(ByVal folderPath As String) As Boolean
    On Error GoTo EH

    Dim fileArray() As String
    Dim i As Long

    ProcessFolderBatch = False

    If Not FolderExists(folderPath) Then
        MsgBox "Folder does not exist:" & vbCrLf & folderPath, vbExclamation, "Waterjet DXF Batch Export"
        Exit Function
    End If

    If Not BuildSortedModelArray(folderPath, INCLUDE_SUBFOLDERS, fileArray) Then
        MsgBox "No .SLDPRT or .SLDASM files were found in:" & vbCrLf & folderPath, vbInformation, "Waterjet DXF Batch Export"
        Exit Function
    End If

    gBatchTopLevelFilesFound = UBound(fileArray) - LBound(fileArray) + 1
    Debug.Print TimeStamp() & " | INFO  | Batch files found = " & gBatchTopLevelFilesFound

    For i = LBound(fileArray) To UBound(fileArray)
        Debug.Print String(110, "-")
        Debug.Print TimeStamp() & " | INFO  | BATCH FILE " & i & " OF " & UBound(fileArray)
        Debug.Print TimeStamp() & " | INFO  | Path = " & fileArray(i)
        ProcessOneBatchFileByPath fileArray(i)
    Next i

    gLastReportPath = WriteBatchReport(folderPath)
    ProcessFolderBatch = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ProcessFolderBatch: " & Err.Number & " - " & Err.Description
End Function

Private Sub ProcessOneBatchFileByPath(ByVal filePath As String)
    On Error GoTo EH

    Dim mdl As SldWorks.ModelDoc2
    Dim openedByUs As Boolean

    Set mdl = OpenModelDocumentRobust(filePath, openedByUs)

    If mdl Is Nothing Then
        gBatchTopLevelFilesFailed = gBatchTopLevelFilesFailed + 1
        AddResultLine "FAILED", filePath, GetFileNameFromPath(filePath), "", "", "", "", "", "Could not open top-level model"
        AddCollectionItem gFailedItems, GetFileNameFromPath(filePath) & " | Could not open top-level model"
        Exit Sub
    End If

    Call ProcessOneTopLevelModel(mdl, filePath, openedByUs)

Cleanup:
    On Error Resume Next
    If openedByUs And CLOSE_DOCS_OPENED_BY_BATCH Then
        Debug.Print TimeStamp() & " | INFO  | Closing batch-opened doc: " & filePath
        CloseDocIfOpen filePath
    End If
    On Error GoTo 0
    Exit Sub

EH:
    gBatchTopLevelFilesFailed = gBatchTopLevelFilesFailed + 1
    Debug.Print TimeStamp() & " | ERROR | ProcessOneBatchFileByPath(" & filePath & "): " & Err.Number & " - " & Err.Description
    AddResultLine "FAILED", filePath, GetFileNameFromPath(filePath), "", "", "", "", "", "Batch file runtime error " & Err.Number & ": " & Err.Description
    AddCollectionItem gFailedItems, GetFileNameFromPath(filePath) & " | Runtime error " & Err.Number & ": " & Err.Description
    Resume Cleanup
End Sub

Private Function ProcessOneTopLevelModel(ByVal mdl As SldWorks.ModelDoc2, _
                                         ByVal filePath As String, _
                                         ByVal openedByUs As Boolean) As Boolean
    On Error GoTo EH

    Dim outDir As String
    Dim modelDir As String
    Dim activeCfg As String
    Dim effectivePartCfg As String

    ProcessOneTopLevelModel = False
    EnsureRunStateReady

    If mdl Is Nothing Then Exit Function

    If mdl.GetType <> swDocPART And mdl.GetType <> swDocASSEMBLY Then
        gBatchTopLevelFilesFailed = gBatchTopLevelFilesFailed + 1
        AddResultLine "FAILED", filePath, GetFileNameFromPath(filePath), "", "", "", "", "", "Unsupported top-level document type: " & DocTypeName(mdl.GetType)
        AddCollectionItem gFailedItems, GetFileNameFromPath(filePath) & " | Unsupported doc type"
        Exit Function
    End If

    Dim actErr As Long
    actErr = 0
    swApp.ActivateDoc3 mdl.GetTitle, True, swDontRebuildActiveDoc, actErr

    Set swModel = mdl

    modelDir = GetFolderFromPath(filePath)
    outDir = EnsureFolderExists(modelDir & "\" & OUT_FOLDER_NAME)

    If Len(outDir) = 0 Then
        gBatchTopLevelFilesFailed = gBatchTopLevelFilesFailed + 1
        AddResultLine "FAILED", filePath, GetFileNameFromPath(filePath), "", "", "", "", "", "Failed to create/access output folder"
        AddCollectionItem gFailedItems, GetFileNameFromPath(filePath) & " | Could not create output folder"
        Exit Function
    End If

    activeCfg = GetActiveConfigurationName(mdl)

    Debug.Print TimeStamp() & " | INFO  | Top-level model title = " & mdl.GetTitle
    Debug.Print TimeStamp() & " | INFO  | Top-level model path  = " & filePath
    Debug.Print TimeStamp() & " | INFO  | Top-level model type  = " & DocTypeName(mdl.GetType)
    Debug.Print TimeStamp() & " | INFO  | Top-level active cfg  = " & activeCfg
    Debug.Print TimeStamp() & " | INFO  | Top-level output dir  = " & outDir
    Debug.Print TimeStamp() & " | INFO  | Opened by batch       = " & CStr(openedByUs)

    Select Case mdl.GetType
        Case swDocPART
            effectivePartCfg = ResolveNestingConfiguration(mdl, activeCfg, True)
            If RegisterUniquePartConfig(filePath, effectivePartCfg, mdl.GetTitle) Then
                gBatchTopLevelFilesProcessed = gBatchTopLevelFilesProcessed + 1
                gBatchTopLevelParts = gBatchTopLevelParts + 1
                ProcessPartDocument mdl, outDir, activeCfg, filePath, mdl.GetTitle
                ProcessOneTopLevelModel = True
            Else
                gBatchTopLevelFilesSkippedDup = gBatchTopLevelFilesSkippedDup + 1
                AddResultLine "SKIPPED", filePath, mdl.GetTitle, activeCfg, effectivePartCfg, "", "", "", "Duplicate top-level part+config already processed"
                AddCollectionItem gSkippedItems, GetFileNameFromPath(filePath) & " | Duplicate part+config"
            End If

        Case swDocASSEMBLY
            If RegisterUniqueAssemblyConfig(filePath, activeCfg, mdl.GetTitle) Then
                gBatchTopLevelFilesProcessed = gBatchTopLevelFilesProcessed + 1
                gBatchTopLevelAssemblies = gBatchTopLevelAssemblies + 1
                ProcessAssemblyDocument mdl, outDir
                ProcessOneTopLevelModel = True
            Else
                gBatchTopLevelFilesSkippedDup = gBatchTopLevelFilesSkippedDup + 1
                AddResultLine "SKIPPED", filePath, mdl.GetTitle, activeCfg, activeCfg, "", "", "", "Duplicate top-level assembly+config already processed"
                AddCollectionItem gSkippedItems, GetFileNameFromPath(filePath) & " | Duplicate assembly+config"
            End If
    End Select

    Exit Function

EH:
    gBatchTopLevelFilesFailed = gBatchTopLevelFilesFailed + 1
    Debug.Print TimeStamp() & " | ERROR | ProcessOneTopLevelModel(" & filePath & "): " & Err.Number & " - " & Err.Description
    AddResultLine "FAILED", filePath, GetFileNameFromPath(filePath), "", "", "", "", "", "Top-level dispatch error " & Err.Number & ": " & Err.Description
    AddCollectionItem gFailedItems, GetFileNameFromPath(filePath) & " | Dispatch error " & Err.Number & ": " & Err.Description
End Function

Private Function RegisterUniquePartConfig(ByVal partPath As String, _
                                          ByVal effectiveCfg As String, _
                                          ByVal sourceLabel As String) As Boolean
    On Error GoTo EH

    EnsureRunStateReady

    Dim key As String
    key = MakeCompositeKey(partPath, effectiveCfg)

    If gDictPartCfg.Exists(key) Then
        Debug.Print TimeStamp() & " | SKIP  | Duplicate part+cfg already processed: " & sourceLabel & " | " & partPath & " | " & effectiveCfg
        RegisterUniquePartConfig = False
    Else
        gDictPartCfg.Add key, True
        gTotalUniquePartConfigs = gTotalUniquePartConfigs + 1
        RegisterUniquePartConfig = True
    End If
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | RegisterUniquePartConfig(" & sourceLabel & "): " & Err.Number & " - " & Err.Description
    RegisterUniquePartConfig = False
End Function

Private Function RegisterUniqueAssemblyConfig(ByVal asmPath As String, _
                                              ByVal cfgName As String, _
                                              ByVal sourceLabel As String) As Boolean
    On Error GoTo EH

    EnsureRunStateReady

    Dim key As String
    key = MakeCompositeKey(asmPath, cfgName)

    If gDictAsmCfg.Exists(key) Then
        Debug.Print TimeStamp() & " | SKIP  | Duplicate assembly+cfg already traversed: " & sourceLabel & " | " & asmPath & " | " & cfgName
        RegisterUniqueAssemblyConfig = False
    Else
        gDictAsmCfg.Add key, True
        RegisterUniqueAssemblyConfig = True
    End If
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | RegisterUniqueAssemblyConfig(" & sourceLabel & "): " & Err.Number & " - " & Err.Description
    RegisterUniqueAssemblyConfig = False
End Function

Private Function OpenModelDocumentRobust(ByVal filePath As String, ByRef openedByUs As Boolean) As SldWorks.ModelDoc2
    On Error GoTo EH

    Dim mdl As SldWorks.ModelDoc2
    Dim docType As Long
    Dim openOpts As Long
    Dim errs As Long
    Dim warns As Long

    openedByUs = False

    If Len(Trim$(filePath)) = 0 Then Exit Function
    If Not FileExists(filePath) Then Exit Function

    Set mdl = swApp.GetOpenDocumentByName(filePath)
    If Not mdl Is Nothing Then
        Set OpenModelDocumentRobust = mdl
        Exit Function
    End If

    docType = GetDocTypeFromPath(filePath)
    If docType = 0 Then Exit Function

    openOpts = swOpenDocOptions_Silent
    If OPEN_TOPLEVEL_DOCS_READ_ONLY Then
        openOpts = openOpts Or swOpenDocOptions_ReadOnly
    End If

    Debug.Print TimeStamp() & " | INFO  | Opening top-level model silently: " & filePath
    Set mdl = swApp.OpenDoc6(filePath, docType, openOpts, "", errs, warns)
    Debug.Print TimeStamp() & " | INFO  | OpenDoc6 result errs=" & errs & " warns=" & warns

    If Not mdl Is Nothing Then
        openedByUs = True
        RegisterBatchOpenedDocPath filePath
    End If

    Set OpenModelDocumentRobust = mdl
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | OpenModelDocumentRobust(" & filePath & "): " & Err.Number & " - " & Err.Description
End Function
'=========================================================================================
' PART / ASSEMBLY DISPATCH
'=========================================================================================
Private Sub ProcessPartDocument(ByVal partMdl As SldWorks.ModelDoc2, _
                                ByVal outDir As String, _
                                ByVal targetConfig As String, _
                                ByVal sourcePath As String, _
                                ByVal sourceLabel As String)
    On Error GoTo EH

    If partMdl Is Nothing Then Exit Sub
    If partMdl.GetType <> swDocPART Then Exit Sub

    gTotalPartDocsProcessed = gTotalPartDocsProcessed + 1

    Dim oldCfg As String
    Dim switchedCfg As Boolean
    Dim effectiveConfig As String

    oldCfg = GetActiveConfigurationName(partMdl)
    switchedCfg = False
    effectiveConfig = ResolveNestingConfiguration(partMdl, targetConfig, True)

    gCurrentSourcePath = sourcePath
    gCurrentSourceLabel = sourceLabel
    gCurrentRequestedConfig = targetConfig
    gCurrentEffectiveConfig = effectiveConfig
    gCurrentOutputDir = outDir

    If Len(Trim$(effectiveConfig)) > 0 Then
        If StrComp(oldCfg, effectiveConfig, vbTextCompare) <> 0 Then
            Debug.Print TimeStamp() & " | INFO  | Switching part config from [" & oldCfg & "] to [" & effectiveConfig & "] for " & sourceLabel
            If ActivateModelConfiguration(partMdl, effectiveConfig) Then
                switchedCfg = True
            Else
                Debug.Print TimeStamp() & " | WARN  | Could not switch to configuration [" & effectiveConfig & "] for " & sourceLabel & ". Continuing with active config [" & oldCfg & "]"
            End If
        End If
    End If

    partMdl.ForceRebuild3 True

    Debug.Print String(100, "-")
    Debug.Print TimeStamp() & " | INFO  | PROCESS PART | " & sourceLabel
    Debug.Print TimeStamp() & " | INFO  | Source path  | " & sourcePath
    Debug.Print TimeStamp() & " | INFO  | Requested cfg| " & targetConfig
    Debug.Print TimeStamp() & " | INFO  | Nesting cfg  | " & effectiveConfig
    Debug.Print TimeStamp() & " | INFO  | Active cfg   | " & GetActiveConfigurationName(partMdl)

    If Not UpdateAllCutLists(partMdl) Then
        Debug.Print TimeStamp() & " | WARN  | Cut-list update returned False / partial"
    End If

    ProcessCutLists partMdl, outDir

Cleanup:
    If switchedCfg Then
        Debug.Print TimeStamp() & " | INFO  | Restoring original part config [" & oldCfg & "] for " & sourceLabel
        Call ActivateModelConfiguration(partMdl, oldCfg)
        partMdl.ForceRebuild3 True
    End If
    Exit Sub

EH:
    Debug.Print TimeStamp() & " | ERROR | ProcessPartDocument(" & sourceLabel & "): " & Err.Number & " - " & Err.Description
    Resume Cleanup
End Sub

Private Sub ProcessAssemblyDocument(ByVal assyMdl As SldWorks.ModelDoc2, ByVal outDir As String)
    On Error GoTo EH

    If assyMdl Is Nothing Then Exit Sub
    If assyMdl.GetType <> swDocASSEMBLY Then Exit Sub

    EnsureRunStateReady

    Dim assy As SldWorks.AssemblyDoc
    Set assy = assyMdl

    ResolveAssemblyLightweight assyMdl

    Dim rootComp As SldWorks.Component2
    Set rootComp = GetAssemblyRootComponent(assyMdl)

    If rootComp Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | Could not obtain root component from assembly: " & assyMdl.GetTitle
        AddResultLine "FAILED", assyMdl.GetPathName, assyMdl.GetTitle, GetActiveConfigurationName(assyMdl), GetActiveConfigurationName(assyMdl), "", "", "", "Could not obtain root component from assembly"
        AddCollectionItem gFailedItems, assyMdl.GetTitle & " | Root component acquisition failed"
        Exit Sub
    End If

    Debug.Print TimeStamp() & " | INFO  | Beginning recursive assembly traversal from root: " & rootComp.Name2

    WalkAssemblyTree rootComp, outDir, gDictPartCfg, gDictAsmCfg

    Exit Sub

EH:
    Debug.Print TimeStamp() & " | ERROR | ProcessAssemblyDocument: " & Err.Number & " - " & Err.Description
    AddResultLine "FAILED", assyMdl.GetPathName, assyMdl.GetTitle, GetActiveConfigurationName(assyMdl), GetActiveConfigurationName(assyMdl), "", "", "", "ProcessAssemblyDocument error " & Err.Number & ": " & Err.Description
    AddCollectionItem gFailedItems, assyMdl.GetTitle & " | ProcessAssemblyDocument error " & Err.Number & ": " & Err.Description
End Sub

Private Sub ResolveAssemblyLightweight(ByVal assyMdl As SldWorks.ModelDoc2)
    On Error GoTo EH

    Dim assy As SldWorks.AssemblyDoc
    Set assy = assyMdl

    Debug.Print TimeStamp() & " | INFO  | Attempting to resolve lightweight assembly components"

    On Error Resume Next
    assy.ResolveAllLightWeightComponents True
    Err.Clear
    On Error GoTo EH

    assyMdl.ForceRebuild3 True
    Exit Sub

EH:
    Debug.Print TimeStamp() & " | WARN  | ResolveAssemblyLightweight: " & Err.Number & " - " & Err.Description
End Sub

Private Function GetAssemblyRootComponent(ByVal assyMdl As SldWorks.ModelDoc2) As SldWorks.Component2
    On Error GoTo EH

    Dim cfg As SldWorks.Configuration
    Set cfg = assyMdl.ConfigurationManager.ActiveConfiguration
    If cfg Is Nothing Then Exit Function

    Set GetAssemblyRootComponent = cfg.GetRootComponent3(True)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetAssemblyRootComponent: " & Err.Number & " - " & Err.Description
End Function

Private Sub WalkAssemblyTree(ByVal parentComp As SldWorks.Component2, _
                             ByVal outDir As String, _
                             ByVal dictPartCfg As Object, _
                             ByVal dictAsmCfg As Object)
    On Error GoTo EH

    If parentComp Is Nothing Then Exit Sub

    gTotalAssemblyNodes = gTotalAssemblyNodes + 1

    Dim vChildren As Variant
    vChildren = parentComp.GetChildren

    If IsEmpty(vChildren) Then Exit Sub
    If Not IsArray(vChildren) Then Exit Sub

    Dim i As Long
    For i = LBound(vChildren) To UBound(vChildren)

        Dim child As SldWorks.Component2
        Set child = vChildren(i)

        gTotalComponentInstances = gTotalComponentInstances + 1

        If child Is Nothing Then
            Debug.Print TimeStamp() & " | WARN  | Child component is Nothing"
            GoTo NextChild
        End If

        If IsComponentSuppressedSafe(child) Then
            gTotalSuppressedSkipped = gTotalSuppressedSkipped + 1
            Debug.Print TimeStamp() & " | SKIP  | Suppressed component: " & child.Name2
            GoTo NextChild
        End If

        Dim openedByUs As Boolean
        Dim compMdl As SldWorks.ModelDoc2
        Set compMdl = EnsureComponentModelLoaded(child, openedByUs)

        If compMdl Is Nothing Then
            gTotalVirtualSkipped = gTotalVirtualSkipped + 1
            Debug.Print TimeStamp() & " | SKIP  | Could not load component model: " & child.Name2
            GoTo NextChild
        End If

        Dim compPath As String
        Dim compCfg As String
        Dim effectivePartCfg As String
        Dim uniqueKey As String

        compPath = GetComponentBestPath(child, compMdl)
        compCfg = Trim$(child.ReferencedConfiguration)
        If Len(compCfg) = 0 Then compCfg = GetActiveConfigurationName(compMdl)

        If compMdl.GetType = swDocPART Then
            effectivePartCfg = ResolveNestingConfiguration(compMdl, compCfg, False)

            If RegisterUniquePartConfig(compPath, effectivePartCfg, child.Name2) Then
                Debug.Print String(90, "-")
                Debug.Print TimeStamp() & " | INFO  | UNIQUE PART+CFG | " & child.Name2
                Debug.Print TimeStamp() & " | INFO  | Part path       | " & compPath
                Debug.Print TimeStamp() & " | INFO  | Ref config      | " & compCfg
                Debug.Print TimeStamp() & " | INFO  | Nesting config  | " & effectivePartCfg

                ProcessPartDocument compMdl, outDir, compCfg, compPath, child.Name2
            Else
                AddResultLine "SKIPPED", compPath, child.Name2, compCfg, effectivePartCfg, "", "", "", "Duplicate part+config already processed through batch/assembly traversal"
            End If

        ElseIf compMdl.GetType = swDocASSEMBLY Then

            If RegisterUniqueAssemblyConfig(compPath, compCfg, child.Name2) Then
                Debug.Print String(90, "-")
                Debug.Print TimeStamp() & " | INFO  | ENTER SUBASM    | " & child.Name2
                Debug.Print TimeStamp() & " | INFO  | Asm path        | " & compPath
                Debug.Print TimeStamp() & " | INFO  | Ref config      | " & compCfg

                WalkAssemblyTree child, outDir, dictPartCfg, dictAsmCfg
            Else
                AddResultLine "SKIPPED", compPath, child.Name2, compCfg, compCfg, "", "", "", "Duplicate subassembly+config already traversed"
            End If

        Else
            Debug.Print TimeStamp() & " | SKIP  | Unsupported component doc type: " & child.Name2 & " | " & DocTypeName(compMdl.GetType)
        End If

        If openedByUs Then
            CloseDocIfOpen compPath
        End If

NextChild:
    Next i

    Exit Sub

EH:
    Debug.Print TimeStamp() & " | ERROR | WalkAssemblyTree(" & parentComp.Name2 & "): " & Err.Number & " - " & Err.Description
End Sub

Private Function EnsureComponentModelLoaded(ByVal comp As SldWorks.Component2, ByRef openedByUs As Boolean) As SldWorks.ModelDoc2
    On Error GoTo EH

    openedByUs = False

    If comp Is Nothing Then Exit Function

    Dim mdl As SldWorks.ModelDoc2
    Set mdl = comp.GetModelDoc2
    If Not mdl Is Nothing Then
        Set EnsureComponentModelLoaded = mdl
        Exit Function
    End If

    Dim path As String
    path = Trim$(comp.GetPathName)
    If Len(path) = 0 Then
        Debug.Print TimeStamp() & " | WARN  | Component path is blank (likely virtual/unresolved): " & comp.Name2
        Exit Function
    End If

    Dim alreadyOpen As Boolean
    alreadyOpen = Not (swApp.GetOpenDocumentByName(path) Is Nothing)

    Dim docType As Long
    docType = GetDocTypeFromPath(path)
    If docType = 0 Then
        Debug.Print TimeStamp() & " | WARN  | Unsupported component file type: " & path
        Exit Function
    End If

    Dim errs As Long, warns As Long
    errs = 0: warns = 0

    Debug.Print TimeStamp() & " | INFO  | Opening component silently: " & path

    Set mdl = swApp.OpenDoc6(path, docType, swOpenDocOptions_Silent Or swOpenDocOptions_ReadOnly, "", errs, warns)

    If mdl Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | OpenDoc6 failed. errs=" & errs & " warns=" & warns & " | " & path
        Exit Function
    End If

    If Not alreadyOpen Then
        openedByUs = True
        RegisterBatchOpenedDocPath path
    End If

    Set EnsureComponentModelLoaded = mdl
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | EnsureComponentModelLoaded(" & comp.Name2 & "): " & Err.Number & " - " & Err.Description
End Function

Private Function GetDocTypeFromPath(ByVal filePath As String) As Long
    Dim ext As String
    ext = LCase$(GetExtensionFromPath(filePath))

    Select Case ext
        Case "sldprt": GetDocTypeFromPath = swDocPART
        Case "sldasm": GetDocTypeFromPath = swDocASSEMBLY
        Case "slddrw": GetDocTypeFromPath = swDocDRAWING
        Case Else:     GetDocTypeFromPath = 0
    End Select
End Function

Private Function GetExtensionFromPath(ByVal filePath As String) As String
    Dim p As Long
    p = InStrRev(filePath, ".")
    If p > 0 Then
        GetExtensionFromPath = Mid$(filePath, p + 1)
    Else
        GetExtensionFromPath = ""
    End If
End Function

Private Function GetComponentBestPath(ByVal comp As SldWorks.Component2, ByVal mdl As SldWorks.ModelDoc2) As String
    On Error GoTo EH

    Dim s As String
    s = Trim$(comp.GetPathName)

    If Len(s) > 0 Then
        GetComponentBestPath = s
        Exit Function
    End If

    If Not mdl Is Nothing Then
        s = Trim$(mdl.GetPathName)
        If Len(s) > 0 Then
            GetComponentBestPath = s
            Exit Function
        End If

        GetComponentBestPath = "[VIRTUAL] " & mdl.GetTitle
        Exit Function
    End If

    GetComponentBestPath = "[UNKNOWN_COMPONENT]"
    Exit Function

EH:
    GetComponentBestPath = "[UNKNOWN_COMPONENT]"
End Function

Private Function IsComponentSuppressedSafe(ByVal comp As SldWorks.Component2) As Boolean
    On Error GoTo EH

    IsComponentSuppressedSafe = False
    If comp Is Nothing Then Exit Function

    On Error Resume Next
    IsComponentSuppressedSafe = CBool(comp.IsSuppressed)
    If Err.Number <> 0 Then
        Err.Clear
        IsComponentSuppressedSafe = False
    End If
    On Error GoTo EH
    Exit Function

EH:
    IsComponentSuppressedSafe = False
End Function

Private Function GetActiveConfigurationName(ByVal mdl As SldWorks.ModelDoc2) As String
    On Error GoTo EH

    GetActiveConfigurationName = ""

    If mdl Is Nothing Then Exit Function
    If mdl.ConfigurationManager Is Nothing Then Exit Function
    If mdl.ConfigurationManager.ActiveConfiguration Is Nothing Then Exit Function

    GetActiveConfigurationName = Trim$(mdl.ConfigurationManager.ActiveConfiguration.Name)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetActiveConfigurationName: " & Err.Number & " - " & Err.Description
End Function


Private Function FindPreferredWaterjetConfiguration(ByVal mdl As SldWorks.ModelDoc2) As String
    On Error GoTo EH

    FindPreferredWaterjetConfiguration = ""

    If mdl Is Nothing Then Exit Function

    Dim vCfgNames As Variant
    vCfgNames = mdl.GetConfigurationNames

    If IsEmpty(vCfgNames) Then Exit Function
    If Not IsArray(vCfgNames) Then Exit Function

    Dim i As Long
    Dim cfgName As String

    For i = LBound(vCfgNames) To UBound(vCfgNames)
        cfgName = Trim$(CStr(vCfgNames(i)))
        If Len(cfgName) > 0 Then
            If StrComp(cfgName, "WATERJET_CUT", vbTextCompare) = 0 Then
                FindPreferredWaterjetConfiguration = CStr(vCfgNames(i))
                Exit Function
            End If
        End If
    Next i

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | FindPreferredWaterjetConfiguration: " & Err.Number & " - " & Err.Description
End Function

Private Function ResolveNestingConfiguration(ByVal mdl As SldWorks.ModelDoc2, _
                                             ByVal requestedConfig As String, _
                                             Optional ByVal writeLog As Boolean = False) As String
    On Error GoTo EH

    ResolveNestingConfiguration = ""

    If mdl Is Nothing Then Exit Function

    Dim preferredCfg As String
    Dim fallbackCfg As String

    preferredCfg = Trim$(FindPreferredWaterjetConfiguration(mdl))
    fallbackCfg = Trim$(requestedConfig)

    If Len(fallbackCfg) = 0 Then
        fallbackCfg = GetActiveConfigurationName(mdl)
    End If

    If Len(preferredCfg) > 0 Then
        ResolveNestingConfiguration = preferredCfg
        If writeLog Then
            Debug.Print TimeStamp() & " | INFO  | Nesting config override detected: using [" & preferredCfg & "] instead of requested [" & fallbackCfg & "]"
        End If
    Else
        ResolveNestingConfiguration = fallbackCfg
        If writeLog Then
            Debug.Print TimeStamp() & " | INFO  | No WATERJET_CUT config found. Using requested/active config [" & ResolveNestingConfiguration & "]"
        End If
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ResolveNestingConfiguration: " & Err.Number & " - " & Err.Description
End Function

Private Function ActivateModelConfiguration(ByVal mdl As SldWorks.ModelDoc2, ByVal cfgName As String) As Boolean
    On Error GoTo EH

    ActivateModelConfiguration = False

    If mdl Is Nothing Then Exit Function
    If Len(Trim$(cfgName)) = 0 Then Exit Function

    Dim ok As Boolean
    ok = mdl.ShowConfiguration2(cfgName)

    If ok Then
        mdl.ForceRebuild3 True
        mdl.GraphicsRedraw2
        ActivateModelConfiguration = True
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ActivateModelConfiguration(" & cfgName & "): " & Err.Number & " - " & Err.Description
End Function

'=========================================================================================
' CUT-LIST TRAVERSAL
'=========================================================================================
Private Sub ProcessCutLists(ByVal mdl As SldWorks.ModelDoc2, ByVal outDir As String)
    On Error GoTo EH

    Dim feat As SldWorks.Feature
    Set feat = mdl.FirstFeature

    Dim foundAnyCutList As Boolean
    foundAnyCutList = False

    Do While Not feat Is Nothing
        If IsRealCutListFolderFeature(feat) Then
            foundAnyCutList = True
            ProcessSingleCutList feat, outDir
        End If
        Set feat = feat.GetNextFeature
    Loop

    If Not foundAnyCutList Then
        Debug.Print TimeStamp() & " | WARN  | No real cut-list folders found in part: " & mdl.GetTitle
    End If

    Exit Sub

EH:
    Debug.Print TimeStamp() & " | ERROR | ProcessCutLists: " & Err.Number & " - " & Err.Description
End Sub

Private Function UpdateAllCutLists(ByVal mdl As SldWorks.ModelDoc2) As Boolean
    On Error GoTo EH

    UpdateAllCutLists = False

    Debug.Print TimeStamp() & " | INFO  | Rebuilding model before cut-list update"
    mdl.ForceRebuild3 True

    Dim feat As SldWorks.Feature
    Set feat = mdl.FirstFeature

    Dim updatedAny As Boolean
    updatedAny = False

    Do While Not feat Is Nothing
        Dim typeName As String
        typeName = feat.GetTypeName2

        If StrComp(typeName, "SolidBodyFolder", vbTextCompare) = 0 _
        Or StrComp(typeName, "CutListFolder", vbTextCompare) = 0 _
        Or StrComp(typeName, "SubWeldFolder", vbTextCompare) = 0 Then

            Dim bf As Object
            Set bf = Nothing

            On Error Resume Next
            Set bf = feat.GetSpecificFeature2
            On Error GoTo EH

            If Not bf Is Nothing Then
                Dim vRet As Variant
                On Error Resume Next
                vRet = bf.UpdateCutList
                On Error GoTo EH

                Debug.Print TimeStamp() & " | INFO  | UpdateCutList on feature [" & feat.Name & "] => " & CStr(vRet)

                If Not IsEmpty(vRet) Then
                    If CBool(vRet) Then updatedAny = True
                End If
            End If
        End If

        Set feat = feat.GetNextFeature
    Loop

    mdl.ForceRebuild3 True
    UpdateAllCutLists = True

    If Not updatedAny Then
        Debug.Print TimeStamp() & " | WARN  | No body folders explicitly updated; rebuild still performed"
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | UpdateAllCutLists: " & Err.Number & " - " & Err.Description
End Function

Private Function IsRealCutListFolderFeature(ByVal feat As SldWorks.Feature) As Boolean
    On Error GoTo EH

    IsRealCutListFolderFeature = False

    If feat Is Nothing Then Exit Function
    If StrComp(feat.GetTypeName2, "CutListFolder", vbTextCompare) <> 0 Then Exit Function

    Dim bf As Object
    Set bf = feat.GetSpecificFeature2
    If bf Is Nothing Then Exit Function

    Dim bodyCount As Long
    bodyCount = 0

    On Error Resume Next
    bodyCount = CLng(bf.GetBodyCount)
    On Error GoTo EH

    Debug.Print TimeStamp() & " | INFO  | Cut-list [" & feat.Name & "] body count = " & bodyCount

    If bodyCount > 0 Then
        IsRealCutListFolderFeature = True
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | IsRealCutListFolderFeature(" & feat.Name & "): " & Err.Number & " - " & Err.Description
End Function

Private Sub ProcessSingleCutList(ByVal cutFeat As SldWorks.Feature, ByVal outDir As String)
    On Error GoTo EH

    Dim resultDetail As String
    Dim outputPath As String
    Dim reportedPartNo As String
    Dim orientationMode As String

    gTotalChecked = gTotalChecked + 1
    resultDetail = ""
    outputPath = ""
    reportedPartNo = ""
    orientationMode = ""

    Debug.Print String(80, "-")
    Debug.Print TimeStamp() & " | INFO  | Processing cut-list feature: " & cutFeat.Name

    Dim cpMgr As SldWorks.CustomPropertyManager
    Set cpMgr = cutFeat.CustomPropertyManager

    Dim dxfRaw As String
    Dim dxfResolved As String
    Dim partNoRaw As String
    Dim partNoResolved As String

    dxfResolved = GetEvaluatedCutListProperty(cpMgr, PROP_DXF, dxfRaw)
    partNoResolved = GetEvaluatedCutListProperty(cpMgr, PROP_PARTNO, partNoRaw)
    reportedPartNo = partNoResolved

    Debug.Print TimeStamp() & " | INFO  | PROP " & PROP_DXF & " raw=[" & dxfRaw & "] eval=[" & dxfResolved & "]"
    Debug.Print TimeStamp() & " | INFO  | PROP " & PROP_PARTNO & " raw=[" & partNoRaw & "] eval=[" & partNoResolved & "]"

    If UCase$(Trim$(dxfResolved)) <> PROP_TRUE_VALUE Then
        gTotalSkipped = gTotalSkipped + 1
        resultDetail = "DXF property is not YES"
        Debug.Print TimeStamp() & " | SKIP  | " & resultDetail
        AddResultLine "SKIPPED", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, "", resultDetail
        AddCollectionItem gSkippedItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | " & resultDetail
        Exit Sub
    End If

    gTotalEligible = gTotalEligible + 1

    Dim repBody As SldWorks.Body2
    Set repBody = GetRepresentativeBodyFromCutList(cutFeat)

    If repBody Is Nothing Then
        gTotalFailed = gTotalFailed + 1
        resultDetail = "No valid representative body found"
        Debug.Print TimeStamp() & " | FAIL  | " & resultDetail
        AddResultLine "FAILED", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, "", resultDetail
        AddCollectionItem gFailedItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | " & resultDetail
        Exit Sub
    End If

    Dim refFace As SldWorks.Face2
    Set refFace = FindLargestPlanarFace(repBody)

    If refFace Is Nothing Then
        gTotalFailed = gTotalFailed + 1
        resultDetail = "No planar face found on representative body"
        Debug.Print TimeStamp() & " | FAIL  | " & resultDetail
        AddResultLine "FAILED", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, "", resultDetail
        AddCollectionItem gFailedItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | " & resultDetail
        Exit Sub
    End If

    Dim faceN(2) As Double
    If Not GetStableFaceNormal(refFace, faceN) Then
        gTotalFailed = gTotalFailed + 1
        resultDetail = "Could not obtain face normal"
        Debug.Print TimeStamp() & " | FAIL  | " & resultDetail
        AddResultLine "FAILED", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, "", resultDetail
        AddCollectionItem gFailedItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | " & resultDetail
        Exit Sub
    End If

    Dim xDir(2) As Double
    Dim usedFallbackEdge As Boolean
    Dim usedPerpCorner As Boolean

    If Not FindHorizontalDirectionFromFace(refFace, faceN, xDir, usedFallbackEdge, usedPerpCorner) Then
        gTotalFailed = gTotalFailed + 1
        resultDetail = "Could not determine horizontal direction"
        Debug.Print TimeStamp() & " | FAIL  | " & resultDetail
        AddResultLine "FAILED", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, "", resultDetail
        AddCollectionItem gFailedItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | " & resultDetail
        Exit Sub
    End If

    If usedPerpCorner Then
        orientationMode = "perpendicular edge pair bottom-left"
        Debug.Print TimeStamp() & " | INFO  | Orientation mode = perpendicular edge pair -> shared or virtual corner driven bottom-left"
    ElseIf usedFallbackEdge Then
        orientationMode = "fallback axis"
        Debug.Print TimeStamp() & " | WARN  | Orientation mode = fallback axis (no usable linear edge found)"
    Else
        orientationMode = "longest single linear edge"
        Debug.Print TimeStamp() & " | INFO  | Orientation mode = longest single linear edge horizontal fallback"
    End If

    Dim baseName As String
    baseName = Trim$(partNoResolved)
    If Len(baseName) = 0 Then
        baseName = cutFeat.Name
        reportedPartNo = cutFeat.Name
        Debug.Print TimeStamp() & " | WARN  | PART NUMBER missing/blank, using cut-list feature name fallback"
    End If

    baseName = SanitizeFileName(baseName)
    If Len(baseName) = 0 Then
        baseName = "CUTLIST"
        reportedPartNo = "CUTLIST"
    End If

    outputPath = BuildExactOutputPath(outDir, baseName, "dxf")

    If Not TryReserveDxfOutputPath(outputPath, resultDetail) Then
        gTotalSkipped = gTotalSkipped + 1
        gTotalDuplicateOutputSkipped = gTotalDuplicateOutputSkipped + 1
        Debug.Print TimeStamp() & " | SKIP  | " & resultDetail & " => " & outputPath
        AddResultLine "SKIPPED", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, outputPath, resultDetail
        AddCollectionItem gSkippedItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | " & resultDetail
        Exit Sub
    End If

    If ExportBodyAsOrientedDxf(repBody, faceN, xDir, outputPath, cutFeat.Name, usedPerpCorner) Then
        gTotalExported = gTotalExported + 1
        resultDetail = "Exported successfully | orientation=" & orientationMode
        Debug.Print TimeStamp() & " | OK    | Exported => " & outputPath
        AddResultLine "SUCCESS", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, outputPath, resultDetail
        AddCollectionItem gSuccessItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | " & outputPath
    Else
        ReleaseReservedDxfOutputPath outputPath
        gTotalFailed = gTotalFailed + 1
        resultDetail = "Export failed | orientation=" & orientationMode
        Debug.Print TimeStamp() & " | FAIL  | Export failed => " & outputPath
        AddResultLine "FAILED", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, outputPath, resultDetail
        AddCollectionItem gFailedItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | Export failed"
    End If

    Exit Sub

EH:
    gTotalFailed = gTotalFailed + 1
    resultDetail = "Runtime error " & Err.Number & ": " & Err.Description
    Debug.Print TimeStamp() & " | ERROR | ProcessSingleCutList(" & cutFeat.Name & "): " & Err.Number & " - " & Err.Description
    AddResultLine "FAILED", gCurrentSourcePath, gCurrentSourceLabel, gCurrentRequestedConfig, gCurrentEffectiveConfig, cutFeat.Name, reportedPartNo, outputPath, resultDetail
    AddCollectionItem gFailedItems, GetFileNameFromPath(gCurrentSourcePath) & " | " & cutFeat.Name & " | " & resultDetail
End Sub

Private Function GetRepresentativeBodyFromCutList(ByVal cutFeat As SldWorks.Feature) As SldWorks.Body2
    On Error GoTo EH

    Dim bf As Object
    Set bf = cutFeat.GetSpecificFeature2

    If bf Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | GetSpecificFeature2 returned Nothing for cut-list [" & cutFeat.Name & "]"
        Exit Function
    End If

    Dim bodyCount As Long
    bodyCount = 0

    On Error Resume Next
    bodyCount = CLng(bf.GetBodyCount)
    On Error GoTo EH

    Debug.Print TimeStamp() & " | INFO  | [" & cutFeat.Name & "] GetBodyCount = " & bodyCount

    If bodyCount <= 0 Then
        Debug.Print TimeStamp() & " | FAIL  | [" & cutFeat.Name & "] has no bodies"
        Exit Function
    End If

    Dim vBodies As Variant
    vBodies = bf.GetBodies

    If IsEmpty(vBodies) Then
        Debug.Print TimeStamp() & " | FAIL  | [" & cutFeat.Name & "] GetBodies returned Empty"
        Exit Function
    End If

    If Not IsArray(vBodies) Then
        Debug.Print TimeStamp() & " | FAIL  | [" & cutFeat.Name & "] GetBodies did not return an array"
        Exit Function
    End If

    Dim i As Long
    Dim b As SldWorks.Body2
    Dim bt As Long

    For i = LBound(vBodies) To UBound(vBodies)
        Set b = Nothing
        On Error Resume Next
        Set b = vBodies(i)
        On Error GoTo EH

        If Not b Is Nothing Then
            bt = SafeGetBodyType(b)
            Debug.Print TimeStamp() & " | INFO  | [" & cutFeat.Name & "] Body(" & i & ") type = " & bt & " [" & BodyTypeName(bt) & "]"

            If bt = BODYTYPE_SOLID Then
                Set GetRepresentativeBodyFromCutList = b
                Debug.Print TimeStamp() & " | INFO  | [" & cutFeat.Name & "] representative = SOLID body index " & i
                Exit Function
            End If
        Else
            Debug.Print TimeStamp() & " | WARN  | [" & cutFeat.Name & "] Body(" & i & ") is Nothing"
        End If
    Next i

    For i = LBound(vBodies) To UBound(vBodies)
        Set b = Nothing
        On Error Resume Next
        Set b = vBodies(i)
        On Error GoTo EH

        If Not b Is Nothing Then
            bt = SafeGetBodyType(b)

            If bt = BODYTYPE_SHEET Or bt = BODYTYPE_GENERAL Then
                Set GetRepresentativeBodyFromCutList = b
                Debug.Print TimeStamp() & " | INFO  | [" & cutFeat.Name & "] representative = fallback body index " & i & " [" & BodyTypeName(bt) & "]"
                Exit Function
            End If
        End If
    Next i

    Debug.Print TimeStamp() & " | FAIL  | [" & cutFeat.Name & "] no usable representative body found"
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetRepresentativeBodyFromCutList(" & cutFeat.Name & "): " & Err.Number & " - " & Err.Description
End Function

Private Function SafeGetBodyType(ByVal body As SldWorks.Body2) As Long
    On Error GoTo EH
    SafeGetBodyType = body.GetType
    Exit Function
EH:
    Debug.Print TimeStamp() & " | ERROR | SafeGetBodyType: " & Err.Number & " - " & Err.Description
    SafeGetBodyType = -999
End Function

Private Function BodyTypeName(ByVal bodyType As Long) As String
    Select Case bodyType
        Case BODYTYPE_SOLID:   BodyTypeName = "SOLID"
        Case BODYTYPE_SHEET:   BodyTypeName = "SHEET"
        Case BODYTYPE_WIRE:    BodyTypeName = "WIRE"
        Case BODYTYPE_MINIMUM: BodyTypeName = "MINIMUM"
        Case BODYTYPE_GENERAL: BodyTypeName = "GENERAL"
        Case BODYTYPE_EMPTY:   BodyTypeName = "EMPTY"
        Case Else:             BodyTypeName = "UNKNOWN"
    End Select
End Function

'=========================================================================================
' PROPERTY HELPERS
'=========================================================================================
Private Function GetEvaluatedCutListProperty(ByVal cpMgr As SldWorks.CustomPropertyManager, _
                                             ByVal propName As String, _
                                             ByRef rawValOut As String) As String
    On Error GoTo EH

    rawValOut = ""

    If cpMgr Is Nothing Then Exit Function

    Dim rawVal As String
    Dim evalVal As String
    Dim wasResolved As Boolean
    Dim isLinked As Boolean
    Dim ret As Long

    rawVal = ""
    evalVal = ""
    wasResolved = False
    isLinked = False

    ret = cpMgr.Get6(propName, False, rawVal, evalVal, wasResolved, isLinked)

    rawValOut = NzStr(rawVal)

    If Len(Trim$(evalVal)) > 0 Then
        GetEvaluatedCutListProperty = Trim$(evalVal)
    Else
        GetEvaluatedCutListProperty = Trim$(rawVal)
    End If

    Exit Function
EH:
    Debug.Print TimeStamp() & " | ERROR | GetEvaluatedCutListProperty(" & propName & "): " & Err.Number & " - " & Err.Description
End Function

'=========================================================================================
' GEOMETRY / ORIENTATION
'=========================================================================================
Private Function FindLargestPlanarFace(ByVal body As SldWorks.Body2) As SldWorks.Face2
    On Error GoTo EH

    Dim vFaces As Variant
    vFaces = body.GetFaces

    If IsEmpty(vFaces) Then Exit Function
    If Not IsArray(vFaces) Then Exit Function

    Dim i As Long
    Dim bestArea As Double
    bestArea = -1#

    For i = LBound(vFaces) To UBound(vFaces)
        Dim f As SldWorks.Face2
        Set f = vFaces(i)

        If Not f Is Nothing Then
            Dim surf As SldWorks.Surface
            Set surf = f.GetSurface

            If Not surf Is Nothing Then
                If surf.IsPlane Then
                    Dim a As Double
                    a = 0#
                    On Error Resume Next
                    a = f.GetArea
                    On Error GoTo EH

                    If a > bestArea Then
                        bestArea = a
                        Set FindLargestPlanarFace = f
                    End If
                End If
            End If
        End If
    Next i

    Debug.Print TimeStamp() & " | INFO  | Largest planar face area = " & FormatNumber(bestArea, 8)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | FindLargestPlanarFace: " & Err.Number & " - " & Err.Description
End Function

Private Function GetStableFaceNormal(ByVal face As SldWorks.Face2, ByRef n() As Double) As Boolean
    On Error GoTo EH

    GetStableFaceNormal = False

    Debug.Print TimeStamp() & " | INFO  | GetStableFaceNormal: start"

    If face Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | GetStableFaceNormal: face is Nothing"
        Exit Function
    End If

    If TryGetNormalFromFaceVertices(face, n) Then
        NormalizeVec n
        If VecLength(n) > EPS Then
            StabilizeVectorSign n
            Debug.Print TimeStamp() & " | INFO  | Face normal from face vertices = (" & Dbl3ToStr(n) & ")"
            GetStableFaceNormal = True
            Exit Function
        End If
    End If

    Debug.Print TimeStamp() & " | WARN  | Face vertex normal method failed, trying Face2.Normal"

    Dim vNorm As Variant
    On Error Resume Next
    vNorm = face.Normal
    On Error GoTo EH

    If VariantHas3Numbers(vNorm) Then
        n(0) = CDbl(vNorm(0))
        n(1) = CDbl(vNorm(1))
        n(2) = CDbl(vNorm(2))
        NormalizeVec n
        If VecLength(n) > EPS Then
            StabilizeVectorSign n
            Debug.Print TimeStamp() & " | INFO  | Face normal from Face2.Normal = (" & Dbl3ToStr(n) & ")"
            GetStableFaceNormal = True
            Exit Function
        End If
    Else
        Debug.Print TimeStamp() & " | WARN  | Face2.Normal did not return usable data"
    End If

    Debug.Print TimeStamp() & " | WARN  | Face2.Normal failed, trying Surface.PlaneParams"

    Dim surf As SldWorks.Surface
    Set surf = face.GetSurface

    If Not surf Is Nothing Then
        Dim planeProps As Variant
        On Error Resume Next
        planeProps = surf.PlaneParams
        On Error GoTo EH

        If VariantHasAtLeast6Numbers(planeProps) Then
            n(0) = CDbl(planeProps(3))
            n(1) = CDbl(planeProps(4))
            n(2) = CDbl(planeProps(5))
            NormalizeVec n
            If VecLength(n) > EPS Then
                StabilizeVectorSign n
                Debug.Print TimeStamp() & " | INFO  | Face normal from Surface.PlaneParams = (" & Dbl3ToStr(n) & ")"
                GetStableFaceNormal = True
                Exit Function
            End If
        Else
            Debug.Print TimeStamp() & " | WARN  | Surface.PlaneParams did not return usable data"
        End If
    Else
        Debug.Print TimeStamp() & " | WARN  | face.GetSurface returned Nothing inside GetStableFaceNormal"
    End If

    Debug.Print TimeStamp() & " | FAIL  | GetStableFaceNormal exhausted all methods"
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetStableFaceNormal: " & Err.Number & " - " & Err.Description
End Function

Private Function TryGetNormalFromFaceVertices(ByVal face As SldWorks.Face2, ByRef n() As Double) As Boolean
    On Error GoTo EH

    TryGetNormalFromFaceVertices = False

    Dim vEdges As Variant
    vEdges = face.GetEdges

    If IsEmpty(vEdges) Then
        Debug.Print TimeStamp() & " | WARN  | TryGetNormalFromFaceVertices: face.GetEdges returned Empty"
        Exit Function
    End If

    If Not IsArray(vEdges) Then
        Debug.Print TimeStamp() & " | WARN  | TryGetNormalFromFaceVertices: face.GetEdges did not return array"
        Exit Function
    End If

    Dim pts() As Double
    Dim ptCount As Long
    ptCount = 0

    Dim i As Long
    For i = LBound(vEdges) To UBound(vEdges)
        Dim ed As SldWorks.Edge
        Set ed = vEdges(i)

        If Not ed Is Nothing Then
            Dim p0(2) As Double, p1(2) As Double
            If GetEdgeEndPoints(ed, p0, p1) Then
                AddUniquePoint3 pts, ptCount, p0
                AddUniquePoint3 pts, ptCount, p1
            End If
        End If
    Next i

    Debug.Print TimeStamp() & " | INFO  | TryGetNormalFromFaceVertices: unique point count = " & ptCount

    If ptCount < 3 Then
        Debug.Print TimeStamp() & " | WARN  | TryGetNormalFromFaceVertices: fewer than 3 unique points"
        Exit Function
    End If

    Dim i0 As Long, i1 As Long, i2 As Long
    For i0 = 0 To ptCount - 3
        For i1 = i0 + 1 To ptCount - 2
            For i2 = i1 + 1 To ptCount - 1

                Dim a(2) As Double, b(2) As Double, cr(2) As Double
                a(0) = pts(0, i1) - pts(0, i0)
                a(1) = pts(1, i1) - pts(1, i0)
                a(2) = pts(2, i1) - pts(2, i0)

                b(0) = pts(0, i2) - pts(0, i0)
                b(1) = pts(1, i2) - pts(1, i0)
                b(2) = pts(2, i2) - pts(2, i0)

                CrossProduct a, b, cr

                If VecLength(cr) > EPS Then
                    NormalizeVec cr
                    n(0) = cr(0)
                    n(1) = cr(1)
                    n(2) = cr(2)

                    Debug.Print TimeStamp() & " | INFO  | TryGetNormalFromFaceVertices: using point indices " & i0 & "," & i1 & "," & i2
                    TryGetNormalFromFaceVertices = True
                    Exit Function
                End If
            Next i2
        Next i1
    Next i0

    Debug.Print TimeStamp() & " | WARN  | TryGetNormalFromFaceVertices: no non-collinear triplet found"
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | TryGetNormalFromFaceVertices: " & Err.Number & " - " & Err.Description
End Function

Private Sub AddUniquePoint3(ByRef pts() As Double, ByRef ptCount As Long, ByRef p() As Double)
    Dim i As Long

    For i = 0 To ptCount - 1
        If Abs(pts(0, i) - p(0)) <= PT_TOL _
        And Abs(pts(1, i) - p(1)) <= PT_TOL _
        And Abs(pts(2, i) - p(2)) <= PT_TOL Then
            Exit Sub
        End If
    Next i

    If ptCount = 0 Then
        ReDim pts(0 To 2, 0 To 0)
    Else
        ReDim Preserve pts(0 To 2, 0 To ptCount)
    End If

    pts(0, ptCount) = p(0)
    pts(1, ptCount) = p(1)
    pts(2, ptCount) = p(2)
    ptCount = ptCount + 1
End Sub

Private Function VariantHas3Numbers(ByVal v As Variant) As Boolean
    On Error GoTo EH

    VariantHas3Numbers = False
    If IsEmpty(v) Then Exit Function
    If Not IsArray(v) Then Exit Function

    Dim lb As Long, ub As Long
    lb = LBound(v)
    ub = UBound(v)

    If (ub - lb + 1) < 3 Then Exit Function

    Dim t0 As Double, t1 As Double, t2 As Double
    t0 = CDbl(v(lb + 0))
    t1 = CDbl(v(lb + 1))
    t2 = CDbl(v(lb + 2))

    VariantHas3Numbers = True
    Exit Function

EH:
    VariantHas3Numbers = False
End Function

Private Function VariantHasAtLeast6Numbers(ByVal v As Variant) As Boolean
    On Error GoTo EH

    VariantHasAtLeast6Numbers = False
    If IsEmpty(v) Then Exit Function
    If Not IsArray(v) Then Exit Function

    Dim lb As Long, ub As Long
    lb = LBound(v)
    ub = UBound(v)

    If (ub - lb + 1) < 6 Then Exit Function

    Dim i As Long, tmp As Double
    For i = 0 To 5
        tmp = CDbl(v(lb + i))
    Next i

    VariantHasAtLeast6Numbers = True
    Exit Function

EH:
    VariantHasAtLeast6Numbers = False
End Function


Private Function GetBestTabAwareGussetOrientationCandidate(ByVal face As SldWorks.Face2, _
                                                           ByRef faceNormal() As Double, _
                                                           ByRef bestPrimaryLen As Double, _
                                                           ByRef bestSecondaryLen As Double, _
                                                           ByRef bestX() As Double, _
                                                           ByRef bestY() As Double, _
                                                           ByRef bestCorner() As Double) As Boolean
    On Error GoTo EH

    GetBestTabAwareGussetOrientationCandidate = False

    If face Is Nothing Then Exit Function

    Dim pts() As Double
    Dim ptCount As Long
    ptCount = 0

    If Not CollectUniqueFaceBoundaryPoints(face, pts, ptCount) Then
        Debug.Print TimeStamp() & " | INFO  | Tab-aware gusset scan: no usable boundary points"
        Exit Function
    End If

    Debug.Print TimeStamp() & " | INFO  | Tab-aware gusset scan: boundary point count = " & ptCount

    Dim vEdges As Variant
    vEdges = face.GetEdges

    If IsEmpty(vEdges) Then Exit Function
    If Not IsArray(vEdges) Then Exit Function

    bestPrimaryLen = -1#
    bestSecondaryLen = -1#

    Dim bestArea As Double
    Dim bestNegMax As Double
    bestArea = -1#
    bestNegMax = 1E+30

    Dim i As Long
    Dim j As Long

    For i = LBound(vEdges) To UBound(vEdges)
        Dim ed1 As SldWorks.Edge
        Set ed1 = vEdges(i)

        If Not ed1 Is Nothing Then
            If EdgeIsLinear(ed1) Then
                Dim e1p0(2) As Double, e1p1(2) As Double

                If GetEdgeEndPoints(ed1, e1p0, e1p1) Then
                    For j = i + 1 To UBound(vEdges)
                        Dim ed2 As SldWorks.Edge
                        Set ed2 = vEdges(j)

                        If Not ed2 Is Nothing Then
                            If EdgeIsLinear(ed2) Then
                                Dim e2p0(2) As Double, e2p1(2) As Double

                                If GetEdgeEndPoints(ed2, e2p0, e2p1) Then
                                    Dim corner(2) As Double
                                    Dim e1Far(2) As Double
                                    Dim e2Far(2) As Double

                                    If TryGetSharedCornerFromEdgeEndpoints(e1p0, e1p1, e2p0, e2p1, corner, e1Far, e2Far) Then
                                        Dim dir1(2) As Double
                                        Dim dir2(2) As Double
                                        Dim len1 As Double
                                        Dim len2 As Double

                                        dir1(0) = e1Far(0) - corner(0)
                                        dir1(1) = e1Far(1) - corner(1)
                                        dir1(2) = e1Far(2) - corner(2)

                                        dir2(0) = e2Far(0) - corner(0)
                                        dir2(1) = e2Far(1) - corner(1)
                                        dir2(2) = e2Far(2) - corner(2)

                                        ProjectVectorOntoPlane dir1, faceNormal, dir1
                                        ProjectVectorOntoPlane dir2, faceNormal, dir2

                                        len1 = VecLength(dir1)
                                        len2 = VecLength(dir2)

                                        If len1 > EPS And len2 > EPS Then
                                            NormalizeVec dir1
                                            NormalizeVec dir2

                                            Dim perpDot As Double
                                            perpDot = Abs(DotProduct(dir1, dir2))

                                            If perpDot <= PERP_DOT_TOL Then
                                                Dim candPrimaryLen As Double
                                                Dim candSecondaryLen As Double
                                                Dim candX(2) As Double
                                                Dim candY(2) As Double
                                                Dim candNegMax As Double
                                                Dim candArea As Double

                                                If EvaluateTabAwareCornerCandidate(pts, ptCount, corner, dir1, dir2, candPrimaryLen, candSecondaryLen, candX, candY, candNegMax, candArea) Then
                                                    Debug.Print TimeStamp() & " | INFO  | Tab-aware candidate accepted | EdgeIdx=" & i & "/" & j & _
                                                                " | corner=(" & Dbl3ToStr(corner) & ")" & _
                                                                " | primary=" & FormatNumber(candPrimaryLen, 8) & _
                                                                " | secondary=" & FormatNumber(candSecondaryLen, 8) & _
                                                                " | negMax=" & FormatNumber(candNegMax, 8) & _
                                                                " | area=" & FormatNumber(candArea, 8)

                                                    If IsTabAwareCandidateBetter(candPrimaryLen, candSecondaryLen, candArea, candNegMax, candX, _
                                                                                 bestPrimaryLen, bestSecondaryLen, bestArea, bestNegMax, bestX) Then
                                                        bestPrimaryLen = candPrimaryLen
                                                        bestSecondaryLen = candSecondaryLen
                                                        bestArea = candArea
                                                        bestNegMax = candNegMax

                                                        bestX(0) = candX(0): bestX(1) = candX(1): bestX(2) = candX(2)
                                                        bestY(0) = candY(0): bestY(1) = candY(1): bestY(2) = candY(2)
                                                        bestCorner(0) = corner(0): bestCorner(1) = corner(1): bestCorner(2) = corner(2)

                                                        GetBestTabAwareGussetOrientationCandidate = True
                                                    End If
                                                Else
                                                    Debug.Print TimeStamp() & " | INFO  | Tab-aware candidate rejected | EdgeIdx=" & i & "/" & j & _
                                                                " | corner=(" & Dbl3ToStr(corner) & ")"
                                                End If
                                            End If
                                        End If
                                    End If
                                End If
                            End If
                        End If
                    Next j
                End If
            End If
        End If
    Next i

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetBestTabAwareGussetOrientationCandidate: " & Err.Number & " - " & Err.Description
End Function

Private Function CollectUniqueFaceBoundaryPoints(ByVal face As SldWorks.Face2, _
                                                 ByRef pts() As Double, _
                                                 ByRef ptCount As Long) As Boolean
    On Error GoTo EH

    CollectUniqueFaceBoundaryPoints = False
    ptCount = 0

    If face Is Nothing Then Exit Function

    Dim vEdges As Variant
    vEdges = face.GetEdges

    If IsEmpty(vEdges) Then Exit Function
    If Not IsArray(vEdges) Then Exit Function

    Dim i As Long
    For i = LBound(vEdges) To UBound(vEdges)
        Dim ed As SldWorks.Edge
        Set ed = vEdges(i)

        If Not ed Is Nothing Then
            Dim p0(2) As Double, p1(2) As Double
            If GetEdgeEndPoints(ed, p0, p1) Then
                AddUniquePoint3 pts, ptCount, p0
                AddUniquePoint3 pts, ptCount, p1
            End If
        End If
    Next i

    CollectUniqueFaceBoundaryPoints = (ptCount >= 3)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | CollectUniqueFaceBoundaryPoints: " & Err.Number & " - " & Err.Description
End Function

Private Function EvaluateTabAwareCornerCandidate(ByRef pts() As Double, _
                                                 ByVal ptCount As Long, _
                                                 ByRef corner() As Double, _
                                                 ByRef dir1() As Double, _
                                                 ByRef dir2() As Double, _
                                                 ByRef bestPrimaryLen As Double, _
                                                 ByRef bestSecondaryLen As Double, _
                                                 ByRef bestX() As Double, _
                                                 ByRef bestY() As Double, _
                                                 ByRef bestNegMax As Double, _
                                                 ByRef bestArea As Double) As Boolean
    On Error GoTo EH

    EvaluateTabAwareCornerCandidate = False

    If ptCount < 3 Then Exit Function

    Dim requiredLeg As Double
    requiredLeg = TAB_AWARE_MIN_EFFECTIVE_LEG_M

    Dim ratioLeg As Double
    ratioLeg = TAB_AWARE_USER_TAB_MAX_M * TAB_AWARE_MIN_LEG_TO_TAB_RATIO
    If ratioLeg > requiredLeg Then requiredLeg = ratioLeg

    bestPrimaryLen = -1#
    bestSecondaryLen = -1#
    bestNegMax = 1E+30
    bestArea = -1#

    Dim sx As Long
    Dim sy As Long

    For sx = -1 To 1 Step 2
        For sy = -1 To 1 Step 2

            Dim candX(2) As Double
            Dim candY(2) As Double

            candX(0) = sx * dir1(0)
            candX(1) = sx * dir1(1)
            candX(2) = sx * dir1(2)

            candY(0) = sy * dir2(0)
            candY(1) = sy * dir2(1)
            candY(2) = sy * dir2(2)

            NormalizeVec candX
            NormalizeVec candY

            Dim maxNegX As Double
            Dim maxNegY As Double
            Dim effX As Double
            Dim effY As Double

            maxNegX = 0#
            maxNegY = 0#
            effX = 0#
            effY = 0#

            Dim i As Long
            For i = 0 To ptCount - 1
                Dim rel(2) As Double
                rel(0) = pts(0, i) - corner(0)
                rel(1) = pts(1, i) - corner(1)
                rel(2) = pts(2, i) - corner(2)

                Dim u As Double
                Dim v As Double
                u = DotProduct(rel, candX)
                v = DotProduct(rel, candY)

                If u < 0# Then
                    If -u > maxNegX Then maxNegX = -u
                Else
                    If Abs(v) <= TAB_AWARE_AXIS_STRIP_HALF_WIDTH_M + EPS Then
                        If u > effX Then effX = u
                    End If
                End If

                If v < 0# Then
                    If -v > maxNegY Then maxNegY = -v
                Else
                    If Abs(u) <= TAB_AWARE_AXIS_STRIP_HALF_WIDTH_M + EPS Then
                        If v > effY Then effY = v
                    End If
                End If
            Next i

            Dim candNegMax As Double
            candNegMax = maxNegX
            If maxNegY > candNegMax Then candNegMax = maxNegY

            Dim candPrimary As Double
            Dim candSecondary As Double
            Dim candArea As Double
            Dim finalX(2) As Double
            Dim finalY(2) As Double

            If TAB_AWARE_PREFER_LONGER_LEG_HORIZONTAL Then
                If effY > effX + EPS Then
                    candPrimary = effY
                    candSecondary = effX
                    finalX(0) = candY(0): finalX(1) = candY(1): finalX(2) = candY(2)
                    finalY(0) = candX(0): finalY(1) = candX(1): finalY(2) = candX(2)
                Else
                    candPrimary = effX
                    candSecondary = effY
                    finalX(0) = candX(0): finalX(1) = candX(1): finalX(2) = candX(2)
                    finalY(0) = candY(0): finalY(1) = candY(1): finalY(2) = candY(2)
                End If
            Else
                candPrimary = effX
                candSecondary = effY
                finalX(0) = candX(0): finalX(1) = candX(1): finalX(2) = candX(2)
                finalY(0) = candY(0): finalY(1) = candY(1): finalY(2) = candY(2)
            End If

            candArea = candPrimary * candSecondary

            Debug.Print TimeStamp() & " | INFO  | Tab-aware sign combo | sx=" & sx & " sy=" & sy & _
                        " | negX=" & FormatNumber(maxNegX, 8) & _
                        " | negY=" & FormatNumber(maxNegY, 8) & _
                        " | effX=" & FormatNumber(effX, 8) & _
                        " | effY=" & FormatNumber(effY, 8) & _
                        " | chosenPrimary=" & FormatNumber(candPrimary, 8) & _
                        " | chosenSecondary=" & FormatNumber(candSecondary, 8)

            If candNegMax <= TAB_AWARE_ALLOWANCE_M + EPS Then
                If candPrimary >= requiredLeg - EPS And candSecondary >= requiredLeg - EPS Then
                    If IsTabAwareCandidateBetter(candPrimary, candSecondary, candArea, candNegMax, finalX, _
                                                 bestPrimaryLen, bestSecondaryLen, bestArea, bestNegMax, bestX) Then
                        bestPrimaryLen = candPrimary
                        bestSecondaryLen = candSecondary
                        bestNegMax = candNegMax
                        bestArea = candArea

                        bestX(0) = finalX(0): bestX(1) = finalX(1): bestX(2) = finalX(2)
                        bestY(0) = finalY(0): bestY(1) = finalY(1): bestY(2) = finalY(2)

                        EvaluateTabAwareCornerCandidate = True
                    End If
                Else
                    Debug.Print TimeStamp() & " | INFO  | Tab-aware sign combo rejected by minimum effective leg requirement = " & FormatNumber(requiredLeg, 8)
                End If
            Else
                Debug.Print TimeStamp() & " | INFO  | Tab-aware sign combo rejected by negative extent allowance = " & FormatNumber(candNegMax, 8)
            End If
        Next sy
    Next sx

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | EvaluateTabAwareCornerCandidate: " & Err.Number & " - " & Err.Description
End Function

Private Function IsTabAwareCandidateBetter(ByVal candPrimary As Double, _
                                           ByVal candSecondary As Double, _
                                           ByVal candArea As Double, _
                                           ByVal candNegMax As Double, _
                                           ByRef candX() As Double, _
                                           ByVal bestPrimary As Double, _
                                           ByVal bestSecondary As Double, _
                                           ByVal bestArea As Double, _
                                           ByVal bestNegMax As Double, _
                                           ByRef bestX() As Double) As Boolean
    IsTabAwareCandidateBetter = False

    If candSecondary > bestSecondary + EPS Then
        IsTabAwareCandidateBetter = True
        Exit Function
    End If

    If Abs(candSecondary - bestSecondary) <= EPS Then
        If candArea > bestArea + EPS Then
            IsTabAwareCandidateBetter = True
            Exit Function
        End If

        If Abs(candArea - bestArea) <= EPS Then
            If candPrimary > bestPrimary + EPS Then
                IsTabAwareCandidateBetter = True
                Exit Function
            End If

            If Abs(candPrimary - bestPrimary) <= EPS Then
                If candNegMax < bestNegMax - EPS Then
                    IsTabAwareCandidateBetter = True
                    Exit Function
                End If

                If Abs(candNegMax - bestNegMax) <= EPS Then
                    If CompareVectorLex(candX, bestX) > 0 Then
                        IsTabAwareCandidateBetter = True
                        Exit Function
                    End If
                End If
            End If
        End If
    End If
End Function


' Determines the in-plane X direction for export.
' Priority order:
'   1) tab-aware gusset manufacturing corner
'   2) legacy shared-corner perpendicular pair
'   3) legacy virtual-corner perpendicular pair
'   4) longest linear edge
'   5) generic fallback axis
Private Function FindHorizontalDirectionFromFace(ByVal face As SldWorks.Face2, _
                                                 ByRef faceNormal() As Double, _
                                                 ByRef xDir() As Double, _
                                                 ByRef usedFallback As Boolean, _
                                                 ByRef usedPerpCorner As Boolean) As Boolean
    On Error GoTo EH

    FindHorizontalDirectionFromFace = False
    usedFallback = False
    usedPerpCorner = False

    Dim tabAwareFound As Boolean
    Dim sharedFound As Boolean
    Dim virtualFound As Boolean

    Dim tabPrimaryLen As Double
    Dim tabSecondaryLen As Double
    Dim tabX(2) As Double
    Dim tabY(2) As Double
    Dim tabCorner(2) As Double

    Dim sharedPrimaryLen As Double
    Dim sharedSecondaryLen As Double
    Dim sharedX(2) As Double
    Dim sharedY(2) As Double
    Dim sharedCorner(2) As Double

    Dim virtualPrimaryLen As Double
    Dim virtualSecondaryLen As Double
    Dim virtualX(2) As Double
    Dim virtualY(2) As Double
    Dim virtualCorner(2) As Double

    If ENABLE_TAB_AWARE_GUSSET_MODE Then
        Debug.Print TimeStamp() & " | INFO  | Tab-aware gusset orientation scan: start"
        tabAwareFound = GetBestTabAwareGussetOrientationCandidate(face, faceNormal, tabPrimaryLen, tabSecondaryLen, tabX, tabY, tabCorner)

        If tabAwareFound Then
            Debug.Print TimeStamp() & " | INFO  | Tab-aware gusset orientation candidate accepted before legacy pair-length logic"
            If ApplyBottomLeftOrientationCandidate(faceNormal, xDir, tabX, tabY, tabCorner, tabPrimaryLen, tabSecondaryLen, "Tab-aware gusset orientation", "Manufacturing corner") Then
                usedPerpCorner = True
                FindHorizontalDirectionFromFace = True
                Exit Function
            Else
                Debug.Print TimeStamp() & " | WARN  | Tab-aware gusset candidate was found but could not be applied"
            End If
        Else
            Debug.Print TimeStamp() & " | INFO  | Tab-aware gusset orientation scan found no qualifying manufacturing corner"
        End If
    End If

    sharedFound = GetBestSharedCornerOrientationCandidate(face, faceNormal, sharedPrimaryLen, sharedSecondaryLen, sharedX, sharedY, sharedCorner)

    If sharedFound Then
        Debug.Print TimeStamp() & " | INFO  | Shared-corner perpendicular pair found; checking non-intersecting perpendicular pairs for chamfer override"
        virtualFound = GetBestVirtualCornerOrientationCandidate(face, faceNormal, virtualPrimaryLen, virtualSecondaryLen, virtualX, virtualY, virtualCorner)

        If virtualFound Then
            Debug.Print TimeStamp() & " | INFO  | Best shared-corner pair lengths     = " & FormatNumber(sharedPrimaryLen, 8) & " / " & FormatNumber(sharedSecondaryLen, 8)
            Debug.Print TimeStamp() & " | INFO  | Best non-intersecting pair lengths = " & FormatNumber(virtualPrimaryLen, 8) & " / " & FormatNumber(virtualSecondaryLen, 8)

            If IsPairLengthBetter(virtualPrimaryLen, virtualSecondaryLen, sharedPrimaryLen, sharedSecondaryLen) Then
                Debug.Print TimeStamp() & " | INFO  | Non-intersecting perpendicular pair is longer than shared-corner pair; using chamfer-ignore virtual corner orientation"
                If ApplyBottomLeftOrientationCandidate(faceNormal, xDir, virtualX, virtualY, virtualCorner, virtualPrimaryLen, virtualSecondaryLen, "Virtual corner orientation", "Virtual corner") Then
                    usedPerpCorner = True
                    FindHorizontalDirectionFromFace = True
                    Exit Function
                End If
            Else
                Debug.Print TimeStamp() & " | INFO  | Shared-corner pair remains preferred because non-intersecting pair is not longer"
                If ApplyBottomLeftOrientationCandidate(faceNormal, xDir, sharedX, sharedY, sharedCorner, sharedPrimaryLen, sharedSecondaryLen, "Corner orientation", "Shared corner") Then
                    usedPerpCorner = True
                    FindHorizontalDirectionFromFace = True
                    Exit Function
                End If
            End If
        Else
            Debug.Print TimeStamp() & " | INFO  | No qualifying non-intersecting perpendicular pair found; using shared-corner pair"
            If ApplyBottomLeftOrientationCandidate(faceNormal, xDir, sharedX, sharedY, sharedCorner, sharedPrimaryLen, sharedSecondaryLen, "Corner orientation", "Shared corner") Then
                usedPerpCorner = True
                FindHorizontalDirectionFromFace = True
                Exit Function
            End If
        End If
    Else
        Debug.Print TimeStamp() & " | INFO  | No valid perpendicular shared-corner pair found; trying non-intersecting perpendicular pair fallback"

        virtualFound = GetBestVirtualCornerOrientationCandidate(face, faceNormal, virtualPrimaryLen, virtualSecondaryLen, virtualX, virtualY, virtualCorner)
        If virtualFound Then
            If ApplyBottomLeftOrientationCandidate(faceNormal, xDir, virtualX, virtualY, virtualCorner, virtualPrimaryLen, virtualSecondaryLen, "Virtual corner orientation", "Virtual corner") Then
                usedPerpCorner = True
                FindHorizontalDirectionFromFace = True
                Exit Function
            End If
        End If
    End If

    Debug.Print TimeStamp() & " | INFO  | No usable perpendicular edge pair found; trying longest single edge fallback"

    Dim vEdges As Variant
    vEdges = face.GetEdges

    Dim bestLen As Double
    bestLen = -1#

    Dim bestVec(2) As Double
    Dim foundLine As Boolean
    foundLine = False

    If Not IsEmpty(vEdges) Then
        If IsArray(vEdges) Then
            Dim i As Long
            For i = LBound(vEdges) To UBound(vEdges)
                Dim ed As SldWorks.Edge
                Set ed = vEdges(i)
                If Not ed Is Nothing Then
                    If EdgeIsLinear(ed) Then
                        Dim p0(2) As Double, p1(2) As Double
                        If GetEdgeEndPoints(ed, p0, p1) Then
                            Dim v(2) As Double
                            v(0) = p1(0) - p0(0)
                            v(1) = p1(1) - p0(1)
                            v(2) = p1(2) - p0(2)

                            ProjectVectorOntoPlane v, faceNormal, v
                            Dim L As Double
                            L = VecLength(v)

                            If L > EPS Then
                                NormalizeVec v
                                StabilizeVectorSign v

                                Dim trueLen As Double
                                trueLen = Distance3(p0, p1)

                                If (trueLen > bestLen + EPS) Or _
                                   (Abs(trueLen - bestLen) <= EPS And CompareVectorLex(v, bestVec) > 0) Then
                                    bestLen = trueLen
                                    bestVec(0) = v(0)
                                    bestVec(1) = v(1)
                                    bestVec(2) = v(2)
                                    foundLine = True
                                End If
                            End If
                        End If
                    End If
                End If
            Next i
        End If
    End If

    If foundLine Then
        xDir(0) = bestVec(0)
        xDir(1) = bestVec(1)
        xDir(2) = bestVec(2)
        NormalizeVec xDir
        Debug.Print TimeStamp() & " | INFO  | Longest edge dir fallback = (" & Dbl3ToStr(xDir) & "), length = " & FormatNumber(bestLen, 8)
        FindHorizontalDirectionFromFace = True
        Exit Function
    End If

    Debug.Print TimeStamp() & " | WARN  | No valid linear edge found on face; using fallback orientation axis"
    ChooseFallbackXAxis faceNormal, xDir
    usedFallback = True
    FindHorizontalDirectionFromFace = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | FindHorizontalDirectionFromFace: " & Err.Number & " - " & Err.Description
End Function


Private Function EdgeIsLinear(ByVal ed As SldWorks.Edge) As Boolean
    On Error GoTo EH

    EdgeIsLinear = False

    Dim crv As SldWorks.Curve
    Set crv = ed.GetCurve
    If crv Is Nothing Then Exit Function

    EdgeIsLinear = crv.IsLine
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | EdgeIsLinear: " & Err.Number & " - " & Err.Description
End Function

Private Function GetEdgeEndPoints(ByVal ed As SldWorks.Edge, ByRef p0() As Double, ByRef p1() As Double) As Boolean
    On Error GoTo EH

    GetEdgeEndPoints = False

    Dim vStart As SldWorks.Vertex
    Dim vEnd As SldWorks.Vertex

    Set vStart = ed.GetStartVertex
    Set vEnd = ed.GetEndVertex

    If vStart Is Nothing Or vEnd Is Nothing Then Exit Function

    Dim a As Variant, b As Variant
    a = vStart.GetPoint
    b = vEnd.GetPoint

    If IsEmpty(a) Or IsEmpty(b) Then Exit Function

    p0(0) = CDbl(a(0)): p0(1) = CDbl(a(1)): p0(2) = CDbl(a(2))
    p1(0) = CDbl(b(0)): p1(1) = CDbl(b(1)): p1(2) = CDbl(b(2))

    GetEdgeEndPoints = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetEdgeEndPoints: " & Err.Number & " - " & Err.Description
End Function

Private Function GetBestSharedCornerOrientationCandidate(ByVal face As SldWorks.Face2, _
                                                         ByRef faceNormal() As Double, _
                                                         ByRef bestPrimaryLen As Double, _
                                                         ByRef bestSecondaryLen As Double, _
                                                         ByRef bestX() As Double, _
                                                         ByRef bestY() As Double, _
                                                         ByRef bestCorner() As Double) As Boolean
    On Error GoTo EH

    GetBestSharedCornerOrientationCandidate = False

    If face Is Nothing Then Exit Function

    Dim vEdges As Variant
    vEdges = face.GetEdges

    If IsEmpty(vEdges) Then
        Debug.Print TimeStamp() & " | INFO  | Corner orientation scan: face has no edges"
        Exit Function
    End If

    If Not IsArray(vEdges) Then
        Debug.Print TimeStamp() & " | INFO  | Corner orientation scan: face edges are not an array"
        Exit Function
    End If

    Dim foundPair As Boolean
    foundPair = False

    bestPrimaryLen = -1#
    bestSecondaryLen = -1#

    Dim i As Long
    Dim j As Long

    For i = LBound(vEdges) To UBound(vEdges)
        Dim ed1 As SldWorks.Edge
        Set ed1 = vEdges(i)

        If Not ed1 Is Nothing Then
            If EdgeIsLinear(ed1) Then

                Dim e1p0(2) As Double, e1p1(2) As Double
                If GetEdgeEndPoints(ed1, e1p0, e1p1) Then

                    For j = i + 1 To UBound(vEdges)
                        Dim ed2 As SldWorks.Edge
                        Set ed2 = vEdges(j)

                        If Not ed2 Is Nothing Then
                            If EdgeIsLinear(ed2) Then

                                Dim e2p0(2) As Double, e2p1(2) As Double
                                If GetEdgeEndPoints(ed2, e2p0, e2p1) Then

                                    Dim corner(2) As Double
                                    Dim e1Far(2) As Double
                                    Dim e2Far(2) As Double

                                    If TryGetSharedCornerFromEdgeEndpoints(e1p0, e1p1, e2p0, e2p1, corner, e1Far, e2Far) Then

                                        Dim dir1(2) As Double, dir2(2) As Double
                                        dir1(0) = e1Far(0) - corner(0)
                                        dir1(1) = e1Far(1) - corner(1)
                                        dir1(2) = e1Far(2) - corner(2)

                                        dir2(0) = e2Far(0) - corner(0)
                                        dir2(1) = e2Far(1) - corner(1)
                                        dir2(2) = e2Far(2) - corner(2)

                                        ProjectVectorOntoPlane dir1, faceNormal, dir1
                                        ProjectVectorOntoPlane dir2, faceNormal, dir2

                                        Dim len1 As Double, len2 As Double
                                        len1 = VecLength(dir1)
                                        len2 = VecLength(dir2)

                                        If len1 > EPS And len2 > EPS Then
                                            NormalizeVec dir1
                                            NormalizeVec dir2

                                            Dim perpDot As Double
                                            perpDot = Abs(DotProduct(dir1, dir2))

                                            Debug.Print TimeStamp() & " | INFO  | Corner pair candidate | EdgeIdx=" & i & "/" & j & _
                                                        " | corner=(" & Dbl3ToStr(corner) & ")" & _
                                                        " | len1=" & FormatNumber(len1, 8) & _
                                                        " | len2=" & FormatNumber(len2, 8) & _
                                                        " | |dot|=" & FormatNumber(perpDot, 8)

                                            If perpDot <= PERP_DOT_TOL Then

                                                Dim primaryLen As Double
                                                Dim secondaryLen As Double
                                                Dim candX(2) As Double
                                                Dim candY(2) As Double

                                                If (len1 > len2 + EPS) Or _
                                                   (Abs(len1 - len2) <= EPS And CompareVectorLex(dir1, dir2) >= 0) Then
                                                    primaryLen = len1
                                                    secondaryLen = len2
                                                    candX(0) = dir1(0): candX(1) = dir1(1): candX(2) = dir1(2)
                                                    candY(0) = dir2(0): candY(1) = dir2(1): candY(2) = dir2(2)
                                                Else
                                                    primaryLen = len2
                                                    secondaryLen = len1
                                                    candX(0) = dir2(0): candX(1) = dir2(1): candX(2) = dir2(2)
                                                    candY(0) = dir1(0): candY(1) = dir1(1): candY(2) = dir1(2)
                                                End If

                                                If (primaryLen > bestPrimaryLen + EPS) Or _
                                                   (Abs(primaryLen - bestPrimaryLen) <= EPS And secondaryLen > bestSecondaryLen + EPS) Or _
                                                   (Abs(primaryLen - bestPrimaryLen) <= EPS And Abs(secondaryLen - bestSecondaryLen) <= EPS And CompareVectorLex(candX, bestX) > 0) Then

                                                    bestPrimaryLen = primaryLen
                                                    bestSecondaryLen = secondaryLen

                                                    bestX(0) = candX(0): bestX(1) = candX(1): bestX(2) = candX(2)
                                                    bestY(0) = candY(0): bestY(1) = candY(1): bestY(2) = candY(2)

                                                    bestCorner(0) = corner(0)
                                                    bestCorner(1) = corner(1)
                                                    bestCorner(2) = corner(2)

                                                    foundPair = True
                                                End If
                                            End If
                                        End If
                                    End If
                                End If
                            End If
                        End If
                    Next j
                End If
            End If
        End If
    Next i

    GetBestSharedCornerOrientationCandidate = foundPair
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetBestSharedCornerOrientationCandidate: " & Err.Number & " - " & Err.Description
End Function

Private Function GetBestVirtualCornerOrientationCandidate(ByVal face As SldWorks.Face2, _
                                                          ByRef faceNormal() As Double, _
                                                          ByRef bestPrimaryLen As Double, _
                                                          ByRef bestSecondaryLen As Double, _
                                                          ByRef bestX() As Double, _
                                                          ByRef bestY() As Double, _
                                                          ByRef bestCorner() As Double) As Boolean
    On Error GoTo EH

    GetBestVirtualCornerOrientationCandidate = False

    If face Is Nothing Then Exit Function

    Dim vEdges As Variant
    vEdges = face.GetEdges

    If IsEmpty(vEdges) Then
        Debug.Print TimeStamp() & " | INFO  | Virtual corner orientation scan: face has no edges"
        Exit Function
    End If

    If Not IsArray(vEdges) Then
        Debug.Print TimeStamp() & " | INFO  | Virtual corner orientation scan: face edges are not an array"
        Exit Function
    End If

    Dim foundPair As Boolean
    foundPair = False

    bestPrimaryLen = -1#
    bestSecondaryLen = -1#

    Dim i As Long
    Dim j As Long

    For i = LBound(vEdges) To UBound(vEdges)
        Dim ed1 As SldWorks.Edge
        Set ed1 = vEdges(i)

        If Not ed1 Is Nothing Then
            If EdgeIsLinear(ed1) Then
                Dim e1p0(2) As Double, e1p1(2) As Double

                If GetEdgeEndPoints(ed1, e1p0, e1p1) Then
                    For j = i + 1 To UBound(vEdges)
                        Dim ed2 As SldWorks.Edge
                        Set ed2 = vEdges(j)

                        If Not ed2 Is Nothing Then
                            If EdgeIsLinear(ed2) Then
                                Dim e2p0(2) As Double, e2p1(2) As Double

                                If GetEdgeEndPoints(ed2, e2p0, e2p1) Then
                                    Dim sharedCorner(2) As Double
                                    Dim sharedFar1(2) As Double
                                    Dim sharedFar2(2) As Double

                                    If Not TryGetSharedCornerFromEdgeEndpoints(e1p0, e1p1, e2p0, e2p1, sharedCorner, sharedFar1, sharedFar2) Then
                                        Dim dir1Raw(2) As Double
                                        Dim dir2Raw(2) As Double
                                        Dim trueLen1 As Double
                                        Dim trueLen2 As Double

                                        dir1Raw(0) = e1p1(0) - e1p0(0)
                                        dir1Raw(1) = e1p1(1) - e1p0(1)
                                        dir1Raw(2) = e1p1(2) - e1p0(2)

                                        dir2Raw(0) = e2p1(0) - e2p0(0)
                                        dir2Raw(1) = e2p1(1) - e2p0(1)
                                        dir2Raw(2) = e2p1(2) - e2p0(2)

                                        ProjectVectorOntoPlane dir1Raw, faceNormal, dir1Raw
                                        ProjectVectorOntoPlane dir2Raw, faceNormal, dir2Raw

                                        trueLen1 = Distance3(e1p0, e1p1)
                                        trueLen2 = Distance3(e2p0, e2p1)

                                        If VecLength(dir1Raw) > EPS And VecLength(dir2Raw) > EPS Then
                                            NormalizeVec dir1Raw
                                            NormalizeVec dir2Raw

                                            Dim perpDot As Double
                                            perpDot = Abs(DotProduct(dir1Raw, dir2Raw))

                                            Debug.Print TimeStamp() & " | INFO  | Virtual corner pair candidate | EdgeIdx=" & i & "/" & j & _
                                                        " | len1=" & FormatNumber(trueLen1, 8) & _
                                                        " | len2=" & FormatNumber(trueLen2, 8) & _
                                                        " | |dot|=" & FormatNumber(perpDot, 8)

                                            If perpDot <= PERP_DOT_TOL Then
                                                Dim virtualCorner(2) As Double

                                                If TryGetProjectedLineIntersection(e1p0, dir1Raw, e2p0, dir2Raw, faceNormal, virtualCorner) Then
                                                    Dim dir1(2) As Double
                                                    Dim dir2(2) As Double

                                                    If GetEdgeDirectionAwayFromReference(e1p0, e1p1, virtualCorner, dir1) Then
                                                        If GetEdgeDirectionAwayFromReference(e2p0, e2p1, virtualCorner, dir2) Then
                                                            Dim primaryLen As Double
                                                            Dim secondaryLen As Double
                                                            Dim candX(2) As Double
                                                            Dim candY(2) As Double

                                                            If (trueLen1 > trueLen2 + EPS) Or _
                                                               (Abs(trueLen1 - trueLen2) <= EPS And CompareVectorLex(dir1, dir2) >= 0) Then
                                                                primaryLen = trueLen1
                                                                secondaryLen = trueLen2
                                                                candX(0) = dir1(0): candX(1) = dir1(1): candX(2) = dir1(2)
                                                                candY(0) = dir2(0): candY(1) = dir2(1): candY(2) = dir2(2)
                                                            Else
                                                                primaryLen = trueLen2
                                                                secondaryLen = trueLen1
                                                                candX(0) = dir2(0): candX(1) = dir2(1): candX(2) = dir2(2)
                                                                candY(0) = dir1(0): candY(1) = dir1(1): candY(2) = dir1(2)
                                                            End If

                                                            If (primaryLen > bestPrimaryLen + EPS) Or _
                                                               (Abs(primaryLen - bestPrimaryLen) <= EPS And secondaryLen > bestSecondaryLen + EPS) Or _
                                                               (Abs(primaryLen - bestPrimaryLen) <= EPS And Abs(secondaryLen - bestSecondaryLen) <= EPS And CompareVectorLex(candX, bestX) > 0) Then
                                                                bestPrimaryLen = primaryLen
                                                                bestSecondaryLen = secondaryLen

                                                                bestX(0) = candX(0): bestX(1) = candX(1): bestX(2) = candX(2)
                                                                bestY(0) = candY(0): bestY(1) = candY(1): bestY(2) = candY(2)

                                                                bestCorner(0) = virtualCorner(0)
                                                                bestCorner(1) = virtualCorner(1)
                                                                bestCorner(2) = virtualCorner(2)

                                                                foundPair = True
                                                            End If
                                                        End If
                                                    End If
                                                End If
                                            End If
                                        End If
                                    End If
                                End If
                            End If
                        End If
                    Next j
                End If
            End If
        End If
    Next i

    GetBestVirtualCornerOrientationCandidate = foundPair
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetBestVirtualCornerOrientationCandidate: " & Err.Number & " - " & Err.Description
End Function

Private Function IsPairLengthBetter(ByVal candidatePrimaryLen As Double, _
                                    ByVal candidateSecondaryLen As Double, _
                                    ByVal referencePrimaryLen As Double, _
                                    ByVal referenceSecondaryLen As Double) As Boolean
    IsPairLengthBetter = False

    If candidatePrimaryLen > referencePrimaryLen + EPS Then
        IsPairLengthBetter = True
        Exit Function
    End If

    If Abs(candidatePrimaryLen - referencePrimaryLen) <= EPS Then
        If candidateSecondaryLen > referenceSecondaryLen + EPS Then
            IsPairLengthBetter = True
            Exit Function
        End If
    End If
End Function

Private Function ApplyBottomLeftOrientationCandidate(ByRef faceNormal() As Double, _
                                                     ByRef xDir() As Double, _
                                                     ByRef bestX() As Double, _
                                                     ByRef bestY() As Double, _
                                                     ByRef bestCorner() As Double, _
                                                     ByVal bestPrimaryLen As Double, _
                                                     ByVal bestSecondaryLen As Double, _
                                                     ByVal selectionLabel As String, _
                                                     ByVal cornerLabel As String) As Boolean
    On Error GoTo EH

    ApplyBottomLeftOrientationCandidate = False

    xDir(0) = bestX(0)
    xDir(1) = bestX(1)
    xDir(2) = bestX(2)
    NormalizeVec xDir

    Dim currentY(2) As Double
    CrossProduct faceNormal, xDir, currentY
    NormalizeVec currentY

    Dim flippedNormal As Boolean
    flippedNormal = False

    If DotProduct(currentY, bestY) < 0# Then
        FlipVec faceNormal
        flippedNormal = True
        CrossProduct faceNormal, xDir, currentY
        NormalizeVec currentY
    End If

    Debug.Print TimeStamp() & " | INFO  | " & selectionLabel & " selected"
    Debug.Print TimeStamp() & " | INFO  |   " & cornerLabel & " = (" & Dbl3ToStr(bestCorner) & ")"
    Debug.Print TimeStamp() & " | INFO  |   Bottom edge X = (" & Dbl3ToStr(xDir) & ") | len = " & FormatNumber(bestPrimaryLen, 8)
    Debug.Print TimeStamp() & " | INFO  |   Left edge Y   = (" & Dbl3ToStr(bestY) & ") | len = " & FormatNumber(bestSecondaryLen, 8)
    Debug.Print TimeStamp() & " | INFO  |   Face normal   = (" & Dbl3ToStr(faceNormal) & ") | flipped=" & CStr(flippedNormal)

    ApplyBottomLeftOrientationCandidate = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ApplyBottomLeftOrientationCandidate: " & Err.Number & " - " & Err.Description
End Function


Private Function TryGetProjectedLineIntersection(ByRef line1Point() As Double, _
                                                 ByRef line1DirUnit() As Double, _
                                                 ByRef line2Point() As Double, _
                                                 ByRef line2DirUnit() As Double, _
                                                 ByRef faceNormal() As Double, _
                                                 ByRef intersection() As Double) As Boolean
    On Error GoTo EH

    TryGetProjectedLineIntersection = False

    Dim uv As Double
    uv = DotProduct(line1DirUnit, line2DirUnit)

    Dim denom As Double
    denom = 1# - (uv * uv)

    If denom <= EPS Then Exit Function

    Dim delta(2) As Double
    delta(0) = line2Point(0) - line1Point(0)
    delta(1) = line2Point(1) - line1Point(1)
    delta(2) = line2Point(2) - line1Point(2)
    ProjectVectorOntoPlane delta, faceNormal, delta

    Dim du As Double
    Dim dv As Double
    du = DotProduct(delta, line1DirUnit)
    dv = DotProduct(delta, line2DirUnit)

    Dim t As Double
    t = (du - (uv * dv)) / denom

    intersection(0) = line1Point(0) + (line1DirUnit(0) * t)
    intersection(1) = line1Point(1) + (line1DirUnit(1) * t)
    intersection(2) = line1Point(2) + (line1DirUnit(2) * t)

    TryGetProjectedLineIntersection = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | TryGetProjectedLineIntersection: " & Err.Number & " - " & Err.Description
End Function

Private Function GetEdgeDirectionAwayFromReference(ByRef p0() As Double, _
                                                   ByRef p1() As Double, _
                                                   ByRef refPoint() As Double, _
                                                   ByRef dirOut() As Double) As Boolean
    On Error GoTo EH

    GetEdgeDirectionAwayFromReference = False

    Dim d0 As Double
    Dim d1 As Double
    d0 = Distance3(p0, refPoint)
    d1 = Distance3(p1, refPoint)

    If (d0 <= PT_TOL) And (d1 <= PT_TOL) Then Exit Function

    If d0 < d1 - PT_TOL Then
        dirOut(0) = p1(0) - p0(0)
        dirOut(1) = p1(1) - p0(1)
        dirOut(2) = p1(2) - p0(2)
    ElseIf d1 < d0 - PT_TOL Then
        dirOut(0) = p0(0) - p1(0)
        dirOut(1) = p0(1) - p1(1)
        dirOut(2) = p0(2) - p1(2)
    Else
        dirOut(0) = p1(0) - p0(0)
        dirOut(1) = p1(1) - p0(1)
        dirOut(2) = p1(2) - p0(2)
        StabilizeVectorSign dirOut
    End If

    If VecLength(dirOut) <= EPS Then Exit Function

    NormalizeVec dirOut
    GetEdgeDirectionAwayFromReference = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetEdgeDirectionAwayFromReference: " & Err.Number & " - " & Err.Description
End Function

Private Function TryGetSharedCornerFromEdgeEndpoints(ByRef a0() As Double, _
                                                     ByRef a1() As Double, _
                                                     ByRef b0() As Double, _
                                                     ByRef b1() As Double, _
                                                     ByRef corner() As Double, _
                                                     ByRef aFar() As Double, _
                                                     ByRef bFar() As Double) As Boolean
    TryGetSharedCornerFromEdgeEndpoints = False

    If PointsCoincident3(a0, b0) Then
        CopyPoint3 a0, corner
        CopyPoint3 a1, aFar
        CopyPoint3 b1, bFar
        TryGetSharedCornerFromEdgeEndpoints = True
        Exit Function
    End If

    If PointsCoincident3(a0, b1) Then
        CopyPoint3 a0, corner
        CopyPoint3 a1, aFar
        CopyPoint3 b0, bFar
        TryGetSharedCornerFromEdgeEndpoints = True
        Exit Function
    End If

    If PointsCoincident3(a1, b0) Then
        CopyPoint3 a1, corner
        CopyPoint3 a0, aFar
        CopyPoint3 b1, bFar
        TryGetSharedCornerFromEdgeEndpoints = True
        Exit Function
    End If

    If PointsCoincident3(a1, b1) Then
        CopyPoint3 a1, corner
        CopyPoint3 a0, aFar
        CopyPoint3 b0, bFar
        TryGetSharedCornerFromEdgeEndpoints = True
        Exit Function
    End If
End Function

Private Function PointsCoincident3(ByRef p1() As Double, ByRef p2() As Double) As Boolean
    PointsCoincident3 = (Distance3(p1, p2) <= PT_TOL)
End Function

Private Sub CopyPoint3(ByRef src() As Double, ByRef dst() As Double)
    dst(0) = src(0)
    dst(1) = src(1)
    dst(2) = src(2)
End Sub

Private Sub ChooseFallbackXAxis(ByRef n() As Double, ByRef x() As Double)
    Dim ref(2) As Double

    If Abs(n(2)) < 0.9 Then
        ref(0) = 0#: ref(1) = 0#: ref(2) = 1#
    Else
        ref(0) = 0#: ref(1) = 1#: ref(2) = 0#
    End If

    CrossProduct ref, n, x
    NormalizeVec x
    StabilizeVectorSign x
End Sub

Private Function BuildViewOrientationTransform(ByRef xAxis() As Double, _
                                               ByRef zAxis() As Double) As SldWorks.MathTransform
    On Error GoTo EH

    Dim x(2) As Double, y(2) As Double, z(2) As Double

    x(0) = xAxis(0): x(1) = xAxis(1): x(2) = xAxis(2)
    z(0) = zAxis(0): z(1) = zAxis(1): z(2) = zAxis(2)

    NormalizeVec x
    NormalizeVec z

    ProjectVectorOntoPlane x, z, x
    NormalizeVec x

    CrossProduct z, x, y
    NormalizeVec y

    CrossProduct y, z, x
    NormalizeVec x

    Dim data(15) As Double
    Dim i As Long
    For i = 0 To 15
        data(i) = 0#
    Next i

    ' Basis vectors as COLUMNS
    data(0) = x(0): data(1) = y(0): data(2) = z(0)
    data(3) = x(1): data(4) = y(1): data(5) = z(1)
    data(6) = x(2): data(7) = y(2): data(8) = z(2)

    data(9) = 0#
    data(10) = 0#
    data(11) = 0#
    data(12) = 1#
    data(13) = 0#
    data(14) = 0#
    data(15) = 0#

    Debug.Print TimeStamp() & " | INFO  | BuildViewOrientationTransform axes:"
    Debug.Print TimeStamp() & " | INFO  |   X = (" & Dbl3ToStr(x) & ")"
    Debug.Print TimeStamp() & " | INFO  |   Y = (" & Dbl3ToStr(y) & ")"
    Debug.Print TimeStamp() & " | INFO  |   Z = (" & Dbl3ToStr(z) & ")"

    Set BuildViewOrientationTransform = swMathUtil.CreateTransform(data)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | BuildViewOrientationTransform: " & Err.Number & " - " & Err.Description
End Function

'=========================================================================================
' EXPORT
'=========================================================================================
' Export pipeline:
'   1) copy source body
'   2) build temp part
'   3) orient/persist named view
'   4) optionally try experimental direct DXF
'   5) fall back to named-view drawing DXF export
' This split keeps orientation persistence and downstream repeatability intact.
Private Function ExportBodyAsOrientedDxf(ByVal srcBody As SldWorks.Body2, _
                                         ByRef faceNormal() As Double, _
                                         ByRef xDir() As Double, _
                                         ByVal finalDxfPath As String, _
                                         ByVal cutListName As String, _
                                         ByVal preserveCornerOrientation As Boolean) As Boolean
    On Error GoTo EH

    ExportBodyAsOrientedDxf = False

    Dim tempRoot As String
    tempRoot = GetLocalTempRoot()

    If Len(tempRoot) = 0 Then
        Debug.Print TimeStamp() & " | FAIL  | Could not create local temp root"
        Exit Function
    End If

    Debug.Print TimeStamp() & " | INFO  | Local temp workspace = " & tempRoot
    Debug.Print TimeStamp() & " | INFO  | DXF export mode = " & GetDxfExportModeName()

    Dim tempPartPath As String
    Dim tempDrwPath As String
    tempPartPath = tempRoot & "body_temp.sldprt"
    tempDrwPath = tempRoot & "body_temp.slddrw"

    Dim tempPartDoc As SldWorks.ModelDoc2
    Dim tempBody As SldWorks.Body2

    Set tempBody = srcBody.Copy
    If tempBody Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | Body copy returned Nothing"
        GoTo CleanupAndExit
    End If

    Set tempPartDoc = CreateTempPartFromBody(tempBody, tempPartPath, Not (USE_FAST_MODE And FAST_MODE_SINGLE_TEMP_PART_SAVE))
    If tempPartDoc Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | Temp part creation failed"
        GoTo CleanupAndExit
    End If

    ' Keep the proven orientation stage intact. The direct export path below now uses
    ' selected-face export plus explicit alignment data rather than a drawing.
    If Not OrientAndNameTempPartView(tempPartDoc, xDir, faceNormal, TEMP_VIEW_NAME) Then
        Debug.Print TimeStamp() & " | FAIL  | Could not orient/name temp part view"
        GoTo CleanupAndExit
    End If

    Debug.Print TimeStamp() & " | INFO  | Saving temp part after custom orientation persistence"
    If Not TrySaveModelToPath(tempPartDoc, tempPartPath) Then
        Debug.Print TimeStamp() & " | FAIL  | Could not persist oriented temp part file"
        GoTo CleanupAndExit
    End If

    If Not ShouldAttemptFastDirectDxfExport(preserveCornerOrientation) Then
        Debug.Print TimeStamp() & " | FAIL  | Direct part DXF export is disabled by current toggle settings"
        GoTo CleanupAndExit
    End If

    Set tempPartDoc = ReopenTempPartForDirectExport(tempPartDoc, tempPartPath)
    If tempPartDoc Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | Could not reopen temp part for direct export"
        GoTo CleanupAndExit
    End If

    Debug.Print TimeStamp() & " | INFO  | Trying direct part DXF export by selected planar face"
    If ExportTempPartToDxf_DirectBySelectedFace(tempPartDoc, tempPartPath, finalDxfPath, xDir, faceNormal, preserveCornerOrientation) Then
        ExportBodyAsOrientedDxf = True
        GoTo CleanupAndExit
    End If

    If EXPERIMENTAL_DIRECT_FALLBACK_TO_DRAWING Then
        Debug.Print TimeStamp() & " | WARN  | Direct selected-face export failed; drawing fallback is enabled"
        If ExportTempPartToDxf_ByNamedViewDrawing(tempPartDoc, tempPartPath, tempDrwPath, finalDxfPath, TEMP_VIEW_NAME, xDir, faceNormal, preserveCornerOrientation) Then
            ExportBodyAsOrientedDxf = True
            GoTo CleanupAndExit
        End If
    Else
        Debug.Print TimeStamp() & " | FAIL  | Direct selected-face export failed and drawing fallback is disabled"
    End If

CleanupAndExit:
    CloseModelDocSafe tempPartDoc
    DeleteFileIfExists tempDrwPath
    DeleteFileIfExists tempPartPath
    DeleteFolderIfEmpty tempRoot
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ExportBodyAsOrientedDxf(" & cutListName & "): " & Err.Number & " - " & Err.Description
End Function

Private Function ExportTempPartToDxf_DirectBySelectedFace(ByVal tempPartDoc As SldWorks.ModelDoc2, _
                                                          ByVal tempPartPath As String, _
                                                          ByVal finalDxfPath As String, _
                                                          ByRef xDir() As Double, _
                                                          ByRef faceNormal() As Double, _
                                                          ByVal preserveCornerOrientation As Boolean) As Boolean
    On Error GoTo EH

    ExportTempPartToDxf_DirectBySelectedFace = False

    If tempPartDoc Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | Direct selected-face export: tempPartDoc is Nothing"
        Exit Function
    End If

    If tempPartDoc.GetType <> swDocPART Then
        Debug.Print TimeStamp() & " | FAIL  | Direct selected-face export requires PART doc, got " & DocTypeName(tempPartDoc.GetType)
        Exit Function
    End If

    Dim swTempPart As SldWorks.PartDoc
    Set swTempPart = tempPartDoc

    If Not ActivateDocumentByTitle(tempPartDoc.GetTitle) Then
        Debug.Print TimeStamp() & " | WARN  | Direct selected-face export could not explicitly activate temp part"
    End If

    tempPartDoc.ClearSelection2 True
    tempPartDoc.ForceRebuild3 True

    If DIRECT_TEMP_PART_FORCE_INCH_UNITS Then
        If ForceModelLinearUnitsToInches(tempPartDoc) Then
            Debug.Print TimeStamp() & " | INFO  | Direct selected-face export temp part linear units = INCHES"
        Else
            Debug.Print TimeStamp() & " | WARN  | Direct selected-face export could not confirm INCH units on temp part"
        End If
    End If

    Dim exportFace As SldWorks.Face2
    Set exportFace = GetLargestPlanarFaceFromPartDocument(tempPartDoc)

    If exportFace Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | Direct selected-face export could not find a planar face on temp part"
        GoTo CleanupExit
    End If

    If Not SelectFaceForDirectDxfExport(tempPartDoc, exportFace) Then
        Debug.Print TimeStamp() & " | FAIL  | Direct selected-face export could not select export face"
        GoTo CleanupExit
    End If

    Dim alignmentData(0 To 11) As Double
    Dim varAlignment As Variant
    BuildDwgAlignmentForSelectedFace alignmentData, xDir, faceNormal, exportFace
    varAlignment = alignmentData

    Debug.Print TimeStamp() & " | INFO  | Direct selected-face export preserveCornerOrientation = " & CStr(preserveCornerOrientation)
    Debug.Print TimeStamp() & " | INFO  | Direct selected-face export using action = swExportToDWG_ExportSelectedFacesOrLoops"

    If TryDirectExportSelectedFaceToDxf(swTempPart, tempPartPath, finalDxfPath, varAlignment) Then
        ExportTempPartToDxf_DirectBySelectedFace = True
    End If

CleanupExit:
    On Error Resume Next
    tempPartDoc.ClearSelection2 True
    On Error GoTo 0
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ExportTempPartToDxf_DirectBySelectedFace: " & Err.Number & " - " & Err.Description
End Function

Private Function TryDirectExportSelectedFaceToDxf(ByVal swTempPart As SldWorks.PartDoc, _
                                                  ByVal modelPath As String, _
                                                  ByVal finalDxfPath As String, _
                                                  ByVal varAlignment As Variant) As Boolean
    On Error GoTo EH

    Dim resultVar As Variant
    Dim ok As Boolean

    TryDirectExportSelectedFaceToDxf = False

    DeleteFileIfExists finalDxfPath

    Debug.Print TimeStamp() & " | INFO  | Direct ExportToDWG2(selected face) start"
    Debug.Print TimeStamp() & " | INFO  |   modelPath    = " & modelPath
    Debug.Print TimeStamp() & " | INFO  |   finalDxfPath = " & finalDxfPath

    On Error Resume Next
    resultVar = CallByName(swTempPart, "ExportToDWG2", VbMethod, _
                           finalDxfPath, _
                           modelPath, _
                           SW_EXPORT_TO_DWG_SELECTED_FACES, _
                           True, _
                           varAlignment, _
                           False, _
                           False, _
                           0, _
                           Empty)
    If Err.Number <> 0 Then
        Debug.Print TimeStamp() & " | WARN  | CallByName ExportToDWG2(selected face) failed: " & Err.Number & " - " & Err.Description
        Err.Clear
        On Error GoTo EH
        Exit Function
    End If
    On Error GoTo EH

    ok = False
    On Error Resume Next
    ok = CBool(resultVar)
    On Error GoTo EH

    Debug.Print TimeStamp() & " | INFO  | ExportToDWG2(selected face) result = " & CStr(ok)

    If Not ok Then Exit Function

    If Not FileExists(finalDxfPath) Then
        Debug.Print TimeStamp() & " | WARN  | ExportToDWG2(selected face) returned True but output file not found"
        Exit Function
    End If

    If GetFileSizeSafe(finalDxfPath) <= 0 Then
        Debug.Print TimeStamp() & " | WARN  | ExportToDWG2(selected face) created zero-byte DXF"
        Exit Function
    End If

    Debug.Print TimeStamp() & " | INFO  | Direct ExportToDWG2(selected face) succeeded. File size = " & CStr(GetFileSizeSafe(finalDxfPath))
    TryDirectExportSelectedFaceToDxf = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | TryDirectExportSelectedFaceToDxf: " & Err.Number & " - " & Err.Description
End Function

Private Sub BuildDwgAlignmentForSelectedFace(ByRef alignmentData() As Double, _
                                             ByRef xAxisIn() As Double, _
                                             ByRef zAxisIn() As Double, _
                                             ByVal exportFace As SldWorks.Face2)
    Dim x(2) As Double
    Dim y(2) As Double
    Dim z(2) As Double
    Dim origin(2) As Double

    x(0) = xAxisIn(0): x(1) = xAxisIn(1): x(2) = xAxisIn(2)
    z(0) = zAxisIn(0): z(1) = zAxisIn(1): z(2) = zAxisIn(2)

    NormalizeVec x
    NormalizeVec z

    ProjectVectorOntoPlane x, z, x
    NormalizeVec x

    CrossProduct z, x, y
    NormalizeVec y

    CrossProduct y, z, x
    NormalizeVec x

    GetFaceRepresentativePoint exportFace, origin

    alignmentData(0) = origin(0)
    alignmentData(1) = origin(1)
    alignmentData(2) = origin(2)

    alignmentData(3) = x(0)
    alignmentData(4) = x(1)
    alignmentData(5) = x(2)

    alignmentData(6) = y(0)
    alignmentData(7) = y(1)
    alignmentData(8) = y(2)

    alignmentData(9) = z(0)
    alignmentData(10) = z(1)
    alignmentData(11) = z(2)

    Debug.Print TimeStamp() & " | INFO  | Selected-face alignment origin = (" & Dbl3ToStr(origin) & ")"
    Debug.Print TimeStamp() & " | INFO  | Selected-face alignment X      = (" & Dbl3ToStr(x) & ")"
    Debug.Print TimeStamp() & " | INFO  | Selected-face alignment Y      = (" & Dbl3ToStr(y) & ")"
    Debug.Print TimeStamp() & " | INFO  | Selected-face alignment Normal = (" & Dbl3ToStr(z) & ")"
End Sub

Private Function GetLargestPlanarFaceFromPartDocument(ByVal mdl As SldWorks.ModelDoc2) As SldWorks.Face2
    On Error GoTo EH

    Dim body As SldWorks.Body2
    Set body = GetFirstUsableBodyFromPartDocument(mdl)

    If body Is Nothing Then Exit Function

    Set GetLargestPlanarFaceFromPartDocument = FindLargestPlanarFace(body)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetLargestPlanarFaceFromPartDocument: " & Err.Number & " - " & Err.Description
End Function

Private Function GetFirstUsableBodyFromPartDocument(ByVal mdl As SldWorks.ModelDoc2) As SldWorks.Body2
    On Error GoTo EH

    Dim p As SldWorks.PartDoc
    Dim vBodies As Variant
    Dim i As Long
    Dim b As SldWorks.Body2

    If mdl Is Nothing Then Exit Function
    If mdl.GetType <> swDocPART Then Exit Function

    Set p = mdl

    vBodies = p.GetBodies2(swSolidBody, True)
    If IsEmpty(vBodies) Then
        vBodies = p.GetBodies2(swAllBodies, True)
    End If
    If IsEmpty(vBodies) Then Exit Function
    If Not IsArray(vBodies) Then Exit Function

    For i = LBound(vBodies) To UBound(vBodies)
        Set b = Nothing
        On Error Resume Next
        Set b = vBodies(i)
        On Error GoTo EH

        If Not b Is Nothing Then
            Set GetFirstUsableBodyFromPartDocument = b
            Exit Function
        End If
    Next i

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetFirstUsableBodyFromPartDocument: " & Err.Number & " - " & Err.Description
End Function

Private Function SelectFaceForDirectDxfExport(ByVal mdl As SldWorks.ModelDoc2, ByVal f As SldWorks.Face2) As Boolean
    On Error GoTo EH

    SelectFaceForDirectDxfExport = False

    If mdl Is Nothing Then Exit Function
    If f Is Nothing Then Exit Function

    mdl.ClearSelection2 True

    On Error Resume Next
    SelectFaceForDirectDxfExport = f.Select4(False, Nothing)
    On Error GoTo EH

    If SelectFaceForDirectDxfExport Then
        Debug.Print TimeStamp() & " | INFO  | Selected planar face for direct DXF export"
    Else
        Debug.Print TimeStamp() & " | WARN  | Face Select4 returned False during direct DXF export"
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | SelectFaceForDirectDxfExport: " & Err.Number & " - " & Err.Description
End Function

Private Sub GetFaceRepresentativePoint(ByVal f As SldWorks.Face2, ByRef p() As Double)
    On Error GoTo EH

    p(0) = 0#
    p(1) = 0#
    p(2) = 0#

    If f Is Nothing Then Exit Sub

    Dim vEdges As Variant
    vEdges = f.GetEdges
    If IsEmpty(vEdges) Then Exit Sub
    If Not IsArray(vEdges) Then Exit Sub

    Dim ed As SldWorks.Edge
    Dim p0(2) As Double
    Dim p1(2) As Double
    Dim i As Long

    For i = LBound(vEdges) To UBound(vEdges)
        Set ed = vEdges(i)
        If Not ed Is Nothing Then
            If GetEdgeEndPoints(ed, p0, p1) Then
                p(0) = p0(0)
                p(1) = p0(1)
                p(2) = p0(2)
                Exit Sub
            End If
        End If
    Next i
    Exit Sub

EH:
    Debug.Print TimeStamp() & " | ERROR | GetFaceRepresentativePoint: " & Err.Number & " - " & Err.Description
End Sub

Private Function ActivateDocumentByTitle(ByVal docTitle As String) As Boolean
    On Error GoTo EH

    ActivateDocumentByTitle = False

    If Len(Trim$(docTitle)) = 0 Then Exit Function

    Dim actErr As Long
    Dim mdl As SldWorks.ModelDoc2

    Set mdl = swApp.ActivateDoc3(docTitle, False, 0, actErr)
    Debug.Print TimeStamp() & " | INFO  | ActivateDoc3 [" & docTitle & "] err=" & actErr

    If Not mdl Is Nothing Then
        ActivateDocumentByTitle = True
        Exit Function
    End If

    If Not swApp.ActiveDoc Is Nothing Then
        If StrComp(swApp.ActiveDoc.GetTitle, docTitle, vbTextCompare) = 0 Then
            ActivateDocumentByTitle = True
        End If
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ActivateDocumentByTitle(" & docTitle & "): " & Err.Number & " - " & Err.Description
End Function

Private Function ForceModelLinearUnitsToInches(ByVal mdl As SldWorks.ModelDoc2) As Boolean
    On Error GoTo EH

    ForceModelLinearUnitsToInches = False

    If mdl Is Nothing Then Exit Function

    Dim beforeUnits As Long
    Dim afterUnits As Long

    On Error Resume Next
    beforeUnits = mdl.GetUserPreferenceIntegerValue(swUnitsLinear)
    Err.Clear
    On Error GoTo EH

    Debug.Print TimeStamp() & " | INFO  | ForceModelLinearUnitsToInches before = " & CStr(beforeUnits)

    mdl.SetUserPreferenceIntegerValue swUnitsLinear, swINCHES

    On Error Resume Next
    afterUnits = mdl.GetUserPreferenceIntegerValue(swUnitsLinear)
    Err.Clear
    On Error GoTo EH

    Debug.Print TimeStamp() & " | INFO  | ForceModelLinearUnitsToInches after  = " & CStr(afterUnits)

    ForceModelLinearUnitsToInches = (afterUnits = swINCHES)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ForceModelLinearUnitsToInches: " & Err.Number & " - " & Err.Description
End Function

Private Function CreateTempPartFromBody(ByVal body As SldWorks.Body2, ByVal tempPartPath As String, Optional ByVal saveImmediately As Boolean = True) As SldWorks.ModelDoc2
    On Error GoTo EH

    Dim partTemplate As String
    partTemplate = GetPartTemplatePath()
    If Len(partTemplate) = 0 Then
        Debug.Print TimeStamp() & " | FAIL  | No default part template available"
        Exit Function
    End If

    Debug.Print TimeStamp() & " | INFO  | Temp part template = " & partTemplate
    Debug.Print TimeStamp() & " | INFO  | Temp part target   = " & tempPartPath

    Dim tempDoc As SldWorks.ModelDoc2
    Set tempDoc = swApp.NewDocument(partTemplate, 0, 0#, 0#)

    If tempDoc Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | New temp part document creation failed"
        Exit Function
    End If

    Dim tempPart As SldWorks.PartDoc
    Set tempPart = tempDoc

    tempDoc.ClearSelection2 True

    Dim newFeat As SldWorks.Feature
    Set newFeat = tempPart.CreateFeatureFromBody3(body, False, swCreateFeatureBodySimplify)

    If newFeat Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | CreateFeatureFromBody3 returned Nothing"
        swApp.CloseDoc tempDoc.GetTitle
        Exit Function
    End If

    tempDoc.ForceRebuild3 True

    If DIRECT_TEMP_PART_FORCE_INCH_UNITS Then
        If ForceModelLinearUnitsToInches(tempDoc) Then
            Debug.Print TimeStamp() & " | INFO  | Temp part units forced to inches for direct DXF export"
        Else
            Debug.Print TimeStamp() & " | WARN  | Temp part units could not be forced to inches"
        End If
    End If

    If Not FAST_MODE_MINIMIZE_EXTRA_REDRAWS Then
        tempDoc.ViewZoomtofit2
    End If

    If saveImmediately Then
        If Not TrySaveModelToPath(tempDoc, tempPartPath) Then
            Debug.Print TimeStamp() & " | FAIL  | All temp part save attempts failed"
            swApp.CloseDoc tempDoc.GetTitle
            Exit Function
        End If

        Debug.Print TimeStamp() & " | INFO  | Temp part saved = " & tempPartPath
    Else
        Debug.Print TimeStamp() & " | INFO  | Temp part created in memory; initial disk save deferred"
    End If

    Set CreateTempPartFromBody = tempDoc
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | CreateTempPartFromBody: " & Err.Number & " - " & Err.Description
End Function

Private Function TrySaveModelToPath(ByVal mdl As SldWorks.ModelDoc2, ByVal filePath As String) As Boolean
    On Error GoTo EH

    TrySaveModelToPath = False

    If mdl Is Nothing Then Exit Function
    If Len(filePath) = 0 Then Exit Function

    DeleteFileIfExists filePath

    Dim actErr As Long
    actErr = 0
    On Error Resume Next
    swApp.ActivateDoc3 mdl.GetTitle, False, swDontRebuildActiveDoc, actErr
    Err.Clear
    On Error GoTo EH

    mdl.ForceRebuild3 True
    mdl.ViewZoomtofit2
    DoEvents

    Dim errs As Long, warns As Long
    errs = 0: warns = 0

    Debug.Print TimeStamp() & " | INFO  | Save attempt 1: Extension.SaveAs"
    If mdl.Extension.SaveAs(filePath, swSaveAsCurrentVersion, swSaveAsOptions_Silent, Nothing, errs, warns) Then
        Debug.Print TimeStamp() & " | INFO  | Save attempt 1 returned True. errs=" & errs & " warns=" & warns
        If FileExists(filePath) Then
            TrySaveModelToPath = True
            Exit Function
        End If
        Debug.Print TimeStamp() & " | WARN  | Save attempt 1 returned True but file not found"
    Else
        Debug.Print TimeStamp() & " | WARN  | Save attempt 1 returned False. errs=" & errs & " warns=" & warns
    End If

    errs = 0: warns = 0
    Debug.Print TimeStamp() & " | INFO  | Save attempt 2: Extension.SaveAs with COPY option"
    If mdl.Extension.SaveAs(filePath, swSaveAsCurrentVersion, swSaveAsOptions_Silent Or swSaveAsOptions_Copy, Nothing, errs, warns) Then
        Debug.Print TimeStamp() & " | INFO  | Save attempt 2 returned True. errs=" & errs & " warns=" & warns
        If FileExists(filePath) Then
            TrySaveModelToPath = True
            Exit Function
        End If
        Debug.Print TimeStamp() & " | WARN  | Save attempt 2 returned True but file not found"
    Else
        Debug.Print TimeStamp() & " | WARN  | Save attempt 2 returned False. errs=" & errs & " warns=" & warns
    End If

    errs = 0: warns = 0
    Debug.Print TimeStamp() & " | INFO  | Save attempt 3: Extension.SaveAs after re-activate"
    actErr = 0
    On Error Resume Next
    swApp.ActivateDoc3 mdl.GetTitle, True, swDontRebuildActiveDoc, actErr
    Err.Clear
    On Error GoTo EH

    mdl.GraphicsRedraw2
    mdl.ViewZoomtofit2
    DoEvents

    If mdl.Extension.SaveAs(filePath, swSaveAsCurrentVersion, swSaveAsOptions_Silent, Nothing, errs, warns) Then
        Debug.Print TimeStamp() & " | INFO  | Save attempt 3 returned True. errs=" & errs & " warns=" & warns
        If FileExists(filePath) Then
            TrySaveModelToPath = True
            Exit Function
        End If
        Debug.Print TimeStamp() & " | WARN  | Save attempt 3 returned True but file not found"
    Else
        Debug.Print TimeStamp() & " | WARN  | Save attempt 3 returned False. errs=" & errs & " warns=" & warns
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | TrySaveModelToPath: " & Err.Number & " - " & Err.Description
End Function

Private Function OrientAndNameTempPartView(ByVal tempDoc As SldWorks.ModelDoc2, _
                                           ByRef xDir() As Double, _
                                           ByRef faceNormal() As Double, _
                                           ByVal viewName As String) As Boolean
    On Error GoTo EH

    OrientAndNameTempPartView = False

    If tempDoc Is Nothing Then Exit Function

    Dim viewXf As SldWorks.MathTransform
    Set viewXf = BuildViewOrientationTransform(xDir, faceNormal)

    If viewXf Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | BuildViewOrientationTransform returned Nothing"
        Exit Function
    End If

    Dim mvObj As Object
    Set mvObj = tempDoc.ActiveView

    If mvObj Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | tempDoc.ActiveView returned Nothing"
        Exit Function
    End If

    Debug.Print TimeStamp() & " | INFO  | Setting current model view orientation"
    On Error Resume Next
    CallByName mvObj, "Orientation3", VbSet, viewXf
    If Err.Number <> 0 Then
        Debug.Print TimeStamp() & " | FAIL  | Setting Orientation3 failed: " & Err.Number & " - " & Err.Description
        Err.Clear
        On Error GoTo EH
        Exit Function
    End If
    On Error GoTo EH

    tempDoc.GraphicsRedraw2
    tempDoc.ViewZoomtofit2

    On Error Resume Next
    tempDoc.DeleteNamedView viewName
    Err.Clear
    On Error GoTo EH

    Debug.Print TimeStamp() & " | INFO  | Naming current view as [" & viewName & "]"
    tempDoc.NameView viewName
    tempDoc.GraphicsRedraw2
    tempDoc.ViewZoomtofit2

    OrientAndNameTempPartView = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | OrientAndNameTempPartView: " & Err.Number & " - " & Err.Description
End Function

Private Function ExportTempPartToDxf_ByNamedViewDrawing(ByVal tempPartDoc As SldWorks.ModelDoc2, _
                                                        ByVal tempPartPath As String, _
                                                        ByVal tempDrwPath As String, _
                                                        ByVal finalDxfPath As String, _
                                                        ByVal modelViewName As String, _
                                                        ByRef xDir() As Double, _
                                                        ByRef faceNormal() As Double, _
                                                        ByVal preserveCornerOrientation As Boolean, _
                                                        Optional ByVal skipTempDrawingDiskSave As Boolean = False, _
                                                        Optional ByVal minimizeExtraRedraws As Boolean = False) As Boolean
    On Error GoTo EH

    ExportTempPartToDxf_ByNamedViewDrawing = False

    Dim drwTemplate As String
    drwTemplate = GetDrawingTemplatePath()
    If Len(drwTemplate) = 0 Then
        Debug.Print TimeStamp() & " | FAIL  | No drawing template available for export"
        Exit Function
    End If

    Dim bbox As Variant
    bbox = GetFirstBodyBoxFromPart(tempPartDoc)

    Dim w As Double, h As Double
    If Not IsEmpty(bbox) Then
        w = Abs(CDbl(bbox(3)) - CDbl(bbox(0))) + 2# * SHEET_MARGIN_M
        h = Abs(CDbl(bbox(4)) - CDbl(bbox(1))) + 2# * SHEET_MARGIN_M
    Else
        w = MIN_SHEET_W_M
        h = MIN_SHEET_H_M
    End If

    If w < MIN_SHEET_W_M Then w = MIN_SHEET_W_M
    If h < MIN_SHEET_H_M Then h = MIN_SHEET_H_M

    Debug.Print TimeStamp() & " | INFO  | Drawing export sheet size (m) = " & FormatNumber(w, 4) & " x " & FormatNumber(h, 4)
    Debug.Print TimeStamp() & " | INFO  | Creating drawing view from model view [" & modelViewName & "]"

    Dim drwDoc As SldWorks.ModelDoc2
    Set drwDoc = swApp.NewDocument(drwTemplate, swDwgPapersUserDefined, w, h)

    If drwDoc Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | New drawing creation failed"
        Exit Function
    End If

    Dim drw As SldWorks.DrawingDoc
    Set drw = drwDoc

    Dim viewX As Double, viewY As Double
    viewX = w / 2#
    viewY = h / 2#

    Dim v As SldWorks.View
    Set v = drw.CreateDrawViewFromModelView3(tempPartPath, modelViewName, viewX, viewY, 0#)

    If v Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | CreateDrawViewFromModelView3 returned Nothing for view [" & modelViewName & "]"
        swApp.CloseDoc drwDoc.GetTitle
        Exit Function
    End If

    On Error Resume Next
    v.UseSheetScale = False
    v.ScaleDecimal = 1#
    On Error GoTo EH

    drwDoc.ForceRebuild3 True
    If Not minimizeExtraRedraws Then
        drwDoc.ViewZoomtofit2
    End If

    If CorrectDrawingViewRoll(v, xDir, faceNormal, preserveCornerOrientation) Then
        drwDoc.ForceRebuild3 True
        If Not minimizeExtraRedraws Then
            drwDoc.ViewZoomtofit2
        End If
    Else
        Debug.Print TimeStamp() & " | WARN  | CorrectDrawingViewRoll did not make a change"
    End If

    If skipTempDrawingDiskSave Then
        Debug.Print TimeStamp() & " | INFO  | Skipping temp drawing disk save for faster legacy-compatible export"
    Else
        If Not TrySaveModelToPath(drwDoc, tempDrwPath) Then
            Debug.Print TimeStamp() & " | WARN  | Temp drawing save failed; continuing DXF save"
        End If
    End If

    Dim errs As Long, warns As Long
    errs = 0: warns = 0
    If drwDoc.Extension.SaveAs(finalDxfPath, swSaveAsCurrentVersion, swSaveAsOptions_Silent, Nothing, errs, warns) Then
        Debug.Print TimeStamp() & " | INFO  | Drawing SaveAs DXF succeeded. errs=" & errs & " warns=" & warns
        If FileExists(finalDxfPath) Then
            ExportTempPartToDxf_ByNamedViewDrawing = True
        End If
    Else
        Debug.Print TimeStamp() & " | FAIL  | Drawing SaveAs DXF failed. errs=" & errs & " warns=" & warns
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ExportTempPartToDxf_ByNamedViewDrawing: " & Err.Number & " - " & Err.Description
End Function

Private Function CorrectDrawingViewRoll(ByVal v As SldWorks.View, _
                                        ByRef xDir() As Double, _
                                        ByRef faceNormal() As Double, _
                                        ByVal preserveCornerOrientation As Boolean) As Boolean
    On Error GoTo EH

    CorrectDrawingViewRoll = False

    If v Is Nothing Then Exit Function

    Dim ang As Double
    If GetModelVectorAngleInView(v, xDir, ang) Then
        Debug.Print TimeStamp() & " | INFO  | View roll correction angle = " & FormatNumber(RadToDeg(ang), 4) & " deg"

        Dim curAngle As Double
        curAngle = 0#
        On Error Resume Next
        curAngle = CDbl(CallByName(v, "Angle", VbGet))
        If Err.Number <> 0 Then
            Debug.Print TimeStamp() & " | WARN  | Could not read View.Angle: " & Err.Number & " - " & Err.Description
            Err.Clear
            On Error GoTo EH
        Else
            CallByName v, "Angle", VbLet, (curAngle - ang)
            If Err.Number <> 0 Then
                Debug.Print TimeStamp() & " | WARN  | Could not write View.Angle: " & Err.Number & " - " & Err.Description
                Err.Clear
                On Error GoTo EH
            Else
                CorrectDrawingViewRoll = True
            End If
        End If
        On Error GoTo EH
    Else
        Debug.Print TimeStamp() & " | WARN  | Could not determine in-view angle for xDir"
    End If

    Dim outline As Variant
    outline = v.GetOutline

    If VariantHasAtLeast4Numbers(outline) Then
        Dim width As Double, height As Double
        width = Abs(CDbl(outline(2)) - CDbl(outline(0)))
        height = Abs(CDbl(outline(3)) - CDbl(outline(1)))

        Debug.Print TimeStamp() & " | INFO  | Post-roll outline W x H = " & FormatNumber(width, 6) & " x " & FormatNumber(height, 6)

        If preserveCornerOrientation Then
            Debug.Print TimeStamp() & " | INFO  | Corner-driven orientation active -> skipping legacy 90-degree width/height auto-rotate"
        ElseIf height > width + EPS Then
            Debug.Print TimeStamp() & " | INFO  | Applying additional +90 deg correction because view is taller than wide"

            Dim curAngle2 As Double
            curAngle2 = 0#
            On Error Resume Next
            curAngle2 = CDbl(CallByName(v, "Angle", VbGet))
            If Err.Number = 0 Then
                CallByName v, "Angle", VbLet, (curAngle2 + PI_VAL / 2#)
                If Err.Number = 0 Then
                    CorrectDrawingViewRoll = True
                Else
                    Debug.Print TimeStamp() & " | WARN  | Secondary 90-degree correction failed: " & Err.Number & " - " & Err.Description
                    Err.Clear
                End If
            Else
                Debug.Print TimeStamp() & " | WARN  | Could not read View.Angle for 90-degree correction: " & Err.Number & " - " & Err.Description
                Err.Clear
            End If
            On Error GoTo EH
        End If
    End If

    Dim vx As Double, vy As Double, vz As Double
    If TransformModelVectorToView(v, faceNormal, vx, vy, vz) Then
        Debug.Print TimeStamp() & " | INFO  | Face normal in view coords = (" & _
                    FormatNumber(vx, 6) & ", " & FormatNumber(vy, 6) & ", " & FormatNumber(vz, 6) & ")"
    End If

    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | CorrectDrawingViewRoll: " & Err.Number & " - " & Err.Description
End Function

Private Function GetModelVectorAngleInView(ByVal v As SldWorks.View, _
                                           ByRef modelVec() As Double, _
                                           ByRef angRad As Double) As Boolean
    On Error GoTo EH

    GetModelVectorAngleInView = False
    angRad = 0#

    Dim vx As Double, vy As Double, vz As Double
    If Not TransformModelVectorToView(v, modelVec, vx, vy, vz) Then Exit Function

    If Abs(vx) <= EPS And Abs(vy) <= EPS Then Exit Function

    angRad = Atn2(vy, vx)
    GetModelVectorAngleInView = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetModelVectorAngleInView: " & Err.Number & " - " & Err.Description
End Function

Private Function TransformModelVectorToView(ByVal v As SldWorks.View, _
                                            ByRef modelVec() As Double, _
                                            ByRef outX As Double, _
                                            ByRef outY As Double, _
                                            ByRef outZ As Double) As Boolean
    On Error GoTo EH

    TransformModelVectorToView = False

    If v Is Nothing Then Exit Function

    Dim mtv As SldWorks.MathTransform
    Set mtv = v.ModelToViewTransform
    If mtv Is Nothing Then Exit Function

    Dim data(2) As Double
    data(0) = modelVec(0)
    data(1) = modelVec(1)
    data(2) = modelVec(2)

    Dim mv As SldWorks.MathVector
    Set mv = swMathUtil.CreateVector(data)
    If mv Is Nothing Then Exit Function

    Dim tv As SldWorks.MathVector
    Set tv = mv.MultiplyTransform(mtv)
    If tv Is Nothing Then Exit Function

    Dim arr As Variant
    arr = tv.ArrayData
    If Not VariantHas3Numbers(arr) Then Exit Function

    outX = CDbl(arr(0))
    outY = CDbl(arr(1))
    outZ = CDbl(arr(2))

    TransformModelVectorToView = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | TransformModelVectorToView: " & Err.Number & " - " & Err.Description
End Function

Private Function VariantHasAtLeast4Numbers(ByVal v As Variant) As Boolean
    On Error GoTo EH

    VariantHasAtLeast4Numbers = False
    If IsEmpty(v) Then Exit Function
    If Not IsArray(v) Then Exit Function

    Dim lb As Long, ub As Long
    lb = LBound(v)
    ub = UBound(v)

    If (ub - lb + 1) < 4 Then Exit Function

    Dim i As Long, tmp As Double
    For i = 0 To 3
        tmp = CDbl(v(lb + i))
    Next i

    VariantHasAtLeast4Numbers = True
    Exit Function

EH:
    VariantHasAtLeast4Numbers = False
End Function

Private Function GetFirstBodyBoxFromPart(ByVal mdl As SldWorks.ModelDoc2) As Variant
    On Error GoTo EH

    Dim p As SldWorks.PartDoc
    Set p = mdl

    Dim vBodies As Variant
    vBodies = p.GetBodies2(swSolidBody, True)

    If IsEmpty(vBodies) Then
        vBodies = p.GetBodies2(swAllBodies, True)
    End If

    If IsEmpty(vBodies) Then Exit Function
    If Not IsArray(vBodies) Then Exit Function

    Dim b As SldWorks.Body2
    Set b = vBodies(LBound(vBodies))

    If b Is Nothing Then Exit Function

    GetFirstBodyBoxFromPart = b.GetBodyBox
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetFirstBodyBoxFromPart: " & Err.Number & " - " & Err.Description
End Function

'=========================================================================================
' TEMPLATE HELPERS
'=========================================================================================
Private Function GetPartTemplatePath() As String
    On Error GoTo EH

    GetPartTemplatePath = Trim$(swApp.GetUserPreferenceStringValue(swDefaultTemplatePart))
    If Len(GetPartTemplatePath) = 0 Then
        GetPartTemplatePath = Trim$(swApp.GetDocumentTemplate(swDocPART, "", 0#, 0#, 0#))
    End If
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetPartTemplatePath: " & Err.Number & " - " & Err.Description
End Function

Private Function GetDrawingTemplatePath() As String
    On Error GoTo EH

    If FileExists(CUSTOM_DRAWING_TEMPLATE) Then
        GetDrawingTemplatePath = CUSTOM_DRAWING_TEMPLATE
        Debug.Print TimeStamp() & " | INFO  | Using custom drawing template = " & GetDrawingTemplatePath
        Exit Function
    End If

    Debug.Print TimeStamp() & " | WARN  | Custom drawing template not found, falling back"

    GetDrawingTemplatePath = Trim$(swApp.GetUserPreferenceStringValue(swDefaultTemplateDrawing))
    If Len(GetDrawingTemplatePath) = 0 Then
        GetDrawingTemplatePath = Trim$(swApp.GetDocumentTemplate(swDocDRAWING, "", 0#, 0#, 0#))
    End If
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetDrawingTemplatePath: " & Err.Number & " - " & Err.Description
End Function

'=========================================================================================
' TEMP PATH HELPERS
'=========================================================================================
Private Function GetLocalTempRoot() As String
    On Error GoTo EH

    Dim baseTemp As String
    baseTemp = Trim$(Environ$("TEMP"))
    If Len(baseTemp) = 0 Then baseTemp = Trim$(Environ$("TMP"))
    If Len(baseTemp) = 0 Then baseTemp = "C:\Temp"

    Dim root As String
    root = baseTemp & "\" & TEMP_PREFIX & SafeNowStamp()

    GetLocalTempRoot = EnsureFolderExists(root)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetLocalTempRoot: " & Err.Number & " - " & Err.Description
    GetLocalTempRoot = ""
End Function

Private Function CleanUserFolderInput(ByVal folderPath As String) As String
    folderPath = Trim$(folderPath)

    If Len(folderPath) = 0 Then
        CleanUserFolderInput = ""
        Exit Function
    End If

    If Left$(folderPath, 1) = """" And Right$(folderPath, 1) = """" Then
        folderPath = Mid$(folderPath, 2, Len(folderPath) - 2)
    End If

    CleanUserFolderInput = folderPath
End Function

Private Function GetDefaultFolderFromActiveModelIfAny() As String
    On Error GoTo EH

    Dim mdl As SldWorks.ModelDoc2
    Set mdl = Nothing

    If swApp Is Nothing Then Exit Function
    Set mdl = swApp.ActiveDoc
    If mdl Is Nothing Then Exit Function

    If mdl.GetType <> swDocPART And mdl.GetType <> swDocASSEMBLY Then Exit Function
    If Len(Trim$(mdl.GetPathName)) = 0 Then Exit Function

    GetDefaultFolderFromActiveModelIfAny = GetFolderFromPath(mdl.GetPathName)
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | GetDefaultFolderFromActiveModelIfAny: " & Err.Number & " - " & Err.Description
End Function

Private Function FolderExists(ByVal folderPath As String) As Boolean
    On Error GoTo EH
    FolderExists = CreateObject("Scripting.FileSystemObject").FolderExists(folderPath)
    Exit Function
EH:
    FolderExists = False
End Function

Private Function BuildSortedModelArray(ByVal rootFolder As String, _
                                       ByVal includeSubfolders As Boolean, _
                                       ByRef fileArray() As String) As Boolean
    On Error GoTo EH

    Dim fileList As Collection
    Dim i As Long

    Set fileList = New Collection
    CollectModelFilesFSO rootFolder, includeSubfolders, fileList

    If fileList.Count = 0 Then
        BuildSortedModelArray = False
        Exit Function
    End If

    ReDim fileArray(1 To fileList.Count)

    For i = 1 To fileList.Count
        fileArray(i) = CStr(fileList(i))
    Next i

    QuickSortStrings fileArray, LBound(fileArray), UBound(fileArray)
    BuildSortedModelArray = True
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | BuildSortedModelArray: " & Err.Number & " - " & Err.Description
    BuildSortedModelArray = False
End Function

Private Sub CollectModelFilesFSO(ByVal rootFolder As String, _
                                 ByVal includeSubfolders As Boolean, _
                                 ByRef fileList As Collection)
    On Error GoTo EH

    Dim fso As Object
    Dim folderObj As Object

    Set fso = CreateObject("Scripting.FileSystemObject")

    If Not fso.FolderExists(rootFolder) Then Exit Sub

    Set folderObj = fso.GetFolder(rootFolder)
    WalkFolderForModelFiles folderObj, includeSubfolders, fileList
    Exit Sub

EH:
    Debug.Print TimeStamp() & " | ERROR | CollectModelFilesFSO: " & Err.Number & " - " & Err.Description
End Sub

Private Sub WalkFolderForModelFiles(ByVal folderObj As Object, _
                                    ByVal includeSubfolders As Boolean, _
                                    ByRef fileList As Collection)
    On Error GoTo EH

    Dim fileObj As Object
    Dim subFolder As Object
    Dim ext As String

    Debug.Print TimeStamp() & " | INFO  | Scanning folder: " & CStr(folderObj.Path)

    For Each fileObj In folderObj.Files
        Dim fileNameOnly As String
        fileNameOnly = CStr(fileObj.Name)

        If SKIP_TEMP_OWNER_FILES_IN_BATCH_SCAN Then
            If Left$(fileNameOnly, 2) = "~$" Then
                Debug.Print TimeStamp() & " | INFO  | Skipping temporary owner file: " & CStr(fileObj.Path)
                GoTo NextFileObj
            End If
        End If

        ext = LCase$(GetExtensionFromPath(CStr(fileObj.Path)))
        If ext = "sldprt" Or ext = "sldasm" Then
            fileList.Add CStr(fileObj.Path)
            Debug.Print TimeStamp() & " | INFO  | Found model file: " & CStr(fileObj.Path)
        End If
NextFileObj:
    Next fileObj

    If includeSubfolders Then
        For Each subFolder In folderObj.SubFolders
            WalkFolderForModelFiles subFolder, True, fileList
        Next subFolder
    End If
    Exit Sub

EH:
    Debug.Print TimeStamp() & " | ERROR | WalkFolderForModelFiles: " & Err.Number & " - " & Err.Description
End Sub

Private Sub QuickSortStrings(ByRef arr() As String, ByVal first As Long, ByVal last As Long)
    Dim low As Long
    Dim high As Long
    Dim midValue As String
    Dim temp As String

    low = first
    high = last
    midValue = arr((first + last) \ 2)

    Do While low <= high
        Do While StrComp(arr(low), midValue, vbTextCompare) < 0
            low = low + 1
        Loop

        Do While StrComp(arr(high), midValue, vbTextCompare) > 0
            high = high - 1
        Loop

        If low <= high Then
            temp = arr(low)
            arr(low) = arr(high)
            arr(high) = temp
            low = low + 1
            high = high - 1
        End If
    Loop

    If first < high Then QuickSortStrings arr, first, high
    If low < last Then QuickSortStrings arr, low, last
End Sub

Private Sub AddResultLine(ByVal statusText As String, _
                          ByVal sourcePath As String, _
                          ByVal sourceLabel As String, _
                          ByVal requestedCfg As String, _
                          ByVal effectiveCfg As String, _
                          ByVal cutListName As String, _
                          ByVal partNumber As String, _
                          ByVal outputPath As String, _
                          ByVal detailText As String)
    On Error Resume Next
    EnsureRunStateReady
    gResultLines.Add statusText & _
        " | SOURCE=" & sourcePath & _
        " | LABEL=" & sourceLabel & _
        " | REQUESTED_CFG=" & requestedCfg & _
        " | EFFECTIVE_CFG=" & effectiveCfg & _
        " | CUTLIST=" & cutListName & _
        " | PART_NUMBER=" & partNumber & _
        " | OUTPUT=" & outputPath & _
        " | DETAIL=" & detailText
End Sub

Private Sub AddCollectionItem(ByVal target As Collection, ByVal textValue As String)
    On Error Resume Next
    If target Is Nothing Then Exit Sub
    target.Add textValue
End Sub

Private Function BuildItemPreview(ByVal items As Collection, ByVal maxItems As Long) As String
    Dim i As Long
    Dim s As String
    Dim shown As Long

    If items Is Nothing Then Exit Function

    For i = 1 To items.Count
        s = s & CStr(items(i)) & vbCrLf
        shown = shown + 1
        If shown >= maxItems Then Exit For
    Next i

    If items.Count > shown Then
        s = s & "... plus " & CStr(items.Count - shown) & " more"
    ElseIf Len(s) > 0 Then
        s = Left$(s, Len(s) - Len(vbCrLf))
    End If

    BuildItemPreview = s
End Function

Private Function WriteBatchReport(ByVal rootFolder As String) As String
    On Error GoTo EH

    Dim reportPath As String
    Dim ff As Integer
    Dim i As Long

    EnsureRunStateReady

    reportPath = rootFolder & "\Waterjet_DXF_Batch_Report_" & SafeNowStamp() & ".txt"

    ff = FreeFile
    Open reportPath For Output As #ff

    Print #ff, "Waterjet DXF Batch Export Report"
    Print #ff, String(100, "=")
    Print #ff, "Created: " & TimeStamp()
    Print #ff, "Run mode: " & gRunMode
    Print #ff, "Root folder: " & rootFolder
    Print #ff, "Include subfolders: " & CStr(INCLUDE_SUBFOLDERS)
    Print #ff, "Open top-level docs read-only: " & CStr(OPEN_TOPLEVEL_DOCS_READ_ONLY)
    Print #ff, "Close docs opened by batch: " & CStr(CLOSE_DOCS_OPENED_BY_BATCH)
    Print #ff, "Force close all tracked batch-opened docs at end: " & CStr(FORCE_CLOSE_ALL_BATCH_OPENED_DOCS_AT_END)
    Print #ff, "Use fast mode: " & CStr(USE_FAST_MODE)
    Print #ff, "Fast mode preserve legacy output flag (informational only): " & CStr(FAST_MODE_PRESERVE_LEGACY_OUTPUT)
    Print #ff, "Fast mode single temp part save: " & CStr(FAST_MODE_SINGLE_TEMP_PART_SAVE)
    Print #ff, "Fast mode skip temp drawing save: " & CStr(FAST_MODE_SKIP_TEMP_DRAWING_SAVE)
    Print #ff, "Direct part DXF enabled: " & CStr(USE_EXPERIMENTAL_FAST_DIRECT_DXF)
    Print #ff, "Direct part allow *Current fallback: " & CStr(EXPERIMENTAL_DIRECT_ALLOW_CURRENT_VIEW_FALLBACK)
    Print #ff, String(100, "-")
    Print #ff, "Top-level model files found: " & gBatchTopLevelFilesFound
    Print #ff, "Top-level model files processed: " & gBatchTopLevelFilesProcessed
    Print #ff, "Top-level assemblies processed: " & gBatchTopLevelAssemblies
    Print #ff, "Top-level parts processed: " & gBatchTopLevelParts
    Print #ff, "Top-level duplicates skipped: " & gBatchTopLevelFilesSkippedDup
    Print #ff, "Top-level open/dispatch failures: " & gBatchTopLevelFilesFailed
    Print #ff, String(100, "-")
    Print #ff, "Assembly component instances seen: " & gTotalComponentInstances
    Print #ff, "Assembly/subassembly nodes visited: " & gTotalAssemblyNodes
    Print #ff, "Unique part+config processed: " & gTotalUniquePartConfigs
    Print #ff, "Part docs processed: " & gTotalPartDocsProcessed
    Print #ff, "Suppressed components skipped: " & gTotalSuppressedSkipped
    Print #ff, "Virtual/unloadable components skipped: " & gTotalVirtualSkipped
    Print #ff, String(100, "-")
    Print #ff, "Cut-list items checked: " & gTotalChecked
    Print #ff, "Eligible (DXF=YES): " & gTotalEligible
    Print #ff, "Exported: " & gTotalExported
    Print #ff, "Skipped: " & gTotalSkipped
    Print #ff, "Duplicate/existing DXF name skips: " & gTotalDuplicateOutputSkipped
    Print #ff, "Failed: " & gTotalFailed
    Print #ff, String(100, "-")
    Print #ff, "Per-item results:"
    Print #ff, ""

    For i = 1 To gResultLines.Count
        Print #ff, CStr(gResultLines(i))
    Next i

    Close #ff
    WriteBatchReport = reportPath
    Exit Function

EH:
    On Error Resume Next
    Close #ff
    On Error GoTo 0
    Debug.Print TimeStamp() & " | ERROR | WriteBatchReport: " & Err.Number & " - " & Err.Description
    WriteBatchReport = ""
End Function

'=========================================================================================
' FILE / PATH HELPERS
'=========================================================================================
Private Function EnsureFolderExists(ByVal folderPath As String) As String
    On Error GoTo EH

    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")

    If Not fso.FolderExists(folderPath) Then
        fso.CreateFolder folderPath
    End If

    If fso.FolderExists(folderPath) Then
        EnsureFolderExists = folderPath
    Else
        EnsureFolderExists = ""
    End If
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | EnsureFolderExists(" & folderPath & "): " & Err.Number & " - " & Err.Description
    EnsureFolderExists = ""
End Function

Private Function GetFolderFromPath(ByVal fullPath As String) As String
    Dim p As Long
    p = InStrRev(fullPath, "\")
    If p > 0 Then
        GetFolderFromPath = Left$(fullPath, p - 1)
    Else
        GetFolderFromPath = ""
    End If
End Function


Private Function BuildExactOutputPath(ByVal folderPath As String, ByVal baseFileNameNoExt As String, ByVal extNoDot As String) As String
    On Error GoTo EH

    BuildExactOutputPath = folderPath & "\" & baseFileNameNoExt & "." & extNoDot
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | BuildExactOutputPath: " & Err.Number & " - " & Err.Description
    BuildExactOutputPath = folderPath & "\" & baseFileNameNoExt & "." & extNoDot
End Function

Private Function TryReserveDxfOutputPath(ByVal outputPath As String, ByRef detailText As String) As Boolean
    On Error GoTo EH

    Dim key As String

    TryReserveDxfOutputPath = False
    detailText = ""

    EnsureRunStateReady

    If Len(Trim$(outputPath)) = 0 Then
        detailText = "Output path is blank"
        Exit Function
    End If

    key = NormalizeKey(outputPath)

    If SKIP_IF_DXF_NAME_ALREADY_EXISTS Then
        If FileExists(outputPath) Then
            detailText = "DXF already exists in output folder"
            Exit Function
        End If
    End If

    If gDictDxfOutputPaths.Exists(key) Then
        detailText = "Duplicate DXF name already encountered in this batch"
        Exit Function
    End If

    gDictDxfOutputPaths.Add key, True
    TryReserveDxfOutputPath = True
    Exit Function

EH:
    detailText = "TryReserveDxfOutputPath error " & Err.Number & ": " & Err.Description
    Debug.Print TimeStamp() & " | ERROR | TryReserveDxfOutputPath: " & Err.Number & " - " & Err.Description
    TryReserveDxfOutputPath = False
End Function

Private Sub ReleaseReservedDxfOutputPath(ByVal outputPath As String)
    On Error Resume Next

    Dim key As String
    EnsureRunStateReady

    key = NormalizeKey(outputPath)
    If Len(key) = 0 Then Exit Sub

    If Not gDictDxfOutputPaths Is Nothing Then
        If gDictDxfOutputPaths.Exists(key) Then gDictDxfOutputPaths.Remove key
    End If
End Sub

Private Function FileExists(ByVal filePath As String) As Boolean
    On Error Resume Next
    FileExists = (Len(Dir$(filePath, vbNormal)) > 0)
End Function

Private Function GetFileSizeSafe(ByVal filePath As String) As Double
    On Error GoTo EH
    GetFileSizeSafe = 0#
    If Not FileExists(filePath) Then Exit Function
    GetFileSizeSafe = CDbl(FileLen(filePath))
    Exit Function
EH:
    GetFileSizeSafe = 0#
End Function

Private Sub DeleteFileIfExists(ByVal filePath As String)
    On Error Resume Next
    If Len(filePath) > 0 Then
        If FileExists(filePath) Then Kill filePath
    End If
End Sub

Private Sub DeleteFolderIfEmpty(ByVal folderPath As String)
    On Error Resume Next
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    If fso.FolderExists(folderPath) Then
        If fso.GetFolder(folderPath).Files.Count = 0 And fso.GetFolder(folderPath).SubFolders.Count = 0 Then
            fso.DeleteFolder folderPath, True
        End If
    End If
End Sub

Private Sub RegisterBatchOpenedDocPath(ByVal filePath As String)
    On Error GoTo EH

    EnsureRunStateReady

    If Len(Trim$(filePath)) = 0 Then Exit Sub

    If gDictBatchOpenedDocPaths Is Nothing Then
        Set gDictBatchOpenedDocPaths = CreateObject("Scripting.Dictionary")
    End If

    If Not gDictBatchOpenedDocPaths.Exists(NormalizeKey(filePath)) Then
        gDictBatchOpenedDocPaths.Add NormalizeKey(filePath), filePath
        Debug.Print TimeStamp() & " | INFO  | Tracking batch-opened doc: " & filePath
    End If
    Exit Sub

EH:
    Debug.Print TimeStamp() & " | WARN  | RegisterBatchOpenedDocPath: " & Err.Number & " - " & Err.Description
End Sub

Private Sub CloseAllTrackedBatchOpenedDocs()
    On Error GoTo EH

    EnsureRunStateReady

    If gDictBatchOpenedDocPaths Is Nothing Then Exit Sub
    If gDictBatchOpenedDocPaths.Count = 0 Then Exit Sub

    Dim keys As Variant
    Dim i As Long
    keys = gDictBatchOpenedDocPaths.Keys

    Debug.Print TimeStamp() & " | INFO  | Closing all tracked batch-opened docs. Count = " & gDictBatchOpenedDocPaths.Count

    For i = UBound(keys) To LBound(keys) Step -1
        Debug.Print TimeStamp() & " | INFO  | Final cleanup close: " & CStr(gDictBatchOpenedDocPaths(keys(i)))
        CloseDocIfOpen CStr(gDictBatchOpenedDocPaths(keys(i)))
    Next i

    Set gDictBatchOpenedDocPaths = CreateObject("Scripting.Dictionary")
    gDictBatchOpenedDocPaths.CompareMode = vbTextCompare
    Exit Sub

EH:
    Debug.Print TimeStamp() & " | WARN  | CloseAllTrackedBatchOpenedDocs: " & Err.Number & " - " & Err.Description
End Sub

' Robust close for temp docs that may not yet have a saved file path.
Private Sub CloseModelDocSafe(ByRef mdl As SldWorks.ModelDoc2)
    On Error Resume Next

    If mdl Is Nothing Then Exit Sub

    Dim ttl As String
    Dim pth As String

    ttl = ""
    pth = ""

    ttl = mdl.GetTitle
    pth = mdl.GetPathName

    If Len(ttl) > 0 Then
        swApp.CloseDoc ttl
    End If

    If Len(pth) > 0 Then
        UnregisterBatchOpenedDocPath pth
    End If

    Set mdl = Nothing
End Sub

' Reopen the temp part from disk after the named view has been saved.
' This makes the direct ExportToDWG2 path use a persisted model state instead of an in-memory temp doc.
Private Function ReopenTempPartForDirectExport(ByRef currentDoc As SldWorks.ModelDoc2, ByVal tempPartPath As String) As SldWorks.ModelDoc2
    On Error GoTo EH

    Set ReopenTempPartForDirectExport = Nothing

    If Len(Trim$(tempPartPath)) = 0 Then Exit Function
    If Not FileExists(tempPartPath) Then
        Debug.Print TimeStamp() & " | FAIL  | ReopenTempPartForDirectExport: temp part file does not exist: " & tempPartPath
        Exit Function
    End If

    Debug.Print TimeStamp() & " | INFO  | Closing in-memory temp part before direct reopen"
    CloseModelDocSafe currentDoc

    Dim errs As Long
    Dim warns As Long
    errs = 0
    warns = 0

    Debug.Print TimeStamp() & " | INFO  | Reopening persisted temp part for direct export: " & tempPartPath
    Set ReopenTempPartForDirectExport = swApp.OpenDoc6(tempPartPath, swDocPART, swOpenDocOptions_Silent, "", errs, warns)

    Debug.Print TimeStamp() & " | INFO  | Reopen temp part result errs=" & errs & " warns=" & warns

    If ReopenTempPartForDirectExport Is Nothing Then
        Debug.Print TimeStamp() & " | FAIL  | ReopenTempPartForDirectExport returned Nothing"
    End If
    Exit Function

EH:
    Debug.Print TimeStamp() & " | ERROR | ReopenTempPartForDirectExport: " & Err.Number & " - " & Err.Description
End Function

Private Sub CloseDocIfOpen(ByVal filePath As String)
    On Error Resume Next
    If Len(filePath) = 0 Then Exit Sub

    Dim openDoc As SldWorks.ModelDoc2
    Set openDoc = swApp.GetOpenDocumentByName(filePath)
    If Not openDoc Is Nothing Then
        swApp.CloseDoc openDoc.GetTitle
        UnregisterBatchOpenedDocPath filePath
        Exit Sub
    End If

    swApp.CloseDoc GetFileNameFromPath(filePath)
    UnregisterBatchOpenedDocPath filePath
End Sub

Private Sub UnregisterBatchOpenedDocPath(ByVal filePath As String)
    On Error Resume Next

    If Len(Trim$(filePath)) = 0 Then Exit Sub
    EnsureRunStateReady

    If gDictBatchOpenedDocPaths Is Nothing Then Exit Sub

    If gDictBatchOpenedDocPaths.Exists(NormalizeKey(filePath)) Then
        gDictBatchOpenedDocPaths.Remove NormalizeKey(filePath)
        Debug.Print TimeStamp() & " | INFO  | Untracked batch-opened doc: " & filePath
    End If
End Sub

Private Function GetFileNameFromPath(ByVal filePath As String) As String
    Dim p As Long
    p = InStrRev(filePath, "\")
    If p > 0 Then
        GetFileNameFromPath = Mid$(filePath, p + 1)
    Else
        GetFileNameFromPath = filePath
    End If
End Function

Private Function SanitizeFileName(ByVal s As String) As String
    Dim badChars As Variant
    Dim i As Long

    s = Trim$(s)
    badChars = Array("\", "/", ":", "*", "?", """", "<", ">", "|")

    For i = LBound(badChars) To UBound(badChars)
        s = Replace$(s, badChars(i), "_")
    Next i

    Do While InStr(s, "__") > 0
        s = Replace$(s, "__", "_")
    Loop

    s = Trim$(s)
    s = TrimDotsAndSpaces(s)

    SanitizeFileName = s
End Function

Private Function TrimDotsAndSpaces(ByVal s As String) As String
    Do While Len(s) > 0 And (Right$(s, 1) = "." Or Right$(s, 1) = " ")
        s = Left$(s, Len(s) - 1)
    Loop
    Do While Len(s) > 0 And (Left$(s, 1) = "." Or Left$(s, 1) = " ")
        s = Mid$(s, 2)
    Loop
    TrimDotsAndSpaces = s
End Function

'=========================================================================================
' VECTOR MATH
'=========================================================================================
Private Function VecLength(ByRef v() As Double) As Double
    VecLength = Sqr(v(0) * v(0) + v(1) * v(1) + v(2) * v(2))
End Function

Private Sub NormalizeVec(ByRef v() As Double)
    Dim L As Double
    L = VecLength(v)
    If L > EPS Then
        v(0) = v(0) / L
        v(1) = v(1) / L
        v(2) = v(2) / L
    End If
End Sub

Private Function DotProduct(ByRef a() As Double, ByRef b() As Double) As Double
    DotProduct = a(0) * b(0) + a(1) * b(1) + a(2) * b(2)
End Function

Private Sub CrossProduct(ByRef a() As Double, ByRef b() As Double, ByRef outV() As Double)
    outV(0) = a(1) * b(2) - a(2) * b(1)
    outV(1) = a(2) * b(0) - a(0) * b(2)
    outV(2) = a(0) * b(1) - a(1) * b(0)
End Sub

Private Sub ProjectVectorOntoPlane(ByRef v() As Double, ByRef n() As Double, ByRef outV() As Double)
    Dim d As Double
    d = DotProduct(v, n)
    outV(0) = v(0) - d * n(0)
    outV(1) = v(1) - d * n(1)
    outV(2) = v(2) - d * n(2)
End Sub

Private Sub StabilizeVectorSign(ByRef v() As Double)
    If Abs(v(0)) > EPS Then
        If v(0) < 0# Then FlipVec v
    ElseIf Abs(v(1)) > EPS Then
        If v(1) < 0# Then FlipVec v
    ElseIf Abs(v(2)) > EPS Then
        If v(2) < 0# Then FlipVec v
    End If
End Sub

Private Sub FlipVec(ByRef v() As Double)
    v(0) = -v(0)
    v(1) = -v(1)
    v(2) = -v(2)
End Sub

Private Function CompareVectorLex(ByRef a() As Double, ByRef b() As Double) As Long
    If Abs(a(0) - b(0)) > EPS Then
        CompareVectorLex = Sgn(a(0) - b(0))
        Exit Function
    End If
    If Abs(a(1) - b(1)) > EPS Then
        CompareVectorLex = Sgn(a(1) - b(1))
        Exit Function
    End If
    If Abs(a(2) - b(2)) > EPS Then
        CompareVectorLex = Sgn(a(2) - b(2))
        Exit Function
    End If
    CompareVectorLex = 0
End Function

Private Function Distance3(ByRef p0() As Double, ByRef p1() As Double) As Double
    Distance3 = Sqr((p1(0) - p0(0)) ^ 2 + (p1(1) - p0(1)) ^ 2 + (p1(2) - p0(2)) ^ 2)
End Function

Private Function Dbl3ToStr(ByRef v() As Double) As String
    Dbl3ToStr = FormatNumber(v(0), 8) & ", " & FormatNumber(v(1), 8) & ", " & FormatNumber(v(2), 8)
End Function

Private Function Atn2(ByVal y As Double, ByVal x As Double) As Double
    If Abs(x) < EPS Then
        If y > 0# Then
            Atn2 = PI_VAL / 2#
        ElseIf y < 0# Then
            Atn2 = -PI_VAL / 2#
        Else
            Atn2 = 0#
        End If
    ElseIf x > 0# Then
        Atn2 = Atn(y / x)
    ElseIf x < 0# Then
        If y >= 0# Then
            Atn2 = Atn(y / x) + PI_VAL
        Else
            Atn2 = Atn(y / x) - PI_VAL
        End If
    End If
End Function

Private Function RadToDeg(ByVal rad As Double) As Double
    RadToDeg = rad * 180# / PI_VAL
End Function

Private Function NormalizeKey(ByVal s As String) As String
    NormalizeKey = UCase$(Trim$(s))
End Function

Private Function MakeCompositeKey(ByVal partA As String, ByVal partB As String) As String
    MakeCompositeKey = NormalizeKey(partA) & "|" & NormalizeKey(partB)
End Function

'=========================================================================================
' STRING / LOG HELPERS
'=========================================================================================
Private Function NzStr(ByVal s As String) As String
    NzStr = Trim$(s & "")
End Function

Private Function TimeStamp() As String
    TimeStamp = Format$(Now, "yyyy-mm-dd hh:nn:ss")
End Function

Private Function SafeNowStamp() As String
    SafeNowStamp = Format$(Now, "yyyymmdd_hhnnss")
End Function
