Jump to content

Rectangle to Archimedean Spiral (Beta)


MJW
 Share

Recommended Posts

Rect-To-Spiral-Demo.png\

Plugin DLL:  RectangleToArchimedeanSpiral.zip

 

This is a beta version of a new plugin I'm writing. It's in the Distort menu

 

It's mostly done. I just need to tweak a few things and add a Help menu. I'm releasing a beta version before I do those things in the hope that it might be useful to @Fiz, who asked whether such a thing was available. It seemed like an interesting problem (and it was), so I wrote this plugin.

 

If there are any requests for improvements, now's a good time for them.

 

CodeLab code:

Spoiler

// Name: Rectangle to Archimedean Spiral
// Submenu: Distort
// Author: MJW
// Title: Rectangle to Archimedean Spiral
// Version: 1.0.0
// Desc: Transform rectangular region into an Archimedean Spiral
// Keywords: Transform Rectangular Archimedean Spiral
// URL:
// Help:
#region UICode
PanSliderControl RectLoc = Pair.Create(0.0,0.0); // Rectangle Location
DoubleSliderControl RectScale = 1; // [0.01,10] Rectangle Scale
DoubleSliderControl RectHeight = 1; // [0.01,4] Rectangle Height
PanSliderControl SpiralLoc = Pair.Create(0.0,0.0); // Spiral Location
DoubleSliderControl SpiralScale = 1;    //[0.01, 10] Spiral Scale
DoubleSliderControl StartOffset = 1;    //[1, 20] Rectangle Offset From Spiral Center
DoubleSliderControl SpiralSpacing = 100; // [10,1000] Spiral Spacing
AngleControl SpiralRotation = 0; // [-180, 180] Spiral Rotation
CheckboxControl SpiralReverse = false; // Reverse Spiral Direction
CheckboxControl RectangleReverse = false; // Spiral Rectangle Outward
CheckboxControl Antialias = false; // Antialias
IntSliderControl AntialiasQuality = 3; // [1,6] {Antialias} Antialias Quality
#endregion

readonly double TwoPi = 2.0 * Math.PI;

Surface Src;
float RectLocX, RectLocY;
float SpiralLocX, SpiralLocY;
float B;
float ScaleX, ScaleY;
float Offset;
float Rotation;
float Reverse;
float ArcLengthScale;

void PreRender(Surface dst, Surface src)
{
    Src = src;
    if (Antialias)
        SetupForSubpixels(AntialiasQuality, AntialiasQuality);

    float canvasHalfWidth = 0.5f * (float)Src.Width;
    float canvasHalfHeight = 0.5f * (float)Src.Height;
    float canvasCenterX = canvasHalfWidth - 0.5f;
    float canvasCenterY = canvasHalfHeight - 0.5f;

    RectLocX = (float)(canvasCenterX + canvasHalfWidth * RectLoc.First);
    RectLocY = (float)(canvasCenterY + canvasHalfHeight * RectLoc.Second);
    SpiralLocX = (float)(canvasCenterX + canvasHalfWidth * SpiralLoc.First);
    SpiralLocY = (float)(canvasCenterY + canvasHalfHeight * SpiralLoc.Second);
    B = (float)(SpiralScale * SpiralSpacing);
    ArcLengthScale = 0.5f * (B / (float)TwoPi);  
    ScaleX = 1.0f / (float)(SpiralScale * RectScale);
    ScaleY = 1.0f / (float)(SpiralScale * RectScale * RectHeight);
    Offset = SpiralArcLength((float)StartOffset);
    Rotation = (float)SpiralRotation / 360.0f;
    Reverse = SpiralReverse ? 1.0f : -1.0f;

    // Adjust for direction reversals.
    if (SpiralReverse)
    {
        ScaleY = -ScaleY;
        Rotation = -Rotation;
    }
    if (RectangleReverse)
    {
        ScaleX = -ScaleX;
        ScaleY = -ScaleY;
    }

    // Adjust the rotation so changing the offset doesn't change rotation.
    float adjAng = (float)(StartOffset);
    adjAng -= (int)adjAng;
    Rotation -= adjAng + Reverse * 0.25f;
    if (Rotation < -0.5f)
        Rotation += 1.0f;
    else if (Rotation > 0.5f)
        Rotation -= 1.0f;    
}

void Render(Surface dst, Surface src, Rectangle rect)
{   
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            dst[x, y] = Antialias ? TransformAA(x, y) : Transform(x, y);
        }
    }
}

double AngleScale = 0.5 / Math.PI;
ColorBgra Transform(float x, float y)
{
    x -= SpiralLocX;
    y -= SpiralLocY;
    float angle = (float)(AngleScale * Math.Atan2(Reverse * y, x));
    angle -= Rotation;
    if (angle < 0.0f)
        angle += 1.0f;
    else if (angle > 1.0f)
        angle -= 1.0f;
    float r = (float)Math.Sqrt(x * x + y * y);

    float a = r / B - angle;
    if (a < 0)
        return ColorBgra.Transparent;

    float k = (int)a;
    float f = a - k;
    angle += k;

    float ty = RectLocY - ScaleY * B * (f - 0.5f);
    float tx = RectLocX - ScaleX * (SpiralArcLength(angle) - Offset);
    return Src.GetBilinearSample(tx, ty);
}

float SpiralArcLength(float theta)
{
    double dtheta = TwoPi * (theta + 0.5);
    double sr = Math.Sqrt(1.0 + dtheta * dtheta);
    return ArcLengthScale * (float)(dtheta * sr + Math.Log(dtheta + sr));
}

ColorBgra TransformAA(float cx, float cy)
{
    ColorBgra CurrentPixel = ColorBgra.White;

    float bx = cx - ssXStart;
    float y = cy - ssYStart;

    int b = 0, g = 0, r = 0, a = 0;
    for (int i = 0; i < ssYSamples; i++)
    {
        float x = bx;
        for (int j = 0; j < ssXSamples; j++)
        {
            CurrentPixel = Transform(x, y);
            int alpha = CurrentPixel.A;
            if (alpha != 0)
            {
                b += alpha * CurrentPixel.B;
                g += alpha * CurrentPixel.G;
                r += alpha * CurrentPixel.R;
                a += alpha;
            }
            x += ssXStep;
        }
        y += ssYStep;
    }

    if (a == 0)
    {
        return ColorBgra.FromBgra(0, 0, 0, 0);
    }
    else
    {
        // Compute the (rounded) averages.
        int twiceA = a << 1;
        b = ((b << 1) + a) / twiceA;
        g = ((g << 1) + a) / twiceA;
        r = ((r << 1) + a) / twiceA;
        a = (twiceA + ssSamples) / ssTwiceSamples;
        return ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a);
    }
}

// Set up antialiasing.
int ssXSamples, ssYSamples, ssSamples, ssTwiceSamples;
float ssXStart, ssYStart, ssXStep, ssYStep;
double ssSamplesRecip;
void SetupForSubpixels(int xSamples, int ySamples)
{
    ssXSamples = xSamples;
    ssYSamples = ySamples;
    ssSamples = xSamples * ySamples;
    ssSamplesRecip = 1.0 / (double)ssSamples;
    ssTwiceSamples = ssSamples << 1;
    ssXStep = 1.0f / (float)xSamples;
    ssYStep = 1.0f / (float)ySamples;
    ssXStart = 0.5f * (1.0f - ssXStep);
    ssYStart = 0.5f * (1.0f - ssYStep);
}

 

 

  • Upvote 7
Link to comment
Share on other sites

3 hours ago, Red ochre said:

What settings give the largest number of turns? Two wrap-a-rounds seems to be about the max? More could be useful if possible.

 

Any number of wrap-a-rounds is possible. To increase the number of cycles, make Spiral Spacing (the distance between the loops) smaller, and set Rectangle Offset From Spiral Center larger, so it has room. To get a large number of wrap-a-rounds naturally requires a long, thin region to transform.

 

The plugin works best when the background is transparent. This also allows easy alignment of the region to transform to the ceter, which is the most convenient location. BTW, Rectangle Location specifies the center of the region to transform,

 

Any suggestions for better control names will be appreciatively considered. I think I'll probably change Rectangle Offset From Spiral Center to simply Offset From Spiral Center.

 

2 hours ago, Djisves said:

My only comment is on the name of the plugin/effect. Perhaps "Archimedean Spiral" is enough.

 

I'll likely make that change.

  • Upvote 1
Link to comment
Share on other sites

Thanks for the tips.

Since a long image is required to wrap around the spiral but the spiral itself fits best onto a squarish canvas/selection, is it worth considering an option to use the clipboard for the long image?  - Just an idea!

  • Like 1

 

Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings

 

PdnForumSig2.jpg

Link to comment
Share on other sites

31 minutes ago, Red ochre said:

Since a long image is required to wrap around the spiral but the spiral itself fits best onto a squarish canvas/selection, is it worth considering an option to use the clipboard for the long image?  - Just an idea!

 

It's a good idea, and one I'll consider. The biggest difficulty (or maybe just annoyance) is that the the Rectangle Location pan control can't, as far as I know, display a thumbnail of the clipboard when the clipboard is being used, and the canvas when it isn't. I've had the same problem in other plugins, and just ignored it, but in this instance it's a little worse because the the aspect ratio of the canvas versus the clipboard would likely be so different.

Link to comment
Share on other sites

I would think for simplicity the image on the clipboard would define the rectangle and grey-out the Rectangle Location and Height controls?
It would then depend upon the user having selected a useful size and aspect ratio to copy.

 

Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings

 

PdnForumSig2.jpg

Link to comment
Share on other sites

7 hours ago, Red ochre said:

It would then depend upon the user having selected a useful size and aspect ratio to copy.

This sounds reasonable but I think it would also mean a lot of trial and error on the part of the user.

Still, I don't have a better suggestion. In my example on the other topic, I used a long image centred on a large square canvas and then cropped into a much smaller rectangle around the spiral.

Xkds4Lh.png

Link to comment
Share on other sites

8 hours ago, Red ochre said:

I would think for simplicity the image on the clipboard would define the rectangle and grey-out the Rectangle Location and Height controls?
It would then depend upon the user having selected a useful size and aspect ratio to copy.

 That might be a good way of doing it. It's slightly restrictive, but if it's, for example, text on a transparent background, the user could first center it with one of the Align plugins, which would naturally put it in the best position.

Link to comment
Share on other sites

On 11/14/2020 at 9:12 AM, MJW said:

Rect-To-Spiral-Demo.png\

Plugin DLL:  RectangleToArchimedeanSpiral.zip

 

This is a beta version of a new plugin I'm writing. It's in the Distort menu

 

It's mostly done. I just need to tweak a few things and add a Help menu. I'm releasing a beta version before I do those things in the hope that it might be useful to @Fiz, who asked whether such a thing was available. It seemed like an interesting problem (and it was), so I wrote this plugin.'

 

If there are any requests for improvements, now's a good time for them.

 

 

Wow! Thanks! 

I'm such a numpty at Paint.net (and any graphics package) I have installed it, but not yet mastered it. I pasted in a graphic of some music, but the spiral had nothing in it apart from whiteness. When I have a bit more time, I'll have another go. 

 

Link to comment
Share on other sites

I've really not yet had any success. The plugin seems to stretch out the original where I'd like to just bend the original rectangle into a spiral with the height and the length being in the same proportions as the original. No doubt a lot of this is my ineptitude with PAINT.NET. I am immensely grateful that @MJW has done this work. Hopefully I can make it work well enough, ultimately. 

Here are some images of what I've tried so far. 
This is what I started with: 

image.thumb.png.c0e815f65e918f390d37d85444e53ef0.png

 

That's a rectangle and it was on a square canvas. I couldn't even get anything worth pasting here to show, but everything was stretched out and mostly invisible in the rectangle. 

 

I tried squashing up the image:
image.png.e1e79efdf86a5af9fd1f972ebe502140.png

 

and then this was the best I got: 

image.png.204a7058e9ed543593f2cc6e77ceeaa5.png

and this is not usable. 

 

As before, I'm a complete novice at this sort of app, so no doubt it's VERY possible I've made some rookie errors. 

In terms of timeline, ideally, I'd like to submit my score on Monday 30th for the poor musicians who are going to workshop it on 7th Dec. My final project has to be submitted on 9th. It'd be so cool to submit a neat spiral score. (I can otherwise bodge something in Powerpoint I guess or maybe even handwrite.) 

 

 

 

 

 

Link to comment
Share on other sites

 

Hi @Fiz,

 

Out of curiosity I tried to achieve the effect you want with your image. This is what I got:

spiral-1.png

And this is how I got it:

 

1. Open your file (as it is).

 

2. Image > Canvas Size > Width = 1200 Height = 1200 Anchor = Middle (so you get a 1200x1200 pic).

 

3. Run the plugin:

 

spiral-2.png

4. Add new layer > Paint Bucket = White.

 

5. Merge that layer down (so it becomes the white background for the Music spiral).

 

Play with the plugin options - it is possible. Mine is only a rough attempt. Good luck. 🎵

 

 

Edited by Vagabondi
  • Like 4
  • Upvote 2
  • You're a Smart Cookie! 1

signature.png.ebe2dec8b8faab2aa57630b352

my gallery is  here

 

Link to comment
Share on other sites

  • 4 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...