MFC と SVG のクリップパス

## クリップパスのかけ方
MFC と SVG に共通して使えるのはパスと四角形の指定によるクリップです。
SVG では多角形や円、楕円なども指定することができますが、 MFC でもパスを利用することで同様のクリッピングを行えます。
クリッピングではパスに含まれる曲線を考慮してを実装するのがよろしいかとは思いますが、本稿では解説のため多角形のパス指定のみに絞り解説します。
曲線に対応したクリップパスを実装したい場合は[MFC から SVG のパス](https://qiita.com/frashell/private/993137f8d65d09b10ff2)を参照してカスタムしてください。

## クリップパス
返り値としてクリップ名を返すようにしておきます。
クリップしたい形状に対して属性値に設定する形になります。

```diff_cpp:SvgImage.h
class SvgImage
{
protected:
+   int m_nClip; // クリップの通し番号用
public:
+   LPCTSTR AddClipPath(CPoint* lpPos, int nSize, int nDepth = 0);
}
```
```cpp:SvgImage.cpp
LPCTSTR SvgImage::AddClipPath(CPoint* lpPos, int nSize, int nDepth)
{
	CString clipName;
	SvgTag *svgClipPath = new SvgTag();
	svgClipPath->m_strName = L"clipPath";
	svgClipPath->m_nDepth = nDepth + 1;
	svgClipPath->m_bOne = FALSE;

	svgClipPath->m_arrAttr.Add(L"id");
	clipName.Format(L"clip%d", m_nClip);
	m_nClip++;
	svgClipPath->m_arrAttrValue.Add(clipName);

	m_arrayTag.Add(svgClipPath);

	AddPolygon(lpPos, nSize, 0, nDepth + 2);
	AddAttribute(L"fill", L"none");
	return clipName;
}
```
クリップ名を決定して id 属性として追加します。
```cpp
	svgClipPath->m_arrAttr.Add(L"id");
	clipName.Format(L"clip%d", m_nClip);
	m_nClip++;
	svgClipPath->m_arrAttrValue.Add(clipName);
```
クリップパスの子タグとして多角形を追加します。
パスや楕円なども追加できますので必要に応じて SvgImage::AddClipPath をオーバーライドするなどして追加してください。
```cpp
	AddPolygon(lpPos, nSize, 0, nDepth + 2);
	AddAttribute(L"fill", L"none");
```

## クリップパスの指定
クリップパス名を多角形や楕円など描画したいタグの属性に追加することでクリッピングが行われます。
```diff_cpp:SvgImage.h
class SvgImage
{
public:
+   void AddAttrClip(LPCTSTR strClip);
}
```
```cpp:SvgImage.cpp
LPCTSTR SvgImage::AddAttrClip(LPCTSTR strClip)
{
	CString strClipUrl;
	strClipUrl.Format(L"url(#%s)", strClip);
	pSvgImage->AddAttribute(L"clip-path", strClipUrl);
}
```

## クリップパスを追加したいとき
同じ名前で多角形を追加したいときはクリップ名を指定したクリップパスの追加処理を行います。
任意のクリッピング名で追加されないことも考慮して新たにクリップパスタグも追加されるようにしています。

```diff_cpp:SvgImage.h
class SvgImage
{
public:
+   LPCTSTR AddClipPath(CPoint* lpPos, int nSize, LPCTSTR name, int nDepth = 0);
}
```
```cpp:SvgImage.cpp
LPCTSTR mdSvgImage::AddClipPath(CPoint* lpPos, int nSize, LPCTSTR name, int nDepth)
{
	CString clipName = name;
	int nSizeTags = (int)m_arrayTag.GetSize();
	int i = 0;
	BOOL bExist = FALSE;

	while ((i < nSizeTags) && (!bExist))
	{
		SvgTag* pSvgTag = m_arrayTag.GetAt(i);
		int nSizeAttr = (int)pSvgTag->m_arrAttr.GetSize();
		if (pSvgTag->m_strName.CompareNoCase(L"clipPath") == 0)
		{
			int nSizeAttr = pSvgTag->m_arrAttr.GetSize();
			for (int j = 0; j < nSizeAttr; j++)
			{
				if (pSvgTag->m_arrAttr.GetAt(j).CompareNoCase(L"id") == 0)
				{
					if (clipName.CompareNoCase(pSvgTag->m_arrAttrValue.GetAt(j)) != 0)
                         continue;
					bExist = TRUE;
                    break;
				}
			}
		}
		i = !bExist ? i + 1 : i;
	}
	if (!bExist)
	{
		SvgTag* svgClipPath = new SvgTag();
		svgClipPath->m_strName = L"clipPath";
		svgClipPath->m_nDepth = nDepth + 1;
		svgClipPath->m_bOne = FALSE;

		svgClipPath->m_arrAttr.Add(L"id");
		svgClipPath->m_arrAttrValue.Add(clipName);

		m_arrayTag.Add(svgClipPath);
	}
	InsertPolygon(lpPos, nSize, i, 0, nDepth + 2);
	AddAttribute(L"fill", L"none");
	return clipName;
}
```
指定されたクリップ名が存在するかどうかを確認します。
全タグ検索し、clipPath タグが見つかればその id 属性を確認してクリップ名が同じならば既に存在しているクリップ名として扱います。
変数 i は後で利用するため、見つかった clipPath タグの直後の位置に設定してループを抜けます。
```cpp
	int i = 0;
	BOOL bExist = FALSE;

	while ((i < nSizeTags) && (!bExist))
	{
		SvgTag* pSvgTag = m_arrayTag.GetAt(i);
		int nSizeAttr = (int)pSvgTag->m_arrAttr.GetSize();
		if (pSvgTag->m_strName.CompareNoCase(L"clipPath") == 0)
		{
			int nSizeAttr = pSvgTag->m_arrAttr.GetSize();
			for (int j = 0; j < nSizeAttr; j++)
			{
				if (pSvgTag->m_arrAttr.GetAt(j).CompareNoCase(L"id") == 0)
				{
					if (clipName.CompareNoCase(pSvgTag->m_arrAttrValue.GetAt(j)) != 0)
                         continue;
					bExist = TRUE;
                    break;
				}
			}
		}
		i = !bExist ? i + 1 : i;
	}
```
クリップ名が存在しなければ追加します。
```cpp
	if (!bExist)
	{
		SvgTag* svgClipPath = new SvgTag();
		svgClipPath->m_strName = L"clipPath";
		svgClipPath->m_nDepth = nDepth + 1;
		svgClipPath->m_bOne = FALSE;

		svgClipPath->m_arrAttr.Add(L"id");
		svgClipPath->m_arrAttrValue.Add(clipName);

		m_arrayTag.Add(svgClipPath);
	}
```
多角形をクリップパスの中に追加します。
AddPolygon 関数だと全タグの最後に追加されてしまうため、 InsertPolygon を追加して実装します。
```cpp
	InsertPolygon(lpPos, nSize, i, 0, nDepth + 2);
```

## 多角形タグを任意の位置に追加
クリップパスを追加したい際に呼び出す SvgImage::InsertPolygon 関数の実装です。

```diff_cpp:SvgImage.h
class SvgImage
{
public:
+  void InsertPolygon(CPoint* lpPos, int nSize, int nLocate, int nDepth = 0);
}
```
```cpp
void SvgImage::InsertPolygon(CPoint* lpPos, int nSize, int nLocate, int nDepth)
{
	SvgTag *pSvgPath = new SvgTag();
	pSvgPath->m_strName = L"polygon";
	pSvgPath->m_nDepth = nDepth + 1;
	pSvgPath->m_bOne = TRUE;

	// pathを作る
	CString csStr;
	CString csPath = L"";
	for (int i = 0; i < nSize; i++)
	{
		mdPoint mPos = lpPos[i];
		csStr = L"";
		double dLeft = mPos.x - m_rectAll.left;
		double dTop = m_rectAll.bottom - mPos.y;
		if (i == 0)
		{
			csStr.Format(_T("%lf %lf"), dLeft, dTop);
		}
		else {
			csStr.Format(_T(", %lf %lf"), dLeft, dTop);
		}
		csPath += csStr;
	}
	pSvgPath->m_arrAttr.Add(L"points");
	pSvgPath->m_arrAttrValue.Add(csPath);

	if (m_arrayTag.GetSize() > nLocate)
	{
		m_arrayTag.InsertAt(nLocate + 1, pSvgPath);
	}
	else
	{
		m_arrayTag.Add(pSvgPath);
	}
}
```
タグの挿入処理を行います。
挿入できない場合は追加処理に変更します。
```cpp
	if (m_arrayTag.GetSize() > nLocate)
	{
		m_arrayTag.InsertAt(nLocate + 1, pSvgPath);
	}
	else
	{
		m_arrayTag.Add(pSvgPath);
	}
```