Creating a new slider automatically and positioning it on the canvas
Connecting this slider to the original component
This is possible with any Grasshopper object, assuming you can find the class necessary to instantiate the component you are interested in.
privatevoid RunScript(bool x, List<object> y, refobject A)
{
Random rnd = new Random();
if(x)
{
//instantiate new slider
Grasshopper.Kernel.Special.GH_NumberSlider slid = new Grasshopper.Kernel.Special.GH_NumberSlider();
slid.CreateAttributes(); //sets up default values, and makes sure your slider doesn't crash rhino//customise slider (position, ranges etc)int inputcount = this.Component.Params.Input[1].SourceCount;
slid.Attributes.Pivot = new PointF((float) this.Component.Attributes.DocObject.Attributes.Bounds.Left - slid.Attributes.Bounds.Width - 30, (float) this.Component.Params.Input[1].Attributes.Bounds.Y + inputcount * 30);
slid.Slider.Maximum = 10;
slid.Slider.Minimum = 0;
slid.Slider.DecimalPlaces = 2;
slid.SetSliderValue((decimal) (rnd.Next(1000) * 0.01));
//Until now, the slider is a hypothetical object.// This command makes it 'real' and adds it to the canvas.
GrasshopperDocument.AddObject(slid, false);
//Connect the new slider to this componentthis.Component.Params.Input[1].AddSource(slid);
}
}
Thanks to Nicolas Chaulet who uncovered a lot of the code above.
The slider in Grasshopper doesn’t have an input. That means that only a user with a mouse can change it, right?
A slider with some kind of input is an ongoing topic on the Grasshopper forum. I know myself I’ve been in the position many times where I’ve defined a number with a slider, only to realise I later need Grasshopper to be changing that number for me. In lieu of an updated slider, I have come up with a solution…
Following on from my earlier post on adding a coloured progress bar to Grasshopper sliders, it became apparent that it’s in fact very easy to change the values of a slider with a bit of code. Watch the video below to see what I mean:
The only hard bit is figuring out how to ‘connect’ to the slider you want to modify. Basically, we look at the objects connected to our C# component, and try to convert them to a slider. If this is successful, then we know we have a slider, and we can start changing its properties.
Modify the slider value
This is the same code as what was in the video above. It simply reads in a value, finds a slider connected to x, and changes the ‘x’ slider to the ‘y’ value.
privatevoid RunScript(object x, double y, refobject A)
{
var input = Component.Params.Input[0].Sources[0]; //get the first thing connected to the first input of this componentvar slider = input as Grasshopper.Kernel.Special.GH_NumberSlider; //try to cast that thing as a sliderif(slider != null) //if the component was successfully cast as a slider
{
slider.SetSliderValue((decimal) y);
}
}
Change the slider min and max values
Changing the minimum and maximum values on the slider is also easy. To do this, we need to dig into the slider a little more. Inside slider.Slider, there are lots more hidden properties which are fun to play with:
privatevoid RunScript(object x, double y, refobject A)
{
var input = Component.Params.Input[0].Sources[0]; //get the first thing connected to the first input of this componentvar slider = input as Grasshopper.Kernel.Special.GH_NumberSlider; //try to cast that thing as a sliderif(slider != null) //if the component was successfully cast as a slider
{
slider.Slider.Minimum = 0;
slider.Slider.Maximum = y;
}
}
Change the appearance of a slider
There are lots of options hidden away for changing the appearance of a slider – my last post being one, adding progress bar by changing the slider’s ‘rail’.
There are lots of further options for changing the background colour, adding a border, or adding shadows.
privatevoid RunScript(object x, double y, refobject A)
{
var input = Component.Params.Input[0].Sources[0]; //get the first thing connected to the first input of this componentvar slider = (Grasshopper.Kernel.Special.GH_NumberSlider) input; //try to cast that thing as a sliderif(slider != null) //if the component was successfully cast as a slider
{
slider.Slider.DrawControlBackground = true;
slider.Slider.DrawControlBorder = true;
slider.Slider.ControlEdgeColour = Color.Blue;
slider.Slider.ControlBackColour = Color.Aquamarine;
}
}
However, like changing the rail type, these changes are always lost when a user manually changes the slider. Obviously, what’s happening in the background is there is a separate process being called to ‘clean’ the slider whenever the slider value is changed via the UI. Finding it is a challenge though. If anyone knows the solution, I’d be glad to hear!
I’ve just discovered a cool feature for the Grasshopper number slider: You can add a ‘progress bar’ rail colour directly within the slider itself!
This is a feature built into Grasshopper itself. As far as I know, it is not accessible using the GUI, and the only way to get to it is using the API, such as by writing a bit of C#.
How to add a progress bar to a Grasshopper slider
The basic idea is that we use C# to find the slider we are interested in. Once we have found it, we can access the slider properties in the API. It was here I found the ‘rail’ property.
There are two key things we need to know, which I have covered in previous posts:
How to access the properties of any component in the Grasshopper canvas (link to post)
How to send data ‘backwards’ along a wire (link to post)
We also need the Color.FromArbg() method to define a new colour.
C# code
We can implement this with the C# component in Grasshopper. Set r, g, and b as integers.
privatevoid RunScript(object x, int r, int g, int b, refobject A)
{
var input = (Component.Params.Input[0].Sources[0]); //get the first thing connected to the first input of this componentvar slider = (Grasshopper.Kernel.Special.GH_NumberSlider) input; //try to cast that thing as a sliderif(slider != null) //if the component was successfully cast as a slider
{
decimal max = slider.Slider.Maximum;
Color c = Color.FromArgb(255, r, g, b);
slider.Slider.RailDisplay = Grasshopper.GUI.Base.GH_SliderRailDisplay.Filled; //add progress bar
slider.Slider.RailFullColour = c; //set progress bar colour
}
}
Watch the colour update in real time
Limitations
This is shamelessly a hacky bit of Grasshoppery fun. As much as I like it, this implementation is still not perfect. Whenever you directly change the slider you have edited the colour of, the progress bar disappears. It seems that in the slider.SetSliderValue() method, Grasshopper is setting the slider rail back to the Simple value (the default).
This perhaps points towards the idea that the colour bar was never meant to be implemented at all, and why it can’t be found in the user interface. Or it could point to the possibility that I have just done something wrong, and there is something else I need to implement!
Loops in programming are very powerful. Oftentimes, we want to repeat the same piece of code several times, maybe with some slight changes each time.
Grasshopper, being a visual programming interface, tries to be a little simpler and easier to grasp than the regular horror of text-based programming. One of the key losses that comes from this simplicity is the inability to natively perform loops – we cannot use the output of one component as an input for a component upstream.
Hoopsnake
Hoopsnake gets around this problem using an approach similar to the way that Excel deals with the problem – what it calls circular references. But while a good solution in certain cases, Hoopsnake isn’t always easy to grasp and can be clumsy to set up in larger canvases – highlighting possibly why Grasshopper has removed the ability in the first place.
David Rutten’s permutation component
An alternative solution was created by David Rutten in this post, and looks at creating loops by cycling through values in the number slider component.
Say you have a few sliders in your canvas. Rutten’s component looks for all sliders. When the slider is changed, the canvas recalculates, as always happens in Grasshopper. The component listens for when all calculations have finished, then it changes the value of one of the sliders. This of course triggers another recalculation. The component listens again for calculations to finish, and changes the slider again. It cleverly repeats this cycle until it has gone through every value in the slider. If the component has found multiple sliders, it will run through every combination of these slider values. So if you have two sliders with integer values of 0-10, the canvas will recalculate 121 times (i.e. 11 x 11).
The curse of dimensionality
Nowadays, Grasshopper is being used for much larger projects than ever envisaged when it was first created. GH documents are likely to have many dozens of sliders. If we have 20 sliders each with just 11 possible settings, Rutten’s component will force the recalculation 11^20 times – i.e. 672 billion billion times! (This concept of small numbers multiplying up very quickly is something referred to as the curse of dimensionality and is a big challenge when designing computational algorithms on large data sets.)
Mostapha’s modification
Perhaps in recognition to the Curse of Dimensionality, or simply the need that you might not want to involve every slider in your canvas in your recursion, Mostapha Roudsari has created a modified version of Rutten’s component. You can find it as part of his example files for his Honeybee plugin. (Jump to example 6, “Parametric grid-based analysis” and look for the component called “runItAll”.)
The key change in this component is that there is now an input called “mySliders”. Instead of running through all sliders like Rutton’s solution, this component will only cycle through those sliders connected to “mySliders”. While very powerful, this creates an initially surprising behaviour since, visually, it looks like the action to change a slider flows backwards along the wire!
If no wires are connected, then the component resorts to Rutten’s component’s behaviour of modifying the values of all sliders in the canvas. This is still useful when you want to modify all sliders without spending time connecting them up.
My token improvement
This component has proven very useful for my own parametric studies, and I have been using it a lot. By using it a lot though, I have made a lot of mistakes. I’ve found it’s all too easy to accidentally run it and realise you’ve connected the wrong sliders, and find that you’ve asked Grasshopper to run a billion permutations, which GH diligently makes a happy start with. The only solution is to kill Grasshopper and reload.
I have added a message box that fires when you press the start button. This tells you the name of the connected sliders that the component will cycle through, and will calculate the number of permutations. You are then free to proceed or cancel.
You can download this component as a UserObject here:
This component has proven to be very useful in parametric studies. For example, if you are looking at optimal depth of window shading to control glare from the sun, you might set the depth of the shading with a slider. You can then set up a run where the glare values are automatically calculated for different shading depths.
Using a slider is quite limited however. What if you want to explore values of 0, 0.5 and 1 only? It is not possible to create a slider containing these three values. One solution is to connect a slider to a ListItem component (below) but this is quite a clumsy solution that takes time to set up and modify.
I have been looking at different components that can be modified remotely. For example, is it possible to use the Num component to hold modified values instead of the slider component? In a couple of recent posts, we have been looking at the ways that data is programatically stored within Grasshopper components. (Short answer – it’s possible to access, but not easy or obvious how to do it!)
Eventually, I want to be able to have a component where input values can be modified within a range. We don’t necessarily need to do an orthogonal grid of values like the recursive slider solution provides. If we are doing a parametric study, there are numerous other ways of choosing which discrete values we want to select as a sample within our ranges. This is a whole field called design of experiements which I am beginning to touch upon.
Appendix: BatchRun source code
privatevoid RunScript(List<System.Object> mySliders, bool startme)
{
if (!startme)
return;
if (_running)
return;
_run = true;
_mySliders = mySliders;
GrasshopperDocument.SolutionEnd += OnSolutionEnd;
Print(_rtnmessage);
}
// <Custom additional code> privatebool _run = false;
privatebool _running = false;
private List<System.Object> _mySliders;
privatestring _rtnmessage;
privatevoid OnSolutionEnd(object sender, GH_SolutionEventArgs e)
{
// Unregister the event, we don't want to get called again.
e.Document.SolutionEnd -= OnSolutionEnd;
// If we're not supposed to run, abort now.if (!_run)
return;
//throw new System.ArgumentException("Parameter cannot be null", "something weird");// If we're already running, abort now.if (_running)
return;
// Reset run and running states.
_run = false;
_running = true;
try
{
// Find the Guid for connected slides
List<System.Guid> guids = new List<System.Guid>(); //empty list for guids
Grasshopper.Kernel.IGH_Param selSlidersInput = Component.Params.Input[0]; //ref for input where sliders are connected to this component
IList<Grasshopper.Kernel.IGH_Param> sources = selSlidersInput.Sources; //list of things connected on this inputbool isAnythingConnected = sources.Any(); //is there actually anything connected?if (isAnythingConnected) { //if something's connected,foreach (var source in sources) //for each of these connected things:
{
IGH_DocumentObject component = source.Attributes.GetTopLevel.DocObject; //for this connected thing, bring it into the code in a way where we can access its properties
Grasshopper.Kernel.Special.GH_NumberSlider mySlider = component as Grasshopper.Kernel.Special.GH_NumberSlider; //...then cast (?) it as a sliderif (mySlider == null) //of course, if the thing isn't a slider, the cast doesn't work, so we get null. let's filter out the nullscontinue;
guids.Add(mySlider.InstanceGuid); //things left over are sliders and are connected to our input. save this guid.//we now have a list of guids of sliders connected to our input, saved in list var 'mySlider'
}
}
// Find all sliders.
List<Grasshopper.Kernel.Special.GH_NumberSlider> sliders = new List<Grasshopper.Kernel.Special.GH_NumberSlider>();
foreach (IGH_DocumentObject docObject in GrasshopperDocument.Objects)
{
Grasshopper.Kernel.Special.GH_NumberSlider slider = docObject as Grasshopper.Kernel.Special.GH_NumberSlider;
if (slider != null)
{
// check if the slider is in the selected listif (isAnythingConnected)
{
if (guids.Contains(slider.InstanceGuid)) sliders.Add(slider);
}
else sliders.Add(slider);
}
}
if (sliders.Count == 0)
{
System.Windows.Forms.MessageBox.Show("No sliders could be found", "<harsh buzzing sound>", MessageBoxButtons.OK);
return;
}
//we now have all sliders//ask the user to give a sanity checkint totalLoops = 1;
string message = null;
foreach(Grasshopper.Kernel.Special.GH_NumberSlider slider in sliders)
{
totalLoops *= (slider.TickCount + 1);
message += slider.ImpliedNickName;
message += "\n";
}
if (System.Windows.Forms.MessageBox.Show(sliders.Count + " slider(s) connected:\n" + message + "\n" + totalLoops.ToString() + " iterations will be done. Continue?", "Start?", MessageBoxButtons.YesNo) == DialogResult.No) return;
// Set all sliders back to zero.//GH_Document.EnableSolutions = false;foreach (Grasshopper.Kernel.Special.GH_NumberSlider slider in sliders)
slider.TickValue = 0;
// Start a giant loop in which we'll permutate our way across all slider layouts.while (true)
{
int idx = 0;
if (!MoveToNextPermutation(ref idx, sliders))
break;
// We've just got a new valid permutation. Solve the new solution.
e.Document.NewSolution(false);
Rhino.RhinoDoc.ActiveDoc.Views.Redraw();
}
}
catch {
_rtnmessage = "something went wrong!";
}
finally {
// Always make sure that _running is switched off.
_running = false;
}
}
privatebool MoveToNextPermutation(refint index, List<Grasshopper.Kernel.Special.GH_NumberSlider> sliders)
{
if (index >= sliders.Count)
returnfalse;
Grasshopper.Kernel.Special.GH_NumberSlider slider = sliders[index];
if (slider.TickValue < slider.TickCount)
{
// Increment the slider.
slider.TickValue++;
returntrue;
}
else
{
// The current slider is already at the maximum value. Reset it back to zero.
slider.TickValue = 0;
// Move on to the next slider.
index++;
// If we've run out of sliders to modify, we're done permutatin'if (index >= sliders.Count)
returnfalse;
return MoveToNextPermutation(ref index, sliders);
}
}
Update: Another solution for modifying sliders
Anders Deleuran has created an alternative way of remotely modifying slider values. His method is similar, but looks for sliders based upon their name. It is written in Python. You can download the file from the Grasshopper forum.