- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I'm trying to create a script that will help me better visualize circuits in 3D. Basically, you select one or more electrical panels, and temporary generic model lines will get drawn in between all the elements in each circuit on the panel. The problem is that a single circuit may have 10 or more receptacles, and I don't want a line going from the panelboard to each receptacle - I'd like to show them daisy-chained together like when you create a circuit. I set up a nearest-neighbor algorithm and a minimum spanning tree algorithm, but neither really matches the results that you'd see if you tab-select a circuit and add wiring. Here's my current implementation in pyRevit. I have attached screenshots of the results of each.
""" Visualize Connected Circuits
Author: Perry Lackowski
Tested: 2021.1
"""
import sys
from pyrevit import DB, forms, revit, script
from Autodesk.Revit.UI.Selection import ObjectType
from Autodesk.Revit.UI.Selection import ISelectionFilter
from enum import Enum
from rpw import ui
doc = revit.doc
uidoc = revit.uidoc
output = script.get_output()
selectable_categories = [
int(DB.BuiltInCategory.OST_ElectricalEquipment),
int(DB.BuiltInCategory.OST_ElectricalFixtures)
]
def UI_get_equipment():
selected_equipment_ids = uidoc.Selection.GetElementIds()
# If pre-selection, convert ids to elements, and remove anything that
# isn't electrical equipment.
if selected_equipment_ids:
selected_equipment = [doc.GetElement(eId) for eId in selected_equipment_ids]
selected_equipment = [x for x in selected_equipment if x.Category.Id.IntegerValue in selectable_categories]
use_preselected_elements = forms.alert(msg='You currently have {} elements selected. Do you want to proceed with the currently selected item(s)?'.format(len(selected_equipment)),ok=False,yes=True, no=True)
if use_preselected_elements:
return selected_equipment
# If post selection, use the Revit's PickObjects to select items.
# selection_filter limits selection to electrical equipment.
selection_filter = electrical_equipment_filter()
try:
selection_reference = uidoc.Selection.PickObjects(ObjectType.Element, selection_filter, 'Select Electrical Equipment to rename.')
except:
# if selection is aborted, it throws an exception...
sys.exit()
if not selection_reference:
sys.exit()
selected_equipment = [doc.GetElement(r.ElementId) for r in selection_reference]
return selected_equipment
class electrical_equipment_filter(ISelectionFilter):
def __init__(self):
pass
def AllowElement(self, element):
if element.Category.Id.IntegerValue in selectable_categories:
return True
else:
return False
def AllowReference(self, element):
return False
def get_parents_and_children(elem):
parents_and_children = list(elem.MEPModel.GetElectricalSystems())
children = list(elem.MEPModel.GetAssignedElectricalSystems())
parents = []
for es in parents_and_children:
if es.Id not in [x.Id for x in children]:
parents.append(es)
# To get the corresponding elements from the circuit, you can typically use
# circuit.Elements. However, if you're looking at the parent circuit, the
# base element element will be the only item in this list. In parent
# circuits, you'll want to look at the BaseEquipment property.
return parents, children
class Colors(Enum):
Red = DB.Color(255,0,0)
Green = DB.Color(0,128,0)
Blue = DB.Color(70,65,240)
def create_line(start, end):
if start.DistanceTo(end) < 1:
return False
new_line = DB.Line.CreateBound(start,end)
return new_line
def make_shape(lines, color):
new_shape = DB.DirectShape.CreateElement(doc, DB.ElementId(DB.BuiltInCategory.OST_GenericModel))
new_shape.SetShape(lines)
graphic_settings = DB.OverrideGraphicSettings()
graphic_settings.SetProjectionLineColor(color)
graphic_settings.SetProjectionLineWeight(6)
doc.ActiveView.SetElementOverrides(new_shape.Id, graphic_settings)
return new_shape
def set_shape_params(shape, circuit_slot, circuit_name):
shape.get_Parameter(DB.BuiltInParameter.ALL_MODEL_MARK).Set('pyRevit Electrical Circuit Visualization')
shape.get_Parameter(DB.BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).Set('{} - {}'.format(circuit_slot, circuit_name))
from itertools import izip, tee
def pairwise(iterable):
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
# def minimum_spanning_tree(source, points):
# # Create a set to keep track of visited points
# visited = set()
# # Initialize the source point
# visited.add(source)
# # Initialize the lines list
# lines = []
# # Loop until all points have been visited
# while len(visited) < len(points) + 1:
# # Find the closest unvisited point to the visited set
# min_dist = float('inf')
# min_point = None
# for visited_point in visited:
# for neighbor in points:
# if neighbor in visited:
# continue
# dist = visited_point.DistanceTo(neighbor)
# if dist < min_dist:
# min_dist = dist
# min_point = neighbor
# # Add the closest point to the visited set and draw a line to it
# visited.add(min_point)
# new_line = create_line(min_point, visited_point)
# lines.append(new_line)
# return lines
def nearest_neighbor(source, points):
unvisited = set(points)
sorted_points = [source]
while unvisited:
closest = min(unvisited, key=lambda p: p.DistanceTo(sorted_points[-1]))
sorted_points.append(closest)
unvisited.remove(closest)
lines = []
for x, y in pairwise(sorted_points):
line = create_line(x, y)
lines.append(line)
return lines
# def star_connect(source, points):
# lines = []
# for point in points:
# line = create_line(source, point)
# lines.append(line)
# return lines
def visualize_circuits(equipment):
equipment_origin = equipment.GetTransform().Origin
parents, children = get_parents_and_children(equipment)
new_shapes = []
lines = []
# First draw circuits for parents (upstream sources)
for circuit in parents:
circuit_slot = circuit.Name
circuit_name = circuit.get_Parameter(DB.BuiltInParameter.RBS_ELEC_CIRCUIT_NAME).AsString()
line = create_line(equipment_origin, circuit.BaseEquipment.GetTransform().Origin)
if line:
lines.append(line)
if lines:
shape = make_shape(lines, Colors.Red.value)
set_shape_params(shape, circuit_slot, circuit_name)
new_shapes.append(shape)
print('Source Vector: {}'.format(output.linkify(shape.Id)))
children = sorted(children, key=lambda x: x.Name)
# Next draw circuits for children (downstream loads)
for circuit in children:
circuit_slot = circuit.Name
circuit_name = circuit.get_Parameter(DB.BuiltInParameter.RBS_ELEC_CIRCUIT_NAME).AsString()
if 'SPARE' in circuit_name or 'SPACE' in circuit_name:
continue
print('{} - {}'.format(circuit_slot, circuit_name))
load_locations = [elem.GetTransform().Origin for elem in circuit.Elements]
lines = nearest_neighbor(equipment_origin, load_locations)
## Two alternate algorithms for drawing lines:
# lines = minimum_spanning_tree(equipment_origin, load_locations)
# lines = star_connect(equipment_origin, load_locations)
if lines:
shape = make_shape(lines, Colors.Blue.value)
set_shape_params(shape, circuit_slot, circuit_name)
new_shapes.append(shape)
print('Load Vector: {}'.format(output.linkify(shape.Id)))
return new_shapes
selected_equipment = UI_get_equipment()
t = DB.Transaction(doc, "Visualize Electrical Equipment")
t.Start()
all_created_circuits = []
for equipment in selected_equipment:
all_created_circuits.append(visualize_circuits(equipment))
if all_created_circuits:
selection = ui.Selection()
selection.clear()
for shape in all_created_circuits:
selection.add(shape)
selection.update()
t.Commit()
Solved! Go to Solution.