### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: BLADES: 8 Curve Types Now with Any Color  (Read 2617 times)

#### Boleeman

• Sr. Member
• Posts: 433
##### BLADES: 8 Curve Types Now with Any Color
« on: March 05, 2024, 11:35:13 am »
8 Curve Types Now.

Hi All.

I converted some VB6 code of mine that creates "Venetian Blind" type Blades.
(I also made something similar in Scratch here https://scratch.mit.edu/projects/947286929/  )

Got the basics to work on a TImage. There are 7 curve types in total so far.

The curves all produce Red Green or Blue blade curves and 3 other colors (see 3rd post).
Also some curves (radial) have slight imperfections.

How can we make the curves any color?

Any help would be greatly appreciated.

Here is what I have so far. Enjoy.
« Last Edit: March 09, 2024, 01:16:52 pm by Boleeman »

#### Boleeman

• Sr. Member
• Posts: 433
« Reply #1 on: March 06, 2024, 08:30:12 am »
Yeah, got the Red, Green and Blue to work for all six curves.

The Leaf curve would make a good opening for an Intro. Screen.
Kinda like an Intro. to a James Bond 007 Movie. Just imagine!

Now, how to make any colour?
« Last Edit: March 06, 2024, 08:32:06 am by Boleeman »

#### Boleeman

• Sr. Member
• Posts: 433
« Reply #2 on: March 06, 2024, 11:51:21 am »
Getting closer. Now 6 colors work only.

This now works for RGB color components that are exactly equal to 0 or 255.

Red=RGB(255,0,0)
Green=RGB(0,255,0)
Blue=RGB(0,0,255)

Yellow= RGB(255,255,0)
cyan=RGB(0,255,255)
magenta=RGB(255,0,255)

Seems like it does not work for orange = (255,128,0), brown=RGB(128,64,0), etc.

so when one or more RGB color components is not exactly equal to 0 or 255.
« Last Edit: March 06, 2024, 11:54:11 am by Boleeman »

#### Dzandaa

• Sr. Member
• Posts: 250
• From C# to Lazarus
##### Re: BLADES: 6 Curve Types & 6 Colors
« Reply #3 on: March 06, 2024, 06:07:24 pm »
Hi,
@Boleeman

I didn't look at your code, but I think that when calculating the intermediate colors, you need to take into account the RGB channels of the selected colors.

Ex:
Selected:
R = 255
G =128
B = 128

Black:

R = 0
G = 0
B = 0

if at a moment
R = 127
G and B must be 63 (same proportion as R for the range of color)

B->

Dzandaa

#### wp

• Hero Member
• Posts: 11912
##### Re: BLADES: 6 Curve Types & 6 Colors
« Reply #4 on: March 06, 2024, 07:45:03 pm »
I don't fully understand the problem, I guess you mean that the colors are not the same from blade to blade?

A blade always seems to make a transition from a given color to black, and you use some positional coordinate in the image to define the gradient factor (called "Intensity" in your code). For a single blade the "Intensity" should be between zero and one (0 --> blade color, 1 --> black). But you do not take care of that. In your project the "Intensity" factor grows from 0 at the top to 8 at the bottom. In the "InterpolateColors" interpolation this leads to numerous overflows of the byte range, and finally results in the false colors.

What to do? Make sure that the "Intensity" passed to InterpolateColors is a number between 0 and 1! The easiest way to achieve this is by taking the fraction of the expression that InterpolateColors gets as last argument:

Code: Pascal  [Select][+][-]
1.   case CurveType of
2.     0: begin
3.          for y := 0 to myHeight - 1 do
4.          begin
5.            patternColor := InterpolateColor(BladeColor, clBlack, frac(PercentCoeff * y / myHeight));
6.            Image.Canvas.Pen.Color := patternColor;
7.            Image.Canvas.Line(0, y, myWidth, y);
8.          end;
9.        end;
« Last Edit: March 06, 2024, 07:48:32 pm by wp »

#### Boleeman

• Sr. Member
• Posts: 433
##### Re: BLADES: 6 Curve Types & 6 Colors
« Reply #5 on: March 07, 2024, 06:58:20 am »
Many thanks Dzandaa and WP for replying.

A great many thanks for that advice, as I was getting into HSL to try to solve the problem and was going round in circles.

Still have a few curves (diagonal and Radial) with a few gaps in them.

I added a save to png and also an ending color select (each blade has a starting color and and end color).

#### Boleeman

• Sr. Member
• Posts: 433
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #6 on: March 07, 2024, 08:19:07 am »
Dark to Light color and Light color to dark color comparison of the Lazarus version.

Now I also want to try to add a XOR type band/stripe effect, like I did for my vb6 program.
(see attached picture below).

#### wp

• Hero Member
• Posts: 11912
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #7 on: March 07, 2024, 08:04:34 pm »
One more remark about your code: you have a groupbox (gbCurveType) with 6 individual radiobuttons, and you need mulitple "if" statements in order to get the "CurveType" from the checked radiobutton (in MakeCurve). A simpler way would be to use a TRadioGroup (rather than a TGroupBox) and add the radiobuttons by means of the Items. Then you simply can determine the selected radio button from the RadioGroup's ItemIndex:
Code: Pascal  [Select][+][-]
1. procedure TForm1.MakeCurve(Sender: TObject);
2. ...
4. ...

#### Boleeman

• Sr. Member
• Posts: 433
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #8 on: March 08, 2024, 10:03:44 am »

I fixed the diagonal pattern by maintaining a square image size and using the settings in Object Inspector for the TImage stretchin, stretchout and proportional.

Now,
1.  How to resize and save the png proportionally? Saving always saves as 750x750 px

2.  How to fix the small glitch in the radial pattern?
Strangely I do not get that in the VB6 version.
Perhaps the Lazarus version has a rounding error?

Interesting to note when the png is saved the identical horizontal pattern is 6kb in size and the vertical pattern is 12 kb in size. Looks like horizontal is compressed better than vertical for png format.
« Last Edit: March 08, 2024, 10:32:20 am by Boleeman »

#### domasz

• Sr. Member
• Posts: 435
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #9 on: March 08, 2024, 11:33:48 am »
Interesting to note when the png is saved the identical horizontal pattern is 6kb in size and the vertical pattern is 12 kb in size. Looks like horizontal is compressed better than vertical for png format.
Yes, PNG saves top to bottom looking for patterns.

#### wp

• Hero Member
• Posts: 11912
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #10 on: March 08, 2024, 11:35:08 am »
1.  How to resize and save the png proportionally? Saving always saves as 750x750 px
Modify your code so that it can draw on any canvas, not just on that of the TImage. Create a png image of the requested size, call your drawing code with the canvas of the png as parameter.

Code: Pascal  [Select][+][-]
1. procedure TForm1.CreateBlades(ACanvas: TCanvas; myWidth, myHeight: Integer; PercentCoeff: Single;
2.   SumRotator, CurvedOffset: Integer; CurveType: Integer; BladeColor: TColor);
3. var
4.   x, y, x2: Integer;
5.   patternColor, endColor: TColor;
6. begin
7.   ACanvas.Brush.Color := clWhite;
8.   ACanvas.FillRect(0, 0, myWidth, myHeight);
9. ...
10. end;
11.
12. procedure TForm1.MakeCurve(Sender: TObject);
13. begin
14.   ...
16. end;
17.
18. procedure TForm1.SaveToPNG(AFileName: String; ASize: Integer);
19. var
20.   png: TCustomBitmap;
21. begin
22.   png := TPortableNetworkgraphic.Create;
23.   try
24.     png.Width := ASize;
25.     png.Height := ASize;
26.     ...
28.     png.SaveToFile(AFileName);
29.   finally
30.     png.Free;
31.   end;
32.

2.  How to fix the small glitch in the radial pattern?
I did not look into your code in detail, but I noticed an asymmetry in the radial case 2 of CreateBlades: x and y run up to myWidth-1 and myHeight-1, but the lines are drawn to mywidth and myheight without the -1. Therefore I guess that you are not drawing the white pixels at all (when I set the Canvas.Brush.Color at the top of the procedure to clYellow they do become yellow).

I fixed it by subtracting 1 also from myWidth and myHeight inside the loop (don't know if the same is necessary also in the other cases):
Code: Pascal  [Select][+][-]
1. case CurveType of
2. ...
3.       2: begin
4.          for x := 0 to myWidth - 1 do
5.          begin
6.            y := x;
7.            patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * x / myWidth));
8.            Image.Canvas.Pen.Color := patternColor;
9.            Image.Canvas.Line(x, 0, myWidth - x - 1, myHeight - 1);
10.          end;
11.
12.          for y := 0 to myHeight - 2 do
13.          begin
14.            x := y;
15.            patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * y / myHeight));
16.            Image.Canvas.Pen.Color := patternColor;
17.            Image.Canvas.Line(0, y, myWidth - 1, myHeight - y - 1);
18.          end;
19.        end;
« Last Edit: March 08, 2024, 11:46:20 am by wp »

#### Boleeman

• Sr. Member
• Posts: 433
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #11 on: March 08, 2024, 12:28:36 pm »
Thanks WP once again.

I had this for the glitch fix, which seemed to work:
Code: Pascal  [Select][+][-]
1.       2: begin
2.          for x := 0 to myWidth - 1 do
3.          begin
4.            y := x;
5.            patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * x / myWidth));
6.            Image.Canvas.Pen.Color := patternColor;
7.            Image.Canvas.Line(x, 0, myWidth - x - 1, myHeight);
8.          end;
9.
10.          for y := 0 to myHeight - 1 do
11.          begin
12.            x := y;
13.            patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * y / myHeight));
14.            Image.Canvas.Pen.Color := patternColor;
15.            Image.Canvas.Line(0, y, myWidth - 1, myHeight - y);
16.          end;
17.        end;

but was a little off from your one (which is the better solution):
Code: Pascal  [Select][+][-]
1.       2: begin
2.          for x := 0 to myWidth - 1 do
3.          begin
4.            y := x;
5.            patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * x / myWidth));
6.            Image.Canvas.Pen.Color := patternColor;
7.            Image.Canvas.Line(x, 0, myWidth - x - 1, myHeight - 1);
8.          end;
9.
10.          for y := 0 to myHeight - 2 do
11.          begin
12.            x := y;
13.            patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * y / myHeight));
14.            Image.Canvas.Pen.Color := patternColor;
15.            Image.Canvas.Line(0, y, myWidth - 1, myHeight - y - 1);
16.          end;
17.        end;

« Last Edit: March 08, 2024, 12:33:11 pm by Boleeman »

#### Boleeman

• Sr. Member
• Posts: 433
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #12 on: March 08, 2024, 01:02:24 pm »
GAP Error when selecting from other curves, then resizing and then selecting DiagP shape.
Don't seem to get the GAP error when switching and resizing between all the other curves.

When I select Radial, then resize form horizontally and then select DiagP I get a gap in the top right corner
(as shown in picture below).

Could that be from a similar sizing bug for the Radial curve or is it because the TImage is not perfectly square?
Kinda stumped on working out what is doing it, but figure it is a fault in the code below somewhere.
Code: Pascal  [Select][+][-]
1.      3: begin
2.          for x := 0 to myWidth - 1 do
3.          begin
4.            y := x;
5.            patternColor := InterpolateColor(BladeColor, endColor, Frac((PercentCoeff * x / myWidth)));
6.            Image.Canvas.Pen.Color := patternColor;
7.            Image.Canvas.Line(0, x, 2 * myWidth - x, 2 * myHeight);
8.          end;
9.
10.          for y := 0 to myHeight - 1 do
11.          begin
12.            x := y;
13.             patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * y / myHeight));
14.             Image.Canvas.Pen.Color := patternColor;
15.            Image.Canvas.Line(y, 0, 2 * myWidth, 2 * myHeight - y);
16.          end;
17.
18.          for x := 0 to myWidth - 1 do
19.          begin
20.            y := x;
21.             patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * x / myWidth));
22.             Image.Canvas.Pen.Color := patternColor;
23.            Image.Canvas.Line(0, x, 2 * myWidth - x, 2 * myHeight);
24.          end;
25.
26.          for y := 0 to myHeight - 1 do
27.          begin
28.            x := y;
29.             patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * y / myHeight));
30.             Image.Canvas.Pen.Color := patternColor;
31.            Image.Canvas.Line(y, 0, 2 * myWidth, 2 * myHeight - y);
32.          end;
33.        end;

Actually I tried this but still get that gap, unless I resize the form to be square and then change from Radial to DiagP and resize.

Code: Pascal  [Select][+][-]
1.       3: begin
2.          for x := 0 to myWidth - 1 do
3.          begin
4.            y := x;
5.            patternColor := InterpolateColor(BladeColor, endColor, Frac((PercentCoeff * x / myWidth)));
6.            Image.Canvas.Pen.Color := patternColor;
7.            Image.Canvas.Line(0, x, 2 * (myWidth - 1) - x, 2 * (myHeight - 1));
8.          end;
9.
10.          for y := 0 to myHeight - 2 do
11.          begin
12.            x := y;
13.             patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * y / myHeight));
14.             Image.Canvas.Pen.Color := patternColor;
15.            Image.Canvas.Line(y, 0, 2 * (myWidth -1), 2 * (myHeight - 1) - y);
16.          end;
17.
18.          for x := 0 to myWidth - 1 do
19.          begin
20.            y := x;
21.             patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * x / myWidth));
22.             Image.Canvas.Pen.Color := patternColor;
23.            Image.Canvas.Line(0, x, 2 * (myWidth - 1) - x, 2 * (myHeight - 1));
24.          end;
25.
26.          for y := 0 to myHeight - 2 do
27.          begin
28.            x := y;
29.             patternColor := InterpolateColor(BladeColor, endColor, Frac(PercentCoeff * y / myHeight));
30.             Image.Canvas.Pen.Color := patternColor;
31.            Image.Canvas.Line(y, 0, 2 * (myWidth - 1), 2 * (myHeight - 1) - y);
32.          end;
33.        end;
« Last Edit: March 08, 2024, 01:52:09 pm by Boleeman »

#### wp

• Hero Member
• Posts: 11912
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #13 on: March 08, 2024, 07:09:51 pm »
I don't understand this code. There is a loop with variable x but it is used at a position where y should be? And why are there so many loops to create the diagonal blades?

Trying to understand what is happening, I commented out all loops (in case 3) except for the first one and looked at the output - it filled the lower left triangle covering all y values along the left edge of the image - yes, this is clear when x should be renamed to y.  And the second loop covers the upper right triangle. And the third and fourth loops are just duplicates.

Let me simplify this. I'd like to run x from 0 all over the horizontal edge: for x := 0 to mywidth-1. Next, I've been twisting my brain to understand the endpoint of your line command... To simplify I just draw a diagonal for x=0: canvas.line(0, 0, mywidth, myheight); then when x advances we add x to the x coordinates: canvas.Line(x, 0, mywidth+x, myheight):
Code: Pascal  [Select][+][-]
1. { case 3}
2.          for x :=0 to mywidth-1 do
3.          begin
4.            patternColor := InterpolateColor(BladeColor, endColor, frac(PercentCoeff * x / mywidth));
5.            Image.Canvas.Pen.Color := patternColor;
6.            Image.Canvas.Line(x, 0, mywidth + x, myHeight);
7.          end;
OK. But this draws only the upper right triangle if the image. How to get the lower left triangle? You are doing this by introducing a second loop, now running vertically. But this complicates the situation because the factor for InterpolateColors must be correctly chosen (I think here is reason for the empty triangle in your screenshot). But why don't we just extend the current x loop to the left and have x run from -(myWidth-1) to +(mywidth-1). A good part of the drawing is outside the canvas limits, but this is not harmful because the canvas clips the lines at its edges.
Code: Pascal  [Select][+][-]
1.          for x := -mywidth+1 to mywidth-1 do
2.          begin
3.            patternColor := InterpolateColor(BladeColor, endColor, frac(PercentCoeff * x / mywidth));
4.            Image.Canvas.Pen.Color := patternColor;
5.            Image.Canvas.Line(x, 0, mywidth + x, myHeight);
6.          end
This looks good. Except for one difference to your screenshot in which the center blade running from top/left to bottom right is "complete", or "symmetric" (hard to describe...). But when x is negative in the first part of the drawing the factor for InterpolateColor is negative, too, and this screws up the color interpolation which wants a factor between 0 and 1. Well, the easiest way to make a negative number positive is by calling the abs() function which just removes the sign of the number:
Code: Pascal  [Select][+][-]
1.          for x := -mywidth+1 to mywidth-1 do
2.          begin
3.            patternColor := InterpolateColor(BladeColor, endColor, abs(frac(PercentCoeff * x / mywidth)));
4.            Image.Canvas.Pen.Color := patternColor;
5.            Image.Canvas.Line(x, 0, mywidth + x, myHeight);
6.          end
Alas! That's it...
« Last Edit: March 08, 2024, 07:15:09 pm by wp »

#### Boleeman

• Sr. Member
• Posts: 433
##### Re: BLADES: 6 Curve Types with Any Color
« Reply #14 on: March 08, 2024, 10:51:17 pm »
WP, Many thanks for looking into that loopy Diagonal code.

This probably means that the other curves could probably be better coded as well, with fewer loops.

I originally made it for VB6 years ago by "blindly experimenting and altering bits and pieces" until I got some interesting patterns happening. Then the other day I tried converting the vb6 code to Lazarus. I suspected that I was overdoing it on the Loops, but was going round in circular loops trying to fix it. I will look at how you approached the problem as this will help me to understand the code better.

I have learnt quite a few things in this project with your help.

1.  Use a frac in frac(PercentCoeff * x / mywidth)
2.  Using a TRadiogroup and initializing the options.
3.  Loops (simplifying and debugging)

WP.    A HUGE thanks for breaking down the solution process, as I needed to understand it better.
« Last Edit: March 08, 2024, 10:57:48 pm by Boleeman »