Project > Quantities
COG Calculator
Weighs the selected objects from closed-mesh volumes and material densities (or their Weight property), marks the combined centre of gravity in the view and lists every object with its weight, centroid and method. Shift+Click for densities.
Status
Launched Ran inside Navisworks at least once with no error logged; nobody checked the result yet.
Related asks to confirm
No quote is on record yet. These threads look related, but a person still has to open and read them.
Events it listens to
None. It runs only when you click it.
Files
Paths are relative to the Playground.extension folder.
Project.tab/Quantities.panel/COG_Calculator.pushbutton/bundle.yamlProject.tab/Quantities.panel/COG_Calculator.pushbutton/config.pyProject.tab/Quantities.panel/COG_Calculator.pushbutton/icon.dark.pngProject.tab/Quantities.panel/COG_Calculator.pushbutton/icon.pngProject.tab/Quantities.panel/COG_Calculator.pushbutton/icon.small.dark.pngProject.tab/Quantities.panel/COG_Calculator.pushbutton/icon.small.pngProject.tab/Quantities.panel/COG_Calculator.pushbutton/script.py
Install
- Only this tool. In Navisworks, click pyNavis > Playground, search for COG Calculator, tick it and click Install.
- The whole extension. Download
Playground.extension.zip, unzip it, put thePlayground.extensionfolder in%APPDATA%\pyNavis\extensions\, and click pyNavis > Reload. COG Calculator is on the Project tab, Quantities panel.
You need pyNavis 1.3.0 or later.
Tried it? Tell us whether it worked or not. That report is what moves a tool up the status ladder.
ReportSource
The tool's own files, word for word as published in pynavis/Playground. Paths are relative to the Playground.extension folder.
script.py
The script that runs on a click.
Project.tab/Quantities.panel/COG_Calculator.pushbutton/script.py View on GitHub# -*- coding: utf-8 -*-
"""Weighs the selected objects and marks their combined centre of gravity in the view."""
from pynavis import script, toast
from namo import cog, fourd_common, items, progress, ui, units
TITLE = 'COG Calculator'
KEY = 'playground-cog'
def measure(objects, doc_units, values, report):
table, _errors = cog.parse_densities(values.get('densities'))
out = []
for index, obj in enumerate(objects):
report(index / float(len(objects) or 1), 'Measuring %s of %s objects'
% ('{:,}'.format(index + 1), '{:,}'.format(len(objects))))
out.append(cog.measure_object(obj, doc_units, table, values.get('fill_factor') or 0.6,
values.get('default_density')))
return out
def show(doc, results, combined, imperial, doc_units):
unit = units.doc_unit_symbol(doc_units)
volume = 'ft³' if imperial else 'm³'
headers = ['Object', 'Mass', 'Volume (%s)' % volume, 'Density (kg/m³)', 'X (%s)' % unit,
'Y (%s)' % unit, 'Z (%s)' % unit, 'Method']
def copy(selected):
if combined['cog'] is None:
toast.info('No centre of gravity', 'No object has a weight.')
return
text = cog.format_point(combined['cog'])
ui.copy(text)
toast.success('Centre of gravity copied', '%s %s' % (text, unit))
def select(selected):
count = items.select([results[i]['item'] for i in selected], doc=doc)
toast.success('Selected %s' % fourd_common.plural(count, 'object'))
def export(selected):
path = fourd_common.ask_save(doc, 'Centre of gravity', 'xlsx', 'Export the weights')
if path is None:
return
try:
cog.write_xlsx(path, results, combined, imperial, doc_units, 'Centre of gravity')
except (IOError, OSError) as error:
toast.error('Could not write the workbook', 'Close it in Excel, then try again. %s' % error)
return
toast.success('Exported the weights', path)
fourd_common.open_file(path)
def remove_marker(selected):
cog.clear_marker()
toast.success('Removed the COG marker')
if combined['cog'] is not None:
status = 'Total %s, centre of gravity %s %s' % (cog.format_mass(combined['mass'], imperial),
cog.format_point(combined['cog']), unit)
else:
status = 'No object has a weight.'
actions = [{'label': 'Copy COG', 'fn': copy, 'needs_selection': False, 'primary': True},
{'label': 'Select objects', 'fn': select},
{'label': 'Export to Excel', 'fn': export, 'needs_selection': False},
{'label': 'Remove marker', 'fn': remove_marker, 'needs_selection': False}]
ui.Modeless.close(KEY)
ui.table_window('Centre of gravity', headers, cog.result_rows(results, imperial, doc_units),
actions=actions, modeless=True, key=KEY, status=status, width=980)
def run():
if ui.is_runner():
toast.info('%s works on the selection' % TITLE, 'Run it from the ribbon.')
return
doc = fourd_common.active_doc()
# Stop the walk one past the limit: a whole-model selection must not be walked in full
# just to be refused.
objects = items.objects(items.selected(doc), budget=cog.MAX_OBJECTS + 1)
if not objects:
toast.info('Nothing selected', 'Select the objects to weigh, then run COG Calculator.')
return
if len(objects) > cog.MAX_OBJECTS:
toast.info('Too many objects selected', 'Select at most %s objects; more are selected.'
% '{:,}'.format(cog.MAX_OBJECTS))
return
values = script.get_config(cog.DEFAULTS)
doc_units = str(doc.Units)
imperial = units.is_imperial(doc_units)
results = progress.run(TITLE, lambda report: measure(objects, doc_units, values, report))
if results is progress.CANCELLED:
return
combined = cog.combine([(r['mass_kg'], r['centroid']) for r in results])
estimated = len([r for r in results if r['method'] == 'bbox'])
weightless = len([r for r in results if r['mass_kg'] is None])
notes = []
if estimated:
notes.append('%s estimated from bounding boxes' % fourd_common.plural(estimated, 'object'))
if weightless:
notes.append('%s without a weight' % fourd_common.plural(weightless, 'object'))
if combined['cog'] is None:
toast.warning('No weight found', 'No object matched a density or carries a weight. '
'Shift+Click to set densities or a default.')
else:
to_m = units.to_meters(1.0, doc_units)
drawn = cog.show_marker(combined['cog'], combined['mass'], imperial,
cog.marker_size(items.bbox(objects), to_m))
detail = '%s %s' % (cog.format_point(combined['cog']), units.doc_unit_symbol(doc_units))
if not drawn:
notes.append('the marker could not be drawn in the view')
if notes:
detail += '; ' + ', '.join(notes)
message = 'Centre of gravity of %s, %s' % (fourd_common.plural(combined['count'], 'object'),
cog.format_mass(combined['mass'], imperial))
if notes:
toast.warning(message, detail)
else:
toast.success(message, detail)
show(doc, results, combined, imperial, doc_units)
run()
config.py
The Shift+Click action.
Project.tab/Quantities.panel/COG_Calculator.pushbutton/config.py View on GitHub# -*- coding: utf-8 -*-
"""Densities, the bounding-box fill factor and the COG marker for COG Calculator."""
from pynavis import script, toast
from namo import cog, ui
def validate_table(text):
_table, errors = cog.parse_densities(text)
return errors[0] if errors else None
def run():
if ui.is_runner():
return
values = script.get_config(cog.DEFAULTS)
fields = [
{'kind': 'textarea', 'key': 'densities', 'label': 'Densities in kg/m³, one material word per line',
'rows': 8, 'default': values.get('densities') or cog.DEFAULTS['densities'], 'validate': validate_table,
'hint': "The first word found in an object's material (else its category) sets its density."},
{'kind': 'number', 'key': 'default_density', 'label': 'Density when nothing matches (blank: leave it out)',
'min': 1, 'default': values.get('default_density'), 'suffix': 'kg/m³'},
{'kind': 'number', 'key': 'fill_factor', 'label': 'Fill factor for bounding-box estimates', 'min': 0.01,
'max': 1, 'default': values.get('fill_factor') or 0.6,
'hint': 'Used when a mesh is not closed: the box volume times this share.'},
{'kind': 'check', 'key': 'clear', 'label': 'Remove the COG marker from the view now', 'default': False},
]
answer = ui.form('COG Calculator settings', fields, ok='Save', width=520)
if answer is None:
return
values.update({'densities': answer['densities'], 'default_density': answer['default_density'],
'fill_factor': answer['fill_factor'] or 0.6})
script.save_config(values)
if answer['clear']:
cog.clear_marker()
toast.success('Saved, and removed the COG marker')
else:
toast.success('Saved the COG settings', '%d materials in the density table.'
% len(cog.parse_densities(values['densities'])[0]))
run()
Library modules this tool uses
From the shared namo library, which installs with every tool. Each name opens the module on GitHub.
namo.cogCentre of gravity and weight of selected objects.namo.fourd_commonHelpers the 4D, Quantities and Field tools share: scopes, default files, text.namo.itemsModel items: walking, collecting, identifying, measuring and showing them.namo.progressGlue between pyNavis' cancellable progress window and Playground's progress callbacks.namo.uiWPF building blocks for Playground tools, themed from the pyNavis design system.namo.unitsUnits: document lengths, conversions, and quantities as people and Revit write them.
bundle.yaml
The bundle contract: the keys pyNavis reads for this tool.
Project.tab/Quantities.panel/COG_Calculator.pushbutton/bundle.yaml View on GitHubtitle: COG\nCalculator
tooltip: Weighs the selected objects from closed-mesh volumes and material densities (or their Weight property), marks the combined centre of gravity in the view and lists every object with its weight, centroid and method. Shift+Click for densities.
context: selection