Skip to content

Instantly share code, notes, and snippets.

@moloneymb
Last active March 3, 2023 09:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save moloneymb/4561a548a3378bc2775f to your computer and use it in GitHub Desktop.
Save moloneymb/4561a548a3378bc2775f to your computer and use it in GitHub Desktop.
// Based on the SDK Sampl GeometryCreation_BooleanOperations
// This is testing code. Many improvements can be made before use in real work
#r @"C:\Program Files\Autodesk\Revit 2016\RevitAPI.dll"
#r @"C:\Program Files\Autodesk\Revit 2016\RevitDBAPI.dll"
#r @"C:\Program Files\Autodesk\Revit 2016\RevitAPIUI.dll"
#r @"C:\Program Files\Mantis\Tsunami.IDEDesktop.dll"
open Autodesk.Revit
open Autodesk.Revit.UI
open System
open System.Linq
open System.Collections.Generic
open Autodesk.Revit.DB
open Autodesk.Revit.DB.Analysis
open Autodesk.Revit.DB.Architecture
open Autodesk.Revit.UI
open Autodesk.Revit.UI.Selection
open Autodesk.Revit.ApplicationServices
open Autodesk.Revit.Attributes
open System.Collections.Generic
open System.Collections.Concurrent
let memoize f =
let cache = new ConcurrentDictionary<'a,'b>()
fun x ->
match cache.TryGetValue(x) with
| (true,y) -> y
| (false,_) ->
let y = f x
cache.[x] <- y
y
module Option =
/// nullable
let ofNullable (x : 'a) = if box x <> null then Some x else None
/// System.Nullable
let ofSystemNullable (x : System.Nullable<'a>) = if x.HasValue then Some x.Value else None
let tryGetAll (xs:'a option []) =
if xs |> Array.exists (function | None -> true | _ -> false)
then None
else xs |> Array.map (Option.get) |> Some
let tryNull (x:'a) = if box x <> null then Some(x) else None
let ofType<'Out> : obj -> 'Out option = function | :? 'Out as x -> Some x | _ -> None
let tryNullOfType<'Out>(x:obj) : 'Out option = x |> tryNull |> ofType<'Out>
let ofUnrelatedType<'In, 'Out> (x : 'In) = box x |> ofType<'Out>
let isOfType<'a> (x:obj) = match x with | :? 'a -> true | _ -> false
let orElse (second:'a option) (first:'a option) = match first with | Some(x) -> Some(x) | None -> match second with | Some(y) -> Some(y) | None -> None
let tryFind (predicate:'a -> bool) (x:'a option) = match x with | Some(x) when predicate x -> Some(x) | _ -> None
let orDefault (d:'a) (x:'a option) = match x with | Some(x) -> x | None -> d
let ofRef (result:bool,byref:'a) = if result then Some(byref) else None
module Seq =
let ofType<'Out> = Seq.choose (Option.ofType<'Out>)
let ofUnrelatedType<'In, 'Out> : 'In seq -> 'Out seq = Seq.choose (Option.ofUnrelatedType<'In, 'Out>)
let tryLast (xs: 'a seq) = xs |> Seq.fold (fun _ x -> Some x) None
let tryHead (xs:'a seq) =
let ys = xs.GetEnumerator()
if ys.MoveNext() then Some(ys.Current) else None
module BooleanOperations =
let inline (<||>) (s1:Solid) (s2:Solid) = BooleanOperationsUtils.ExecuteBooleanOperation(s1,s2,BooleanOperationsType.Union)
let inline (<->) (s1:Solid) (s2:Solid) = BooleanOperationsUtils.ExecuteBooleanOperation(s1,s2,BooleanOperationsType.Difference)
let inline (<&&>) (s1:Solid) (s2:Solid) = BooleanOperationsUtils.ExecuteBooleanOperation(s1,s2,BooleanOperationsType.Intersect)
type CylinderDirection =
| BasisX
| BasisY
| BasisZ
let π = Math.PI
let getViewByName(name:string) (doc:Document) =
use xs = new FilteredElementCollector(doc)
seq { for x in xs.OfClass(typeof<View>) -> x} |> Seq.ofUnrelatedType<Element,View> |> Seq.filter (fun x -> x.Name = name) |> Seq.tryHead
let getView3DByName(name:string) (doc:Document) =
use xs = new FilteredElementCollector(doc)
seq { for x in xs.OfClass(typeof<View3D>) -> x} |> Seq.ofUnrelatedType<Element,View3D> |> Seq.filter (fun x -> x.Name = name) |> Seq.tryHead
let get3DViewElementId(doc:Document) =
use xs = new FilteredElementCollector(doc)
seq { for x in xs.OfClass(typeof<ViewFamilyType>) -> x} |> Seq.ofUnrelatedType<Element,ViewFamilyType> |> Seq.filter (fun e -> e.Name = "3D View") |> Seq.tryHead |> Option.map (fun e -> e.Id)
let create3DView =
memoize (fun (name:string) (doc:Document) ->
let view3DId = get3DViewElementId(doc).Value
let view = View3D.CreateIsometric(doc, view3DId)
let viewOrientation3D = new ViewOrientation3D(XYZ(1.,-1.,-1.), XYZ(1.,1.,1.), XYZ(1.,1.,-2.))
view.SetOrientation(viewOrientation3D)
view.SaveOrientation()
view.Name <- name
view)
let createCenterBasedBox(center:XYZ, edgelength:float) =
let halfedgelength = edgelength / 2.0
let profileloops = List<CurveLoop>()
let profileloop = new CurveLoop();
[|
(-1.,-1.,-1.), (-1., 1.,-1.)
(-1., 1.,-1.), ( 1., 1.,-1.)
( 1., 1.,-1.), ( 1.,-1.,-1.)
( 1.,-1.,-1.), (-1.,-1.,-1.)
|] |> Array.iter (fun ((a,b,c),(x,y,z)) ->
let h = halfedgelength
profileloop.Append(Line.CreateBound(XYZ(center.X + (h*a), center.Y + (h*b), center.Z + (h*c)),
XYZ(center.X + (h*x), center.Y + (h*y), center.Z + (h*z)))))
profileloops.Add(profileloop)
let extrusiondir = XYZ.BasisZ
GeometryCreationUtilities.CreateExtrusionGeometry(profileloops, extrusiondir, edgelength)
let createCenterBasedSphere(center:XYZ, radius:float) =
let frame = new Frame(center, XYZ.BasisX, XYZ.BasisY, XYZ.BasisZ)
let profileloops = List<CurveLoop>()
let profileloop = new CurveLoop()
let cemiEllipse = Ellipse.Create(center, radius, radius, XYZ.BasisX, XYZ.BasisZ, -π / 2., π / 2.)
profileloop.Append(cemiEllipse)
profileloop.Append(Line.CreateBound(XYZ(center.X, center.Y, center.Z + radius), XYZ(center.X, center.Y, center.Z - radius)))
profileloops.Add(profileloop)
GeometryCreationUtilities.CreateRevolvedGeometry(frame, profileloops, -π, π)
let createCenterBasedCylinder(center:XYZ, bottomradius:double, height:float, cylinderdirections:CylinderDirection) =
let halfheight = height / 2.
let bottomcenter,topcenter =
match cylinderdirections with
| BasisX -> XYZ(center.X - halfheight, center.Y, center.Z),XYZ(center.X + halfheight, center.Y, center.Z)
| BasisY -> XYZ(center.X, center.Y - halfheight, center.Z),XYZ(center.X, center.Y + halfheight, center.Z)
| BasisZ -> XYZ(center.X, center.Y, center.Z - halfheight),XYZ(center.X, center.Y, center.Z + halfheight)
let sweepPath = new CurveLoop()
sweepPath.Append(Line.CreateBound(bottomcenter, topcenter))
let profileloops = List<CurveLoop>()
let profileloop = new CurveLoop()
let cemiEllipse1 =
Ellipse.Create(bottomcenter, bottomradius, bottomradius,
(if cylinderdirections = BasisX then XYZ.BasisY else XYZ.BasisX),
(if cylinderdirections = BasisZ then XYZ.BasisY else XYZ.BasisZ),
-π, 0.)
let cemiEllipse2 =
Ellipse.Create(bottomcenter, bottomradius, bottomradius,
(if cylinderdirections = BasisX then XYZ.BasisY else XYZ.BasisX),
(if cylinderdirections = BasisZ then XYZ.BasisY else XYZ.BasisZ),
0., π)
profileloop.Append(cemiEllipse1)
profileloop.Append(cemiEllipse2)
profileloops.Add(profileloop)
GeometryCreationUtilities.CreateSweptGeometry(sweepPath,0,0., profileloops)
let mutable private schemaId = Option<int>.None
let paintSolid(s:Solid, viewName:string, doc:Document) =
let view = create3DView viewName doc
let sfm =
match SpatialFieldManager.GetSpatialFieldManager(view) |> Option.ofNullable with
| Some(x) -> x
| None -> SpatialFieldManager.CreateSpatialFieldManager(view,1)
let getSchema() =
let resultSchema1 = new AnalysisResultSchema("PaintedSolid" + viewName, "Description")
let displayStyle = AnalysisDisplayStyle.CreateAnalysisDisplayStyle(doc, "Real_Color_Surface" + viewName,
new AnalysisDisplayColoredSurfaceSettings(),
new AnalysisDisplayColorSettings(),
new AnalysisDisplayLegendSettings())
resultSchema1.AnalysisDisplayStyleId <- displayStyle.Id
sfm.RegisterResult(resultSchema1)
let sId =
match schemaId with
| None ->
let sId = getSchema()
schemaId <- Some(sId)
sId
| Some(x) when not (sfm.GetRegisteredResults().Contains(x)) ->
let sId = getSchema()
schemaId <- Some(sId)
sId
| Some(x) -> x
//sfm.GetRegisteredResults()
for face in s.Faces do
let idx = sfm.AddSpatialFieldPrimitive(face,Transform.Identity)
let compteValueAtPointForFace(face:Face,measurementNo:int) : (UV[]*ValueAtPoint[]) =
let bb = face.GetBoundingBox()
[|
for u in seq {bb.Min.U .. (bb.Max.U - bb.Min.U) .. bb.Max.U + 0.0000001 } do
for v in seq {bb.Min.V .. (bb.Max.V - bb.Min.V) .. bb.Max.V + 0.0000001} do // Note original code devides by one which is weird
let uvPnt = UV(u,v)
let faceXYZ = face.Evaluate(uvPnt)
let valPnt = new ValueAtPoint([|for ii = 1 to measurementNo do yield faceXYZ.DistanceTo(XYZ.Zero) * (float ii)|])
yield (uvPnt,valPnt)
|] |> fun xs -> (xs |> Array.map fst),(xs |> Array.map snd)
let (uvPts,valList) = compteValueAtPointForFace(face,1)
let pnts = new FieldDomainPointsByUV(uvPts)
let vals = new FieldValues(valList)
sfm.UpdateSpatialFieldPrimitive(idx,pnts, vals, sId)
open BooleanOperations
let zero = XYZ.Zero
let xaxis = CylinderDirection.BasisX
let yaxis = CylinderDirection.BasisY
let zaxis = CylinderDirection.BasisZ
let box(p,size) = createCenterBasedBox(p, size)
let sphere(p,r) = createCenterBasedSphere(p, r)
let cylinder(p,radius,length,direction) = createCenterBasedCylinder(p, radius, length, direction)
let union y x = x <||> y
let difference y x = x <-> y
let intersect y x = x <&&> y
let paint doc s = paintSolid(s, "CSGTree", doc)
module Mantis =
let transaction (f:Document->unit) =
Mantis.run (fun app ->
let document = app.ActiveUIDocument.Document
let tran = new Transaction(document, "GeometryCreation_BooleanOperation");
tran.Start() |> ignore
f document
tran.Commit() |> ignore
)
Mantis.transaction (fun doc ->
sphere(zero,20.)
|> intersect (box(zero,25.))
|> difference
([
cylinder(zero, 5., 40., xaxis)
cylinder(zero, 5., 40., yaxis)
cylinder(zero, 5., 40., zaxis)
] |> Seq.reduce union)
|> paint doc
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment