static Dictionary <BoneAnnotation, Matrix> GetSkeletalTransforms(SkeletonAnnotation an, SkeletonAnnotation orgAn) { Dictionary <BoneAnnotation, Matrix> transformDict = new Dictionary <BoneAnnotation, Matrix>(); for (int i = 0; i < an.bones.Count; i++) { var b = an.bones[i]; var ob = orgAn.bones[i]; float angle1 = (float)Math.Atan2(ob.dst.position.Y - ob.src.position.Y, ob.dst.position.X - ob.src.position.X); float angle2 = (float)Math.Atan2(b.dst.position.Y - b.src.position.Y, b.dst.position.X - b.src.position.X); angle1 = FMath.ToDegree(angle1); angle2 = FMath.ToDegree(angle2); float len1 = FMath.Distance(ob.src.position, ob.dst.position); float len2 = FMath.Distance(b.src.position, b.dst.position); if (len1 <= 1e-4) { transformDict[b] = new Matrix(); continue; } Matrix transform = new Matrix(); transform.Translate(-ob.src.position.X, -ob.src.position.Y, MatrixOrder.Append); transform.Rotate(-angle1, MatrixOrder.Append); transform.Scale(len2 / len1, len2 / len1, MatrixOrder.Append); transform.Rotate(angle2, MatrixOrder.Append); transform.Translate(b.src.position.X, b.src.position.Y, MatrixOrder.Append); transformDict[b] = transform; } return(transformDict); }
static float SectionHeight(PatchSkeletalMesh mesh, PatchSection section, PatchSkeletonBone b) { List <PointF> path = GetPath(mesh).Select(v => v.position).ToList(); var curves = SectionToAdjuscentCurves(path, section, 5, 30); if (path == null || curves == null || b == null) { return(0); } if (curves.Item1 == null || curves.Item2 == null) { return(0); } float height = 0; float boneLen = FMath.Distance(b.src.position, b.dst.position); if (boneLen <= 1e-4) { return(0); } PointF x, y; CalcBoneCoordinate(b, out x, out y); // 各セグメントのボーンからのズレを求める int cnt = Math.Min(curves.Item1.Length, curves.Item2.Length); for (int i = 0; i < cnt; i++) { int idx1 = curves.Item1.First + curves.Item1.Length - 1 - i; int idx2 = curves.Item2.First + i; while (idx1 < 0) { idx1 += path.Count; } while (idx2 < 0) { idx2 += path.Count; } PointF pt1 = path[idx1 % path.Count]; PointF pt2 = path[idx2 % path.Count]; pt1.X -= b.src.position.X; pt1.Y -= b.src.position.Y; pt2.X -= b.src.position.X; pt2.Y -= b.src.position.Y; height += pt1.X * y.X + pt1.Y * y.Y; height += pt2.X * y.X + pt2.Y * y.Y; } height /= 2 * cnt; return(height); }
/// <param name="checkLength">sectionから何pixel分の輪郭を計算に使うか/param> static float SectionHeight(PatchSkeletalMesh mesh, PatchSection section, PatchSkeletonBone b, float curveLength) { List <PatchVertex> path = GetPath(mesh); var rawcurves = section2adjCurves(path, section); if (path == null || rawcurves == null || b == null) { return(0); } var curves = TrimCurves(path, rawcurves, 0, curveLength); if (curves.Item1 == null || curves.Item2 == null) { return(0); } float boneLen = FMath.Distance(b.src.position, b.dst.position); if (boneLen <= 1e-4) { return(0); } PointF x, y; BoneCoordinate(b, out x, out y); // 各セグメントのボーンからのズレを求める float height = 0; int cnt = Math.Min(curves.Item1.Length, curves.Item2.Length); for (int i = 0; i < cnt; i++) { int idx1 = curves.Item1.First + curves.Item1.Length - 1 - i; int idx2 = curves.Item2.First + i; while (idx1 < 0) { idx1 += path.Count; } while (idx2 < 0) { idx2 += path.Count; } PointF pt1 = path[idx1 % path.Count].position; PointF pt2 = path[idx2 % path.Count].position; pt1.X -= b.src.position.X; pt1.Y -= b.src.position.Y; pt2.X -= b.src.position.X; pt2.Y -= b.src.position.Y; height += pt1.X * y.X + pt1.Y * y.Y; height += pt2.X * y.X + pt2.Y * y.Y; } height /= 2 * cnt; return(height); }
static float GetSectionHeight(List <PointF> path, Tuple <CharacterRange, CharacterRange> curves, BoneAnnotation b) { if (path == null || curves == null || b == null) { return(0); } if (curves.Item1 == null || curves.Item2 == null) { return(0); } float height = 0; float boneLen = FMath.Distance(b.src.position, b.dst.position); if (boneLen <= 1e-4) { return(0); } PointF x, y; GetCoordinateFromBone(b, out x, out y); // 各セグメントのボーンからのズレを求める int cnt = Math.Min(curves.Item1.Length, curves.Item2.Length); for (int i = 0; i < cnt; i++) { int idx1 = curves.Item1.First + curves.Item1.Length - 1 - i; int idx2 = curves.Item2.First + i; while (idx1 < 0) { idx1 += path.Count; } while (idx2 < 0) { idx2 += path.Count; } PointF pt1 = path[idx1 % path.Count]; PointF pt2 = path[idx2 % path.Count]; pt1.X -= b.src.position.X; pt1.Y -= b.src.position.Y; pt2.X -= b.src.position.X; pt2.Y -= b.src.position.Y; height += pt1.X * y.X + pt1.Y * y.Y; height += pt2.X * y.X + pt2.Y * y.Y; } height /= 2 * cnt; return(height); }
public static List <PointF> Deform(List <PointF> pts, List <Tuple <PointF, PointF> > moves) { List <PointF> newPts = new List <PointF>(); for (int i = 0; i < pts.Count; i++) { bool finish = false; List <float> ws = new List <float>(); float w_sum = 0; foreach (var mv in moves) { if (mv.Item1 == pts[i]) { newPts.Add(mv.Item2); break; } float w = (float)(1 / (0.01 + Math.Pow(FMath.Distance(mv.Item1, pts[i]), 2))); ws.Add(w); w_sum += w; } if (finish) { continue; } if (w_sum <= 1e-4) { newPts.Add(pts[i]); continue; } float inv_w_sum = 1 / w_sum; for (int j = 0; j < moves.Count; j++) { ws[j] *= inv_w_sum; } float x = pts[i].X; float y = pts[i].Y; for (int j = 0; j < moves.Count; j++) { var mv = moves[j]; float dx = mv.Item2.X - mv.Item1.X; float dy = mv.Item2.Y - mv.Item1.Y; x += dx * ws[j]; y += dy * ws[j]; } newPts.Add(new PointF(x, y)); } return(newPts); }
// ボーンのdst方向にあるのがpatch2 static void OverlayPatches(PatchSkeletalMesh patch1, PatchSkeletalMesh patch2, PatchSkeleton refSkeleton, PatchSection section1, PatchSection section2, PatchSkeletonBone bone, float border) { PatchSkeletonBone refBone = RefBone(refSkeleton, bone); float min1 = float.MaxValue; float max2 = float.MinValue; List <PatchVertex> path1 = GetPath(patch1); List <PatchVertex> path2 = GetPath(patch2); for (int i = section1.First; i < section1.First + section1.Length; i++) { var pt = path1[FMath.Rem(i, path1.Count)].position; float param = FMath.ParameterOnLine(pt, refBone.src.position, refBone.dst.position); if (float.IsNaN(param)) { continue; } min1 = Math.Min(min1, param); } for (int i = section2.First; i < section2.First + section2.Length; i++) { var pt = path2[FMath.Rem(i, path2.Count)].position; float param = FMath.ParameterOnLine(pt, refBone.src.position, refBone.dst.position); if (float.IsNaN(param)) { continue; } max2 = Math.Max(max2, param); } PointF x, y; BoneCoordinate(refBone, out x, out y); float boneLength = FMath.Distance(refBone.src.position, refBone.dst.position); float dparam = border / boneLength; float delta = (min1 - max2 - dparam) * boneLength; float dx = x.X * delta; float dy = x.Y * delta; // mesh2の頂点・制御点を平行移動する foreach (var v in patch2.mesh.vertices) { v.position = new PointF(v.position.X + dx, v.position.Y + dy); } foreach (var c in patch2.mesh.CopyControlPoints()) { patch2.mesh.TranslateControlPoint(c.position, new PointF(c.position.X + dx, c.position.Y + dy), false); } }
// ptとdirが表す直線状を、ptから双方向に操作したときに最初に見つかるピクセル // iterは32ビット長を仮定 unsafe static PointF GetNearestPixel(BitmapIterator iter, PointF pt, PointF dir, Rectangle bounds, Predicate <int> isFound = null) { if (iter.PixelSize != 4) { return(new PointF(-1, -1)); } float len = FMath.Distance(dir, PointF.Empty); if (len <= 1e-4) { return(new PointF(-1, -1)); } float dx = dir.X / len; float dy = dir.Y / len; int cnt = 0; while (true) { float x1 = pt.X + dx * cnt; float y1 = pt.Y + dy * cnt; float x2 = pt.X - dx * cnt; float y2 = pt.Y - dy * cnt; bool out1 = !bounds.Contains((int)x1, (int)y1); bool out2 = !bounds.Contains((int)x2, (int)y2); if (out1 && out2) { return(new PointF(-1, -1)); } if (!out1 && CheckNearestPixel(x1, y1, iter, isFound)) { return(new PointF(x1, y1)); } if (!out2 && CheckNearestPixel(x2, y2, iter, isFound)) { return(new PointF(x2, y2)); } cnt++; } }
static void CalcBoneCoordinate(PatchSkeletonBone b, out PointF x, out PointF y) { x = new PointF(1, 0); y = new PointF(0, 1); float boneLen = FMath.Distance(b.src.position, b.dst.position); if (boneLen <= 1e-4) { return; } float dx = (b.dst.position.X - b.src.position.X) / boneLen; float dy = (b.dst.position.Y - b.src.position.Y) / boneLen; x = new PointF(dx, dy); y = new PointF(dy, -dx); }
static void GetCoordinateFromBone(BoneAnnotation b, out PointF x, out PointF y) { x = new PointF(1, 0); y = new PointF(0, 1); float boneLen = FMath.Distance(b.src.position, b.dst.position); if (boneLen <= 1e-4) { return; } float dx = (b.dst.position.X - b.src.position.X) / boneLen; float dy = (b.dst.position.Y - b.src.position.Y) / boneLen; x = new PointF(dx, dy); y = new PointF(dy, -dx); }
public static void SetSkeletalControlPoints(ARAPDeformation arap, SkeletonAnnotation an, int linearSpan, Dictionary <BoneAnnotation, List <PointF> > boneToControls) { if (arap == null) { return; } boneToControls.Clear(); arap.ClearControlPoints(); // ボーン沿いに制御点を追加 HashSet <PointF> pts = new HashSet <PointF>(); if (an != null && linearSpan >= 1) { foreach (var b in an.bones) { PointF p0 = b.src.position; PointF p1 = b.dst.position; float dist = FMath.Distance(p0, p1); int ptNum = Math.Max(2, (int)(dist / linearSpan) + 1); boneToControls[b] = new List <PointF>(); for (int i = 0; i < ptNum; i++) { float t = (float)i / (ptNum - 1); PointF p = i == 0 ? p0 : i == ptNum - 1 ? p1 : FMath.Interpolate(p0, p1, t); if (!pts.Contains(p)) { arap.AddControlPoint(p, p); } boneToControls[b].Add(p); pts.Add(p); } } } }
static CharacterRange TrimCurve(List <PatchVertex> path, CharacterRange curve, float offset, float length) { float total = 0; int start = 0; int cnt = 1; for (int i = curve.First; i < curve.First + curve.Length - 1; i++) { PointF p1 = path[FMath.Rem(i, path.Count)].position; PointF p2 = path[FMath.Rem(i + 1, path.Count)].position; total += FMath.Distance(p1, p2); if (total < offset) { start++; break; } if (total > length) { break; } cnt++; } return(new CharacterRange(FMath.Rem(curve.First + start, path.Count), cnt)); }
/* * // ボーンに沿うようにメッシュの頂点を微調整し、そこに制御点を打つ * public List<int> AddBoneConstraint(AnnotationBone bone, float threshold) * { * var idxes = new List<int>(); * var src = bone.jointSrc.PositionInBmp; * var dst = bone.jointDst.PositionInBmp; * var dir = bone.Dir; * var maxt = Vector3.Dot(dir, dst - src); * * for (int i = 0; i < orgMeshPointList.Count; i++) * { * var p = orgMeshPointList[i]; * var t = Vector3.Dot(dir, p - src); * if (0 <= t && t <= maxt) * { * var h = src + t * dir; * float dist = Vector3.DistanceSquared(p, h); * if (dist <= threshold) * { * orgMeshPointList[i] = h; * meshPointList[i] = h; * orgControls.Add(h); * controls.Add(h); * controlsToPart.Add(meshPtToPart[i]); * idxes.Add(orgControls.Count - 1); * } * } * } * * return idxes; * } */ public void Precompute() { // todo /* * meshPtToPart.Clear(); * for (int i = 0; i < meshPointList.Count; i++) * meshPtToPart.Add(0); * * controlsToPart.Clear(); * for (int i = 0; i < controls.Count; i++) * controlsToPart.Add(0); */ //------------ if (controls.Count < 3) { return; } weights = new float[meshPointList.Count * controls.Count]; A00 = new float[meshPointList.Count * controls.Count]; A01 = new float[meshPointList.Count * controls.Count]; A10 = new float[meshPointList.Count * controls.Count]; A11 = new float[meshPointList.Count * controls.Count]; D = new PointF[meshPointList.Count]; for (int vIdx = 0; vIdx < meshPointList.Count; vIdx++) { int offset = vIdx * controls.Count; for (int i = 0; i < controls.Count; i++) { if (meshPtToPart[vIdx] == controlsToPart[i]) { // ぴったり同じ位置だったら無限の重みを与える if (FMath.Distance(orgControls[i], orgMeshPointList[vIdx]) <= 1e-4) { weights[i + offset] = float.PositiveInfinity; } else { weights[i + offset] = (float)(1 / (0.01 + Math.Pow(FMath.Distance(orgControls[i], orgMeshPointList[vIdx]), 2))); } } else { weights[i + offset] = 0; } } PointF?Pa = CompWeightAvg(orgControls, weights, vIdx); if (Pa == null || !Pa.HasValue) { return; } PointF[] Ph = new PointF[orgControls.Count]; for (int i = 0; i < orgControls.Count; i++) { if (!orgControls[i].IsEmpty) { Ph[i].X = orgControls[i].X - Pa.Value.X; Ph[i].Y = orgControls[i].Y - Pa.Value.Y; } } float mu = 0; for (int i = 0; i < controls.Count; i++) { mu += (float)(Ph[i].X * Ph[i].X + Ph[i].Y * Ph[i].Y) * weights[i + offset]; } D[vIdx].X = orgMeshPointList[vIdx].X - Pa.Value.X; D[vIdx].Y = orgMeshPointList[vIdx].Y - Pa.Value.Y; for (int i = 0; i < controls.Count; i++) { int idx = i + offset; A00[idx] = weights[idx] / mu * (Ph[i].X * D[vIdx].X + Ph[i].Y * D[vIdx].Y); A01[idx] = -weights[idx] / mu * (Ph[i].X * (-D[vIdx].Y) + Ph[i].Y * D[vIdx].X); A10[idx] = -weights[idx] / mu * (-Ph[i].Y * D[vIdx].X + Ph[i].X * D[vIdx].Y); A11[idx] = weights[idx] / mu * (Ph[i].Y * D[vIdx].Y + Ph[i].X * D[vIdx].X); } } }
void Precompute() { if (orgControls.Count < 3) return; weights = new float[meshPointList.Count * orgControls.Count]; A00 = new float[meshPointList.Count * orgControls.Count]; A01 = new float[meshPointList.Count * orgControls.Count]; A10 = new float[meshPointList.Count * orgControls.Count]; A11 = new float[meshPointList.Count * orgControls.Count]; D = new PointF[meshPointList.Count]; for (int vIdx = 0; vIdx < meshPointList.Count; vIdx++) { int offset = vIdx * orgControls.Count; for (int i = 0; i < orgControls.Count; i++) { if (meshPtToPart[vIdx] == controlsToPart[i]) { // ぴったり同じ位置だったら無限の重みを与える if (FMath.Distance(orgControls[i], orgMeshPointList[vIdx]) <= 1e-4) weights[i + offset] = float.PositiveInfinity; else weights[i + offset] = (float)(1 / (0.01 + Math.Pow(FMath.Distance(orgControls[i], orgMeshPointList[vIdx]), 2))); } else { weights[i + offset] = 0; } } // 追加 2014/11/08 int nonzeroCnt = 0; for (int i = 0; i < orgControls.Count; i++) if (weights[i + offset] != 0) nonzeroCnt++; if (nonzeroCnt <= 1) continue; // return; PointF? Pa = AverageWeight(orgControls, weights, vIdx); if (Pa == null || !Pa.HasValue) // 変更 2014/11/08 continue; // return; PointF[] Ph = new PointF[orgControls.Count]; for (int i = 0; i < orgControls.Count; i++) { if (!orgControls[i].IsEmpty) { Ph[i].X = orgControls[i].X - Pa.Value.X; Ph[i].Y = orgControls[i].Y - Pa.Value.Y; } } float mu = 0; for (int i = 0; i < orgControls.Count; i++) mu += (float)(Ph[i].X * Ph[i].X + Ph[i].Y * Ph[i].Y) * weights[i + offset]; D[vIdx].X = orgMeshPointList[vIdx].X - Pa.Value.X; D[vIdx].Y = orgMeshPointList[vIdx].Y - Pa.Value.Y; for (int i = 0; i < orgControls.Count; i++) { int idx = i + offset; A00[idx] = weights[idx] / mu * (Ph[i].X * D[vIdx].X + Ph[i].Y * D[vIdx].Y); A01[idx] = -weights[idx] / mu * (Ph[i].X * (-D[vIdx].Y) + Ph[i].Y * D[vIdx].X); A10[idx] = -weights[idx] / mu * (-Ph[i].Y * D[vIdx].X + Ph[i].X * D[vIdx].Y); A11[idx] = weights[idx] / mu * (Ph[i].Y * D[vIdx].Y + Ph[i].X * D[vIdx].X); } } }
private static float OverlapWidth(PatchSkeletalMesh patch1, PatchSkeletalMesh patch2, PatchSkeleton refSkeleton, PatchSection section1, PatchSection section2, PatchSkeletonBone bone, int maxLength, int maxAngle) { var refBone = RefBone(refSkeleton, bone); var path1 = GetPath(patch1); var curves1 = section2adjCurves(path1, section1); var sorted1 = GetSortedCurves(path1.Select(v => v.position).ToList(), curves1, refBone); var path2 = GetPath(patch2); var curves2 = section2adjCurves(path2, section2); var sorted2 = GetSortedCurves(path2.Select(v => v.position).ToList(), curves2, refBone); float[] param = new float[] { 0, 0, 0, 0 }; var sortedCurves = new[] { sorted1[0], sorted1[1], sorted2[0], sorted2[1], }; float minCos = (float)Math.Cos(Math.PI / 180 * maxAngle); for (int i = 0; i < sortedCurves.Length; i++) { var c = sortedCurves[i]; var path = i < 2 ? path1 : path2; List <PointF> pts = new List <PointF>(); float total = 0; for (int j = c.Count - 1; j >= 0; j--) { var p = c[j]; pts.Add(p); if (pts.Count >= 3 && FMath.GetAngleCos(pts) < minCos) { pts.RemoveAt(pts.Count - 1); break; } if (pts.Count >= 2) { total += FMath.Distance(pts[pts.Count - 2], pts[pts.Count - 1]); } if (total >= maxLength) { pts.RemoveAt(pts.Count - 1); break; } } param[i] = FMath.ParameterOnLine(pts.Last(), refBone.src.position, refBone.dst.position); } float min1 = Enumerable.Range(section1.First, section1.Length).Min(idx => FMath.ParameterOnLine(path1[FMath.Rem(idx, path1.Count)].position, refBone.src.position, refBone.dst.position)); float max1 = Enumerable.Range(section1.First, section1.Length).Max(idx => FMath.ParameterOnLine(path1[FMath.Rem(idx, path1.Count)].position, refBone.src.position, refBone.dst.position)); int idx1 = Enumerable.Range(section1.First, section1.Length).First(idx => max1 == FMath.ParameterOnLine(path1[FMath.Rem(idx, path1.Count)].position, refBone.src.position, refBone.dst.position)); float min2 = Enumerable.Range(section2.First, section2.Length).Min(idx => FMath.ParameterOnLine(path2[FMath.Rem(idx, path2.Count)].position, refBone.src.position, refBone.dst.position)); float max2 = Enumerable.Range(section2.First, section2.Length).Max(idx => FMath.ParameterOnLine(path2[FMath.Rem(idx, path2.Count)].position, refBone.src.position, refBone.dst.position)); float idx2 = Enumerable.Range(section2.First, section2.Length).First(idx => max2 == FMath.ParameterOnLine(path2[FMath.Rem(idx, path2.Count)].position, refBone.src.position, refBone.dst.position)); System.Diagnostics.Debug.Assert(param[0] <= max1); System.Diagnostics.Debug.Assert(param[1] <= max1); System.Diagnostics.Debug.Assert(param[2] >= min2); System.Diagnostics.Debug.Assert(param[3] >= min2); float[] dParams = new[] { Math.Max(0, min1 - param[0]), Math.Max(0, min1 - param[1]), Math.Max(0, param[2] - max2), Math.Max(0, param[3] - max2), }; float minDParam = dParams.Min(); float boarderWidth = minDParam * FMath.Distance(refBone.src.position, refBone.dst.position); return(boarderWidth); }
/// <summary> /// /// </summary> /// <param name="path"></param> /// <param name="course">0以上.値が大きいほど荒くなる</param> /// <returns></returns> public static List <PointF> Subdivide(List <PointF> path, float course) { if (course <= 0) { return(null); } float x = float.MaxValue, y = float.MaxValue, x1 = float.MinValue, y1 = float.MinValue; foreach (var p in path) { x = Math.Min(p.X, x); y = Math.Min(p.Y, y); x1 = Math.Max(p.X, x1); y1 = Math.Max(p.Y, y1); } int w = (int)(x1 - x), h = (int)(y1 - y); x = x - w; y = y - h; w *= 3; h *= 3; Rectangle bounds = new Rectangle((int)x, (int)y, w, h); Pen pen = new Pen(Brushes.Red, 2); List <PointF> divPath = new List <PointF>(); using (Bitmap line = new Bitmap((int)x + w, (int)y + h)) { using (var g = Graphics.FromImage(line)) { g.Clear(Color.Transparent); g.DrawCurve(pen, path.ToArray()); } using (BitmapIterator iter = new BitmapIterator(line, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { for (int i = 0; i < path.Count - 1; i++) { divPath.Add(path[i]); List <PointF> seg = SubdivideSegment(path[i], path[i + 1], course); if (seg == null) { continue; } float len = FMath.Distance(path[i], path[i + 1]); if (len <= 1e-4) { continue; } float dy = (path[i].X - path[i + 1].X) / len; float dx = (path[i + 1].Y - path[i].Y) / len; PointF dir = new PointF(dx, dy); foreach (var p in seg) { PointF pt = GetNearestPixel(iter, p, dir, bounds, null); if (pt.X >= 0 && pt.Y >= 0) { divPath.Add(pt); } } } } } divPath.Add(path.Last()); return(divPath); }