{ "Uuid": "c653f2d9-ecac-465e-962b-6bc31aed293b", "IsCustomNode": false, "Description": "", "Name": "37_beams_joists_automatic", "ElementResolver": { "ResolutionMap": {} }, "Inputs": [], "Outputs": [], "Nodes": [ { "ConcreteType": "PythonNodeModels.PythonNode, PythonNodeModels", "Code": "# -*- coding: utf-8 -*-\r\n# 37 — Vigas y trabes (Structural Framing) desde CSV: tramos sueltos + trabes en cuadrícula.\r\n#\r\n# IN[0] : tabla o ruta .csv. Columna obligatoria Mode por fila de datos:\r\n# Mode=BEAM → FramingTypeName, LevelName, X1_mm, Y1_mm, X2_mm, Y2_mm\r\n# Mode=JOIST_AUTO → FramingTypeName, LevelName, Pa_x_mm, Pa_y_mm, Pb_x_mm, Pb_y_mm,\r\n# Qa_x_mm, Qa_y_mm, Qb_x_mm, Qb_y_mm, Spacing_mm [, FirstOffset_mm]\r\n# Genera trabes perpendiculares al vano entre dos bordes paralelos (mismo sentido que 22: mm → pies).\r\n#\r\n# Salida: elementos creados / errores.\r\n\r\nimport sys\r\nimport csv\r\nimport os\r\n\r\nimport clr\r\nclr.AddReference('RevitAPI')\r\nclr.AddReference('RevitServices')\r\nfrom Autodesk.Revit.DB import (\r\n FilteredElementCollector,\r\n FamilySymbol,\r\n Level,\r\n Line,\r\n XYZ,\r\n LocationCurve,\r\n BuiltInCategory,\r\n Category,\r\n)\r\nfrom Autodesk.Revit.DB.Structure import StructuralType\r\nfrom RevitServices.Persistence import DocumentManager\r\nfrom RevitServices.Transactions import TransactionManager\r\n\r\ndoc = DocumentManager.Instance.CurrentDBDocument\r\nMM_TO_FT = 1.0 / 304.8\r\n_PARALLEL_TOL = 0.995\r\n_PY3 = sys.version_info[0] >= 3\r\n\r\n\r\ndef _ustr(value):\r\n if value is None:\r\n return ''\r\n if _PY3:\r\n return str(value).strip()\r\n try:\r\n if isinstance(value, unicode):\r\n return value.strip()\r\n except NameError:\r\n pass\r\n return str(value).strip()\r\n\r\n\r\ndef _exc_str(ex):\r\n if _PY3:\r\n return str(ex)\r\n try:\r\n return unicode(ex)\r\n except NameError:\r\n return str(ex)\r\n\r\n\r\ndef _to_py_list(obj, max_index=256):\r\n if obj is None:\r\n return []\r\n if isinstance(obj, (list, tuple)):\r\n return list(obj)\r\n try:\r\n return list(obj)\r\n except TypeError:\r\n pass\r\n out = []\r\n for i in range(max_index):\r\n try:\r\n out.append(obj[i])\r\n except Exception:\r\n break\r\n return out\r\n\r\n\r\ndef _dynamo_in_ports():\r\n raw = globals().get('IN', [])\r\n ports = _to_py_list(raw, max_index=32)\r\n if len(ports) == 0 and raw is not None:\r\n tmp = []\r\n for i in range(32):\r\n try:\r\n tmp.append(raw[i])\r\n except Exception:\r\n break\r\n ports = tmp\r\n return ports\r\n\r\n\r\ndef _table_rows_from_in0(raw):\r\n if raw is None:\r\n return []\r\n if isinstance(raw, (list, tuple)) and len(raw) > 0:\r\n if isinstance(raw[0], (list, tuple)):\r\n return [list(r) for r in raw]\r\n sp = _ustr(raw)\r\n if not sp or not os.path.isfile(sp) or not sp.lower().endswith('.csv'):\r\n return []\r\n out = []\r\n try:\r\n if _PY3:\r\n fobj = open(sp, 'r', encoding='utf-8-sig', newline='')\r\n else:\r\n import codecs\r\n fobj = codecs.open(sp, 'r', 'utf-8-sig')\r\n try:\r\n for row in csv.reader(fobj):\r\n out.append(list(row))\r\n finally:\r\n fobj.close()\r\n except Exception:\r\n return []\r\n return out\r\n\r\n\r\ndef _hdr(row):\r\n d = {}\r\n for i, x in enumerate(row):\r\n if x is None:\r\n continue\r\n name = _ustr(x)\r\n if len(name) > 0 and ord(name[0]) == 0xFEFF:\r\n name = name[1:].lstrip()\r\n if name:\r\n d[name] = i\r\n return d\r\n\r\n\r\ndef _cell(row, h, k):\r\n if k not in h:\r\n return None\r\n i = h[k]\r\n if i >= len(row):\r\n return None\r\n return row[i]\r\n\r\n\r\ndef _float_cell(row, h, key, default=None):\r\n v = _cell(row, h, key)\r\n if v is None or _ustr(v) == '':\r\n return default\r\n try:\r\n return float(v)\r\n except Exception:\r\n return None\r\n\r\n\r\n_ports = _dynamo_in_ports()\r\nrows = _table_rows_from_in0(_ports[0]) if len(_ports) > 0 else []\r\nlv_by_name = {_ustr(l.Name): l for l in FilteredElementCollector(doc).OfClass(Level).ToElements()}\r\nbeam_cat = Category.GetCategory(doc, BuiltInCategory.OST_StructuralFraming)\r\nsyms = []\r\nif beam_cat is not None:\r\n for s in FilteredElementCollector(doc).OfClass(FamilySymbol).ToElements():\r\n if s.Category is not None and s.Category.Id == beam_cat.Id and s.IsActive:\r\n syms.append(s)\r\nby_name = {_ustr(s.Name): s for s in syms}\r\n\r\n\r\ndef _place_beam(sym, lvl, p0, p1, created, errors, label):\r\n z = lvl.Elevation\r\n p0z = XYZ(p0.X, p0.Y, z)\r\n p1z = XYZ(p1.X, p1.Y, z)\r\n try:\r\n beam = doc.Create.NewFamilyInstance(p0z, sym, lvl, StructuralType.Beam)\r\n loc = beam.Location\r\n if isinstance(loc, LocationCurve):\r\n loc.Curve = Line.CreateBound(p0z, p1z)\r\n created.append(beam)\r\n except Exception as ex:\r\n errors.append(label + ': ' + _exc_str(ex))\r\n\r\n\r\nif not rows or len(rows) < 2:\r\n OUT = ['Error: tabla vacía o IN[0] no es tabla ni ruta .csv válida.']\r\nelse:\r\n h = _hdr(rows[0])\r\n if 'Mode' not in h:\r\n OUT = ['Falta columna Mode (BEAM | JOIST_AUTO).']\r\n else:\r\n created = []\r\n errors = []\r\n TransactionManager.Instance.EnsureInTransaction(doc)\r\n try:\r\n for r in rows[1:]:\r\n mode = _ustr(_cell(r, h, 'Mode')).upper()\r\n if mode == '' or mode.startswith('#'):\r\n continue\r\n fn = _ustr(_cell(r, h, 'FramingTypeName'))\r\n lvn = _ustr(_cell(r, h, 'LevelName'))\r\n if fn not in by_name:\r\n errors.append('Tipo no encontrado (activo): ' + fn)\r\n continue\r\n if lvn not in lv_by_name:\r\n errors.append('Nivel no encontrado: ' + lvn)\r\n continue\r\n lvl = lv_by_name[lvn]\r\n sym = by_name[fn]\r\n\r\n if mode == 'BEAM':\r\n req = ['X1_mm', 'Y1_mm', 'X2_mm', 'Y2_mm']\r\n if any(x not in h for x in req):\r\n errors.append('BEAM: faltan columnas ' + ', '.join(req))\r\n continue\r\n try:\r\n x1 = float(_cell(r, h, 'X1_mm')) * MM_TO_FT\r\n y1 = float(_cell(r, h, 'Y1_mm')) * MM_TO_FT\r\n x2 = float(_cell(r, h, 'X2_mm')) * MM_TO_FT\r\n y2 = float(_cell(r, h, 'Y2_mm')) * MM_TO_FT\r\n except Exception:\r\n errors.append('BEAM: coordenadas inválidas')\r\n continue\r\n z0 = lvl.Elevation\r\n _place_beam(sym, lvl, XYZ(x1, y1, z0), XYZ(x2, y2, z0), created, errors, fn)\r\n\r\n elif mode == 'JOIST_AUTO':\r\n jcols = [\r\n 'Pa_x_mm', 'Pa_y_mm', 'Pb_x_mm', 'Pb_y_mm',\r\n 'Qa_x_mm', 'Qa_y_mm', 'Qb_x_mm', 'Qb_y_mm', 'Spacing_mm',\r\n ]\r\n if any(x not in h for x in jcols):\r\n errors.append('JOIST_AUTO: faltan ' + ', '.join(jcols))\r\n continue\r\n try:\r\n pax = float(_cell(r, h, 'Pa_x_mm')) * MM_TO_FT\r\n pay = float(_cell(r, h, 'Pa_y_mm')) * MM_TO_FT\r\n pbx = float(_cell(r, h, 'Pb_x_mm')) * MM_TO_FT\r\n pby = float(_cell(r, h, 'Pb_y_mm')) * MM_TO_FT\r\n qax = float(_cell(r, h, 'Qa_x_mm')) * MM_TO_FT\r\n qay = float(_cell(r, h, 'Qa_y_mm')) * MM_TO_FT\r\n qbx = float(_cell(r, h, 'Qb_x_mm')) * MM_TO_FT\r\n qby = float(_cell(r, h, 'Qb_y_mm')) * MM_TO_FT\r\n spacing_mm = float(_cell(r, h, 'Spacing_mm'))\r\n except Exception:\r\n errors.append('JOIST_AUTO: número inválido')\r\n continue\r\n if spacing_mm <= 0:\r\n errors.append('JOIST_AUTO: Spacing_mm debe ser > 0')\r\n continue\r\n off_mm = _float_cell(r, h, 'FirstOffset_mm', 0.0)\r\n if off_mm is None:\r\n off_mm = 0.0\r\n spacing_ft = spacing_mm * MM_TO_FT\r\n off_ft = off_mm * MM_TO_FT\r\n\r\n z0 = lvl.Elevation\r\n pa = XYZ(pax, pay, z0)\r\n pb = XYZ(pbx, pby, z0)\r\n qa = XYZ(qax, qay, z0)\r\n qb = XYZ(qbx, qby, z0)\r\n va = pb - pa\r\n vb = qb - qa\r\n la = va.GetLength()\r\n lb = vb.GetLength()\r\n if la < 1e-9 or lb < 1e-9:\r\n errors.append('JOIST_AUTO: borde demasiado corto')\r\n continue\r\n ila = 1.0 / la\r\n ilb = 1.0 / lb\r\n da = XYZ(va.X * ila, va.Y * ila, va.Z * ila)\r\n db = XYZ(vb.X * ilb, vb.Y * ilb, vb.Z * ilb)\r\n if abs(da.DotProduct(db)) < _PARALLEL_TOL:\r\n errors.append('JOIST_AUTO: bordes Pa–Pb y Qa–Qb deben ser paralelos')\r\n continue\r\n t = off_ft\r\n n = 0\r\n while t <= la + 1e-6:\r\n p1 = pa + XYZ(da.X * t, da.Y * t, da.Z * t)\r\n p2 = qa + XYZ(db.X * (t * lb * ila), db.Y * (t * lb * ila), db.Z * (t * lb * ila))\r\n _place_beam(sym, lvl, p1, p2, created, errors, fn + ' #' + str(n))\r\n t += spacing_ft\r\n n += 1\r\n if n > 10000:\r\n errors.append('JOIST_AUTO: demasiadas piezas (revisa Spacing_mm)')\r\n break\r\n else:\r\n errors.append('Mode desconocido: ' + mode)\r\n finally:\r\n TransactionManager.Instance.TransactionTaskDone()\r\n OUT = created + (['ERRORES:'] + errors if errors else [])\r\n# --- RÜM: mensaje de cierre (URL en rum_platform_url.py) ---\r\ntry:\r\n import sys as _rum_sys\r\n _rum_root = r'c:\\RUM_Platform\\RUM_Tools\\Dynamo_Routines'\r\n if _rum_root not in _rum_sys.path:\r\n _rum_sys.path.insert(0, _rum_root)\r\n import rum_finalize as _rum_fin\r\n OUT = _rum_fin.apply(OUT)\r\nexcept Exception:\r\n pass\r\n", "Engine": "CPython3", "VariableInputPorts": true, "Id": "1a2a1335b61999e6a674297d8b0f9e73", "NodeType": "PythonScriptNode", "Inputs": [ { "Id": "7c0b0f4eef4a62dda08b35e445d7214c", "Name": "IN[0]", "Description": "Tabla o ruta .csv (File Path)", "UsingDefaultValue": false, "Level": 2, "UseLevels": false, "KeepListStructure": false } ], "Outputs": [ { "Id": "da8a1d984dd97ac97b0aed1d540e845b", "Name": "OUT", "Description": "Result of the python script", "UsingDefaultValue": false, "Level": 2, "UseLevels": false, "KeepListStructure": false } ], "Replication": "Disabled", "Description": "Runs an embedded Python script." }, { "ConcreteType": "CoreNodeModels.Input.Filename, CoreNodeModels", "Id": "255a3369ab316d84657ca780f6211527", "NodeType": "ExtensionNode", "Inputs": [], "Outputs": [ { "Id": "196aedb445db525c29584c557ada5d20", "Name": "", "Description": "File Path", "UsingDefaultValue": false, "Level": 2, "UseLevels": false, "KeepListStructure": false } ], "Replication": "Disabled", "Description": "Allows you to select a file on the system and returns its file path", "HintPath": "C:\\RUM_Platform\\RUM_Tools\\Dynamo_Routines\\37_beams_joists_automatic\\template_beams_joists_auto.csv", "InputValue": ".\\template_beams_joists_auto.csv" } ], "Connectors": [ { "Start": "196aedb445db525c29584c557ada5d20", "End": "7c0b0f4eef4a62dda08b35e445d7214c", "Id": "868399b7c723a03e766da436ba03bdb3", "IsHidden": "False" } ], "Dependencies": [], "NodeLibraryDependencies": [], "EnableLegacyPolyCurveBehavior": true, "Thumbnail": "", "GraphDocumentationURL": null, "ExtensionWorkspaceData": [ { "ExtensionGuid": "28992e1d-abb9-417f-8b1b-05e053bee670", "Name": "Properties", "Version": "3.3", "Data": {} }, { "ExtensionGuid": "DFBD9CC0-DB40-457A-939E-8C8555555A9D", "Name": "Generative Design", "Version": "8.2", "Data": {} } ], "Author": "RÜM", "Linting": { "activeLinter": "None", "activeLinterId": "7b75fb44-43fd-4631-a878-29f4d5d8399a", "warningCount": 0, "errorCount": 0 }, "Bindings": [], "View": { "Dynamo": { "ScaleFactor": 1, "HasRunWithoutCrash": false, "IsVisibleInDynamoLibrary": true, "Version": "3.3.0.6316", "RunType": "Manual", "RunPeriod": "1000" }, "Camera": { "Name": "_Background Preview", "EyeX": 0, "EyeY": 0, "EyeZ": 10, "LookX": 0, "LookY": 0, "LookZ": 0, "UpX": 0, "UpY": 1, "UpZ": 0 }, "ConnectorPins": [], "Annotations": [], "X": 0, "Y": 0, "Zoom": 0.75, "NodeViews": [ { "Id": "255a3369ab316d84657ca780f6211527", "Name": "CSV / ruta", "IsSetAsInput": false, "IsSetAsOutput": false, "Excluded": false, "ShowGeometry": true, "X": 40, "Y": 320 }, { "Id": "1a2a1335b61999e6a674297d8b0f9e73", "Name": "RÜM 37 Vigas y trabes auto CSV", "IsSetAsInput": false, "IsSetAsOutput": false, "Excluded": false, "ShowGeometry": true, "X": 520, "Y": 300 } ] } }