giovedì, ottobre 31, 2013

Automatic Floor Sketch


Partendo da una selezione dei locali traccia i pavimenti di finitura corrispondenti. Considera solo gli elementi che delimitano il locale.

mercoledì, ottobre 23, 2013

AEC You and Me

Blog interesessantissimo di Julien Benoit, che ho avuto il piacere di conoscere al Revit Technology Conference di Delft.
Una persona squisita, intelligente e appassionata del proprio lavoro come si può facilmente capire dai primi post che sta pubblicando in questi giorni.
Julien è uno dei moderatori di Revitforum.org, collaboratora per uno dei maggiori general contractor in Francia, Bouygues Construction.
Sta cominciando a condividere i progressi che sta facendo nell'esplorazione delle potenzialità di Dynamo nel lavoro di tutti i giorni.
Da seguire.

martedì, ottobre 22, 2013

Text Type Replacement - Codice Sorgente

Visto l'interesse al post precedente pubblico il codice sorgente per la versione 2012




giovedì, ottobre 17, 2013

View Depth Override - 3D


Questa volta sto lavorando sulle viste 3D in prospettiva degli esterni, come si vede dal video l'algoritmo non è ancora perfetto a causa degli errori introdotti dall'angolo tra il punto di vista e il modello.
Ho trovato però un modo più elegante ed efficiente di eseguire i calcoli anche per la macro delle viste 2D.
La fase successiva interesserà le viste 3D degli interni, e quando finalmente sarò pronto per l'Addin si potrà pensare anche all'aggiornamento automatico delle viste dove viene applicato il comando.

Analytical Tracer - Codice Sorgente

Questo il codice sorgente per la versione 2014, funziona solo per elementi quali pilastri strutturali e travi lineari.
Nella versione 2012 purtroppo non sono riuscito a capire come mai non riuscisse a tracciare correttamente le aste analitiche dei pilastri.
Si potrebbero anche differenziare i tipi di linea per travi e pilastri ma occorre differenziarli poi anche nella tabella dell'esportazione in DWG.

Qui si può scaricare il file sorgente:




If you are happy with my code you can show me some gratitude paying what you want on PayPal:

puntorevit@gmail.com

martedì, ottobre 15, 2013

Analytical Tracer


Con questa macro si possono esportare dei modelli unifilari per elementi strutturali rettilinei che altrimenti risulterebbero divisi in 3 segmenti per ciascuna asta del modello analitico.
Ho fatto una prova veloce con la 2014 si accettano suggerimenti :)

lunedì, ottobre 14, 2013

View Depth Override 2014 - Codice Sorgente

A questo link potete trovare il codice per la versione 2014, in questo caso si possono sostituire graficamente anche i motivi di superficie degli oggetti ma non è possibile agire sulle ombre.



TextNote Type Replacement


Ho dovuto scrivere questa piccola macro per controllare che i testi con lo stesso contenuto inseriti su più tavole avessero il tipo associato corretto.
Morale: non usate i testi!
PLEASE DON'T USE TEXTNOTES!!!

mercoledì, ottobre 09, 2013

View Depth Override - Codice Sorgente


Ecco il codice sorgente per il View Depth Override. Per quelli che non conoscono le API ancora un po' di pazienza e pubblicherò l'add-in per il comando, quella sopra sarà l'icona che pensavo di utilizzare, rende l'idea?

Tanto per cominciare è scritto in C#, testato su Revit Architecture 2012, ecco gli using statements da inserire all'inizio:

using System;
using System.Collections.Generic;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System.Linq;

Ed ecco il codice vero e proprio, ho inserito dei commenti in inglese per renderlo comprensibile a più persone, nel caso non fosse chiaro chiedete e proverò a spiegarmi meglio:

private XYZ MaxVertex2(XYZ a, XYZ b)
        {
            //Given two points determines the maximum of the Bounding Box that encloses them
            double X = a.X;
            double Y = a.Y;
            double Z = a.Z;
            if (Math.Round(b.X, 5) > X)
                X = b.X;
            if (Math.Round(b.Y, 5) > Y)
                Y = b.Y;
            if (Math.Round(b.Z, 5) > Z)
                Z = b.Z;
            XYZ max = new XYZ(X, Y, Z);
            return max;
        }
        private XYZ MinVertex2(XYZ a, XYZ b)
        {
            //Given two points determines the minimum of the Bounding Box that encloses them
            double X = a.X;
            double Y = a.Y;
            double Z = a.Z;
            if (Math.Round(b.X, 5) < X)
                X = b.X;
            if (Math.Round(b.Y, 5) < Y)
                Y = b.Y;
            if (Math.Round(b.Z, 5) < Z)
                Z = b.Z;
            XYZ min = new XYZ(X, Y, Z);
            return min;
        }
        private ICollection Integral(XYZ min1, XYZ max1, XYZ min2, XYZ max2)
        {
            //If the view is not parrallel to X or Y axis in Global Coordinates,
            //the selecting process is done using an integral approach
            //each segment is subdivided into a high number of subdivisions (200)
            //and smaller bounding boxes are used to filter the objects
            //that's the reason why with a non-parallel view the command is slower
            //This is the list of ElementId created and immediatly cleared
            ICollection ids = new FilteredElementCollector(this.ActiveUIDocument.Document)
            .WhereElementIsNotElementType()
            .ToElementIds();
            ids.Clear();
            foreach (Document doc in this.Application.Documents)
            {
                int subdivisions = 200;
                //Here determines the minimum and the maximum point for the segment based on the four vertices
                //that have been passed from the other function
                XYZ a = MinVertex2(min1, max1);
                XYZ a1 = a;
                XYZ b = MaxVertex2(min1, max1);
                XYZ c = MinVertex2(min2, max2);
                XYZ d = MaxVertex2(min2, max2);
                XYZ increment = (d - b) / subdivisions;
                for (int i = 0; i < subdivisions; i++)
                {
                    //This is tricky: sometimes if the view is perfectly parallel something
                    //about the vertices goes wrong (I guess there's a small tolerance)
                    //Anyway to reduce the amount of calculations if the coordinates are the same
                    //at the fifth decimal digit I assumed the coordinates to be equal
                    //I know it isn't perfect but it worked for me
                    if (Math.Round(a.X, 5) == Math.Round(b.X, 5) || Math.Round(a.Y, 5) == Math.Round(b.Y, 5))
                    {
                        i = subdivisions + 1;
                        a = MinVertex2(a1, d);
                        b = MaxVertex2(a1, d);
                    }
                    Outline ol = new Outline(a, b);
                    if (ol.IsEmpty == false)
                    {
                        //Here comes the filtering part where I tried to avoid all kinds of objects
                        //that are not useful for this task because they can't be overridden
                        //such as Element Types or Sketches of Floors/Roofs/ ceilings and so on
                        BoundingBoxIntersectsFilter BBIIF = new BoundingBoxIntersectsFilter(ol);
                        IEnumerable elems = new FilteredElementCollector(doc)
                        .WherePasses(BBIIF)
                        .WhereElementIsNotElementType()
                        .WhereElementIsViewIndependent()
                        .Cast()
                        .Where(q => q.Category != null && q.Category.HasMaterialQuantities);
                        if (elems.Count() > 0)
                        {
                            foreach (Element e in elems)
                            {
                                ids.Add(e.Id);
                            }
                        }
                    }
                    else
                    {
                        a = a + increment;
                        b = b + increment;
                    }
                    a = a + increment;
                    b = b + increment;
                }
            }
            return ids;
        }
        private ICollection IntegralCollection(View view, int i)
        {
            //Here is where the coordinates of the Bounding Box of the view are calculated
            //I don't like that this calculations are repeted each time this function is called (one for each segment)
            //it could be done more efficiently and for sure more clearly using the Transform...
            //but I didn't even know what that was when I recorded the video
            //the double scale is set to 1 because at first I started from the UV Bounding Box,
            //which contains also annotations and not just the model categories
            //I then used the Bounding BOX XYZ and reused the code I wrote before
            XYZ CurrentViewOrigin = view.Origin;
            double scale = 1;
            XYZ VRight = view.RightDirection;
            XYZ VUp = view.UpDirection;
            XYZ Vdir = view.ViewDirection;
            XYZ Vmin = view.CropBox.Min;
            XYZ Vmax = view.CropBox.Max;
            //I used letters for the Vertices of the main Bounding Box
            //rather then numbers for the inner box that defines the middle segment
            //I know this part looks messy but it was good to refresh some algebra concepts :)
            //here is were I should use the Transform
            XYZ Va = new XYZ(CurrentViewOrigin.X + scale * (Vmin.X * VRight.X + Vmin.Y * VUp.X) + Vmax.Z * Vdir.X, CurrentViewOrigin.Y + scale * (Vmin.X * VRight.Y + Vmin.Y * VUp.Y) + Vmax.Z * Vdir.Y, CurrentViewOrigin.Z + scale * (Vmin.X * VRight.Z + Vmin.Y * VUp.Z) + Vmax.Z * Vdir.Z);
            XYZ Vb = new XYZ(CurrentViewOrigin.X + scale * (Vmin.X * VRight.X + Vmax.Y * VUp.X) + Vmax.Z * Vdir.X, CurrentViewOrigin.Y + scale * (Vmin.X * VRight.Y + Vmax.Y * VUp.Y) + Vmax.Z * Vdir.Y, CurrentViewOrigin.Z + scale * (Vmin.X * VRight.Z + Vmax.Y * VUp.Z) + Vmax.Z * Vdir.Z);
            XYZ Vc = new XYZ(CurrentViewOrigin.X + scale * (Vmax.X * VRight.X + Vmax.Y * VUp.X) + Vmax.Z * Vdir.X, CurrentViewOrigin.Y + scale * (Vmax.X * VRight.Y + Vmax.Y * VUp.Y) + Vmax.Z * Vdir.Y, CurrentViewOrigin.Z + scale * (Vmax.X * VRight.Z + Vmax.Y * VUp.Z) + Vmax.Z * Vdir.Z);
            XYZ Vd = new XYZ(CurrentViewOrigin.X + scale * (Vmax.X * VRight.X + Vmin.Y * VUp.X) + Vmax.Z * Vdir.X, CurrentViewOrigin.Y + scale * (Vmax.X * VRight.Y + Vmin.Y * VUp.Y) + Vmax.Z * Vdir.Y, CurrentViewOrigin.Z + scale * (Vmax.X * VRight.Z + Vmin.Y * VUp.Z) + Vmax.Z * Vdir.Z);
            XYZ Ve = new XYZ(CurrentViewOrigin.X + scale * (Vmin.X * VRight.X + Vmin.Y * VUp.X) + Vmin.Z * Vdir.X, CurrentViewOrigin.Y + scale * (Vmin.X * VRight.Y + Vmin.Y * VUp.Y) + Vmin.Z * Vdir.Y, CurrentViewOrigin.Z + scale * (Vmin.X * VRight.Z + Vmin.Y * VUp.Z) + Vmin.Z * Vdir.Z);
            XYZ Vf = new XYZ(CurrentViewOrigin.X + scale * (Vmin.X * VRight.X + Vmax.Y * VUp.X) + Vmin.Z * Vdir.X, CurrentViewOrigin.Y + scale * (Vmin.X * VRight.Y + Vmax.Y * VUp.Y) + Vmin.Z * Vdir.Y, CurrentViewOrigin.Z + scale * (Vmin.X * VRight.Z + Vmax.Y * VUp.Z) + Vmin.Z * Vdir.Z);
            XYZ Vg = new XYZ(CurrentViewOrigin.X + scale * (Vmax.X * VRight.X + Vmax.Y * VUp.X) + Vmin.Z * Vdir.X, CurrentViewOrigin.Y + scale * (Vmax.X * VRight.Y + Vmax.Y * VUp.Y) + Vmin.Z * Vdir.Y, CurrentViewOrigin.Z + scale * (Vmax.X * VRight.Z + Vmax.Y * VUp.Z) + Vmin.Z * Vdir.Z);
            XYZ Vh = new XYZ(CurrentViewOrigin.X + scale * (Vmax.X * VRight.X + Vmin.Y * VUp.X) + Vmin.Z * Vdir.X, CurrentViewOrigin.Y + scale * (Vmax.X * VRight.Y + Vmin.Y * VUp.Y) + Vmin.Z * Vdir.Y, CurrentViewOrigin.Z + scale * (Vmax.X * VRight.Z + Vmin.Y * VUp.Z) + Vmin.Z * Vdir.Z);
            XYZ V1 = Ve + (Va - Ve) * 2 / 3;
            XYZ V2 = Vf + (Vb - Vf) * 2 / 3;
            XYZ V3 = Vg + (Vc - Vg) * 2 / 3;
            XYZ V4 = Vh + (Vd - Vh) * 2 / 3;
            XYZ V5 = Ve + (Va - Ve) / 3;
            XYZ V6 = Vf + (Vb - Vf) / 3;
            XYZ V7 = Vg + (Vc - Vg) / 3;
            XYZ V8 = Vh + (Vd - Vh) / 3;
            //This function returns a list of ElementId to be overridden depending on which one of
            //the three segments the objects fall in
            ICollection list = new FilteredElementCollector(ActiveUIDocument.Document)
            .WhereElementIsNotElementType()
            .ToElementIds();
            list.Clear();
            if (i == 0)
            {
                list = Integral(Va, V2, Vd, V3);
            }
            else
            {
                if (i == 1)
                {
                    list = Integral(V1, Vf, V4, Vg);
                }
                else
                {
                    list = Integral(V5, Vf, V8, Vg);
                }
            }

            return list;
        }
        public void ViewDepthOverride()
        {
            //known issues: doesn't work with linked files and 3D Views
            UIDocument uidoc = this.ActiveUIDocument;
            Document doc = uidoc.Document;
            //Creates the lists of ElementId to pass to the Projection Color Override by Element
            //those are just empty container at the moment
            ICollection ids0 = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).ToElementIds();
            ICollection ids1 = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).ToElementIds();
            ICollection ids2 = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).ToElementIds();
            ids0.Clear();
            ids1.Clear();
            ids2.Clear();
            View CurrentView = doc.ActiveView;
            //It works fine for 2D views, building sections and elevations for example
            //it should work also with floor plans and even tilted Detail Views
            //but it wasn't my goal when I started
            //won't work for a 3D View
            int clip = CurrentView.get_Parameter(BuiltInParameter.VIEWER_BOUND_FAR_CLIPPING).AsInteger();
            //If the far clipping is not active the default depth is 10 feet and won't work correctly
            if (clip == 0)
            {
                TaskDialog.Show("View Depth Override", "In order to use this macro far clipping must be activated.");
            }
            else
            {
                //If the far clipping is active then the view depth can be subdivided into 3 segments:
                //foreground (0)
                //middle (1)
                //background (2)
                ids0 = IntegralCollection(CurrentView, 0);
                ids1 = IntegralCollection(CurrentView, 1);
                ids2 = IntegralCollection(CurrentView, 2);
                //Just a check to handle some common errors, for instance not even one
                //ElementId was found in the foreground view interval
                if (ids0.Count == 0)
                {
                    TaskDialog.Show("View Depth Override", "Something went wrong in the closer segment.\n\nPlease adjust the view depth to include some objects.");
                    ElementId e = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).FirstElement().Id;
                    ids0.Add(e);
                    ids1.Add(e);
                    ids2.Add(e);
                }
                else
                {
                    //Again just a check to handle some common errors, in this case not even one
                    //ElementId was found in the background view interval
                    //because the view depth is too much rather then just enough to enclos
                    //the objects in the model
                    if (ids2.Count == 0)
                    {
                        TaskDialog.Show("View Depth Override", "Something went wrong in the farther segment to be overridden in Grey 192.\n\nPlease check that the view depth in the current view is just enough to include the objects you need.");
                        ElementId e = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).FirstElement().Id;
                        ids2.Add(e);
                        ids1.Add(e);
                    }
                }
            }
            //Begins the transaction to override the elements
            using (Transaction t = new Transaction(doc, "View Depth Override"))
            {
                t.Start();
                while (ids0.Count > 0)
                {
                    //Stores the color for the foreground
                    Color Color0 = CurrentView.get_ProjColorOverrideByElement(ids0);
                    if (ids1.Count != 0)
                    {
                        //Overrides the middle segment
                        CurrentView.set_ProjColorOverrideByElement(ids1, new Color((byte)128, (byte)128, (byte)128));
                    }
                    else
                    {
                        //Just a precaution, not sure it is really necessary
                        TaskDialog.Show("View Depth Override", "Something went wrong in the middle segment to be overridden in Grey 128.\n\nPlease check that the view depth in the current view is just enough to include the objects you need.");
                        break;
                    }
                    if (ids2.Count != 0)
                    {
                        CurrentView.set_ProjColorOverrideByElement(ids2, new Color((byte)192, (byte)192, (byte)192));
                    }
                    else
                    {
                        //Overrides the background segment
                        TaskDialog.Show("View Depth Override", "Something went wrong in the farther segment to be overridden in Grey 192.\n\nPlease check that the view depth in the current view is just enough to include the objects you need.");
                        break;
                    }
                    //Resets the foreground color in case of objects overlapping
                    //foreground and middle segment
                    CurrentView.set_ProjColorOverrideByElement(ids0, Color0);
                    break;
                }
                doc.Regenerate();
                uidoc.RefreshActiveView();
                t.Commit();
            }
        }  


martedì, ottobre 08, 2013

View Depth Override

Da qualche tempo mi sto interessando più da vicino al mondo della personalizzazione di Revit attraverso l'implementazione delle API.
Una dei primi comandi su cui sto lavorando è il View Depth Override ossia la sostituzione automatica delle linee di proiezione degli oggetti in funzione della profondità di campo della vista.

Qui c'è un video dimostrativo:



Il concetto è semplice: per viste di sezione o prospetto, si imposta la profondità della vista e si esegue il comando. La profondità della vista viene divisa in tre settori (primo piano, intermedio e sfondo), vengono effettuate delle sostituzioni grafiche (grigio 128 per il settore intermedio e grigio 192 per quello di sfondo) per simulare la profondità della vista.
Il comando viene eseguito anche se non ci sono oggetti in primo piano, quindi la posizione della vista rispetto al modello ha effettivamente rilevanza per il risultato finale.
Al momento non funziona per i file collegati ed è un comando di tipo "fire and forget" ossia viene eseguito una volta e poi non si aggiorna automaticamente se intervengono cambiamenti nel modello, ma non escludo si possa intervenire per eliminare questi inconvenienti.
Se interessa posso pubblicare il comando ma devo prima testarlo sulle release successive, tenuto conto che lo faccio più per passione e non intendo diventare un programmatore :)

Update qui!