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
- Select Elements: Pre-select analytical members in the Revit view
- Launch Tool: Click "Analytical Snap To Level" button on the ribbon
- Pick Level: Select target level from the level picker dialog
- 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
InvalidOperationExceptionif no elements are selected - Throws
InvalidOperationExceptionif 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 snappedSkippedCount: 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 |
Related Documentation
- 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 |