Visualizzazione post con etichetta View Depth Override. Mostra tutti i post
Visualizzazione post con etichetta View Depth Override. Mostra tutti i post

venerdì, luglio 18, 2014

View Depth Override External Command - Bugs Fixed



You can downlaod a zip file containing the .DLL and the .Addin manifest for the external command.
I've to thank a reader for pointing out some issues with the planting category (because previous version didn't take into account categories with no material quantities just like planting).
Also there was another issue with 2D views because the override was performed on a polar distance like in 3D views, now it's been fixed using the distance between the center of the bounding box of an object and the plane of the view.
I discovered that CGS has copied my first idea, and that was basically the goal of me starting to publish the code: to make companies develop better add-ins, but unfortunately this is not the case.
Now you can choose to pay a subscription to them for this tool (along with many others of course), which by the way doesn't work on perspective views while mine does, so why don't use just mine instead of waisting money? please if you're happy with my code show me some gratitude Paying What You Want via PayPal to:

puntorevit@gmail.com

*UPDATE 2015
The command for Revit 2015 works just fine, there's still need for the .addin to reside in the correct folder and the paths inside the file to be eventually updated.
Anyway the addin file should be copied in here:

C:\ProgramData\Autodesk\Revit\Addins\2015

here's the addin file with paths updated for 2015 version.

sabato, febbraio 01, 2014

3D DWG to RFA - Divide and conquer macros




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

puntorevit@gmail.com


Dopo aver completato la modellazione di tutti gli edifici ho dovuto separarli uno per uno per avere la possibilità di eseguire comandi come il View Depth Override che, altrimenti, con un'unica famiglia di massa (che chiamerò MA-0.rfa) non servirebbero a niente.
La procedura che ho elaborato è la seguente:

  1. Per ogni Form nella famiglia MA-0, registrare in un file CSV esterno su una riga l'ID dell'oggetto, le coordinate XYZ del punto di minimo del suo BoundingBoxXYZ e un numero incrementale per una famiglia segnaposto del tipo MA-NNN.rfa
  2. Creare le N copie segnaposto del file MA-0 contenenti esattamente gli stessi oggetti con lo stesso ID di MA-0
  3. Aprire ciascuno dei file segnaposto secondo l'ordine del file CSV, selezionare un oggetto con il suo ID e spostarlo in modo che il punto di minimo del suo BoundingBoxXYZ coincida con l'origine della famiglia
  4. Eliminare tutti gli altri oggetti e salvare la famiglia.
  5. Utilizzando lo stesso file CSV si possono poi posizionare le famiglie all'interno del modello come già mostrato da Jeremy Tammik sul suo blog a più riprese, ma sto pensando di utilizzare Dynamo, giusto per vedere come integrarlo all'interno della procedura.
La prima cosa interessante che ho scoperto è in che modo Revit gestisce due o più forme che vengono combinate tra loro con join geometry (unisci geometria) e cut (taglia), nel caso dei vuoti.
I vuoti non sono classificati come Form, almeno internamente la loro categoria di appartenenza è "null".
Quando si filtrano gli Element per la categoria interna OST_MassForm, che corrisponde alla categoria forme delle masse, si ottiene un conteggio "doppio" nel senso che gli elementi di questa categoria possono essere sia Form solidi semplici, sia GeomCombination, risultanti dalla combinazione appunto di membri come i Form e i Void; in sostanza questi due tipi di elementi hanno proprietà simili ma diverse e vanno gestiti in modo opportuno. Dopo qualche tentativo andato male su un file di prova molto piccolo, ho deciso di semplificare eliminando i void da una copia di MA-0 (che ho chiamato solamente MA-) da utilizzare come base per le operazioni successive, è un punto su cui devo ancora lavorare per un ulteriore affinamento della procedura.
Il BoundingBoxXYZ è una proprietà degli elementi ma dipende dalla vista, eseguendo una macro da una vista 3D si ottiene quello che serve per determinare la posizione dello spazio di ciascun oggetto che andrà scritta scomponendola nelle sue coordinate XYZ all'interno di un file CSV. L'unità di misura è quella interna di Revit (ft) ma si può pensare di convertirla in metri anche se non è strettamente necessario per questo scopo (il file CSV è infatti un passaggio necessario per riposizionare successivamente le famiglie all'interno del modello).
Per stabilire il numero di copie segnaposto necessarie basta selezionare tutti gli oggetti presenti in MA-.rfa della categoria Form, la duplicazione si può ottenere con un file batch di poche righe, da modificare secondo le proprie esigenze:


pushd %~dp0
@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set TESTFOLDER=MA
md "%TESTFOLDER%"
set /a times=2
FOR /L %%i IN (1,1,%times%) DO (
        copy "MA-.rfa" ".\%TESTFOLDER%" 
cd ".\%TESTFOLDER%"
ren "MA-.rfa" "MA-%%i.rfa"
cd ".."
)

Basta copiare e incollare in un editor di .txt e salvare con estensione .bat all'interno della cartella dove si trova il file MA-.rfa, successivamente verrà creata una cartella MA all'interno della quale verranno create delle copie con indice incrementale di MA-.rfa. La prima riga consente l'esecuzione anche su percorsi di rete. La duplicazione di 712 file è avvenuta in una manciata di secondi, ha richiesto però uno spazio libero di circa 8GB.


Un'altra cosa interessante che ho scoperto è la possibilità di caricare in background nella sessione di Revit, altri documenti, senza quindi renderli visibili all'utente (proprio come avviene quando si apre un modello con dei file collegati al suo interno: i file collegati vengono caricati nella sessione corrente di Revit ma non sono modificabili). In questo caso, dato che i file non sono collegati, possono anche essere modificati e salvati.
Questa caratteristica mi ha poi fatto venire in mente delle possibili applicazioni per delle operazioni veramente tediose quando si opera con i file collegati, e sarà oggetto prossimamente di qualche riflessione più attenta.

Una volta che il primo file MA-1 è caricato, dal file CSV si ottiene il valore dell'ID dell'oggetto e lo si sposta nell'origine passando ancora per l'Element associato all'ID (si sarebbe potuto anche ottenere lo spostamento direttamente dal file CSV, ma in questo modo occorrono meno righe di codice).
Per lo spostamento si  ricorre a ElementTransformUtils.MoveElement che consente di specificare l'ID dell'oggetto da spostare all'interno di uno specifico documento e il vettore di spostamento (in questo caso dal punto di minimo del BoundingBoxXYZ all'origine). Successivamente si eliminano tutti gli altri oggetti Form escludendo l'ID che deriva dal file CSV. Il documento viene chiuso e quindi direttamente salvato.
Operando ciclicamente su tutte le copie MA-1, MA-2,..., MA-712 ci sono volute 12 ore e 30 minuti e ulteriori 4GB di spazio perché purtroppo non tutti gli oggetti sono stati eliminati, infatti i file MA-NNN.rfa sono passati da una dimensione iniziale di circa 12MB a circa 6MB, ma sono state mantenute anche le copie di backup MA-NNN.0001.rfa.
Ho creato poi un'altra macro molto simile alla precedente dove ho sostanzialmente mantenuto solo la parte in cui vengono eliminati gli oggetti con ID diverso da quello derivante dal file CSV e questa volta sembra aver sortito gli effetti desiderati riducendo la dimensione a circa 850kB (si potrebbe forse fare qualcosa di meglio ma ho notato che manualmente non si ottengono risultati sensibilmente migliori, per cui mi accontento).
Questo secondo passaggio riesce a gestire circa 2/3 file al minuto, quindi poco meno di 6 ore per tutti i file con una stima di circa 600MB di spazio totale richiesto.

Ecco le macro, suggerimenti e critiche sono ben accetti.

sabato, gennaio 11, 2014

View Override Distance - Dynamo

EDIT* MajorFace.dyf copy to:

C:\Autodesk\Dynamo\Core\definitions




Ho cominciato da un paio di giorni ad esplorare più nel dettaglio Dynamo.
Da prima ho notato una grossa limitazione sulla localizzazione del programma nel senso che i parametri che possono essere restituiti al momento non sono quelli interni di Revit (come ero abituato dalle API), ma sono gli stessi parametri che troviamo a livello di interfaccia grafica utente. Questo da un lato è positivo perché permette di controllare in fretta i parametri di istanza e tipo ma è limitativo per la circolazione delle definizioni (così vengono chiamati gli script di Dynamo) perché devono essere inseriti i nomi dei parametri a livello utente e quindi diversi a seconda della lingua di installazione di Revit (o Vasari).
Per contro la restituzione dei parametri riporta i valori interni di Revit e non quelli che visualizza l'utente generando, se non altro, della confusione.
La cosa rassicurante è sapere che vengono rilasciati giornalmente degli aggiornamenti su questo server che purtroppo però è spesso molto lento e il download difficilmente va a buon fine, almeno questo è quello che è capitato a me. Si tratta di versioni sperimentali e quindi non garantite al 100%, compilate dai codici in continua evoluzione che si trovano su GitHub.
A questo link invece si possono trovare le versioni ufficiali di Dynamo.

Dopo aver preso confidenza con i nodi a disposizione, ho cercato di replicare il comportamento dell'external command del ViewDepthOverride.

Non sono riuscito a trovare il modo di estrapolare direttamente i dati relativi alla posizione spaziale delle viste ma ho trovato un work around che potrebbe essere utilizzato anche in altri ambiti.
Una delle cose migliori di Dynamo è che consente agli utenti di crearsi delle proprie funzioni personalizzate, esistono per altro dei pacchetti di  estensioni con nodi creati da altri utenti che contribuiscono di molto alla buona riuscita di tutto il progetto. In questo modo è possibile riutilizzare strutture complesse e soprattutto di rendere molto più leggibile il tutto.

Questa la definizione cui sono arrivato:

*Thanks to Julien Benoit



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

puntorevit@gmail.com


mercoledì, gennaio 08, 2014

View Depth Override - Polar Distance Update



Ecco un aggiornamento del codice precedente, ora la distanza degli oggetti per le viste 2D  è calcolata rispetto al piano della vista e non più dal punto di origine (opzione che rimane valida per le viste di prospettiva).
Per ragioni di grafica ho impostato il limite del grigio non più a 255 ma a 205 poiché c'è il rischio che risulti troppo chiaro in fase di stampa, ma avendo pubblicato il codice ognuno può impostarlo a piacere modificando tutte le occorrenze della variabile: 

int grey=205;

Ho fatto anche qualche modifica per la gestione degli errori, ciononostante è un lavoro preliminare e rimane valido il mio appello per ricevere feedback nei commenti.

Questo il codice sorgente, qui invece il file DLL e l'ADDIN che vanno copiati qui:

C:\ProgramData\Autodesk\Revit\Addins\2014

Potrebbe essere utile anche consultare questo articolo per sbloccare i file .dll



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

puntorevit@gmail.com

View Depth Override Polar Distance - External Command




Ho cambiato del tutto approccio al problema e ho riscritto per intero il comando.
Stavolta la sostituzione grafica non è più limitata ai grigi 128 e 192, ma è un vero e proprio gradiente funzione della distanza tra gli oggetti e l'origine della vista.
Così facendo è possibile applicare praticamente lo stesso codice sia alle viste 2D sia alle prospettive.
Questa versione del comando è molto semplice, leggera e veloce nell'esecuzione ed anche più elegante nel codice.
I comandi sono 3:
  • View Depth Override - Polar Distance per le viste 2D attive
  • View Depth Override 3D - Write List per scrivere il file sul desktop con l'elenco delle viste di prospettiva
  • View Depth Override 3D - Read List per eseguire il view depth override sulle viste elencate nel file (si può editare manualmente il contenuto del file per decidere su quali viste 3D eseguire il comando che andrà lanciato da una qualsiasi altra vista)
Quando si edita il file di testo si deve mantenere il nome e il percorso sul desktop.

Una cosa importante è settare la profondità della vista poiché rappresenta la massima distanza entro cui viene calcolato il gradiente della sostituzione grafica.
Si tratta ancora di un work in progress ed esistono delle casistiche che possono portare ad un overflow (una specie di ciclo infinito), per questo nel codice ho inserito dei punti in cui le eventuali eccezioni al funzionamento riporteranno dei messaggi, nel caso prego chi intenda fare uso di questo comando di segnalarmele nei commenti.

Questo il codice sorgente, qui invece il file DLL e l'ADDIN che vanno copiati qui:

C:\ProgramData\Autodesk\Revit\Addins\2014

Potrebbe essere utile anche consultare questo articolo per sbloccare i file .dll

Ecco una dimostrazione della funzionalità, prossimamente il comando su viste 2D considererà la distanza dal piano della vista così da evitare  gradienti indesiderati su oggetti che appartengono allo stesso piano.

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



puntorevit@gmail.com

venerdì, dicembre 20, 2013

View Depth Override - Alcune considerazioni



Il grande Jeremy Tammik di The Building Coder mi ha fatto l'onore di pubblicare sul suo blog il mio codice relativo al View Depth Override sia per viste 2D sia per viste 3D.
Nei commenti, un lettore di nome Ning, ha delle perplessità riguardo alle coordinate che ho utilizzato nel codice, questa la mia risposta nel tentativo di dipanare i suoi dubbi.
E' una breve storia (ancora non conclusa per altro) del processo che mi ha condotto a determinate scelte per impostare il codice, la scrivo in inglese (perdonate gli erroracci) ma se qualcuno non capisse posso tradurla in italiano nei commenti... ogni aiuto o suggerimento in tal senso è ben accetto!

Questo quanto chiede Ning:

"hi Paolo and Jeremy,

i'm trying to understand that 3DView Depth Override stuff, by using my own simplified code, works OK for plan / section / elevation views, but i have trouble to understand the coordinate system in terms of relationship between 3DView's Cropbox and Outline used in BoundingBoxIntersectsFilter, i assume the followings:
1) Boundingbox of 3DView's Cropbox is in coordinate system on top of the computer screen in which Z is perpendicular to the computer screen
2) Outline used in BoundingBoxIntersectsFilter is in Revit coordinate system

a bit description in your blog will be great, perhaps Jeremy can give a more technical detailed explanation in the future"

Questa la mia risposta:

Hi Ning,
sorry for the late reply, I'll try to make myself clear about what you're asking.
When I tried to code this macro I was at the beginning of my "API journey" and I'm sure I wouldn't be making the same choices if I had to do it again from scratch.
You're assumptions are correct: View BoundingBox has to be transformed into absolute coordinates to work properly as an Outline.
All started with 2D views: view coordinates are realtive to each view and there are three vectors defining the space: the RightDirection (let's say local x axis), the UpDirection (local y axis) and ViewDirection (local z axis, which is orthogonal to the screen as you mentioned).
That said, I guess one can obtain the Outline necessary to the BoundingBoxFilter from here, taking a shortcut and simplyfing the code a bit, but still, you need to convert from local to absolute coordinates to build a proper Outline.
This is also important to implement the subfunction called "Integral", which collects all ElementIds in a series of smaller BoundingBoxes (200 subdivisions for each one of the three segments of the main BoundingBox), because in case of views that are not parallel to the edges of the screen, a single BoundingBox would generate errors in the selection.
When I began to study a macro for 3D views I decided to restrict the code only for perspective views because that made more sense to me; I tried to reuse as much as possible the code I had for 2D views, but I took a different approach to determine the Bounding Box: I choosed to consider the entire Bounding Box of the model (this is the part where Jeremy's blog comes in), then I rotated (it's better say projected, transformed) this overall BoundingBox "parellel" to the perspective view (but keeping the top and the bottom faces parallel to the ground) and then execute the same code for 2D Views.
It's a work in progress though, I couldn't find the Bounding Box of a perspective view as I did before for a 2D view, and that's an issue still opened since I hadn't look into it since I published the code. I'm open to all the help I can get :)
I hope this helped you in a better understanding of what's behind the code.

Cheers


-Paolo


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

puntorevit@gmail.com

mercoledì, dicembre 04, 2013

View Depth Override - the CAD side of the moon



Qualche tempo fa mi è stato chiesto nei commenti come poter unificare le linee delle diverse tonalità di grigio una volta esportate da Revit dopo avere utilizzato il View Depth Override.
Per prima cosa si deve impostare l'esportazione dei formati CAD da Revit su TrueColour e utilizzare le impostazioni byLayer e le sostituzioni byEntity.
Una volta in AutoCAD si deve caricare il GreyMerge.lsp alla riga di comando digitando "appload".
Digitando poi GREYMERGE si richiama il comando che inserisce i due layer "_Grey-1" e "_Grey-2" attribuendo loro rispettivamente i colori RGB 128,128,128 e 192,192,192.
Successivamente gli oggetti vengono spostati di layer concordemente al loro colore, si potrà quindi impostare tutto su byLayer.

GreyMerge.lsp

sabato, novembre 16, 2013

3D View Depth Override - Update


In questo nuovo video mostro come funziona la versione del View Depth Override per le viste prospettiche.
Dato che non è possibile azionare le macro da una vista prospettica ho trovato un sistema per aggirare il problema.
Con la macro ViewListWrite viene creato un file di testo sul desktop chiamato [MyProjectName]-3DViews.txt, in cui vengono elencate per ciascuna riga le viste di prospettiva presenti nel progetto.
Con la macro ViewListRead viene eseguito il View Depth Override tante volte quante sono le viste elencate nel file di testo. Questo file di testo è editabile dall'utente ed è quindi possibile decidere su quali viste far eseguire la macro.
Il codice è migliorato dalla prima versione, grazie anche a degli spunti che ho trovato sul blog di Jeremy Tammik the Building Coder, in particolare per l'uso del Linq per determinare i punti di massimo e minimo.
Ho introdotto anche l'uso del Transform per rendere più leggibili le operazioni di calcolo dei punti del BoundingBoxXYZ orientato come la vista, la qual cosa si può felicemente applicare alla versione 2D del View Depth Override.

Si tratta sempre di un work in progress, i commenti sono bene accetti per cercare di migliorare :)

Codice sorgente:
Versione 2012
Versione 2014

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.

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.



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!