/// <summary> /// Build quadratic bezier /// </summary> /// <param name="x1">start x</param> /// <param name="y1">start y</param> /// <param name="cx">control point x</param> /// <param name="cy">control point y</param> /// <param name="x2">end x</param> /// <param name="y2">end y</param> private void BuildQuadratic( RawPolygonList polygonList, double x1, double y1, double cx, double cy, double x2, double y2, double scale) { #region calculate number of step double lengthOfCurve = Math.Sqrt((cx - x1) * (cx - x1) + (cy - y1) * (cy - y1)) + Math.Sqrt((x2 - cx) * (x2 - cx) + (y2 - cy) * (y2 - cy)); double mSteps = lengthOfCurve * DistanceToNumberOfStepScale * scale; int i = 0; double mu; while (i < mSteps) { #region calculate point[i] mu = i / mSteps; //add point [i] to polygon polygonList.LineTo( x1 * (1 - mu) * (1 - mu) + 2 * cx * mu * (1 - mu) + x2 * mu * mu, y1 * (1 - mu) * (1 - mu) + 2 * cy * mu * (1 - mu) + y2 * mu * mu ); #endregion i++; } #endregion polygonList.LineTo(x2, y2); }
/// <summary> /// build curve and append to polygon list /// </summary> /// <param name="polygonList">polygon list</param> /// <param name="x1">x1</param> /// <param name="y1">y1</param> /// <param name="c1x">control point 1 x</param> /// <param name="c1y">control point 1 y</param> /// <param name="c2x">control point 2 x</param> /// <param name="c2y">control point 2 y</param> /// <param name="x2">x2</param> /// <param name="y2">y2</param> /// <param name="scale"></param> private void BuildCurve(RawPolygonList polygonList, double x1, double y1, double c1x, double c1y, double c2x, double c2y, double x2, double y2, double scale) { #region calculate number of step double lengthOfCurve = Math.Sqrt((c1x - x1) * (c1x - x1) + (c1y - y1) * (c1y - y1)) + Math.Sqrt((c2x - c1x) * (c2x - c1x) + (c2y - c1y) * (c2y - c1y)) + Math.Sqrt((x2 - c2x) * (x2 - c2x) + (y2 - c2y) * (y2 - c2y)); double mSteps = lengthOfCurve * DistanceToNumberOfStepScale * scale; #endregion #region add next point to polygon int i = 0; //double resultX, resultY, mu; double mu; double restOfMu; //add next point to polygon while (i < mSteps) { #region calculate point[i] mu = i / mSteps; restOfMu = 1 - mu; //add point [i] to polygon polygonList.LineTo ( x1 * restOfMu * restOfMu * restOfMu + 3 * c1x * mu * restOfMu * restOfMu + 3 * c2x * mu * mu * restOfMu + x2 * mu * mu * mu, y1 * restOfMu * restOfMu * restOfMu + 3 * c1y * mu * restOfMu * restOfMu + 3 * c2y * mu * mu * restOfMu + y2 * mu * mu * mu ); #endregion i++; } #endregion polygonList.LineTo(x2, y2); }
/// <summary> /// Generate polygon from path /// </summary> /// <param name="path">path</param> /// <param name="averageScale">average scale of transform, this to decrease number of point generate for curve ...</param> /// <returns></returns> public double[][] Generate(DrawingPath path, double averageScale) { RawPolygonList polygonList = new RawPolygonList(); DrawingPathCommand[] commands = path.Commands; double[] coordinates = path.Coordinates; int commandCount = path.CommandCount; int coordinateCount = path.CoordinateCount; int coordinateIndex = 0; //foreach(PathCommand command in commands) for (int commandIndex = 0; commandIndex < commandCount; commandIndex++) { switch (commands[commandIndex]) { case DrawingPathCommand.MoveTo: case DrawingPathCommand.NewFigure: #region move to if (path.Commands[commandIndex + 1] == DrawingPathCommand.ArcTo) { coordinateIndex += 2; break; } polygonList.MoveTo(coordinates[coordinateIndex++], coordinates[coordinateIndex++]); break; #endregion case DrawingPathCommand.NewFigureAndCloseLast: #region close last and new figure // close last polygonList.CloseCurrentPolygon(); // move to polygonList.MoveTo(coordinates[coordinateIndex++], coordinates[coordinateIndex++]); break; #endregion case DrawingPathCommand.LineTo: #region line to polygonList.LineTo(coordinates[coordinateIndex++], coordinates[coordinateIndex++]); #endregion break; case DrawingPathCommand.ArcTo: #region arc to BuildArc(polygonList, coordinates[coordinateIndex - 2], // start x coordinates[coordinateIndex - 1], // start y coordinates[coordinateIndex++], // radius x coordinates[coordinateIndex++], // radius y coordinates[coordinateIndex++], // angle coordinates[coordinateIndex++] == DrawingPath.IsLargeArc, // is large arc coordinates[coordinateIndex++] == DrawingPath.IsSweepLeftSide, // is sweep left side coordinates[coordinateIndex++], // dest x coordinates[coordinateIndex++], // dest y averageScale); #endregion break; case DrawingPathCommand.CurveTo: #region curve to BuildCurve(polygonList, coordinates[coordinateIndex - 2], // start x coordinates[coordinateIndex - 1], // start y coordinates[coordinateIndex++], // control point 1 x coordinates[coordinateIndex++], // control point 1 y coordinates[coordinateIndex++], // control point 2 x coordinates[coordinateIndex++], // control point 2 y coordinates[coordinateIndex++], // end x coordinates[coordinateIndex++], // end y averageScale); #endregion break; case DrawingPathCommand.QuadraticTo: #region quadratic bezier BuildQuadratic(polygonList, coordinates[coordinateIndex - 2], // start x coordinates[coordinateIndex - 1], // start y coordinates[coordinateIndex++], // control point x coordinates[coordinateIndex++], // control point y coordinates[coordinateIndex++], // end x coordinates[coordinateIndex++], // end y averageScale); #endregion break; } } // finish generation polygonList.Finish(); return polygonList.RawDatas; }
/// <summary> /// build arc to beziers /// </summary> /// <param name="polygonList">polygon List</param> /// <param name="centerX">coordinate X of center point</param> /// <param name="centerY">coordinate Y of center point</param> /// <param name="radiusX">radius X</param> /// <param name="radiusY">radius Y</param> /// <param name="angle">angle of arc</param> /// <param name="startAngle">angle start sweep</param> /// <param name="sweepAngle">angle sweep</param> /// <param name="scale">scale</param> private void BuildBezierArc(RawPolygonList polygonList, double centerX, double centerY, double radiusX, double radiusY, double angle, double startAngle, double sweepAngle, double scale) { #region variable int numberVertices; double tempCenterX = centerX; double tempCenterY = centerY; centerX = centerY = 0.0; double[] vertices = new double[26]; #endregion #region recalculate start angle and sweep angle startAngle = startAngle % TwoPi; if (sweepAngle > TwoPi) sweepAngle = TwoPi; else if (sweepAngle < -TwoPi) sweepAngle = -TwoPi; #endregion #region if sweep angle < 1e -10 then cannot draw arc if (Math.Abs(sweepAngle) < 1e-10) { numberVertices = 4; polygonList.LineTo( centerX + radiusX * Math.Cos(startAngle), centerY + radiusY * Math.Sin(startAngle)); polygonList.LineTo( centerX + radiusX * Math.Cos(startAngle + sweepAngle) , centerY + radiusY * Math.Sin(startAngle + sweepAngle)); return; } #endregion #region calculate all control point in bezier double totalSweep = 0.0, localSweep = 0.0; double prevSweep; numberVertices = 2; double x0, y0, tx, ty, sn, cs, tempX; bool done = false; #region when sweep angle less than 0 if (sweepAngle > 0) { do { prevSweep = totalSweep; localSweep = SmallStepAngle; totalSweep += localSweep; if (totalSweep >= sweepAngle - 0.01) { localSweep = sweepAngle - prevSweep; done = true; } #region create new control points in beziers x0 = Math.Cos(localSweep / 2.0); y0 = Math.Sin(localSweep / 2.0); tx = (1.0 - x0) * 4.0 / 3.0; ty = y0 - tx * x0 / y0; tempX = x0 + tx; //calculate sin and code of middle angle sn = Math.Sin(startAngle + localSweep / 2.0); cs = Math.Cos(startAngle + localSweep / 2.0); vertices[numberVertices - 2] = radiusX * (x0 * cs + y0 * sn); vertices[numberVertices - 1] = radiusY * (x0 * sn - y0 * cs); vertices[numberVertices] = radiusX * (tempX * cs + ty * sn); vertices[numberVertices + 1] = radiusY * (tempX * sn - ty * cs); vertices[numberVertices + 2] = radiusX * (tempX * cs - ty * sn); vertices[numberVertices + 3] = radiusY * (tempX * sn + ty * cs); vertices[numberVertices + 4] = radiusX * (x0 * cs - y0 * sn); vertices[numberVertices + 5] = radiusY * (x0 * sn + y0 * cs); #endregion numberVertices += 6; startAngle += localSweep; } while (!done && numberVertices < 26); } #endregion #region when negative sweep angle else { do { prevSweep = totalSweep; localSweep = SmallStepAngleNegative; totalSweep += localSweep; if (totalSweep <= sweepAngle + 0.01) { localSweep = sweepAngle - prevSweep; done = true; } #region create new control points in beziers x0 = Math.Cos(localSweep / 2.0); y0 = Math.Sin(localSweep / 2.0); tx = (1.0 - x0) * 4.0 / 3.0; ty = y0 - tx * x0 / y0; tempX = x0 + tx; //calculate sin and code of middle angle sn = Math.Sin(startAngle + localSweep / 2.0); cs = Math.Cos(startAngle + localSweep / 2.0); vertices[numberVertices - 2] = radiusX * (x0 * cs + y0 * sn); vertices[numberVertices - 1] = radiusY * (x0 * sn - y0 * cs); vertices[numberVertices] = radiusX * (tempX * cs + ty * sn); vertices[numberVertices + 1] = radiusY * (tempX * sn - ty * cs); vertices[numberVertices + 2] = radiusX * (tempX * cs - ty * sn); vertices[numberVertices + 3] = radiusY * (tempX * sn + ty * cs); vertices[numberVertices + 4] = radiusX * (x0 * cs - y0 * sn); vertices[numberVertices + 5] = radiusY * (x0 * sn + y0 * cs); #endregion numberVertices += 6; startAngle += localSweep; } while (!done && numberVertices < 26); } #endregion #endregion #region generate points from control points of bezier curves double cosAngle = Math.Cos(angle); double sinAngle = Math.Sin(angle); int i = 0; polygonList.MoveTo(vertices[0] * cosAngle - vertices[1] * sinAngle + tempCenterX, vertices[0] * sinAngle + vertices[1] * cosAngle + tempCenterY); while (i + 7 < numberVertices) { BuildCurve(polygonList, vertices[i + 0] * cosAngle - vertices[i + 1] * sinAngle + tempCenterX, vertices[i + 0] * sinAngle + vertices[i + 1] * cosAngle + tempCenterY, vertices[i + 2] * cosAngle - vertices[i + 3] * sinAngle + tempCenterX, vertices[i + 2] * sinAngle + vertices[i + 3] * cosAngle + tempCenterY, vertices[i + 4] * cosAngle - vertices[i + 5] * sinAngle + tempCenterX, vertices[i + 4] * sinAngle + vertices[i + 5] * cosAngle + tempCenterY, vertices[i + 6] * cosAngle - vertices[i + 7] * sinAngle + tempCenterX, vertices[i + 6] * sinAngle + vertices[i + 7] * cosAngle + tempCenterY, scale); i += 6; } #endregion }
/// <summary> /// Generate polygon from path /// </summary> /// <param name="path">path</param> /// <param name="averageScale">average scale of transform, this to decrease number of point generate for curve ...</param> /// <returns></returns> public double[][] Generate(DrawingPath path, double averageScale) { RawPolygonList polygonList = new RawPolygonList(); DrawingPathCommand[] commands = path.Commands; double[] coordinates = path.Coordinates; int commandCount = path.CommandCount; int coordinateCount = path.CoordinateCount; int coordinateIndex = 0; //foreach(PathCommand command in commands) for (int commandIndex = 0; commandIndex < commandCount; commandIndex++) { switch (commands[commandIndex]) { case DrawingPathCommand.MoveTo: case DrawingPathCommand.NewFigure: #region move to if (path.Commands[commandIndex + 1] == DrawingPathCommand.ArcTo) { coordinateIndex += 2; break; } polygonList.MoveTo(coordinates[coordinateIndex++], coordinates[coordinateIndex++]); break; #endregion case DrawingPathCommand.NewFigureAndCloseLast: #region close last and new figure // close last polygonList.CloseCurrentPolygon(); // move to polygonList.MoveTo(coordinates[coordinateIndex++], coordinates[coordinateIndex++]); break; #endregion case DrawingPathCommand.LineTo: #region line to polygonList.LineTo(coordinates[coordinateIndex++], coordinates[coordinateIndex++]); #endregion break; case DrawingPathCommand.ArcTo: #region arc to BuildArc(polygonList, coordinates[coordinateIndex - 2], // start x coordinates[coordinateIndex - 1], // start y coordinates[coordinateIndex++], // radius x coordinates[coordinateIndex++], // radius y coordinates[coordinateIndex++], // angle coordinates[coordinateIndex++] == DrawingPath.IsLargeArc, // is large arc coordinates[coordinateIndex++] == DrawingPath.IsSweepLeftSide, // is sweep left side coordinates[coordinateIndex++], // dest x coordinates[coordinateIndex++], // dest y averageScale); #endregion break; case DrawingPathCommand.CurveTo: #region curve to BuildCurve(polygonList, coordinates[coordinateIndex - 2], // start x coordinates[coordinateIndex - 1], // start y coordinates[coordinateIndex++], // control point 1 x coordinates[coordinateIndex++], // control point 1 y coordinates[coordinateIndex++], // control point 2 x coordinates[coordinateIndex++], // control point 2 y coordinates[coordinateIndex++], // end x coordinates[coordinateIndex++], // end y averageScale); #endregion break; case DrawingPathCommand.QuadraticTo: #region quadratic bezier BuildQuadratic(polygonList, coordinates[coordinateIndex - 2], // start x coordinates[coordinateIndex - 1], // start y coordinates[coordinateIndex++], // control point x coordinates[coordinateIndex++], // control point y coordinates[coordinateIndex++], // end x coordinates[coordinateIndex++], // end y averageScale); #endregion break; } } // finish generation polygonList.Finish(); return(polygonList.RawDatas); }
/// <summary> /// build arc to beziers /// </summary> /// <param name="polygonList">polygon List</param> /// <param name="centerX">coordinate X of center point</param> /// <param name="centerY">coordinate Y of center point</param> /// <param name="radiusX">radius X</param> /// <param name="radiusY">radius Y</param> /// <param name="angle">angle of arc</param> /// <param name="startAngle">angle start sweep</param> /// <param name="sweepAngle">angle sweep</param> /// <param name="scale">scale</param> private void BuildBezierArc(RawPolygonList polygonList, double centerX, double centerY, double radiusX, double radiusY, double angle, double startAngle, double sweepAngle, double scale) { #region variable int numberVertices; double tempCenterX = centerX; double tempCenterY = centerY; centerX = centerY = 0.0; double[] vertices = new double[26]; #endregion #region recalculate start angle and sweep angle startAngle = startAngle % TwoPi; if (sweepAngle > TwoPi) { sweepAngle = TwoPi; } else if (sweepAngle < -TwoPi) { sweepAngle = -TwoPi; } #endregion #region if sweep angle < 1e -10 then cannot draw arc if (Math.Abs(sweepAngle) < 1e-10) { numberVertices = 4; polygonList.LineTo( centerX + radiusX * Math.Cos(startAngle), centerY + radiusY * Math.Sin(startAngle)); polygonList.LineTo( centerX + radiusX * Math.Cos(startAngle + sweepAngle) , centerY + radiusY * Math.Sin(startAngle + sweepAngle)); return; } #endregion #region calculate all control point in bezier double totalSweep = 0.0, localSweep = 0.0; double prevSweep; numberVertices = 2; double x0, y0, tx, ty, sn, cs, tempX; bool done = false; #region when sweep angle less than 0 if (sweepAngle > 0) { do { prevSweep = totalSweep; localSweep = SmallStepAngle; totalSweep += localSweep; if (totalSweep >= sweepAngle - 0.01) { localSweep = sweepAngle - prevSweep; done = true; } #region create new control points in beziers x0 = Math.Cos(localSweep / 2.0); y0 = Math.Sin(localSweep / 2.0); tx = (1.0 - x0) * 4.0 / 3.0; ty = y0 - tx * x0 / y0; tempX = x0 + tx; //calculate sin and code of middle angle sn = Math.Sin(startAngle + localSweep / 2.0); cs = Math.Cos(startAngle + localSweep / 2.0); vertices[numberVertices - 2] = radiusX * (x0 * cs + y0 * sn); vertices[numberVertices - 1] = radiusY * (x0 * sn - y0 * cs); vertices[numberVertices] = radiusX * (tempX * cs + ty * sn); vertices[numberVertices + 1] = radiusY * (tempX * sn - ty * cs); vertices[numberVertices + 2] = radiusX * (tempX * cs - ty * sn); vertices[numberVertices + 3] = radiusY * (tempX * sn + ty * cs); vertices[numberVertices + 4] = radiusX * (x0 * cs - y0 * sn); vertices[numberVertices + 5] = radiusY * (x0 * sn + y0 * cs); #endregion numberVertices += 6; startAngle += localSweep; }while (!done && numberVertices < 26); } #endregion #region when negative sweep angle else { do { prevSweep = totalSweep; localSweep = SmallStepAngleNegative; totalSweep += localSweep; if (totalSweep <= sweepAngle + 0.01) { localSweep = sweepAngle - prevSweep; done = true; } #region create new control points in beziers x0 = Math.Cos(localSweep / 2.0); y0 = Math.Sin(localSweep / 2.0); tx = (1.0 - x0) * 4.0 / 3.0; ty = y0 - tx * x0 / y0; tempX = x0 + tx; //calculate sin and code of middle angle sn = Math.Sin(startAngle + localSweep / 2.0); cs = Math.Cos(startAngle + localSweep / 2.0); vertices[numberVertices - 2] = radiusX * (x0 * cs + y0 * sn); vertices[numberVertices - 1] = radiusY * (x0 * sn - y0 * cs); vertices[numberVertices] = radiusX * (tempX * cs + ty * sn); vertices[numberVertices + 1] = radiusY * (tempX * sn - ty * cs); vertices[numberVertices + 2] = radiusX * (tempX * cs - ty * sn); vertices[numberVertices + 3] = radiusY * (tempX * sn + ty * cs); vertices[numberVertices + 4] = radiusX * (x0 * cs - y0 * sn); vertices[numberVertices + 5] = radiusY * (x0 * sn + y0 * cs); #endregion numberVertices += 6; startAngle += localSweep; }while (!done && numberVertices < 26); } #endregion #endregion #region generate points from control points of bezier curves double cosAngle = Math.Cos(angle); double sinAngle = Math.Sin(angle); int i = 0; polygonList.MoveTo(vertices[0] * cosAngle - vertices[1] * sinAngle + tempCenterX, vertices[0] * sinAngle + vertices[1] * cosAngle + tempCenterY); while (i + 7 < numberVertices) { BuildCurve(polygonList, vertices[i + 0] * cosAngle - vertices[i + 1] * sinAngle + tempCenterX, vertices[i + 0] * sinAngle + vertices[i + 1] * cosAngle + tempCenterY, vertices[i + 2] * cosAngle - vertices[i + 3] * sinAngle + tempCenterX, vertices[i + 2] * sinAngle + vertices[i + 3] * cosAngle + tempCenterY, vertices[i + 4] * cosAngle - vertices[i + 5] * sinAngle + tempCenterX, vertices[i + 4] * sinAngle + vertices[i + 5] * cosAngle + tempCenterY, vertices[i + 6] * cosAngle - vertices[i + 7] * sinAngle + tempCenterX, vertices[i + 6] * sinAngle + vertices[i + 7] * cosAngle + tempCenterY, scale); i += 6; } #endregion }