Jump to content

Archimedean Dots (May 4, 2022)


Recommended Posts

Draws dots along the path of an Archimedean spiral

 

Screenshot-ArchimedeanDots.png.c55a39cdf03cc4dc44f2a565dd6838c8.png

Found in Effects > Render

 

Versions:

1.0(20220502) - Initial Release

1.1(20220504) - Add Initial Loop Number / Initial Point Offset option. Add Center on Top option to change the order on which the dots are drawn and/or colored.

 

C# Code Snippet (Modified from CodeLab generated code)

License: LPGL-3.0

 

Spoiler
        protected override void OnRender(Rectangle[] rois, int startIndex, int length)
        {
            if (length == 0 || IsCancelRequested) return;
            Render(DstArgs.Surface, SrcArgs.Surface, EnvironmentParameters.SelectionBounds);
        }

        #region UICode
        private DoubleSliderControl loops = 5; // [2,100] Loops
        private IntSliderControl spacing = 25; // [1,100] Loop Spacing
        private ListBoxControl stepType = 1; // |Point Alignment|Point Spacing
        private DoubleSliderControl step = 10; // [1,360]
        private ListBoxControl offsetType = 1; // |Initial Loop Number|Initial Point Offset
        private IntSliderControl offset = 0; // [0,100]
        private IntSliderControl size = 5; // [1,100] Dot Size && Color
        private ListBoxControl color = 0; // |Primary Color|Secondary Color|Raindow 1|Rainbow 2
        private IntSliderControl startHue = 0; // [0,360,1]
        private IntSliderControl transparency = 255; // [0,255,4]
        private CheckboxControl centerOnTop = true; // Center on Top
        private CheckboxControl clockwise = true; // Clockwise
        private PanSliderControl center = Pair.Create(0.000, 0.000); // Center
        private AngleControl rotation = 0; // [0,360] Rotation
        #endregion

        private List<Vector3> cachedPoints;
        private State cachedState;

        private void Render(Surface dst, Surface src, Rectangle rect)
        {
            dst.CopySurface(src, rect.Location, rect);

            State state = new(this);
            List<Vector3> points = state.Equals(cachedState) ? cachedPoints : stepType == 0 ? GenerateAlignedPoints() : GenerateEquidistantPoints();
            if (IsCancelRequested) return;

            List<Vector3> ordered;
            if (centerOnTop || color < 2)
            {
                ordered = points;
            }
            else
            {
                ordered = new(points);
                ordered.Reverse();
            }

            using Graphics g = new RenderArgs(dst).Graphics;
            using Region gClipRegion = new(rect);
            g.Clip = gClipRegion;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.TextRenderingHint = TextRenderingHint.AntiAlias;

            float centerX = (float)(rect.Left + (center.First + 1) / 2 * rect.Width);
            float centerY = (float)(rect.Top + (center.Second + 1) / 2 * rect.Height);

            g.TranslateTransform(centerX, centerY);
            g.RotateTransform((float)-rotation);

            using SolidBrush brush = new(Color.Transparent);

            if (color == 0)
            {
                brush.Color = EnvironmentParameters.PrimaryColor;
            }
            else if (color == 1)
            {
                brush.Color = EnvironmentParameters.SecondaryColor;
            }
            HsvColor hsv = new(startHue == 0 ? 360 : startHue - 1, 100, 100);

            float halfSize = size / 2f;

            foreach (Vector3 v in ordered)
            {
                if (IsCancelRequested) return;

                if (color == 2)
                {
                    hsv.Hue = (hsv.Hue + 1) % 361;
                    ColorBgra bgra = hsv.ToColor();
                    bgra.A = (byte)transparency;
                    brush.Color = bgra;
                }
                else if (color == 3)
                {
                    hsv.Hue = (startHue + (int)Math.Round(v.Z, MidpointRounding.ToEven)) % 361;
                    ColorBgra bgra = hsv.ToColor();
                    bgra.A = (byte)transparency;
                    brush.Color = bgra;
                }
                g.FillEllipse(brush, v.X - halfSize, v.Y - halfSize, size, size);
            }
            cachedPoints = points;
            cachedState = state;
        }

        private List<Vector3> GenerateAlignedPoints()
        {
            double a = offsetType == 1 ? offset : offset * spacing;
            double b = spacing / 360.0;
            double toRad = Math.PI / (clockwise ? 180 : -180);

            List<Vector3> points = new();
            for (double degree = loops * 360; degree >= 0; degree -= step)
            {
                if (IsCancelRequested) return null;

                double radius = a + b * degree;
                (double sin, double cos) = Math.SinCos(degree * toRad);
                float x = (float)(radius * cos);
                float y = (float)(radius * sin);
                points.Add(new Vector3(x, y, (float)degree));
            }
            return points;
        }

        private List<Vector3> GenerateEquidistantPoints()
        {
            double a = offsetType == 1 ? offset : offset * spacing;
            double b = spacing / Math.PI / 2;

            List<Vector3> points = new();
            for (double radian = loops * Math.PI * 2; radian >= 0;)
            {
                if (IsCancelRequested) return null;

                double radius = a + b * radian;
                (double sin, double cos) = Math.SinCos(clockwise ? radian : -radian);
                float x = (float)(radius * cos);
                float y = (float)(radius * sin);
                float z = (float)(radian * 180 / Math.PI);
                points.Add(new Vector3(x, y, z));

                radian -= step / radius;
            }
            return points;
        }

        internal class State
        {
            private readonly double loops;
            private readonly int spacing;
            private readonly byte stepType;
            private readonly double step;
            private readonly byte offsetType;
            private readonly int offset;
            private readonly bool clockwise;

            public State(ArchimedeanDotsEffectPlugin plugin)
            {
                loops = plugin.loops;
                spacing = plugin.spacing;
                stepType = plugin.stepType;
                step = plugin.step;
                offsetType = plugin.offsetType;
                offset = plugin.offset;
                clockwise = plugin.clockwise;
            }

            public override bool Equals(object obj)
            {
                return obj is State state &&
                       loops == state.loops &&
                       spacing == state.spacing &&
                       stepType == state.stepType &&
                       step == state.step &&
                       offsetType == state.offsetType &&
                       offset == state.offset &&
                       clockwise == state.clockwise;
            }

            public override int GetHashCode()
            {
                return HashCode.Combine(loops, spacing, stepType, step, offsetType, offset, clockwise);
            }
        }
    }

 

 

 

ArchimedeanDots.zip

Edited by VeLC
Version 1.1
  • Like 4
  • Upvote 1
Link to comment
Share on other sites

Link to comment
Share on other sites

  • VeLC changed the title to Archimedean Dots (May 4, 2022)

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...