C# Script to Export to Colored Layers from Grasshopper without Baking

I was working on a grasshopper script here: Regenerating 3D lines after nesting operation and ended up trying to get the part of my script to export files from grasshopper without baking the geometry working.

I ended up putting a lot of time into this part of it and it’s working pretty well now, so I thought I would start a new topic with an appropriate name so maybe others who could use this script could find it.

@Joseph_Oster gave me some encouragement and had some great suggestions for it. I did a bit of error checking and refinement, and it’s working pretty well, but it could use some more polishing.

What I am trying to accomplish with this script is an easy way to export various geometries in different colors and on different layers from Grasshopper without baking anything. It uses the Grasshopper trees to keep things organized… I made a demo file:
just attach the geometry for each layer, give the layers names and colors and export!

There is a bug in Grasshopper when using the export command that I have not been able to solve completely, but I at least identified it.
if a Grasshopper C# script contains:

Rhino.RhinoApp.RunScript("_-Export " + exportOptions + " \"" + filepath + "\"" + " _Enter", true);

when you run the script with whatever input you use to enable the script, if that input goes back to false before the Export finishes, then it leaves Grasshopper in this very odd state where it’s locked and you can’t do anything with anything. you can’t move components, or edit them or anything, it’s completely locked. Strangely, you can run the script again, and if you do, it will always unlock. I’ve been trying to figure it out and its defiantly only Rhino.RhinoApp.RunScript("_-Export " that causes this (that I know of)
The larger the file you are trying to export, the more you notice this.

I have tried a LOT to keep the input active trying to implement timers and all kinds of schemes to work around this bug, but in the end, the way Grasshopper’s solver works, Timers and things like that just won’t work. I think that is why whenever you see a script like this it’s connected to a Boolean Toggle,
because with the toggle, it stays true until you toggle it back. but the toggle is also a pain. first of all, you need to double click it… that’s a pain, also you MUST remember to shut it back off of it will export every time you make a change. so that’s another pain.

So, I tried something else… an Analog input!


since it takes time to rotate the knob all the way around, and all the way back, the input is kept active until the Export is finished… you are the timer!

so, this solves another issue I was having as well. I wanted to open a Rhino Document, process geometry from the document then extracts the document name and export a new file in a new directory with the same name, but with " - Processed" appended to the end of it. The problem is, when you just load a new document into Rhino, Grasshopper doesn’t run the scripts that could get the new file name, the AnalogInput solves this issue as well, and in fact give you a way to check the way the exported filename will be assembled and give you a chance to look at the name before doing the export.

So you start with the minimum value on the Analog Input and as you rotate it, it builds the export filename:

I tried to make this a universal script, so you can give it your own names or let it get the Rhino document names as needed. I tried to set default values for everything so it would work easily and let you tweak it as needed. Rotating it more, I get the Rhino Document name because I don’t have the filename input connected

then as you rotate it, it prepends as and/or appends to the file name, adds the extension and checks to see if you ended up with the same name and path as your Rhino file, and if so, it adds “- Exported” to it so you don’t overwrite your file.

if you like the exported path and filename, you just keep rotating it right until it exports, if you don’t like it, rotate all the way left and start over. Note that once you start the export you need to leave the knob all the way to Maximum until it’s done otherwise you still get the Grasshopper lockout problem.

After I got that working, I thought that it would be cool to also use the Boolean Button… so I made it autodetect the Button or the Knob! to do this, I made the knob in the range of 2.55 to 3.45 that prevents it from having a 0 or a 1, so 0 and 1 can be a digital input on the same pin… I also default the input to -1 to mean it’s disconnected.

So when you are setting it up, connect the knob, turn it slowly, make sure you like the directories and filename options… once you get it all working right, switch it out to the button and just remember to hold it down until the export finishes.


Here’s a description of the inputs and outputs:
Geometeries - where you connect a tree of Geometry
LayerNames - where you connect a tree of layer names
LayerColors - where you connect a tree of layer colors
Directory - the directory to export into the \ at the end is optional… it will come out right either way
FilePrepend - something added to the beginning of the file name, intended for use when you are extracting the rhino filename
Filename - what you want the filename to be, if you leave it blank it will use the Rhino document name, if there is no Rhino document loaded it will make use a default name if this input is not attached.
Extension - the extension of the exported file
ExportOptions - a string of options for use with the _-Export command the same as you would use on the Rhino command line.
RhinoCommands - a list of Rhino Commands to run on the geometry right before exporting, I often need to _Explode things, _Convert, and _SimplifyCrv, this gives a way to do those processes right before export without editing the script itself.
AnalogTrigger - This is the input to trigger the Export, you can connect a Boolean like the Boolean Toggle or the Button, but keep in mind you need to hold the button down until the script finishes or Grasshopper will get locked up. You can also connect an Analog input like the Control Knob or the Number Slider. Analog inputs need to be in the range of 2.55 to 3.45 (this is what looked best with the Control Knob. they are 0.05 away from the bottom position in each direction.) the reason they are in this range is that it keeps the Analog input away from 0 and 1 which and it gives me a way to detect what you have connected to it and removes ambiguity.
out - is the default c# output for compiler messages.
Messages - are messages I write out as the script is executing, these are also on the command line, but the Messages output is a bit easier to read and I clear it as needed to keep it from getting too cluttered.
ExportDirectory - The directory the file will be exported to, this populates at when the Directory is processed in the script as the Analog value increases.
ExportFilename - The filename without extension that will be exported to, this changes as the analog value increases, and it adds on the Prepend and Append. etc…
ExportExtension - is the extension that will be exported to.
ExportFile - is a preview to the entire full path of the file to be exported to.

Here is the script in a sample grasshopper document:

1 Like

I haven’t tried your new code yet but I love this kind of outside-the-box thinking. Brilliant :exclamation:

1 Like

Thank you, please let me know if you encounter any bugs with it, I tried to test as many configurations as I could think of and also tried to make it as generic as possible. but it really needs further testing.

I also tried to do a lot of error checking and just take care of some obvious things on its own. So, you can supply the desired file format with the “.” like “.dxf” or without it like "dxf " and it will just fix it. it does the same thing with the directory, you can give it the trailing "" or not, so either "c:\test" or “c:\test” is fine.

I also had some stuff in there that made it useful for my process, but I realized that was really very specific to what I was trying to do, and would not apply so someone else, so some of it I just fixed with grasshopper components before the script, but some of it really needed to run in the script, so I made the RhinoCommands input where you can give a list of Rhino commands that run right before the _Export
I usually need to do _Explode, _Convert, _SimplifyCrv before any export because it breaks up the NURBS curves and makes them into fewer tangent arcs which work way better in my CAM program, but if you don’t have curves in your geometry, you wouldn’t want to run those at all… so this RhinoCommands input makes it much more useful for more people.

They look like the same thing to me?

I see no reason for the extreme verbosity of trace / error messages. In this case, I hadn’t yet changed the ‘Directory’ text panel so expected to see the fail message you added but I don’t see it?

Command: _-Grasshopper
Grasshopper option ( Window Document Solver Banner ): _Document
Grasshopper Document ( Open Save SaveAs Close CloseAll ): _Open
Grasshopper file ( Browse ): “C:\Users\josep\Downloads\Export Without Baking.gh”
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Grasshopper option ( Window Document Solver Banner ): _Enter
Grasshopper option ( Window Document Solver Banner )
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Connected as an Analog Input - Ready to Export
Increase value to Export - keep at Maximum until export is completed
Connected as an Analog Input - Already Active
Set Input to Minimum to Reset

When I changed the directory to one that exists and tried again, it fails. I see this message:

Connected as an Analog Input - Already Active

So I quite Rhino, restarted your GH file, changed the directory and tried again. It failed again with no clue as to why? I’m ready to give up now. :frowning:

The analog trigger sounded like a clever work-around yesterday but in fact is a PITA to use. UGH! There must be a better way using a single button. A few years ago I cobbled together a bake to layer component that uses a single button to trigger multiple copies of the component and it works well:

Some comments on the GH prior to your C#:

  • I see no reason for the Brep Join prior to ArrBox. It’s only effect is to convert curves to surfaces which can be accomplished by a Srf param.

  • I see no reason for nine Flatten components when the same goal can be accomplished with three Entwine components.

  • The ‘Extension’ Value List should be a dropdown instead of a cycle.

  • I hate relays.

Export Without Baking_2023Aug21a.gh (19.2 KB)

I’m not sure yet how I would use this, if it worked, but it’s broken.

1 Like

Firstly thanks for sharing, this seems really interesting to me and I see a lot of interesting use cases for different workflow opportunities.

Though your script currently supports layer color export I’m interested in potentially adapting it to export materials as well.

I need to dig into your code and look around but again, very interesting and I love the idea of being able to have a “Preview Model” sitting on the GPU/Display that is light weight, and then being able to publish/export all the 3D data to an external model without needing to bake or weight down the “control/visualization/preview” model.

One of the attractive features of yesterday’s version of your code is that it was short; 160 lines in the C# component compared to 598 lines of code in this latest version :exclamation:

Using yesterday’s simpler version, I just tried adding a “running” variable and a finally{} clause that resets it to false. So the button detection goes like this:

    if (export && !running)
    {
      running = true;
      try
      {

(is “export” a key word?)
But that alone didn’t fix the problem. Then I noticed this:

Starting the export process…
Command
Exporting to C:\Users\josep\Downloads\Sample.3dm
Command: _-Export
Save file name ( Version=7 SaveSmall=No GeometryOnly=No SaveTextures=No SaveNotes=No SavePlugInData=Yes Browse ): Version=5
Save file name ( Version=5 SaveSmall=No GeometryOnly=No SaveTextures=No SaveNotes=No SavePlugInData=Yes Browse ): SaveSmall=No
Save file name ( Version=5 SaveSmall=No GeometryOnly=No SaveTextures=No SaveNotes=No SavePlugInData=Yes Browse ): GeometryOnly=No
Save file name ( Version=5 SaveSmall=No GeometryOnly=No SaveTextures=No SaveNotes=No SavePlugInData=Yes Browse ): SaveTextures=No
Save file name ( Version=5 SaveSmall=No GeometryOnly=No SaveTextures=No SaveNotes=No SavePlugInData=Yes Browse ): SaveNotes=No
Save file name ( Version=5 SaveSmall=No GeometryOnly=No SaveTextures=No SaveNotes=No SavePlugInData=Yes Browse ): SavePlugInData=No
Save file name ( Version=5 SaveSmall=No GeometryOnly=No SaveTextures=No SaveNotes=No SavePlugInData=No Browse ): “C:\Users\josep\Downloads\Sample.3dm”
File successfully saved as Rhino 5 file C:\Users\josep\Downloads\Sample.3dm.

Is the file being exported multiple times?
So I changed the last parameter of this line (‘echo’) to false. From this:

Rhino.RhinoApp.RunScript("_-Export " + exportOptions + " \"" + filepath + "\"", true);

to this:

Rhino.RhinoApp.RunScript("_-Export " + exportOptions + " \"" + filepath + "\"", false);

Now it seems to work fine, without freezing the canvas :exclamation: :question:

Export Without Baking_2023Aug21b.gh (16.8 KB)

There is the Rhino.FileIO namespace (rhino3d.com) allowing you to e.g. create a 3dm file in memory and write it to disk.
This should allow you to write geometry to that file with attributes (including layers, color etc.).
There are already a few topics about that, which may give some more input on this:
Write a 3dm file with c# in Grasshopper - Grasshopper Developer - McNeel Forum

Exporting grasshopper geometry to rhino file without baking it - Grasshopper - McNeel Forum

If I understand you correctly, this should also give you what you want, without needing to rely on a Rhino command and likely also avoid the problems/bugs you ran into.

1 Like

It was supposed to be c:\test or c:\test\

no , it’s setting all the options from the export options

It was defiantly the export that was causing the freezing. I commented that out and still did all the creating and removing temporary layers on even a large drawing and it didn’t freeze… so the echo to the screen is the reason for the lockup??

Wow, I’ll go try that on the simpler version with a big file and see it is fixed. I would love to just have a button to trigger this.

I don’t see why it couldn’t export materials as well. I don’t use materials, but the same logic should work. I’ll see if I can add that in.

@Joseph_Oster on my big drawing your version named Export Without Baking_2023Aug21b.gh still locked up if I release the button while it’s running. but I do really like the echo shut off on the export better.

I also agree with you that I have too many messages. I put them to make sure things work the way I want but once it’s working, I can take them all out and just leave useful information.

It turns out after I’ve been playing with it for a while that I actually still like the button best even if I need to hold it down until the script finishes. I’m in the process of making a cleaner version that also fixes the bugs. I’ll post it here when I get it done.

I wonder why I never saw this problem using the Python ‘Bake to Layer’ code I referred to? By the way, that code does assign materials to layers but only the materials I created, not materials imported from the Material Library’. I didn’t care about that at the time and haven’t been motivated to go back and add that feature.

Thanks for the info, I’ll look into it. I need to export .3dm, .iges, and .dxf files so I don’t know if that can be adapted to the other file formats.

Unfortunately, I don’t know the first thing about materials, I never use them in Rhino, just turning on shaded and assigning colors to things is sufficient for my needs.

I wonder if it’s just how long it takes the script to run… it doesn’t take a really long time, maybe 5 seconds.

That would be wonderful!

Here is a new version.
I went back to the simpler version with just a Boolean input, but I added in some error checking:

  • Checks if the directory exists.
  • Checks if the extension is valid.
  • Checks if you have the same number of indexes in Geometry, Layers, and Colors.
  • If you leave the directory disconnected it uses the loaded Rhino document directory.
  • If you leave the filename disconnected, it uses the loaded Rhino document name, the Prepend and Append input allow you to modify the name.
  • Checks to see if the exported file name will be the same as the Rhino document name (if you don’t connect any inputs that’s what would happen), and if so, it appends - Exported to prevent an overwrite of the Rhino document.
  • I added DiagnosticOutput so I can turn on verbose messages for debugging purposes.

I had to go back to the counter, so that I can push and hold the button until the script runs and it only runs once, otherwise it runs over and over until I let go… this seems to be a workaround for the lockup problem as now you can just push the button and hold it down as long as you want and it only runs once… As long as you keep holding it down until the script finishes, it doesn’t seem to lock up Grasshopper.

Export Without Baking_2023Aug22.gh (21.0 KB)

I appreciate the help testing this. I’m probably the least qualified to test it because I know what I was thinking when I wrote it, so of course I only test it in a way that would work. Seeing how others try to use it and finding out what issues they have when they try to do it their way is very valuable information.

@michaelvollrath I did have a little bit of a look at materials, but I don’t know how they work in Grasshopper. I saw a create material component, but I have no idea how to use it. Do you have a simple example I can have a look at?

1 Like

Provided you don’t rely on third party plugins such as Human, in R7 you can use Python or C# to create materials or the quite limited “create material” which is mostly just based around a simple shader that intakes color, emissivity, glossiness, etc. but nothing special in terms of no texture mapping or actual textures present.

In R8 there are now “Model Materials” which allow you to set many more properties of and create materials directly in GH and these can get baked into the objects/document with the new Bake Content component. However, at this time, UV mapping and texture inputs are not directly set with these nodes, you can however, query existing Rhino materials that have all the maps set up and then apply said materials to your objects so thats pretty awesome!! I think it would be possible to extend this by having a script query into the Rhino linked material user/default libraries and import from there but I haven’t messed around with that.

This thread is one example that gets into PBR material creation via scripting in GH and may or may not be worth your time to read but sharing in case:

@michaelvollrath I thought I would play around with materials, but I hit a roadblock that I don’t know how to get around. To get colors to work, I need to define my input as System.Drawing.Color, which is easy because I just pick it from the list, but for materials I would need to input something like Rhino.Render.RenderMaterial (I guess) I’m not sure if I’m even on the right path here… I only have this list of possible inputs:

I guess in order to get the materials into the script I would have to make my own component? and not just use the C# component with a script? I just don’t know how to get the Material properties into the script.

Here it is so far, maybe someone who knows more about Grasshopper scripts can figure it out.
Export Without Baking with Materials - not working.gh (23.3 KB)

1 Like

I know the PBR material editor script I shared earlier modified .xml attributes. I’ll try and take a look and see how that could work in relation to this script.

Thanks for sharing!

You might want to look (again?) at the ‘Bake to Layer’ component with ‘M’ (Material) input? post I mentioned three days ago as there are some similarities to what you are doing.

1 Like

Yes it’s very similar, except instead of baking it, I want to just write it out to a Rhino file.

I had another look at it, the first time I looked, it kinda went over my head, but now that I see what the problem is it’s starting to make more sense. I didn’t realize that mMaterial was a Python script, I thought it was a Grasshopper component, on my screen it looks like this:
image

so, I figured it was just, yet another component related to materials that I don’t know anything about, because I don’t even know how to use materials in Rhino. The end game for my efforts with Rhino and Grasshopper is to cut things out on various CNC machines, so I’ve never needed any kind of material…

So if I’m following this right, Grasshopper doesn’t have any way to bring the M material directly into any kind of a script, so you have your own script mNMaterial that builds the material from various inputs, provides the M output as well as some other outputs that you can actually do something with…

I have a pile of triangles to cut, but next time I get a chance to tinker I’ll have a closer look at how it all works… I’m starting to see a solution.

2 Likes

@Joseph_Oster I wanted to say thank you for your suggestions. especially the Entwine component. On my real project I had 11 tree levels so 33 Flattens… instead of just the same 3 Entwine components. I also had to add 5 more layers, so I have 16 now, and it was SO easy to add more layers with Entwine compared to copy and pasting all those flatten components… so, Thank You!

1 Like