├── .editorconfig ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── AutoLoadGrasshopperDef.py ├── LICENSE ├── LayerObjectRandomizer.py └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # windows-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = crlf 9 | insert_final_newline = true 10 | 11 | [*.py] 12 | charset = utf-8 13 | indent_style = space 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rvb linguist-language=vbnet 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: runxel 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: runxel 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-project 2 | *.sublime-workspace 3 | -------------------------------------------------------------------------------- /AutoLoadGrasshopperDef.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ AutoLoadGrasshopperDef.py 4 | author: Lucas Becker 5 | version: 2019-07-05 6 | 7 | Opens a corresponding GH file (if existing) when opening a Rhino file. 8 | It is recommended to have this script in your Rhino startup command list: 9 | _-RunPythonScript AutoLoadGrasshopperDef.py 10 | 11 | Info! Scripted in a way so it only works when opening .3dm files and 12 | the Grasshopper definition needs to rest in the same folder. 13 | This could be changed – if you want to adjust this :) 14 | Caveat! This script can't do anything when Rhino is not already open 15 | and you double-click open a Rhino file. 16 | That's a limitation which I doubt can be overcome... 17 | """ 18 | 19 | import Rhino 20 | import scriptcontext 21 | import os.path, sys 22 | import logging as log 23 | 24 | log.basicConfig(stream=sys.stderr, level=log.INFO) 25 | 26 | """ 27 | https://developer.rhino3d.com/samples/rhinopython/current-model-info/ 28 | which was originally not working, since the script gets started in the 29 | document context of the previous document, which not longer exists, when 30 | the user is opening a new Rhino file 31 | this is also the reason why we don't use 32 | rs.Command("! _-Grasshopper _Document _Open {} _Enter".format(new_path)) 33 | in the delegate – it will simply not work 34 | 35 | https://discourse.mcneel.com/t/execute-script-on-file-opening 36 | in this thread @clement sheds some light on it and has some fantastic 37 | workarounds up his sleeve 38 | this Script wouldn't be possible without his help! 39 | """ 40 | 41 | def OpenDefinition(file_path): 42 | """ Another workaround because we can't use rs.Command() """ 43 | log.debug("OpenDefinition: {}".format(file_path)) 44 | 45 | Grasshopper = Rhino.RhinoApp.GetPlugInObject("Grasshopper") 46 | if not Grasshopper: return False 47 | 48 | Grasshopper.OpenDocument(file_path) 49 | 50 | def AfterLoadEvent(sender, e): 51 | """ event handler / delegate """ 52 | path = e.FileName # can't use rs.DocumentPath() 53 | if path[-3:] == "3dm": 54 | new_path = path[:-4] + ".gh" # switch ".3dm" with ".gh" 55 | # Windows and its backslash paths:... 56 | new_path = new_path.split("\\") 57 | new_path = "\\".join(new_path) 58 | # not using 59 | # os.path.join(*new_path) ### splat operator * for list unpacking 60 | # because it's faulty with absolute Win paths (drive letters, duh!) 61 | exists = os.path.isfile(new_path) 62 | if exists: 63 | OpenDefinition(new_path) 64 | else: 65 | print "No corresponding GH file found." 66 | 67 | def AutoLoadGrasshopperDef(): 68 | """ Subscribe to the EndOpenDocument event so we know when it's okay 69 | to fire the Grasshopper definition load 70 | """ 71 | key_after_load = "AfterLoadEvent" 72 | if scriptcontext.sticky.has_key(key_after_load): 73 | log.debug("GH autoload deactivated") 74 | Rhino.RhinoDoc.EndOpenDocument -= scriptcontext.sticky[key_after_load] 75 | scriptcontext.sticky.Remove(key_after_load) 76 | else: 77 | log.debug("GH autoload activated") 78 | scriptcontext.sticky[key_after_load] = eval(key_after_load) 79 | Rhino.RhinoDoc.EndOpenDocument += eval(key_after_load) 80 | 81 | ######################## 82 | if __name__=="__main__": 83 | AutoLoadGrasshopperDef() 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Lucas Becker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LayerObjectRandomizer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Creates a specified number of layers and assigns objects from a selection 4 | randomly to one of the layers created. Script by Mitch Heynick 20.02.16 5 | modified by Lucas Becker 2016-02-22""" 6 | 7 | import rhinoscriptsyntax as rs 8 | import random 9 | 10 | def RandomColor(): 11 | r=random.randint(0,255) 12 | g=random.randint(0,255) 13 | b=random.randint(0,255) 14 | return r,g,b 15 | 16 | def LayerObjectRandomizer(): 17 | prefix = "RandomLayer" 18 | separator = "_" 19 | 20 | objs = rs.GetObjects("Select objects", preselect=True) 21 | if not objs: return 22 | 23 | intObjLen = len(objs) 24 | leadZero = len(str(abs(intObjLen))) 25 | 26 | layer_count = rs.GetInteger("Number of layers to create?", minimum=1, maximum=intObjLen) 27 | if not layer_count: return 28 | 29 | layer_state = rs.GetBoolean("Group the new layers?", ("ParentLayer", "Off", "On"), True) 30 | if layer_state is None: return 31 | if layer_state[0]: 32 | parentLayer = "Random Layer Parent" 33 | rs.AddLayer(parentLayer) 34 | else: 35 | parentLayer = "" 36 | 37 | 38 | rs.EnableRedraw(False) 39 | layer_set=[] 40 | for i in range(layer_count): 41 | iZero = str(i+1).zfill(leadZero) 42 | layer="{}{}{}".format(prefix, separator, iZero) 43 | if not rs.IsLayer(layer): 44 | layer_set.append(rs.AddLayer(layer, RandomColor(), parent=parentLayer)) 45 | if layer_count == intObjLen: 46 | for obj in objs: 47 | popIndex = random.choice(layer_set) 48 | rs.ObjectLayer(obj, popIndex) 49 | layer_set.remove(popIndex) 50 | else: 51 | for obj in objs: 52 | rs.ObjectLayer(obj,random.choice(layer_set)) 53 | 54 | # make the usage as module possible 55 | if ( __name__ == '__main__' ): 56 | LayerObjectRandomizer() 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rhino Scripts 🦏 2 | 3 | Useful Rhino Scripts. Written in Python. 4 | 5 | Also have a look at the [provided samples here :octocat:](https://github.com/mcneel/rhino-developer-samples). 6 | 7 | #### Other scripts 8 | - [CADacombs](https://github.com/CADacombs/rhinopython) 9 | - [StGeorges](https://github.com/stgeorges/pythonscripts) 10 | - [kleerkoat](https://github.com/kleerkoat/rhinoScripts) (mostly collected forum RhinoScripts) 11 | 12 | --- 13 | 14 | ### [AutoLoadGrasshopperDef.py](https://github.com/runxel/rhino-scripts/blob/master/AutoLoadGrasshopperDef.py) 15 | Let Rhino automatically load a corresponding Grasshopper definition, when you have openend a `.3dm` file. The Grasshopper definition needs to have the same name as the Rhino file and must be in the same folder. 16 | 17 | ### [LayerObjectRandomizer.py](https://github.com/runxel/rhino-scripts/blob/master/LayerObjectRandomizer.py) 18 | This script makes as many new layers as wanted and puts the selected objects randomly in it. 19 | --------------------------------------------------------------------------------