Table of Contents

Analytical Snap To Level

Analytical Snap To Level is a Revit tool that snaps selected analytical model elements (beams and columns) to a user-specified level elevation.

Overview

This tool provides a streamlined workflow for aligning analytical model geometry to structural levels. It supports two structural roles with role-specific snapping behavior:

  • Beams: Both endpoints are moved to the target level elevation
  • Columns: Only the endpoint closest to the target level is moved, preserving the other endpoint's position

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalLevelSnapper.cs:88-119

Features

Feature Description
Selection-Based Operates on pre-selected analytical elements
Level Picker Interactive dialog to choose target level
Role-Aware Snapping Different snap behavior for beams vs columns
Batch Processing Processes multiple elements in a single transaction
Summary Report Displays processed/skipped counts after execution

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalSnapToLevelCommand.cs:19-70

Workflow

  1. Select Elements: Pre-select analytical members in the Revit view
  2. Launch Tool: Click "Analytical Snap To Level" button on the ribbon
  3. Pick Level: Select target level from the level picker dialog
  4. Review Results: View summary showing processed and skipped element counts

Supported Elements

The tool only processes elements that meet these criteria:

  • Category: OST_AnalyticalMember
  • Type: Must be castable to AnalyticalMember
  • Structural Role: "Beam" or "Column" (other roles are skipped)
  • Geometry: Must have a valid curve

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalLevelSnapper.cs:55-81

Snapping Behavior

Beams

Beams are horizontal members. Both endpoints are moved to the target elevation while preserving X and Y coordinates:

Original:   Start(X1, Y1, Z1) ---- End(X2, Y2, Z2)
Snapped:    Start(X1, Y1, TargetElev) ---- End(X2, Y2, TargetElev)

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalLevelSnapper.cs:88-93

Columns

Columns are vertical members. Only the endpoint closest to the target level is moved:

Original:   Top(X, Y, 20) | Bottom(X, Y, 10)
Target at 12: Top(X, Y, 20) | Bottom(X, Y, 12)  [bottom was closer]

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalLevelSnapper.cs:95-112

Architecture

Module Structure

src/Tools/Structural/AnalyticalSnapToLevel/
+-- manifest.yml                              # Tool declaration
+-- AnalyticalSnapToLevelToolModule.cs        # DI module registration
+-- DBTools.Structural.AnalyticalSnapToLevel.csproj  # Project file
+-- Features/
|   +-- AnalyticalSnapToLevelCommand.cs       # Ribbon command entry point
|   +-- AnalyticalLevelSnapper.cs             # Core snapping logic
+-- Assets/
    +-- analytical_snap_icon.png              # Ribbon icon

Source: Directory structure of src/Tools/Structural/AnalyticalSnapToLevel/

Key Classes

Class File Purpose
AnalyticalSnapToLevelToolModule AnalyticalSnapToLevelToolModule.cs:5-7 Empty tool module for registration
AnalyticalSnapToLevelCommand Features/AnalyticalSnapToLevelCommand.cs:17-71 Command entry point; orchestrates workflow
AnalyticalLevelSnapper Features/AnalyticalLevelSnapper.cs:16-135 Core snapping service

Source: src/Tools/Structural/AnalyticalSnapToLevel/AnalyticalSnapToLevelToolModule.cs:5-7

Data Flow

+----------------------------+
| AnalyticalSnapToLevelCommand|
+-------------+--------------+
              |
              | 1. Get selected element IDs
              | 2. Show level picker
              v
    +-------------------+
    | LevelPickerHelper |  (from DBTools.Core)
    +-------------------+
              |
              | Returns selected Level
              v
+----------------------------+
|   AnalyticalLevelSnapper   |
+-------------+--------------+
              |
              | 3. Iterate selected IDs
              | 4. Filter to AnalyticalMember
              | 5. Check structural role
              | 6. Compute new curve
              | 7. SetCurve on member
              v
+----------------------------+
|     Summary Alert          |
+----------------------------+

Dependencies

The tool depends on these core services:

Service Source Purpose
IAlertService DBTools.Core Show level picker and summary dialogs
LevelPickerHelper DBTools.Core Utility for level selection UI
ITransactionRunner DBTools.Core Transaction execution via call gate
ILogger Microsoft.Extensions.Logging Debug logging

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalSnapToLevelCommand.cs:35-46

Settings

This tool has no persistent settings. All configuration is provided at runtime via:

  • Element Selection: User pre-selects elements before invoking the command
  • Target Level: User picks from level dialog at runtime

Manifest

id: DBTools.Structural.AnalyticalSnapToLevel
assembly: DBTools
moduleType: DBTools.Structural.AnalyticalSnapToLevel.AnalyticalSnapToLevelToolModule
order: 0
tool:
  ribbonTools:
    - internalName: DBTools.AnalyticalSnapToLevel
      commandType: DBTools.Structural.AnalyticalSnapToLevel.AnalyticalSnapToLevelCommand
      availabilityType: DBTools.App.Tools.Availability.DbtSelectionAvailability
      runProfile: InlineUi
      displayText: "Analytical Snap\nTo Level"
      iconBaseKey: analytical_snap
      tooltip: "Snap analytical model elements to specified level"
      controlKind: PushButton
      order: 20

Source: src/Tools/Structural/AnalyticalSnapToLevel/manifest.yml:1-15

Manifest Properties

Property Value Description
id DBTools.Structural.AnalyticalSnapToLevel Unique tool identifier
availabilityType DbtSelectionAvailability Button enabled when elements are selected
runProfile InlineUi Runs with inline UI support
controlKind PushButton Standard push button control
order 20 Position in ribbon panel

Availability

The command uses DbtSelectionAvailability, which enables the ribbon button when at least one element is selected:

public class DbtSelectionAvailability : IExternalCommandAvailability
{
    public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
    {
        var selection = uidoc.Selection?.GetElementIds();
        if (selection == null || selection.Count == 0) return false;
        // ...
        return selection.Any(id => /* element matches */);
    }
}

Source: src/DBTools.App/Tools/Availability/DbtSelectionAvailability.cs:13-51

Error Handling

The tool handles errors at multiple levels:

Command Level

  • Throws InvalidOperationException if no elements are selected
  • Throws InvalidOperationException if no target level is selected

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalSnapToLevelCommand.cs:29-40

Snapper Level

Elements are silently skipped (with debug logging) when:

  • Element is null
  • Element category is not OST_AnalyticalMember
  • Element cannot be cast to AnalyticalMember
  • Element has no curve geometry
  • Structural role is not "Beam" or "Column"
  • Any exception occurs during curve modification

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalLevelSnapper.cs:46-129

Logging

The tool logs debug information via ILogger:

// Target level selection
logger.LogDebug("[AnalyticalSnap] Target level: {LevelName} (Elev: {Elevation})", 
    targetLevel.Name, targetLevel.Elevation);

// Skip reasons
_logger.LogDebug("[AnalyticalSnap] Skipping element {ElementId} - not an analytical member.", 
    RevitId.ToInt(elementId));

// Success
_logger.LogDebug("[AnalyticalSnap] Snapped {Role} {ElementId} to level {LevelName}.", 
    role, RevitId.ToInt(elementId), level.Name);

// Summary
logger.LogDebug("[AnalyticalSnap] Processed {Processed}; skipped {Skipped}.", 
    processed, skipped);

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalSnapToLevelCommand.cs:42-52

API Reference

AnalyticalLevelSnapper

The core snapping service:

public sealed class AnalyticalLevelSnapper
{
    public AnalyticalLevelSnapper(ITransactionRunner tx, Document doc, ILogger logger);
    
    public (int ProcessedCount, int SkippedCount) Run(
        ICollection<ElementId> selectedIds, 
        Level targetLevel);
}

Parameters

Parameter Type Description
tx ITransactionRunner Transaction runner for Revit operations
doc Document Active Revit document
logger ILogger Logger for debug output

Return Value

Returns a tuple with:

  • ProcessedCount: Number of elements successfully snapped
  • SkippedCount: Number of elements skipped (invalid category, role, or errors)

Source: src/Tools/Structural/AnalyticalSnapToLevel/Features/AnalyticalLevelSnapper.cs:22-29

Troubleshooting

Issue Cause Resolution
Button disabled No elements selected Select analytical elements before clicking
"No elements selected" error Selection cleared before command ran Re-select elements and try again
High skip count Non-analytical elements in selection Select only analytical members
Elements not moving Role is not "Beam" or "Column" Check element's Structural Role parameter
Partial success Some elements have errors Check debug log for skip reasons
  • SGT Tool - Structural Grid Tool (uses similar snapping concepts)
  • Framing Joins - Related structural framing tool

Source Files Reviewed

File Purpose
manifest.yml Tool declaration
AnalyticalSnapToLevelToolModule.cs Module registration
AnalyticalSnapToLevelCommand.cs Command entry point
AnalyticalLevelSnapper.cs Core snapping logic
DBTools.Structural.AnalyticalSnapToLevel.csproj Project configuration
DbtSelectionAvailability.cs Availability predicate
LevelPickerHelper.cs Level picker utility