unit Unit1;

//{$DEFINE SG_DEBUG}

{$IFDEF SG_DEBUG}
  {$DEFINE MEMCHK}
{$ENDIF}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, DXFImage, sgConsts,
  {$IFNDEF sgDXFONLY}
  DWG, HPGL2, CGM, SVG,
  {$ENDIF}
  DXFExport, CADtoDXF,
  {$IFDEF MEMCHK}
  MemCheck,
  {$ENDIF}
  ExtDlgs, DXFConv, ComCtrls, Menus, Buttons, ToolWin, Printers,
  sgDrawingNavigator;

const
  sNotVectorial: string = 'Raster drawings can not be saved to DXF format';

type
  TfmMain = class(TForm)
    OpenPictureDialog1: TOpenPictureDialog;
    SavePictureDialog: TSavePictureDialog;
    Panel1: TPanel;
    sbrCoords: TStatusBar;
    mmMemu: TMainMenu;
    mFile: TMenuItem;
    nOpen: TMenuItem;
    mSave: TMenuItem;
    N1: TMenuItem;
    mExit: TMenuItem;
    mView: TMenuItem;
    mZoomIn: TMenuItem;
    mZoomOut: TMenuItem;
    mFunctions: TMenuItem;
    mAddEntity: TMenuItem;
    mDeleteLastAdded: TMenuItem;
    mAssigneEntity: TMenuItem;
    mAssingTsgDXFLine: TMenuItem;
    mAssignTsgDXFSpline: TMenuItem;
    N2: TMenuItem;
    mAssignAll: TMenuItem;
    mAssignTsgDXFText: TMenuItem;
    mAddBitMap: TMenuItem;
    mPrint: TMenuItem;
    PrintDialog1: TPrintDialog;
    procedure btnOpenClick(Sender: TObject);
    procedure btnZoomInClick(Sender: TObject);
    procedure btnZoomOutClick(Sender: TObject);
    procedure btnAddClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
    procedure sgPaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure btnSaveToDXFClick(Sender: TObject);
    procedure mExitClick(Sender: TObject);
    procedure mAssignAllClick(Sender: TObject);
    procedure mAssingTsgDXFLineClick(Sender: TObject);
    procedure mAssignTsgDXFSplineClick(Sender: TObject);
    procedure mAssignTsgDXFTextClick(Sender: TObject);
    procedure mAddBitMapClick(Sender: TObject);
    procedure mPrintClick(Sender: TObject);
  private
    FLastAdded: TsgDXFEntity;
    FsgPaintBox: TsgDrawingNavigator;
  public
    property sgPaintBox: TsgDrawingNavigator read FsgPaintBox;
  end;

var
  fmMain: TfmMain;
  FSaveToFile: string = '';

implementation

uses
  fEntity, SyncObjs;

{$R *.DFM}

procedure AddEntity(AConverter: TsgDXFConverter; AEnt: TsgDXFEntity);
begin
  AConverter.Sections[csEntities].AddEntity(AEnt);
  if Assigned(AConverter.OnCreate) then
    AConverter.OnCreate(AEnt);
  AConverter.Loads(AEnt);
end;

function DialogBySetupParamsBitMap(var AStr: string; var APoint, AScale: TFPoint): Boolean;
var
  vForm: TForm;
  vBitBtn: TBitBtn;
  vButton: TButton;
  vEdit: TEdit;
  vStatTexts: array [0..4] of TStaticText;
  vEditTexts: array [0..4] of TEdit;
  vOD: TOpenDialog;
  vGBDir: TGroupBox;
  vGBPos: TGroupBox;
  vGBScale: TGroupBox;
  I: Integer;

  procedure OnButtonClick(Self: Pointer; Sender: TObject);
  var
    OD: TOpenDialog;
    Edit: TEdit;
  begin
    OD := Pointer(TButton(Sender).Tag);
    if OD.Execute then
    begin
      Edit := Pointer(OD.Tag);
      Edit.Text := OD.FileName;
      TBitBtn(Pointer(Edit.Tag)).Enabled := FileExists(Edit.Text);
    end;
  end;

  procedure SetSize(AControl: TWinControl; const ALeft, ATop, AWidth, AHeight: Integer);
  begin
    AControl.Left := ALeft;
    AControl.Top := ATop;
    AControl.Width := AWidth;
    AControl.Height := AHeight;
  end;

begin
  vForm := TForm.Create(nil);
  vForm.BorderStyle := bsToolWindow;
  vForm.Caption := 'Initialize parametrs of BitMap';

  vGBDir := TGroupBox.Create(vForm);
  vGBDir.Parent := vForm;
  SetSize(vGBDir, 8, 8, 352, 48);
  vGBDir.Caption := ' Path ';

  vEdit := TEdit.Create(vGBDir);
  vEdit.Parent := vGBDir;
  vEdit.AutoSize := False;
  SetSize(vEdit, 8, 16, 252, 24);
  vEdit.ReadOnly := True;
  vEdit.TabStop := False;

  vButton := TButton.Create(vGBDir);
  vButton.Parent := vGBDir;
  SetSize(vButton, 272, 16, 72, 23);
  vButton.Caption := 'Open';
  @vButton.OnClick := @OnButtonClick;

  vGBPos := TGroupBox.Create(vForm);
  vGBPos.Parent := vForm;
  SetSize(vGBPos, 8, 62, 352, 104);
  vGBPos.Caption := ' Position ';

  vStatTexts[0] := TStaticText.Create(vGBPos);
  vStatTexts[0].Parent := vGBPos;
  SetSize(vStatTexts[0], 8, 16, 16, 24);
  vStatTexts[0].Caption := 'X:';
  vEditTexts[0] := TEdit.Create(vGBPos);
  vEditTexts[0].Parent := vGBPos;
  SetSize(vEditTexts[0], 24, 16, 320, 24);
  vEditTexts[0].AutoSize := False;
  vEditTexts[0].Text := '0.0';

  vStatTexts[1] := TStaticText.Create(vGBPos);
  vStatTexts[1].Parent := vGBPos;
  SetSize(vStatTexts[1], 8, 44, 16, 24);
  vStatTexts[1].Caption := 'Y:';
  vEditTexts[1] := TEdit.Create(vGBPos);
  vEditTexts[1].Parent := vGBPos;
  SetSize(vEditTexts[1], 24, 44, 320, 24);
  vEditTexts[1].AutoSize := False;
  vEditTexts[1].Text := '0.0';

  vStatTexts[2] := TStaticText.Create(vGBPos);
  vStatTexts[2].Parent := vGBPos;
  SetSize(vStatTexts[2], 8, 72, 16, 24);
  vStatTexts[2].Caption := 'Z:';
  vEditTexts[2] := TEdit.Create(vGBPos);
  vEditTexts[2].Parent := vGBPos;
  SetSize(vEditTexts[2], 24, 72, 320, 24);
  vEditTexts[2].AutoSize := False;
  vEditTexts[2].Text := '0.0';

  vGBScale := TGroupBox.Create(vForm);
  vGBScale.Parent := vForm;
  SetSize(vGBScale, 8, 168, 352, 76);
  vGBScale.Caption := ' Scale ';

  vStatTexts[3] := TStaticText.Create(vGBScale);
  vStatTexts[3].Parent := vGBScale;
  SetSize(vStatTexts[3], 8, 16, 16, 24);
  vStatTexts[3].Caption := 'X:';
  vEditTexts[3] := TEdit.Create(vGBScale);
  vEditTexts[3].Parent := vGBScale;
  SetSize(vEditTexts[3], 24, 16, 320, 24);
  vEditTexts[3].AutoSize := False;
  vEditTexts[3].Text := '1.0';

  vStatTexts[4] := TStaticText.Create(vGBScale);
  vStatTexts[4].Parent := vGBScale;
  SetSize(vStatTexts[4], 8, 44, 16, 24);
  vStatTexts[4].Caption := 'Y:';
  vEditTexts[4] := TEdit.Create(vGBScale);
  vEditTexts[4].Parent := vGBScale;
  SetSize(vEditTexts[4], 24, 44, 320, 24);
  vEditTexts[4].AutoSize := False;
  vEditTexts[4].Text := '1.0';

  vBitBtn := TBitBtn.Create(vForm);
  vBitBtn.Parent := vForm;
  SetSize(vBitBtn, vGBScale.BoundsRect.Right - 72 - 8,
    vGBScale.BoundsRect.Bottom + 8, 72, 23);
  vBitBtn.Kind := bkOK;
  vBitBtn.Enabled := False;

  vOD := TOpenDialog.Create(vForm);
  vOD.Filter := 'BitMap | *.bmp';

  vButton.Tag := Integer(Pointer(vOD));
  vOD.Tag := Integer(Pointer(vEdit));
  vEdit.Tag := Integer(Pointer(vBitBtn));

  vForm.ClientWidth := vGBScale.BoundsRect.Right + 8;
  vForm.ClientHeight := vBitBtn.BoundsRect.Bottom + 8;
  if Application.MainForm <> nil then
  begin
    vForm.Left := Application.MainForm.Left + Application.MainForm.Width div 2 -
      vForm.Width div 2;
    vForm.Top := Application.MainForm.Top + Application.MainForm.Height div 2 -
      vForm.Height div 2;
  end;
  try
    Result := vForm.ShowModal = mrOk;
    if Result then
    begin
      AStr := vEdit.Text;
      APoint.X := ConvToFloatDef(vEditTexts[0].Text, 0.0);
      APoint.Y := ConvToFloatDef(vEditTexts[1].Text, 0.0);
      APoint.Z := ConvToFloatDef(vEditTexts[2].Text, 0.0);
      AScale.X := ConvToFloatDef(vEditTexts[3].Text, 1.0);
      AScale.Y := ConvToFloatDef(vEditTexts[4].Text, 1.0);
      AScale.Z := 1;
    end;
  finally
    vEdit.Free;
    vBitBtn.Free;
    vButton.Free;
    for I := 0 to 4 do
    begin
      vStatTexts[I].Free;
      vEditTexts[I].Free;
    end;
    vGBDir.Free;
    vGBPos.Free;
    vGBScale.Free;
    vOD.Free;
    vForm.Free;
  end;
end;

procedure TfmMain.btnOpenClick(Sender: TObject);
begin
  if OpenPictureDialog1.Execute then
  begin
    sbrCoords.Panels.Items[0].Text := 'Please wait...';
    sgPaintBox.LoadFromFile(OpenPictureDialog1.FileName);
    mSave.Enabled := True;
    mView.Enabled := True;
    mPrint.Enabled := True;
    mFunctions.Enabled := True;
  end;
  sbrCoords.Panels.Items[0].Text := OpenPictureDialog1.FileName;
end;

procedure TfmMain.btnZoomInClick(Sender: TObject);
begin
  sgPaintBox.AlterScale(2, False, sgPaintBox.Center);
end;

procedure TfmMain.btnZoomOutClick(Sender: TObject);
begin
  sgPaintBox.AlterScale(0.5, False, sgPaintBox.Center);
end;

procedure TfmMain.btnAddClick(Sender: TObject);
var
  Ent: TsgDXFEntity;
  Im: TsgDXFImage;
begin
  if not (sgPaintBox.Picture.Graphic is TsgDXFImage) then
    Exit;
  Im := TsgDXFImage(sgPaintBox.Picture.Graphic);
  Ent := GetEntity;
  if Ent <> nil then
  begin
    AddEntity(Im.Converter, Ent);
    Im.GetExtents;
    sgPaintBox.Refresh;
    FLastAdded := Ent;
  end;
end;

procedure TfmMain.FormCreate(Sender: TObject);
begin
  FsgPaintBox := TSGDrawingNavigator.Create(Self);
  FsgPaintBox.Parent := Self;
  FsgPaintBox.AutoFocus := True;
  FsgPaintBox.RectZooming := True;
  FsgPaintBox.OnMouseMove := sgPaintBoxMouseMove;
  FsgPaintBox.Align := alClient;
  FLastAdded := nil;
end;

procedure TfmMain.btnDeleteClick(Sender: TObject);
var
  Im: TsgDXFImage;
begin
  if not (sgPaintBox.Picture.Graphic is TsgDXFImage) then
    Exit;
  Im := TsgDXFImage(sgPaintBox.Picture.Graphic);
  if FLastAdded <> nil then
  begin
    Im.Converter.DeleteEntity(FLastAdded, True);
    Im.GetExtents;
    FLastAdded := nil;
    sgPaintBox.Refresh;
  end;
end;

procedure TfmMain.sgPaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  vPt: TFPoint;
  vUnits: string;
begin
  if sgPaintBox.Picture.Graphic is TsgDXFImage then
  begin
    vPt := sgPaintBox.GetDrawingCoords(X, Y, vUnits);
    sbrCoords.Panels[1].Text := Format('%.3f; %.3f; %.3f (%s)', [vPt.X, vPt.Y, vPt.Z, vUnits]);
  end;
end;

procedure TfmMain.btnSaveToDXFClick(Sender: TObject);
var
  vExpMetafile: TsgDXFExport;
  vExpCADfile: TsgCADtoDXF;
  vGr: TGraphic;
  vFileName: string;
begin
  if not SavePictureDialog.Execute then Exit;
  if AnsiLowerCase(ExtractFileExt(SavePictureDialog.FileName)) <> '.dxf' then
     SavePictureDialog.FileName := SavePictureDialog.FileName + '.dxf';
  vFileName := SavePictureDialog.FileName;
  vGr := sgPaintBox.Picture.Graphic;
  if (vGr is TsgDXFImage) or (vGr is TMetafile) then
  begin
    if not (vGr is TsgDXFImage) then
    begin
      vExpMetafile := TsgDXFExport.Create;
      try
        vExpMetafile.Canvas.StretchDraw(Rect(0, 0, vGr.Width, vGr.Height), vGr);
        vExpMetafile.EndDraw;
        vExpMetafile.SaveToFile(vFileName);
      finally
        vExpMetafile.Free;
      end;
    end
    else
    begin
      vExpCADfile := TsgCADtoDXF.Create(TsgDXFImage(vGr));
      try
        vExpCADfile.SaveToFile(vFileName);
      finally
        vExpCADfile.Free;
      end;
    end;
    MessageBox(Application.Handle, PChar('DXF Export is made via DXF Export VCL software which is not part of CAD Import VCL and should be bought additionally if needed.'), 'WARNING', MB_ICONWARNING);
  end
  else
    ShowMessage(sNotVectorial);
end;

procedure TfmMain.mExitClick(Sender: TObject);
begin
  Close;
end;

procedure TfmMain.mAssignAllClick(Sender: TObject);
type
  TClassOfEntity = class of TsgDXFEntity;
var
  vImg: TsgDXFImage;
  vEnt: TsgDXFEntity;
  vNewEnt: TsgDXFEntity;
  I, vCnt: Integer;
begin
  vImg := TsgDXFImage(sgPaintBox.Picture.Graphic);
  if vImg = nil then Exit;
  vCnt :=  vImg.Converter.Counts[csEntities];
  I := 0;
  try
    while I < vCnt do
    begin
      vEnt := vImg.Converter.Sections[csEntities].Entities[I];
      vNewEnt := TClassOfEntity(vEnt.ClassType).Create;
      vNewEnt.AssignEntity(vEnt);
      vImg.Converter.DeleteEntity(vEnt, True);
      Dec(vCnt);
      vNewEnt.SetColor(clRed);
      AddEntity(vImg.Converter, vNewEnt);
    end;
    sgPaintBox.Refresh;
  except
    ShowMessage('This file contains entities for which AssignEntity will be implemented in future verions of CADImportVCL');
    sgPaintBox.Refresh;
  end;
end;

procedure TfmMain.mAssingTsgDXFLineClick(Sender: TObject);
var
  vImg: TsgDXFImage;
  vEnt: TsgDXFEntity;
  vNewEnt: TsgDXFLine;
  I, vCnt: Integer;
begin
  vImg := TsgDXFImage(sgPaintBox.Picture.Graphic);
  if vImg = nil then Exit;
  vCnt :=  vImg.Converter.Counts[csEntities];
  I := 0;
  while I < vCnt do
  begin
    vEnt := vImg.Converter.Sections[csEntities].Entities[I];
    if vEnt.ClassType = TsgDXFLine then
    begin
      vNewEnt := TsgDXFLine.Create;
      vNewEnt.AssignEntity(vEnt);
      vImg.Converter.DeleteEntity(vEnt, True);
      Dec(vCnt);
      vNewEnt.SetColor(clRed);
      AddEntity(vImg.Converter, vNewEnt);
    end
    else
      Inc(I);
  end;
  sgPaintBox.Refresh;
end;

procedure TfmMain.mAssignTsgDXFSplineClick(Sender: TObject);
var
  vImg: TsgDXFImage;
  vEnt: TsgDXFEntity;
  vNewEnt: TsgDXFSpline;
  I, vCnt: Integer;
begin
  vImg := TsgDXFImage(sgPaintBox.Picture.Graphic);
  if vImg = nil then Exit;
  vCnt :=  vImg.Converter.Counts[csEntities];
  I := 0;
  while I < vCnt do
  begin
    vEnt := vImg.Converter.Sections[csEntities].Entities[I];
    if vEnt.ClassType = TsgDXFSpline then
    begin
      vNewEnt := TsgDXFSpline.Create;
      vNewEnt.AssignEntity(vEnt);
      vImg.Converter.DeleteEntity(vEnt, True);
      Dec(vCnt);
      vNewEnt.SetColor(clRed);
      AddEntity(vImg.Converter, vNewEnt);
    end
    else
      Inc(I);
  end;
  sgPaintBox.Refresh;
end;

procedure TfmMain.mAssignTsgDXFTextClick(Sender: TObject);
var
  vImg: TsgDXFImage;
  vEnt: TsgDXFEntity;
  vNewEnt: TsgDXFText;
  I, vCnt: Integer;
begin
  vImg := TsgDXFImage(sgPaintBox.Picture.Graphic);
  if vImg = nil then Exit;
  vCnt :=  vImg.Converter.Counts[csEntities];
  I := 0;
  while I < vCnt do
  begin
    vEnt := vImg.Converter.Sections[csEntities].Entities[I];
    if vEnt.ClassType = TsgDXFText then
    begin
      vNewEnt := TsgDXFText.Create;
      vNewEnt.AssignEntity(vEnt);
      vImg.Converter.DeleteEntity(vEnt, True);
      Dec(vCnt);
      vNewEnt.SetColor(clRed);
      AddEntity(vImg.Converter, vNewEnt);
    end
    else
      Inc(I);
  end;
  sgPaintBox.Refresh;
end;

procedure TfmMain.mAddBitMapClick(Sender: TObject);
var
  Ent: TsgDXFEntity;
  Im: TsgDXFImage;

  function GetBitMap: TsgDXFEntity;
  var
    vStr: string;
    vPoint, vScale: TFPoint;
    vBitMap: TBitmap;
    vImageEnt: TsgDXFImageEnt absolute Result;
  begin
    Result := nil;
    if DialogBySetupParamsBitMap(vStr, vPoint, vScale) then
    begin
      vBitMap := TBitmap.Create;
      try
        vBitMap.LoadFromFile(vStr);
      except
        vBitMap.Free;
        Exit;
      end;
      vImageEnt := TsgDXFImageEnt.Create;
      vImageEnt.Point := vPoint;
      vImageEnt.Point1 := MakeFPoint(vBitMap.Width, 0, 0);
      vImageEnt.Point2 := MakeFPoint(0, vBitMap.Height, 0);
      vImageEnt.Point3 := vScale;
      vImageEnt.SetImage(vBitMap);
      vBitMap.Free;
    end;
  end;

begin
  if not (sgPaintBox.Picture.Graphic is TsgDXFImage) then
    Exit;
  Im := TsgDXFImage(sgPaintBox.Picture.Graphic);
  Ent := GetBitMap;
  if Ent <> nil then
  begin
    AddEntity(Im.Converter, Ent);
    FLastAdded := Ent;
    Im.GetExtents;
    Im.RefreshCurrentLayout;
  end;
end;

procedure TfmMain.mPrintClick(Sender: TObject);
const PO: array[Boolean] of TPrinterOrientation = (poPortrait,poLandscape);
var
  W,H: Double;
  PW,PH: Integer;
  vStoredNullWidth: Integer;

  procedure SetNullWidth(AGraphic: TGraphic; AWidth: Integer);
  begin
    if AGraphic is TsgDXFImage then
    begin
      vStoredNullWidth := TsgDXFImage(AGraphic).NullWidth;
      TsgDXFImage(AGraphic).NullWidth := AWidth;
    end;
  end;

  procedure RestoreNullWidth(AGraphic: TGraphic);
  begin
    if AGraphic is TsgDXFImage then
      TsgDXFImage(AGraphic).NullWidth := vStoredNullWidth;
  end;

begin
  with sgPaintBox.Picture do
    Printer.Orientation := PO[Width > Height];
  if PrintDialog1.Execute then
  begin
    SetNullWidth(sgPaintBox.Picture.Graphic, 3);
    with Printer, sgPaintBox.Picture do
    begin
      W := PageWidth / Width;
      H := PageHeight / Height;
      if W > H then W := H;
      PW := Round(W * Width);
      PH := Round(W * Height);
      BeginDoc;
      Canvas.StretchDraw(Bounds((PageWidth-PW) div 2, (PageHeight-PH) div 2, PW, PH), Graphic);
      EndDoc;
    end;
    RestoreNullWidth(sgPaintBox.Picture.Graphic);
    sgPaintBox.Repaint;
  end;
end;

initialization
  {$IFDEF MEMCHK}
  MemChk
  {$ENDIF}

end.
