Random SVG star

Here is small application that morphs between star shapes.

The VCL project looks like this: The form color is set to “black” and it contains an TSVG2Image aligned “alClient” and a TTimer.

Also, the “DoubleBuffered” property of the form is set to “True”, otherwise we’ll end up with a lot of unwanted flicker.

RandomStar1

Then the following code in the unit:

[code language="Delphi"]

unit Unit1;

// -----------------------------------------------------------------------------
//
// Random SVG star
//
// B.J.H. Verhue
//
// -----------------------------------------------------------------------------

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.ExtCtrls,
  BVE.SVG2Image.VCL;

type
  TForm1 = class(TForm)
    SVG2Image1: TSVG2Image;
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure SVG2Image1Click(Sender: TObject);
  private
    FTime: integer;
    function LFO(const f: single): single;
    function LFO2(const s, f: single): single;
  public
    function StarPath(const aEdgeCount: integer; const aRIn, aROut, aOuterEdge: single): string;
    procedure Update;
  end;

var
  Form1: TForm1;

implementation
uses
  Vcl.Clipbrd,
  System.Math;

{$R *.dfm}

{ TForm1 }

function TForm1.LFO(const f: single): single;
begin
  // Low frequency oscilator

  Result := Sin(2* PI * f * FTime / 1000);
end;

function TForm1.LFO2(const s, f: single): single;
begin
  // Coupled LFO's to simulate randomness

  Result := LFO(s * LFO(f));
end;

function SVGColor(const aR, aG, aB: single): string;
begin
  Result := '#'
     + IntToHex(Round(255 * Abs(aR)), 2)
     + IntToHex(Round(255 * Abs(aG)), 2)
     + IntToHex(Round(255 * Abs(aB)), 2);
end;

function TForm1.StarPath(const aEdgeCount: integer; const aRIn, aROut,
  aOuterEdge: single): string;
var
  i: integer;
  InnerAngle, OuterAngle, X, Y: single;
begin
  // Create starshaped pathdata
  // aEdgeCount : number of edges or points
  // aRIn       : radius of star core
  // aROuter    : outer radius of star
  // aOuterEdge : width of star point

  Result := '';

  InnerAngle := 2 * PI / aEdgeCount;
  OuterAngle := arctan2(aOuterEdge, aROut);

  for i := 0 to aEdgeCount - 1 do
  begin
    X := aRIn * Sin(i * InnerAngle);
    Y := aRIn * Cos(i * InnerAngle);

    if i = 0 then
      Result := Result + Format('M%0:.2f,%1:.2f', [X, Y])
    else
      Result := Result + Format('L%0:.2f,%1:.2f', [X, Y]);

    X := aROut * Sin((i + 0.5 - OuterAngle) * InnerAngle);
    Y := aROut * Cos((i + 0.5 - OuterAngle) * InnerAngle);
    Result := Result + Format('L%0:.2f,%1:.2f', [X, Y]);

    X := aROut * Sin((i + 0.5 + OuterAngle) * InnerAngle);
    Y := aROut * Cos((i + 0.5 + OuterAngle) * InnerAngle);
    Result := Result + Format('L%0:.2f,%1:.2f', [X, Y]);

  end;
  Result := Result + 'Z"/>';
end;

procedure TForm1.SVG2Image1Click(Sender: TObject);
begin
  Timer1.Enabled := not Timer1.Enabled;

  // Put SVG on clipboard if paused

  if not Timer1.Enabled then
    Clipboard.AsText := SVG2Image1.SVG.Text;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // Timer interval is set to 25ms, FPS=40

  Update;
  Inc(FTime, Timer1.Interval);
end;

procedure TForm1.Update;
begin
  // Create SVG content and assign to SVG2Image

  SVG2Image1.SVG.Text :=
      '<svg version="1.1" id="star"' + ' viewBox="-100 -100 200 200"' + ' xmlns="http://www.w3.org/2000/svg">'

  // Defs section with a random radial gradient

    + ' <defs>'
      + ' <radialGradient id="radGrad1">'
      + ' <stop offset="0%" stop-color="' + SVGColor(LFO2(0.01, 0.02), LFO2(0.015, 0.03), LFO2(0.008, 0.015)) + '" />'
      + ' <stop offset="100%" stop-color="' + SVGColor(LFO2(0.02, 0.01), LFO2(0.025, 0.015), LFO2(0.03, 0.008)) + '" />'
      + ' </radialGradient>'
    + '</defs>'

  // Path element with random starshape

    + '<path stroke="pink" fill="url(#radGrad1)" stroke-width="3" d="' + StarPath( Round(19 + 16 * LFO2(0.01, 0.01)), 50 + 30 * LFO2(0.03, 0.005), 80 + 10 * LFO2(0.008, 0.01), 16 + 16 * LFO2(0.01, 0.002)) + '"/>'

    + '</svg>';

  SVG2Image1.Repaint;
end;

initialization
  // Set decimal seperator for "Format" function
  FormatSettings.DecimalSeparator := '.';

end.
[/code]

Sources can be found on github. To compile the examples, you need the demo or the full version of the SVG control package.