{
  "Uuid": "90b11cd2-d38b-5302-9923-b7834956220c",
  "IsCustomNode": false,
  "Description": "Create finish walls around selected rooms.",
  "Name": "Wall_Finish_Generator",
  "ElementResolver": {
    "ResolutionMap": {}
  },
  "Inputs": [],
  "Outputs": [],
  "Nodes": [
    {
      "ConcreteType": "PythonNodeModels.PythonNode, PythonNodeModels",
      "Code": "# CADAuthority Dynamo Script\n# Safety: open and run this graph in MANUAL mode. Every model-changing action asks for confirmation.\nimport clr\nimport csv\nimport math\nimport os\nimport random\nimport traceback\n\nclr.AddReference(\"RevitAPI\")\nclr.AddReference(\"RevitAPIUI\")\nclr.AddReference(\"RevitServices\")\nclr.AddReference(\"System.Windows.Forms\")\nclr.AddReference(\"System.Drawing\")\nclr.AddReference(\"Microsoft.VisualBasic\")\n\nimport Autodesk.Revit.DB as DB\nfrom Autodesk.Revit.UI import TaskDialog, TaskDialogCommonButtons, TaskDialogResult\nfrom Autodesk.Revit.UI.Selection import ObjectType\nfrom RevitServices.Persistence import DocumentManager\nfrom System.Collections.Generic import List\nfrom System.Drawing import Color\nfrom System.Windows.Forms import OpenFileDialog, SaveFileDialog, FolderBrowserDialog, DialogResult\nfrom Microsoft.VisualBasic import Interaction\n\ndoc = DocumentManager.Instance.CurrentDBDocument\nuiapp = DocumentManager.Instance.CurrentUIApplication\nuidoc = uiapp.ActiveUIDocument\napp = uiapp.Application\n\ndef ask(prompt, default=\"\"):\n    return Interaction.InputBox(prompt, \"CADAuthority Dynamo\", default)\n\ndef confirm(message):\n    result = TaskDialog.Show(\"CADAuthority Dynamo\", message, TaskDialogCommonButtons.Yes | TaskDialogCommonButtons.No)\n    return result == TaskDialogResult.Yes\n\ndef message(text):\n    TaskDialog.Show(\"CADAuthority Dynamo\", str(text))\n\ndef open_file(title, file_filter=\"CSV files (*.csv)|*.csv|All files (*.*)|*.*\"):\n    dialog = OpenFileDialog()\n    dialog.Title = title\n    dialog.Filter = file_filter\n    return dialog.FileName if dialog.ShowDialog() == DialogResult.OK else None\n\ndef save_file(title, default_name, file_filter=\"CSV files (*.csv)|*.csv\"):\n    dialog = SaveFileDialog()\n    dialog.Title = title\n    dialog.Filter = file_filter\n    dialog.FileName = default_name\n    return dialog.FileName if dialog.ShowDialog() == DialogResult.OK else None\n\ndef choose_folder(title):\n    dialog = FolderBrowserDialog()\n    dialog.Description = title\n    return dialog.SelectedPath if dialog.ShowDialog() == DialogResult.OK else None\n\ndef pick_one(prompt):\n    ref = uidoc.Selection.PickObject(ObjectType.Element, prompt)\n    return doc.GetElement(ref.ElementId)\n\ndef pick_many(prompt):\n    refs = uidoc.Selection.PickObjects(ObjectType.Element, prompt)\n    return [doc.GetElement(r.ElementId) for r in refs]\n\ndef selected_elements():\n    return [doc.GetElement(i) for i in uidoc.Selection.GetElementIds()]\n\ndef selected_or_pick(prompt):\n    items = selected_elements()\n    return items if items else pick_many(prompt)\n\ndef element_name(element):\n    try:\n        return element.Name\n    except:\n        try:\n            return DB.Element.Name.GetValue(element)\n        except:\n            return \"\"\n\ndef parameter(element, name):\n    if not element or not name:\n        return None\n    p = element.LookupParameter(name)\n    if p:\n        return p\n    try:\n        return element.get_Parameter(getattr(DB.BuiltInParameter, name))\n    except:\n        return None\n\ndef parameter_text(element, name):\n    p = parameter(element, name)\n    if not p:\n        return \"\"\n    try:\n        value = p.AsString()\n        if value is not None:\n            return value\n    except:\n        pass\n    try:\n        value = p.AsValueString()\n        if value is not None:\n            return value\n    except:\n        pass\n    try:\n        return str(p.AsInteger())\n    except:\n        return \"\"\n\ndef set_parameter(element, name, value):\n    p = parameter(element, name)\n    if not p or p.IsReadOnly:\n        return False\n    try:\n        if p.StorageType == DB.StorageType.String:\n            p.Set(str(value))\n        elif p.StorageType == DB.StorageType.Integer:\n            p.Set(int(float(value)))\n        elif p.StorageType == DB.StorageType.Double:\n            p.Set(float(value))\n        elif p.StorageType == DB.StorageType.ElementId:\n            p.Set(DB.ElementId(int(value)))\n        else:\n            return False\n        return True\n    except:\n        try:\n            p.SetValueString(str(value))\n            return True\n        except:\n            return False\n\ndef transaction(name, action):\n    tx = DB.Transaction(doc, name)\n    tx.Start()\n    try:\n        result = action()\n        tx.Commit()\n        return result\n    except:\n        tx.RollBack()\n        raise\n\ndef csv_rows(path):\n    with open(path, \"r\") as stream:\n        return list(csv.DictReader(stream))\n\ndef write_csv(path, headers, rows):\n    with open(path, \"w\") as stream:\n        writer = csv.writer(stream)\n        writer.writerow(headers)\n        for row in rows:\n            writer.writerow(row)\n\ndef bbox_intersection(a, b):\n    if not a or not b:\n        return None\n    xmin = max(a.Min.X, b.Min.X); ymin = max(a.Min.Y, b.Min.Y); zmin = max(a.Min.Z, b.Min.Z)\n    xmax = min(a.Max.X, b.Max.X); ymax = min(a.Max.Y, b.Max.Y); zmax = min(a.Max.Z, b.Max.Z)\n    if xmin > xmax or ymin > ymax or zmin > zmax:\n        return None\n    return DB.XYZ((xmin+xmax)/2.0, (ymin+ymax)/2.0, (zmin+zmax)/2.0)\n\ndef find_symbol(name, category=None):\n    symbols = DB.FilteredElementCollector(doc).OfClass(DB.FamilySymbol).ToElements()\n    matches = []\n    for symbol in symbols:\n        if category and (not symbol.Category or symbol.Category.Id.IntegerValue != int(category)):\n            continue\n        label = (element_name(symbol.Family) + \": \" + element_name(symbol)).strip()\n        if name.lower() in label.lower():\n            matches.append(symbol)\n    return matches[0] if matches else None\n\ndef activate_symbol(symbol):\n    if symbol and not symbol.IsActive:\n        symbol.Activate()\n        doc.Regenerate()\n\ndef room_loops(room):\n    options = DB.SpatialElementBoundaryOptions()\n    boundaries = room.GetBoundarySegments(options)\n    loops = []\n    for boundary in boundaries:\n        loop = DB.CurveLoop()\n        for segment in boundary:\n            loop.Append(segment.GetCurve())\n        loops.append(loop)\n    return loops\n\ndef xyz_of(element):\n    loc = element.Location\n    if isinstance(loc, DB.LocationPoint):\n        return loc.Point\n    if isinstance(loc, DB.LocationCurve):\n        return loc.Curve.Evaluate(0.5, True)\n    box = element.get_BoundingBox(None)\n    return DB.XYZ((box.Min.X+box.Max.X)/2.0, (box.Min.Y+box.Max.Y)/2.0, (box.Min.Z+box.Max.Z)/2.0) if box else DB.XYZ.Zero\n\ndef run_safe(action):\n    try:\n        return action()\n    except Exception as ex:\n        return \"ERROR: \" + str(ex) + \"\\n\" + traceback.format_exc()\n\ndef action():\n    rooms = [r for r in selected_or_pick(\"Pick rooms, then Finish\") if isinstance(r, DB.Architecture.Room) and r.Area > 0]\n    types = list(DB.FilteredElementCollector(doc).OfClass(DB.WallType).ToElements())\n    if not rooms or not types: return \"Rooms or wall types not found\"\n    type_name = ask(\"Finish wall type contains\", element_name(types[0]))\n    wtype = next((t for t in types if type_name.lower() in element_name(t).lower()), types[0])\n    height = float(ask(\"Wall height in millimetres\", \"2400\") or 2400) / 304.8\n    if not confirm(\"Create finish walls around {0} rooms? Review duplicates first.\".format(len(rooms))): return \"Cancelled\"\n    def change():\n        made = 0\n        options = DB.SpatialElementBoundaryOptions()\n        for room in rooms:\n            boundaries = room.GetBoundarySegments(options)\n            if not boundaries: continue\n            for segment in boundaries[0]:\n                DB.Wall.Create(doc, segment.GetCurve(), wtype.Id, room.LevelId, height, 0.0, False, False); made += 1\n        return made\n    return \"Created {0} finish-wall segments\".format(transaction(\"CADAuthority - Room Finish Walls\", change))\nOUT = run_safe(action)\n",
      "Engine": "CPython3",
      "VariableInputPorts": true,
      "Id": "912565111f6155569d6555450a90fd09",
      "NodeType": "PythonScriptNode",
      "Inputs": [],
      "Outputs": [
        {
          "Id": "6a416373bb0b5195b4bb6089ffe14eac",
          "Name": "OUT",
          "Description": "Operation result",
          "UsingDefaultValue": false,
          "Level": 2,
          "UseLevels": false,
          "KeepListStructure": false
        }
      ],
      "Replication": "Disabled",
      "Description": "Runs the embedded CADAuthority Revit automation script."
    }
  ],
  "Connectors": [],
  "Dependencies": [],
  "NodeLibraryDependencies": [],
  "Thumbnail": "",
  "GraphDocumentationURL": "https://cadauthority.com/free-dynamo-scripts-download-dyn/",
  "ExtensionWorkspaceData": [],
  "Author": "CADAuthority",
  "Linting": {
    "activeLinter": "None",
    "activeLinterId": "00000000-0000-0000-0000-000000000000",
    "warningCount": 0,
    "errorCount": 0
  },
  "Bindings": [],
  "View": {
    "Dynamo": {
      "ScaleFactor": 1.0,
      "HasRunWithoutCrash": true,
      "IsVisibleInDynamoLibrary": true,
      "Version": "2.17.0.3472",
      "RunType": "Manual",
      "RunPeriod": "1000"
    },
    "Camera": {
      "Name": "Background Preview",
      "EyeX": -17.0,
      "EyeY": 24.0,
      "EyeZ": 50.0,
      "LookX": 12.0,
      "LookY": -13.0,
      "LookZ": -58.0,
      "UpX": 0.0,
      "UpY": 1.0,
      "UpZ": 0.0
    },
    "NodeViews": [
      {
        "ShowGeometry": true,
        "Name": "Wall_Finish_Generator",
        "Id": "912565111f6155569d6555450a90fd09",
        "IsSetAsInput": false,
        "IsSetAsOutput": false,
        "Excluded": false,
        "X": 120.0,
        "Y": 120.0
      }
    ],
    "Annotations": [],
    "X": 0.0,
    "Y": 0.0,
    "Zoom": 1.0
  }
}
