R12 形式の DXF ファイルを作る (2016/ 8/19, 2018/12/13)

2018/12/13
 ・PLOYLINE (直線のみ)の作成を追加
 ・Variant 型からの変換を追加
 ・TEXT 作成の不具合を修正

■R12 形式の DXF ファイルは、ハンドル番号、ハンドル数、クラス名が不要なので、作りやすいです。
 ただし、R12 時代に無い図形は作成できません。例えば、LWPOLYLINE は、POLYLINE で作成します。

※ファイルを挿入するとき、ファイル名の前に "*" を付けると、分解挿入されます。
 分解挿入の時、尺度指定の S オプションが使えなくなり、X, Y の尺度を指定する必要があります。


{ 2018/12/13
 ・Variant -> TAcadPoint 変換を追加
  ・POLYLINE 作成(直線のみ)を追加
  ・TEXT 作成の不具合を修正
}
unit MakeR12Dxf_V;
 
interface
uses
  Windows,         //for TPoint
  System.UITypes,
  System.Variants, //for OleVariant;
  SysUtils,
  Graphics,
  Classes;         //for TStrings

type
  TAcadPoint = Record
    x, y, z :Extended;
  end;

  function V3ToAcPt(vpt : OleVariant): TAcadPoint;
  function VptsToAcPts(vpts : OleVariant; var acpts: array of TAcadPoint): boolean;
  procedure MakeR12DxfHead(sl: TStrings);
  procedure AddR12DxfEntHead(sl: TStringList);
  procedure AddR12DxfLineEnt(sl: TStrings; p1, p2: TAcadPoint; const Layer: string; const LType: string; LColor: integer);
  procedure AddR12DxfTextEnt(sl: TStrings;
      const Txt: string; pt10, pt11: TAcadPoint; Takasa, Haba, Kakudo: double;
      code72, code73: integer; const Kiten: string; const Lay: string; color: integer);
  procedure AddR12DxfCircleEnt(sl: TStrings; Center: TAcadPoint; hankei: double; const Layer: string; const LType: string; LColor: integer);
  procedure AddR12DxfArcEnt(sl: TStrings; Center: TAcadPoint; hankei, StDeg, EdDeg: double; const Layer: string; const LType: string; LColor: integer);
  procedure AddR12DxfPolylineEnt(sl: TStrings; pts : array of TAcadPoint; closed : boolean ; const Layer: string; const LType: string; LColor: integer);
  procedure AddR12DxfTerm(sl: TStrings);
  procedure AddR12DxfLtypeHidden2(sl: TStrings);
  procedure AddR12DxfLtypeDummy(sl: TStrings);

implementation

//{$R *.dfm}

// バリアントの3D配列を TAcadPoint に
function V3ToAcPt(vpt : OleVariant): TAcadPoint;
begin
  result.x := vpt[0];
  result.y := vpt[1];
  result.z := vpt[2];
end;

// バリアントの1次元配列(2D座標)を TAcadPoint に
// vpts は、x1, y1, x2, y2, ....
// acpt は、acpt[0], acpt[1], ...
function VptsToAcPts(vpts : OleVariant; var acpts: array of TAcadPoint): boolean;
var
  n, m, i :integer;
begin
  // 一次元配列の数
  n := VarArrayHighBound(vpts, 1) + 1;
  m := Length(acpts);
  if n div 2 = m then begin
    for i := 0 to n div 2 -1 do begin
      acpts[i].x := vpts[i * 2];
      acpts[i].y := vpts[i * 2 + 1];
      acpts[i].z := 0.0;
    end;
  end;
  result := n div 2 =m;
end;

procedure MakeR12DxfHead(sl: TStrings);
begin
    sl.Clear;
    sl.Add('  0' + #13#10 + 'SECTION' + #13#10 + '  2' + #13#10 + 'HEADER' + #13#10 +
      '  9' + #13#10 + '$ACADVER' + #13#10 + '  1' + #13#10 + 'AC1009' + #13#10 +
      '  9' + #13#10 + '$CELTYPE' + #13#10 + '  6' + #13#10 + 'BYLAYER' + #13#10 +
      '  9' + #13#10 + '$CECOLOR' + #13#10 + ' 62' + #13#10 + '256' + #13#10 +
      '  0' + #13#10 + 'ENDSEC');
    sl.Add('  0' + #13#10 + 'SECTION' + #13#10 + '  2' + #13#10 + 'TABLES');
end;

procedure AddR12DxfTerm(sl: TStrings);
begin
    sl.Add('  0' + #13#10 + 'ENDSEC');
    sl.Add('  0' + #13#10 + 'EOF');
end;

procedure AddR12DxfLtypeDummy(sl: TStrings);
begin
  with sl do
  begin
    // TABLESセクションのうちLTYPEだけを追加
    Add('  0' + #13#10 + 'TABLE');
    Add('  2' + #13#10 + 'LTYPE');
    Add(' 70' + #13#10 + '     2' + #13#10 + '  0' + #13#10 + 'LTYPE');
    Add('  2' + #13#10 + 'CONTINUOUS' + #13#10 + ' 70' + #13#10 + '     0' + #13#10 + '  3' + #13#10 + 'Solid line');
    Add(' 72' + #13#10 + '    65' + #13#10 + ' 73' + #13#10 + '     0' + #13#10 + ' 40' + #13#10 + '0.0');
    Add('  0' + #13#10 + 'ENDTAB');
  end
end;

procedure AddR12DxfLtypeHidden2(sl: TStrings);
begin
  with sl do
  begin
    // TABLESセクションのうちLTYPEだけを追加
    Add('  2' + #13#10 + 'LTYPE');
    Add('  0' + #13#10 + 'LTYPE' + #13#10 + '  2' + #13#10 + 'HIDDEN2' +
        #13#10 + ' 70' + #13#10 + '     0');
    Add('  3' + #13#10 + 'Hidden (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _');
    Add(' 72' + #13#10 + '    65' + #13#10 + ' 73' + #13#10 + '     2');
    Add(' 40' + #13#10 + '4.7625' + #13#10 + ' 49' + #13#10 + '3.175' +
        #13#10 + ' 49' + #13#10 + '-1.5875');
    Add('  0' + #13#10 + 'ENDTAB');
  end
end;

procedure AddR12DxfEntHead(sl: TStringList);
begin
  with sl do begin
    Add('  0' + #13#10 + 'ENDSEC');
    Add('  0' + #13#10 + 'SECTION' + #13#10 + '  2' + #13#10 + 'ENTITIES');
  end;
end;

// R12形式なので、TTFは設定できない
// すでに存在するSTYLEの場合は、フォント名は適当で良い
procedure AddR12DxfTextStyle(sl: TStrings; const style, font1, font2: string);
begin
  with sl do begin
    Add('  0' + #13#10 + 'TABLE' + #13#10 + '  2' + #13#10 + 'STYLE' + #13#10 + ' 70' + #13#10 + '     1');
    Add('  0' + #13#10 + 'STYLE' + #13#10 + '  2' + #13#10 + style + #13#10 + ' 70' + #13#10 +'     0');
    Add(' 40' + #13#10 + '0.0' + #13#10 + ' 41' + #13#10 + '1.0' + #13#10 + ' 50' + #13#10 + '0.0');
    Add(' 71' + #13#10 + '     0' + #13#10 + ' 42' + #13#10 + '2.5' + #13#10 + '  3' + #13#10 + font1);
    Add('  4' + #13#10 + font2 + #13#10 + '  0' + #13#10 + 'ENDTAB');
  end;
end;

procedure AddR12DxfLineEnt(sl: TStrings; p1, p2: TAcadPoint; const Layer: string; const LType: string; LColor: integer);
begin
  with sl do begin
    Add('  0' + #13#10 + 'LINE');
    Add('  8' + #13#10 + Layer + #13#10 + '  6' + #13#10 + LType + #13#10 +
        ' 62' + #13#10 + IntToStr(LColor) + #13#10 + ' 48' + #13#10 + '1.0');
    Add(' 10' + #13#10 + FloatToStr(p1.x) + #13#10 + ' 20' + #13#10 + FloatToStr(p1.y) + #13#10 +
        ' 30' + #13#10 + FloatToStr(p1.z));
    Add(' 11' + #13#10 + FloatToStr(p2.x) + #13#10 + ' 21' + #13#10 + FloatToStr(p2.y) + #13#10 +
        ' 31' + #13#10 + FloatToStr(p2.z));
  end;
end;

procedure AddR12DxfCircleEnt(sl: TStrings; Center: TAcadPoint; hankei: double; const Layer: string; const LType: string; LColor: integer);
begin
  with sl do begin
    Add('  0' + #13#10 + 'CIRCLE');
    Add('  8' + #13#10 + Layer + #13#10 + '  6' + #13#10 + LType + #13#10 +
        ' 62' + #13#10 + IntToStr(LColor) + #13#10 + ' 48' + #13#10 + '1.0');
    Add(' 10' + #13#10 + FloatToStr(Center.x) + #13#10 + ' 20' + #13#10 + FloatToStr(Center.y) + #13#10 +
        ' 30' + #13#10 + FloatToStr(Center.z));
    Add(' 40' + #13#10 + FloatToStr(Hankei));
  end;
end;

procedure AddR12DxfArcEnt(sl: TStrings; Center: TAcadPoint; hankei, StDeg, EdDeg: double; const Layer: string; const LType: string; LColor: integer);
begin
  with sl do begin
    Add('  0' + #13#10 + 'ARC');
    Add('  8' + #13#10 + Layer + #13#10 + '  6' + #13#10 + LType + #13#10 +
        ' 62' + #13#10 + IntToStr(LColor) + #13#10 + ' 48' + #13#10 + '1.0');
    Add(' 10' + #13#10 + FloatToStr(Center.x) + #13#10 + ' 20' + #13#10 + FloatToStr(Center.y) + #13#10 +
        ' 30' + #13#10 + FloatToStr(Center.z));
    Add(' 40' + #13#10 + FloatToStr(Hankei) + #13#10 +
        ' 50' + #13#10 + FloatToStr(StDeg) + #13#10 + ' 51' + #13#10 + FloatToStr(EdDeg));
  end;
end;

procedure AddR12DxfPolylineEnt(sl: TStrings; pts : array of TAcadPoint; closed : boolean ; const Layer: string; const LType: string; LColor: integer);
var
  i : integer;
begin
  with sl do begin
    Add('  0' + #13#10 + 'POLYLINE');
    Add('  8' + #13#10 + Layer + #13#10 + '  6' + #13#10 + LType + #13#10 +
        ' 62' + #13#10 + IntToStr(LColor) + #13#10 + ' 48' + #13#10 + '1.0');
    Add(' 66' + #13#10 + '1');
    Add(' 10' + #13#10 + '0.0' + #13#10 + ' 20' + #13#10 + '0.0' + #13#10 + ' 30' + #13#10 + '0.0');
    if closed then
      Add(' 70' + #13#10 + '1')
    else
      Add(' 70' + #13#10 + '0');
    for i := 0 to Length(pts) - 1 do begin
      Add('  0' + #13#10 + 'VERTEX');
      Add('  8' + #13#10 + Layer);
      Add(' 10' + #13#10 + FloatToStr(pts[i].x) + #13#10 + ' 20' + #13#10 + FloatToStr(pts[i].y) + #13#10 +
        ' 30' + #13#10 + '0.0');
    end;
    Add('  0' + #13#10 + 'SEQEND');
  end;
end;

// 201812/13 修正
procedure AddR12DxfTextEnt(sl: TStrings;
    const Txt: string; pt10, pt11: TAcadPoint; Takasa, Haba, Kakudo: double;
    code72, code73: integer; const Kiten: string; const Lay: string; color :Integer);
var
  n: integer;
  s: string;
begin
  if kiten <> '' then begin
    s := Kiten;
    n := Length(s);
    if (n = 2) then begin
      if (s[1] = 'M') then code73 := 2
      else if (s[1] = 'T') then code73 := 3
      else if (s[1] = 'B') then code73 := 1;
      if (s[2] = 'L') then code72 := 0
      else if (s[2] = 'C') then code72 := 1
      else if (s[2] = 'R') then code72 := 2;
    end
    else if n = 1 then begin
      if (s[1] = 'L') then code72 := 0
      else if (s[1] = 'C') then code72 := 1
      else if (s[1] = 'R') then code72 := 2
      else if (s[1] = 'A') then code72 := 3
      else if (s[1] = 'M') then code72 := 4
      else if (s[1] = 'F') then code72 := 5;
    end;
  end;
  with sl do begin
    Add('  0' + #13#10 + 'TEXT');
    Add('  8' + #13#10 + Lay + #13#10 +
        ' 62' + #13#10 + IntToStr(color));
    Add(' 10' + #13#10 + FloatToStr(pt10.x) + #13#10 + ' 20' + #13#10 + FloatToStr(pt10.y) + #13#10 +
        ' 30' + #13#10 + FloatToStr(pt10.z));
    Add(' 11' + #13#10 + FloatToStr(pt11.x) + #13#10 + ' 21' + #13#10 + FloatToStr(pt11.y) + #13#10 +
        ' 31' + #13#10 + FloatToStr(pt11.z));
    Add('  1' + #13#10 + Txt);
    Add(' 40' + #13#10 + FloatToStr(takasa));
    Add(' 41' + #13#10 + FloatToStr(Haba));
    Add(' 50' + #13#10 + FloatToStr(Kakudo));
    Add(' 72' + #13#10 + IntToStr(code72) + #13#10 +' 73' + #13#10 + IntToStr(code73));
  end;
end;

end.

//-----------------------------------------------------
// 使い方
// ※ Variant 型は、他の CAD 用のプログラムを流用するために追加しています
//   通常は、TAcadPoint を使います
//-----------------------------------------------------

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
  p1, p2 : TAcadPoint;
  DxfName : TFileName;
  vpt1, vpt2 : OleVariant;
  vpts : OleVariant;
  acpts : array of TAcadPoint;
begin
  // 通常はこちら
  // LINE 用テストデータ
  p1.x := 100;
  p1.y := 200;
  p1.z := 0;
  p2.x := 200;
  p2.y := 100;
  p2.z := 0;

  // バリアントを使う場合はこちら
  // LINE 用テストデータ
  // Variant To TAcadPoint の変換が必要
  vpt1 := VarArrayCreate([0, 2], VarDouble);
  vpt2 := vpt1;
  vpt1[0] := 100;
  vpt1[1] := 200;
  vpt1[2] := 0;

  vpt2[0] := 300;
  vpt2[1] := 400;
  vpt2[2] := 0;

  // POLYLINE 用テストデータ
  vpts := VarArrayCreate([0, 7], VarDouble);
  vpts[0] := 100;
  vpts[1] := 100;

  vpts[2] := 200;
  vpts[3] := 100;

  vpts[4] := 200;
  vpts[5] := 50;

  vpts[6] := 100;
  vpts[7] := 50;

  SetLength(acpts, 4);
  // Variant To TAcadPoint の変換
  VptsToAcPts(vpts , acpts);

  sl := TStringList.Create;
  try
   // ヘッダー
    MakeR12DxfHead(sl);
    // TABLESセクションのうちLTYPEだけを追加
    AddR12DxfLtypeDummy(sl);
    // ENTITIES 開始
    AddR12DxfEntHead(sl);
    // LINE を追加
    AddR12DxfLineEnt(sl, V3ToAcPt(vpt1), V3ToAcPt(vpt2), '0', 'BYLAYER', 256);
    // TEXT を追加
    AddR12DxfTextEnt(sl, 'TEST', V3ToAcPt(vpt1), V3ToAcPt(vpt2), 10, 1, 0, 0, 0, 'ML', '0', 256);
    // POLYLINE を追加
    AddR12DxfPolylineEnt(sl, acpts, True, '0', 'BYLAYER', 256);
    // ENDSEC
    AddR12DxfTerm(sl);
    // DXFファイルを追加
    DxfName := ChangeFileExt(ParamStr(0), '.dxf');
    // ファイルに保存
    sl.SaveToFile(DxfName);
    // Memo に表示
    Memo1.Lines.Assign(sl);
  finally
    sl.Free;
  end;
end;