Grasshopper: Get intersection points between a list of lines with C#

The following code will produce a list of points that correspond with the intersection points between an assorted list of lines.

It is written for the C# component in Grasshopper, but can be adapted to a compiled component if desired.

Note the check that 0<a<1 and 0<b<1. This represents the requirement that we want intersections to occur along the length of the line, otherwise we aren’t interested in them. If we are also interested in implied intersections (i.e. where we imagine each line continues to infinity in both directions) then we can discard this ‘if’ statement.

And a note to my future self, this code worked on quick tests but was buggy on a real project, so the code below is for reference only!

  private void RunScript(List<Line> x, ref object A)
  {

    List<Point3d> rtn_pts = new List<Point3d>();
    for (int i = 0; i < x.Count - 1; i++)
    {
      for (int j = i + 1; j < x.Count; j++)
      {
        double a;
        double b;
        Rhino.Geometry.Intersect.Intersection.LineLine(x[i], x[j], out a, out b);
        if(a >= 0 && a <= 1 && b > 0 && b < 1)
        {
          rtn_pts.Add(new Point3d(x[i].PointAt(a)));
        }
      }
    }

    A = rtn_pts;

  }

Grasshopper: Manipulate the branches of an existing tree with C#

Say you have a datatree in Grasshopper. It has two levels of branches, and each branch of data has a number of items. We want to ‘flip’ the branches around, but leave the lists of data at the end of each branch intact.

As an example, let’s use the following structure:

And here’s what we want it to look like after:

As a param mapper, this action would look like this:

grasshopper-param-mapper

How would we perform this action using the C# code block?

  private void RunScript(DataTree<object> x, object y, ref object A)
  {

    DataTree<object> rtnTree = new DataTree<object>();
    for (int p = 0; p < x.BranchCount; p++)
    {
      int[] pathint = x.Path(p).Indices;
      GH_Path flippath = new GH_Path(new[] {pathint[1],pathint[0]});

      foreach (var item in x.Branch(new[] {pathint[0],pathint[1]}))
      {
        rtnTree.Add(item, flippath);
      }

    }

    A = rtnTree;

Send a command from Grasshopper to Rhino in C#

In Grasshopper, it’s possible to send commands directly from Grasshopper to Rhino. How? By using the C# component.

Simply add the following line to your C# code.

Rhino.RhinoApp.RunScript(string script, bool echo);
//script: The command to send
//echo: Set to true for the command to appear in Rhino's command prompt

For example, if I want to automatically save the Rhino document, I could add the following to a C# component:

Rhino.RhinoApp.RunScript("Save", true);

For more complex commands, we can use some special syntax. For example, if I want to save the file with a custom filename, I can use:

Rhino.RhinoApp.RunScript("_-Save test3.3dm", true);

The hyphen allows us to modify parameters, and the underscore allows us to enter more than one command at once without Rhino waiting for manual user input.

More information

The full list of Rhino commands is available here.

For more detail about using the RunScript method, read this.

C#: Convert an Image to a BitmapImage

How to convert a System.Drawing.Image to a System.Windows.Media.Imaging.BitmapImage.

using System.Windows.Media.Imaging;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

//...

public BitmapImage Convert(Image img)
{
    using (var memory = new MemoryStream())
    {
        img.Save(memory, ImageFormat.Png);
        memory.Position = 0;

        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = memory;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();

        return bitmapImage;
    }
}

This and other approaches to solving this problem can be found on this StackOverflow page.

How to implement IEnumerable in C#

When you create your own class, you may want an easy way to cycle though the data within the class.

Ever wondered how the ‘foreach’ works?

foreach (double val in MyClass)
{
    //do something with val
}

Something like this is only possible if we tell MyClass what values within MyClass we want to cycle through.

This is possible by implementing IEnumerable.

An example

Let’s say MyClass looks like this:

using System.Collections.Generic;

namespace MyNameSpace
{
    public class MyClass
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;
    }
}

What if we create an instance of MyClass, and want to run a foreach loop on it?

What we’d like is for the foreach to loop through the three values contained in myData.

But it doesn’t work yet. What’s missing?

error enumerator

The error reads: foreach statement cannot operate on variables of type ‘MyNamespace.MyClass’ because ‘MyNamespace.MyClass’ does not contain a public definition for ‘GetEnumerator’.

So how do we add the definition for GetEnumerator?

How to implement IEnumerable in C#

We do this by implementing the IEnumerator interface to our class.

using System.Collections.Generic;
using System.Collections;

namespace MyNamespace
{
    public class MyClass : IEnumerable
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;

        public IEnumerator GetEnumerator()
        {
            foreach(double val in myData)
            {
                yield return val;
            }
        }
    }
}

We have done three things:

  1. Added using System.Collections;
  2. Added : IEnumerable to let the compiler know that there’s something in the class we can iterate through
  3. Added method GetEnumerator(). This is what is called when we try to iterate through an instance of the class.

Note the use of the special keyword yield. This allows us to return an enumerated value without breaking from the GetEnumerator method.

So, how does this look now?

working-enumerator

Perfect!

Specify a return type with IEnumerable

Notice how we never specified anywhere that the enumerator would be returning a double?

By default, the enumerator returns an object. When we run our foreach loop, it is clever enough to implicitly cast each enumerated output as a double, since we specified that we wanted each item to be of type double.

What if another programmer, using our class, doesn’t know to expect a double, and they just use a var (or something else)?

We get an object instead – and an error.

enum-error-var

Can we tell GetEnumerator which data type it should be returning?

In your class, implement IEnumerable<T> instead. Then, there are two methods (not one) to implement.

using System.Collections.Generic;
using System.Collections;

namespace MyNamespace
{
    public class MyClass : IEnumerable<double> //Specify your return type in the angle brackets.
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;

        //Put your enumerator code in this method. Specify your return type in the angle brackets again here.
        public IEnumerator<double> GetEnumerator()
        {
            foreach(double val in myData)
            {
                yield return val;
            }
        }

        //This method is also needed, but usually you don't need to change it from this.
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

Now, we should find that, even if we type ‘var’, Visual Studio knows what type to expect.

var-enum-working

This is the way that I would recommend. Specify your data types – it will help reduce bugs and mistakes later on in your project.

C#: Get DPI of screen

It used to be the case that most monitors had a screen resolution of 96DPI. When developing software, this meant developers could hard-code values when designing their interfaces.

This is no longer the case. The latest generation of screens and laptops are likely to have very high resolutions, perhaps greater than 200DPI (dots per inch).

At this resolution, fonts and windows would become uselessly small. This is my 13″ laptop display at 3200×1800:

desktop

As you can see, it is quite unusable. Windows can therefore scale up window elements to a more natural size.

Here is my screen at 200% scaling:

desktop2

What this means for developers

When we develop software, we must make sure it works well across a range of user environments, and this now includes testing for a range of screen resolutions.

Fortunately, modern UI libraries will automatically scale built-in elements according to the screen. For example, if you specify the width of a button to be 100 pixels in WPF, on my screen with a scaling of 200%, this button would automatically be rendered at 200 pixels.

However, you may find that some custom elements do not scale properly. You may also be using some ‘fudge factors’ in your code-behind (such as the C# file associated with a XAML file in WPF) that are constant. These may be written in such a way that your UI library doesn’t scale them.

For example, say you have a margin of 10 pixels written in your code-behind. How do you tell your program to scale up this value to any scaling factor?

Get the scaling factor in C#

This is quite easy. Simply calculate the following value in your code:

double factor = System.Windows.PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice.M11;

Then, wherever you have a constant value in your code-behind, multiply it by factor, e.g.

button.Left = 10 * factor;

Output

The code above returns a value that is usually between 1 and 2.5. If the user’s system DPI settings are set to 200%, a value of 2.0 should be returned.

To calculate the scaling factor in terms of DPI, multiply by 96. For example, if the system settings are at 200%, the DPI is 196DPI. Note that this value can be changed by the user if they want the screen to appear smaller or larger, so the concept of an ‘inch’ doesn’t always rigorously apply.

C#: Simple BackgroundWorker example

Here is a simple example on how to use the BackgroundWorker class in C#.

What is the BackgroundWorker?

Are you writing a Windows program with some heavy calculations going on in the background? The BackgroundWorker class will take those calculations and put them in a separate thread, helping to prevent your UI from freezing up.

Multi-threaded programming is tricky at the best of times. The fundamental problem is that you don’t want two threads trying to access the same bit of memory at once. The BackgroundWorker simplifies a lot of the work you would otherwise need to do yourself, but it can still be difficult to set it up.

BackgroundWorker example

This example downloads an image from the Internet and saves it to the user’s desktop. It does this 10 times (so we can see how to monitor progress). The image address is provided by the user in a WPF form, and we report the number of files downloaded in a progress bar on that form.

The form stays responsive the whole time, and doesn’t lock up while the files are being downloaded – something which would happen if we didn’t create our own thread.

background worker c# example program UI

Example code

Create a new C# WPF project in Visual Studio.

XAML

We need to create a form with a textbox, a button, a progress bar and a label. This isn’t essential to the BackgroundWorker, but it will help create our example program.

If you aren’t familiar with WPF or XAML, XAML is the markup language used to create interfaces in WPF, much like HTML to web development. Here is the contents of my MainWindow.XAML used to create the form above.

<Window x:Class="BackgroundWorkerExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BackgroundWorkerExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    
    <DockPanel>
        
        <DockPanel LastChildFill="True" DockPanel.Dock="Top" Margin="10">
            <Label DockPanel.Dock="Left" Width="100">URL</Label>
            <Button Name="btnGo" Click="btnGo_Click" DockPanel.Dock="Right" Width="100">Go</Button>
            <TextBox Name="tbURL"/>
        </DockPanel>
        
        <DockPanel LastChildFill="True" DockPanel.Dock="Top" Margin="10">
            <Label DockPanel.Dock="Left" Width="100">Progress</Label>
            <ProgressBar DockPanel.Dock="Bottom" Name="progBar" Value="0" />
        </DockPanel>

        <DockPanel LastChildFill="True">
            <Label DockPanel.Dock="Top">Status</Label>
            <ScrollViewer><Label Name="lblStatus" /></ScrollViewer>
        </DockPanel>
        
    </DockPanel>
    
</Window>

C# code

The actual code goes into MainApplication.xaml.cs. Here is the contents of my file.

It looks pretty long, but it’s not so bad. A lot of it is actually comments…

using System;
using System.Windows;
using System.ComponentModel;
using System.IO;

namespace BackgroundWorkerExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnGo_Click(object sender, RoutedEventArgs e)
        {
            lblStatus.Content = "";

            BackgroundWorker worker = new BackgroundWorker();

            //BackgroundWorker is event-driven. We use events to control what happens
            //during and after calculations.
            //First, we need to set up the different events.
            worker.DoWork += Worker_DoWork;
            worker.ProgressChanged += Worker_ProgressChanged;
            worker.WorkerReportsProgress = true;
            worker.RunWorkerCompleted += Worker_RunWorkerCompleted;

            //Then, we set the Worker off. 
            //This triggers the DoWork event.
            //Notice the word Async - it means that Worker gets its own thread,
            //and the main thread will carry on with its own calculations separately.
            //We can pass any data that the worker needs as a parameter.
            worker.RunWorkerAsync(tbURL.Text);
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            //DoWork is the most important event. It is where the actual calculations are done.

            System.Net.WebClient client = new System.Net.WebClient();

            for (int i = 0; i < 10; i++)
            {
                //We pass data to the worker using the Argument property.
                //Don't try to read data from the form directly.
                string url = (string)e.Argument;

                //download image
                byte[] imageStream = client.DownloadData(url);
                MemoryStream memoryStream = new MemoryStream(imageStream);
                System.Drawing.Image img = System.Drawing.Image.FromStream(memoryStream);

                //save image to computer
                string desktop = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
                img.Save(desktop + @"\image " + i.ToString() + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

                //Now that the image is saved, we can update the Worker's progress.
                //We do this by going back to the Worker with a cast
                int progress = (i + 1) * 10; //Between 0-100
                ((BackgroundWorker)sender).ReportProgress(progress);
            }

            //When finished, the thread will close itself. We don't need to close or stop the thread ourselves.  
        }

        private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //This method is called whenever we call ReportProgress()
            //Note that progress is not calculated automatically. 
            //We need to calculate the progress ourselves inside Worker_DoWork.
            //This method is optional.

            lblStatus.Content += e.ProgressPercentage.ToString() + "% complete. \n";
            progBar.Value = e.ProgressPercentage;
        }

        private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //This method is optional but very useful. 
            //It is called once Worker_DoWork has finished.

            lblStatus.Content += "All images downloaded successfully.";
            progBar.Value = 0;
        }


    }
}

Running the program

Here is a picture of a satsuma.

satsuma-mandarin[1]

Here is its URL:

http://james-ramsden.com/wp-content/uploads/2016/05/satsuma-mandarin1.jpg

If we put this URL into this program, we should be able to download this image 10 times over onto our desktop.

backgroundworker c# example program running

Here it is downloading the images, depositing lots of satsumas on my desktop. Or are they mandarins? I don’t really know.

mandarins satsumas

You may also want to try downloading some larger images that take longer to download. The BackgroundWorker works as expected. The interface is updated with each successful download. (If we didn’t use the BackgroundWorker, the interface would freeze until all 10 had been downloaded.) And the interface remains responsive the entire time.

Further reading

There are many ways to multithread in .NET, and the correct choice depends on what you are trying to do.

For instance, I recently had heavy calculations on a problem in Grasshopper, on entirely back-end code. This was dealt with by using Parallel.ForEach loops, which converts a ForEach loop into a multi-threaded equivalent.

If you’re looking to calculate something as quickly as possible using all the cores on your machine, then this is a good place to start. This approach depends on your task being able to be divided up into independent subtasks, so that each subtask can be calculated separately. (If one subtask depends on the output of another, it is going to be difficult to multithread.)

All in all, multithreading is a big topic, with many approaches possible, and there are many ways you can unfortunately get it wrong. It’s not something you are going to master in a day. But it’s worth spending the time on it, and it’s very rewarding when you do get it working 🙂

C#: Recursively get all files in a folder and its subfolders

How to recursively get all files in a folder, including files contained within subfolders.

Or, in other words, how to find every file contained within a parent folder, and within any folders inside the parent folder.

The easy way

There is a method built into .NET, though it is buried as an overload, so you may not have known it was there until someone points it out to you…

foreach (string file in System.IO.Directory.GetFiles(
    parentDirectory, "*",SearchOption.AllDirectories))
{
    //do something with file
}

This loops through every file contained within the folder, including all files contained within any subfolders. Each loop returns a string of the address of each file.

The second parameter is a search filter. The value above of "*" simply means “return anything”. We could filter for Word documents by changing this to "*.docx", for example.

The alternative way

Microsoft offered a solution of their own in this old article, which provides an interesting iterative approach.

I have modified the above method slightly to also include files immediately within the parent directory. Microsoft’s solution currently doesn’t do this

The code below adds all filepaths to a List. If you wish to do something else, you need to change what happens within DoAction(). You might actually want to give DoAction a more sensible name too, depending on what your action is.

        List<string> files = new List<string>();

        private void getFilesRecursive(string sDir)
        {
            try
            {
                foreach (string d in Directory.GetDirectories(sDir))
                {
                    getFilesRecursive(d);
                }
                foreach (var file in Directory.GetFiles(sDir))
                {
                    //This is where you would manipulate each file found, e.g.:
                    DoAction(file);
                }
            }
            catch (System.Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        private void DoAction(string filepath)
        {
            files.Add(filepath);
        }

Get file types supported by BitmapImage

The BitmapImage class is the preferred way of handing image files in a WPF application.

A great advantage of BitmapImage is that it can open and handle a wide range of image types. It uses the Windows Imaging Component, which allows third parties to incorporate custom image formats into BitmapImage.

Ultimately, what this means is that, quite amazingly, I am able to open fancy file types like Nikon’s NEF raw format in a way that feels native in my C# applications.

But this presents a challenge. If a user wants to open an image in my application, how do I know if BitmapImage can handle it?

I could use a try-catch, but this is quite inefficient. And since BitmapImage is extensible, I can’t just check the file extension against a hard-coded list. I need to be able to check what file types are supported on a particular machine.

I presented this problem on this Stack Overflow post, where NineBerry provided a solution.

How the solution works

The gist of NineBerry’s solution is that we look in the registry for any third party codecs that help BitmapImage to open any additional file types. Once we have found them, we can then read which file extensions are typically associated with these file types.

NineBerry’s solution doesn’t find natively supported file types. We can use Microsoft’s documentation to hard-code these natively-supported types.

Then, when a user wants to open a file, we can compare its file extension with the extensions we found above.

Note that this isn’t fool-proof. A file with a supposedly valid extension may still not open. The file may be corrupted, have an incorrect extension, or the extension may have multiple uses. Still, this method should catch the vast majority of unsuitable files, and help to speed up your program.

How to use the class in your program

I took NineBerry’s code and turned it into a class with a few useful methods and properties. Here you can…

Check if a filename contains a BitmapImage-suitable file extension

string path = "myImage.jpg";
BitmapImageCheck bic = new BitmapImageCheck();
bool imageValid = bic.IsExtensionSupported(path); //returns true

path can be a file name with extension, an extension by itself, or a full address.

Get a list of supported file extensions

Get a list of file extensions provided natively, provided by third party decoders, or these two lists combined.

BitmapImageCheck bic = new BitmapImageCheck();
string[] supportedExtensions = bic.AllSupportedExtensions;

Get information about the decoders found on your system

Use the ToString() method to get information about where support for different file extensions is coming from.

BitmapImageCheck bic = new BitmapImageCheck();
MessageBox.Show(bic.ToString());

bitmapimage image extensions messagebox

C# class to get supported file types

Copy the code below into a new C# file in your project. The only thing you might want to change is the namespace.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32;

namespace WpfApplication1.Classes
{
    /// <summary>
    /// Provides methods for checking whether a file can likely be opened as a BitmapImage, based upon its file extension
    /// </summary>
    public class BitmapImageCheck : IDisposable
    {
        #region class variables
        string baseKeyPath;
        RegistryKey baseKey;
        private const string WICDecoderCategory = "{7ED96837-96F0-4812-B211-F13C24117ED3}";
        private string[] allExtensions;
        private string[] nativeExtensions;
        private string[] customExtensions;
        #endregion

        #region constructors
        public BitmapImageCheck()
        {
            if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
            {
                baseKeyPath = "Wow6432Node\\CLSID";
            }
            else
            {
                baseKeyPath = "CLSID";
            }
            baseKey = Registry.ClassesRoot.OpenSubKey(baseKeyPath, false);
            recalculateExtensions();
        }
        #endregion

        #region properties
        /// <summary>
        /// File extensions that are supported by decoders found elsewhere on the system
        /// </summary>
        public string[] CustomSupportedExtensions
        {
            get
            {
                return customExtensions;
            }
        }

        /// <summary>
        /// File extensions that are supported natively by .NET
        /// </summary>
        public string[] NativeSupportedExtensions
        {
            get
            {
                return nativeExtensions;
            }
        }

        /// <summary>
        /// File extensions that are supported both natively by NET, and by decoders found elsewhere on the system
        /// </summary>
        public string[] AllSupportedExtensions
        {
            get
            {
                return allExtensions;
            }
        }
        #endregion

        #region public methods
        /// <summary>
        /// Check whether a file is likely to be supported by BitmapImage based upon its extension
        /// </summary>
        /// <param name="extension">File extension (with or without leading full stop), file name or file path</param>
        /// <returns>True if extension appears to contain a supported file extension, false if no suitable extension was found</returns>
        public bool IsExtensionSupported(string extension)
        {
            //prepare extension, should a full path be given
            if (extension.Contains("."))
            {
                extension = extension.Substring(extension.LastIndexOf('.') + 1);
            }
            extension = extension.ToUpper();
            extension = extension.Insert(0, ".");

            if (AllSupportedExtensions.Contains(extension)) return true;
            return false;
        }
        #endregion

        #region private methods
        /// <summary>
        /// Re-calculate which extensions are available on this system. It's unlikely this ever needs to be called outside of the constructor.
        /// </summary>
        private void recalculateExtensions()
        {
            customExtensions = GetSupportedExtensions().ToArray();
            nativeExtensions = new string[] { ".BMP", ".GIF", ".ICO", ".JPEG", ".PNG", ".TIFF", ".DDS", ".JPG", ".JXR", ".HDP", ".WDP" };

            string[] cse = customExtensions;
            string[] nse = nativeExtensions;
            string[] ase = new string[cse.Length + nse.Length];
            Array.Copy(nse, ase, nse.Length);
            Array.Copy(cse, 0, ase, nse.Length, cse.Length);
            allExtensions = ase;
        }

        /// <summary>
        /// Represents information about a WIC decoder
        /// </summary>
        private struct DecoderInfo
        {
            public string FriendlyName;
            public string FileExtensions;
        }

        /// <summary>
        /// Gets a list of additionally registered WIC decoders
        /// </summary>
        /// <returns></returns>
        private IEnumerable<DecoderInfo> GetAdditionalDecoders()
        {
            var result = new List<DecoderInfo>();

            foreach (var codecKey in GetCodecKeys())
            {
                DecoderInfo decoderInfo = new DecoderInfo();
                decoderInfo.FriendlyName = Convert.ToString(codecKey.GetValue("FriendlyName", ""));
                decoderInfo.FileExtensions = Convert.ToString(codecKey.GetValue("FileExtensions", ""));
                result.Add(decoderInfo);
            }
            return result;
        }

        private List<string> GetSupportedExtensions()
        {
            var decoders = GetAdditionalDecoders();
            List<string> rtnlist = new List<string>();

            foreach (var decoder in decoders)
            {
                string[] extensions = decoder.FileExtensions.Split(',');
                foreach (var extension in extensions) rtnlist.Add(extension);
            }
            return rtnlist;
        }

        private IEnumerable<RegistryKey> GetCodecKeys()
        {
            var result = new List<RegistryKey>();

            if (baseKey != null)
            {
                var categoryKey = baseKey.OpenSubKey(WICDecoderCategory + "\\instance", false);
                if (categoryKey != null)
                {
                    // Read the guids of the registered decoders
                    var codecGuids = categoryKey.GetSubKeyNames();

                    foreach (var codecGuid in GetCodecGuids())
                    {
                        // Read the properties of the single registered decoder
                        var codecKey = baseKey.OpenSubKey(codecGuid);
                        if (codecKey != null)
                        {
                            result.Add(codecKey);
                        }
                    }
                }
            }

            return result;
        }

        private string[] GetCodecGuids()
        {
            if (baseKey != null)
            {
                var categoryKey = baseKey.OpenSubKey(WICDecoderCategory + "\\instance", false);
                if (categoryKey != null)
                {
                    // Read the guids of the registered decoders
                    return categoryKey.GetSubKeyNames();
                }
            }
            return null;
        }

        #endregion


        #region overrides and whatnot

        public override string ToString()
        {
            string rtnstring = "";

            rtnstring += "\nNative support for the following extensions is available: ";
            foreach (var item in nativeExtensions)
            {
                rtnstring += item + ",";
            }
            if (nativeExtensions.Count() > 0) rtnstring = rtnstring.Remove(rtnstring.Length - 1);

            var decoders = GetAdditionalDecoders();
            if (decoders.Count() == 0) rtnstring += "\n\nNo custom decoders found.";
            else
            {
                rtnstring += "\n\nThese custom decoders were also found:";
                foreach (var decoder in decoders)
                {
                    rtnstring += "\n" + decoder.FriendlyName + ", supporting extensions " + decoder.FileExtensions;
                }
            }

            return rtnstring;
        }

        public void Dispose()
        {
            baseKey.Dispose();
        }
        #endregion
    }
}

Get more information about the decoders on your system

The public methods implemented were intended to make available a basic level of file extension information.

For more advanced tasks, some of the private methods in the class could be made public. It is possible to expose further information about each codec, such as their locations in the registry.

Change the colour of the Grasshopper canvas

To change the colour of the Grasshopper canvas, Johannes Braumann very helpfully provided a C# script. However, the script is now out of date with recent builds of Grasshopper, having been published in 2010. Below is an updated, working version.

This script helped me change the colour of the Grasshopper canvas to be white – which has saved me a lot of time in updating images in posts like this.

The component looks like this:

Grasshopper change canvas colour component

Do-it-yourself C# script

Paste the script into a C# component. Take care to set input names and types to match the parameters below. This code is lightly modified from Braumann’s script.

  private void RunScript(bool switcher, Color canvas_back, Color canvas_grid, ref object A)
  {
    if (switcher == true)
    {
      Grasshopper.GUI.Canvas.GH_Skin.canvas_grid = canvas_grid;
      Grasshopper.GUI.Canvas.GH_Skin.canvas_back = canvas_back; 
      Grasshopper.GUI.Canvas.GH_Skin.canvas_edge = Color.FromArgb(255, 0, 0, 0); 
      Grasshopper.GUI.Canvas.GH_Skin.canvas_shade = Color.FromArgb(80, 0, 0, 0); 
    }
    else
    {
      //DEFAULTS
      Grasshopper.GUI.Canvas.GH_Skin.canvas_grid = Color.FromArgb(30, 0, 0, 0);
      Grasshopper.GUI.Canvas.GH_Skin.canvas_back = Color.FromArgb(255, 212, 208, 200); 
      Grasshopper.GUI.Canvas.GH_Skin.canvas_edge = Color.FromArgb(255, 0, 0, 0); 
      Grasshopper.GUI.Canvas.GH_Skin.canvas_shade = Color.FromArgb(80, 0, 0, 0);
    }
  }

Download a ready-made component

If you would rather not play about with the C# component, you can just open this Grasshopper file instead.