MJW Posted November 14, 2020 Share Posted November 14, 2020 \ 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); } 7 Quote Link to comment Share on other sites More sharing options...
Red ochre Posted November 14, 2020 Share Posted November 14, 2020 Very impressive! works well. What settings give the largest number of turns? Two wrap-a-rounds seems to be about the max? More could be useful if possible. Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings Link to comment Share on other sites More sharing options...
Djisves Posted November 14, 2020 Share Posted November 14, 2020 Thank you @MJW, I have just tried it and it works just fine. My only comment is on the name of the plugin/effect. Perhaps "Archimedean Spiral" is enough. Quote Link to comment Share on other sites More sharing options...
MJW Posted November 14, 2020 Author Share Posted November 14, 2020 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. 1 Quote Link to comment Share on other sites More sharing options...
Red ochre Posted November 14, 2020 Share Posted November 14, 2020 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! 1 Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings Link to comment Share on other sites More sharing options...
MJW Posted November 14, 2020 Author Share Posted November 14, 2020 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. Quote Link to comment Share on other sites More sharing options...
Red ochre Posted November 14, 2020 Share Posted November 14, 2020 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. Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings Link to comment Share on other sites More sharing options...
Djisves Posted November 15, 2020 Share Posted November 15, 2020 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. Quote Link to comment Share on other sites More sharing options...
MJW Posted November 15, 2020 Author Share Posted November 15, 2020 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. Quote Link to comment Share on other sites More sharing options...
Fiz Posted November 22, 2020 Share Posted November 22, 2020 On 11/14/2020 at 9:12 AM, MJW said: \ 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. Quote Link to comment Share on other sites More sharing options...
Fiz Posted November 23, 2020 Share Posted November 23, 2020 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: 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: and then this was the best I got: 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.) Quote Link to comment Share on other sites More sharing options...
Vagabondi Posted November 23, 2020 Share Posted November 23, 2020 (edited) Hi @Fiz, Out of curiosity I tried to achieve the effect you want with your image. This is what I got: 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: 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 November 23, 2020 by Vagabondi 4 2 1 Quote my gallery is here Link to comment Share on other sites More sharing options...
MJW Posted November 23, 2020 Author Share Posted November 23, 2020 8 hours ago, Fiz said: I tried squashing up the image: There's no reason to try to pre-distort the image in that way -- that's what the plugin's Scale and Height controls are for. Quote Link to comment Share on other sites More sharing options...
Fiz Posted December 16, 2020 Share Posted December 16, 2020 I have submitted my score. I kept fiddling with the controls until it was about right. Any pianist trying it will hate me! 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.