MATLABを 三角定規やコンパスのように使って 図を描く

## １． はじめに

　みなさん、**図１**や**図２**のような図版が必要になったとき、どのようなツールを使って描いていますか？

**図１**
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/b298e055-3720-4370-8c58-a1931f8f4aa4.png">
**図２**
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/cc5e4e82-d7e9-437b-81b5-b45c4a1cd29a.png">

　私は Jw_cad や Inkscape などを使っていましたが、それほど経験豊富というわけでもなく、その都度、使い方を思い出しながら苦労して描いていました。かなりの手間がかかるので、このような図を描くのはあまり好きではありません。Qiita の記事ネタはあっても、この種の図が必要となると躊躇してしまいます。
 
　MATLABは、計算で得られた数値データを図示するようなときには非常に便利なのですが、図１や図２を描くのはなかなか難しそうです。しかし、使い慣れた MATLAB でこれが可能になればどんなに助かることでしょう。・・・ということで、MATLAB を製図道具のように使える関数群を作ってみました。ローカル関数として、個々の作図のプログラムの中に取り込むには量が多すぎるので、クラス定義として別のファイルにまとめました。図１や図２は、これを使って描いたものです。

（動作環境：　MATLAB R2019a、Toolboxなし、Windows11）

## ２． MATLAB 製図の概要

　このクラスには、次のような関数を含んでいます。ただし、灰色で示したものはクラス内の関数から呼び出して利用するためのもので、ユーザーが直接利用する機会は少ないと思われます。また、青色のものは、ユーザーによる利用はもちろん、クラス内でも利用されているので、プログラム改変時には注意が必要です。  

　ここで、個々の関数の説明文を長々と書いても伝わりにくいと思いますので、**図３**には、これらを使った作図手順を分かりやすい形で図に示しています。
 
=== 描画系 ===  
<font color="#00c">draw_points_xy　　　x,y座標値で指定して点を打つ</font>  
<font color="#00c">draw_points_on_the_line　　　線上の指定位置に点を打つ</font>  
<font color="#00c">draw_polyline_xy　　　x,y座標値で指定して折線を引く</font>  
<font color="#00c">draw_polyline　　　指定点を繋いで折線を引く</font>  
<font color="#00c">draw_sloped_line　　　傾いた直線を引く</font>  
<font color="#00c">draw_parallel_line　　　平行な直線を引く</font>  
<font color="#00c">draw_perpendicular_line　　　垂直な直線を引く</font>  
draw_hatching_lines　　　ハッチング線で埋める  
draw_circle　　　折線で円を描く  
draw_free_curve　　　自由な曲線を引く  
draw_cloud_shaped_line　　　雲形の線を引く  
draw_arrowhead_at_line_tip　　　線の端に矢印を置く  
=== 編集系 ===  
surround_area_by_a_loop　　　領域を補助線で囲む  
combine_lines_into_one　　　途切れた線を１本にまとめる  
change_line_to_broken_one　　　実線を破線にする  
round_the_corner　　　２直線が作る角を丸める  
<font color="#00c">invert_direction_of_path　　　線の方向を反転する</font>  
change_property_of_lines　　　点や線の属性を変える  
<font color="#00c">erase_open_line_partially　　　開曲線の部分消去</font>  
erase_closed_line_partially　　　閉曲線の部分消去  
<font color="#00c">erase_lines_and_points　　　点や線の全体を消去</font>  
<font color="#aaa">intersection_of_lines_xy　　　直線の線分の交点の検出</font>  
<font color="#00c">intersection_of_polylines　　　折線同士の交点の検出</font>  
resize_drawing　　　図の一部のサイズを変える  
rotate_drawing　　　図の一部を回転させる  
copy_and_paste　　　図の一部をコピペする  
=== 取得系 ===  
<font color="#00c">get_handles_from_tagname　　　名称からハンドルを取得</font>  
get_length_between_two_points　　　２点間の直線距離  
<font color="#00c">get_length_of_line　　　折線全体の長さ</font>  
get_polar_angle_of_line　　　直線の傾き角度  
get_angle_between_two_lines　　　２直線の交角を取得  
<font color="#aaa">get_radius_of_curvature　　　円の曲率半径を推定</font>  
=== 補助系 ===  
<font color="#00c">show_all_the_tagnames　　　点や線の名称を図上に表示</font>  
<font color="#aaa">check_duplication_of_tagname　　　名称の重複を確認</font>  
change_tagname　　　点や線の名称を変える  
trim_and_expand_figure_margin　　　図全体の余白調整  
command　　　コマンドの一覧を表示  
help　　　ヘルプの一覧を表示  
pause　　　条件つきの実行休止  
<br>

　それぞれの小さな図の中で、上部に記述しているのが作図のためのコマンドです。これを実行して得られた結果がその下の図になります。コマンドの頭につけている```AA.```については後述します。緑色の矢印で示す順に追っていけば、各コマンドの大まかな機能は分かっていただけると思います。
 
　ご覧の通り、すべての点や線に、P1,P2 とか L1,L2 というような名称を付けて扱っているのがこの関数群の特徴です。この名称は、点や線を形成するLineタイプのobjectに、Tagプロパティとして登録されています。図３の中にはコマンドとしては明示していませんが、どのステップの図でも、作図のあとには```show_all_the_tagnames```コマンドを実行して、これらの点や線の名称が表示されるようにしています（日本語のテキストは説明のために書き加えたものです）。

**図３**
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/1bb16092-c361-48a6-904b-0cc59b5bfc7a.png">
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/17871353-c046-4eed-ae2f-b32a4f0a45cf.png">
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/f1797597-0c13-4035-a82c-60847ae1a2ee.png">
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/3b396d77-2c9f-42d9-b160-1f518b5324a7.png">


　「色々できることは分かったが、こんな長ったらしいコマンドなど覚えられないよ」という声が聞こえてきそうです。ごもっともです。自分でも覚えられません。しかし、ご心配は要りません。**コマンドとしては```AA.command```と```AA.help```だけを覚えておけば大丈夫です**。

　MATLAB のコマンドラインから、```AA.command```と入力すれば、**図４**のようにコマンド一覧が表示されます。この中から使いたいコマンドの行全体をコピーして、自分のプログラムの中に貼り付けます。返り値を使わなければ、頭の部分は削除して、あとは、figure内の点や線の名称を見ながら、適当な引数を上書きすれば一行分が完成します。

**図４**
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/9fd09e91-60ae-4f5e-8da6-40c58f67ee90.png">

　また、```AA.help```と入力すれば、**図５**のようにヘルプ一覧が表示されます。目的のコマンドの行全体をコピーして、最新のコマンドラインに貼り付けて実行すれば、例えば**図６**のようなヘルプの文章が表示されます。

**図５**
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/bf975cd4-e002-4f3e-83e9-1f2635edbc7c.png">

**図６**
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/fa0b18d9-a8c6-4497-8a36-a112813a0107.png">
## ３． 作図の要領

　以下に、作図プログラムのひな型を示しておきます。これに従っておけば、ほぼ効率的な作図ができると思われます。実際にはどのような変数名にしても構いませんが、ここでは、説明上、AA と BB は予約語扱いにしています。AAは、このクラスの関数であることを明示するための代名詞、BBは、後述の条件付き実行休止コマンド```AA.pause```のためのグローバル変数として使用しています。

　下記の「AAクラスの作図コマンドの記述スペース」にコマンドを一行ずつ書き加えて実行させます。その都度、点や線の最新の名称が図上に表示されるので、これを参照しながら、次のコマンドを書き込みます。以下、この作業の繰り返しになります。まだるっこいとお思いでしょうが、これが早道です。

　なお、ハッチングの```AA.draw_hatching_lines```コマンドや、破線化の```AA.change_line_to_broken_one```コマンドは実行に少々時間がかかります。そのうえ、破線化したあとは交点検出などの機能が効かなくなるため、これらを使うのは作図の最後の段階にするのが賢明です。

　描かれる線の色や幅は青色の0.5のみですが、返り値の hL などを利用すれば、```hL.Color='r'```のようにして直ちに変更することはできます。しかし、現実的には、全体の構図が決まってから、最後にまとめて```AA.change_property_of_lines```コマンドで仕上げるほうが効率的だと思われます。

　また、一般の変数名とは異なり、点や線の名称に使用する文字には、記号や漢字も使えますが、次の点に注意してください。

* 「 _ 」と「 ^ 」は、下付き文字や上付き文字の記号とみなされ、図中での名称の表記が崩れます。
* 「 @ 」は、クラス内の関数が一時的な名称を必要としたときに、頭に付加する文字として使われています。ユーザーがこれを使うと、競合によって誤動作の原因になることがあります。
* 「 * 」は、図形をコピペするときに、元図とコピー図の名称が重複しないように、元図側の名称の末尾に自動的に付けられます。ユーザーが不用意に使うと混乱の原因になります。
* 漢字は、MATLABのバージョンによって文字コードが異なるため、予想外の動作が危惧されます。使わない方が賢明です。

```
clear
close all
clear global               % AA.pause を使うときに必要になる。

AA=MATLAB_drafting_tsubolabo001_class;
                           % クラス定義を変数AAへ取り込む。
                           % 以降、各コマンドの前には「AA.」を付ける。
global BB; BB=[0,Inf,1];   % AA.pause の動作条件

figure(1)                  % 好みにもよるが、この程度の条件設定が便利。
hold on; grid on; axis equal; 
% axis([0 10 0 10]);       % 図の大きさの見通しがつくまではコメント化

% ========= 
% 
% 　　ここは、「AAクラスの作図コマンドの記述スペース」。
% 　　　ここに一行分を加えるたびに、プログラムを再実行する。
% 
% ========= 

AA.show_all_the_tagnames(gca);   % 図中に、点や線の名称を表示。
                                 % 上記の一通りの作図が終われば、
                                 % 名称表示は邪魔なのでコメント化する。

% ========= 
% 
% 　　ここでは、上記の作業が一通り完了したあと、 
% 　　　　　　MATLAB のオリジナルコマンドを使って図を仕上げる。
% 　　　　　　　　　　（テキストの書き込みなど）
% 
% ========= 

% === 好みにより画像の調整 ===
% AA.trim_and_expand_figure_margin (gcf,[0 0 0 0]);
% axis off
% set(gcf,'Color','w');

% === 好みにより画像ファイル化 ===
% basename=mfilename('fullpath');
% print(gcf,'-dpng',[basename '.png'],'-r300');
```
## ４． レビューの要領

　以上で、図としては完成しますが、実は困った問題があります。出来上がった作図プログラムをあとで解読しようとしても、[プログラム中の点や線の名称] と [図の中の実際の位置] との対応づけが困難で、可読性があまり良くありません。そこで、```AA.pause```コマンドの出番になります。

　```AA.pause```は、プログラムの実行を条件付きで一時休止できるコマンドです。休止時には、その時点で最新の点や線の全名称が図中に表示されるとともに、コマンドラインには、休止中のプログラムの行番が表示されます。この内容を確認後、キーボードの任意のキーをクリックすれば次のステップに進みます。

　この```AA.pause```を、プログラムの作図コマンドの全末尾に書き込んでおき、[図中の点や線の名称] と [表示されたプログラムの行番] を対比しながらプログラムの実行を進めれば、１ステップごとの作図経過を確認できるようになり、可読性が向上します。

　一時休止の条件は、グローバル変数 BB を使って次のように設定します。  

```BB=[n1,n2,n3];```
* n1: 　正の整数。n1 個目の```AA.pause```コマンドまでは、一切停止せずに直行し、そこで初めて休止します。ただし、n1=0 の場合は、すべての```AA.pause```が無視されます。
* n2: 　正の整数。n1 で一時休止したあと、残り n2 個分の```AA.pause```だけは受け付けられますが、そこでプログラムの実行は完全に停止します。最後まで実行させたいときには、残りの```AA.pause```コマンドの総数よりも大きな値を指定します。Inf でも結構です。
* n3: 　1 のとき、休止すれば点や線の名称を表示します。0 のとき、一時休止しても名称は表示しません。

　例えば、```BB=[1,Inf,1];```とすれば、全```AA.pause```の位置でプログラム実行が一時停止し、点や線の名称が表示されます。

　```AA.pause```コマンドは、レビューするときに追加するのではなく、作図プログラム完成時点から挿入しておくことを推奨します。添付している図１や図２の作図用プログラムには、全ての主要位置にこの```AA.pause```コマンドが入っています。```BB```を種々に書き換えて実際に動作させてみれば、その効果を実感できます。
## ５． おわりに
　自分で言うのもなんですが、なかなか便利なツールができました。類似図を描くときには、古いプログラムの再利用もできそうです。便利に御利用いただければ光栄です。ただし、バグ出しは十分に済んでおりません。不測の動作があってもご容赦ください。その場合にはコメントをいただけると助かります。

　このあとの改良作業として、電気電子部品図の整備と電気回路図への適用、三次元図への拡張と裸眼立体視図への応用など、色々とアイデアが沸いてきます。一方、ここで紹介したクラスのままでは機能不足を感じ、「俺ならこうする」とお思いの方も多く居られそうです。その場合にはお好きなように改良してください。ただし、同一クラス名で混乱が起こらないように、独自のファイル名にして、バージョン管理も十分にお願いします。また、コマンド一覧に青色で示したものについては、改変の影響が他にも及ぶ可能性があるのでご注意ください。

　最後に、MATLAB 様への要望を２点あげておきます。
* 線の端を丸めるプロパティがあると便利です。
MATLAB にも LineJoin プロパティはあり、デフォルトで 'round'になっています。しかし、これは単独の線の端ではなく、折線が作る角の形のことです。SVG の stoke-linecap="round" のような、一本の線の端の形を丸くする機能があれば便利です。この記事のクラスで作る破線は、線幅を太くすると、各線分の端部が鋭利でトゲトゲしくなり温かみがありません。
* 利用できるLaTeX文字を増やしていただきたい。
ここまでの図が描けるとなると、数式表示の自由度も上げたくなります。今のところ、```\boldsymbol```フォントが使えないのは残念です。
## ６． プログラム
:::note warn
ここには、MATLAB R2019a 用に作成した Shift_JIS 形式のプログラムを貼り付けています。MATLABの最近のバージョンではUTF-8が標準のようですので、そのままでは文字化けが起こるかもしれません。また、一部のコマンドの書き方は形式が古いような気もします（clear globalなど）。問題がある場合には適宜対応ください。
:::
### 図１の作図用プログラム
```matlab
% MATLAB_drafting06.m

% MATLAB_draftingクラスの利用例（球面レンズの近軸近似）

clear
close all
clear global

AA=MATLAB_drafting_tsubolabo001_class;     % クラスファイルの取り込み
global BB; BB=[0,Inf,1];

figure(1)
hold on; grid on; axis equal; 
axis([-0.5 10 -2.2 4.5]);

AA.draw_points_xy(gca,[0 0;5 0;9.5 0],{'P1' 'P2' 'P3'}); AA.pause;

AA.draw_polyline(gca,{'P1' 'P2' 'P3'},'L0'); AA.pause;
AA.draw_circle(gca,4,'P2','Lc'); AA.pause;
AA.draw_sloped_line(gca,'P2',140,0,6,'L1','','Pa'); AA.pause;
AA.draw_sloped_line(gca,'P2',120,0,5,'L2','','Pb'); AA.pause;

AA.intersection_of_polylines(gca,'Lc','L1','Pax'); AA.pause;
AA.intersection_of_polylines(gca,'Lc','L2','Pbx'); AA.pause;
AA.intersection_of_polylines(gca,'Lc','L0',{'P1x' 'P3x'}); AA.pause;
AA.erase_closed_line_partially ...
                            (gca,'Lc','P1x','Pbx','ascend'); AA.pause;
AA.erase_lines_and_points(gca,{'L2' 'Pb' 'Pbx' 'P3x'}); AA.pause;

AA.draw_sloped_line(gca,'Pax',-20,0,9,'L4','','P4e'); AA.pause;
AA.draw_sloped_line(gca,'Pax',-20,-2,0,'L5','P5b',''); AA.pause;
AA.draw_sloped_line(gca,'Pax',-27,0,8,'L3','','P3e'); AA.pause;
AA.erase_open_line_partially(gca,'L1','Pax','Pax','L6'); AA.pause;

AA.intersection_of_polylines(gca,'L3','L0','P6'); AA.pause;
AA.intersection_of_polylines(gca,'L4','L0','P7'); AA.pause;
AA.erase_open_line_partially(gca,'L3','P6','P3e',''); AA.pause;
AA.erase_open_line_partially(gca,'L4','P7','P4e',''); AA.pause;
AA.erase_lines_and_points(gca,{'P3e' 'P4e'}); AA.pause;

AA.draw_circle(gca,1.1,'Pax','Lang1'); AA.pause;

AA.intersection_of_polylines(gca,'Lang1','L5','P8'); AA.pause;
AA.intersection_of_polylines(gca,'Lang1','L6','P9'); AA.pause;
AA.intersection_of_polylines(gca,'Lang1','L1','P10'); AA.pause;
AA.intersection_of_polylines(gca,'Lang1','L4','P11'); AA.pause;

AA.erase_closed_line_partially ...
                        (gca,'Lang1','P8','P10','ascend'); AA.pause;
AA.erase_open_line_partially ...
                        (gca,'Lang1','P9','P11','Lang2'); AA.pause;

AA.draw_circle(gca,1.8,'Pax','Lang3'); AA.pause;
AA.intersection_of_polylines(gca,'Lang3','L1','P12'); AA.pause;
AA.intersection_of_polylines(gca,'Lang3','L3','P13'); AA.pause;
AA.erase_closed_line_partially ...
                        (gca,'Lang3','P12','P13','descend'); AA.pause;
AA.erase_lines_and_points(gca,{'P12' 'P13'}); AA.pause;

AA.draw_circle(gca,0.65,'P2','Lang4'); AA.pause;
AA.intersection_of_polylines ...
                           (gca,'Lang4','L0',{'P14' 'P15'}); AA.pause;
AA.intersection_of_polylines ...
                           (gca,'Lang4','L1',{'P16' 'P17'}); AA.pause;
AA.erase_closed_line_partially ...
                         (gca,'Lang4','P14','P16','ascend'); AA.pause;
AA.erase_lines_and_points(gca,{'P14' 'P15' 'P16'}); AA.pause;

AA.draw_circle(gca,0.85,'P6','Lang5'); AA.pause;
AA.intersection_of_polylines ...
                           (gca,'Lang5','L0',{'P14' 'P15'}); AA.pause;
AA.intersection_of_polylines ...
                           (gca,'Lang5','L3',{'P16' 'P17'}); AA.pause;
AA.erase_closed_line_partially ...
                         (gca,'Lang5','P14','P16','ascend'); AA.pause;
AA.erase_lines_and_points(gca,{'P14' 'P15' 'P16'}); AA.pause;

AA.draw_circle(gca,1.1,'P7','Lang6'); AA.pause;
AA.intersection_of_polylines ...
                           (gca,'Lang6','L0',{'P14' 'P15'}); AA.pause;
AA.intersection_of_polylines ...
                           (gca,'Lang6','L4',{'P16' 'P17'}); AA.pause;
AA.erase_closed_line_partially ...
                         (gca,'Lang6','P14','P16','ascend'); AA.pause;
AA.erase_lines_and_points(gca,{'P14' 'P15' 'P16'}); AA.pause;

AA.draw_perpendicular_line ...
                  (gca,'L0','Pax',-3,0,'Lh','Phx','#dummy'); AA.pause;
AA.intersection_of_polylines(gca,'L0','Lh','Phy'); AA.pause;
AA.erase_open_line_partially(gca,'Lh','Phx','Phy','#dummy'); AA.pause;
AA.draw_arrowhead_at_line_tip(gca,'Lh',1,30,0.2,'Vh1'); AA.pause;
AA.draw_arrowhead_at_line_tip(gca,'Lh',2,30,0.2,'Vh2'); AA.pause;
AA.draw_points_on_the_line(gca,'Lh',[0.8 1.4],{'Pha' 'Phb'});
AA.erase_open_line_partially(gca,'Lh','Pha','Phb','Lhx'); AA.pause;
AA.erase_lines_and_points ...
 (gca,{'Phx' 'Phy' 'Pha' 'Phb' 'P11' 'P1' 'P3' 'Pa' 'P5b'}); AA.pause;

AA.erase_lines_and_points(gca,{'P8' 'P9' 'P10' 'P11'}); AA.pause;
AA.change_property_of_lines ...
                          (gca,'LineWidth',1.5,{'L5' 'L3'}); AA.pause;

AA.draw_perpendicular_line ...
               (gca,'L0','P1x',0,-1.7,'L10','#dummy','P14'); AA.pause;
AA.draw_perpendicular_line ...
                (gca,'L0','P2',0,-0.7,'L11','#dummy','P15'); AA.pause;
AA.draw_perpendicular_line ...
                (gca,'L0','P6',0,-1.2,'L12','#dummy','P16'); AA.pause;
AA.draw_perpendicular_line ...
                (gca,'L0','P7',0,-1.7,'L13','#dummy','P17'); AA.pause;

AA.draw_points_on_the_line ...
              (gca,'L10',[0.5 1.0 1.5],{'P18' 'P19' 'P20'}); AA.pause;
AA.draw_points_on_the_line(gca,'L11',0.5,'P21'); AA.pause;
AA.draw_points_on_the_line(gca,'L12',1.0,'P22'); AA.pause;
AA.draw_points_on_the_line(gca,'L13',1.5,'P23'); AA.pause;

AA.draw_polyline(gca,{'P18' 'P21'},'L14'); AA.pause;
AA.draw_polyline(gca,{'P19' 'P22'},'L15'); AA.pause;
AA.draw_polyline(gca,{'P20' 'P23'},'L16'); AA.pause;

AA.draw_arrowhead_at_line_tip(gca,'L14',1,30,0.2,'V1'); AA.pause;
AA.draw_arrowhead_at_line_tip(gca,'L14',2,30,0.2,'V2'); AA.pause;
AA.draw_arrowhead_at_line_tip(gca,'L15',1,30,0.2,'V3'); AA.pause;
AA.draw_arrowhead_at_line_tip(gca,'L15',2,30,0.2,'V4'); AA.pause;
AA.draw_arrowhead_at_line_tip(gca,'L16',1,30,0.2,'V5'); AA.pause;
AA.draw_arrowhead_at_line_tip(gca,'L16',2,30,0.2,'V6'); AA.pause;

AA.draw_points_on_the_line ...
                       (gca,'L14',[1.7 2.3],{'P30' 'P31'}); AA.pause;
AA.erase_open_line_partially(gca,'L14','P30','P31','L14x'); AA.pause;
AA.erase_lines_and_points(gca,{'P30' 'P31'}); AA.pause;

AA.draw_points_on_the_line ...
                       (gca,'L15',[2.7 3.3],{'P30' 'P31'}); AA.pause;
AA.erase_open_line_partially(gca,'L15','P30','P31','L15x'); AA.pause;
AA.erase_lines_and_points(gca,{'P30' 'P31'}); AA.pause;

AA.draw_points_on_the_line ...
                       (gca,'L16',[3.7 4.3],{'P30' 'P31'}); AA.pause;
AA.erase_open_line_partially(gca,'L16','P30','P31','L16x'); AA.pause;
AA.erase_lines_and_points(gca,{'P30' 'P31'}); AA.pause;

AA.erase_lines_and_points(gca, ...
      {'P14' 'P15' 'P16' 'P17' 'P18' 'P19' ...
                  'P20' 'P21' 'P22' 'P23'}); AA.pause;

AA.change_line_to_broken_one(gca,'L0',[10 -1 1 -1],1); AA.pause;
AA.change_property_of_lines(gca,'LineWidth',1,'L0'); AA.pause;

AA.combine_lines_into_one(gca,{'L1' 'L6'}); AA.pause;
AA.change_line_to_broken_one(gca,'L1',[10 -1 1 -1],0.6); AA.pause;

AA.change_property_of_lines(gca,'Color','r','Lc'); AA.pause;
AA.change_property_of_lines(gca,'LineWidth',1,'Lc'); AA.pause;

%AA.show_all_the_tagnames(gca);

text(1.9,2.95,'$$\mathrm{A}$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(0.8,0.25,'$$\mathrm{S}$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(5.0,0.3,'$$\mathrm{O}$$','FontSize',14,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(7.05,0.25,'$$\mathrm{P}''$$','FontSize',15, ...
     'Interpreter','latex','HorizontalAlign','center', ...
     'VerticalAlign','middle'); AA.pause;
text(9.0,0.25,'$$\mathrm{P}$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(0.8,3.2,'$$i$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(3.1,2.4,'$$i$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(3.7,1.3,'$$i''$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(4.15,0.3,'$$\varphi$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(5.9,0.2,'$$u''$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(7.7,0.2,'$$u$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(3,-0.5,'$$r$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(4.1,-1,'$$s''$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(5,-1.5,'$$s$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(1.95,1.05,'$$h$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(2.5,3.5,'$$n$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(3,3.1,'$$n''$$','FontSize',15,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;

% 画像ファイル化の前処理
AA.trim_and_expand_figure_margin (gcf,[-70 -80 -70 -50]); AA.pause;
axis off; AA.pause;
set(gcf,'Color','w'); AA.pause;

% figureを画像ファイル化
basename=mfilename('fullpath');
print(gcf,'-dpng',[basename '.png'],'-r300');
```
### 図２の作図用プログラム
```matlab
% MATLAB_drafting04.m

% MATLAB_draftingクラスの利用例（ピタゴラスの定理）

clear
close all
clear global

AA=MATLAB_drafting_tsubolabo001_class;     % クラスファイルの取り込み
global BB; BB=[0,Inf,1];

figure(1)
hold on; grid on; axis equal; 
axis([-2 5 -3.5 4]);

AA.draw_points_xy(gca,[0 0;3 0],{'P1' 'P2'}); AA.pause;
AA.draw_polyline(gca,{'P1' 'P2'},'L12'); AA.pause;
AA.draw_sloped_line(gca,'P1',30,0,4,'L13','@Pa1','@Pa2'); AA.pause;

AA.draw_perpendicular_line ...
                (gca,'L13','P2',0,4,'L23','@Pa3','@Pa4'); AA.pause;
AA.intersection_of_polylines(gca,'L13','L23',{'P3'}); AA.pause;
AA.erase_open_line_partially(gca,'L13','P3','@Pa2','@L1'); AA.pause;
AA.erase_open_line_partially(gca,'L23','P3','@Pa4','@L1'); AA.pause;
delete(AA.get_handles_from_tagname(gca,{'@Pa2' '@Pa4'})); AA.pause;

% 三角形の辺の向きは、時計回りに統一した方が便利なので、
% 　一部の辺の向きを反転する。
AA.invert_direction_of_path(gca,'L12'); AA.pause;
AA.invert_direction_of_path(gca,'L23'); AA.pause;

% a辺回りの四角形
La=AA.get_length_between_two_points(gca,'P2','P3'); AA.pause;
AA.draw_perpendicular_line ...
                  (gca,'L23','P3',0,La,'La3','@Pa1','P4'); AA.pause;
AA.draw_perpendicular_line ...
                  (gca,'L23','P2',0,La,'La2','@Pa2','P5'); AA.pause;
AA.draw_polyline(gca,{'P4' 'P5'},'L45'); AA.pause;

Lb=AA.get_length_between_two_points(gca,'P1','P3'); AA.pause;
AA.draw_perpendicular_line ...
                  (gca,'L13','P1',0,Lb,'Lb1','@Pa1','P6'); AA.pause;
AA.draw_perpendicular_line ...
                  (gca,'L13','P3',0,Lb,'Lb3','@Pa2','P7'); AA.pause;
AA.draw_polyline(gca,{'P6' 'P7'},'L67'); AA.pause;

Lc=AA.get_length_between_two_points(gca,'P1','P2'); AA.pause;
AA.draw_perpendicular_line ...
                  (gca,'L12','P2',0,Lc,'Lc2','@Pa1','P8'); AA.pause;
AA.draw_perpendicular_line ...
                  (gca,'L12','P1',0,Lc,'Lc1','@Pa2','P9'); AA.pause;
AA.draw_polyline(gca,{'P8' 'P9'},'L89'); AA.pause;

AA.draw_circle(gca,0.6,'P3','Lang'); AA.pause;
AA.intersection_of_polylines(gca,'Lang','L13','P1ang'); AA.pause;
AA.intersection_of_polylines(gca,'Lang','L23','P2ang'); AA.pause;
AA.erase_closed_line_partially ...
                  (gca,'Lang','P1ang','P2ang','descend'); AA.pause;

AA.draw_arrowhead_at_line_tip(gca,'Lang',1,30,0.25,'V1'); AA.pause;
AA.draw_arrowhead_at_line_tip(gca,'Lang',2,30,0.25,'V2'); AA.pause;

hloop=AA.surround_area_by_a_loop ...
                  (gca,{'L12' 'Lc2' 'L89' 'Lc1'},'@Lx1'); AA.pause;
AA.draw_hatching_lines(gca,'@Lx1',0.2,60,'H1'); AA.pause;
delete(hloop); AA.pause;

hloop=AA.surround_area_by_a_loop ...
                  (gca,{'L23' 'La2' 'L45' 'La3'},'@Lx2'); AA.pause;
AA.draw_hatching_lines(gca,'@Lx2',0.2,60,'H2'); AA.pause;
delete(hloop); AA.pause;

hloop=AA.surround_area_by_a_loop ...
                  (gca,{'L13' 'Lb3' 'L67' 'Lb1'},'@Lx3'); AA.pause;
AA.draw_hatching_lines(gca,'@Lx3',0.2,60,'H3'); AA.pause;
delete(hloop); AA.pause;

AA.change_property_of_lines ...
                   (gca,'Color','#aaa',{'H1' 'H2' 'H3'}); AA.pause;
AA.change_property_of_lines ...
               (gca,'LineWidth',1.5,{'L12' 'L13' 'L23'}); AA.pause;

h=AA.get_handles_from_tagname ...
               (gca,{'P1' 'P2' 'P3' 'P4' 'P5' 'P6' ...
                     'P7' 'P8' 'P9' 'P1ang' 'P2ang'}); AA.pause;
delete(h); AA.pause;

h1=AA.draw_points_xy(gca,[3 3],'Pa'); AA.pause;
h2=AA.draw_points_xy(gca,[0.4 -1.45],'Px'); AA.pause;
h3=AA.draw_circle(gca,1,'Pa','La'); AA.pause;
AA.resize_drawing(gca,'Pa',[1.8,0.8],'La'); AA.pause;
AA.invert_direction_of_path(gca,'La'); AA.pause;
h4=AA.draw_cloud_shaped_line(gca,'La',20,1.05,'Lz'); AA.pause;
h=AA.copy_and_paste(gca,'Pa','Px','Lz'); AA.pause;
h.Color='r'; AA.pause;
h.LineWidth=1.5; AA.pause;
delete([h1 h2 h3 h4]); AA.pause;

fill(h.XData,h.YData,'w','EdgeColor','none', ...
                                     'FaceAlpha',0.7); AA.pause;

%hTxt=AA.show_all_the_tagnames(gca);

text(2.8,0.8,'$$a$$','FontSize',16,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(1.05,0.85,'$$b$$','FontSize',16,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(1.5,-0.2,'$$c$$','FontSize',16,'Interpreter','latex', ...
     'HorizontalAlign','center','VerticalAlign','middle'); AA.pause;
text(2.05,0.45,'$$90^{\circ}$$','FontSize',13, ...
               'Interpreter','latex','HorizontalAlign','center', ...
               'VerticalAlign','middle'); AA.pause;
text(0.4,-1.5,'$$a^2+b^2=c^2$$','FontSize',16, ...
               'Interpreter','latex','HorizontalAlign','center', ...
               'VerticalAlign','middle'); AA.pause;

% 画像ファイル化の前処理
AA.trim_and_expand_figure_margin (gcf,[-30 -40 -60 -30]); AA.pause;
axis off; AA.pause;
set(gcf,'Color','w'); AA.pause;

% figureを画像ファイル化
basename=mfilename('fullpath');
print(gcf,'-dpng',[basename '.png'],'-r300');
```
### MATLAB 製図 のクラス定義プログラム
約2500行あります。

<details><summary>折り畳んであります。</summary>

```matlab
% MATLAB_drafting_tsubolabo001_class.m

% MATLAB をアナログ製図道具のように利用できる関数群から成るクラスの定義
% 
% コマンドの目次（以下のコードはこの順で記述している）
% 
% 返り値　　　　コマンド　　　　　　　　　　　　引数
% hPs=          draw_points_xy                 (obj,ha,xys,tagPcell)
% hPs=          draw_points_on_the_line        (obj,ha,tagL,lengths,tagPcell,option)
% hLPs=         draw_polyline_xy               (obj,ha,x,y,tagL,tagPb,tagPe)
% hL=           draw_polyline                  (obj,ha,tagPcell,tagL)
% hLPs=         draw_sloped_line               (obj,ha,tagPo,ang,len1,len2,tagL,tagP1,tagP2)
% hLPs=         draw_parallel_line             (obj,ha,tagLo,tagPo,len1,len2,tagL,tagP1,tagP2)
% hLPs=         draw_perpendicular_line        (obj,ha,tagLo,tagPo,len1,len2,tagL,tagP1,tagP2)
% hL=           draw_hatching_lines            (obj,ha,tagLc,pitch,ang,tagHL)
% hL=           draw_circle                    (obj,ha,r,tagPc,tagL,nagon,ang)
% hL=           draw_free_curve                (obj,ha,tagPcell,angs,tagL)
% hL=           draw_cloud_shaped_line         (obj,ha,tagL0,ndiv,kr,tagLc,ang)
% hA=           draw_arrowhead_at_line_tip     (obj,ha,tagL,pos,ang,leng,tagV,dang,Ropt)
% hL=           surround_area_by_a_loop        (obj,ha,tagLcell,tagL)
% hL=           combine_lines_into_one         (obj,ha,tagLcell)
% hL=           change_line_to_broken_one      (obj,ha,tagL,pattern,pitch,pos0)
% hRPs=         round_the_corner               (obj,ha,r,tagP,tagL1,tagL2,tagR,tagPcell)
% hL=           invert_direction_of_path       (obj,ha,tagL)
%               change_property_of_lines       (obj,ha,prop,var,tagLPcell)
% hLs=          erase_open_line_partially      (obj,ha,tagL,tagP1,tagP2,tagL2)
% hL=           erase_closed_line_partially    (obj,ha,tagL,tagP1,tagP2,direction)
%               erase_lines_and_points         (obj,ha,tagLPcell)
% [x,y,k1,k2]=  intersection_of_lines_xy       (obj,XD1,YD1,XD2,YD2)
% hPs=          intersection_of_polylines      (obj,ha,tagL1,tagL2,tagPcell,option)
% hLPs=         resize_drawing                 (obj,ha,tagP0,kxy,tagLPcell)
% hLPs=         rotate_drawing                 (obj,ha,tagP0,ang,tagLPcell)
% hLPs=         copy_and_paste                 (obj,ha,tagP0,tagP1,tagLPcell)
% hLPs=         get_handles_from_tagname       (obj,ha,tagLPcell,option)
% len=          get_length_between_two_points  (obj,ha,tagP1,tagP2)
% len=          get_length_of_line             (obj,ha,tagL)
% ang=          get_polar_angle_of_line        (obj,ha,tagL)
% ang=          get_angle_between_two_lines    (obj,ha,tagL1,tagL2)
% [R,ang]=      get_radius_of_curvature        (obj,ha,tagL)
% hTs=          show_all_the_tagnames          (obj,ha)
%               check_duplication_of_tagname   (obj,ha,tagLPcell)
%               change_tagname                 (obj,ha,tagLPcell_old,tagLPcell_new)
%               trim_and_expand_figure_margin  (obj,hf,KK)
%               command                        (obj)
%               help                           (obj)
%               pause                          (obj)
% 
% 図の中の点や線に付けた「名称」（char型）は、点や線の「Tag」プロパ
% 　ティの値として登録され、識別子として利用される。
% 　描画時に点や線のハンドルは取得できるが、十分に注意しないと、点や線
%   との対応づけが曖昧になってくるので、永続的な識別子として使うのには
% 　適当ではない。
% 
% １つの図の中で、異なる点や線に対して同一の「名称」を使うと、異常動作
% 　の原因になるので注意すること。エラー警告を出すようにはしているが、
% 　完璧ではない。
% 　
% 「名称」はchar型なので、変数名とは異なり記号や漢字も含めることができ
% 　る。しかし、下記は使用しないこと。「"#"」や「"&"」は良さそう。
% 　　「"_" と "^"」は、下付き文字や上付き文字の記号とみなされ、図中の
% 　　　　「名称」表記が崩れる。
% 　　「"@"」は、クラス内の関数が一時的な「名称」を必要としたときに、
% 　　　　頭に付加する文字として使われている。ユーザーがこれを使うと、
% 　　　　競合によって誤動作の原因になることがある。
% 　　「"*"」は、図形をコピペするときに、元図とコピー図の「名称」が
% 　　　　重複しないように、元図側の「名称」の末尾に自動的に付けられ
% 　　　　る。ユーザーが不用意に使うと混乱の原因になる。
% 　　「漢字」は、MATLABのバージョンによって文字コードが異なるので、
% 　　　　予想外の動作が危惧される。使わない方が良い。
% 
% 変数名「AA」や「BB」は、このクラスで特殊用途として使用しているので、
% 　ユーザーとしての利用は避けること（予約語の扱い）。



classdef MATLAB_drafting_tsubolabo001_class

  properties
    % このクラスでは、この部分は使わない。
  end

  methods

    % ■■■ 以下、同一カラムの end まで、製図用の関数群 ■■■

    % 関数定義の引数群の "( )" の中で、最初に「obj,」を記述している
    % 　のは、お決まりの作法なのであまり深く考える必要は無い。
    % 　これは正規の引数ではない。他と重複しなければ、"obj" 以外の
    % 　名前でも構わないが、あえて変更する利点もない。
    % 
    % メインプログラムから、このクラスの中の関数を呼び出すときには、
    % 　例えば、AA = MATLAB_drafting_tsubolabo001_class; などとして、
    % 　ここのクラス名を短い名前に置き換えたうえ、
    % 
    % 　『... = AA.関数名(「obj,」部分を除いた引数群)』
    % 　あるいは、
    % 　『... = 関数名( AA,「obj,」部分を除いた引数群)』
    % 　とする。
    % 
    % この classdef 内にある関数同士で、他の関数を呼び出すときには、
    % 
    % 　『... = obj.関数名(「obj,」部分を除いた引数群))』
    % 　あるいは、
    % 　『... = 関数名(obj,「obj,」部分を除いた引数群))』
    % 　とする。
    % 
    % なお、メインプログラムの中にあったローカル関数を、クラス内に移動
    % 　して書き直す場合、上記の「obj,」の追加以外にも、若干の変更を
    % 　要する。
    % 　例えば、nargin の数値は、引数の「obj」が追加されるため、
    % 　1つだけ大きくなることに注意が必要。


    % ====================================
    % xy座標値に従って点群を描画し、各点に名称を付けてハンドルを取得
    % ====================================

    function hPs=draw_points_xy(obj,ha,xys,tagPcell)

      % xy座標値に従って点群を描画し、各点に名称を付けてハンドルを取得
      % 【入力】
      % ha:       現axesのハンドル
      % xys:      各点のx,y座標値（[x1 y1;x2 y2;x3 y3;...] の形式）
      % tagPcell: 各点に付けたい名称を並べたchar型のセル配列。
      %           　（{ '名称1' '名称2' '名称3' ... } の形式）
      %           　１点だけの場合は、{ }で囲まなくても可。
      % 【出力】
      % hPs:      描いた点のハンドル。点の数だけの要素を持つ配列

      if ~iscell(tagPcell)
        tagPcell={tagPcell};
      end

      obj.check_duplication_of_tagname(ha,tagPcell);

      for n=1:size(xys,1)
        hPs(n)=plot(ha,xys(n,1),xys(n,2),'.','Color','#888','MarkerSize',16);
        hold on
        hPs(n).Tag=tagPcell{n};
      end
    
    end

    % ====================================
    % 指定線上で、始点からの指定長さの各位置に点群を描く。
    % 　これらの各位置は、元の線の新たな折れ点要素としても追加される。
    % ====================================

    function hPs=draw_points_on_the_line(obj,ha,tagL,lengths,tagPcell,option)

      % 指定線上で、始点からの指定長さの各位置に点群を描く。
      % 　これらの各位置は、元の線の新たな折れ点要素としても追加される。
      % 【入力】
      % ha:         現axesのハンドル
      % tagL:       対象とする線の名称（char型）
      % lengths:    始点からそれぞれの点までの長さの数値を並べた
      %             　行ベクトル。これらの数値は正数のみが許される。
      %               0以下や線長以上の数値が含まれるとエラーとなる。
      % tagPcell:   各点に付けたい名称を並べたchar型のセル配列。
      %           　（{ '名称1' '名称2' '名称3' ... } の形式）
      %           　１点だけの場合は、{ }で囲まなくても可。
      % 【入力（任意）】
      % option:    隠し引数。0を入力すると、元の線への折れ点の挿入が
      %            　禁止される。
      % 【出力】
      % hPs:        描かれた点群のハンドル（点の数だけの長さの配列）

      if ~iscell(tagPcell)
        tagPcell={tagPcell};
      end

      obj.check_duplication_of_tagname(ha,tagPcell);

      h_line=obj.get_handles_from_tagname(ha,tagL);
      X=h_line.XData;
      Y=h_line.YData;

      % 隣り合う折れ点の間の区間長。
      % 　dX,dY,dLの第n要素は、X,Y,Lの第n要素と第n-1要素間の距離。
      % 　ただし、dX,dY,dLの第1要素は0。

      dX=diff(X);
      dX=[0 dX];
      dY=diff(Y);
      dY=[0 dY];
      dL=hypot(dX,dY);       % 区間長

      % 各折れ点の、始点からの長さ。始点L(1)は0、終点L(end)は線長。
      L=cumsum(dL);

      judge=(lengths<=0 | lengths>=max(L));
                             % lengthsの規定値外の要素は1、
                             % 規定値内の要素は0となる。
      if sum(judge)~=0
        disp([newline 'lengthsの各要素の値vnは、' ...
              '0<vn<線長 とすること。' newline])
        error('　')
      end

      % 以降、X,Y,Lの要素数は上記の初期値のままではなく、
      % 　処理が進むにつれて増加していくので要注意。

      hPs=[];

      for n=1:length(lengths)
        difL=L-lengths(n);
        nn=min(find(difL>0));    % 指定点は、指定線の折れ点番号が
                                 % 　nn-1とnnの間の区間に存在する。
                                 % 　（条件より、nnは最低でも2）
        span=L(nn)-L(nn-1);      % 存在区間の長さ。条件より、span≠0。
        kk=1-difL(nn)/span;      % 存在区間の長さを1.0として、指定点は
                                 % 　若番側の端からkkの位置にある。
        % 指定点の座標
        xadd=X(nn-1)+kk*(X(nn)-X(nn-1));
        yadd=Y(nn-1)+kk*(Y(nn)-Y(nn-1));

        % 指定点を描画
        h=obj.draw_points_xy(ha,[xadd yadd],tagPcell{n});

        hPs=[hPs h];
        
        % 指定点と同位置に、指定線の折れ点も追加する準備。
        L=[L(1:nn-1) lengths(n) L(nn:end)];
        X=[X(1:nn-1) xadd X(nn:end)];
        Y=[Y(1:nn-1) yadd Y(nn:end)];
      end

      % 指定点を、元の線の折点としても追加（禁止されなければ）
      if (nargin>=6)&(option==0)
        % 折れ点の追加はしない。
      else
        h_line.XData=X;
        h_line.YData=Y;
      end

    end

    % ====================================
    % x,y座標値をもとに一本の折線を描画し、
    % 　その線全体,始点,終点の名前を命名し、ハンドルも取得
    % ====================================
    
    function hLPs=draw_polyline_xy(obj,ha,x,y,tagL,tagPb,tagPe)

      % x,y座標値をもとに一本の折線を描画し、
      % 　その線全体,始点,終点の名前を命名し、ハンドルも取得
      % 【入力】
      % ha:        現axesのハンドル
      % x:         折線の横軸の座標群を並べた行ベクトル
      % y:         　〃　縦軸　　　　　　〃
      % tagL:      描かれた折線全体に付けたい名称（char型）
      % tagPb:     始点（若番側）に付けたい名称　（char型）
      % tagPe:     終点（老番側）　　　　　〃
      % 【出力】
      % hLPs:      hLPs(1): 折線全体のハンドル
      %            hLPs(2): 始点のハンドル
      %          　hLPs(3): 終点　　〃
    
      if prod(size(x)==size(y))==0
        disp([newline 'xとyでサイズが異なります' newline])
        error('　')
      end
      if ~ishandle(ha)
        disp([newline '指定されたhaは有効なハンドル' ...
                                         'ではありません' newline])
        error('　')
      end

      obj.check_duplication_of_tagname(ha,{tagL,tagPb,tagPe});
    
      hL=plot(ha,x,y,'b');
      hold on
      hL.Tag=tagL;
      
      hPb=plot(ha,x(1),y(1),'.','Color','#888','MarkerSize',16);
      hPb.Tag=tagPb;
      hPe=plot(ha,x(end),y(end),'.','Color','#888','MarkerSize',16);
      hPe.Tag=tagPe;

      hLPs=[hL,hPb,hPe];
    
    end

    % ====================================
    % 名称で指定した点群を繋いで一本の折線を描画し、
    % 　その線全体に名称をつけてハンドルを取得
    % ====================================
    
    function hL=draw_polyline(obj,ha,tagPcell,tagL)
    
      % 指定した点群を繋いで一本の折線を描画し、
      % 　その線全体に名称をつけてハンドルを取得
      % 【入力】
      % ha:       現axesのハンドル
      % tagPcell: 点の名称（char型）を並べたセル配列（始点 → 終点の順）
      % tagL:     描かれた折線全体に付けたい名称（char型）
      % 【出力】
      % hL:       描かれた折線のハンドル。
    
      if ~ischar(tagL)
        disp([newline 'tagLは「'' ''」で囲んだ文字列として' ...
                                         '指定してください' newline])
        error('　')
      end

      if ~iscell(tagPcell)
        disp([newline 'tagPcellはcell配列で指定してください' newline])
        error('　')
      end

      obj.check_duplication_of_tagname(ha,{tagL});
    
      x=[]; y=[];
      for n=1:length(tagPcell)
        h=obj.get_handles_from_tagname(ha,tagPcell{n});
        x=[x h.XData];
        y=[y h.YData];
      end
    
      hL=plot(ha,x,y,'b');
      hold on
      hL.Tag=tagL;
    
    end

    % ====================================
    % [指定した基準点を通って指定角度の方向に伸びる仮想直線]上から、
    % 　[指定長さの線分]を抽出して、実在の線として描く。
    % ====================================

    function hLPs=draw_sloped_line(obj,ha,tagPo,ang,len1,len2,tagL,tagP1,tagP2)

      % [指定した基準点を通って指定角度の方向に伸びる仮想直線]上から、
      % 　[指定長さの線分]を抽出して、実在の線として描く。
      % 【入力】
      % ha:      現axesのハンドル
      % tagPo:   直線の基準点の名称（char型）
      %          　基本的に、この点は線の一要素としては取り込まれ
      %          　ない（この点が始点・終点となる場合は除く）。
      % ang:     傾斜角（x軸を基準とした反時計回りの角度[deg]）
      % len1:    [基準点]から[描画線分の始点]までの長さ
      % len2:     　　　　　〃　　　　　終点　　〃
      %          　len1,len2 の符号は、傾斜角だけ傾いて見たとき、
      %          　左側が -、右側が + とする。
      % tagL:    描かれた直線に付けたい名称（char型）
      % tagP1:   直線の始点に付けたい名称（char型）
      % tagP2:   　〃　終点　　　　　〃
      %          　直線の始点/終点 が基準点と同一点になったときには、
      %          　tagP1/tagP2 で指定された名称は無視され、
      %            基準点の名称がそのまま継承される。
      % 【出力】
      % hLPs:      直線,始点,終点のハンドルから成る配列

      obj.check_duplication_of_tagname(ha,{tagL,tagP1,tagP2});

      hP0=obj.get_handles_from_tagname(ha,tagPo);
                                    % 直線の基準点のハンドル
      x0=hP0.XData;                 % 基準点の座標値
      y0=hP0.YData;
      x1=x0+len1*cosd(ang);         % 始点の座標値
      y1=y0+len1*sind(ang);
      x2=x0+len2*cosd(ang);         % 終点の座標値
      y2=y0+len2*sind(ang);

      if len1==len2
        disp([newline '線の長さが 0 なので描けません。' newline]);
        error('　');
      end

      % 始点と終点を描く。
      % 　始点や終点が基準点と同一点となるときには、重ね描きはしない。

      if len1~=0
        hP1=obj.draw_points_xy(ha,[x1 y1],tagP1);       % 始点
      end
      if len2~=0
        hP2=obj.draw_points_xy(ha,[x2 y2],tagP2);       % 終点
      end

      % 線を引いて、返り値を設定する。

      if len1==0        % 始点と基準点が同一点のとき
        hL=obj.draw_polyline(ha,{tagPo tagP2},tagL);
        hLPs=[hL hP0 hP2];
      elseif len2==0    % 終点と基準点が同一点のとき
        hL=obj.draw_polyline(ha,{tagP1 tagPo},tagL);
        hLPs=[hL hP1 hP0];
      else              % 始点・終点とも、基準点ではないとき
        hL=obj.draw_polyline(ha,{tagP1 tagP2},tagL);
        hLPs=[hL hP1 hP2];
      end

    end

    % ====================================
    % 指定点を通り、指定した直線と平行に線を描く。
    % ====================================

    function hLPs=draw_parallel_line(obj,ha,tagLo,tagPo,len1,len2,tagL,tagP1,tagP2)

      % 指定点を通り、指定した直線と平行に線を描く。
      % 【入力】
      % ha:      現axesのハンドル
      % tagLo:   基準にする直線の名称（char型）
      %          　これを、[線の配列要素の若番→老番の方向]を向いた
      %          　[基準ベクトル]とみなす。
      % tagPo:   平行な線が通るべき点の名称（char型）
      %          　（ここでは[基準点]と呼ぶ）
      %          　なお、この点は、描かれた平行線の折れ点要素として
      %          　組み込まれることはない。
      %          　（この点が平行線の始点・終点となる場合は除く）。
      % len1:    [基準点]から[平行線の始点]までの長さ
      % len2:     　　　　〃　　　　　終点　　〃
      %          　ただし、len1,len2 の符号は、
      %          　[基準ベクトル]の[正/負]方向を[+/-]とする。
      % tagL:    描かれた平行線に付けたい名称（char型）
      % tagP1:   平行線の始点に付けたい名称（char型）
      % tagP2:   　〃　　終点　　　　　〃
      %          　平行線の始点/終点 が基準点と同一点になったときには、
      %          　tagP1/tagP2 で指定された名称は無視され、
      %            基準点の名称がそのまま継承される。
      % 【出力】
      % hLPs:     描かれた平行線、その始点と終点のハンドルから成る配列

      obj.check_duplication_of_tagname(ha,{tagL,tagP1,tagP2});

      h=obj.get_handles_from_tagname(ha,tagLo);
      X=h.XData;
      Y=h.YData;
      dx=X(end)-X(1);
      dy=Y(end)-Y(1);
      ang=atan2d(dy,dx);
      hLPs=obj.draw_sloped_line(ha,tagPo,ang,len1,len2,tagL,tagP1,tagP2);

    end

    % ====================================
    % 指定点を通り、指定した直線に垂直な線を描く。
    % ====================================

    function hLPs=draw_perpendicular_line(obj,ha,tagLo,tagPo,len1,len2,tagL,tagP1,tagP2)

      % 指定点を通り、指定した直線に垂直な線を描く。
      % 【入力】
      % ha:      現axesのハンドル
      % tagLo:   基準にする直線の名称（char型）。
      %          　これを、[線の配列要素の若番→老番の方向]を向いた
      %          　[基準ベクトル]とみなす。
      % tagPo:   垂直線が通るべき点の名称（char型）。
      %          　（ここでは[基準点]と呼ぶ）
      %          　なお、この点は、描かれた垂直線の折れ点要素として
      %          　組み込まれることはない。
      %          　（この点が垂直線の始点・終点となる場合は除く）。
      % len1:    [基準点]から[垂直線の始点]までの長さ
      % len2:     　　　　〃　　　　　終点　　〃
      %          　ただし、len1,len2 の符号は、
      %          　基準ベクトルを反時計回りに90度回転させたベクトルの
      %          　[正/負]方向を[+/-]とする。
      % tagL:    描かれた垂直線に付けたい名称（char型）
      % tagP1:   垂直線の始点に付けたい名称（char型）
      % tagP2:   　〃　　終点　　　　　〃
      %          　垂直線の始点/終点 が基準点と同一点になったときには、
      %          　tagP1/tagP2 で指定された名称は無視され、
      %            基準点の名称がそのまま継承される。
      % 【出力】
      % hLPs:     描かれた垂直線、その始点と終点のハンドルから成る配列

      obj.check_duplication_of_tagname(ha,{tagL,tagP1,tagP2});

      h=obj.get_handles_from_tagname(ha,tagLo);
      X=h.XData;
      Y=h.YData;
      dx=X(end)-X(1);
      dy=Y(end)-Y(1);
      ang=atan2d(dy,dx);
      hLPs=obj.draw_sloped_line(ha,tagPo,ang+90,len1,len2,tagL,tagP1,tagP2);

    end

    % ====================================
    % 閉曲線の内側にハッチングを施す。
    % ====================================

    function hL=draw_hatching_lines(obj,ha,tagLc,pitch,ang,tagHL)

      % 閉曲線の内側にハッチングを施す。
      % 　やや時間がかかるので、最後の仕上げ段階での利用が望ましい。
      % 【入力】
      % ha:         現axesのハンドル
      % tagLc:      閉曲線の名称（char型）
      % pitch:      ハッチング線の間隔
      % ang:        　　　〃　　　傾斜 [degree]
      % tagHL:      ハッチング線の全体につける名称（char型）
      % 【出力】
      % hL:         ハッチング線全体のハンドル（スカラー）

      obj.check_duplication_of_tagname(ha,{tagHL});

      h_line=obj.get_handles_from_tagname(ha,tagLc);
      X1=h_line.XData;
      Y1=h_line.YData;
      M1=[X1;Y1];              % 閉曲線の、折線としての座標ベクトル

      % 閉曲線を、原点を中心にして、ハッチング線の角度とは逆方向に回転。
      % 　（この状態にすると、ハッチング線は水平線群になるはず）

      Rot=[cosd(ang) -sind(ang);sind(ang) cosd(ang)];   % 回転行列
      M2=Rot'*M1;              % 閉曲線を逆回転
      X2=M2(1,:);
      Y2=M2(2,:);
      Lox=[min(X2)-pitch/2 max(X2)+pitch/2
           min(Y2)-pitch/2 min(Y2)-pitch/2];
                               % 逆回転状態下のハッチングの仮基準線
                               % 　（水平な線分）
      Lo=Rot*Lox;              % 回転を元に戻した後のハッチング基準線
      lenLo=max(X2)-min(X2)+pitch;
                               % ハッチング基準線の長さ

      % 以下、名称に@を付けているのは、このfunction内でのみ使用する
      % 　暫定Tagであることを識別しやすくするため（消去忘れ防止用）。
      obj.draw_points_xy(ha,[Lo(1,1) Lo(2,1);Lo(1,2) Lo(2,2)], ...
                         {'@Po1' '@Po2'});
      obj.draw_polyline(ha,{'@Po1' '@Po2'},'@Lo');
                               % ハッチング基準線
      obj.draw_perpendicular_line(ha,'@Lo','@Po1',0, ...
                                    max(Y2)-min(Y2)+2*pitch, ...
                                   '@Lv','@Pv1','@Pv2');
                               % ハッチング基準線に直交する線

      hatch_lines_x=[];
      hatch_lines_y=[];
      for n=1:floor((max(Y2)-min(Y2)+pitch)/pitch)
        h1=obj.draw_points_on_the_line(ha,'@Lv',n*pitch,'@Px1');
        h2=obj.draw_parallel_line(ha,'@Lo','@Px1',0,lenLo,'@Lw', ...
                                   '@Pw1','@Pw2');
        h3=obj.intersection_of_polylines(ha,'@Lw',tagLc, ...
                    {'@is1' '@is2' '@is3' '@is4' '@is5' ...
                     '@is6' '@is7' '@is8' '@is9' },2);

        if isempty(h3)
          delete([h1 h2]);
          continue;
        end

        if mod(length(h3),2)~=0
          disp([newline '微妙な交点があり、' ...
               '処理できません。' newline 'ハッチングのpitchを' ...
               '少し変更してみてください。' newline])
          error('　')
        end
        if length(h3)>8
          disp([newline '閉曲線の凹凸が多すぎます。' newline ...
                '1本のハッチング線との交点は' ...
                '最大で8点までです。' newline])
          error('　')
        end

        tag_cell={'@Px1'};
        for m=1:length(h3)
          tag_cell=[tag_cell h3(m).Tag];
        end
        tag_cell=[tag_cell 'Pw2'];

        for m=1:length(h3)/2
          % @Lw線（各ループごとに更新される）の左片を消去
          h4=obj.erase_open_line_partially(ha,'@Lw', ...
                               tag_cell{2*m-1},tag_cell{2*m},'@Ladd');
          % @Lw線の残った右片を、次の交点部分で切断
          h4=obj.erase_open_line_partially(ha,'@Lw', ...
                             tag_cell{2*m+1},tag_cell{2*m+1},'@Ladd');
          % 切り取られた左片部分をハッチング線として加える。
          hatch_lines_x=[hatch_lines_x NaN h4(1).XData];
          hatch_lines_y=[hatch_lines_y NaN h4(1).YData];
          delete(h4(1));     % @Lw線名が重複しないように、事前に消去。
          h4(2).Tag='@Lw';   % 分割されてできた2本目の線の名称を
                             % 　'@Ladd' から '@Lw' に変更。
        end

        delete([h1 h2 h3 h4]);
      end

      hatch_lines_x(1)=[];      % 第1要素のNaNを消去
      hatch_lines_y(1)=[];
      hLP=obj.draw_polyline_xy(ha,hatch_lines_x, ...
                                           hatch_lines_y, ...
                                        tagHL,'@Pb','@Pe');
      hL=hLP(1);
      delete([hLP(2) hLP(3)]);

      obj.erase_lines_and_points(ha,{'@Lo' '@Lv' '@Po1' '@Po2' '@Pv2'});

    end

    % ====================================
    % 全円の描画とそのハンドルを取得。
    % 　線の始点・終点を強調するための描画はしない。
    % ====================================
    
    function hL = draw_circle(obj,ha,r,tagPc,tagL,nagon,ang)
    
      % 全円の描画とそのハンドルを取得。
      % 　線の始点・終点を強調するための描画はしない。
      % 【入力】
      % ha:      現axesのハンドル
      % r:       円の半径
      % tagPc:   円の中心点の名称（char型）
      % tagL:    描かれた円周線に付けたい名称（char型）
      % 【入力(任意)】
      % nagon:   多角形のときに指定する。[nagon角形]になる。
      %          　無指定のときは円（200角形）
      % ang:     多角形の傾き[degree]
      %          　無指定のときは0。
      % 【出力】
      % hL:      円周全体のハンドル

      obj.check_duplication_of_tagname(ha,{tagL});

      ndiv=200;        % 近似円の多角形の角数
      th0=0;           % 円を描き始める角度

      if nargin>=6
        ndiv=nagon;
        if nargin>=7
          th0=ang;
        end
      end

      h=obj.get_handles_from_tagname(ha,tagPc);
      Pc=[h.XData  h.YData];
      th=linspace(0,360,ndiv+1);
      x=r*cosd(th+th0)+Pc(1);
      y=r*sind(th+th0)+Pc(2);
    
      hL=plot(ha,x,y,'b');
      hL.Tag=tagL;
      hold on
    
    end

    % ====================================
    % スプライン曲線で、歪んだ円や開曲線を描く。
    % 　始点,終点に同一点を指定したときは閉曲線。
    % ====================================

    function hL=draw_free_curve(obj,ha,tagPcell,angs,kang,tagL)

      % スプライン曲線で、歪んだ円や開曲線を描く。
      % 　始点,終点に同一点を指定したときは閉曲線になる。
      % 【入力】
      % ha:         現axesのハンドル
      % tagPcell:   線を通過させる点の名称（char型）を
      % 　　　　　　　通過順に並べたセル配列。
      % angs:       始点/終点での線の傾き [degree]。NaNのときは自由端。
      %             　スカラーのときは、始点/終点とも同一の傾き。
      %             　2要素の配列のときは、それぞれ始点,終点での傾き。
      % kang:       angsの効果が及ぶ範囲の広さ（適量の目安は1.0）
      % tagL:       描かれた線に付ける名称（char型）
      % 【出力】
      % hL:         描かれた線のハンドル。

      obj.check_duplication_of_tagname(ha,{tagL});

      if length(angs)==1
        ang1=angs(1);
        ang2=ang1;
      else
        ang1=angs(1);
        ang2=angs(2);
      end

      if isnan(ang1)
        dx1=0;
        dy1=0;
      else
        dx1=cosd(ang1);
        dy1=sind(ang1);
      end
      if isnan(ang2)
        dx2=0;
        dy2=0;
      else
        dx2=cosd(ang2);
        dy2=sind(ang2);
      end

      N=length(tagPcell);

      % スプライン曲線の長さの概算見積もり
      xx=[];
      yy=[];
      hPs=obj.get_handles_from_tagname(ha,tagPcell);
      for n=1:N
        xx=[xx hPs(n).XData];
        yy=[yy hPs(n).YData];
      end
      L=cumsum(hypot(diff(xx),diff(yy)));
      len=L(end);

      % 媒介変数
      t=linspace(0,1,N)*len*kang;

      xo=[dx1]; yo=[dy1];
      for n=1:N
        h=obj.get_handles_from_tagname(ha,tagPcell{n});
        xo=[xo h.XData];
        yo=[yo h.YData];
      end
      xo=[xo dx2]; yo=[yo dy2];

      % スプライン曲線として平滑化
      Lsp = spline(t,[xo;yo]);
      XY = ppval(Lsp, linspace(0,max(t),101));
      X=XY(1,:);   % 平滑化された線の
      Y=XY(2,:);   %     折点のx,y座標値
      h=plot(ha,X,Y,'-b');
      h.Tag=tagL;

    end

    % ====================================
    % 雲形、ヒイラギの葉形の輪郭線を描く。
    % ====================================

    function hL=draw_cloud_shaped_line(obj,ha,tagL0,ndiv,kr,tagLc,ang)
   
      % 雲形、ヒイラギの葉形の輪郭線を描く。
      % 【入力】
      % ha:         現axesのハンドル
      % tagL0:      核にする閉曲線の名称（char型）
      %             　ただし、閉曲線の経路の向きによって、
      %             　反時計回りのときは雲形の描画。
      %             　時計回りのときはヒイラギの葉形の描画。
      % ndiv:       突起の数（正の整数）。小さ過ぎると描画に失敗する。
      %             　一概には言えないが、5以上を推奨。
      % kr:         突起の基になる小円の半径を調整する係数。
      %             　1.0の場合は、隣接する小円同士が接するか否かの
      %             　限界値。1.1以上に設定するのが望ましい。
      % tagLc:      でき上がった線につける名称。
      % 【入力(任意)】
      % ang;        円弧の片方だけを延長させるための角度 [degree]
      % 【出力】
      % hL:         でき上がった線のハンドル

      obj.check_duplication_of_tagname(ha,{tagLc});

      if nargin>=7
        % angは指定値どおり
        fang=1;   % フラグ
      else
        ang=0;
        fang=0;
      end
      len=obj.get_length_of_line(ha,tagL0);
      hL0=obj.get_handles_from_tagname(ha,tagL0);
      xb=hL0.XData(1);
      yb=hL0.YData(1);
      xe=hL0.XData(end);
      ye=hL0.YData(end);

      % 小円の中心点につける暫定的な名称を準備しておく。
      % 　'@P01','@P02','@P03',...
      Pname={};
      for n=1:ndiv+1
        Pn=['@P' num2str(n,'%02d')];
        Pname=[Pname Pn];
      end

      % 始点は1番,終点は[ndiv+1]番
      obj.draw_points_xy(ha,[xb yb;xe ye],{Pname{1} Pname{end}});
      % その間の分割点に2番～ndiv番
      h=obj.draw_points_on_the_line(ha,tagL0,len*[1:ndiv-1]/ndiv,Pname(2:ndiv),0);

      r=0.5*kr*len/n;    % 小円の半径

      th_min_max=[];
      th_min=NaN;
      for n=1:ndiv
        h1=obj.get_handles_from_tagname(ha,Pname{n});
        h2=obj.get_handles_from_tagname(ha,Pname{n+1});
        x1=h1.XData;
        y1=h1.YData;
        x2=h2.XData;
        y2=h2.YData;

        % 隣接する円どうしの交点の検出
        JJ = @(th) hypot(x2-x1-r*cosd(th),y2-y1-r*sind(th))-r;
        thb=atan2d(y1-y2,x1-x2);
        the=thb+180;
        th=fzero(JJ,[thb the]);

        % 角度表現を -180 ～ +180 度内に収める。
        if th>180
          th=th-360;
        elseif th<-180
          th=th+360;
        end

        th_max=th;
        th_min_max=[th_min_max;[th_min th_max]];
        
        % 次の円にとっての最低θ
        th_min=atan2d(y1-y2+r*sind(th),x1-x2+r*cosd(th));

        if n==ndiv      % 最初の円のNaNのままだった未定値を確定
          th_min_max(1,1)=th_min;
        end
      end

      Xcloud=[];
      Ycloud=[];
      for n=1:ndiv
        h1=obj.get_handles_from_tagname(ha,Pname{n});
        x0=h1.XData;
        y0=h1.YData;

        th_from=th_min_max(n,1);
        th_to=th_min_max(n,2);
        ddth=th_to-th_from;

        % 0 ～ 360 度の範囲内で表現できる進み角度を求めている中で、
        % 　負の値になるということは、1周（360度）進んだ状態から見た
        % 　ときに、それだけ遅れていることになる。それを補正する。
        if ddth<0
          ddth=ddth+360;
        end

        thc=[th_from-ang+linspace(0,ddth+ang,21)];
        xcloud=x0+r*cosd(thc);
        ycloud=y0+r*sind(thc);
        if (n==1)|(fang==0)            % 始点がNaNになると、色々と
                                       % 　都合が悪いので、場合分け。
          Xcloud=[Xcloud xcloud];      % 連続曲線
          Ycloud=[Ycloud ycloud];
        else                           % (n~=1)&(fang==1)
          Xcloud=[Xcloud NaN xcloud];  % 不連続曲線
          Ycloud=[Ycloud NaN ycloud];  % 　（fillコマンドは使えない）
        end
      end
      if fang==0      % このときは、始点と終点をピッタリと致させ、
                      % 　完全な閉曲線にする。
        Xcloud=[Xcloud Xcloud(1)];
        Ycloud=[Ycloud Ycloud(1)];
      end

      hLP=obj.draw_polyline_xy(ha,Xcloud,Ycloud,tagLc,'@dummy1','@dummy2');
      hL=hLP(1);
      obj.erase_lines_and_points(ha,{Pname{:},'@dummy1','@dummy2' });

    end

    % ====================================
    % 矢印の頭のＶ字の描画
    % ====================================

    function hA=draw_arrowhead_at_line_tip ...
                             (obj,ha,tagL,pos,ang,leng,tagV,dang,Ropt)

      % 矢印の頭のＶ字の描画
      % 【入力】
      % ha:        現axesのハンドル
      % tagL:      矢印をつける曲線の名称（char型）
      %            　曲線が閉じている場合は、先に切断しておくこと。
      %            　これにより、切断点が始点,終点になる。
      % pos:       矢印をつける位置。始点側は1、終点側は2。
      % ang:       矢印のV字の両翼間の開き角度 [degree]
      % leng:      矢印のV字の翼の長さ
      % tagV:      描いたV字に付ける名称（char型）。
      % 【入力(任意)】
      % dang:      Roptを指定しないとき
      %            　矢印のV字全体の傾きの補正量（相対角度）[degree]
      %            　　極性は、尖端部を支点として、
      %            　　翼部の反時計方向への偏移が+、逆は-。
      %            Roptも指定したとき
      %            　矢印が向く方向の角度（絶対角度） [degree]
      %            　　x軸方向を基準として、反時計回りが +。
      % Ropt:      強制指定する翼部の曲率半径（自動推定値は無視）。
      %            　+ のとき、始点/終点側の翼は右/左カーブ。
      %            　- 　　　　　　　 〃　　　　左/右カーブ。　
      %            　直線のときはInf。
      % 【出力】
      % hA:     描かれたV字図形のハンドル

      obj.check_duplication_of_tagname(ha,{tagV});

      if nargin>=8     % V字の傾き角度の関連量dangを指示されたとき
        % dangは指定どおり;
      else
        dang=0;
      end

      if nargin<=8     % 翼部の曲率半径の指定がないとき（推定する）
        [R dirx]=obj.get_radius_of_curvature(ha,tagL);
                       % R:推定半径（左/右カーブで+/-）。
                       % dirx(1),dirx(2):始点,終点での接線の
                       % 　外向き側の角度
      else
        R=Ropt;
        dirx=[0 0];
      end

      h=obj.get_handles_from_tagname(ha,tagL);
      X=h.XData;
      Y=h.YData;

      if pos==1              % 曲線の始点に矢印
        x0=X(1);             % 　矢印の尖端部の座標
        y0=Y(1);
        dir=dirx(1);
      elseif pos==2          % 曲線の終点に矢印
        x0=X(end);
        y0=Y(end);
        dir=dirx(2);
      else
        disp([newline 'posの指示が不適当です。' newline]);
        error('　');
      end

      if ~isinf(abs(R))      % 曲率半径が無限大（直線）ではないとき
        thdx=180*leng/(pi*abs(R));   % 矢印の翼の長さ相当の円周角
        if (pos==1 & sign(R)==-1)|(pos==2 & sign(R)==1)
            s90=90;
            thd=-thdx;
        else
            s90=-90;
            thd=thdx;
        end

        R=abs(R);            % 以降、Rは正数のみ

        % 矢印の左側の翼の円弧のx,yデータ
        xa0=x0+R*cosd(dir+dang+s90+ang/2);       % 中心座標
        ya0=y0+R*sind(dir+dang+s90+ang/2);
        tha=dir+dang-s90+ang/2;                  % 描画開始角度
        xa=R*cosd(linspace(tha,tha+thd,5))+xa0;
        ya=R*sind(linspace(tha,tha+thd,5))+ya0;

        % 　〃　右側　　　　〃
        xb0=x0+R*cosd(dir+dang+s90-ang/2);
        yb0=y0+R*sind(dir+dang+s90-ang/2);
        thb=dir+dang-s90-ang/2;
        xb=R*cosd(linspace(thb,thb+thd,5))+xb0;
        yb=R*sind(linspace(thb,thb+thd,5))+yb0;
        xb(1)=[];  yb(1)=[];

      else         % 与えられた線が直線とみなせるとき
        xa=[x0 x0+leng*cosd(dir+dang+180+ang/2)];
        ya=[y0 y0+leng*sind(dir+dang+180+ang/2)];
        xb=x0+leng*cosd(dir+dang+180-ang/2);
        yb=y0+leng*sind(dir+dang+180-ang/2);
      end

      x=[fliplr(xa) xb];
      y=[fliplr(ya) yb];
      hA=plot(ha,x,y,'b');
      hA.Tag=tagV;

    end

    % ====================================
    % [複数の曲線で構成された閉領域]を囲む１つの閉曲線を定義する。
    % 　ハッチングを描く前に使うと便利。
    % ====================================

    function hL=surround_area_by_a_loop(obj,ha,tagLcell,tagL)

      % [複数の曲線で構成された閉領域]を囲む１つの閉曲線を定義する。
      % 　結果は透明な淡赤色の線で表示される。ハッチングを施す前に
      % 　使うと便利だが、あくまで補助線的なものなので、ハッチング後
      % 　は、delete(hL)で消去するのが望ましい。
      % 【入力】
      % ha:         現axesのハンドル
      % tagLcell:   領域を取り囲む線群の名称（char型）を、
      %             　繋ぎ順に並べたセル配列。
      %             　このコマンドでは、線の正負の向きは問わない。
      %             線と線の接続点は、折点要素として、
      %             　両線で共有されていなければならない。
      %             始点と終点が同一点にならなかった場合には、
      %             　両点間は直線で結ばれる。
      % tagL:       できあがった閉曲線につける名称（char型）
      % 【出力】
      % hL:         描かれた閉曲線のハンドル。

      obj.check_duplication_of_tagname(ha,{tagL});

      h=obj.get_handles_from_tagname(ha,tagLcell);
      X=h(1).XData;
      Y=h(1).YData;

      X2=h(2).XData;
      Y2=h(2).YData;
      if ~((X(end)==X2(1)  )&(Y(end)==Y2(1)  )| ...
           (X(end)==X2(end))&(Y(end)==Y2(end)))
        X=fliplr(X);
        Y=fliplr(Y);
      end

      N=length(tagLcell);

      for n=2:N
        Xq=h(n).XData;
        Yq=h(n).YData;
        if (X(end)==Xq(1) & Y(end)==Yq(1))
                          % 接続済の線の最後の要素と、
                          % 　この線の最初の要素が同一点のとき。
          % 特に何もしない。
        elseif (X(end)==h(n).XData(end) & Y(end)==h(n).YData(end))
                          % 接続済の線の最後の要素と、
                          % 　この線の最後の要素が同一点のとき。
          % この線の要素の並び順を反転
          Xq=fliplr(Xq);
          Yq=fliplr(Yq);
        else
          disp([newline '引数 tagLcell の指定方法' ...
                                            'が不適切です' newline])
          error('　')
        end
        Xq(1)=[];       % この線の最初の要素は、前の線の最後の要素と
        Yq(1)=[];       % 　重複するので削除する。
        X=[X  Xq];      % 第n線を繋げる。
        Y=[Y  Yq];
      end

      hL=plot(ha,X,Y,'r','Linewidth',5);
      hL.Tag=tagL;
      hL.Color(4)=0.2;  % 線色をやや透明にする。

    end

    % ====================================
    % 異なる名称の複数の線で構成された連続線を、
    % 　１本に統合して単一名称にする。
    % 　（統合は利点,欠点を併せ持つ。必要なときのみ使用するのが賢明）
    % ====================================

    function hL=combine_lines_into_one(obj,ha,tagLcell)

      % 異なる名称の複数の線で構成された連続線を、
      % 　１本に統合して単一名称にする。
      % 　（統合は利点,欠点を併せ持つ。必要なときのみ使用するのが賢明）
      % 【入力】
      % ha:         現axesのハンドル
      % tagLcell:   線群の各名称（char型）を繋ぎ順に並べたセル配列。
      %             　このコマンドでは、線の正負の向きは問わない。
      %             線と線の接続点は、両線で共有されて
      %             　いなければならない。
      %             できあがった線の名称や線色,線幅,経路方向などは、
      %             　tagLcellの中の最初の線のものが継承される。
      % 【出力】
      % hL:         統合された曲線のハンドル。

      h=obj.get_handles_from_tagname(ha,tagLcell);
      X=h(1).XData;
      Y=h(1).YData;
      inverted=0;      % 最初の線は経路未反転の初期状態

      X2=h(2).XData;
      Y2=h(2).YData;
      if ~((X(end)==X2(1)  )&(Y(end)==Y2(1)  )| ...
           (X(end)==X2(end))&(Y(end)==Y2(end)))
        X=fliplr(X);
        Y=fliplr(Y);
        inverted=1;    % 最初の線は、都合により経路反転された
      end

      N=length(tagLcell);

      for n=2:N
        Xq=h(n).XData;
        Yq=h(n).YData;
        if (X(end)==Xq(1) & Y(end)==Yq(1))
                          % 接続済の線の最後の要素と、
                          % 　この線の最初の要素が同一点のとき。
          % 特に何もしない。
        elseif (X(end)==h(n).XData(end) & Y(end)==h(n).YData(end))
                          % 接続済の線の最後の要素と、
                          % 　この線の最後の要素が同一点のとき。
          % この線の要素の並び順を反転
          Xq=fliplr(Xq);
          Yq=fliplr(Yq);
        else
          disp([newline '引数 tagLcell の指定方法' ...
                                            'が不適切です' newline])
          error('　')
        end
        Xq(1)=[];       % この線の最初の要素は、前の線の最後の要素と
        Yq(1)=[];       % 　重複するので削除する。
        X=[X  Xq];      % 第n線を繋げる。
        Y=[Y  Yq];
      end

      delete(h(2:N));   % 最初の線だけを残し、元の線群は削除

      % 最初の線のx,yデータを統合後のデータと置き換える。
      if inverted==1    % ただし、統合後の線の経路方向を
        X=fliplr(X);    % 　最初の線と同一方向に合わせてから。
        Y=fliplr(Y);
      end
      set(h(1),'XData',X,'YData',Y);
      hL=h(1);

    end

    % ====================================
    % 実線を破線に変える
    % ====================================

    function hL=change_line_to_broken_one(obj,ha,tagL,pattern,pitch,pos0)

      % 実線を破線に変える。
      % 　破線に変えた後は、交点の検出や線上点の追加などに、エラーが
      % 　出やすくなる。最後の仕上げ段階で利用するのが望ましい。
      % 【入力】
      % ha:       現axesのハンドル
      % tagL:     加工の対象にする実線の名称（char型）
      %           　加工後も名称は変わらない。
      % pattern:  １ピッチ内の破線のパターン。
      %           　正負の値を交互に並べた配列
      %           　例　　[a -b c -d e -f]
      %           　意味: 長さaの実線、長さbの空白、長さcの実線 ....
      %           　各部の実寸は、pitch*[a b c d e f]/(a+b+c+d+e+f)
      %           　pattern(1)とpattern(end)は逆符号にすること。
      % pitch:    パターン１周期分の長さ
      % 【入力(任意)】
      % pos0:     pitch内のどの位置から描画を開始するかを指定する値。
      %           　pitch長に対する比率（ 0 <= pos0 < 1 ）。
      %           　指定しなければ0。
      % 【出力】
      % hL:       破線のハンドル

      if nargin>=6
        % pos0は指定どおり
      else
        pos0=0;
      end

      % ===== 元の折線の情報を整理

      h_line=obj.get_handles_from_tagname(ha,tagL);
      X=h_line.XData;
      Y=h_line.YData;
      % 長さがほぼ0の線分を除去する（interp1コマンドでのエラーを回避）
      ndel=find(hypot(diff(X),diff(Y))<eps(100));
      X(ndel)=[];
      Y(ndel)=[];

      dX=diff(X);
      dX=[0 dX];
      Xr=cumsum(dX);     % 元の線の始点からの[折点の相対位置]のx成分
      dY=diff(Y);
      dY=[0 dY];
      Yr=cumsum(dY);     % 　　　　　　　　〃　　　　　　　　　y成分
      dL=hypot(dX,dY);   % 折線の各線分の長さ
      L=cumsum(dL);      % 元の線の始点から、各線分の始点までの長さ。
                         % 　最終要素は、最終線分の終点までの長さ、
                         % 　言い換えれば、元の線の全長。
      len=L(end);        % 元の線の全長

      % ===== （開始）破線パターンの情報の整理

      dQ=abs(pattern);   % 相対単位で表した破線パターン
      sQ=sign(pattern);  % 各短線分が実線部(+1)か、空白部(-1)かの区別
      Q=sum(dQ);         % 相対単位で表した1ピッチの長さ
      Lp=pitch*cumsum(dQ)/Q;
                         % 絶対単位で表した[1ピッチ内の各区切り区間]の
                         % 　終点部の、ピッチ始点からの位置。
      Lp0 =pos0*pitch;   % 絶対単位での、描画を開始するピッチ内の位置。

      % 1回目の描画ピッチ（pos0位置で中途切断されている）内の
      % 　絶対単位での破線パターン

      Lpx=Lp;
      sQx=sQ;
      if Lp0~=0          % 描画を開始するパターン位置を指定されたとき
        nth=min(find(Lpx>Lp0));
        if nth==1
          Lpx=Lpx-Lp0;
          % sQxはそのまま。
        elseif nth>=2
          Lpx(1:nth-1)=[];
          Lpx=Lpx-Lp0;
          sQx(1:nth-1)=[];
        end
      else               % 描画開始位置を指定されないとき
        % Lpx,sQxともそのまま
      end

      LLp=[Lpx];         % 全線長を通しての破線パターン（区間点の位置）
      ssQ=[sQx];         % 　　　　　　　〃　　　　　　（実線/空線）

      % 2ピッチ目以降の破線パターンの追加
      Ls=-Lp0;
      Np=ceil(len/pitch);   % ピッチ数換算の線の長さ
      for n=2:Np+1
        LLp=[LLp Ls+pitch*(n-1)+Lp];
        ssQ=[ssQ sQ];
        if n==Np+1          % 最終ピッチ（過長部をカットする必要あり）
          nover=find(LLp>=len);
          LLp(nover)=[];
          LLp=[LLp len];
          nover(1)=[];
          ssQ(nover)=[];
        end
      end

      % ===== （終了）破線パターンの情報の整理

      % ===== 元の折線と破線パターンの合成

      x=[];    % 破線の座標データ（初期値）
      y=[];
      Nb=length(LLp);     % パターン情報の要素数
      for n=1:Nb
        if n==1
          Lmin=0;
        else
          Lmin=LLp(n-1);
        end
        Lmax=LLp(n);

        if ssQ(n)==1         % 実線部のデータのみを集積していく。
          ne=find((L>Lmin)&(L<Lmax));
          Lt=[Lmin  L(ne)  Lmax];
          Xt=interp1(L,Xr,Lt);
          Yt=interp1(L,Yr,Lt);
          x=[x Xt NaN];
          y=[y Yt NaN];
        end
      end
      x(end)=[];        % 最後のNaNを削除
      y(end)=[];

      set(h_line,'XData',X(1)+x,'YData',Y(1)+y);
      hL=h_line;

    end

    % ====================================
    % 二本の直線の接続点の尖った角を丸める
    % ====================================

    function hRPs=round_the_corner(obj,ha,r,tagP,tagL1,tagL2,tagR,tagPcell)

      % 二本の直線の接続点の尖った角を丸める
      % 【入力】
      % ha:         現axesのハンドル
      % r:          丸めに使用する円弧の半径
      % tagP:       二本の直線の接続部の点の名称（char型）
      % tagL1:      １本目の直線の名称（char型）
      % tagL2:      ２本目　　　　〃
      % tagR:       丸めてできた円弧の名称（char型）
      % 【入力(任意)】
      % tagPcell;   直線と丸め円弧の接続点の名称（char型）を
      %             　１本目,２本目の順に並べたセル配列。
      %             指定すると、有効な点として登録されて図に残る。
      % 【出力】
      % hRPs:        tagPcellを指定しないとき
      %             　丸めてできた円弧のハンドル。
      %             tagPcellを指定したとき
      %             　hRPs(1): 円弧のハンドル
      %             　hRPs(2): １本目の線との接続点のハンドル
      %             　hRPs(3): ２本目　　　　〃

      obj.check_duplication_of_tagname(ha,{tagR});

      hP=obj.get_handles_from_tagname(ha,tagP);
      hL1=obj.get_handles_from_tagname(ha,tagL1);
      hL2=obj.get_handles_from_tagname(ha,tagL2);
      x0=hP.XData;
      y0=hP.YData;

      % tagL1,tagL2が、[x0,y0]を始点とする外向きのベクトルとなるように
      % 　経路を反転（最後に元に戻すので、既存の図形に影響は与えない）
      inverted={};    % 元に戻すべき線名の記録
      X1=hL1.XData;
      Y1=hL1.YData;
      if X1(end)==x0 & Y1(end)==y0      % １本目の直線が内向きのとき
        % 反転
        hL1=obj.invert_direction_of_path(ha,tagL1);
        X1=hL1.XData;
        Y1=hL1.YData;
        inverted=[inverted tagL1];
      end
      X2=hL2.XData;
      Y2=hL2.YData;
      if X2(end)==x0 & Y2(end)==y0      % ２本目の直線が内向きのとき
        hL2=obj.invert_direction_of_path(ha,tagL2);
        % 反転
        X2=hL2.XData;
        Y2=hL2.YData;
        inverted=[inverted tagL2];
      end

      V1=(X1(end)-x0)+i*(Y1(end)-y0);  % tagL1,tagL2線を複素数化
      V2=(X2(end)-x0)+i*(Y2(end)-y0);
      th1=atan2d(imag(V1),real(V1));   % V1の傾斜角度
      th2=atan2d(imag(V2),real(V2));   % V2の傾斜角度
      V1=V1/abs(V1);                   % 正規化
      V2=V2/abs(V2);                   % 正規化
      ang=2*atan(abs(V1-V2)/abs(V1+V2))*180/pi;
                                       % V1,V2間の角度差（絶対値）
      sa=sign(angle((V1-V2)/(V1+V2))); % V1がV2より進みのとき+1、
                                       % 遅れのとき-1。

      R=r/(tand(ang/2));               % [x0,y0]から、[直線と丸め円
                                       % 　との接点]までの距離
      if nargin==8
        nameP1=tagPcell{1};
        nameP2=tagPcell{2};
      else
        nameP1='@P1';
        nameP2='@P2';
      end

      % 不要となる一部分の直線を消去
      hP1=obj.draw_points_on_the_line(ha,tagL1,R,nameP1);
      hP2=obj.draw_points_on_the_line(ha,tagL2,R,nameP2);
      obj.erase_open_line_partially(ha,tagL1,nameP1,tagP,'@dummy');
      obj.erase_open_line_partially(ha,tagL2,nameP2,tagP,'@dummy');

      % 丸め円の中心の座標 [xc,yc]
      xc=x0+R*cosd(th1)+r*cosd(th1-sa*90);
      yc=y0+R*sind(th1)+r*sind(th1-sa*90);

      % 丸め円の円弧の描画データを作成
      if sa>=0                   % V1が進みのとき
        thb=180+(th2+90);           % 　丸め円の描画開始角度
      else                       % V1が遅れのとき
        thb=180+(th1+90);
      end
      the=thb-(180-ang);         % 丸め円の描画終了角度
      th=linspace(thb,the,11);   % 丸め円は折線10本で近似
      xr=xc+r*cosd(th);
      yr=yc+r*sind(th);
      % 接続部の折れ点を両線でピッタリと一致させる。
      if sa>=0
        xr(1)=hP2.XData;
        yr(1)=hP2.YData;
        xr(end)=hP1.XData;
        yr(end)=hP1.YData;
      else
        xr(1)=hP1.XData;
        yr(1)=hP1.YData;
        xr(end)=hP2.XData;
        yr(end)=hP2.YData;
      end
      % 丸め円の円弧を描画
      hR=obj.draw_polyline_xy(ha,xr,yr,tagR,'@P3','@P4');

      % 経路の反転を元に戻す
      if ~isempty(inverted)
        for n=1:length(inverted)
          obj.invert_direction_of_path(ha,inverted{n});
        end
      end

      % 補助点を消去
      if nargin<8
        hRPs=hR(1);
        obj.erase_lines_and_points(ha,{nameP1 nameP2 '@P3' '@P4'});
      else
        hRPs=[hR(1) hP1 hP2];
        obj.erase_lines_and_points(ha,{'@P3' '@P4'});
      end

    end

    % ====================================
    % 指定した線の配置や形は変えずに、辿る経路を反転する。
    % 　[線の消去]や[平行線/垂直線の作図]は、経路方向を基準にして
    % 　行われる。そのため、線や基準線の向きを事前に反転してから
    % 　作業する方が便利なこともある。
    % ====================================

    function hL=invert_direction_of_path(obj,ha,tagL)

      % 指定した線の配置や形は変えずに、辿る経路を反転する。
      % 　[線の消去]や[平行線/垂直線の作図]は、経路方向を基準にして
      % 　行われる。そのため、線や基準線の向きを事前に反転してから
      % 　作業する方が便利なこともある。
      % 【入力】
      % ha:     現axesのハンドル
      % tagL:   方向を反転させる線の名称（char型）
      % 【出力】
      % hL:     反転結果の線へのハンドル

      hL=obj.get_handles_from_tagname(ha,tagL);
      set(hL,'XData',fliplr(hL.XData),'YData',fliplr(hL.YData));

    end

    % ====================================
    % 指定した線や点のプロパティを変更する
    % ====================================

    function change_property_of_lines(obj,ha,prop,var,tagLPcell)

      % 指定した線や点のプロパティを変更する
      % 【入力】
      % ha:         現axesのハンドル
      % prop:       変更するプロパティの種類（'Color','LineWidth'など）。
      %             　同時に指定できるプロパティは１種類のみ。
      % var:        プロパティの値
      % tagLPcell:  線や点の名称（char型）を要素とするセル配列。
      %             　名称が１つだけの場合は、{ }で囲まなくても可。
      %             　指定名称が存在しなくても、エラー扱いにはしない。
      % 【出力】
      % 　　　　なし

      if ~iscell(tagLPcell)
        tagLPcell={tagLPcell};
      end
    
      for n=1:length(tagLPcell)
        h=obj.get_handles_from_tagname(ha,tagLPcell{n},0);
        set(h,prop,var);
      end

    end

    % ====================================
    % 開いた折線から、線上の指定した2点間にある部分を消去。
    % 　線の端部の消去、線の中間部の消去、線の二分割などに使う。
    % 　処理によって線の数が増えたときには、元の線の若番側の線は
    % 　元の名称のままとし、老番側の線に新たな名称を付ける。
    % 　返り値は、処理結果の折線のハンドル。
    % ====================================
    
    function hLs=erase_open_line_partially(obj,ha,tagL,tagP1,tagP2,tagL2)
    
      % 開いた折線から、線上の指定した2点間にある部分を消去。
      % 　線の端部の消去、線の中間部の消去、線の二分割などに使う。
      % 　処理によって線の数が増えたときには、元の線の若番側の線は
      % 　元の名称のままとし、老番側の線に新たな名称を付ける。
      % 　返り値は、処理結果の折線のハンドル。
      % 【入力】
      % ha:      現axesのハンドル
      % tagL:    部分消去される前の折線の名称（cha型）
      % tagP1:   1つ目の点の名称（cha型）
      % tagP2:   2つ目　　　　〃
      %          　tagP1, tagP2 の記述順序は問わない。
      %          　tagP1=tagP2 なら、二分割
      % tagL2:   折線が2分割されたとき、老番側の線につける名称（cha型）
      %          　部分消去後も1本だけのとき、この名称は廃棄される。
      % 【出力】
      % hLs:     処理結果の折線のハンドル。二分割されたときは、
      %          　若番側、老番側の順の2要素の配列。

      obj.check_duplication_of_tagname(ha,{tagL2});

      hLo=obj.get_handles_from_tagname(ha,tagL);
      hP1=obj.get_handles_from_tagname(ha,tagP1);
      hP2=obj.get_handles_from_tagname(ha,tagP2);

      X=hLo.XData; Y=hLo.YData; 
      for n=1:length(X)
        if X(n)==hP1.XData & Y(n)==hP1.YData
          cut1=n;
          break;
        end
        if n==length(X)
          disp([newline '指定した点は、指定した' ...
                                    '線上にはありません。' newline])
          error('　')
        end
      end
      for n=1:length(X)
        if X(n)==hP2.XData & Y(n)==hP2.YData
          cut2=n;
          break;
        end
        if n==length(X)
          disp([newline '指定した点は、指定した' ...
                                    '線上にはありません。' newline])
          error('　')
        end
      end
      cut=sort([cut1 cut2]);
      X1=X(1:cut(1));
      Y1=Y(1:cut(1));
      X2=X(cut(2):end);
      Y2=Y(cut(2):end);
    
      if length(X1)==1      % 若番側には線分が残らないとき
        set(hLo,'XData',X2,'YData',Y2);
        hLs=hLo;
      else                  % 若番側にも線分が残っているとき
        set(hLo,'XData',X1,'YData',Y1);
        if length(X2)==1    % 老番側には線分が残らないとき
          hLs=hLo;
        else                % 老番側にも線分が残っているとき
          hL2=plot(ha,X2,Y2,'b','Tag',tagL2);
          hLs=[hLo hL2];
        end
      end
        
    end

    % ====================================
    % 閉じた折線から、指定した2点間にある部分を消去。
    % 　閉じた線なので、一部を消去したあとも、線は１本だけ。
    % ====================================
    
    function hL=erase_closed_line_partially(obj,ha,tagL,tagP1,tagP2,direction)
    
      % 閉じた折線から、指定した2点間にある部分を消去。
      % 　閉じた線なので、一部を消去したあとも、線は１本だけ。
      % 【入力】
      % ha:         現axesのハンドル
      % tagL:       部分消去される前の折線の名称（cha型）
      % tagP1:      1つ目の点の名称（cha型）
      % tagP2:      2つ目　　　　〃
      %             　tagP1 から tagP2 に向かって、direction で指定
      %             　された向きで消去。
      % direction:  'ascend' あるいは 'descend'。
      %             'ascend'  : 要素番号が増加する方向に消去。
      %             'descend' : 　　〃　　減少　　　〃
      % 【出力】
      % hL:         処理結果の折線のハンドル。元が閉じた線なので、
      %             　一部を消去したあとも、線は１本だけ。
    
      hLo=obj.get_handles_from_tagname(ha,tagL);
      hP1=obj.get_handles_from_tagname(ha,tagP1);
      hP2=obj.get_handles_from_tagname(ha,tagP2);
      X=hLo.XData; Y=hLo.YData; 

      for n=1:length(X)
        if X(n)==hP1.XData & Y(n)==hP1.YData
          cut1=n;
          break;
        end
        if n==length(X)
          disp([newline '指定した点は、指定した' ...
                                    '線上にはありません。' newline])
          error('　')
        end
      end
      for n=1:length(X)
        if X(n)==hP2.XData & Y(n)==hP2.YData
          cut2=n;
          break;
        end
        if n==length(X)
          disp([newline '指定した点は、指定した' ...
                                    '線上にはありません。' newline])
          error('　')
        end
      end
    
      if strcmp(direction,'descend')
        % descend 方向を消去 = ascend 方向を残す
        if cut1<cut2
          X1=X(cut1:cut2);
          Y1=Y(cut1:cut2);
        else
          X1=[X(cut1:end) X(1:cut2)];
          Y1=[Y(cut1:end) Y(1:cut2)];
        end
      end
    
      if strcmp(direction,'ascend')
        % ascend 方向を消去 = descend 方向を残す
        if cut1>cut2
          X1=X(cut1:-1:cut2);
          Y1=Y(cut1:-1:cut2);
        else
          X1=[X(cut1:-1:1) X(end:-1:cut2)];
          Y1=[Y(cut1:-1:1) Y(end:-1:cut2)];
        end
        X1=fliplr(X1);   % 消去後も、残った部分には
        Y1=fliplr(Y1);   % 　元の折れ点の並び順を継承させる。
      end
    
      set(hLo,'XData',X1,'YData',Y1);
      hL=hLo;
    
    end

    % ====================================
    % 指定した点や線を全消去する。
    % 　主に、不要になった補助線や補助点用に使う。
    % ====================================

    function erase_lines_and_points(obj,ha,tagLPcell)

      % 指定した点や線を全消去する。
      % 　主に、不要になった補助線や補助点用に使う。
      % 【入力】
      % ha:         現axesのハンドル
      % tagLPcell:  消去する線や点の名称（char型）を並べたセル配列。
      %             　名称が１つだけの場合は、{ }で囲まなくても可。
      %             　指定名称が存在しなくても、エラー扱いにはしない。
      % 【出力】
      % 　　　　なし

      if ~iscell(tagLPcell)
        tagLPcell={tagLPcell};
      end
    
      h=obj.get_handles_from_tagname(ha,tagLPcell,0);
      delete(h);

    end

    % ====================================
    % 平面上の2線分の交点の座標の取得（線分は座標で入力）
    % ====================================
    
    function [x,y,k1,k2]=intersection_of_lines_xy(obj,XD1,YD1,XD2,YD2)
    
      % 平面上の2線分の交点の座標の取得（線分は座標で入力）。
      % 　クラス内の他の関数からの利用がメイン。
      % 【入力】
      % XD1:   1本目の線分の両端のx座標 [x1a x1b]
      % YD1:    　　　　〃　　　　y座標 [y1a y1b]
      % XD2:   2本目の線分の両端のx座標 [x2a x2b]
      % YD2:    　　　　〃　　　　y座標 [y2a y2b]
      %        折線内の一線分として処理する場合、下記のように割り付けると
      %        混乱が少ないと思う。
      %        a: 折線データ配列内の若番側の端。
      %        b: 　　　　〃　　　　老番側の端。
      % 【出力】
      % x,y:   交点の座標。交点が存在しないとき、あるいは、
      %        交点が線分上に無いときにはNaN。
      % k1:    1本目の線分のa端から測った交点の位置（線分長を1とした割合）
      % k2:    2本目の線分の　　　　　　　　　　〃
    
      P1=[XD1(1) YD1(1) 0]; P2=[XD1(2) YD1(2) 0];  % 1本目の線の両端座標
      P3=[XD2(1) YD2(1) 0]; P4=[XD2(2) YD2(2) 0];  % 1本目の線の両端座標
    
      D=(norm(cross(P2-P1,P4-P3)))^2;
    
      if D>=eps(1)       % 平行線同士ではない（交点あり）

        crossA=cross(P4-P3,P3-P1);
        crossB=cross(P4-P3,P2-P1);
        crossC=cross(P2-P1,P3-P1);
        crossD=cross(P2-P1,P4-P3);
        k1=crossA(3)*crossB(3)/D;
        k2=-crossC(3)*crossD(3)/D;
    
        if k1<=0     % 本来なら k1<0 とすべきだが、次の理由により変更。
                     % 　実際の交点は一つでも、特殊条件下では、
                     % 　[この線分の終端]と[次の線分の始端]の双方で
                     % 　交点が検出され、交点が二つになることがある。
          pi1=-1;
        elseif k1<=1
          pi1=0;
        else
          pi1=1;
        end
      
        if k2<=0     % 同上の理由で k2<0 とはしない。
          pi2=-1;
        elseif k2<=1
          pi2=0;
        else
          pi2=1;
        end
    
        if (pi1~=0 | pi2~=0)
                     % 交点はあっても、線分上以外にあるとき。
          x=NaN;
          y=NaN;
        else         % 純粋な交点があるとき。
          crossE=P1+k1*(P2-P1);
          x=crossE(1);
          y=crossE(2);
        end
    
      else           % D<eps(1)（平行線同士で交点なし）
          x=NaN;
          y=NaN;
          k1=NaN;
          k2=NaN;
      end
    
    end

    % ====================================
    % 2本の折線同士の交点を検出・描画し、それに名称を付けて、
    % 　ハンドルも取得。
    % 　この交点は、新たな折れ点として両折線に追加される。
    % ====================================
    
    function hPs = intersection_of_polylines( ...
                                   obj,ha,tagL1,tagL2,tagPcell,option)
    
      % 2本の折線同士の交点を検出・描画し、それに名称を付けて、
      % 　ハンドルも取得。
      % 　この交点は、新たな折れ点として両折線に追加される。
      % 【入力】
      % ha:        現axesのハンドル
      % tagL1:     1本目の折線の名称（char型）
      % tagL2:     2本目　　　　　〃
      % tagPcell:  交点に付ける名称（char型）群からなるセル配列。
      %            　１点だけの場合は、{ }で囲まなくても可。
      %            　交点の数が予測できなければ、多めに指定しておく。
      %            　余った名称は廃棄される。
      %            　足りなけければ警告が出るので増やす。
      % 【入力（任意）】
      % option:    隠し引数。必要に応じて 1 か 2 を入力。
      %            　option本目の折線への交点の挿入がスキップされる。
      % 【出力】
      % hPs:       交点のハンドル群（行ベクトル）
      %            　要素の並び順は、
      %            　option無指定のとき、プログラムの発見順。
      %            　option=1のとき、2本目の折線の経路順。
      %            　option=2のとき、1本目の折線の経路順。

      if ~iscell(tagPcell)
        tagPcell={tagPcell};
      end

      obj.check_duplication_of_tagname(ha,tagPcell);

      h1=obj.get_handles_from_tagname(ha,tagL1);
      h2=obj.get_handles_from_tagname(ha,tagL2);
      x1=h1.XData;  y1=h1.YData;
      x2=h2.XData;  y2=h2.YData;
    
      hPs=[];
    
      ni=0;   % 見つかった交点の数
      for n1=1:length(x1)-1
        XD1=[x1(n1) x1(n1+1)];
        YD1=[y1(n1) y1(n1+1)];
        for n2=1:length(x2)-1
          XD2=[x2(n2) x2(n2+1)];
          YD2=[y2(n2) y2(n2+1)];
          [x,y,k1,k2]=obj.intersection_of_lines_xy(XD1,YD1,XD2,YD2);
                      % x,y: 交点の座標、k1,k2: 交点の線分上の位置
          if ~isnan(x)
            ni=ni+1;
            h=plot(ha,x,y,'.','Color','#888','MarkerSize',16);
            if length(tagPcell)>=ni
              h.Tag=tagPcell{ni};
            else
              disp([newline '交点が' num2str(ni) '個あります。' ...
                    'つける名称の数が不足しています。' newline]);
              error('　');
            end
            hPs=[hPs h];
            where(ni,:)=[n1 n2 k1 k2 x y ni];  % 交点の位置を記録する行列
          end
        end       % end of for n2=
      end         % end of for n1=
    
      if ni==0
        return;
      end

      % 折線への交点の挿入

      N_start=1; N_stop=2;        % 一般には、折線1,2の両方を処理。
                                  % 　このとき、交点のハンドルは、
                                  % 　交点の発見順で並んだまま。
      if (nargin>=6 & option==1)  % このとき、折線1へは処理をしない。
                                  % 　（引数の数には「obj」も含む）
        N_start=2;
        % 交点のハンドルは、折線2の始点→終点の経路順で並べ変える。
        h_where=sortrows(where,[2,4]);
        hPs=hPs(h_where(:,7)');
      elseif (nargin>=6 & option==2)
        N_stop=1;                % このとき、折線2への処理はしない
        % 交点のハンドルは、折線1の始点→終点の経路順で並べ変える。
        h_where=sortrows(where,[1,3]);
        hPs=hPs(h_where(:,7)');
      end
      for nn=N_start:N_stop       % 指定した折線について交点の挿入処理
        if nn==1
          xn=x1; yn=y1;           % 新折線データの初期値
          % 挿入順に合わせてwhereを並べ替え。
          % 　折線の若番順に並べ、同一線分上に複数の交点があるときは、
          % 　さらに線分上の位置の昇順に並べる。
          where=sortrows(where,[1,3]);   % 挿入順に合わせて並べ替え
          pos=where(:,1)';        % 折線上の交点を挿入する位置
        else
          xn=x2; yn=y2;
          where=sortrows(where,[2,4]);   % 挿入順に合わせて並べ替え
          pos=where(:,2)';
        end

        for np=1:size(where,1)    % 交点数だけ繰り返す
          xn=[xn(1:pos(np)) where(np,5) xn(pos(np)+1:end)];  % 交点挿入
          yn=[yn(1:pos(np)) where(np,6) yn(pos(np)+1:end)];
          pos=pos+1;              % 後続する交点の挿入位置を1つ増やす
        end
    
        if nn==1
          set(h1,'XData',xn,'YData',yn);
        else
          set(h2,'XData',xn,'YData',yn);
        end
      end       % end of for nn=
    end

    % ====================================
    % 図形のサイズの変更
    % ====================================

    function hLPs=resize_drawing(obj,ha,tagP0,kxy,tagLPcell)

      % 図形のサイズの変更
      % 【入力】
      % ha:         現axesのハンドル
      % tagP0:      変形の中心点の名称（char型）
      % kxy:        x,y方向の倍率（[kx ky]形式の配列）。
      %             　スカラーのときは、x,yとも同倍率。
      % tagLPcell:  図形を構成する点や線の名称（char型のセル配列）
      %             　tagP0は含めない。
      % 【出力】
      % hLPs:       サイズ変更後の、tagLPcellの各要素のハンドル

      if ~iscell(tagLPcell)
        tagLPcell={tagLPcell};
      end
      if length(kxy)==1
        kxy=[kxy kxy];
      end

      hP0=obj.get_handles_from_tagname(ha,tagP0);
      x0=hP0.XData;
      y0=hP0.YData;
      hLP=obj.get_handles_from_tagname(ha,tagLPcell);
      kx=kxy(1);
      ky=kxy(2);
      hLPs=[];
      for n=1:length(hLP)
        x=kx*(hLP(n).XData-x0)+x0;
        y=ky*(hLP(n).YData-y0)+y0;
        set(hLP(n),'XData',x,'YData',y);
        hLPs=[hLPs hLP(n)];
      end

    end

    % ====================================
    % 図形の回転
    % ====================================

    function hLPs=rotate_drawing(obj,ha,tagP0,ang,tagLPcell)

      % 図形の回転
      % 【入力】
      % ha:         現axesのハンドル
      % tagP0:      回転の中心点の名称（char型）
      % ang:        回転角度 [degree]
      % tagLPcell:  図形を構成する点や線の名称（char型のセル配列）
      %             　tagP0は含めない。
      % 【出力】
      % hLPs:       回転後の、tagLPcellの各要素のハンドル

      if ~iscell(tagLPcell)
        tagLPcell={tagLPcell};
      end

      hP0=obj.get_handles_from_tagname(ha,tagP0);
      x0=hP0.XData;
      y0=hP0.YData;
      hLP=obj.get_handles_from_tagname(ha,tagLPcell);
      R=[cosd(ang) -sind(ang);sind(ang) cosd(ang)];
      hLPs=[];
      for n=1:length(hLP)
        xy=R*[hLP(n).XData-x0;hLP(n).YData-y0];
        x=xy(1,:)+x0;
        y=xy(2,:)+y0;
        set(hLP(n),'XData',x,'YData',y);
        hLPs=[hLPs hLP(n)];
      end

    end

    % ====================================
    % コピーして貼り付け
    % ====================================

    function hLPs=copy_and_paste(obj,ha,tagP0,tagP1,tagLPcell)

      % コピーして貼り付け
      % 【入力】
      % ha:         現axesのハンドル
      % tagP0:      コピーする図形の中心点の名称（char型）
      % tagP1:      コピー先の中心点の名称（char型）
      % tagLPcell:  コピーする図形を構成する点や線の名称。
      %             　（char型のセル配列）。tagP0は含めない。
      %             　コピー先での線や点の名称は、コピー前と同一名称
      %             　となる。一方、コピー元の線や点は、tagLPcellで
      %             　指示した文字列の後に'*'を付加した名称に変え、
      %             　同一名称の重複を避ける。
      % 【出力】
      % hLPs:       コピー先での、tagLPcellの各要素へのハンドル。

      if ~iscell(tagLPcell)
        tagLPcell={tagLPcell};
      end

      h=findall(ha,'Type','Line');       % 既存の名称を含む情報を取得。

      if ~isempty(h)
        for n=1:length(tagLPcell)
          tagLPx=[tagLPcell{n} '*'];
          for m=1:length(h)
            if strcmp(tagLPx,h(m).Tag)    % 既存名称と比較
              disp([newline 'コピー元にある名称 ''' tagLPcell{n} ...
                     ''' は、コピーが済んだ後に ''' tagLPx ...
                     ''' に改名予定ですが、' newline 'この名称は' ...
                     '既に使用されています。' newline '既存の名称' ...
                     'を変えるか、コピー元の名称を変えてください。'...
                     newline]);
              error('　');
            end
          end
        end
      end

      hP0=obj.get_handles_from_tagname(ha,tagP0);
      x0=hP0.XData;
      y0=hP0.YData;
      hP1=obj.get_handles_from_tagname(ha,tagP1);
      x1=hP1.XData;
      y1=hP1.YData;
      hLP=obj.get_handles_from_tagname(ha,tagLPcell);

      hLPs=copyobj(hLP,gca);
      for n=1:length(hLP)
        hLP(n).Tag=[hLP(n).Tag '*'];
                      % 線や点の既存名称はコピー先に継承させ、
                      % 　コピー元の名称の末尾には'*'を追加する。
        x=hLP(n).XData-x0+x1;
        y=hLP(n).YData-y0+y1;
        set(hLPs(n),'XData',x,'YData',y);
      end

    end

    % ====================================
    % 指定した名称から、その点や線のハンドルを取得
    % ====================================
    
    function hLPs=get_handles_from_tagname(obj,ha,tagLPcell,option)

      % 指定した名称から、その点や線のハンドルを取得
      % 【入力】
      % ha:         現axesのハンドル
      % tagLPcell:  線や点の名称（char型）を要素とするセル配列。
      %             　名称が１つだけの場合は、{ }で囲まなくても可。
      % 【入力（任意）】
      % option:     隠し引数。
      %             　これを入力しないとき、指定した名称が存在しな
      %             　　ければ、そのことを警告し、エラーで停止する。
      %             　0を入力したとき、指定した名称が存在しなくても、
      %             　　エラー扱いにはせず実行を続ける。
      %             　　ただし、tagLPcellの要素数よりもhLPsの要素数が
      %             　　少なくなることがあるので注意が必要。
      % 【出力】
      % hLPs:        指定名称の線や点へのハンドルを要素とする配列

      if ~iscell(tagLPcell)
        tagLPcell={tagLPcell};
      end
    
      hLPs=[];
      for n=1:length(tagLPcell)
        h=findall(ha,'Tag',tagLPcell{n});
        if ~isempty(h)
          hLPs=[hLPs h];
        else
          if (nargin>=4)&(option==0)
            continue;
          else
            disp([newline '名称''' tagLPcell{n} ...
                                    '''は存在しません。' newline])
            error('　')
          end
        end
      end

    end

    % ====================================
    % ２点間の直線距離を取得する。
    % ====================================

    function len=get_length_between_two_points(obj,ha,tagP1,tagP2)

      % ２点間の直線距離を取得する。
      % 【入力】
      % ha:      現axesのハンドル
      % tagP1:   １つ目の点の名称（char型）
      % tagP2:   ２つ目　　　　〃
      %          　tagP1,tagP2 の記述順序は問わない。
      % 【出力】
      % len:     ２点間の直線距離

      h1=obj.get_handles_from_tagname(ha,tagP1);
      h2=obj.get_handles_from_tagname(ha,tagP2);
      x1=h1.XData;
      y1=h1.YData;
      x2=h2.XData;
      y2=h2.YData;
      len=hypot(x2-x1,y2-y1);

    end

    % ====================================
    % 線（曲線も可）の長さを取得する。
    % ====================================

    function len=get_length_of_line(obj,ha,tagL)

      % 線（曲線も可）の長さを取得する。
      % 【入力】
      % ha:      現axesのハンドル
      % tagL:    長さを計測する線の名称（char型）
      % 【出力】
      % len:     線の長さ

      h=obj.get_handles_from_tagname(ha,tagL);
      X=h.XData;
      Y=h.YData;
      dX=diff(X);
      dX=[0 dX];
      dY=diff(Y);
      dY=[0 dY];
      dL=hypot(dX,dY);
      L=cumsum(dL);
      len=L(end);

    end

    % ====================================
    % 直線の傾き角度を取得する
    % ====================================

    function ang=get_polar_angle_of_line(obj,ha,tagL)

      % 直線の傾き角度を取得する
      % 【入力】
      % ha:      現axesのハンドル
      % tagL:    直線の名称（char型）
      % 【出力】
      % ang:     直線の角度[degree]。-180 ～ +180。
      %          　直線を、始点から終点方向を向く
      %          　ベクトルとみなし、x軸方向を基準（0[deg]）として、
      %          　反時計回り/時計回り の角度を +/- とする。

      h=obj.get_handles_from_tagname(ha,tagL);
      X=h.XData;
      Y=h.YData;
      ang=atan2d(Y(end)-Y(1),X(end)-X(1));
    end

    % ====================================
    % ２直線間の角度差を取得する。
    % ====================================

    function ang=get_angle_between_two_lines(obj,ha,tagL1,tagL2)

      % ２直線間の角度差を取得する。
      % 【入力】
      % ha:      現axesのハンドル
      % tagL1:  １本目の線の名称（char型）
      % tagL2:  ２本目の線　　　〃
      % 【出力】
      % ang:    １本目の線から見た２本目の線の角度[degree]。
      %         　反時計回り/時計回り の角度を +/- とする。
      %         　ただし、tagL1,tagL2 の各線をベクトルとみなし、
      %         　線の配列要素が、若番→老番の方向を正の向きとする。
      %         　期待する向きと異なる場合は ang±90 で補正のこと。

      h1=obj.get_handles_from_tagname(ha,tagL1);
      h2=obj.get_handles_from_tagname(ha,tagL2);
      X1=h1.XData;
      Y1=h1.YData;
      X2=h2.XData;
      Y2=h2.YData;
      ang1=atan2d(Y1(end)-Y1(1),X1(end)-X1(1));
      ang2=atan2d(Y2(end)-Y2(1),X2(end)-X2(1));
      ang=ang2-ang1;

    end

    % ====================================
    % 円や円弧の曲率半径を推定する。ついでに、両端の接線の角度も。
    % ====================================

    function [R,ang]=get_radius_of_curvature(obj,ha,tagL)

      % 円や円弧の曲率半径を推定する。ついでに、両端の接線の角度も。
      % 　クラス内の関数から使うのが主目的で、ユーザーが直接使う
      % 　ことはあまり想定していない。
      % 　（長さが0の線分の存在もあり得るため、円弧の個々の線分の
      % 　　データの使用は避けたアルゴリズムにしている）
      % 【入力】
      % ha:     現axesのハンドル
      % tagL:   円弧の名称（char型）
      % 【出力】
      % R:      円弧の曲率半径の推定値。ほぼ直線のときはInf。
      %         　円弧の正方向を向いて進むとき、左/右へのカーブは +/-。
      % ang     円弧の始点/終点部に付けるべき矢印ベクトルの傾き。
      %         　x軸を基準とした角度 [degree]。
      %         　反時計回りは+、時計回りは-。
      %         　始点はang(1)、終点はang(2)

      h=obj.get_handles_from_tagname(ha,tagL);
      X=h.XData;
      Y=h.YData;
      % 閉じた円の場合には、Ldが0となって計算できないので 切断する。
      % 　プロパティの書き換えはしないので、既成図には影響なし。
      if (X(1)==X(end)) & (Y(1)==Y(end))
        X(end)=[];
        Y(end)=[];
      end

      Xd=X(end)-X(1);
      Yd=Y(end)-Y(1);
      Vd=[Xd Yd 0];         % 弦を[始点から終点に向かうベクトル]と見る

      mX=mean(X);           % 円弧の折点群の重心の座標
      mY=mean(Y);
      Vm=[mX-X(1) mY-Y(1) 0];   % 始点から重心に向かうベクトル

      dX=diff(X);
      dX=[0 dX];
      dY=diff(Y);
      dY=[0 dY];
      dL=hypot(dX,dY);      % 円弧を構成する折線の各線分の長さ
      Lcx=cumsum(dL);       % 円弧の始点から各折点までの長さ
      Lc=Lcx(end);          % 円弧の長さ
      Ld=hypot(Xd,Yd);      % 弦の長さ
      thLd=atan2d(Yd,Xd);   % 弦ベクトルの傾き角度

      if (Lc-Ld)/Ld<2e-6    % 円弧がほぼ直線とみなせるとき
        R=Inf;
        ang2=thLd;          % 終点側
        ang1=ang2+180;      % 始点側
        ang=[ang1 ang2];
        return
      end

      % 直線とはみなせない円弧のとき、半径=Ld/(2*k)の式のkを求める
      k=fzero(@(k)(sin(k*Lc/Ld)-k),[0.0001 pi*Ld/Lc]);
      thhalf=asind(k);      % [弦に相当する部分の円周角]の半分

      % 円弧のカーブしている方向を判定
      CC=cross(Vm,Vd);
      dir=sign(CC(3));   % 反時計方向へのカーブでdir=1、時計方向で-1
      if dir==0
        dir=1;
      end

      R=dir*Ld/(2*k);    % 推定された半径

      % 円弧の始点/終点部に付けるべき矢印ベクトルの傾き。
      if Lc/Ld<=pi/2
        ang1=thLd+180-dir*thhalf;
        ang2=thLd+dir*thhalf;
      else
        ang1=thLd+dir*thhalf;
        ang2=thLd+180-dir*thhalf;
      end
      ang=[ang1 ang2];

    end

    % ====================================
    % 点の近くや、線の中央にその名称を表示。
    % 　線については、始点から75%の長さの線上に、
    % 　若番→老番の方向に赤色矢印も表示。
    % ====================================
    
    function hTs=show_all_the_tagnames(obj,ha)
    
      % 点の近くや、線の中央（始点から50%の長さの位置）に、その名称を
      % 　表示。線については、始点から75%の長さの線上に、若番→老番の
      % 　方向に赤色矢印も表示。
      % 　名称の文字が重なって見え難い場合でも、拡大すれば確認は可能。
      % 　一枚の図の中でこれを複数回使用すると、古い表示と新しい表示が
      % 　重なって勘違いの元になるので注意が必要。
      % 【入力】
      % ha:    現axesのハンドル
      % 【出力】
      % hTs:   点や線の名称（textオブジェクト）へのハンドル。
      %        　textの数に等しい要素数の配列。
      %        　（並び順はMATLAB任せなので不明）

      ax=axis(ha);
      dx=(ax(2)-ax(1))/80;  % 名称文字の、点や線からの相対位置
      dd=dx/5;        % text 同士が重なって判読不能となるのを回避。
                      % 　ddを最大値として、表示位置を乱数で少々移動。
                      % 　図を超拡大すれば、判読の確率が上がる。

      h=findall(ha,'Type','Line');

      hTs=[];
      Nh=length(h);
      for n=1:Nh
        ddx=rand*dd; ddy=ddx;
        if length(h(n).XData)==1        % 点のとき
          ht=text(ha,h(n).XData(1)+dx+ddx,h(n).YData(1)+ddy,h(n).Tag);
          hTs=[hTs ht];
        elseif length(h(n).XData)==2    % 単線分のとき
          x=(h(n).XData(1)+h(n).XData(2))/2;
          y=(h(n).YData(1)+h(n).YData(2))/2;
          ht=text(ha,x+dx+ddx,y+ddy,h(n).Tag,'HorizontalAlign','center');
          hTs=[hTs ht];
          % 線の正方向を示すV字
          spanx=(h(n).XData(2)-h(n).XData(1));
          spany=(h(n).YData(2)-h(n).YData(1));
          ang=atan2d(spany,spanx);
          x=x+spanx/4;
          y=y+spany/4;
          ht=text(ha,x,y,'V','Rotation',ang+90,'HorizontalAlign', ...
               'center','VerticalAlign','middle','Color','r');
          hTs=[hTs ht];
        else                            % 折線のとき
          X=h(n).XData;
          Y=h(n).YData;

          dX=diff(X);
          n_nan=find(isnan(dX));  % ハッチング線などに含まれる
          dX(n_nan)=0;            % 　NaN要素の影響を除く。
          dX=[0 dX];

          dY=diff(Y);
          n_nan=find(isnan(dY));  % 同上
          dY(n_nan)=0;            % 
          dY=[0 dY];

          dL=hypot(dX,dY);
          L=cumsum(dL);      % 線の各折点の始点からの長さ
          len=L(end);        % 線の全長
          lengths=[0.5 0.75]*len;   % 名称と方向マークの各表示位置
          for m=1:2
            difL=L-lengths(m);
            nn=min(find(difL>0));   % 指定点は、指定線の折れ点番号が
                                    % 　nn-1とnnの間の区間に存在する。
                                    % 　（条件より、nnは最低でも2）
            span=L(nn)-L(nn-1);     % 存在区間の長さ。条件より、span≠0。
            kk=1-difL(nn)/span;     % 存在区間の長さを1.0として、指定点は
                                    % 　若番側の端からkkの位置にある。
            % 表示指定点の座標
            x=X(nn-1)+kk*(X(nn)-X(nn-1));
            y=Y(nn-1)+kk*(Y(nn)-Y(nn-1));

            if m==1
              % 線の名称を表示
              ht=text(ha,x+dx+ddx,y+ddy,h(n).Tag,'HorizontalAlign','center');
              hTs=[hTs ht];
            else
              % 線の正方向を示すV字を表示
              spanx=(h(n).XData(nn)-h(n).XData(nn-1));
              spany=(h(n).YData(nn)-h(n).YData(nn-1));
              ang=atan2d(spany,spanx);
              ht=text(ha,x,y,'V','Rotation',ang+90,'HorizontalAlign', ...
                   'center','VerticalAlign','middle','Color','r');
              hTs=[hTs ht];
            end                 % end of if m==1
          end                   % end of for m=1:2
        end                     % end of if length(h(n).XData)
      end                       % end of for n=1:Nh

    end                         % end of function

    % ====================================
    % 命名しようとしている線や点の名称が、他と重複していないかをチェック
    % ====================================

    function check_duplication_of_tagname(obj,ha,tagLPcell)
      
      % 命名しようとしている線や点の名称が、
      % 　他と重複していないかをチェックする。
      % 　クラス内の他のコマンドからの呼び出しを目的としたもの。
      % 【入力】
      % ha:         現axesのハンドル
      % tagLPcell:  重複をチェックしたい名称（char型のセル配列）。
      % 【出力】
      % 重複があれば、その名称がコマンドラインに表示される

      h=findall(ha,'Type','Line');       % 既存の名称を含む情報を取得。

      if ~isempty(h)
        for n=1:length(tagLPcell)        % 指定した名称分だけ繰り返す。
          for m=1:length(h)
            if strcmp(tagLPcell{n},h(m).Tag)    % 既存名称と比較
              disp([newline '指定の名称 ''' tagLPcell{n} ...
                              ''' は既に使用されています。' newline])
              error(' ')
            end
          end
        end
      end

    end

    % ====================================
    % 点や線の名称を変える
    % ====================================

    function change_tagname(obj,ha,tagLPcell_old,tagLPcell_new)

      % 点や線の名称を変える
      % 【入力】
      % ha:             現axesのハンドル
      % tagLPcell_old:  改名前の名称（char型のセル配列）。
      % tagLPcell_new:  改名後の名称（char型のセル配列）。
      %        _oldと_newの各要素は対応づけられた順に並んでいること。
      % 【出力】
      % 　　戻り値は無し

      if ~iscell(tagLPcell_old)
        tagLPcell_old={tagLPcell_old};
      end
      if ~iscell(tagLPcell_new)
        tagLPcell_new={tagLPcell_new};
      end
      if length(tagLPcell_new)~=length(tagLPcell_old)
        disp([newline '改名の前後で、名称の数が' ...
                                      '一致していません。' newline])
        error('　')
      end

      obj.check_duplication_of_tagname(ha,tagLPcell_new);

      for n=1:length(tagLPcell_new)
        h=obj.get_handles_from_tagname(ha,tagLPcell_old{n});
        h.Tag=tagLPcell_new{n};
      end

    end

    % ====================================
    % figureの上下左右の余白調整
    % ====================================

    function trim_and_expand_figure_margin(obj,hf,KK)
    
      % 【入力】
      % 　hf:  余白調整したいfigureのハンドル
      % 　KK:  余白の変更量から成る配列。 単位は[pixel]。
      % 　     　（[上余白 下余白 左余白 右余白]。増量は+、減量は-）
      % 【出力】
      % 　返り値は無し
    
      posf=hf.Position;            % 余白変更前のfigureのposition
                                   % 　[左端 下端 幅 高さ]、単位は[pixel]。
      naxe=size(hf.Children,1);    % figure内のChildren（axesやcolorbar）
                                   % 　などの総数。
    
      % 全Childrenのposition行列を作成
      % 　各行がそれぞれのChildrenに対応。
      % 　各列は[左端 下端 幅 高さ]、単位は[normalized]。
    
      Posa=[];                     % position行列の初期値
      for n=1:naxe
        axes(hf.Children(n))
        hn(n)=gca;
        Posa=[Posa ; hn(n).Position];
      end
    
      % 各Childrenの、余白変更前のfigure内でのpixel単位でのPositionを計算。
      Posp(:,[1 3])=Posa(:,[1 3])*posf(3);
      Posp(:,[2 4])=Posa(:,[2 4])*posf(4);
      
      % 余白変更後のfigureのpixel単位でのPositionを計算。
      posfx=posf+[-KK(3) -KK(2) KK(3)+KK(4) KK(1)+KK(2)];
      
      % 余白変更後のfigure内での各Childrenのpixel単位でのPositionを計算する。
      Pospx(:,1)=Posp(:,1)+KK(3);
      Pospx(:,2)=Posp(:,2)+KK(2);
      Pospx(:,3)=Posp(:,3);
      Pospx(:,4)=Posp(:,4);
      
      % 余白変更後の各Childrenのnormalized単位でのPositionを計算する。
      Posax(:,[1 3])=Pospx(:,[1 3])/posfx(3);
      Posax(:,[2 4])=Pospx(:,[2 4])/posfx(4);
      
      % figureとChildrenのpositionを更新
      hf.Position=posfx;
      for nn=1:naxe
        hn(nn).Position=Posax(nn,:);
      end
    
    end

    % ====================================
    % コマンド一覧をコマンドラインに表示する。
    % ====================================

    function command(obj)

    % コマンドラインからの実行用。引数なし。
    % コマンド一覧をコマンドラインに表示する。
    % 使いたいコマンドの行をプログラムにコピペして利用するのに便利。

      disp([ newline ...
       '=== 描画系 ===' newline ...
       'hPs=         AA.draw_points_xy                (ha,xys,tagPcell);' newline ...
       'hPs=         AA.draw_points_on_the_line       (ha,tagL,lengths,tagPcell,option);' newline ...
       'hLPs=        AA.draw_polyline_xy              (ha,x,y,tagL,tagPb,tagPe);' newline ...
       'hL=          AA.draw_polyline                 (ha,tagPcell,tagL);' newline ...
       'hLPs=        AA.draw_sloped_line              (ha,tagPo,ang,len1,len2,tagL,tagP1,tagP2);' newline ...
       'hLPs=        AA.draw_parallel_line            (ha,tagLo,tagPo,len1,len2,tagL,tagP1,tagP2);' newline ...
       'hLPs=        AA.draw_perpendicular_line       (ha,tagLo,tagPo,len1,len2,tagL,tagP1,tagP2);' newline ...
       'hL=          AA.draw_hatching_lines           (ha,tagLc,pitch,ang,tagHL);' newline ...
       'hL=          AA.draw_circle                   (ha,r,tagPc,tagL,nagon,ang);' newline ...
       'hL=          AA.draw_free_curve               (ha,tagPcell,angs,tagL);' newline ...
       'hL=          AA.draw_cloud_shaped_line        (ha,tagL0,ndiv,kr,tagLc,ang);' newline ...
       'hA=          AA.draw_arrowhead_at_line_tip    (ha,tagL,pos,ang,leng,tagV,dang,Ropt);' newline ...
       '=== 編集系 ===' newline ...
       'hL=          AA.surround_area_by_a_loop       (ha,tagLcell,tagL);' newline ...
       'hL=          AA.combine_lines_into_one        (ha,tagLcell);' newline ...
       'hL=          AA.change_line_to_broken_one     (ha,tagL,pattern,pitch,pos0);' newline ...
       'hRPs=        AA.round_the_corner              (ha,r,tagP,tagL1,tagL2,tagR,tagPcell);' newline ...
       'hL=          AA.invert_direction_of_path      (ha,tagL);' newline ...
       '             AA.change_property_of_lines      (ha,prop,var,tagLPcell);' newline ...
       'hLs=         AA.erase_open_line_partially     (ha,tagL,tagP1,tagP2,tagL2);' newline ...
       'hL=          AA.erase_closed_line_partially   (ha,tagL,tagP1,tagP2,direction);' newline ...
       '             AA.erase_lines_and_points        (ha,tagLPcell);' newline ...
       '[x,y,k1,k2]= AA.intersection_of_lines_xy      (XD1,YD1,XD2,YD2);' newline ...
       'hPs=         AA.intersection_of_polylines     (ha,tagL1,tagL2,tagPcell,option);' newline ...
       'hLPs=        AA.resize_drawing                (ha,tagP0,kxy,tagLPcell);' newline ...
       'hLPs=        AA.rotate_drawing                (ha,tagP0,ang,tagLPcell);' newline ...
       'hLPs=        AA.copy_and_paste                (ha,tagP0,tagP1,tagLPcell);' newline ...
       '=== 取得系 ===' newline ...
       'hLPs=        AA.get_handles_from_tagname      (ha,tagLPcell,option);' newline ...
       'len=         AA.get_length_between_two_points (ha,tagP1,tagP2);' newline ...
       'len=         AA.get_length_of_line            (ha,tagL);' newline ...
       'ang=         AA.get_polar_angle_of_line       (ha,tagL);' newline ...
       'ang=         AA.get_angle_between_two_lines   (ha,tagL1,tagL2);' newline ...
       '[R,ang]=     AA.get_radius_of_curvature       (ha,tagL);' newline ...
       '=== 補助系 ===' newline ...
       'hTs=         AA.show_all_the_tagnames         (ha);' newline ...
       '             AA.check_duplication_of_tagname  (ha,tagLPcell);' newline ...
       '             AA.change_tagname                (obj,ha,tagLPcell_old,tagLPcell_new);' newline ...
       '             AA.trim_and_expand_figure_margin (hf,KK);' newline ...
       '             AA.command' newline ...
       '             AA.help' newline ...
       '             AA.pause;' newline]);

    end

    % ====================================
    % ヘルプ一覧をコマンドラインに表示する。
    % ====================================

    function help(obj)

    % コマンドラインからの実行用。引数なし。
    % ヘルプ一覧をコマンドラインに表示する。
    % 表示された一覧から、ヘルプを見たいコマンドの行をコピーし、
    % コマンドラインの最新行に貼り付けて実行させる。

      disp([ newline ...
       '=== 描画系 ===' newline ...
       'help AA.draw_points_xy' newline ...
       'help AA.draw_points_on_the_line' newline ...
       'help AA.draw_polyline_xy' newline ...
       'help AA.draw_polyline' newline ...
       'help AA.draw_sloped_line' newline ...
       'help AA.draw_parallel_line' newline ...
       'help AA.draw_perpendicular_line' newline ...
       'help AA.draw_hatching_lines' newline ...
       'help AA.draw_circle' newline ...
       'help AA.draw_free_curve' newline ...
       'help AA.draw_cloud_shaped_line' newline ...
       'help AA.draw_arrowhead_at_line_tip' newline ...
       '=== 編集系 ===' newline ...
       'help AA.surround_area_by_a_loop' newline ...
       'help AA.combine_lines_into_one' newline ...
       'help AA.change_line_to_broken_one' newline ...
       'help AA.round_the_corner' newline ...
       'help AA.invert_direction_of_path' newline ...
       'help AA.change_property_of_lines' newline ...
       'help AA.erase_open_line_partially' newline ...
       'help AA.erase_closed_line_partially' newline ...
       'help AA.erase_lines_and_points' newline ...
       'help AA.intersection_of_lines_xy' newline ...
       'help AA.intersection_of_polylines' newline ...
       'help AA.resize_drawing' newline ...
       'help AA.rotate_drawing' newline ...
       'help AA.copy_and_paste' newline ...
       '=== 取得系 ===' newline ...
       'help AA.get_handles_from_tagname' newline ...
       'help AA.get_length_between_two_points' newline ...
       'help AA.get_length_of_line' newline ...
       'help AA.get_polar_angle_of_line' newline ...
       'help AA.get_angle_between_two_lines' newline ...
       'help AA.get_radius_of_curvature' newline ...
       '=== 補助系 ===' newline ...
       'help AA.show_all_the_tagnames' newline ...
       'help AA.check_duplication_of_tagname' newline ...
       'help AA.change_tagname' newline ...
       'help AA.trim_and_expand_figure_margin' newline ...
       'help AA.command' newline ...
       'help AA.help' newline ...
       'help AA.pause;' newline]);

    end

    % ====================================
    % レビュー用の条件付きの一時停止コマンド
    % ====================================

    function pause(obj)

      % レビュー用の条件付きの一時停止コマンド。
      % 　プログラムを一時停止させたい各位置にこのコマンドを書いておく。
      % 　事前に定義したglobal変数BBの条件に合えば、このコマンド位置で
      % 　プログラムの実行が一時停止し、必要に応じ、図中に線や点の名称
      % 　を表示する。次に進むにはEnterキーを押す。
      % 　
      % 【入力】,【出力】
      % 　簡単に利用できるように、引数・返り値とも不要にしている。
      % 　その代わりにglobal変数を使用している。
      % 　このコマンドを使用する前に、呼び出し側のプログラムに次の１行
      % 　を追加しておく必要がある。
      % 　global BB; BB=[n1,n2,n3];
      % 　　　n1: 正の整数。n1個目のpauseコマンドまでは、一時停止せず
      % 　　　　　　に直行し、そこで初めて停止する。
      % 　　　　　ただし、n1=0の場合は、すべてのpauseが無視される。
      % 　　　n2: 正の整数。n1で一時停止したあと、残りn2個分のpauseだ
      % 　　　　　　けは受付られるが、そこでプログラムの実行は完全停止
      % 　　　　　　する。最後まで実行させたいときには、残りのpauseコ
      % 　　　　　　マンドの総数よりも大きな値を指定する。Infでもよい。
      % 　　　n3: [1/0]のとき、点や線の名称を表示[する/しない]。
      % 　　　BB=[1,Inf,1]なら、全Pause位置で一時停止し、名称も表示。
      % 　この宣言が無い場合には、全てのpauseコマンドは無視される。
      % 　（ただし、最初のpauseコマンド行より前に clear global が記述
      %   　されていること）。

      global BB;
      if isempty(BB)
        return;
      end
      if length(BB)==3
        BB=[BB 0];
      end
      stop1=BB(1);
      stepx=BB(2);
      showt=BB(3);
      count=BB(4);
      
      count=count+1;

      if stop1==0
        BB(4)=count;
        return
      end
      if count<stop1;
        BB(4)=count;
        return
      elseif count<=stop1+stepx
        ST=dbstack(1);
        disp(['行' num2str(ST.line) '（プログラム ' ST.name  ...
              ' ）の実行を終えて停止中です。何かキーを押せば次に進みます。'])
        if showt==1
          h=obj.show_all_the_tagnames(gca);
        end
        if count~=stop1+stepx
          pause
          if showt==1
            delete(h)
          end
        else
          disp([newline '指示により、ここまでで実行を停止します。' ...
                 newline '以下にエラーが表示されますが、' ...
                '形式的なものなので気にする必要はありません。' newline]);
          error('　');
        end
      end
      BB(4)=count;

    end

    % ====================================

  end       % end of methods
end         % end of classdef
```

</details>