Recursive Steiner Chains

Hi guys, I found this old script that draws a Recursive Steiner Chain on a canvas in JavaScript ES6. I’ve never used JS before and this post was the first time I’ve even heard of “golfing” code. I really want to get this working in Unity but I’m struggling to even begin converting it.

This is my pathetic unfinished attempt at conversion:

public class SteinerChain : MonoBehaviour
{
    float width = 5f;
    float height = 5f;
    int[] array = new int[] { 13, 7, 11, 5, 3 };

    void Start()
    {
        S();
    }

    void S(float radius = 0, int d = 0, int r = 0)
    {
        int n = array[d];
        float i = (radius - radius * Mathf.Sin(Mathf.PI / n)) / (Mathf.Sin(Mathf.PI / n) + 1);

        if (d++ < array.Length)
        {
            S(i, d, n);
            for (; r < n; ++r)
            {
                // save(); transform
                // translate(0, (o + i) / 2); origin to middle of the 2 base circles
                S((radius - i) / 2, d);
                // restore(); transform
                // rotate((2 * PI) / n); transform
            }
        }
    }
}

I’m new to coding in general and I haven’t even gotten to how I’d render it. Would it be best to render it as a texture? Or perhaps instantiating circles of varying size? I’d love some guidance on this!

I’d love to offer some guidance but I have no idea what a Steiner Chain is off the top of my head and I’m not about to go on a wiki dive for you.

I also have no idea what code golfing means in this context. It sounds like trying to type code in the least keystrokes?!

The first question of course is “What are you TRYING to do?”

The second is to observe that the code above is only a few lines. You might do better working with basic C# tutorials until you feel comfortable enough to actually translate code from one language to another.

And finally, since nobody here knows what you’re even doing, here is how to report your problem productively in the Unity3D forums:

http://plbm.com/?p=220

How to understand compiler and other errors and even fix them yourself:

Remember: NOBODY here memorizes error codes. That’s not a thing. The error code is absolutely the least useful part of the error. It serves no purpose at all. Forget the error code. Put it out of your mind.

The complete error message contains everything you need to know to fix the error yourself.

Always start with the FIRST error in the console window, as sometimes that error causes or compounds some or all of the subsequent errors.

The important parts of the error message are:

  • the description of the error itself (google this; you are NEVER the first one!)
  • the file it occurred in (critical!)
  • the line number and character position (the two numbers in parentheses)
  • also possibly useful is the stack trace (all the lines of text in the lower console window)

All of that information is in the actual error message and you must pay attention to it. Learn how to identify it instantly so you don’t have to stop your progress and fiddle around with the forum.

You’re definitely right, I haven’t really thought out the question. I usually get too excited and try to get everything written down without explaining myself. Sorry! I’ll try to explain my problem with a brief overview:

A Steiner Chain is basically a bunch of circles tangent to two non-intersecting circles, one inside the other. Pictured here (a recursive one is adding more circles inside the black circles):

Base:

Recursive:

This article on Wolfram explains it pretty well.

I’ll try summarize the post in my original link; basically I want to create a function that draws this, given the radius of the larger blue circle, and a list of integers corresponding to the number of circles in each iteration of chains. An example being Foo(600, [5, 4, 3]) which gives:

  • 1 large circle of radius 600
  • 5 inner circles within that circle
  • 4 circles within each of those 5
  • 3 circles within each of those 4

I tried ignoring the actual answer and attempting to solve the original challenge, but this was as far as I got:

public class RecursiveSteinerChain : MonoBehaviour
{
    int[] circleCountPerIteration = new int[] { 5, 4, 3 };

    void Start()
    {
        Foo(600, circleCountPerIteration[0]);
    }

    void Foo(float r, int circleCount, int i = 0)
    {
        var nextR = (r - r * Mathf.Sin(Mathf.PI / circleCount)) / (Mathf.Sin(Mathf.PI / circleCount) + 1);

        if (i++ < circleCountPerIteration.Length - 1)
            Foo(nextR, circleCountPerIteration[i], i);
           
        // ?
    }
}

I’m unsure how to do the actual translation of the drawing functionality to C#, since I’m dumb and I have trouble wrapping my head around coordinate systems and canvases. I don’t technically have any errors to debug; I don’t know how to approach the issue in the first place.

Unity doesn’t have draw primitives like this. You can get pixel-drawing routines from the asset store though and integrate those, or alternately you can produce the circles with geometry (vertices and polygons).

If you want examples of the latter, feel free to sniff around my MakeGeo project.

MakeGeo is presently hosted at these locations:

https://bitbucket.org/kurtdekker/makegeo

This looks amazing but I think I want to avoid using meshes for such a simple project. I’ll mess around with the pixel-drawing routines you mentioned though, thanks!

After a bit of fiddling around, I got the drawing of the circles up and running. Though now I still have no idea how to do the translation and rotation of the canvas with this system. Right now I’m drawing every circle at the origin point (0, 0).

using System.Collections.Generic;
using UnityEngine;

public class RecursiveSteinerChain : MonoBehaviour
{
    List<Circle> circles = new List<Circle>();
    public int[] circleCountsPerIteration = new int[] { };
    public float circleRadius;
    public float lineWidth;

    public class Circle
    {
        public Vector2 position;
        public float radius;
    }

    void Start()
    {
        Foo(circleRadius, circleCountsPerIteration[0]);
    }

    void Foo(float r, int circleCount, int i = 0)
    {
        var nextR = (r - r * Mathf.Sin(Mathf.PI / circleCount)) / (Mathf.Sin(Mathf.PI / circleCount) + 1);
        var centerPoint = new Vector2(0, 0);
        circles.Add(new Circle() { position = centerPoint, radius = r });

        if (i++ < circleCountsPerIteration.Length - 1)
            Foo(nextR, circleCountsPerIteration[i], i);
    }

    void LateUpdate()
    {
        Visualizer.SetColour(Color.white);
        foreach (Circle c in circles)
            Visualizer.DrawRing(c.position, -transform.forward, 0, 360, c.radius - lineWidth, c.radius);
    }
}

Which just gives me this, obviously incorrect output.