多角形を四角形に変換する

四角形を多角形に変換するコマンドは、デフォルトのメニュー・コマンドにあるのですが、その逆はありません。不可逆変換なのです(笑)。四角形は、中心をスナップすることができる点が便利なので、時々多角形を四角形にわざわざ描き替えたりしていましたが、結構そういう機会があるので、コーディングしてみました。このコマンドは、多角形を四角形に変換するためのものです。 ※オブジェクトタイプが曲線で、頂点数が4の場合(多角形の辺を非表示にしているときなど)にも対応しました。

最近 Vectorworks 2011 にバージョンアップしたのですが、四角形のデータ形式が変わって傾いても四角形というデータタイプを保持できるようになりました。(たぶん 2008 からだと思うのですが・・・考えてみれば、Vectorworks の四角形の定義は、数学上の正方形か長方形のことですよね。)下記がフローです。

  1. 多角形の頂点数が4かどうかをチェック > GetVertNum
  2. 4点の座標を取得 > GetPolyPt
  3. 隣り合う辺のなす角が直角か?3つ調べれば良い > DotProduct でベクトル変数を直接計算
  4. 許容精度を設ける。S=1/50 の図面で、1E-008(0.00000001)程度。
  5. 多角形(曲線)の頂点インデックスは右回りと左回りがあるので、2Dの外積の正負で判定する。
  6. RectangleN で描画する。
  7. 元の多角形の属性を GET し、新たに描画した四角形に SET する。

以下ソースです。注意点として、Ver.2010 以前の場合は、48行目の GetTypeN > GetType に変更してください。

また、プラグインコマンド編集ダイアログのパラメータボタンをクリックして、以下のように登録してください。

  • 名前:RAccuracy
  • フィールド名:許容精度
  • 型:Number
  • 初期値:1E-008(縮尺 1/50程度の場合です。縮尺によって数値を調整してください。


{*******************************************************************************
                ChangeRectangle
                
                多角形を四角形に変換する
                
                Copyright 2010 兵藤善紀建築設計事務所
                www.hyodo-arch.com      2010.04.14

    2011.10.18  Ver 0.21    許容精度の判定に絶対値ABS関数を使う。
    2011.10.03  Ver 0.20    角度属性に対応。
    2010.06.10  Ver 0.13    曲線に対応
    2010.04.14  Ver 0.12    複数図形を選択しているときにも処理させる努力
    2010.04.14  Ver 0.11    クラス属性に対応させる努力
    2010.04.14  Ver 0.10    まず最初のバージョン
 *******************************************************************************}

PROCEDURE ChangeRect;
CONST
    ObjectsOpt          = 2;    {ForEach~のパラメータ。選択図形を操作}
    TraversalOpt        = 1;    {ForEach~のパラメータ。グループ内も操作}
    LayersOpt           = 4;    {ForEach~のパラメータ。編集可能なレイヤを操作}
VAR
    RAccuracy                   :REAL;      {許容精度}
    ObjTypeMess                 :String;    {ダイアログ用メッセージ}
    
{**************************** ChangeRectangle   四角形に変換 ****************************}
FUNCTION ChangeRectangle(hh:handle):boolean;
VAR
    Rhh                         :Handle;    {ハンドル, hh:選択図形、Rhh:新たに描いた四角形}
    NumOfVertex                 :Integer;   {多角形の頂点の数}
    PolyPt:ARRAY[1..4] OF Vector;           {頂点座標用配列}
    RDP:ARRAY[1..4] OF REAL;                {内積格納用配列}
    Raa,Rbb                     :Vector;    {外積を調べるベクトル}
    RRotation                   :REAL;      {外積から多角形の頂点が右回り 左回りを判定}
    
    ii                          :Integer;   {カウンタ}

    hhClass                     :String;    {クラス名}
    hhRed:ARRAY[1..4] OF Longint;           {面と線のRed値}
    hhGreen:ARRAY[1..4] OF Longint;         {面と線のGreen値}
    hhBlue:ARRAY[1..4] OF Longint;          {面と線のBlue値}
    hhFPat                      :Longint;   {模様の番号}
    hhLS, hhLW                  :Integer;   {線の種類。線の太さ}

BEGIN
    NumOfVertex:=GetVertNum(hh);

    IF ((GetTypeN(hh)=5) | (GetTypeN(hh)=21)) AND (NumOfVertex=4) THEN
        Begin
            FOR ii:=1 TO NumOfVertex DO GetPolyPt(hh,ii,PolyPt[ii].x,PolyPt[ii].y);
            RDP[1]:=DotProduct(PolyPt[2]-PolyPt[1],PolyPt[4]-PolyPt[1]);    {内積が 0 なら直角。3つの角を調べる}
            RDP[2]:=DotProduct(PolyPt[3]-PolyPt[2],PolyPt[1]-PolyPt[2]);
            RDP[3]:=DotProduct(PolyPt[4]-PolyPt[3],PolyPt[2]-PolyPt[3]);
    {ObjTypeMess:=Concat('RDP[1] = ',RDP[1],'  RDP[2] = ',RDP[2],'  RDP[3] = ',RDP[3]);
    AlrtDialog(ObjTypeMess);}
            If  (
                (   Abs(RDP[1])0 then RRotation:=1 else RRotation:=-1;
                    RectangleN  (
                                PolyPt[1].x,PolyPt[1].y,
                                Raa.x,Raa.y,
                                Distance(PolyPt[2].x,PolyPt[2].y,PolyPt[1].x,PolyPt[1].y),
                                RRotation*Distance(PolyPt[4].x,PolyPt[4].y,PolyPt[1].x,PolyPt[1].y)
                                );
                    Rhh:=LNewObj;

                    hhClass:=GetClass(hh);                          {クラス名の取得}
                    GetFillFore (hh,hhRed[1],hhGreen[1],hhBlue[1]); {面の色の取得}
                    GetFillBack (hh,hhRed[2],hhGreen[2],hhBlue[2]); {面の地色の取得}
                    GetPenFore  (hh,hhRed[3],hhGreen[3],hhBlue[3]); {線の色の取得}
                    GetPenBack  (hh,hhRed[4],hhGreen[4],hhBlue[4]); {線の地色の取得}
                    hhFPat:=GetFPat(hh);                {模様の取得}
                    hhLS:=GetLS(hh);                    {線の種類の取得}
                    hhLW:=GetLW(hh);                    {線の太さの取得}

                    SetClass(Rhh,hhClass);                              {クラス名の設定}
                    if IsFillColorByClass(hh) then      {******** 面の色 ********}
                        SetFillColorByClass(Rhh)        {クラス属性を使用している場合の設定}
                    else
                        begin                           {クラス属性を使用していない場合の設定}
                            SetFillFore (Rhh,hhRed[1],hhGreen[1],hhBlue[1]);    {面の色の設定}
                            SetFillBack (Rhh,hhRed[2],hhGreen[2],hhBlue[2]);    {面の地色の設定}
                        end;

                    if IsFPatByClass(hh) then           {******** 模様 ********}
                        SetFPatByClass(Rhh)             {クラス属性を使用している場合の設定}
                    else SetFPat(Rhh,hhFPat);           {クラス属性を使用していない場合の設定}

                    if IsPenColorByClass(hh) then       {******** 線の色 ********}
                        SetPenColorByClass(Rhh)         {クラス属性を使用している場合の設定}
                    else
                        begin
                            SetPenFore  (Rhh,hhRed[3],hhGreen[3],hhBlue[3]);    {線の色の設定}
                            SetPenBack  (Rhh,hhRed[4],hhGreen[4],hhBlue[4]);    {線の地色の設定}
                        end;

                    if IsLSByClass(hh) then             {******** 線の種類 ********}
                        SetLSByClass(Rhh)               {クラス属性を使用している場合の設定}
                    else SetLS(Rhh,hhLS);               {クラス属性を使用していない場合の設定}

                    if IsLWByClass(hh) then             {******** 線の太さ ********}
                        SetLWByClass(Rhh)               {クラス属性を使用している場合の設定}
                    else SetLW(Rhh,hhLW);               {クラス属性を使用していない場合の設定}

                    DelObject(hh);                  {元の多角形を削除}
            {ObjTypeMess:=Concat('  hhRed[1] = ',hhRed[1],'  hhRed[2] = ',hhRed[2],'  hhRed[3] = ',hhRed[3]);
            AlrtDialog(ObjTypeMess);}
                end;
        End;
    {ELSE
        Begin
        ObjTypeMess:=Concat('選択されていた図形を四角形に変換することはできませんでした。Object Type = ',Num2StrF(GetType(hh)),' 頂点の数=',NumOfVertex);
        AlrtDialog(ObjTypeMess);
        End;}
END;{****************** ChangeRectangle 四角形に変換 END******************}

BEGIN
    RAccuracy:=PRACCURACY;      {許容誤差。プラグインコマンド編集>パラメータ で 名前:RAccuracy、フィールド名:許容精度、型:Number、初期値:1E-008 としておく。}
    ForEachObjectInLayer(ChangeRectangle,ObjectsOpt,TraversalOpt,LayersOpt);
    {Message('SelectObjLayer=',SelectObjLayer,' SelectObjClass=',SelectObjClass);}
END;
RUN ( ChangeRect );

以下に Ver.12.5 までのソースも置いておきます。


{*******************************************************************************
                ChangeRectangle
                
                多角形を四角形に変換する
                
                Copyright 2010 兵藤善紀建築設計事務所
                www.hyodo-arch.com      2010.04.14

    2010.06.10  Ver 0.13    曲線に対応
    2010.04.14  Ver 0.12    複数図形を選択しているときにも処理させる努力
    2010.04.14  Ver 0.11    クラス属性に対応させる努力
    2010.04.14  Ver 0.10    まず最初のバージョン
 *******************************************************************************}

PROCEDURE ChangeRect;
CONST
    ObjectsOpt          = 2;    {ForEach~のパラメータ。選択図形を操作}
    TraversalOpt        = 1;    {ForEach~のパラメータ。グループ内も操作}
    LayersOpt           = 4;    {ForEach~のパラメータ。編集可能なレイヤを操作}
VAR
    ObjTypeMess                 :String;    {ダイアログ用メッセージ}

{**************************** ChangeRectangle   四角形に変換 ****************************}
FUNCTION ChangeRectangle(hh:handle):boolean;
VAR
    Rhh                         :Handle;    {ハンドル, hh:選択図形、Rhh:新たに描いた四角形}
    NumOfVertex                 :Integer;   {多角形の頂点の数}
    PolyPt:ARRAY[1..4] OF Vector;           {クラスID 用配列}
    ii                          :Integer;   {カウンタ}

    hhClass                     :String;    {クラス名}
    hhRed:ARRAY[1..4] OF Longint;           {面と線のRed値}
    hhGreen:ARRAY[1..4] OF Longint;         {面と線のGreen値}
    hhBlue:ARRAY[1..4] OF Longint;          {面と線のBlue値}
    hhFPat                      :Longint;   {模様の番号}
    hhLS                        :Integer;   {線の種類}
    hhLW                        :Integer;   {線の太さ}

BEGIN
    NumOfVertex:=GetVertNum(hh);

    IF ((GetType(hh)=5) | (GetType(hh)=21)) AND (NumOfVertex=4) THEN
        Begin
            FOR ii:=1 TO NumOfVertex DO GetPolyPt(hh,ii,PolyPt[ii].x,PolyPt[ii].y);
            If  (
                (PolyPt[1].x=PolyPt[4].x) AND (PolyPt[1].y=PolyPt[2].y)
            AND (PolyPt[3].x=PolyPt[2].x) AND (PolyPt[3].y=PolyPt[4].y)
                )
            OR  (
                (PolyPt[1].x=PolyPt[2].x) AND (PolyPt[1].y=PolyPt[4].y)
            AND (PolyPt[3].x=PolyPt[4].x) AND (PolyPt[3].y=PolyPt[2].y)
                )
            Then
                begin
                    Rect(PolyPt[1].x,PolyPt[1].y,PolyPt[3].x,PolyPt[3].y);
                    Rhh:=LNewObj;
                    
                    hhClass:=GetClass(hh);                              {クラス名の取得}
                    
                    GetFillFore (hh,hhRed[1],hhGreen[1],hhBlue[1]); {面の色の取得}
                    GetFillBack (hh,hhRed[2],hhGreen[2],hhBlue[2]); {面の地色の取得}
                    GetPenFore  (hh,hhRed[3],hhGreen[3],hhBlue[3]); {線の色の取得}
                    GetPenBack  (hh,hhRed[4],hhGreen[4],hhBlue[4]); {線の地色の取得}
                    hhFPat:=GetFPat(hh);                {模様の取得}
                    hhLS:=GetLS(hh);                    {線の種類の取得}
                    hhLW:=GetLW(hh);                    {線の太さの取得}

                    SetClass(Rhh,hhClass);                              {クラス名の設定}
                    if IsFillColorByClass(hh) then      {******** 面の色 ********}
                        SetFillColorByClass(Rhh)        {クラス属性を使用している場合の設定}
                    else
                        begin                           {クラス属性を使用していない場合の設定}
                            SetFillFore (Rhh,hhRed[1],hhGreen[1],hhBlue[1]);    {面の色の設定}
                            SetFillBack (Rhh,hhRed[2],hhGreen[2],hhBlue[2]);    {面の地色の設定}
                        end;

                    if IsFPatByClass(hh) then           {******** 模様 ********}
                        SetFPatByClass(Rhh)             {クラス属性を使用している場合の設定}
                    else SetFPat(Rhh,hhFPat);           {クラス属性を使用していない場合の設定}

                    if IsPenColorByClass(hh) then       {******** 線の色 ********}
                        SetPenColorByClass(Rhh)         {クラス属性を使用している場合の設定}
                    else
                        begin
                            SetPenFore  (Rhh,hhRed[3],hhGreen[3],hhBlue[3]);    {線の色の設定}
                            SetPenBack  (Rhh,hhRed[4],hhGreen[4],hhBlue[4]);    {線の地色の設定}
                        end;

                    if IsLSByClass(hh) then             {******** 線の種類 ********}
                        SetLSByClass(Rhh)               {クラス属性を使用している場合の設定}
                    else SetLS(Rhh,hhLS);               {クラス属性を使用していない場合の設定}

                    if IsLWByClass(hh) then             {******** 線の太さ ********}
                        SetLWByClass(Rhh)               {クラス属性を使用している場合の設定}
                    else SetLW(Rhh,hhLW);               {クラス属性を使用していない場合の設定}

                    DelObject(hh);                  {元の多角形を削除}
                end;
        End;
    {ELSE
        Begin
        ObjTypeMess:=Concat('選択されていた図形を四角形に変換することはできませんでした。Object Type = ',Num2StrF(GetType(hh)),' 頂点の数=',NumOfVertex);
        AlrtDialog(ObjTypeMess);
        End;}
END;{****************** ChangeRectangle 四角形に変換 END******************}


BEGIN
    ForEachObjectInLayer(ChangeRectangle,ObjectsOpt,TraversalOpt,LayersOpt);

    {Message('SelectObjLayer=',SelectObjLayer,' SelectObjClass=',SelectObjClass);}

END;
RUN ( ChangeRect );