Creating custom diagrams as icons within components like the Trigonometry one?


We love the Trigonometry component and would like to do the same for Silkworm’s Flow component to explain what the parameters of a cross sectional area of 3D printed filament looks like. Struggling to insert a large diagram like the Trigonometry one shown below. Does anyone know how?


We tried to insert a large icon in visual studio but it seems to expand on the canvas instead of making the component bigger?

You’ll have to create your own custom attributes for that component. Those attributes will have to do custom layout and drawing. I can post an example tomorrow when I’m back in office.

Thanks so much!

Here’s the attributes for the trig component. By far most of the code is concerned with drawing all the shapes.

  public class RightTriangleTrigAttributes : GH_ComponentAttributes
    public RightTriangleTrigAttributes(RightTriangleTrig owner) : base(owner) { }

    protected override void Layout()
      Pivot = GH_Convert.ToPoint(Pivot);

      m_innerBounds = new RectangleF(Pivot.X, Pivot.Y, 125, 100);
      LayoutInputParams(Owner, m_innerBounds);
      LayoutOutputParams(Owner, m_innerBounds);
      Bounds = LayoutBounds(Owner, m_innerBounds);
    protected override void Render(Grasshopper.GUI.Canvas.GH_Canvas canvas, Graphics graphics, Grasshopper.GUI.Canvas.GH_CanvasChannel channel)
      if (channel != Grasshopper.GUI.Canvas.GH_CanvasChannel.Objects)
        base.Render(canvas, graphics, channel);

      RenderComponentCapsule(canvas, graphics, true, false, false, true, true, true);

      Rectangle rec = GH_Convert.ToRectangle(m_innerBounds);
      graphics.FillRectangle(Brushes.White, rec);

      if (Grasshopper.GUI.Canvas.GH_Canvas.ZoomFadeLow > 0)
        Rectangle reci = rec;
        reci.Inflate(-10, -10);

        Point pA = new Point(reci.Left, reci.Bottom);
        Point pB = new Point(reci.Right, reci.Bottom);
        Point pC = new Point(reci.Right, reci.Top);

        Point pP = new Point((pA.X + pB.X) / 2, (pA.Y + pB.Y) / 2);
        Point pQ = new Point((pB.X + pC.X) / 2, (pB.Y + pC.Y) / 2);
        Point pR = new Point((pA.X + pC.X) / 2, (pA.Y + pC.Y) / 2);
        pP.Offset(16, 1);
        pQ.Offset(0, 10);

        int radius = 14;
        Rectangle rP = new Rectangle(pP, new Size(radius, radius));
        Rectangle rQ = new Rectangle(pQ, new Size(radius, radius));
        Rectangle rR = new Rectangle(pR, new Size(radius, radius));
        rP.Offset(-radius / 2, -radius / 2);
        rQ.Offset(-radius / 2, -radius / 2);
        rR.Offset(-radius / 2, -radius / 2);

        Point pAlpha = new Point(pA.X + 26, pA.Y - 8);
        Point pBeta = new Point(pC.X - 10, pC.Y + 24);
        Rectangle rAlpha = new Rectangle(pA.X - 1, pA.Y - 1, 2, 2);
        Rectangle rBeta = new Rectangle(pC.X - 1, pC.Y - 1, 2, 2);
        rAlpha.Inflate(20, 20);
        rBeta.Inflate(18, 18);

        Pen triPen = new Pen(Color.Black, 3);
        triPen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
        graphics.DrawPolygon(triPen, new Point[] { pA, pB, pC, pA });

        graphics.DrawArc(Pens.Black, rAlpha, 323, 37);
        graphics.DrawArc(Pens.Black, rBeta, 90, 53);
        graphics.DrawLine(Pens.Black, pB.X - 8, pB.Y, pB.X - 8, pB.Y - 8);
        graphics.DrawLine(Pens.Black, pB.X - 8, pB.Y - 8, pB.X, pB.Y - 8);

        graphics.FillEllipse(Brushes.Black, rP);
        graphics.FillEllipse(Brushes.Black, rQ);
        graphics.FillEllipse(Brushes.Black, rR);

        // ReSharper disable once CompareOfFloatsByEqualityOperator
        if (canvas.Viewport.Zoom == 1.0f)
          pP.Offset(0, 1);
          pQ.Offset(0, 1);
          pR.Offset(0, 1);
          pAlpha.Offset(0, 1);
          pBeta.Offset(0, 1);
        Grasshopper.GUI.GH_GraphicsUtil.RenderCenteredText(graphics, "P", GH_FontServer.ConsoleAdjusted, Color.White, pP);
        Grasshopper.GUI.GH_GraphicsUtil.RenderCenteredText(graphics, "Q", GH_FontServer.ConsoleAdjusted, Color.White, pQ);
        Grasshopper.GUI.GH_GraphicsUtil.RenderCenteredText(graphics, "R", GH_FontServer.ConsoleAdjusted, Color.White, pR);
        Grasshopper.GUI.GH_GraphicsUtil.RenderCenteredText(graphics, "α", GH_FontServer.ConsoleAdjusted, Color.Black, pAlpha);
        Grasshopper.GUI.GH_GraphicsUtil.RenderCenteredText(graphics, "β", GH_FontServer.ConsoleAdjusted, Color.Black, pBeta);
        Grasshopper.GUI.GH_GraphicsUtil.ShadowRectangle(graphics, rec);
      if (Grasshopper.GUI.Canvas.GH_Canvas.ZoomFadeLow < 255)
        Brush blendfill = new SolidBrush(Color.FromArgb(255 - Grasshopper.GUI.Canvas.GH_Canvas.ZoomFadeLow, Color.White));
        graphics.FillRectangle(blendfill, rec);
      graphics.DrawRectangle(Pens.Black, rec);

Amazing - We will test this ASAP. Thanks again David. Very helpful.

Hi guys,

what if the graphics was dynamic accordingly to the output of the custom component itself?

More in detail.

I am developing a custom component that tries to check if a data tree matches another one used as a reference. Depending on the result of the check, I’d like to embed this huge rectangle - a sort of traffic light - green if the two data trees match, red if they don’t (for simplicity just skip the further input and outputs in the screenshot below).


As you can imagine, I’ve already created a custom attribute for this component but I still don’t understand how to read back the owner’s property or the output parameter or whatever, in order to trigger the update of the graphics (i.e. the rectangle fill and some additional text, as if it was a native Legend component with one value only per color/tag).

Any hint would be really appreciated…