unit ClientVisuals;
interface
uses
Windows, Messages, SysUtils, Types, UITypes, Classes, ASVisualization,
MainForm, ASKernelDefs, Generics.Defaults, Generics.Collections, Controls,
Forms, VisForm, ASObjects, Graphics, Menus, ClientDefs, DoublePoint, VisCtl,
rgl, VisCtl2D;
type
TVisFrm = class(TForm)
strict private
FAspect: Double;
strict protected
const
CMD_SETSIZE = 101;
CMD_DISCARD = 102;
protected
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
function CanResize(var NewWidth: Integer; var NewHeight: Integer): Boolean;
override;
procedure KeyPress(var Key: Char); override;
procedure DoClose(var Action: TCloseAction); override;
public
constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
end;
TManagedVisCtl2D = class(TVisCtl2D)
strict private
FDiagramName: string;
FmnuDetach: TMenuItem;
FmnuDock: TMenuItem;
class var FInstances: TDictionary<string, TManagedVisCtl2D>;
class constructor ClassCreate;
class destructor ClassDestroy;
procedure Detach(Sender: TObject);
procedure Dock(Sender: TObject); reintroduce;
procedure ContextPopup(Sender: TObject);
protected
procedure SetWindowWidth(AWidth: Integer); override;
procedure SetWindowHeight(AHeight: Integer); override;
procedure SetDetached(ADetached: Boolean); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property DiagramName: string read FDiagramName;
class property Instances: TDictionary<string, TManagedVisCtl2D> read FInstances;
end;
TTemporaryVisCtl2D = class(TVisCtl2D);
TManagedVisCtl3D = class(TVisCtl3D)
strict private
FSceneName: string;
FmnuDetach: TMenuItem;
FmnuDock: TMenuItem;
class var FInstances: TDictionary<string, TManagedVisCtl3D>;
class constructor ClassCreate;
class destructor ClassDestroy;
procedure Detach(Sender: TObject);
procedure Dock(Sender: TObject); reintroduce;
procedure ContextPopup(Sender: TObject);
protected
procedure SetWindowWidth(AWidth: Integer); override;
procedure SetWindowHeight(AHeight: Integer); override;
procedure SetDetached(ADetached: Boolean); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property SceneName: string read FSceneName;
class property Instances: TDictionary<string, TManagedVisCtl3D> read FInstances;
end;
TTemporaryVisCtl3D = class(TVisCtl3D);
TVisualRec = class
ID: TGUID;
Name: string;
ClassType: TVisObjClass;
Title,
Description: string;
end;
TVisualization = record
strict private
class var FDiagram: string;
class var FScene: string;
class var FPopups: Boolean;
class var FDiagramNumber: UInt64;
class var FSceneNumber: UInt64;
class function BarChart(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function PieChart(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function Histogram(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function ScatterPlot(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; overload; static;
class function ScatterPlot(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D; overload; static;
class function Surface(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D; static;
class function SpaceCurve(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D; static;
class function Object3D(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D; static;
class function RegionPlot(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function Heatmap(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function VectorField(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; overload; static;
class function VectorField(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D; overload; static;
class function Line(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function Rectangle(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function Circle(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function Polygon(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; static;
class function Text(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; overload; static;
class function Text(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D; overload; static;
class function Pixmap(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; overload; static;
class function Pixmap(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D; overload; static;
class function Arrow(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable; overload; static;
class function Arrow(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D; overload; static;
class function GetDrawableByGUID(const AGUID: TGUID): TVisObj; static;
public
class constructor ClassCreate;
class procedure Visualize(AVisual: TVisual; ARef: PAlgosimReference = nil); static;
class procedure RemoveVisual(const AGUID: TGUID); static;
class procedure ConfigVisual(const AGUID: TGUID; ASettings: TAlgosimStructure); static;
class procedure ExportVisual(const AGUID: TGUID; const AFileName: TFileName;
ASettings: TAlgosimStructure); static;
class function QueryVisual(var AVisualRec: TVisualRec): Boolean; static;
class procedure EnumVisuals(AList: TList<TGUID>); static;
class property Diagram: string read FDiagram write FDiagram;
class property Scene: string read FScene write FScene;
class property Popups: Boolean read FPopups write FPopups;
class function GetDiagram(out ANewDiagram: Boolean): TVisCtl2D; static;
class function GetScene(out ANewScene: Boolean): TVisCtl3D; static;
class function ShowDiagram(ADiagram: TDiagram): Boolean; overload; static;
class function ShowDiagram(ADiagram: TVisCtl2D): Boolean; overload; static;
class function ShowDiagram(const ADiagram: string): Boolean; overload; static;
class function ShowDiagram(const ADiagram: TGUID): Boolean; overload; static;
class function ShowScene(AScene: TScene): Boolean; overload; static;
class function ShowScene(AScene: TVisCtl3D): Boolean; overload; static;
class function ShowScene(const AScene: string): Boolean; overload; static;
class function ShowScene(const AScene: TGUID): Boolean; overload; static;
class function ShowVisCtl(const AID: TGUID): Boolean; static;
end;
implementation
uses
ASNum, Gallery, Math, FormFader, WinMgrForm, ImageSizeForm;
class function TVisualization.Arrow(ACtl: TVisCtl3D;
AVisual: TVisual): TDrawable3D;
var
Arrow: TArrow absolute Result;
begin
Arrow := TArrow.Create(ACtl);
var Data := AVisual.Data as TArrowDataR3;
Arrow.Position := Data.a;
Arrow.Vector := Data.v;
Arrow.Color := clRed;
end;
class function TVisualization.Arrow(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
Line: TLine absolute Result;
begin
Line := TLine.Create(ACtl, ACtl.View);
var Data := AVisual.Data as TArrowDataR2;
Line.Start := Data.a;
Line.&End := Data.a + Data.v;
Line.EndMarker.Kind := lemSemiArrow;
end;
class function TVisualization.BarChart(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable;
var
BarChart: TBarChart absolute Result;
CatData: TCategoryData;
begin
BarChart := TBarChart.Create(ACtl, ACtl.View);
for CatData in AVisual.Data as TCategoryDataList do
BarChart.AddBar(CatData.Name, CatData.Value);
end;
{$IF SizeOf(ASR) = SizeOf(Double)}
type
ASRArrayToDoubleArray = TArray<Double>;
ASR2ArrayToDoublePointArray = TArray<TPointD>;
{$ELSE}
function ASRArrayToDoubleArray(const A: TArray<TASR>): TArray<Double>;
begin
SetLength(Result, Length(A));
for var i := 0 to High(A) do
Result[i] := A[i];
end;
function ASR2ArrayToDoublePointArray(const A: TArray<TASR2>): TArray<TPointD>;
begin
SetLength(Result, Length(A));
for var i := 0 to High(A) do
begin
Result[i].X := A[i].X;
Result[i].Y := A[i].Y;
end;
end;
{$ENDIF}
class function TVisualization.Heatmap(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
Heatmap: THeatmap absolute Result;
begin
Heatmap := THeatmap.Create(ACtl, ACtl.View);
with AVisual.Data as THeatmapData do
begin
var bm := Pixmap.CreateGDIBitmap;
try
Heatmap.Bitmap := bm;
finally
bm.Free;
end;
Heatmap.Rect := TRectD.Create(x0, y1, x1, y0);
end;
end;
class function TVisualization.Histogram(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable;
var
Histogram: THistogram absolute Result;
begin
Histogram := THistogram.Create(ACtl, ACtl.View);
Histogram.Data := ASRArrayToDoubleArray((AVisual.Data as THistogramData).Numbers);
end;
class function TVisualization.Line(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
Line: TLine absolute Result;
begin
Line := TLine.Create(ACtl, ACtl.View);
Line.Start := (AVisual.Data as TLineDataR2).a;
Line.&End := (AVisual.Data as TLineDataR2).b;
end;
class function TVisualization.Circle(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
Circle: TCircle absolute Result;
begin
Circle := TCircle.Create(ACtl, ACtl.View);
Circle.Center := (AVisual.Data as TCircleData).a;
Circle.Radius := (AVisual.Data as TCircleData).r;
end;
class constructor TVisualization.ClassCreate;
begin
FPopups := True;
end;
class procedure TVisualization.ConfigVisual(const AGUID: TGUID;
ASettings: TAlgosimStructure);
var
Drawable: TDrawable;
Drawable3D: TDrawable3D;
begin
if TDrawable.TryGetDrawableByGUID(AGUID, Drawable) then
begin
Drawable.Control.BeginBackgroundPaint;
try
if Assigned(ASettings) then
Drawable.Configure(ASettings)
else
Drawable.ShowOptionsForm(AlgosimMainForm);
finally
Drawable.Control.EndBackgroundPaint;
end;
end
else if TDrawable3D.TryGetDrawableByGUID(AGUID, Drawable3D) then
begin
Drawable3D.Control.BeginBackgroundPaint;
try
if Assigned(ASettings) then
Drawable3D.Configure(ASettings)
else
Drawable3D.ShowOptionsForm(AlgosimMainForm);
finally
Drawable3D.Control.EndBackgroundPaint;
end;
end;
end;
class procedure TVisualization.EnumVisuals(AList: TList<TGUID>);
begin
for var D in TDrawable.Instances do
AList.Add(D.Key);
for var D in TDrawable3D.Instances do
AList.Add(D.Key);
end;
class procedure TVisualization.ExportVisual(const AGUID: TGUID;
const AFileName: TFileName; ASettings: TAlgosimStructure);
var
Drawable: TDrawable;
Drawable3D: TDrawable3D;
begin
if TDrawable.TryGetDrawableByGUID(AGUID, Drawable) then
begin
if string(AFileName).IsEmpty then
Drawable.Control.SaveAsSVG
else
begin
var LOptions := DefaultSVGExportOptions;
var LWidth, LHeight: string;
if ASettings.HasMember('width') then
LWidth := ASettings['width'].ToString;
if ASettings.HasMember('height') then
LHeight := ASettings['height'].ToString;
LOptions.SetDimensionsFromText(LWidth, LHeight);
if ASettings.HasMember('stretch') then
LOptions.Stretch := ASettings['stretch'].ToBoolean;
if ASettings.HasMember('title') then
LOptions.Title := ASettings['title'].ToString;
if ASettings.HasMember('description') then
LOptions.Description := ASettings['description'].ToString;
if ASettings.HasMember('language') then
LOptions.Language := ASettings['language'].ToString;
Drawable.Control.SaveAsSVG(AFileName, LOptions);
end;
end
else if TDrawable3D.TryGetDrawableByGUID(AGUID, Drawable3D) then
begin
var LWidth := Drawable3D.Control.ClientWidth;
var LHeight := Drawable3D.Control.ClientHeight;
if ASettings.HasMember('width') then
LWidth := ASettings['width'].ToInteger;
if ASettings.HasMember('height') then
LHeight := ASettings['height'].ToInteger;
Drawable3D.Control.SaveToBitmap(AFileName, LWidth, LHeight);
end;
end;
class function TVisualization.GetDiagram(out ANewDiagram: Boolean): TVisCtl2D;
var
ManagedDiagram: TManagedVisCtl2D;
begin
if FDiagram.IsEmpty then
begin
if FPopups then
begin
Inc(FDiagramNumber);
var frm := TVisFrm.CreateNew(AlgosimMainForm);
frm.Caption := 'Unnamed diagram' + #32 + FDiagramNumber.ToString;
var ctl := TVisCtl2D.Create(frm);
ctl.Parent := frm;
ctl.Align := alClient;
frm.Show;
ANewDiagram := True;
Result := ctl;
end
else
begin
if TDrawable.ModalLevel > 0 then
raise Exception.Create('Cannot remove a drawable object when a settings dialog is open.');
TGallery.Vacuum;
Result := TTemporaryVisCtl2D.Create(AlgosimMainForm);
Result.Parent := AlgosimMainForm.GalleryPanel;
Result.Align := alClient;
ANewDiagram := True;
end;
end
else if TManagedVisCtl2D.Instances.TryGetValue(FDiagram, ManagedDiagram) then
begin
Result := ManagedDiagram;
ANewDiagram := False;
end
else
begin
TGallery.Vacuum;
Result := TManagedVisCtl2D.Create(nil);
Result.Parent := AlgosimMainForm.GalleryPanel;
Result.Align := alClient;
ANewDiagram := True;
end;
end;
class function TVisualization.GetDrawableByGUID(const AGUID: TGUID): TVisObj;
var
Drawable: TDrawable;
Drawable3D: TDrawable3D;
begin
if TDrawable.TryGetDrawableByGUID(AGUID, Drawable) then
Result := Drawable
else if TDrawable3D.TryGetDrawableByGUID(AGUID, Drawable3D) then
Result := Drawable3D
else
Result := nil;
end;
class function TVisualization.GetScene(out ANewScene: Boolean): TVisCtl3D;
var
ManagedScene: TManagedVisCtl3D;
begin
if FScene.IsEmpty then
begin
if FPopups then
begin
Inc(FSceneNumber);
var frm := TVisFrm.CreateNew(AlgosimMainForm);
frm.Caption := 'Unnamed scene' + #32 + FSceneNumber.ToString;
var ctl := TVisCtl3D.Create(frm);
ctl.Parent := frm;
ctl.Align := alClient;
frm.Show;
ANewScene := True;
Result := ctl;
end
else
begin
if TDrawable3D.ModalLevel > 0 then
raise Exception.Create('Cannot remove a drawable object when a settings dialog is open.');
TGallery.Vacuum;
Result := TTemporaryVisCtl3D.Create(AlgosimMainForm);
Result.Parent := AlgosimMainForm.GalleryPanel;
Result.Align := alClient;
ANewScene := True;
end;
end
else if TManagedVisCtl3D.Instances.TryGetValue(FScene, ManagedScene) then
begin
Result := ManagedScene;
ANewScene := False;
end
else
begin
TGallery.Vacuum;
Result := TManagedVisCtl3D.Create(nil);
Result.Parent := AlgosimMainForm.GalleryPanel;
Result.Align := alClient;
ANewScene := True;
end;
end;
class function TVisualization.PieChart(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable;
var
PieChart: TPieChart absolute Result;
CatData: TCategoryData;
begin
PieChart := TPieChart.Create(ACtl, ACtl.View);
for CatData in AVisual.Data as TCategoryDataList do
PieChart.AddSlice(CatData.Name, CatData.Value);
end;
class function TVisualization.Pixmap(ACtl: TVisCtl3D;
AVisual: TVisual): TDrawable3D;
var
Pixmap: TImageRect absolute Result;
begin
Pixmap := TImageRect.Create(ACtl);
var bm := (AVisual.Data as TPixmapData).Pixmap.CreateGDIBitmap;
try
Pixmap.Bitmap := bm
finally
bm.Free;
end;
end;
class function TVisualization.Pixmap(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
Pixmap: TPixmap absolute Result;
begin
Pixmap := TPixmap.Create(ACtl, ACtl.View);
Pixmap.Rect := (AVisual.Data as TPixmapData).Rect;
var bm := (AVisual.Data as TPixmapData).Pixmap.CreateGDIBitmap;
try
Pixmap.Bitmap := bm
finally
bm.Free;
end;
end;
class function TVisualization.Polygon(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
Polygon: TPolygon absolute Result;
begin
Polygon := TPolygon.Create(ACtl, ACtl.View);
Polygon.Points := ASR2ArrayToDoublePointArray((AVisual.Data as TPolygonData).Points)
end;
class function TVisualization.QueryVisual(var AVisualRec: TVisualRec): Boolean;
begin
var D := GetDrawableByGUID(AVisualRec.ID);
Result := Assigned(D) and (D.GUID = AVisualRec.ID);
if Result then
begin
AVisualRec.Name := D.Name;
if D.ClassType.InheritsFrom(TVisObj) then
AVisualRec.ClassType := TVisObjClass(D.ClassType);
AVisualRec.Title := D.Title;
AVisualRec.Description := D.Description;
end;
end;
class function TVisualization.Rectangle(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
Rectangle: TRectangle absolute Result;
begin
Rectangle := TRectangle.Create(ACtl, ACtl.View);
with AVisual.Data as TRectangleData do
Rectangle.Rect := TRectD.Create(a.X, a.Y - h, a.X + w, a.Y);
end;
class function TVisualization.RegionPlot(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable;
var
RegionPlot: TRegion absolute Result;
begin
RegionPlot := TRegion.Create(ACtl, ACtl.View);
with AVisual.Data as TRegionDataR2 do
begin
RegionPlot.SliceData := Slices;
RegionPlot.Axis := Axis;
RegionPlot.UnboundedMin := UnboundedMin;
RegionPlot.UnboundedMax := UnboundedMax;
end;
RegionPlot.Points := False;
RegionPlot.Lines := False;
end;
class procedure TVisualization.RemoveVisual(const AGUID: TGUID);
var
Drawable: TDrawable;
Drawable3D: TDrawable3D;
begin
if TDrawable.TryGetDrawableByGUID(AGUID, Drawable) then
begin
if TDrawable.ModalLevel > 0 then
raise Exception.Create('Cannot remove a drawable object when a settings dialog is open.');
if Drawable is TDiagram then
begin
if Drawable.Control.Parent is TVisFrm then
Drawable.Control.Parent.Free
else if Drawable.Control is TManagedVisCtl2D then
Drawable.Control.Free;
end
else
begin
Drawable.Control.BeginBackgroundPaint;
try
Drawable.Control.RemoveObject(Drawable);
finally
Drawable.Control.EndBackgroundPaint;
end;
end;
end
else if TDrawable3D.TryGetDrawableByGUID(AGUID, Drawable3D) then
begin
if TDrawable3D.ModalLevel > 0 then
raise Exception.Create('Cannot remove a drawable object when a settings dialog is open.');
if Drawable3D is TScene then
begin
if Drawable3D.Control.Parent is TVisFrm then
Drawable3D.Control.Parent.Free
else if Drawable3D.Control is TManagedVisCtl3D then
Drawable3D.Control.Free;
end
else
begin
Drawable3D.Control.BeginBackgroundPaint;
try
Drawable3D.Control.RemoveObject(Drawable3D);
finally
Drawable3D.Control.EndBackgroundPaint;
end;
end;
end;
end;
class function TVisualization.ScatterPlot(ACtl: TVisCtl2D; AVisual: TVisual): TDrawable;
var
ScatterPlot: TXYPlot absolute Result;
begin
ScatterPlot := TXYPlot.Create(ACtl, ACtl.View);
ScatterPlot.Data := ASR2ArrayToDoublePointArray((AVisual.Data as TScatterDataR2).Points);
end;
class function TVisualization.ScatterPlot(ACtl: TVisCtl3D; AVisual: TVisual): TDrawable3D;
var
SimpleScatterPlot: TSimpleScatterPlot absolute Result;
AdvScatterPlot: TAdvScatterPlot absolute Result;
begin
if AVisual.Data is TScatterDataR3 then
begin
SimpleScatterPlot := TSimpleScatterPlot.Create(ACtl);
SimpleScatterPlot.DataAsDoubles := (AVisual.Data as TScatterDataR3).Points;
end
else if AVisual.Data is TScatterDataR3cs then
begin
AdvScatterPlot := TAdvScatterPlot.Create(ACtl);
AdvScatterPlot.DataAsDoubles := (AVisual.Data as TScatterDataR3cs).Points;
end;
end;
class function TVisualization.ShowDiagram(ADiagram: TVisCtl2D): Boolean;
begin
Result := ShowDiagram(ADiagram.Diagram);
end;
class function TVisualization.ShowDiagram(ADiagram: TDiagram): Boolean;
begin
var Ctl := ADiagram.Control;
Result := Assigned(Ctl);
if Result then
begin
if Ctl.Parent = AlgosimMainForm.GalleryPanel then
begin
if AlgosimMainForm.GUIMode = guiConsole then
AlgosimMainForm.GUIMode := guiMixed;
TGallery.ShowControl(Ctl);
end
else
begin
var Frm := GetParentForm(Ctl);
if Assigned(Frm) then
Frm.Show;
end;
end;
end;
class function TVisualization.ShowScene(AScene: TVisCtl3D): Boolean;
begin
Result := ShowScene(AScene.Scene);
end;
class function TVisualization.ShowScene(AScene: TScene): Boolean;
begin
var Ctl := AScene.Control;
Result := Assigned(Ctl);
if Result then
begin
if Ctl.Parent = AlgosimMainForm.GalleryPanel then
begin
if AlgosimMainForm.GUIMode = guiConsole then
AlgosimMainForm.GUIMode := guiMixed;
TGallery.ShowControl(Ctl);
end
else
begin
var Frm := GetParentForm(Ctl);
if Assigned(Frm) then
Frm.Show;
end;
end;
end;
class function TVisualization.VectorField(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
VectorField: TVectorField absolute Result;
VFD: TVectorFieldDataR2;
begin
VFD := AVisual.Data as TVectorFieldDataR2;
VectorField := TVectorField.Create(ACtl, ACtl.View);
VectorField.BeginAddVector;
try
for var i := 0 to High(VFD.Vectors) do
VectorField.AddVector(VFD.Vectors[i].Key, VFD.Vectors[i].Value);
finally
VectorField.EndAddVector;
end;
end;
class function TVisualization.VectorField(ACtl: TVisCtl3D;
AVisual: TVisual): TDrawable3D;
var
VectorField: rgl.TVectorField absolute Result;
VFD: TVectorFieldDataR3;
begin
VFD := AVisual.Data as TVectorFieldDataR3;
VectorField := rgl.TVectorField.Create(ACtl);
VectorField.Data := VFD.Vectors;
VectorField.PerVertexColors := VFD.Colored;
end;
class procedure TVisualization.Visualize(AVisual: TVisual; ARef: PAlgosimReference);
begin
case AVisual.Target of
vt2D:
begin
var NewDiagram: Boolean;
var Ctl := GetDiagram(NewDiagram);
if Ctl = nil then
raise Exception.Create('No diagram control.');
Ctl.BeginBackgroundPaint;
try
var Obj := TDrawable(nil);
case AVisual.Kind of
vkNull: ;
vkBarChart:
Obj := BarChart(Ctl, AVisual);
vkPieChart:
Obj := PieChart(Ctl, AVisual);
vkHistogram:
Obj := Histogram(Ctl, AVisual);
vkXYPlot:
Obj := ScatterPlot(Ctl, AVisual);
vkRegion:
Obj := RegionPlot(Ctl, AVisual);
vkHeatmap:
Obj := Heatmap(Ctl, AVisual);
vkVectorField:
Obj := VectorField(Ctl, AVisual);
vkPixmap:
Obj := Pixmap(Ctl, AVisual);
vkText:
Obj := Text(Ctl, AVisual);
vkLine:
Obj := Line(Ctl, AVisual);
vkRectangle:
Obj := Rectangle(Ctl, AVisual);
vkCircle:
Obj := Circle(Ctl, AVisual);
vkPolygon:
Obj := Polygon(Ctl, AVisual);
vkArrow:
Obj := Arrow(Ctl, AVisual);
end;
if Assigned(Obj) then
begin
Ctl.AddObject(Obj);
if NewDiagram and Assigned(AVisual.ViewSetupProc2D) then
AVisual.ViewSetupProc2D(Ctl, Obj);
if Assigned(AVisual.OwnSetupProc2D) then
AVisual.OwnSetupProc2D(Ctl, Obj);
if Assigned(ARef) then
ARef^ := Obj.CreateReference;
end;
finally
Ctl.EndBackgroundPaint;
end;
end;
vt3D:
begin
var NewScene: Boolean;
var Ctl := GetScene(NewScene);
if Ctl = nil then
raise Exception.Create('No visualization control.');
Ctl.BeginBackgroundPaint;
try
var Obj := TDrawable3D(nil);
case AVisual.Kind of
vkNull: ;
vkXYZPlot, vkXYZcsPlot:
Obj := ScatterPlot(Ctl, AVisual);
vkSurface:
Obj := Surface(Ctl, AVisual);
vkSpaceCurve:
Obj := SpaceCurve(Ctl, AVisual);
vkObject3D:
Obj := Object3D(Ctl, AVisual);
vkVectorField:
Obj := VectorField(Ctl, AVisual);
vkPixmap:
Obj := Pixmap(Ctl, AVisual);
vkText:
Obj := Text(Ctl, AVisual);
vkArrow:
Obj := Arrow(Ctl, AVisual);
end;
if Assigned(Obj) then
begin
Ctl.AddObject(Obj);
if NewScene and Assigned(AVisual.ViewSetupProc3D) then
AVisual.ViewSetupProc3D(Ctl, Obj);
if Assigned(AVisual.OwnSetupProc3D) then
AVisual.OwnSetupProc3D(Ctl, Obj);
if Assigned(ARef) then
ARef^ := Obj.CreateReference;
end;
finally
Ctl.EndBackgroundPaint;
end;
end;
end;
end;
class function TVisualization.ShowDiagram(const ADiagram: string): Boolean;
var
Ctl: TManagedVisCtl2D;
begin
Result := TManagedVisCtl2D.Instances.TryGetValue(ADiagram, Ctl);
if Result then
ShowDiagram(Ctl);
end;
class function TVisualization.ShowDiagram(const ADiagram: TGUID): Boolean;
var
Drawable: TDrawable;
begin
Result := TDrawable.TryGetDrawableByGUID(ADiagram, Drawable);
if Result then
ShowDiagram(Drawable.Control);
end;
class function TVisualization.ShowScene(const AScene: string): Boolean;
var
Ctl: TManagedVisCtl3D;
begin
Result := TManagedVisCtl3D.Instances.TryGetValue(AScene, Ctl);
if Result then
ShowScene(Ctl);
end;
class function TVisualization.ShowScene(const AScene: TGUID): Boolean;
var
Drawable: TDrawable3D;
begin
Result := TDrawable3D.TryGetDrawableByGUID(AScene, Drawable);
if Result then
ShowScene(Drawable.Control);
end;
class function TVisualization.ShowVisCtl(const AID: TGUID): Boolean;
var
Drawable2D: TDrawable;
Drawable3D: TDrawable3D;
begin
Result := TDrawable.TryGetDrawableByGUID(AID, Drawable2D);
if Result then
begin
ShowDiagram(Drawable2D.Control);
Exit;
end;
Result := TDrawable3D.TryGetDrawableByGUID(AID, Drawable3D);
if Result then
begin
ShowScene(Drawable3D.Control);
Exit;
end;
end;
class function TVisualization.SpaceCurve(ACtl: TVisCtl3D;
AVisual: TVisual): TDrawable3D;
var
Curve: TCurve3D absolute Result;
CCurve: TColoredCurve3D absolute Result;
begin
if AVisual.Data is TCurveDataR3c then
begin
CCurve := TColoredCurve3D.Create(ACtl);
CCurve.Data := (AVisual.Data as TCurveDataR3c).Points;
end
else if AVisual.Data is TCurveDataR3 then
begin
Curve := TCurve3D.Create(ACtl);
Curve.Data := (AVisual.Data as TCurveDataR3).Points;
end
else
raise Exception.Create('Unknown space curve data class.');
end;
class function TVisualization.Object3D(ACtl: TVisCtl3D;
AVisual: TVisual): TDrawable3D;
begin
var D := AVisual.Data as TObject3DData;
Result := D.ObjectClass.Create(ACtl);
if not D.Name.IsEmpty then
Result.Name := D.Name;
if Result is TGeometricObject3D then
TGeometricObject3D(Result).Color := clRed;
if (Result is TObjModel) and not D.Data.IsEmpty then
TObjModel(Result).LoadModel(D.Data);
end;
class function TVisualization.Surface(ACtl: TVisCtl3D;
AVisual: TVisual): TDrawable3D;
var
Surf: TCustomSurface absolute Result;
CSurf: TCustomColoredSurface absolute Result;
begin
if AVisual.Data is TColoredSurfaceData then
begin
CSurf := TCustomColoredSurface.Create(ACtl);
CSurf.Data := (AVisual.Data as TColoredSurfaceData).Data;
CSurf.Domain := (AVisual.Data as TColoredSurfaceData).Domain;
CSurf.Nx := (AVisual.Data as TColoredSurfaceData).Nx;
CSurf.Ny := (AVisual.Data as TColoredSurfaceData).Ny;
end
else if AVisual.Data is TSurfaceData then
begin
Surf := TCustomSurface.Create(ACtl);
Surf.Data := (AVisual.Data as TSurfaceData).Data;
Surf.Domain := (AVisual.Data as TSurfaceData).Domain;
Surf.Nx := (AVisual.Data as TSurfaceData).Nx;
Surf.Ny := (AVisual.Data as TSurfaceData).Ny;
Surf.Color := clRed;
end
else
raise Exception.Create('Unknown surface data class.');
end;
class function TVisualization.Text(ACtl: TVisCtl3D;
AVisual: TVisual): TDrawable3D;
var
Text: TTextRect absolute Result;
begin
Text := TTextRect.Create(ACtl);
Text.Text := (AVisual.Data as TTextData).Text;
end;
class function TVisualization.Text(ACtl: TVisCtl2D;
AVisual: TVisual): TDrawable;
var
Text: TText absolute Result;
begin
Text := TText.Create(ACtl, ACtl.View);
Text.Position := (AVisual.Data as TTextData).Position;
Text.Text := (AVisual.Data as TTextData).Text;
end;
class constructor TManagedVisCtl2D.ClassCreate;
begin
FInstances := TDictionary<string, TManagedVisCtl2D>.Create;
end;
class destructor TManagedVisCtl2D.ClassDestroy;
begin
if Assigned(FInstances) then
for var Ctl in FInstances do
Ctl.Value.Free;
FreeAndNil(FInstances);
end;
procedure TManagedVisCtl2D.ContextPopup(Sender: TObject);
begin
FmnuDetach.Visible := Parent = AlgosimMainForm.GalleryPanel;
FmnuDock.Visible := Parent is TVisFrm;
end;
constructor TManagedVisCtl2D.Create(AOwner: TComponent);
begin
inherited;
FDiagramName := TVisualization.Diagram;
if Assigned(FInstances) then
FInstances.Add(TVisualization.Diagram, Self);
FmnuDetach := TMenuItem.Create(Self);
FmnuDetach.Caption := 'Detach';
FmnuDetach.Hint := 'Detaches this diagram from the main form.';
FmnuDetach.Tag := NativeInt(Self);
FmnuDetach.OnClick := Detach;
AddMenuItem(FmnuDetach);
FmnuDock := TMenuItem.Create(Self);
FmnuDock.Caption := 'Dock';
FmnuDock.Hint := 'Attaches this diagram in the main form.';
FmnuDock.Tag := NativeInt(Self);
FmnuDock.OnClick := Dock;
AddMenuItem(FmnuDock);
OnBeforeContextPopup := ContextPopup;
end;
destructor TManagedVisCtl2D.Destroy;
begin
if Assigned(FInstances) then
FInstances.Remove(FDiagramName);
inherited;
end;
procedure TManagedVisCtl2D.Detach(Sender: TObject);
begin
if Parent = AlgosimMainForm.GalleryPanel then
begin
var frm := TVisFrm.CreateNew(AlgosimMainForm);
frm.Caption := FDiagramName;
Parent := frm;
Align := alClient;
frm.Show;
end;
end;
procedure TManagedVisCtl2D.Dock(Sender: TObject);
begin
if Parent is TVisFrm then
begin
var frm := TVisFrm(Parent);
Parent := AlgosimMainForm.GalleryPanel;
Align := alClient;
TGallery.ShowControl(Self);
frm.Free;
end;
end;
procedure TManagedVisCtl2D.SetDetached(ADetached: Boolean);
begin
if ADetached then
Detach(Self)
else
Dock(Self);
end;
procedure TManagedVisCtl2D.SetWindowHeight(AHeight: Integer);
begin
Detach(Self);
if Parent is TVisFrm then
TVisFrm(Parent).ClientHeight := AHeight;
end;
procedure TManagedVisCtl2D.SetWindowWidth(AWidth: Integer);
begin
Detach(Self);
if Parent is TVisFrm then
TVisFrm(Parent).ClientWidth := AWidth;
end;
class constructor TManagedVisCtl3D.ClassCreate;
begin
FInstances := TDictionary<string, TManagedVisCtl3D>.Create;
end;
class destructor TManagedVisCtl3D.ClassDestroy;
begin
if Assigned(FInstances) then
for var Ctl in FInstances do
Ctl.Value.Free;
FreeAndNil(FInstances);
end;
procedure TManagedVisCtl3D.ContextPopup(Sender: TObject);
begin
FmnuDetach.Visible := Parent = AlgosimMainForm.GalleryPanel;
FmnuDock.Visible := Parent is TVisFrm;
end;
constructor TManagedVisCtl3D.Create(AOwner: TComponent);
begin
inherited;
FSceneName := TVisualization.Scene;
if Assigned(FInstances) then
FInstances.Add(TVisualization.Scene, Self);
FmnuDetach := TMenuItem.Create(Self);
FmnuDetach.Caption := 'Detach';
FmnuDetach.Hint := 'Detaches this scene from the main form.';
FmnuDetach.Tag := NativeInt(Self);
FmnuDetach.OnClick := Detach;
AddMenuItem(FmnuDetach);
FmnuDock := TMenuItem.Create(Self);
FmnuDock.Caption := 'Dock';
FmnuDock.Hint := 'Attaches this scene in the main form.';
FmnuDock.Tag := NativeInt(Self);
FmnuDock.OnClick := Dock;
AddMenuItem(FmnuDock);
OnBeforeContextPopup := ContextPopup;
end;
destructor TManagedVisCtl3D.Destroy;
begin
if Assigned(FInstances) then
FInstances.Remove(FSceneName);
inherited;
end;
procedure TManagedVisCtl3D.Detach(Sender: TObject);
begin
if Parent = AlgosimMainForm.GalleryPanel then
begin
var frm := TVisFrm.CreateNew(AlgosimMainForm);
frm.Caption := FSceneName;
Parent := frm;
Align := alClient;
frm.Show;
end;
end;
procedure TManagedVisCtl3D.Dock(Sender: TObject);
begin
if Parent is TVisFrm then
begin
var frm := TVisFrm(Parent);
Parent := AlgosimMainForm.GalleryPanel;
Align := alClient;
TGallery.ShowControl(Self);
frm.Free;
end;
end;
procedure TManagedVisCtl3D.SetDetached(ADetached: Boolean);
begin
if ADetached then
Detach(Self)
else
Dock(Self);
end;
procedure TManagedVisCtl3D.SetWindowHeight(AHeight: Integer);
begin
Detach(Self);
if Parent is TVisFrm then
TVisFrm(Parent).ClientHeight := AHeight;
end;
procedure TManagedVisCtl3D.SetWindowWidth(AWidth: Integer);
begin
Detach(Self);
if Parent is TVisFrm then
TVisFrm(Parent).ClientWidth := AWidth;
end;
function TVisFrm.CanResize(var NewWidth, NewHeight: Integer): Boolean;
begin
Result := True;
if FAspect <> 0.0 then
NewWidth := Max(64, Round(NewHeight * FAspect));
end;
procedure TVisFrm.CMTextChanged(var Message: TMessage);
begin
inherited;
TWndMgr.CaptionChanged(Self);
end;
constructor TVisFrm.CreateNew(AOwner: TComponent; Dummy: Integer);
var
WA, FA: TRect;
PrefW, PrefH, MinSize: Integer;
type
TSide = (sNone, sTop, sRight, sBottom, sLeft);
procedure Init;
begin
var MF := AlgosimMainForm;
FA := MF.BoundsRect;
var MON := MF.Monitor;
if Assigned(MON) then
WA := MON.WorkareaRect
else
WA := Screen.WorkAreaRect;
PrefW := _Scale(800);
PrefH := _Scale(600);
MinSize := _Scale(400);
DefaultMonitor := dmDesktop;
Position := poDesigned;
end;
procedure PlaceToTheRight;
begin
Width := Math.Min(PrefW, WA.Right - FA.Right);
Left := FA.Right;
Height := Math.Min(PrefH, WA.Height);
Top := WA.Top + Random(WA.Height - Height);
end;
procedure PlaceToTheLeft;
begin
Width := Math.Min(PrefW, FA.Left - WA.Left);
Left := FA.Left - Width;
Height := Math.Min(PrefH, WA.Height);
Top := WA.Top + Random(WA.Height - Height);
end;
procedure PlaceAbove;
begin
Height := Math.Min(PrefH, FA.Top - WA.Top);
Top := FA.Top - Height;
Width := Math.Min(PrefW, WA.Width);
Left := WA.Left + Random(WA.Width - Width);
end;
procedure PlaceBelow;
begin
Height := Math.Min(PrefH, WA.Bottom - FA.Bottom);
Top := FA.Bottom;
Width := Math.Min(PrefW, WA.Width);
Left := WA.Left + Random(WA.Width - Width);
end;
function BestSide: TSide;
var
SpaceTop, SpaceRight, SpaceBottom, SpaceLeft: Integer;
Spaces: TArray<Integer>;
MaxSpace: Integer;
begin
SpaceTop := FA.Top - WA.Top;
SpaceRight := WA.Right - FA.Right;
SpaceBottom := WA.Bottom - FA.Bottom;
SpaceLeft := FA.Left - WA.Left;
Spaces := [SpaceTop, SpaceRight, SpaceBottom, SpaceLeft];
MaxSpace := MaxIntValue(Spaces);
if MaxSpace < MinSize then
Exit(sNone)
else
Result := TSide(IndexInt(MaxSpace, Spaces) + 1);
end;
function PlaceNextToForm: Boolean;
begin
Result := True;
case BestSide of
sTop:
PlaceAbove;
sRight:
PlaceToTheRight;
sBottom:
PlaceBelow;
sLeft:
PlaceToTheLeft;
else
Result := False;
end;
end;
function PlaceOnSecondScreen: Boolean;
begin
Exit(False);
end;
function PlaceOnRightSideOfMainForm: Boolean;
begin
Width := Math.Min(PrefW, WA.Width);
Height := Math.Min(PrefH, WA.Height);
Left := WA.Right - Width;
Top := WA.Top + Random(WA.Height - Height);
Result := True;
end;
begin
inherited;
Color := clWhite;
Init;
if PlaceNextToForm or PlaceOnSecondScreen or PlaceOnRightSideOfMainForm then
MakeFullyVisible;
TFormFader.Create(Self);
TWindowWatcher.Create(Self);
var SysMenu := GetSystemMenu(Handle, False);
if SysMenu = 0 then Exit;
AppendMenu(SysMenu, MF_SEPARATOR, 0, nil);
AppendMenu(SysMenu, MF_STRING, CMD_SETSIZE, 'Set size...');
AppendMenu(SysMenu, MF_SEPARATOR, 0, nil);
AppendMenu(SysMenu, MF_STRING, CMD_DISCARD, 'Close and discard'#9'Shift+Alt+F4');
KeyPreview := True;
end;
procedure TVisFrm.DoClose(var Action: TCloseAction);
begin
inherited;
if IsKeyDown(VK_SHIFT) then
Action := caFree;
end;
procedure TVisFrm.KeyPress(var Key: Char);
begin
inherited;
case Key of
's':
begin
Perform(WM_SYSCOMMAND, CMD_SETSIZE, 0);
Key := #0;
end;
end;
end;
procedure TVisFrm.WMSysCommand(var Msg: TWMSysCommand);
begin
inherited;
case Msg.CmdType of
CMD_SETSIZE:
begin
var W := ClientWidth;
var H := ClientHeight;
var Aspect: Double := FAspect;
if ImageSizeDialog(Self, W, H, Aspect) then
begin
FAspect := 0.0;
if WindowState <> TWindowState.wsNormal then
WindowState := wsNormal;
ClientWidth := W;
ClientHeight := H;
FAspect := Aspect;
end;
end;
CMD_DISCARD:
Release;
end;
end;
end.