Here's how to export Clash Result images using Navisworks API

Here's how to export Clash Result images using Navisworks API

Anonymous
Not applicable
7,046 Views
13 Replies
Message 1 of 14

Here's how to export Clash Result images using Navisworks API

Anonymous
Not applicable

I couldn't find any documentation for Navisworks 2019 API, and the documentation here (https://apidocs.co/apps/navisworks/2018/87317537-2911-4c08-b492-6496c82b3ed0.htm) does not seem to have a method for exporting images of clash results (have Autodesk abandoned Navisworks API or something?). Thus, I've attempted to code it myself.

 

Document doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
DocumentClash documentClash = doc.GetClash();
DocumentClashTests oDCT = documentClash.TestsData;

// Assuming you've already run all Clash Tests and have the results
foreach(ClashTest test in oDCT.Tests) {
	if(test.Children.Count <= 0) {
		continue;
	}
	
	foreach (ClashResult result in test.Children) {
		Viewpoint viewpoint = doc.CurrentViewpoint.Value;
		
		// Get the 2 clashing elements from the ClashResult
		ModelItem item1 = result.Item1;
		ModelItem item2 = result.Item2;
		
		ModelItemCollection items = new ModelItemCollection();
		items.Add(item1);
		items.Add(item2);
		
		// Select the 2 clashing elements
		doc.CurrentSelection.Clear();
		doc.CurrentSelection.CopyFrom(items);
		doc.ActiveView.FocusOnCurrentSelection();
		doc.Models.ResetAllHidden();
		
		// Hide everything except for the 2 clashing elements
		ModelItemCollection modelItemCollection1 = new ModelItemCollection();
		ModelItemCollection modelItemCollection2 = new ModelItemCollection();

		using (IEnumerator<ModelItem> enumerator = doc.CurrentSelection.SelectedItems.GetEnumerator()) {
			while (((IEnumerator) enumerator).MoveNext()) {
				ModelItem current = enumerator.Current;

				if (current.AncestorsAndSelf != null)
					modelItemCollection2.AddRange((IEnumerable<ModelItem>) current.AncestorsAndSelf);

				if (current.Descendants != null)
					modelItemCollection2.AddRange((IEnumerable<ModelItem>) current.Descendants);
			}
		}

		using (IEnumerator<ModelItem> enumerator = modelItemCollection2.GetEnumerator()) {
			while (((IEnumerator) enumerator).MoveNext()) {
				ModelItem current = enumerator.Current;

				if (!NativeHandle.ReferenceEquals((NativeHandle) current.Parent, (NativeHandle) null))
					modelItemCollection1.AddRange((IEnumerable<ModelItem>) current.Parent.Children);
			}
		}

		using (IEnumerator<ModelItem> enumerator = modelItemCollection2.GetEnumerator()) {
			while (((IEnumerator) enumerator).MoveNext()) {
				ModelItem current = enumerator.Current;
				modelItemCollection1.Remove(current);
			}
		}

		doc.Models.SetHidden((IEnumerable<ModelItem>) modelItemCollection1, true);
		doc.Models.SetHidden(
			((IEnumerable<Model>) doc.Models)
			.SelectMany<Model, ModelItem>(
				(Func<Model, IEnumerable<ModelItem>>) (c => (IEnumerable<ModelItem>) c.RootItem.Children))
			.Except<ModelItem>((IEnumerable<ModelItem>) modelItemCollection1)
			.Except<ModelItem>((IEnumerable<ModelItem>) modelItemCollection2)
			, true
		);

		doc.CurrentSelection.Clear();

		// Adjust the camera, lighting, and paint the clashing elements in Red and Green respectively
		Viewpoint copy = viewpoint.CreateCopy();
		copy.Lighting = (ViewpointLighting) 0;
	
		doc.Models.ResetAllPermanentMaterials();
		doc.CurrentViewpoint.CopyFrom(copy);

		Autodesk.Navisworks.Api.Color RED = Autodesk.Navisworks.Api.Color.Red;
		Autodesk.Navisworks.Api.Color GREEN = Autodesk.Navisworks.Api.Color.Green;
	
		if (!NativeHandle.ReferenceEquals((NativeHandle) items.ElementAtOrDefault<ModelItem>(0), (NativeHandle) null))
			doc.Models.OverridePermanentColor((IEnumerable<ModelItem>) new ModelItem[1] {items.ElementAtOrDefault<ModelItem>(0)}, RED);
	
		if (!NativeHandle.ReferenceEquals((NativeHandle) items.ElementAtOrDefault<ModelItem>(1), (NativeHandle) null))
			doc.Models.OverridePermanentColor((IEnumerable<ModelItem>) new ModelItem[1] {items.ElementAtOrDefault<ModelItem>(1)}, GREEN);
	
		doc.ActiveView.LookFromFrontRightTop();
		doc.ActiveView.RequestDelayedRedraw((ViewRedrawRequests) 3);
		
		// Save the Clash image
		Bitmap clashImage = doc.ActiveView.GenerateThumbnail(500, 500);
		
		string clashResultImageDir = "" // wherever you need to save
		string clashResultImageName = clashResultImageDir + "\\" + test.DisplayName + "\\" + result.DisplayName + ".png";
		
		clashImage.Save(clashResultImageName, ImageFormat.Png);
	}

}

P.S: Unless you have a high-end PC, the exporting process can take quite a long time (especially if the number of clashes in the document is higher than 5000). You're welcome to suggest improvements.

 

7,047 Views
13 Replies
Replies (13)
Message 2 of 14

Anonymous
Not applicable

Hello,

 

First of all, thank you very, very much for this extremely useful piece of code!

 

I recently created an application which uses an adapted version of the present algorithm to generate clash images.

 

In order to test this code I executed it on a somewhat large model, which contains approximately 74000 clashes. The generation gives good results, and is relatively fast (3/4 images per second). However I noticed that, at a given point in time, the disk space on my C: drive ran out.

 

After performing an analysis using WinDirStat, I noticed that the disk space was being used by the temporary user (%TEMP%) folder. Sometime after generating a few thousand images, the process creates a folder called “ogs<number>”, and a subfolder called “<numer>_TextureCache”. Inside this subfolder, there are thousands of files with the extension “*.dds”. I also noticed that this happens when the process memory (roamer.exe, since it is a Navisworks plugin) reaches a given amount of approximately 11 GB. I also suspect that the fact that Navisworks starts caching values, it directly impacts the image generation rate.

 

After thoroughly searching the API documentation, I cannot find any parameter which lets me have any kind of control over the cache management.

 

Have you noticed this issue? Do you have any clue how to avoid it?

 

Thank you in advance

Message 3 of 14

garylzimmer
Enthusiast
Enthusiast

Thanks! Super useful! I understand everything leading up to this line:

// Hide everything except for the 2 clashing elements
		ModelItemCollection modelItemCollection1 = new ModelItemCollection();
		ModelItemCollection modelItemCollection2 = new ModelItemCollection();

What is going on with all those IEnumerator lines?

0 Likes
Message 4 of 14

jlpLATQ9
Participant
Participant

Hi man,  first of all thanks a lot! I spend a few hours trying to look for Navisworks own "GetClashDetectionViewport" but could not find it. This method seems to work - however not when handling 3D objects.  It will only 'isolate' the touching elements. This is the same Test and the first image is from Naviswork while the second is from this algorithm. Can you figure out how to "paint" the entire 3D elements and not just the touching elements ? 

jlpLATQ9_0-1639476783977.png

jlpLATQ9_1-1639476795325.png

 

The model is from the Revits Sample models (rac_basic_sample_project_2019.rvt). 

 

Can you help me mate ? 🙂 

0 Likes
Message 5 of 14

chowellWXPN8
Contributor
Contributor

I think because you set isolation to "Hide other".
You can switch it to "Dim other" and try to do.

 

chowellWXPN8_0-1639644261442.png

 

0 Likes
Message 6 of 14

burkhard.walger
Contributor
Contributor

To not disturb .NET type inference I have cleaned up the code a little bit:

void SaveAllClashPictures(string image_dir)
{ 
  var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
  var tests = doc.GetClash().TestsData.Tests;

  // Assuming you've already run all Clash Tests and have the results
  foreach(ClashTest test in tests) 
  {
    if(test.Children.Count <= 0) continue;
  
    foreach (ClashResult result in test.Children) 
    {
      var viewpoint = doc.CurrentViewpoint.Value;
      // Create a collection of the 2 clashing items from the ClashResult
      var items = new ModelItemCollection();
      items.Add(result.Item1);
      items.Add(result.Item2);
      // Select the 2 clashing items
      doc.CurrentSelection.Clear();
      doc.CurrentSelection.CopyFrom(items);
      // Focus on the clashing items
      doc.ActiveView.FocusOnCurrentSelection();
      // Make all items visible
      doc.Models.ResetAllHidden();
    
      // Hide everything except for the 2 clashing items
      var to_hide = new ModelItemCollection();
      var to_show = new ModelItemCollection();
      foreach (var item in doc.CurrentSelection.SelectedItems) 
      {
        // Collect all items upstream to the root
        if (item.AncestorsAndSelf != null)
          to_show.AddRange(item.AncestorsAndSelf);
        // Collect all subtrees of the item
        if (item.Descendants != null)
          to_show.AddRange(item.Descendants);
      }

      foreach(var item in to_show)
      {
        // If an item has no parent (root item) save the subtrees 
        if (! NativeHandle.ReferenceEquals(item.Parent, null))
          to_hide.AddRange(item.Parent.Children);
      }
      // Remove the to be shown items from list of the to be hidden items
      foreach(var item in to_show) 
        to_hide.Remove(item);
      // Hide all explicitly to be hidden items
      doc.Models.SetHidden(to_hide, true);
      // Hide all other items except those already hidden and those to be shown
      doc.Models.SetHidden(doc.Models
                              .SelectMany<Model, ModelItem>(
                                  (Func<Model, ModelItemEnumerableCollection>) (c => c.RootItem.Children)
                                )
                              .Except<ModelItem>(to_hide)
                              .Except<ModelItem>(to_show)
                            , true);
      // Remove selction color from the clashing items
      doc.CurrentSelection.Clear();

      // Adjust the camera and lighting
      var copy = viewpoint.CreateCopy();
      copy.Lighting = ViewpointLighting.None;
      doc.Models.ResetAllPermanentMaterials();
      doc.CurrentViewpoint.CopyFrom(copy);

      // Paint the clashing items in Red and Green respectively
      doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault<ModelItem>(0) }, Color.Red);
      doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault<ModelItem>(1) }, Color.Green);
      // Adjust the camera angle
      doc.ActiveView.LookFromFrontRightTop();
      // Prevent redraw for every test and item
      doc.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
    
      // Save the Clash image
      var clashImage = doc.ActiveView.GenerateThumbnail(500, 500);
    
      // Save the view image as PNG file
      var clashResultImageName = System.IO.Path.Combine(image_dir, test.DisplayName , result.DisplayName + ".png");
      clashImage.Save(clashResultImageName, ImageFormat.Png);
    }
  }  
}

 Hope that helps.

Message 7 of 14

jlpLATQ9
Participant
Participant

Did you find a solution to the problem in regards to Naviswork storing images in temp\ogs when using GenerateThumbnail? 

 

0 Likes
Message 8 of 14

DmitryStrelnikov
Explorer
Explorer

Hello. I tried to speed up the code and rewrote some of the code to reduce the number of loops. This allowed the application to speed up by 3-4 times. I hope it helps

public class ImageCreator
    {

        private int width = 500;
        private int height = 500;

        public void CreateAndFillImages(string directoryPath)
        {
            Document doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
            DocumentClash documentClash = doc.GetClash();
            DocumentClashTests oDCT = documentClash.TestsData;
            //hide all
            HideAllItems(doc);
            foreach (ClashTest test in oDCT.Tests)
            {
                List<ClashResult> outedResults = new List<ClashResult>();
                RecurseFillResults(test, ref outedResults);
                if (outedResults != null && outedResults.Count > 0 && !string.IsNullOrEmpty(directoryPath) && Directory.Exists(directoryPath))
                {

                    foreach (ClashResult r in outedResults)
                    {
                        CreateAndFillImage(doc, r, directoryPath, test.DisplayName);
                    }
                }
            }
            //show all
            doc.Models.ResetAllHidden();
        }


        private void CreateAndFillImage(Document doc, ClashResult clResult, string directoryPath, string testName)
        {
            if (clResult != null)
            {
                Viewpoint viewpoint = doc.CurrentViewpoint.Value;
                // Get the 2 clashing elements from the ClashResult
                ModelItem item1 = clResult.Item1;
                ModelItem item2 = clResult.Item2;
                if (item1 != null && item2 != null)
                {
                    ModelItemCollection items = new ModelItemCollection();
                    items.Add(item1);
                    items.Add(item2);
                    doc.CurrentSelection.Clear();

                    //Show clash items
                    ModelItemCollection modelItemsToShow = new ModelItemCollection();
                    foreach (ModelItem item in items)
                    {
                        if (item.AncestorsAndSelf != null)
                            modelItemsToShow.AddRange(item.AncestorsAndSelf);

                        if (item.Descendants != null)
                            modelItemsToShow.AddRange(item.Descendants);
                    }
                    doc.Models.SetHidden(modelItemsToShow, false);
                    // Select the 2 clashing elements
                    doc.CurrentSelection.Clear();
                    doc.CurrentSelection.CopyFrom(items);
                    doc.ActiveView.FocusOnCurrentSelection();
                    doc.CurrentSelection.Clear();

                    // Adjust the camera, lighting, and paint the clashing elements in Red and Green respectively
                    Viewpoint copy = viewpoint.CreateCopy();
                    copy.Lighting = 0;

                    doc.Models.ResetAllPermanentMaterials();
                    doc.CurrentViewpoint.CopyFrom(copy);

                    Autodesk.Navisworks.Api.Color RED = Autodesk.Navisworks.Api.Color.Red;
                    Autodesk.Navisworks.Api.Color GREEN = Autodesk.Navisworks.Api.Color.Green;

                    if (!NativeHandle.ReferenceEquals(items.ElementAtOrDefault(0), null))
                        doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault(0) }, RED);

                    if (!NativeHandle.ReferenceEquals(items.ElementAtOrDefault(1), null))
                        doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault(1) }, GREEN);

                    doc.ActiveView.LookFromFrontRightTop();
                    doc.ActiveView.RequestDelayedRedraw((ViewRedrawRequests)3);

                    // Save the Clash image
                    using (Bitmap clashImage = doc.ActiveView.GenerateThumbnail(width, height))
                    {
                        string clashResultImageName = Path.Combine(directoryPath, $"{testName}_{clResult.DisplayName}.png");

                        clashImage.Save(clashResultImageName, ImageFormat.Png);
                    }
                    //Hide clashelements
                    doc.Models.SetHidden(modelItemsToShow, true);
                }
            }
        }

        private void HideAllItems(Document doc)
        {
            ModelItemCollection rootItems = new ModelItemCollection();
            ModelItemCollection allitems = new ModelItemCollection();
            foreach (var m in doc.Models)
            {
                if (m.RootItem != null)
                {
                    rootItems.Add(m.RootItem);
                }
            }
            GetAllItems(rootItems, ref allitems);
            if (allitems != null && allitems.Count > 0)
            {
                doc.Models.SetHidden(allitems, true);
            }
        }

        private void GetAllItems(IEnumerable<ModelItem> items, ref ModelItemCollection allItems)
        {
            if (items != null)
            {
                foreach (ModelItem item in items)
                {
                    allItems.Add(item);
                    if (item.Children != null)
                    {
                        GetAllItems(item.Children, ref allItems);
                    }

                }
            }
        }

        private void RecurseFillResults(GroupItem group, ref List<ClashResult> outedResults)
        {

            foreach (SavedItem child in group.Children)
            {
                /* If we only wanted to access first-level children
                 * without reference to whether they were groups or results
                 * then we could:
                 *
                 * // access groups and results via shared interface
                 * IClashResult result = child as IClashResult;
                 * 
                 * operate on that and not recurse further. */

                // GroupItem is the base-class of ClashResultGroup which defines
                // group-like behaviour, if we needed to access ay ClashResultGroup properties
                // we could cast to that equivalently... 
                GroupItem child_group = child as GroupItem;

                // is this a group?
                if (child_group != null)
                {
                    // operate on the group's children
                    RecurseFillResults(child_group, ref outedResults);
                }
                else
                {
                    // Not a group, so must be a result.
                    ClashResult result = child as ClashResult;
                    if (outedResults != null)
                    {
                        outedResults.Add(result);
                    }
                }
            }
        }
    }
Message 9 of 14

DAHAM_CHUNGHUNHO
Participant
Participant

Not tested yet. But might be handy.

 

 

 

 

 

using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Interop;
using System.Drawing;
using System.IO;

//Autodesk.Navisworks.Api.Interop.ClashCurrentIssue

namespace ImageCreator
{
    public class ImageCreator
    {
        private int width = 500;
        private int height = 500;
        
        public void CreateImage(string directoryPath)
        {
            Document doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
            var instance = LcClCurrentIssue.GetInstance((LcOpState)Autodesk.Navisworks.Api.Application.MainDocument.State);
            var currenttest = ClashCurrentIssue.CurrentTest; // get selected(clicked) Test from gui
            foreach (SavedItem result in currenttest.Children)
            {
                
                instance.SetCurrentIssueFromSavedItem((SavedItem)result,0,false);
                instance.OnGotFocus();


                using (Bitmap clashImage = doc.ActiveView.GenerateImage(ImageGenerationStyle.Scene,width, height))
                {
                    string clashResultImageName = Path.Combine(directoryPath,
                        $"{currenttest.DisplayName}_{result.DisplayName}.png");
                    clashImage.Save(clashResultImageName);

                }
            }

        }


    }
}

 

 

 

 

 

 

 

 

Message 10 of 14

DmitryStrelnikov
Explorer
Explorer

This method works great of Navisworks api 2019 with method:

doc.ActiveView.GenerateThumbnail(width, height))

I was happy. However this does not work with Navisworks 2022 method:

doc.ActiveView.GenerateImage(ImageGenerationStyle.Scene,width, height))

Do you have a solution to this problem?

0 Likes
Message 11 of 14

DAHAM_CHUNGHUNHO
Participant
Participant
Hi there.

My version is 2021, so it's hard to give you an accurate answer.

According to the API documentation in the 2022 version,
doc.ActiveView seems to only provide the GenerateImage method.

I think your code is correct, but did you write the code to save the bitmap?
The GenerateImage method returns a bitmap, so you need code to save the image.

If this is not the problem, please let me know what you are trying to do or provide part of the code you wrote to see what the problem is.
0 Likes
Message 12 of 14

DmitryStrelnikov
Explorer
Explorer

It was my fault. I start code with closed Navisworks "clash detective" window (I got ClashResult by code). If "clash detective" window is open, method creates images correctly and very quickly. So before start plugin "clash detective" window must be opened. And should use ImageGenerationStyle.ScenePlusOverlay.

Message 13 of 14

DAHAM_CHUNGHUNHO
Participant
Participant
Ah, so I think the problem was in the ClashCurrentIssue.CurrentTest part.

This code refers to the test result selected in the GUI, i.e. in the "clash detective" window, so if that window is closed, as you say, that could be causing the problem.

Thanks for sharing your example.
Message 14 of 14

dotman7
Observer
Observer

 

private void deleteTexture()
{
    try
    {
        string tempFolderPath = Path.GetTempPath(); // Get temp folder directory
        string ogsFolderPath = Path.Combine(tempFolderPath, "ogs");

        // Get first folder directory from ogs folder
        string[] ogsSubDirectories = Directory.GetDirectories(ogsFolderPath);
        if (ogsSubDirectories.Length > 0)
        {
            string firstSubDirectory = ogsSubDirectories[0];

            // Get first folder directory from subdirectory
            string[] foldersInFirstSubDirectory = Directory.GetDirectories(firstSubDirectory);
            if (foldersInFirstSubDirectory.Length > 0)
            {
                string firstFolderPath = foldersInFirstSubDirectory[0];

                // For Debugging (Check if directory is temp/ogs/(number)/(number)_TextureCache
                /*if (pathDebug)
                {
                    MessageBox.Show(firstFolderPath);
                    pathDebug = false;
                }*/

                // Get file size info
                DirectoryInfo directoryInfo = new DirectoryInfo(firstFolderPath);
                FileInfo[] files = directoryInfo.GetFiles();    
                long fileSizeInBytes = 0;
                foreach (FileInfo file in files)
                {
                    fileSizeInBytes += file.Length;
                }
    
                long fileSizeInGB = fileSizeInBytes / (1024 * 1024 * 1024); // Change Byte to Gigabyte

                // Delete TextureCache folder if size is more than 5GB
                if (fileSizeInGB > 5)
                {
                    Directory.Delete(firstFolderPath, true);
                }
            }
            else
            {
                MessageBox.Show("No folder in subdirectory");
            }
        }
        else
        {
            MessageBox.Show("No folder in ogs folder");
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

 

I also worked on this problem and solved this by accessing the temp file each time the image is created(using GenerateImage()) and manually deleting the <number>_TextureCache folder whenever the size is more than certain gigabytes.

Tell me if there is a better solution!!

0 Likes