unit LoadDXF;

Interface

uses SysUtils, Classes, Windows, Graphics, ComCtrls, Variants, PCTypesUtils,DrawObjects,Menus, DlgBase,  ExtDlgs,
  U_Common_Classes, PCLayerDlg, OleCtnrs, Buttons, PCgui, GuiStrings, DrawEngine, PowerCad;

type
  TSections = (scHeader, scTables, scBlocks, scEntities, scClasses, scUnknow);

  TArcDirection = (adClockwise, adCounterClockwise);

  TSourceBlockName = array[0..12] of Char;

  TRealType = Single;

  TVector2D = record
   X, Y: TRealType;
  end;

  TTransf2D = array[1..3, 1..3] of TRealType;

  TPoint2D = record
   X, Y, W: TRealType;
  end;

  TRect2D = record
   case Byte of
    0: (Left, Bottom, W1, Right, Top, W2: TRealType);
    1: (FirstEdge, SecondEdge: TPoint2D);
  end;

  TGroupTable = array[0..512] of Variant;

  TDXFRead = class(TMyObject)
  private
    fStream: TextFile;
    fCurrentSection: TSections;
    fGroupCode: Word;
    fGroupValue: Variant;
    fProgressBar: TProgressBar;

    procedure SetProgressBar(PB: TProgressBar);
  public
    constructor Create(FileName: String);
    destructor Destroy; override;
    procedure Rewind;
    { Return False if EOF. }
    function ConsumeGroup: Boolean;
    procedure NextSection;
    function ReadAnEntry(GroupDel: Word; var Values: TGroupTable): Word;

    property GroupCode: Word read FGroupCode;
    property GroupValue: Variant read FGroupValue;
    property CurrentSection: TSections read FCurrentSection;
    property PositionBar: TProgressBar read fProgressBar write SetProgressBar;
  end;

(*
  TDXFWrite = class(TMyObject)
  private
    FStream: TextFile;
  public
    constructor Create(FileName: String);
    destructor Destroy; override;

    procedure Reset;
    procedure BeginSection(Sect: TSections);
    procedure EndSection(Sect: TSections);
    procedure WriteGroup(GroupCode: Word; GroupValue: Variant);
    procedure WriteAnEntry(var Values: TGroupTable);
  end;
*)

  EDXFException = Exception;
  EDXFEndOfFile = EAbort;
  EDXFInvalidDXF = EDXFException;

  TDXF2DImport = class(TMyObject)
  private
    fTextFont: TFont;
    FCADCmp2D: TPowerCAD;
    fDXFRead: TDXFRead;
    fScale: TRealType;
    fHasExtension, fUnableToReadAll, fVerbose: Boolean;
    fSetLayers: Boolean; // Se True i layers letti modificano quelli del CAD in base all'ordine di recupero.
    fAngleDir: TArcDirection;
    fExtension: TRect2D;
    FLayNb: integer;
    fLayerList: TStringList; { Contain the name of the layers and the layer itself. }
    fBlockList: TStringList; { Contain the name of the blocks and the block itself. }

(*    procedure ReadEntitiesAsContainer(const Container: TContainer2D);   *)
    { Read the DXF blocks. }
    procedure ReadBlocks;
    { Read the DXF entities. }
    procedure ReadEntities;
  protected
    // Leggono le entità
    function ReadLine2D(Entry: TGroupTable): TFigure;
    function ReadTrace2D(Entry: TGroupTable): TFigure;
    function ReadSolid2D(Entry: TGroupTable): TFigure;
    function ReadArc2D(Entry: TGroupTable): TFigure;
    function ReadCircle2D(Entry: TGroupTable): TFigure;
    function ReadEllipse2D(Entry: TGroupTable): TFigure;
    function ReadPolyline2D(Entry: TGroupTable): TFigure;
    function ReadText2D(Entry: TGroupTable): TFigure;
    function ReadSourceBlock(Entry: TGroupTable): TFigure;
    function ReadBlock(Entry: TGroupTable): TFigure;
    function ReadEntity(IgnoreBlock: Boolean): TFigure;

    function GoToSection(Sect: TSections): Boolean;

    property DXFRead: TDXFRead read FDXFRead;
  public
    constructor Create(const FileName: String; const aCAD: TPowerCad);
    destructor Destroy; override;

//    procedure SetTextFont(F: TVectFont);

    { Read the DXF header. }
    procedure ReadHeader;
    { Read the DXF tables. }
    procedure ReadTables;

    { Read the DXF informations. }
    procedure ReadDXF;
    { Read the DXF informations as a block. }
//    function ReadDXFAsSourceBlock(const Name: TSourceBlockName): Boolean;
    { Read the DXF informations as a container. }
//    function ReadDXFAsContainer: TContainer2D;
    procedure SetTextFont(F: TFont);

    property HasExtension: Boolean read fHasExtension;
    property Extension: TRect2D read FExtension;
    property BlockList: TStringList read FBlockList;
    property LayerList: TStringList read FLayerList;
    property Scale: TRealType read fScale write fScale;
    property SetLayers: Boolean read fSetLayers write fSetLayers;
    property UnableToReadAllTheFile: Boolean read fUnableToReadAll;
    property Verbose: Boolean read fVerbose write fVerbose;
  end;

(*
  TDXF2DExport = class(TMyObject)
  private
    FDXFWrite: TDXFWrite;
    FCADCmp2D: TCADCmp2D;
  protected
    procedure WriteLine2D(Line: TLine2D);
    procedure WriteFrame2D(Frm: TFrame2D);
    procedure WriteCurve2D(Curve: TCurve2D);
    procedure WriteOutline2D(Poly: TOutline2D);
    procedure WriteText2D(Text: TJustifiedVectText2D);
    procedure WriteBlock(Block: TBlock2D);
    procedure WriteEntity(Obj: TObject2D);
  public
    constructor Create(const FileName: String; const CAD: TCADCmp2D);
    destructor Destroy; override;

    { Write the DXF headers. }
    procedure WriteHeader;
    { Write the DXF tables. }
    procedure WriteTables;
    { Write the DXF blocks. }
    procedure WriteBlocks;
    { Write the DXF entities. }
    procedure WriteEntities;
    { Write the DXF informations. }
    procedure WriteDXF;
  end;
*)

function Point2D(const X, Y: TRealType): TPoint2D;
function PointDistance2D(const P1, P2: TPoint2D): TRealType;
function VectorLength2D(const V: TVector2D): TRealType;
function Vector2D(PFrom, PTo: TPoint2D): TVector2D;
function CartesianPoint2D(const P: TPoint2D): TPoint2D;
function ReorderRect2D(const R: TRect2D): TRect2D;
function CartesianRect2D(const R: TRect2D): TRect2D;

var
  Colors : array[0..255] of TColor;

Implementation

uses Math, Dialogs, Forms, PCDrawing, U_Common;

// function body
function CartesianRect2D(const R: TRect2D): TRect2D;
begin
  Result.FirstEdge := CartesianPoint2D(R.FirstEdge);
  Result.SecondEdge := CartesianPoint2D(R.SecondEdge);
end;

function ReOrderRect2D(const R: TRect2D): TRect2D;
begin
  Result := CartesianRect2D(R);
  if R.Left > R.Right then
   begin
     Result.Left := R.Right;
     Result.Right := R.Left;
   end;
  if R.Bottom > R.Top then
   begin
     Result.Bottom := R.Top;
     Result.Top := R.Bottom;
   end;
end;

function CartesianPoint2D(const P: TPoint2D): TPoint2D;
begin
  if (P.W <> 1.0) and (P.W <> 0.0) then
   begin
     Result.X := P.X / P.W;
     Result.Y := P.Y / P.W;
     Result.W := 1.0;
   end
  else
   Result := P;
end;

function Vector2D(PFrom, PTo: TPoint2D): TVector2D;
begin
  if PFrom.W <> PTo.W then
   begin
     PFrom := CartesianPoint2D(PFrom);
     PTo := CartesianPoint2D(PTo);
   end;
  Result.X := PTo.X - PFrom.X;
  Result.Y := PTo.Y - PFrom.Y;
end;

function VectorLength2D(const V: TVector2D): TRealType;
begin
  Result := Sqrt(V.X * V.X + V.Y * V.Y);
end;

function Point2D(const X, Y: TRealType): TPoint2D;
begin
  Result.X := X;
  Result.Y := Y;
  Result.W := 1.0;
end;

function PointDistance2D(const P1, P2: TPoint2D): TRealType;
begin
  Result := VectorLength2D(Vector2D(P1, P2));
end;

function ColorToIndex(Col: TColor; Active: Boolean): Integer;
begin
  Result := 7;
  case Col of
   clBlack,
   clWhite: Result := 7;
   clRed: Result := 1;
   clYellow: Result := 2;
   clLime: Result := 3;
   clAqua: Result := 4;
   clBlue: Result := 5;
   clFuchsia: Result := 6;
   clGray: Result := 8;
   clLtGray: Result := 9;
  end;
  if not Active then Result := -1 * Result;
end;

{ --================== TDXFRead ==================-- }

procedure TDXFRead.SetProgressBar(PB: TProgressBar);
begin
  fProgressBar := PB;
  if Assigned(fProgressBar) then
   begin
     fProgressBar.Min := 0;
     fProgressBar.Max := FileSize(fStream);
     fProgressBar.Position := FilePos(fStream);
   end;
end;

constructor TDXFRead.Create(FileName: String);
begin
  inherited Create;
  fProgressBar := nil;
  AssignFile(FStream, FileName);
  Reset(FStream);
  ConsumeGroup;
  NextSection;
end;

destructor TDXFRead.Destroy;
begin
  CloseFile(FStream);
  inherited Destroy;
end;

procedure TDXFRead.Rewind;
begin
  Reset(FStream);
  if Assigned(fProgressBar) then
   fProgressBar.Position := 0;
  ConsumeGroup;
  NextSection;
end;

function TDXFRead.ConsumeGroup;
var
  TxtLine: String;
  LastSep: Char;
begin
  LastSep := DecimalSeparator;
  try
    ReadLn(FStream, FGroupCode);
    ReadLn(FStream, TxtLine);
    if EOF(FStream) then
     begin
       Result := False;
       Exit;
     end;
    case FGroupCode of
      0..9,
      999,
      1000..1009: FGroupValue := Trim(TxtLine);
      10..59,
      140..147,
      210..239,
      1010..1059: begin
        if Pos('.', TxtLine) > 0 then
         DecimalSeparator := '.'
        else
         DecimalSeparator := ',';
        try
         FGroupValue := StrToFloat_My(Trim(TxtLine));
        except
         FGroupValue := varEmpty;
        end;
      end;
      60..79,
      170..175,
      1060..1079 : begin
        if Pos('.', TxtLine) > 0 then
         DecimalSeparator := '.'
        else
         DecimalSeparator := ',';
        try
         FGroupValue := StrToInt(Trim(TxtLine));
        except
         FGroupValue := varEmpty;
        end;
      end;
    else
      FGroupValue := TxtLine;
    end;
    Result := True;
  finally
    DecimalSeparator := LastSep;
  end;
  if Assigned(fProgressBar) then
   fProgressBar.Position := FilePos(fStream);
end;

procedure TDXFRead.NextSection;
begin
  FCurrentSection := scUnknow;
  while not ((FGroupCode = 0) and (FGroupValue = 'SECTION')) do
   if ConsumeGroup = False then Exit;
  ConsumeGroup;
  if FGroupValue = 'HEADER' then
   FCurrentSection := scHeader
  else if FGroupValue = 'CLASSES' then
   FCurrentSection := scClasses
  else if FGroupValue = 'TABLES' then
   FCurrentSection := scTables
  else if FGroupValue = 'BLOCKS' then
   FCurrentSection := scBlocks
  else if FGroupValue = 'ENTITIES' then
   FCurrentSection := scEntities;
  ConsumeGroup;
end;

{ GroupDel = Group delimiter }
function TDXFRead.ReadAnEntry(GroupDel: Word; var Values: TGroupTable): Word;
begin
  Result := 0;
  { Find the start of the entry. }
  while (FGroupCode <> GroupDel) and (FGroupCode <> 0) do
   if ConsumeGroup = False then Exit;
  if (FGroupCode = 0) and (FGroupCode <> GroupDel) then Exit;
  { Read all values. }
  repeat
   if FGroupCode < 256 then
    Values[FGroupCode] := FGroupValue
   else if FGroupCode > 999 then
    { The extended data types are remapped from 256=1000. }
    Values[FGroupCode - 744] := FGroupValue;
   ConsumeGroup;
  until (FGroupCode = GroupDel) or (FGroupCode = 0);
  Result := GroupDel;
end;

(*
{ --================== DXFWriter ==================-- }

constructor TDXFWrite.Create(FileName: String);
begin
  inherited Create;
  AssignFile(FStream, FileName);
  Rewrite(FStream);
end;

destructor TDXFWrite.Destroy;
begin
  CloseFile(FStream);
  inherited Destroy;
end;

procedure TDXFWrite.Reset;
begin
  Rewrite(FStream);
end;

procedure TDXFWrite.WriteGroup(GroupCode: Word; GroupValue: Variant);
var
  TxtLine: String;
  LastSep: Char;
begin
  LastSep := DecimalSeparator;
  try
    DecimalSeparator := '.';
    WriteLn(FStream, Format('%3d', [GroupCode]));
    case GroupCode of
      0..9,
      999,
      1000..1009: TxtLine := Copy(GroupValue, 1, 255);
      10..59,
      140..147,
      210..239,
      1010..1059: TxtLine := Format('%.6f', [Double(GroupValue)]);
      60..79,
      170..175,
      1060..1079 : TxtLine := Format('%6d', [Integer(GroupValue)]);
    else
      TxtLine := '';
    end;
    WriteLn(FStream, TxtLine);
  finally
    DecimalSeparator := LastSep;
  end;
end;

procedure TDXFWrite.BeginSection(Sect: TSections);
begin
  WriteGroup(0, 'SECTION');
  case Sect of
   scHeader: WriteGroup(2, 'HEADER');
   scTables: WriteGroup(2, 'TABLES');
   scBlocks: WriteGroup(2, 'BLOCKS');
   scEntities: WriteGroup(2, 'ENTITIES');
  end;
end;

procedure TDXFWrite.EndSection(Sect: TSections);
begin
  WriteGroup(0, 'ENDSEC');
end;

{ Table Index => DXF file group value
  0-255       => 0-255
  256-512     => 1000-1255 }
procedure TDXFWrite.WriteAnEntry(var Values: TGroupTable);
var
  Cont: Word;
begin
  for Cont := 0 to 255 do
   if VarType(Values[Cont]) > 0 then
    WriteGroup(Cont, Values[Cont]);
  for Cont := 1000 to 1255 do
   if VarType(Values[Cont - 744]) > 0 then
    WriteGroup(Cont, Values[Cont - 744]);
end;
*)
{ --================ DXF2DImport ==================-- }

procedure TDXF2DImport.SetTextFont(F: TFont);
begin
  fTextFont := F;
end;

function TDXF2DImport.ReadLine2D(Entry: TGroupTable): TFigure;
Var
  CurPos: TPoint;
  CurPos1: TPoint;
begin
  CurPos:= Point(Entry[10],Entry[20]);
  CurPos1:= Point(Entry[11],Entry[21]);
  Result := TFigure(FCADCmp2D.Line(FLayNb, CurPos.X, CurPos.Y, CurPos1.X, CurPos1.Y, 0, 0, 0, 0, False));
end;

function TDXF2DImport.ReadCircle2D(Entry: TGroupTable): TFigure;
var
  CurPos: TPoint;
  CurPos1: TPoint;
begin
  CurPos := Point(Entry[10], Entry[20]);
  CurPos1 := Point(Entry[40], Entry[40]);
  Result := TFigure(FCADCmp2D.Circle(FLayNb, CurPos.X, CurPos.Y, CurPos1.X, 0, 0, 0, 0, 0, False));
//  Result := TEllipse2D.Create(0, Point2D(Entry[10] - Entry[40], Entry[20] - Entry[40]),
//                                 Point2D(Entry[10] + Entry[40], Entry[20] + Entry[40]));
end;

function TDXF2DImport.ReadEllipse2D(Entry: TGroupTable): TFigure;
var
  CenterPt, MajorAx: TPoint2D;
  MinorLen, MajorLen, SA, EA, RotA: TRealType;
  CurPos: TPoint;
  CurPos1: TPoint;
begin
  CenterPt := Point2D(Entry[10], Entry[20]);
  MajorAx := Point2D(Entry[11], Entry[21]);
  MinorLen := Entry[40];
  MajorLen := PointDistance2D(CenterPt, MajorAx);
  CurPos := Point(Entry[10], Entry[20]);
  CurPos1 := Point(Entry[40], Entry[40]);
  SA := Entry[41];
  EA := Entry[42];
  RotA := ArcTan2(MajorAx.Y - CenterPt.Y, MajorAx.X - CenterPt.X);

  if (SA = 0.0) and (EA = 2 * Pi) then
   // Ellisse completa
    Result := TFigure(FCADCmp2D.Ellipse(FLayNb, CurPos.X, CurPos.Y, MajorLen, MinorLen, RotA, 0, 0, 0, 0, 0, False))
  else
    // Arco di ellisse
    FCADCmp2D.DeselectAll(FLayNb);
    result := TFigure(FCADCmp2D.Arc(FLayNb, CurPos.X, CurPos.Y, CurPos1.X, SA, EA, 0, 0, 0, 0, 0, 0, True));
    if RotA <> 0 then
      FCADCmp2D.RotateSelection(RotA,
          DoublePoint(Point2D(CenterPt.X - MajorLen, CenterPt.X - MinorLen).X, Point2D(CenterPt.X + MajorLen, CenterPt.X + MinorLen).Y));
end;

function TDXF2DImport.ReadArc2D(Entry: TGroupTable): TFigure;
var
  SA, EA: TRealType;
  CurPos: TPoint;
  CurPos1: TPoint;
begin
  SA := DegToRad(Entry[50]);
  EA := DegToRad(Entry[51]);
  CurPos := Point(Entry[10], Entry[20]);
  CurPos1 := Point(Entry[40], Entry[40]);
  result := TFigure(FCADCmp2D.Arc(FLayNb, CurPos.X, CurPos.Y, CurPos1.X, SA, EA, 0, 0, 0, 0, 0, 0, False));
//  Result.Direction := FAngleDir;
end;

function TDXF2DImport.ReadTrace2D(Entry: TGroupTable): TFigure;
var
  PointsArr: array[1..4] of TDoublePoint;
begin
//  SetLength(PointsArr, 4);
  PointsArr[1] := DoublePoint(Entry[10], Entry[20]);
  PointsArr[2] := DoublePoint(Entry[11], Entry[21]);
  PointsArr[3] := DoublePoint(Entry[12], Entry[22]);
  PointsArr[4] := DoublePoint(Entry[13], Entry[23]);
  Result := TFigure(FCADCmp2D.PolyLine(FLayNb, PointsArr[1], 4, 0, 0, 0, 0, 0, 0, False, False));
end;

function TDXF2DImport.ReadSolid2D(Entry: TGroupTable): TFigure;
var
  PointsArr: array[1..5] of TDoublePoint;
begin
  if VarType(Entry[13]) = varEmpty then
  begin
    PointsArr[1] := DoublePoint(Entry[10], Entry[20]);
    PointsArr[2] := DoublePoint(Entry[11], Entry[21]);
    PointsArr[3] := DoublePoint(Entry[12], Entry[22]);
    PointsArr[4] := DoublePoint(Entry[13], Entry[23]);
    Result := TFigure(FCADCmp2D.PolyLine(FLayNb, PointsArr[1], 4, 0, 0, 0, 0, 0, 0, False, False));
  end
  else
  begin
    PointsArr[1] := DoublePoint(Entry[10], Entry[20]);
    PointsArr[2] := DoublePoint(Entry[11], Entry[21]);
    PointsArr[3] := DoublePoint(Entry[12], Entry[22]);
    PointsArr[4] := DoublePoint(Entry[13], Entry[23]);
    PointsArr[5] := DoublePoint(Entry[10], Entry[20]);
    Result := TFigure(FCADCmp2D.PolyLine(FLayNb, PointsArr[1], 5, 0, 0, 0, 0, 0, 0, True, False));
  end;  
end;

function TDXF2DImport.ReadText2D(Entry: TGroupTable): TFigure;
var
  TmpRect: TRect2D;
begin
//  Result := nil;
  if fTextFont = nil then
   Exit;
  TmpRect.FirstEdge := Point2D(Entry[10], Entry[20]);
  TmpRect.SecondEdge := Point2D(Entry[10], Entry[20] + Entry[40]);
//  Result := TJustifiedVectText2D.Create(0, fTextFont, TmpRect, Entry[40], Entry[1]);
  if Entry[1] = 'F' then
  begin
//   Result.DrawBox := True;
  end;
  if VarType(Entry[72]) <> 0 then
   begin
//     case Entry[72] of
//      1: Result.HorizontalJust := jhCenter;
//      2: Result.HorizontalJust := jhRight;
//     end;
//     case Entry[73] of
//      1: Result.VerticalJust := jvBottom;
//      2: Result.VerticalJust := jvCenter;
//     end;
     if (Entry[72] > 0) or (Entry[73] > 0) then
//      Result.Points[1] := Point2D(Entry[11], Entry[21]);
   end;
  if VarType(Entry[50]) <> 0 then
   begin
//     Result.Transform(Translate2D(-Result.Points[1].X, -Result.Points[1].Y));
//     Result.Transform(Rotate2D(DegToRad(Entry[50])));
//     Result.Transform(Translate2D(Result.Points[1].X, Result.Points[1].Y));
   end;
//  Result.DrawBox := False;
end;

function TDXF2DImport.ReadPolyline2D(Entry: TGroupTable): TFigure;
var
  LocalEntry: TGroupTable;
  IsClosedInM, IsSpline2D: Boolean;
begin
  // Considera le polilinee 2D e 3D alla stessa maniera.
  // Determina i flags della polilinea.
  if VarType(Entry[70]) <> varEmpty then
   begin
     IsClosedInM := Entry[70] and 1 = 1;
     IsSpline2D := (Entry[70] and 2 = 2) or (Entry[70] and 4 = 4);
   end
  else
   begin
     IsClosedInM := False;
     IsSpline2D := False;
   end;

  if IsSpline2D then
   begin // Leggo la spline2D
//     Result := TPolyline2D.Create(0, [Point2D(0, 0)]);
//     Result.Points.Delete(0);
     // Leggo soli punti aggiunti, quindi gruppo 70 con bit 8.
     fDXFRead.ReadAnEntry(0, LocalEntry);
     while LocalEntry[0] = 'VERTEX' do
      begin
        if (VarType(LocalEntry[70]) <> varEmpty)
           and (LocalEntry[70] and 8 = 8) then
//         Result.Points.Add(Point2D(LocalEntry[10], LocalEntry[20]));
        FDXFRead.ReadAnEntry(0, LocalEntry);
      end;
//     if Result.Points.Count = 0 then
      begin // La spline è stata creata senza i punti aggiuntivi.
//        Result.Free;
//        Result := nil;
        fUnableToReadAll := True;
        if fVerbose then
         ShowMessage('Spline without spline-fitting isn''t supported.');
      end;
   end
  else
   begin // Leggo la polilinea2D
//     Result := TPolyline2D.Create(0, [Point2D(0, 0)]);
//     Result.Points.Delete(0);
     // Leggo tutti i punti.
     fDXFRead.ReadAnEntry(0, LocalEntry);
     while LocalEntry[0] = 'VERTEX' do
      begin
//        Result.Points.Add(Point2D(LocalEntry[10], LocalEntry[20]));
        FDXFRead.ReadAnEntry(0, LocalEntry);
      end;
     if IsClosedInM then // la chiudo.
//      Result.Points.Add(Result.Points[0]);
   end;
  if (LocalEntry[0] <> 'SEQEND') then
   begin
     fUnableToReadAll := True;
     Raise EDXFInvalidDXF.Create('Invalid DXF file.');
   end;
end;

function TDXF2DImport.ReadEntity(IgnoreBlock: Boolean): TFigure;
var
  Entry: TGroupTable;
  NLayer: Integer;
begin
//  Result := nil;
  FDXFRead.ReadAnEntry(0, Entry);
  NLayer := FLayerList.IndexOf(Entry[8]);
  if NLayer > -1 then
   FCADCmp2D.ActiveLayer := 1;
  if Entry[0] = 'LINE' then
   Result := ReadLine2D(Entry)
  else if Entry[0] = 'ARC' then
   Result := ReadArc2D(Entry)
  else if Entry[0] = 'TRACE' then
   Result := ReadTrace2D(Entry)
  else if Entry[0] = 'SOLID' then
   Result := ReadSolid2D(Entry)
  else if Entry[0] = 'CIRCLE' then
   Result := ReadCircle2D(Entry)
  else if Entry[0] = 'ELLIPSE' then
   Result := ReadEllipse2D(Entry)
  else if Entry[0] = 'POLYLINE' then
   Result := ReadPolyline2D(Entry)
  else if Entry[0] = 'TEXT' then
   Result := ReadText2D(Entry)
  else if (not IgnoreBlock) and (Entry[0] = 'INSERT') then
   Result := ReadBlock(Entry);
end;

function TDXF2DImport.ReadBlock(Entry: TGroupTable): TFigure;
begin
  result := nil;
  ShowMessage('Some blocks cannot be read.');
  Exit;
end;

(*
procedure TDXF2DImport.ReadEntitiesAsContainer(const Container: TContainer2D);
var
 Tmp: TObject2D;
 ID: Integer;
begin
  ID := 0;
  while DXFRead.GroupValue <> 'ENDSEC' do
   begin
     Tmp := ReadEntity(True);
     if Assigned(Tmp) then
      begin
        Tmp.Transform(Scale2D(fScale, fScale));
        Tmp.ApplyTransform;
        Tmp.ID := ID;
        Container.Objects.Add(Tmp);
        Inc(ID)
      end
     else
      fUnableToReadAll := True;
   end;
  Container.UpdateExtension(Self);
end;
*)

constructor TDXF2DImport.Create(const FileName: String; const aCAD: TPowerCad);
begin
  inherited Create;
  fDXFRead := TDXFRead.Create(FileName);
  fCADCmp2D := aCAD;
  FLayNb := 1;
  fLayerList := TStringList.Create;
  fBlockList := TStringList.Create;
  fSetLayers := True;
  fScale := 1.0;
  fHasExtension := False;
  fUnableToReadAll := False;
end;

destructor TDXF2DImport.Destroy;
begin
  fDXFRead.Free;
  fLayerList.Free;
  fBlockList.Free;
  inherited Destroy;
end;

function TDXF2DImport.GoToSection(Sect: TSections): Boolean;
begin
  FDXFRead.Rewind;
  while (FDXFRead.CurrentSection <> scUnknow) and
        (FDXFRead.CurrentSection <> Sect) do
   FDXFRead.NextSection;
  Result := FDXFRead.CurrentSection = Sect;
end;

procedure TDXF2DImport.ReadDXF;
begin
  if fCADCmp2D = nil then Exit;
  fUnableToReadAll := False;
  FDXFRead.Rewind;
  while (FDXFRead.CurrentSection <> scUnknow) do
   begin
     case FDXFRead.CurrentSection of
      scHeader: ReadHeader;
      scTables: ReadTables;
      scBlocks: ReadBlocks;
      scEntities: ReadEntities;
     end;
     FDXFRead.NextSection;
   end;
end;

procedure TDXF2DImport.ReadHeader;
var
  Entry: TGroupTable;
begin
  fHasExtension := False;
  fExtension.Right := 1000;
  fExtension.Top := 1000;
  fExtension.W1 := 1.0;
  fExtension.Left := -1000;
  fExtension.Bottom := -1000;
  fExtension.W2 := 1.0;
  fAngleDir := adCounterClockwise;
  while fDXFRead.ReadAnEntry(9, Entry) <> 0 do
   begin
     if Entry[9] = '$ANGDIR' then
      begin
        if Entry[70] = 1 then
         fAngleDir := adClockwise
        else
         fAngleDir := adCounterClockwise;
      end
     else if Entry[9] = '$EXTMAX' then
      begin
        fExtension.Right := Entry[10];
        fExtension.Top := Entry[20];
        fHasExtension := True;
      end
     else if Entry[9] = '$EXTMIN' then
      begin
        fExtension.Left := Entry[10];
        fExtension.Bottom := Entry[20];
        fHasExtension := True;
      end;
   end;
  if fHasExtension then
   FExtension := ReOrderRect2D(FExtension);
end;

procedure TDXF2DImport.ReadTables;
var
  Entry: TGroupTable;
begin
  if fCADCmp2D = nil then Exit;
  fDXFRead.ReadAnEntry(0, Entry);
  while fDXFRead.GroupValue <> 'ENDSEC' do
   begin
     if Entry[0] = 'LAYER' then
      begin
        if fSetLayers then
//         with fCADCmp2D.Layers[fLayerList.Count] do
//          begin
//            Pen.Color := Colors[Abs(Round(Entry[62]))];
//            Brush.Style := bsClear;
//            Active := Entry[62] >= 0;
//            Name := Entry[2];
//          end;
        fLayerList.AddObject(Entry[2], fCADCmp2D.Layers[fLayerList.Count]);
      end;
     fDXFRead.ReadAnEntry(0, Entry);
   end;
end;

procedure TDXF2DImport.ReadEntities;
var
 Tmp: TFigure;
begin
  if fCADCmp2D = nil then Exit;
  while DXFRead.GroupValue <> 'ENDSEC' do
   begin
     Tmp := ReadEntity(False);
     if Assigned(Tmp) then
      begin
//        Tmp.Transform(Scale2D(fScale, fScale));
//        Tmp.ApplyTransform;
//        FCADCmp2D.AddCustomFigure(FLayNb, Tmp, False);
      end
     else
      fUnableToReadAll := True;
   end;
end;

procedure TDXF2DImport.ReadBlocks;
var
  Entry: TGroupTable;
  NLayer: Integer;
  tmp: TFigure;
begin
  if fCADCmp2D = nil then Exit;
  while DXFRead.GroupValue <> 'ENDSEC' do
   begin
     FDXFRead.ReadAnEntry(0, Entry);
//     NLayer := FLayerList.IndexOf(Entry[8]);
//     if NLayer > -1 then
//      FCADCmp2D.CurrentLayer := NLayer;
     if Entry[0] = 'BLOCK' then
      begin
        Tmp := ReadSourceBlock(Entry);
        if Assigned(Tmp) then
         begin
           FCADCmp2D.AddCustomFigure(1, Tmp, False);
           fBlockList.AddObject(Entry[2], Tmp);
         end;
      end;
   end;
end;

function TDXF2DImport.ReadSourceBlock(Entry: TGroupTable): TFigure;
var
  BasePoint: TPoint2D;
//  Tmp: TObject2D;
  TmpName: TSourceBlockName;
begin
  Result := nil;
  if (Entry[70] and $4) or (Entry[70] and $1) then
   { XRef and anonymous are not allowed. }
   Exit;
  BasePoint.X := Entry[10];
  BasePoint.Y := Entry[20];
  BasePoint.W := 1.0;
//  if Entry[2] = '' then
//   TmpName := StringToBlockName(Format('BLOCK%d', [FCADCmp2D.SourceBlocksCount]))
//  else
//   TmpName := StringToBlockName(Entry[2]);
//  Result := TSourceBlock2D.Create(0, TmpName, [nil]);
  while FDXFRead.GroupValue <> 'ENDBLK' do
   begin
     ReadEntity(False);
//     if Assigned(Tmp) then
//      Result.Objects.Add(Tmp);
   end;
//  if Result.Objects.Count > 0 then
//   begin
//     Result.Transform(Translate2D(-BasePoint.X, -BasePoint.Y));
//     Result.ApplyTransform;
//     Result.UpdateExtension(Self);
//   end
//  else
//   begin
//     Result.Free;
//     Result := nil;
//   end;
end;

(*
{ --================ DXF2DExport ==================-- }

constructor TDXF2DExport.Create(const FileName: String; const CAD: TCADCmp2D);
begin
  inherited Create;
  FDXFWrite := TDXFWrite.Create(FileName);
  FCADCmp2D := CAD;
end;

destructor TDXF2DExport.Destroy;
begin
  FDXFWrite.WriteGroup(0, 'EOF');
  FDXFWrite.Free;
  inherited Destroy;
end;

procedure TDXF2DExport.WriteDXF;
begin
  if fCADCmp2D = nil then Exit;
  FDXFWrite.Reset;
  WriteHeader;
  WriteTables;
  WriteBlocks;
  WriteEntities;
end;

procedure TDXF2DExport.WriteHeader;
var
  TmpInt: Integer;
begin
  if fCADCmp2D = nil then Exit;
  with FDXFWrite do
   begin
     BeginSection(scHeader);
     { Angle direction, Default CounterClockWise }
     WriteGroup(9, '$ANGDIR');
     TmpInt := 0;
     WriteGroup(70, TmpInt);
     { Extension of the drawing }
     WriteGroup(9, '$EXTMAX');
     WriteGroup(10, FCADCmp2D.DrawingExtension.Right);
     WriteGroup(20, FCADCmp2D.DrawingExtension.Top);
     WriteGroup(30, 0);
     WriteGroup(9, '$EXTMIN');
     WriteGroup(10, FCADCmp2D.DrawingExtension.Left);
     WriteGroup(20, FCADCmp2D.DrawingExtension.Bottom);
     WriteGroup(30, 0);
     WriteGroup(9, '$CECOLOR');
     WriteGroup(62, 256);
     WriteGroup(9, '$CELTYPE');
     WriteGroup(6, 'BYLAYER');
     { End Header Section }
     EndSection(scHeader);
   end;
end;

procedure TDXF2DExport.WriteTables;
var
  Count: Integer;
begin
  if fCADCmp2D = nil then Exit;
  with FDXFWrite do
   begin
     { Begin Tables Section }
     BeginSection(scTables);
     WriteGroup(0, 'TABLE');
     WriteGroup(2, 'LTYPE');
     WriteGroup(70, 1);
     WriteGroup(0, 'LTYPE');
     WriteGroup(2, 'CONTINUOUS');
     WriteGroup(70, 64);
     WriteGroup(3, 'Solid line');
     WriteGroup(72, 65);
     WriteGroup(73, 0);
     WriteGroup(40, 0.0);
     WriteGroup(0, 'ENDTAB');
     WriteGroup(0, 'TABLE');
     WriteGroup(2, 'LAYER');
     WriteGroup(70, 257);
     WriteGroup(0, 'LAYER');
     WriteGroup(2, '0');
     WriteGroup(70, 64);
     WriteGroup(62, 7);
     WriteGroup(6, 'CONTINUOUS');
     for Count := 0 to 255 do
      with FCADCmp2D.Layers[Count] do
       if Modified then
        begin
          WriteGroup(0, 'LAYER');
          WriteGroup(2, Name);
          WriteGroup(70, 64);
          WriteGroup(62, ColorToIndex(Pen.Color, Active));
          WriteGroup(6, 'CONTINUOUS');
        end;
     WriteGroup(0, 'ENDTAB');
     { End Tables Section }
     EndSection(scTables);
   end;
end;

procedure TDXF2DExport.WriteEntities;
var
  TmpIter: TGraphicObjIterator;
  TmpObj: TObject2D;
begin
  if fCADCmp2D = nil then Exit;
  FDXFWrite.BeginSection(scEntities);
  TmpIter := fCADCmp2D.ObjectsIterator;
  try
    TmpObj := TmpIter.First as TObject2D;
    while TmpObj <> nil do
     begin
       WriteEntity(TmpObj);
       TmpObj := TmpIter.Next as TObject2D;
     end;
  finally
    TmpIter.Free;
    FDXFWrite.EndSection(scEntities);
  end;
end;

procedure TDXF2DExport.WriteBlocks;
var
  TmpIter: TGraphicObjIterator;
  TmpObj: TObject2D;
begin
  if fCADCmp2D = nil then Exit;
  FDXFWrite.BeginSection(scBlocks);
  TmpIter := fCADCmp2D.SourceBlocksIterator;
  try
    TmpObj := TmpIter.First as TObject2D;
    while TmpObj <> nil do
     begin
       WriteEntity(TmpObj);
       TmpObj := TmpIter.Next as TObject2D;
     end;
  finally
    TmpIter.Free;
    FDXFWrite.EndSection(scBlocks);
  end;
end;

procedure TDXF2DExport.WriteLine2D(Line: TLine2D);
var
 TmpPnt: TPoint2D;
begin
  with FDXFWrite, Line do
   begin
     TmpPnt := TransformPoint2D(Points[0], ModelTransform);
     WriteGroup(10, TmpPnt.X);
     WriteGroup(20, TmpPnt.Y);
     WriteGroup(30, 0);
     TmpPnt := TransformPoint2D(Points[1], ModelTransform);
     WriteGroup(11, TmpPnt.X);
     WriteGroup(21, TmpPnt.Y);
     WriteGroup(31, 0);
   end;
end;

procedure TDXF2DExport.WriteFrame2D(Frm: TFrame2D);
var
 TmpPnt: TPoint2D;
begin
  with FDXFWrite, Frm do
   begin
     WriteGroup(66, 1);
     WriteGroup(10, 0);
     WriteGroup(20, 0);
     WriteGroup(30, 0); // Questo setta l'elevazione.
     WriteGroup(0, 'VERTEX');
     WriteGroup(8, FCADCmp2D.Layers[Frm.Layer].Name);
     TmpPnt := TransformPoint2D(Points[0], ModelTransform);
     WriteGroup(10, TmpPnt.X);
     WriteGroup(20, TmpPnt.Y);
     WriteGroup(30, 0);
     WriteGroup(0, 'VERTEX');
     WriteGroup(8, FCADCmp2D.Layers[Frm.Layer].Name);
     TmpPnt := TransformPoint2D(Point2D(Points[0].X, Points[1].Y), ModelTransform);
     WriteGroup(10, TmpPnt.X);
     WriteGroup(20, TmpPnt.Y);
     WriteGroup(30, 0);
     WriteGroup(0, 'VERTEX');
     WriteGroup(8, FCADCmp2D.Layers[Frm.Layer].Name);
     TmpPnt := TransformPoint2D(Points[1], ModelTransform);
     WriteGroup(10, TmpPnt.X);
     WriteGroup(20, TmpPnt.Y);
     WriteGroup(30, 0);
     WriteGroup(0, 'VERTEX');
     WriteGroup(8, FCADCmp2D.Layers[Frm.Layer].Name);
     TmpPnt := TransformPoint2D(Point2D(Points[1].X, Points[0].Y), ModelTransform);
     WriteGroup(10, TmpPnt.X);
     WriteGroup(20, TmpPnt.Y);
     WriteGroup(30, 0);
     WriteGroup(0, 'VERTEX');
     WriteGroup(8, FCADCmp2D.Layers[Frm.Layer].Name);
     TmpPnt := TransformPoint2D(Points[0], ModelTransform);
     WriteGroup(10, TmpPnt.X);
     WriteGroup(20, TmpPnt.Y);
     WriteGroup(30, 0);
     WriteGroup(0, 'SEQEND');
   end;
end;

procedure TDXF2DExport.WriteText2D(Text: TJustifiedVectText2D);
var
  TmpPnt, TmpPnt1: TPoint2D;
  InsPnt: TPoint2D;
  AllPnt: TPoint2D;
  TmpAngle: Single;
begin
  with FDXFWrite, Text do
   begin
     WriteGroup(1, Text);
     WriteGroup(40, Height);
     TmpPnt := TransformPoint2D(Points[0], ModelTransform);
     AllPnt := TransformPoint2D(Points[1], ModelTransform);
     InsPnt := TransformPoint2D(Point2D(Points[0].X, Points[1].Y - Height), ModelTransform);
     WriteGroup(10, InsPnt.X);
     WriteGroup(20, InsPnt.Y);
     WriteGroup(30, 0);
     // I compute the angle of the Text from the rotation of it.
     TmpPnt := TransformPoint2D(Point2D(0, 0), ModelTransform);
     TmpPnt1 := TransformPoint2D(Point2D(10, 0), ModelTransform);
     TmpAngle := ArcTan2(TmpPnt1.Y - TmpPnt.Y, TmpPnt1.X - TmpPnt.X);
     WriteGroup(50, RadToDeg(TmpAngle));
     case HorizontalJust of
       jhCenter: begin
         WriteGroup(11, AllPnt.X - InsPnt.X);
         WriteGroup(21, AllPnt.Y - InsPnt.Y);
         WriteGroup(31, 0);
         WriteGroup(72, 1);
       end;
       jhRight: begin
         WriteGroup(11, AllPnt.X);
         WriteGroup(21, AllPnt.Y);
         WriteGroup(31, 0);
         WriteGroup(72, 2);
       end;
     end;
  end;
end;

procedure TDXF2DExport.WriteCurve2D(Curve: TCurve2D);
var
  Count: Integer;
  TmpPnt: TPoint2D;
begin
  with FDXFWrite, Curve do
   begin
     BeginUseProfilePoints;
     try
       WriteGroup(66, 1);
       WriteGroup(10, 0);
       WriteGroup(20, 0);
       WriteGroup(30, 0); // Questo setta l'elevazione.
       for Count := 0 to ProfilePoints.Count - 1 do
        begin
          WriteGroup(0, 'VERTEX');
          WriteGroup(8, FCADCmp2D.Layers[Curve.Layer].Name);
          TmpPnt := TransformPoint2D(ProfilePoints[Count], ModelTransform);
          WriteGroup(10, TmpPnt.X);
          WriteGroup(20, TmpPnt.Y);
          WriteGroup(30, 0);
        end;
       WriteGroup(0, 'SEQEND');
     finally
       EndUseProfilePoints;
     end;
   end;
end;

procedure TDXF2DExport.WriteOutline2D(Poly: TOutline2D);
var
  Count: Integer;
  TmpPnt: TPoint2D;
begin
  with FDXFWrite, Poly do
   begin
     WriteGroup(66, 1);
     WriteGroup(10, 0);
     WriteGroup(20, 0);
     WriteGroup(30, 0); // Questo setta l'elevazione.
     for Count := 0 to Points.Count - 1 do
      begin
        WriteGroup(0, 'VERTEX');
        WriteGroup(8, FCADCmp2D.Layers[Poly.Layer].Name);
        TmpPnt := TransformPoint2D(Points[Count], ModelTransform);
        WriteGroup(10, TmpPnt.X);
        WriteGroup(20, TmpPnt.Y);
        WriteGroup(30, 0);
      end;
     WriteGroup(0, 'SEQEND');
   end;
end;

procedure TDXF2DExport.WriteBlock(Block: TBlock2D);
begin
  with FDXFWrite, Block do
   begin
     WriteGroup(2, Format('%s', [SourceName]));
     WriteGroup(10, ModelTransform[3,1]);
     WriteGroup(20, ModelTransform[3,2]);
     WriteGroup(30, 0);
     WriteGroup(41, ModelTransform[1,1]);
     WriteGroup(42, ModelTransform[2,2]);
   end;
end;

procedure TDXF2DExport.WriteEntity(Obj: TObject2D);
begin
  with FDXFWrite do
   if Obj is TLine2D then
    begin
      WriteGroup(0, 'LINE');
      WriteGroup(8, FCADCmp2D.Layers[Obj.Layer].Name);
      WriteLine2D(Obj as TLine2D);
    end
   else if Obj is TFrame2D then
    begin
      WriteGroup(0, 'POLYLINE');
      WriteGroup(8, FCADCmp2D.Layers[Obj.Layer].Name);
      WriteFrame2D(Obj as TFrame2D);
    end
   else if Obj is TCurve2D then
    begin
      WriteGroup(0, 'POLYLINE');
      WriteGroup(8, FCADCmp2D.Layers[Obj.Layer].Name);
      WriteCurve2D(Obj as TCurve2D);
    end
   else if Obj is TOutline2D then
    begin
      WriteGroup(0, 'POLYLINE');
      WriteGroup(8, FCADCmp2D.Layers[Obj.Layer].Name);
      WriteOutline2D(Obj as TOutline2D);
    end
   else if Obj is TJustifiedVectText2D then
    begin
      WriteGroup(0, 'TEXT');
      WriteGroup(8, FCADCmp2D.Layers[Obj.Layer].Name);
      WriteText2D(Obj as TJustifiedVectText2D);
    end
   else if Obj is TBlock2D then
    begin
      WriteGroup(0, 'INSERT');
      WriteGroup(8, FCADCmp2D.Layers[Obj.Layer].Name);
      WriteBlock(Obj as TBlock2D);
    end;
end;
*)

const
 ColArray1: array[1..4] of Byte = (0, 63, 127, 191);
 ColArray2: array[1..4] of Byte = (127, 159, 191, 223);
var
 Cont: Byte;

initialization
  Colors[0] := RGB(255, 255, 255);
  Colors[1] := RGB(255, 0, 0);
  Colors[2] := RGB(255, 255, 0);
  Colors[3] := RGB(0, 255, 0);
  Colors[4] := RGB(0, 255, 255);
  Colors[5] := RGB(0, 0, 255);
  Colors[6] := RGB(255, 0, 255);
  Colors[7] := RGB(0, 0, 0);
  Colors[8] := RGB(134, 134, 134);
  Colors[9] := RGB(187, 187, 187);
  Colors[250] := RGB(0, 0, 0);
  Colors[251] := RGB(45, 45, 45);
  Colors[252] := RGB(91, 91, 91);
  Colors[253] := RGB(137, 137, 137);
  Colors[254] := RGB(183, 183, 183);
  Colors[255] := RGB(179, 179, 179);
  for Cont := 1 to 4 do
   begin
     Colors[Cont * 10] := RGB(255, ColArray1[Cont], 0);
     Colors[40 + Cont * 10] := RGB(ColArray1[5 - Cont], 255, 0);
     Colors[80 + Cont * 10] := RGB(0, 255, ColArray1[Cont]);
     Colors[120 + Cont * 10] := RGB(0, ColArray1[5 - Cont], 255);
     Colors[160 + Cont * 10] := RGB(ColArray1[Cont], 0, 255);
   end;
  Colors[210] := RGB(255, 0, 255);
  Colors[220] := RGB(255, 0, 191);
  Colors[230] := RGB(255, 0, 127);
  Colors[240] := RGB(255, 0, 63);
  for Cont := 1 to 4 do
   begin
     Colors[Cont * 10 + 1] := RGB(255, ColArray2[Cont], 127);
     Colors[41 + Cont * 10] := RGB(ColArray2[5 - Cont], 255, 127);
     Colors[81 + Cont * 10] := RGB(127, 255, ColArray2[Cont]);
     Colors[121 + Cont * 10] := RGB(127, ColArray2[5 - Cont], 255);
     Colors[161 + Cont * 10] := RGB(ColArray2[Cont], 127, 255);
   end;
  Colors[211] := RGB(255, 127, 255);
  Colors[221] := RGB(255, 127, 223);
  Colors[231] := RGB(255, 127, 191);
  Colors[241] := RGB(255, 127, 159);
end.

