/// <summary> /// distancetest判断两个stroke的距离 /// 当且仅当两个stroke的都是线性的 /// </summary> /// <param name="newstroke"></param> /// <returns></returns> public TESTRESULT DistanceTest(MyStroke newstroke, MyStroke prestroke) { if (prestroke == null || prestroke.linearity == Linearity.Curve) return TESTRESULT.SEPERATE; Point start1 = newstroke.adjustedPoints[0]; Point end1 = newstroke.adjustedPoints[newstroke.adjustedPoints.Length - 1]; Point start2 = prestroke.adjustedPoints[0]; Point end2 = prestroke.adjustedPoints[prestroke.adjustedPoints.Length - 1]; if (distanceP2P(start1, start2) < THRESHOLD_DISTANCE || distanceP2P(start1, end2) < THRESHOLD_DISTANCE || distanceP2P(end1, start2) < THRESHOLD_DISTANCE || distanceP2P(end1, end2) < THRESHOLD_DISTANCE) return TESTRESULT.GROUP; else if (distanceP2L(start2, newstroke.m, -1, newstroke.c) < THRESHOLD_DISTANCE || distanceP2L(end2, newstroke.m, -1, newstroke.c) < THRESHOLD_DISTANCE || distanceP2L(start1, prestroke.m, -1, prestroke.c) < THRESHOLD_DISTANCE || distanceP2L(end1, prestroke.m, -1, prestroke.c) < THRESHOLD_DISTANCE) return TESTRESULT.GROUP; else return TESTRESULT.SEPERATE; }
/// <summary> /// 线性测试 /// 判断stroke是直线或者曲线 /// 如果是直线,将直线方程的参数写入到改stroke对象中 /// 并且计算出改直线上每个点到其直线方程的垂足 /// </summary> /// <param name="stroke"></param> /// <returns></returns> public Linearity LinearityTest(MyStroke stroke) { double linearity = 0; Point[] points = stroke.points; Point start = stroke.startPoint; Point end = stroke.endPoint; double sumofp2p = 0.0; for (int i = 1, j = points.Length; i < j; i++) { sumofp2p += distanceP2P(points[i], points[i - 1]); } linearity = distanceP2P(end, start) / sumofp2p; if (linearity >= THRESHOLD_LINEARITY) { stroke.linearity = Linearity.Line;//设置stroke的m和c属性 computeMC(stroke);//为stroke添加m和c return Linearity.Line; } else { stroke.linearity = Linearity.Curve; return Linearity.Curve; } }
/// <summary> /// angular test 判断两条直线的角度差别是否在threshold范围之内, /// 以判断他们是否可以分到一组 /// </summary> /// <param name="newstroke"></param> /// <param name="prestroke"></param> /// <returns></returns> public TESTRESULT AngleTest(MyStroke newstroke, MyStroke prestroke) { if (prestroke == null || prestroke.linearity == Linearity.Curve) return TESTRESULT.SEPERATE; if (getAngleBetweenLine(newstroke.m, prestroke.m) < THRESHOLD_ANGULAR) return TESTRESULT.GROUP; else return TESTRESULT.SEPERATE; }
//--------------------------------------------------------------------------------------- //YHY-090421 //判断一个stroke是否是近似圆 public bool JudgeCircle(MyStroke stroke) { double dist = distanceP2P(stroke.startPoint, stroke.endPoint); double rationWtoH = 0; if (stroke.boundingBox.Width > stroke.boundingBox.Height && stroke.boundingBox.Height != 0) rationWtoH = stroke.boundingBox.Width / stroke.boundingBox.Height; else if (stroke.boundingBox.Height > stroke.boundingBox.Width && stroke.boundingBox.Width != 0) rationWtoH = stroke.boundingBox.Height / stroke.boundingBox.Width; double rationLenthtoBoundingbox; stroke.setLenth(); rationLenthtoBoundingbox = stroke.lenth / ((stroke.boundingBox.Height + stroke.boundingBox.Width) * 2); if (dist < 1000 && rationWtoH < 1.5 && rationLenthtoBoundingbox < 0.9) return true; return false; }
/// <summary> /// 将mystroke从草图中删除 /// 当stroke在某个strokegroup中的时候,将整个strokegroup删除 /// </summary> /// <param name="mystroke"></param> public void deleteStroke(MyStroke mystroke) { if (!mystroke.isInGroup) { mystroke.inkstroke.Ink.DeleteStroke(mystroke.inkstroke); strokeList.Remove(mystroke); } else { if (mystroke.group != null) { deleteGroup(mystroke.group); } } if (mystroke == laststroke) { laststroke = strokeList.ToArray()[strokeList.Count - 1]; } }
/// <summary> /// 修正了bug /// 计算笔迹ms的方程y = mx + c的参数m和c /// </summary> /// <param name="ms"></param> public void computeMC(MyStroke ms) { ///只有ms是直线的时候才可以计算 if (ms.linearity == Linearity.Curve) return; Point[] points = ms.points; double m = 0; double c = 0; double sumx2 = 0, sumx = 0, sumy = 0, sumxy = 0; Point pre = points[0]; foreach (Point p in points) { int x = p.X, y = p.Y; sumx += x; sumy += y; sumx2 += x * x; sumxy += x * y; } m = (points.Length * sumxy - sumx * sumy) / (points.Length * sumx2 - sumx * sumx); c = (sumx2 * sumy - sumx * sumxy) / (points.Length * sumx2 - sumx * sumx); ms.m = m; ms.c = c; Point[] foots = new Point[points.Length]; //拟合到直线上,计算每个点的垂足 for (int i = 0; i < foots.Length; i++) { foots[i] = getFoot(points[i], m, -1, c); } ms.adjustedPoints = foots; }
/// <summary> /// 计算两条直线之间的最短距离 /// </summary> /// <param name="stroke1"></param> /// <param name="stroke2"></param> /// <returns></returns> public double distanceL2L(MyStroke stroke1, MyStroke stroke2) { Point[] ps1 = stroke1.adjustedPoints; Point[] ps2 = stroke2.adjustedPoints; Point start1 = ps1[0]; Point end1 = ps1[ps1.Length - 1]; Point start2 = ps2[0]; Point end2 = ps2[ps2.Length - 1]; double distance = distanceP2L(start1, stroke2.m, -1, stroke2.c); double distancetemp = 0; Point[] points = { start1, end1, start2, end2 }; MyStroke[] strokes = { stroke1, stroke1, stroke2, stroke2 }; for (int i = 0; i < points.Length; i++) { distancetemp = distanceP2L(points[i], strokes[i].m, -1, strokes[i].c); if (distancetemp < distance) distance = distancetemp; } return distance; }
////////////////////////////////////////////////////////////////////////// /// <summary> /// operations /// </summary> /// ///<summary> /// 在group中增加一个stroke /// 将stroke的时间设置为相同 这样通过时间轴回放的时候 会同时绘制group中的笔画 /// </summary> /// public void addStroke(MyStroke myStroke) { strokeList.AddLast(myStroke); myStroke.isInGroup = true; myStroke.endTime = this.StartTime; myStroke.group = this; if (myStroke.linearity == Linearity.Curve) { return; } ///设置startPoint和endPoint Point p1 = myStroke.startPoint; Point p2 = myStroke.endPoint; if (strokeList.Count == 1) { if (p1.X < p2.X) { startPoint = new Point(p1.X, p1.Y); endPoint = new Point(p2.X, p2.Y); } else { startPoint = new Point(p2.X, p2.Y); endPoint = new Point(p1.X, p1.Y); } } else if (strokeList.Count > 1) { if (p1.X < p2.X) { if (p1.X < startPoint.X) { startPoint.X = p1.X; startPoint.Y = p1.Y; } if (p2.X > endPoint.X) { endPoint.X = p2.X; endPoint.Y = p2.Y; } } else { if (p2.X < startPoint.X) { startPoint.X = p2.X; startPoint.Y = p2.Y; } if (p1.X > endPoint.X) { endPoint.X = p1.X; endPoint.Y = p1.Y; } } } tool.computeSkeleton(this); computeSkeletonFromEnds(); }
/// <summary> /// 从strokegroup中删除stroke /// </summary> /// <param name="myStroke"></param> public void removeStroke(MyStroke myStroke) { strokeList.Remove(myStroke); }
public void addStroke(Stroke stroke) { List <Stroke> changes = new List <Stroke>();//需要刷新的stroke MyStroke newstroke = new MyStroke(stroke); newstroke.setBoundingBox(); //YHY-090412 Sketch currentpage = sketch.currentpage.Value.content; //当前页 ///线性测试 if (tool.LinearityTest(newstroke) == Linearity.Line) { //新增加的stroke是直线 ///straight-line segment fitting foreach (StrokeGroup strokegroup in currentpage.groupList) { if (strokegroup.TYPE == GroupType.DEFAULT) { MyStroke mstroke = strokegroup.strokeList.First.Value; if ((tool.DistanceTest(newstroke, mstroke) == TESTRESULT.GROUP) && (tool.AngleTest(newstroke, mstroke) == TESTRESULT.GROUP)) { //通过了距离测试和角度测试 strokegroup.addStroke(newstroke); drawingInk.Ink.DeleteStroke(strokegroup.Skeleton); strokegroup.Skeleton = drawingInk.Ink.CreateStroke(strokegroup.SkeletonPoints); strokegroup.Skeleton.DrawingAttributes = this.Skeleton_Attibute; strokegroup.setBoundingBox();//YHY-090412 changes.Add(strokegroup.Skeleton); break; } } } ///如果新增加的笔画没有找到组织,那么将给它单独建一个组 if (!newstroke.isInGroup) { //如果没有通过距离测试和角度测试 //Seperate Line StrokeGroup sg = new StrokeGroup(GroupType.DEFAULT); sketch.groupIndex++; sg.groupID = sketch.groupIndex; //YHY-090413 增加一个标志 sg.addStroke(newstroke); sg.Skeleton = drawingInk.Ink.CreateStroke(sg.SkeletonPoints); sg.Skeleton.DrawingAttributes = this.Skeleton_Attibute; //YHY-090410 sg.GRAPH = Graphic.Line; sg.setBoundingBox(); sg.geometry = new G_Line(newstroke.startPoint, newstroke.endPoint);//YHY-090415 currentpage.addGroup(sg); //判断新加入的strokegroup代表的图元和历史图元之间的约束关系 refreshConstraint(sg); //yhy-090409 //将新建的group加入到skechContextDG中去 if (sketch.sketchContextDG.isEmpty()) { sketch.sketchContextDG.nodeIndex++; SKContextDGNode node = new SKContextDGNode(sg, sg.StartTime, sketch.sketchContextDG.nodeIndex); node.groupID = sg.groupID; sketch.sketchContextDG.newHead(node); sketch.sketchContextDG.firstNode = node; sketch.sketchContextDG.lastNode = node; } else { sketch.sketchContextDG.addNode(sg); } } //Groupping: add curve object to strokeGroup and sketch //YHY-090409 //////////////////////////////////////////////////////////////////////////////// //YHY-090414 //Just for test, ReGroup //sketchReGroupping(); //drawBoundingBoxtoTestGrouppingResult(); ////////////////////////////////////////////////////////////////////////////////// /* StrokeGroup ngroup = newstroke.group; * * foreach (StrokeGroup group in currentpage.groupList) * { * if (group == ngroup) * continue; * * if (tool.distanceP2P(ngroup.startPoint, group.startPoint) < Threshold_Distance) * { * ngroup.startPoint = group.startPoint; * ngroup.computeSkeletonFromEnds(); * } * else if (tool.distanceP2P(ngroup.startPoint, group.endPoint) < Threshold_Distance) * { * ngroup.startPoint = group.endPoint; * ngroup.computeSkeletonFromEnds(); * } * else if (tool.distanceP2P(ngroup.endPoint, group.startPoint) < Threshold_Distance) * { * ngroup.endPoint = group.startPoint; * ngroup.computeSkeletonFromEnds(); * } * else if (tool.distanceP2P(ngroup.endPoint, group.endPoint) < Threshold_Distance) * { * ngroup.endPoint = group.endPoint; * ngroup.computeSkeletonFromEnds(); * } * }*/ } else if (tool.LinearityTest(newstroke) == Linearity.Curve) { //如果线性测试的结果是曲线 //TODO::ToolForStroke.circle(et al).judge function //YHY-090408 //先不考虑曲线补笔等草图特效的问题,假设所有曲线相关的sketch都是单笔的 //判断是否为circle bool isCircle = false; isCircle = tool.JudgeCircle(newstroke); //YHY-090410 //创建新的group,先不考虑曲线补笔等情况 if (!newstroke.isInGroup) { StrokeGroup sg = new StrokeGroup(GroupType.DEFAULT); sketch.groupIndex++; sg.groupID = sketch.groupIndex; sg.addStroke(newstroke); sg.setBoundingBox(); if (isCircle) { //TODO::暂时统一都认为是圆,以后需要改成椭圆 sg.GRAPH = Graphic.Circle; Point p = new Point(); double r; p.X = sg.boundingBox.Left + sg.boundingBox.Width / 2; p.Y = sg.boundingBox.Top + sg.boundingBox.Height / 2; r = System.Math.Min(sg.boundingBox.Height, sg.boundingBox.Width) / 2; sg.geometry = new G_Cricle(p, r);//YHY-090415 } else { sg.GRAPH = Graphic.Curve; } //sg.Skeleton = inkpicture.Ink.CreateStroke(sg.SkeletonPoints); //TODO: Get curve skeleton //sg.Skeleton.DrawingAttributes = this.Skeleton_Attibute; currentpage.addGroup(sg); //捕捉约束 //判断新加入的strokegroup代表的图元和历史图元之间的约束关系 refreshConstraint(sg); //yhy-090409 //将新建的group加入到skechContextDG中去 if (sketch.sketchContextDG.isEmpty()) { sketch.sketchContextDG.nodeIndex++; SKContextDGNode node = new SKContextDGNode(sg, sg.StartTime, sketch.sketchContextDG.nodeIndex); node.groupID = sg.groupID; sketch.sketchContextDG.newHead(node); sketch.sketchContextDG.firstNode = node; sketch.sketchContextDG.lastNode = node; } else { sketch.sketchContextDG.addNode(sg); } } //Groupping: add curve object to strokeGroup and sketch //YHY-090408 //////////////////////////////////////////////////////////////////////////////// //YHY-090414 //Just for test, ReGroup // sketchReGroupping(); // drawBoundingBoxtoTestGrouppingResult(); ////////////////////////////////////////////////////////////////////////////////// //TODO::constraint capture function //如果未归为prior group,则需判断新添笔迹(group)与原来笔迹的约束关系 //YHY-090416 //判断新添加strokegroup里的图元和其余历史图元之间的关系 } currentpage.addStroke(newstroke); ///对纸张进行局部更新 if (stroke != null) { inkPanel.Invalidate(tool.InkSpaceToPixelRect(inkPanel.Handle, drawingInk.Renderer, stroke.GetBoundingBox())); foreach (Stroke st in changes) { inkPanel.Invalidate(tool.InkSpaceToPixelRect(inkPanel.Handle, drawingInk.Renderer, st.GetBoundingBox())); } } }
/// <summary> /// 将stroke从sketch中删除 /// </summary> /// <param name="mystroke"></param> public void removeStroke(MyStroke mystroke) { this.strokeList.Remove(mystroke); }
////////////////////////////////////////////////////////////////////////////////// ///operations /// ///<summary> /// 向sketch中增加stroke /// </summary> /// <param name="mystroke">需要增加的stroke</param> public void addStroke(MyStroke mystroke) { this.strokeList.Add(mystroke); this.laststroke = mystroke; }
public double distanceStroketoStroke(MyStroke s1, MyStroke s2) { double minDist = 1000000; foreach (Point pt1 in s1.points) { foreach (Point pt2 in s2.points) { double dist = P2P(pt1, pt2); if (dist < minDist) { minDist = dist; } } } //以下被封代码的距离计算公式来自于chiu 1998 paper /* double dx, dy; * double a = 1.2; //阈值, 需要确定 * * dx = s2.boundingBox.Left - (s1.boundingBox.Left + s1.boundingBox.Width); * if (dx < 0) * dx = 0; * dy = s2.boundingBox.Top - (s1.boundingBox.Top + s1.boundingBox.Height); * if (dy < 0) * dy = 0; * dist = dx + a * dy; */ //YHY-090412 //Own /* 计算包围盒之间的最小距离方法 * Point p1 = new Point(); * Point p2 = new Point(); * double dist; * * //================================ * p1.X = s1.boundingBox.Left; * p1.Y = s1.boundingBox.Top; * // * p2.X = s2.boundingBox.Left; * p2.Y = s2.boundingBox.Top; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Right; * p2.Y = s2.boundingBox.Top; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Right; * p2.Y = s2.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Left; * p2.Y = s2.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * * //================================ * p1.X = s1.boundingBox.Right; * p1.Y = s1.boundingBox.Top; * // * p2.X = s2.boundingBox.Left; * p2.Y = s2.boundingBox.Top; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Right; * p2.Y = s2.boundingBox.Top; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Right; * p2.Y = s2.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Left; * p2.Y = s2.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * * //================================ * p1.X = s1.boundingBox.Left; * p1.Y = s1.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Left; * p2.Y = s2.boundingBox.Top; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Right; * p2.Y = s2.boundingBox.Top; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Right; * p2.Y = s2.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Left; * p2.Y = s2.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * * //================================ * p1.X = s1.boundingBox.Right; * p1.Y = s1.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Left; * p2.Y = s2.boundingBox.Top; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Right; * p2.Y = s2.boundingBox.Top; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Right; * p2.Y = s2.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist; * // * p2.X = s2.boundingBox.Left; * p2.Y = s2.boundingBox.Bottom; * dist = P2P(p1, p2); * if (dist < minDist) * minDist = dist;*/ return(minDist); }