//型変換 public static Poly ParsePolyFromQRCode(Procon2017MCTProtocol.SendablePolygon polygon, bool isPiece, sbyte initPieceId = -1) { int i; List <Point> points = new List <Point>(); List <Line> lines = new List <Line>(); for (i = 0; i < polygon.Points.Count; i++) { points.Add(new Point(polygon.Points[i].X, polygon.Points[i].Y)); } points.Add(points[0]); //表示する線分の設定 (ピースのみ) if (isPiece) { for (i = 0; i < points.Count - 1; i++) { lines.Add(new Line(points[i], points[i + 1], initPieceId)); } } Poly poly = new Poly(points, lines, isPiece, false); if (isPiece && poly.Area < 0) { poly.points.Reverse(); } if (!isPiece && poly.Area > 0) { poly.points.Reverse(); } return(poly); }
//結合度計算 //ピース同士ならO(N + M), 枠にピースを入れる場合は枠をM角形としてO(NM) (定数軽い) public static int Evaluation(Poly dstPoly, Poly srcPoly, int dstPointId, int srcPointId) { if (dstPoly.isPiece) { return(EvaluationSub(dstPoly, srcPoly, dstPointId, srcPointId)); } else { double eps = 1e-10; bool[] calced = new bool[srcPoly.Count]; List <Tuple <Point, int> > pointList = new List <Tuple <Point, int> >(); int score = 0; for (int i = 0; i < srcPoly.Count; i++) { calced[i] = false; } for (int i = 0; i < dstPoly.Count; i++) { pointList.Add(new Tuple <Point, int>(dstPoly.points[i], i)); } pointList.Sort((a, b) => Point.Compare(a.Item1, b.Item1)); for (int i = 0; i < srcPoly.Count; i++) { if (calced[i]) { continue; } int st = 0, ed = dstPoly.Count, mid; //oooxxxx while (ed - st >= 2) { mid = (st + ed) / 2; if (Point.Compare(pointList[mid].Item1, srcPoly.points[i]) <= 0) { st = mid; } else { ed = mid; } } if (st == dstPoly.Count || (srcPoly.points[i] - pointList[st].Item1).Norm > eps) { continue; } //探索 score += EvaluationSub(dstPoly, srcPoly, pointList[st].Item2, i, calced); } return(score); } }
//クローン (深いコピー) public Poly Clone() { Poly ret = new Poly(new List <Point>(points), new List <Line>(lines), isPiece, isPositionDetected); for (int i = 0; i < ret.lines.Count; i++) { ret.lines[i] = ret.lines[i].Clone(); } ret.isPiece = this.isPiece; ret.isExist = this.isExist; return(ret); }
//辺の追加 private void AddEdgeTo(Line line, Poly dstPoly, Poly srcPoly) { double eps = 1e-5; List <int> pointId = new List <int>(); for (int i = 0; i < pointList.Count; i++) { if (line.Distance(pointList[i]) <= eps) { pointId.Add(i); } } for (int i = 0; i < pointId.Count - 1; i++) { for (int j = pointId.Count - 1; j > i; j--) { double a = (pointList[pointId[j - 1]] - line.start).Norm; double b = (pointList[pointId[j]] - line.start).Norm; if (a > b) { int t = pointId[j - 1]; pointId[j - 1] = pointId[j]; pointId[j] = t; } } } double dist = 1e-6 * 5; for (int i = 0; i < pointId.Count - 1; i++) { Point a = pointList[pointId[i]]; Point b = pointList[pointId[i + 1]]; Point mid = (a + b) / 2; Point l = (b - a) * new Point(0, 1); l /= l.Abs; l *= dist; l += mid; Point r = (b - a) * new Point(0, -1); r /= r.Abs; r *= dist; r += mid; //右手が壁じゃなかったら辺をはる if (!IsWall(dstPoly, srcPoly, r)) { edgeTo[pointId[i]].Add(pointId[i + 1]); } if (!IsWall(dstPoly, srcPoly, l)) { edgeTo[pointId[i + 1]].Add(pointId[i]); } } }
//線分が接触しているか public bool isHitLine(Poly poly) { List <Point> points = SizingPoly(); for (int i = 0; i < Count; i++) { Line line1 = new Line(points[i], points[i + 1]); for (int j = 0; j < poly.Count; j++) { Line line2 = new Line(poly.points[j], poly.points[j + 1]); if (Line.IsHit(line1, line2)) { return(true); } } } return(false); }
//点pointが壁に含まれるか?(境界の結果はPoly.isCover関数に依存) //壁の定義: //ピース同士の場合 … マージする2ピースの和領域 //枠穴とピースの場合 … {枠穴 - ピース}以外の領域 private bool IsWall(Poly dstPoly, Poly srcPoly, Point point) { if (dstPoly.isPiece) { return(dstPoly.isCover(point) || srcPoly.isCover(point)); } else { if (srcPoly.isCover(point)) { return(true); } else if (dstPoly.isCover(point)) { return(false); } else { return(true); } } }
//結合度計算のサブ(接している連続した部分1つについて、結合度を計算) //多角形dstPolyの頂点dstPointId, 多角形srcPolyの頂点srcPointIdが同じ位置にある。 private static int EvaluationSub(Poly dstPoly, Poly srcPoly, int dstPointId, int srcPointId, bool[] calced = null) { const double eps = 1e-10; int d = dstPointId; int s = srcPointId; int[] count = { 0, 0, 0, 0 }; //count = {一致辺の個数, 始点と方向は一致してるが一致辺ではない辺の個数, 180°角の個数, 360°角の個数}; int[] weight = { 4, 1, 2, 3 }; //weight…各項目の点数重み (定数) Point a, b; //走査1 int counter = 0; while ((dstPoly[d] - srcPoly[s]).Norm <= eps && counter < dstPoly.Count) { if (calced != null) { calced[toIndex(s, srcPoly.Count)] = true; } d++; s--; counter++; } //例外処理 if (counter == dstPoly.Count) { return(dstPoly.Count * weight[0]); } //完全一致 count[0] += d - dstPointId - 1; a = dstPoly[d] - dstPoly[d - 1]; b = srcPoly[s] - srcPoly[s + 1]; if (Math.Abs(Point.Cross(a, b)) <= eps) //平行 { if (Point.Dot(a, b) >= 0) { count[1]++; } else { count[2]++; } } //走査2 d = dstPointId; s = srcPointId; while ((dstPoly[d] - srcPoly[s]).Norm <= eps) { if (calced != null) { calced[toIndex(s, srcPoly.Count)] = true; } d--; s++; } count[0] += s - srcPointId - 1; a = dstPoly[d] - dstPoly[d + 1]; b = srcPoly[s] - srcPoly[s - 1]; if (Math.Abs(Point.Cross(a, b)) <= eps) //平行 { if (Point.Dot(a, b) >= 0) { count[1]++; } else { count[2]++; } } //角の個数は, count[0] + count[1] - 1で導出できる count[3] = count[0] + count[1] - 1; int score = 0; for (int i = 0; i < 4; i++) { score += weight[i] * count[i]; } return(score); }
//2多角形をマージする。srcPolyが移動してきた多角形(ピースであることが保証される) public List <Poly> Marge(Poly dstPoly, Poly srcPoly) { debugDstPoly = dstPoly; debugSrcPoly = srcPoly; pointList = new List <Point>(); for (int i = 0; i < dstPoly.Count; i++) { AddPointList(dstPoly.points[i]); } for (int i = 0; i < srcPoly.Count; i++) { AddPointList(srcPoly.points[i]); } edgeTo = new List <int> [pointList.Count]; for (int i = 0; i < pointList.Count; i++) { edgeTo[i] = new List <int>(); } for (int i = 0; i < dstPoly.Count; i++) { AddEdgeTo(new Line(dstPoly.points[i], dstPoly.points[i + 1]), dstPoly, srcPoly); } for (int i = 0; i < srcPoly.Count; i++) { AddEdgeTo(new Line(srcPoly.points[i], srcPoly.points[i + 1]), dstPoly, srcPoly); } used = new List <bool> [pointList.Count]; for (int i = 0; i < pointList.Count; i++) { used[i] = new List <bool>(); } for (int i = 0; i < pointList.Count; i++) { for (int j = 0; j < edgeTo[i].Count; j++) { used[i].Add(false); } } //サイクル検出 List <Poly> polys = new List <Poly>(); List <Line> lines = new List <Line>(); //実体をコピーします! if (dstPoly.isPiece) { for (int i = 0; i < dstPoly.lines.Count; i++) { lines.Add(dstPoly.lines[i].Clone()); } for (int i = 0; i < srcPoly.lines.Count; i++) { lines.Add(srcPoly.lines[i].Clone()); } } for (int i = 0; i < pointList.Count; i++) { for (int j = 0; j < edgeTo[i].Count; j++) { if (used[i][j]) { continue; } polys.AddRange(getPolys(i, j, dstPoly.isPiece, lines, srcPoly.isPositionDetected || dstPoly.isPositionDetected)); } } //エラー処理 if (dstPoly.isPiece && polys.Count > 1) { return(new List <Poly>()); } //2ピースの内部に穴があるケース if (!dstPoly.isPiece && polys.Count == 0) { polys.Add(new Poly(new List <Point>(), lines, false, false)); return(polys); } //枠穴に完全にピースが収まったケース //冗長点削除 + 辺生成 List <Poly> ret = new List <Poly>(); for (int i = 0; i < polys.Count; i++) { ret.Add(fixPoly(polys[i].points, lines, polys[i].isPiece, srcPoly.isPositionDetected || dstPoly.isPositionDetected)); } return(ret); }