{ Apophysis Copyright (C) 2001-2004 Mark Townsend Apophysis Copyright (C) 2005-2006 Ronald Hordijk, Piotr Borys, Peter Sdobnov Apophysis Copyright (C) 2007-2008 Piotr Borys, Peter Sdobnov Apophysis "3D hack" Copyright (C) 2007-2008 Peter Sdobnov Apophysis "7X" Copyright (C) 2009-2010 Georg Kiehne Apophysis AV "Phoenix Edition" Copyright (C) 2021-2022 Alice V. Koryagina This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. } unit Mutate; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, ControlPoint, ComCtrls, Menus, Buttons, Cmap, RenderingInterface; type TMutateForm = class(TForm) gbDirections: TGroupBox; Timer: TTimer; GroupBox2: TGroupBox; scrollTime: TScrollBar; cmbTrend: TComboBox; chkSameNum: TCheckBox; QualityPopup: TPopupMenu; mnuLowQuality: TMenuItem; mnuMediumQuality: TMenuItem; mnuHighQuality: TMenuItem; N3: TMenuItem; mnuResetLocation: TMenuItem; mnuBack: TMenuItem; N1: TMenuItem; mnuMaintainSym: TMenuItem; N2: TMenuItem; pnlDirections: TPanel; Panel7: TPanel; Panel4: TPanel; Panel0: TPanel; Image0: TImage; Panel8: TPanel; Panel3: TPanel; Panel2: TPanel; Panel1: TPanel; Panel5: TPanel; pnlSpeed: TPanel; txtTime: TEdit; pnlTrend: TPanel; Panel6: TPanel; procedure FormShow(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Image0Click(Sender: TObject); procedure MutantClick(Sender: TObject); procedure sbTimeChange(Sender: TObject); procedure TimerTimer(Sender: TObject); procedure scrollTimeChange(Sender: TObject); procedure cmbTrendChange(Sender: TObject); procedure mnuHighQualityClick(Sender: TObject); procedure mnuLowQualityClick(Sender: TObject); procedure mnuMediumQualityClick(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure chkSameNumClick(Sender: TObject); procedure mnuResetLocationClick(Sender: TObject); procedure mnuBackClick(Sender: TObject); procedure mnuMaintainSymClick(Sender: TObject); private name: string; PreviewDensity: double; Updating: boolean; cps: array[0..8] of TControlPoint; Mutants: array[0..8] of TControlPoint; MImages: array[1..8] of TImage; // AV Render: TRenderer; Time: double; bstop: boolean; brightness, gamma, vibrancy, contrast, gamma_threshold: double; width, height: integer; // AV: to keep original flame size seed, InitSeed: integer; procedure RandomSet; procedure ShowMain; procedure ShowMutants; procedure Interpolate; procedure RandomCP(var cp: TControlPoint; const minT, maxT: integer); // AV: removed from ControlPoint procedure SetVariation(var cp: TControlPoint); // AV: wrote a local method public Zoom: Double; Center: array[0..1] of double; cmap: TColorMap; procedure UpdateDisplay; procedure UpdateFlame; end; var MutateForm: TMutateForm; implementation uses Main, Global, Registry, Editor, XFormMan, Adjust, Translation; {$R *.DFM} procedure TMutateForm.UpdateFlame; begin MainForm.StopThread; MainForm.UpdateUndo; MainCp.Copy(cps[0]); Transforms := MainCp.TrianglesFromCP(MainTriangles); MainCp.cmap := cmap; MainCp.name := name; // this is kinda funny MainCp.AdjustScale(width, height); // AV: restore normal size if mnuResetLocation.checked then begin MainForm.Center[0] := cps[0].Center[0]; MainForm.Center[1] := cps[0].Center[1]; end; MainForm.RedrawTimer.enabled := true; if EditForm.Visible then EditForm.UpdateDisplay; if AdjustForm.Visible then AdjustForm.UpdateDisplay; end; procedure TMutateForm.UpdateDisplay; begin cps[0].copy(MainCp); cps[0].AdjustScale(Image0.Width, Image0.Height); cps[0].cmap := MainCp.cmap; cmap := MainCp.cmap; name := Maincp.name; zoom := MainCp.zoom; width := MainCp.Width; // AV height := MainCp.Height; // AV center[0] := MainCp.center[0]; center[1] := MainCp.center[1]; vibrancy := cps[0].vibrancy; gamma := cps[0].gamma; gamma_threshold := cps[0].gamma_threshold; // AV brightness := cps[0].brightness; contrast := cps[0].contrast; // AV Interpolate; ShowMain; Application.ProcessMessages; ShowMutants; end; procedure TMutateForm.SetVariation(var cp: TControlPoint); var i, k, n: integer; begin if cmbTrend.ItemIndex = NRVAR then // AV: set random variations from selected repeat n := random(NrVar); until Variations[n] else // AV: sorted list is used from now n := GetVariationIndex(cmbTrend.Items[cmbTrend.ItemIndex]); for i := 0 to cp.NumXforms-1 do begin for k := 0 to NrVar-1 do // AV: simplified the calculations cp.xform[i].SetVariation(k, 0); cp.xform[i].SetVariation(n, 1); end; end; procedure TMutateForm.ShowMain; begin cps[0].Width := Image0.Width; cps[0].Height := Image0.Height; cps[0].spatial_oversample := defOversample; cps[0].spatial_filter_radius := defFilterRadius; cps[0].sample_density := PreviewDensity; cps[0].brightness := brightness; cps[0].contrast := contrast; // AV cps[0].gamma := gamma; cps[0].gamma_threshold := gamma_threshold; // AV cps[0].vibrancy := vibrancy; cps[0].sample_density := PreviewDensity; cps[0].cmap := cmap; cps[0].background := MainCp.background; if mnuResetLocation.checked then begin cps[0].CalcBoundbox; zoom := 0; center[0] := cps[0].center[0]; center[1] := cps[0].Center[1]; end; cps[0].zoom := zoom; cps[0].center[0] := center[0]; cps[0].center[1] := center[1]; Render.SetCP(cps[0]); Render.Render; Image0.Picture.Graphic := Render.GetImage; end; procedure TMutateForm.ShowMutants; var i: byte; //t: cardinal; begin if Visible = false then exit; Updating := true; //t := GetTickCount; for i := 1 to 8 do begin mutants[i].Width := Image0.Width; mutants[i].Height := Image0.Height; mutants[i].spatial_filter_radius := defFilterRadius; mutants[i].spatial_oversample := defOversample; mutants[i].sample_density := PreviewDensity; mutants[i].brightness := brightness; mutants[i].contrast := contrast; // AV mutants[i].gamma := gamma; mutants[i].vibrancy := vibrancy; mutants[i].gamma_threshold := gamma_threshold; // AV if mnuResetLocation.checked then begin mutants[i].CalcBoundbox; mutants[i].zoom := 0; end else begin mutants[i].zoom := zoom; mutants[i].center[0] := center[0]; mutants[i].center[1] := center[1]; end; Render.SetCP(mutants[i]); Render.Render; // AV: replaced separate TImages by an array MImages[i].Picture.Graphic := Render.GetImage; MImages[i].Refresh; Updating := false; end; //ShowMessage((GetTickCount - t).ToString); end; procedure TMutateForm.Interpolate; var i, j: Integer; begin if MainCp = nil then Exit; //cps[0].Time := 0; for i := 1 to 8 do begin if bstop then exit; cps[0].Time := 0; cps[i].Time := 1; (* -X- something is not right here... Mutants[i] may be destroyed already Investigate? *) // AV: it's OK... Mutants are just placeholders for a new flame Mutants[i].Clear; Mutants[i].InterpolateX(cps[0], cps[i], Time / 100); Mutants[i].background := MainCp.background; if mnuMaintainSym.Checked then // maintain symmetry for j := 0 to transforms - 1 do if cps[0].xform[j].Symmetry = 1 then mutants[i].xform[j].Assign(cps[0].xform[j]); end; end; procedure TMutateForm.RandomCP(var cp: TControlPoint; const minT, maxT: integer); // AV var nrXforms, i: integer; begin nrXforms := random(MaxT - MinT + 1) + MinT; for i := 0 to nrXforms - 1 do begin cp.xform[i].density := 1.0 / nrXforms; cp.xform[i].color := i / (nrXforms - 1); cp.xform[i].RandomizeCoefs(cp.xform[i].c); end; SetVariation(cp); //AV end; procedure TMutateForm.RandomSet; var i: byte; begin RandSeed := seed; for i := 1 to 8 do begin cps[i].clear; if chkSameNum.checked then RandomCP(cps[i], transforms, transforms) else RandomCP(cps[i], mutantMinTransforms, mutantMaxTransforms); { // AV: now it's done inside InterpolateX method if cps[0].HasFinalXForm = false then begin cps[i].xform[cps[i].NumXForms].Clear; cps[i].xform[cps[i].NumXForms].symmetry := 1; end; } end; Interpolate; end; procedure TMutateForm.FormShow(Sender: TObject); var Registry: TRegistry; begin { Read position from registry } Registry := TRegistry.Create; try Registry.RootKey := HKEY_CURRENT_USER; if Registry.OpenKey('Software\' + APP_NAME + '\Forms\Mutate', False) then begin if Registry.ValueExists('Left') then MutateForm.Left := Registry.ReadInteger('Left'); if Registry.ValueExists('Top') then MutateForm.Top := Registry.ReadInteger('Top'); end; Registry.CloseKey; finally Registry.Free; end; { if (cps[0].xform[0].density <> 0) and Assigned(MainCp) then begin // hmm...!? //Interpolate; // AV: this method is already called inside RandomSet ShowMain; ShowMutants; end; } end; procedure TMutateForm.FormCreate(Sender: TObject); var i: integer; begin self.Caption := TextByKey('mutation-title'); gbDirections.Caption := TextByKey('mutation-directions'); pnlSpeed.Caption := TextByKey('mutation-speed'); pnlTrend.Caption := TextByKey('mutation-trend'); chkSameNum.Caption := TextByKey('mutation-keepnumberoftransforms'); mnuLowQuality.Caption := TextByKey('common-lowquality'); mnuMediumQuality.Caption := TextByKey('common-mediumquality'); mnuHighQuality.Caption := TextByKey('common-highquality'); mnuResetLocation.Caption := TextByKey('common-resetlocation'); mnuMaintainSym.Caption := TextByKey('mutation-maintainsymmetry'); mnuBack.Caption := TextByKey('mutation-previous'); cmbTrend.Items.clear; for i:= 0 to NRVAR -1 do // AV cmbTrend.Items.Add(varnames(i)); cmbTrend.Sorted := False; // AV: 'random' item must be last cmbTrend.Items.Add(TextByKey('mutation-randomtrend')); case MutatePrevQual of 0: begin mnuLowQuality.Checked := true; PreviewDensity := prevLowQuality; end; 1: begin mnuMediumQuality.Checked := true; PreviewDensity := prevMediumQuality; end; 2: begin mnuHighQuality.Checked := true; PreviewDensity := prevHighQuality; end; end; Render := TRenderer.Create; for i := 0 to 8 do begin cps[i] := TControlPoint.Create; Mutants[i] := TControlPoint.Create; end; for i := 1 to 8 do // AV: added array for speed begin MImages[i] := TImage.Create(self); MImages[i].Parent := TPanel(self.FindComponent('Panel' + IntToStr(i))); MImages[i].Align := alClient; MImages[i].Tag := i; MImages[i].OnClick := MutantClick; MImages[i].PopupMenu := QualityPopup; end; Time := 25; // 35; scrollTime.Position := 25; txtTime.Text := '0.25'; // AV cmbTrend.ItemIndex := NRVAR; // AV InitSeed := random(1234567890); seed := InitSeed; RandomSet; end; procedure TMutateForm.FormDestroy(Sender: TObject); var i: integer; begin Render.Stop; Render.Free; for i := 0 to 8 do begin cps[i].Free; Mutants[i].Free; end; end; procedure TMutateForm.Image0Click(Sender: TObject); begin Render.Stop; mnuBack.Enabled := true; inc(seed); RandomSet; ShowMutants; end; procedure TMutateForm.MutantClick(Sender: TObject); var i: integer; cpt: TControlPoint; begin cpt := TControlPoint.Create; bstop := true; // AV: optimized faster version without checking indices i := TImage(Sender).Tag; cps[0].Time := 0; cps[i].Time := 1; cpt.InterpolateX(cps[0], cps[i], Time / 100); if mnuMaintainSym.Checked then // maintain symmetry begin for i := 0 to transforms - 1 do begin if cps[0].xform[i].Symmetry = 1 then cpt.xform[i].Assign(cps[0].xform[i]); end; end; // AV: it's faster to make a copy by reference that use TControlPoint.Copy() cps[0].Free; // AV cps[0] := cpt; // AV bstop := false; ShowMain; Interpolate; ShowMutants; UpdateFlame; end; procedure TMutateForm.sbTimeChange(Sender: TObject); begin bstop := true; Render.Stop; Time := scrollTime.Position; bstop := false; Interpolate; ShowMutants; end; procedure TMutateForm.TimerTimer(Sender: TObject); begin Timer.Enabled := false; if (Time <> scrollTime.Position) and (not updating) then begin Time := scrollTime.Position; Interpolate; ShowMutants; end; end; procedure TMutateForm.scrollTimeChange(Sender: TObject); begin Timer.Enabled := true; txtTime.Text := FloatToStr(scrollTime.Position / 100); end; procedure TMutateForm.cmbTrendChange(Sender: TObject); var i: byte; begin for i := 1 to 8 do SetVariation(cps[i]); //AV Interpolate; ShowMutants; end; procedure TMutateForm.mnuHighQualityClick(Sender: TObject); begin mnuHighQuality.Checked := True; PreviewDensity := prevHighQuality; MutatePrevQual := 2; ShowMain; ShowMutants; end; procedure TMutateForm.mnuLowQualityClick(Sender: TObject); begin mnuLowQuality.Checked := True; PreviewDensity := prevLowQuality; MutatePrevQual := 0; ShowMain; ShowMutants; end; procedure TMutateForm.mnuMediumQualityClick(Sender: TObject); begin mnuMediumQuality.Checked := True; PreviewDensity := prevMediumQuality; MutatePrevQual := 1; ShowMain; ShowMutants; end; procedure TMutateForm.FormClose(Sender: TObject; var Action: TCloseAction); var Registry: TRegistry; begin { Write position to registry } Registry := TRegistry.Create; try Registry.RootKey := HKEY_CURRENT_USER; if Registry.OpenKey('\Software\' + APP_NAME + '\Forms\Mutate', True) then begin Registry.WriteInteger('Top', MutateForm.Top); Registry.WriteInteger('Left', MutateForm.Left); end; finally Registry.Free; end; end; procedure TMutateForm.chkSameNumClick(Sender: TObject); begin RandomSet; // Interpolate; // AV: this method is already called inside RandomSet ShowMutants; end; procedure TMutateForm.mnuResetLocationClick(Sender: TObject); begin mnuResetLocation.Checked := not mnuResetLocation.Checked; if not mnuResetLocation.checked then begin cps[0].width := MainCp.width; cps[0].height := MainCp.height; cps[0].pixels_per_unit := MainCp.pixels_per_unit; cps[0].AdjustScale(Image0.width, Image0.Height); cps[0].zoom := MainCp.zoom; cps[0].center[0] := MainCp.center[0]; cps[0].center[1] := MainCp.center[1]; zoom := cps[0].zoom; center[0] := cps[0].center[0]; center[1] := cps[0].center[1]; end; ShowMain; ShowMutants; end; procedure TMutateForm.mnuBackClick(Sender: TObject); begin Render.Stop; if seed > InitSeed then dec(seed); if seed = InitSeed then mnuBack.enabled := false; RandomSet; ShowMutants; end; procedure TMutateForm.mnuMaintainSymClick(Sender: TObject); begin mnuMaintainSym.Checked := not mnuMaintainSym.Checked; Interpolate; ShowMutants; end; end.