Tutorials
Jun 23, 2025

Automatically Colour Rhino Curves by Degree 

Automatically Colour Rhino Curves by Degree 

Automatically Colour Rhino Curves by Degree 

Quick look   Lines become black, arcs blue, cubic curves red… all without pressing a button. This guide shows you how to drop a tiny Python script into Rhino so every new curve auto‑colours itself based on its degree (1–11).

Why bother?

  • Instant visual feedback – spot stray lines or high‑degree NURBS at a glance.
  • Fewer manual steps – no more Select → Run Script → … every time you draw.
  • Custom palette – tweak colours to match your studio standard or print style.

Prerequisites

You needVersionRhino for WindowsormacOS6,7 or8 (tested)Basic familiarity with running a RhinoPython script–

Note for Mac users: Rhino 8 ships with CPython 3.x. The script below works in both IronPython 2.7 (Win/macOS ≤7) and CPython 3.x (Rhino 8) with no edits.

Step 1 — Copy the script

Open Rhino. In the command bar write EditPythonScript and paste in the code block below. Save the file as AutoCurveColours.py and press Play.

The only part you’ll ever edit is the colour palette at the top.

# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Auto Curve‑Colourer for Rhino 6/7/8  (toggle + revert)
# -----------------------------------------------------------------------------
# • Colours EVERY curve (existing + newly‑created) by its degree (1‑11).
# • One alias = ON / OFF.  When OFF, all curves return to their original colour.
# • Works in IronPython 2.7 (Rhino ≤7) and CPython 3.x (Rhino 8).
# -----------------------------------------------------------------------------
# Part of the Rhino Masterclass at Cademy.xyz  –  2025
# -----------------------------------------------------------------------------

import Rhino
import scriptcontext as sc
import System.Drawing as sd

# ────────────────────────────────────────────────────────────
# USER PALETTE  –  edit the swatches to your preference
# ────────────────────────────────────────────────────────────
COLOR_MAP = {
    1:  sd.Color.Black,      # lines        (deg‑1)
    2:  sd.Color.Blue,       # arcs         (deg‑2)
    3:  sd.Color.Red,        # cubic NURBS  (deg‑3)
    4:  sd.Color.Orange,
    5:  sd.Color.LimeGreen,
    6:  sd.Color.Turquoise,
    7:  sd.Color.Cyan,
    8:  sd.Color.RoyalBlue,
    9:  sd.Color.Indigo,
    10: sd.Color.Violet,
    11: sd.Color.Brown,
}
DEFAULT_COLOR = sd.Color.LightGray          # degree > 11
# ────────────────────────────────────────────────────────────


# ---------------------------------------------------------------------------
# INTERNAL KEY NAMES (all stored in scriptcontext.sticky)
# ---------------------------------------------------------------------------
_handlers_key = "cademy_curve_colour_handlers"     # {doc_id: handler}
_enabled_key  = "cademy_curve_colour_enabled"      # {doc_id: bool}
_state_key    = "cademy_curve_colour_state"        # {doc_id: {guid: (src,col)}}
# ---------------------------------------------------------------------------


# ===========================================================================
# CORE COLOUR + RESTORE FUNCTIONS
# ===========================================================================

def _colour_curve(rh_obj):
    """Apply the degree‑based colour to a single curve object."""
    geo = rh_obj.Geometry
    if not isinstance(geo, Rhino.Geometry.Curve):
        return False
    deg   = geo.Degree
    col   = COLOR_MAP.get(deg, DEFAULT_COLOR)
    attr  = rh_obj.Attributes
    attr.ColorSource = Rhino.DocObjects.ObjectColorSource.ColorFromObject
    attr.ObjectColor = col
    rh_obj.CommitChanges()
    return True


def _record_and_colour_all(doc, state_dict):
    """Store current colours for *every* curve then recolour them."""
    for rh_obj in doc.Objects:
        geo = rh_obj.Geometry
        if not isinstance(geo, Rhino.Geometry.Curve):
            continue
        attr = rh_obj.Attributes
        # Save original colour once (idempotent)
        state_dict[rh_obj.Id] = (attr.ColorSource, attr.ObjectColor)
        _colour_curve(rh_obj)


def _restore_all(doc, state_dict):
    """Revert curves to their saved colour source & colour."""
    for guid, (src, col) in state_dict.items():
        rh_obj = doc.Objects.FindId(guid)
        if not rh_obj:   # object was deleted
            continue
        attr = rh_obj.Attributes
        attr.ColorSource = src
        attr.ObjectColor = col
        rh_obj.CommitChanges()


# ===========================================================================
# MAIN TOGGLE (alias runs this file once to flip state per document)
# ===========================================================================

def toggle_auto_colour():
    """Enable or disable auto‑colouring for the ACTIVE Rhino document."""

    doc       = Rhino.RhinoDoc.ActiveDoc
    doc_id    = doc.RuntimeSerialNumber

    # Retrieve or create our three per‑session dictionaries.
    handlers  = sc.sticky.get(_handlers_key, {})
    enabled   = sc.sticky.get(_enabled_key,  {})
    all_state = sc.sticky.get(_state_key,    {})  # {doc_id: state_dict}
    state     = all_state.get(doc_id, {})          # per‑doc colour cache

    # -------- DISABLE ------------------------------------------------------
    if enabled.get(doc_id, False):
        enabled[doc_id] = False
        _restore_all(doc, state)
        print("Auto curve‑colouring DISABLED – Cademy.xyz")
        sc.sticky[_enabled_key]  = enabled
        sc.sticky[_state_key]    = all_state
        return

    # -------- ENABLE -------------------------------------------------------
    # 1. Create handler once per document, if not already present
    if doc_id not in handlers:
        def _handler(sender, e, this_doc_id=doc_id):
            if not sc.sticky.get(_enabled_key, {}).get(this_doc_id, False):
                return  # feature is OFF
            # Save original colour if we never saw this curve before
            state_dict = sc.sticky.get(_state_key, {}).get(this_doc_id, {})
            rh_obj = e.TheObject
            if rh_obj and rh_obj.Id not in state_dict:
                attr = rh_obj.Attributes
                state_dict[rh_obj.Id] = (attr.ColorSource, attr.ObjectColor)
                sc.sticky[_state_key][this_doc_id] = state_dict
            _colour_curve(rh_obj)
        doc.AddRhinoObject += _handler
        handlers[doc_id] = _handler
        sc.sticky[_handlers_key] = handlers

    # 2. Mark as enabled
    enabled[doc_id] = True
    sc.sticky[_enabled_key] = enabled

    # 3. Colour every existing curve & cache originals
    if doc_id not in all_state:
        all_state[doc_id] = {}
    _record_and_colour_all(doc, all_state[doc_id])
    sc.sticky[_state_key] = all_state

    print("🟢 Auto curve‑colouring ENABLED — Cademy.xyz (run again to disable)")


# ---------------------------------------------------------------------------
if __name__ == "__main__":
    toggle_auto_colour()

Re-Run the script to disable

We tag the handler with the document’s runtime serial number. When you open a second Rhino file, it gets its own independent watcher. And because we keep the exact same function instance in sc.sticky, removing it with -= always succeeds—even after the script has been re‑run.

How the magic works

  1. on_add_object is registered to RhinoDoc.AddRhinoObject — Rhino calls it for every new object.
  2. The handler filters out anything that isn’t a curve.
  3. For curves, it looks up the degree in COLOR_MAP and switches the object’s colour source to By Object.
  4. The sticky dictionary (sc.sticky) remembers the handler between runs so the same script acts as both ON and OFF toggle.

Step 2 — Add a Rhino alias (one‑click toggle)

  1. Rhino > Options > Aliases (Preferences > Aliases on macOS)
  2. Click New, give it a short name like AutoColours.
  3. Command field:
  4. _-RunPythonScript "AutoCurveColours.py"
  5. OK. Done!

Now type AutoColours in the command line once to turn the watcher on. Type it again to turn it off.

Step 3 — Test drive

  1. Enable the alias.
  2. Draw a Line → should turn black instantly.
  3. Draw an Arcblue.
  4. Draw a free Curve (default degree 3) → red.

(Sketch a few higher‑degree curves with the ChangeDegree command if you want to see the rest of the rainbow.)

Don’t miss out, enroll today.