/// <summary> /// Converts an SVGDocument into lines of Turing Code. Syntax in OpenTuring v2.0 /// </summary> /// <param name="file">The SvgDocument</param> /// <returns>Turing Code</returns> public static string toTuring(SvgDocument file) { gH = (int)file.Height.Value; gW = (int)file.Width.Value; StringBuilder sb = new StringBuilder(); sb.AppendLine(string.Format("setscreen (\"graphics: {0}; {1}\")", gW, gH)); sb.AppendLine("var c1: int % Stroke colour always"); sb.AppendLine("var c2: int % Fill colour always"); // Reduce the number of unnecessary repetitions of the usage of c1 & c2 string cacheC1 = ""; string cacheC2 = ""; // Loop through children and find the type of the SVG Element foreach (var c in file.Children) { #region Lines if (c is SvgLine) //Line { var line = c as SvgLine; SvgColourServer sv = line.Stroke as SvgColourServer; if (cacheC1 != string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sv.Colour.R / 255f, sv.Colour.G / 255f, sv.Colour.B / 255f)) { cacheC1 = string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sv.Colour.R / 255f, sv.Colour.G / 255f, sv.Colour.B / 255f); sb.AppendLine(cacheC1); } if (sv.ToString() != "none") { sb.AppendLine(drawLine((int)line.StartX.Value, (int)line.StartY.Value, (int)line.EndX.Value, (int)line.EndY.Value, (int)line.StrokeWidth.Value, getColor(sv.Colour.Name, false))); } } #endregion #region Rectangles else if (c is SvgRectangle) //Rectangle { var rect = c as SvgRectangle; int x1 = (int)rect.X.Value; int y1 = gH - ((int)rect.Y.Value - (int)rect.Height.Value); int x2 = x1 + (int)rect.Width.Value; int y2 = y1 + (int)rect.Height.Value; var sColor = rect.Stroke as SvgColourServer; var fColor = rect.Fill as SvgColourServer; // Get the colour and decide if we should use RGB or Turing (text) colours if ((sColor.ToString() != "none") && !isTuringColor(sColor.Colour.ToString())) { if (cacheC1 != string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sColor.Colour.R / 255f, sColor.Colour.G / 255f, sColor.Colour.B / 255f)) { cacheC1 = string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sColor.Colour.R / 255f, sColor.Colour.G / 255f, sColor.Colour.B / 255f); sb.AppendLine(cacheC1); } } if ((fColor.ToString() != "none") && !isTuringColor(fColor.Colour.ToString())) { if (cacheC2 != string.Format("c2 := RGB.AddColour({0}, {1}, {2})", fColor.Colour.R / 255f, fColor.Colour.G / 255f, fColor.Colour.B / 255f)) { cacheC2 = string.Format("c2 := RGB.AddColour({0}, {1}, {2})", fColor.Colour.R / 255f, fColor.Colour.G / 255f, fColor.Colour.B / 255f); sb.AppendLine(cacheC2); } } // Check and apply the stroke if ((rect.StrokeWidth.Value > 0) && (sColor.ToString() != "none") && (!sColor.Colour.IsEmpty) && (sColor.Colour.A > 0)) { int width = (int)rect.StrokeWidth.Value; sb.AppendLine(drawFillOval(x1 - width, y1 - width, x2 + width, y2 + width, getColor(sColor.Colour.ToString(), false))); } // Check if it is filled if ((fColor.ToString() != "none") && (!fColor.Colour.IsEmpty) && (fColor.Colour.A > 0)) { sb.AppendLine(drawFillBox(x1, y1, x2, y2, getColor(fColor.Colour.Name, true))); } else { sb.AppendLine(drawBox(x1, y1, x2, y2, getColor(sColor.Colour.Name, false))); } } //End if #endregion #region Paths // Thanks to Nathan Lo for helping me in this bit of horror // A sh*t ton of math is about crash down on you else if (c is SvgPath) // F*cking arcs { // SVG takes 2 points and 1 radius to draw arcs // Turing takes in 2 angles, 1 radius and 1 center point to draw it's arcs // See the problem? I see it.... var path = c as SvgPath; foreach (var p in path.PathData) { #region Arcs if (p is SvgArcSegment) { var arc = p as SvgArcSegment; //Assumption made about RadiusX = RadiusY, as the SVG program I used only supported circular arcs //So if you want to draw elliptical arcs, you are f****d. And I don't care anymore double cX, cY, sweepFrom, sweepTo; if ((arc.Sweep == SvgArcSweep.Positive) && (arc.Size == SvgArcSize.Large)) //Deal with the SVG arc flags { cX = nCenterX(arc.Start.X, gH - arc.Start.Y, arc.End.X, gH - arc.End.Y, arc.RadiusX); cY = nCenterY(arc.Start.X, gH - arc.Start.Y, arc.End.X, gH - arc.End.Y, arc.RadiusX); sweepTo = 180 * Math.Atan2(Math.Abs(cY - gH + arc.Start.Y), Math.Abs(arc.Start.X - cX)) / Math.PI; sweepFrom = 180 * Math.Atan2(Math.Abs(cY - gH + arc.End.Y), Math.Abs(arc.End.X - cX)) / Math.PI; sweepTo = adjustAngle(sweepTo, gH - arc.Start.Y - cY, arc.Start.X - cX); sweepFrom = adjustAngle(sweepFrom, gH - arc.End.Y - cY, arc.End.X - cX); } else if ((arc.Sweep == SvgArcSweep.Positive) && (arc.Size == SvgArcSize.Small)) { cX = pCenterX(arc.Start.X, gH - arc.Start.Y, arc.End.X, gH - arc.End.Y, arc.RadiusX); cY = pCenterY(arc.Start.X, gH - arc.Start.Y, arc.End.X, gH - arc.End.Y, arc.RadiusX); sweepTo = 180 * Math.Atan2(Math.Abs(cY - gH + arc.Start.Y), Math.Abs(arc.Start.X - cX)) / Math.PI; sweepFrom = 180 * Math.Atan2(Math.Abs(cY - gH + arc.End.Y), Math.Abs(arc.End.X - cX)) / Math.PI; sweepTo = adjustAngle(sweepTo, gH - arc.Start.Y - cY, arc.Start.X - cX); sweepFrom = adjustAngle(sweepFrom, gH - arc.End.Y - cY, arc.End.X - cX); } else if ((arc.Sweep == SvgArcSweep.Negative) && (arc.Size == SvgArcSize.Small)) { cX = nCenterX(arc.Start.X, gH - arc.Start.Y, arc.End.X, gH - arc.End.Y, arc.RadiusX); cY = nCenterY(arc.Start.X, gH - arc.Start.Y, arc.End.X, gH - arc.End.Y, arc.RadiusX); sweepFrom = 180 * Math.Atan2(Math.Abs(cY - gH + arc.Start.Y), Math.Abs(arc.Start.X - cX)) / Math.PI; sweepTo = 180 * Math.Atan2(Math.Abs(cY - gH + arc.End.Y), Math.Abs(arc.End.X - cX)) / Math.PI; sweepFrom = adjustAngle(sweepFrom, gH - arc.Start.Y - cY, arc.Start.X - cX); sweepTo = adjustAngle(sweepTo, gH - arc.End.Y - cY, arc.End.X - cX); } else { cX = pCenterX(arc.Start.X, gH - arc.Start.Y, arc.End.X, gH - arc.End.Y, arc.RadiusX); cY = pCenterY(arc.Start.X, gH - arc.Start.Y, arc.End.X, gH - arc.End.Y, arc.RadiusX); sweepFrom = 180 * Math.Atan2(Math.Abs(cY - gH + arc.Start.Y), Math.Abs(arc.Start.X - cX)) / Math.PI; sweepTo = 180 * Math.Atan2(Math.Abs(cY - gH + arc.End.Y), Math.Abs(arc.End.X - cX)) / Math.PI; sweepFrom = adjustAngle(sweepFrom, gH - arc.Start.Y - cY, arc.Start.X - cX); sweepTo = adjustAngle(sweepTo, gH - arc.End.Y - cY, arc.End.X - cX); } //Deal with colour var sv = c.Stroke as SvgColourServer; if (cacheC1 != string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sv.Colour.R / 255f, sv.Colour.G / 255f, sv.Colour.B / 255f)) { cacheC1 = string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sv.Colour.R / 255f, sv.Colour.G / 255f, sv.Colour.B / 255f); sb.AppendLine(cacheC1); } if (sv.ToString() != "none") { sb.AppendLine(drawArc((int)cX, (int)cY, (int)arc.RadiusX, (int)arc.RadiusY, (int)sweepFrom, (int)sweepTo, getColor(sv.Colour.Name, false))); } } #endregion #region Cubic Beziers // Bezier curves // Full credits to: https://github.com/domoszlai/bezier2biarc // for the implementation. You are awesome! I don't have to read those research papers now :P else if (p is SvgCubicCurveSegment) { var b = p as SvgCubicCurveSegment; // Broken. TODO: Fix this please sb.AppendLine("% Bezier curves currenly broken. Sorry. Element id [" + c.ID + "]"); continue; //Bezier is broken CubicBezier curve = new CubicBezier( new Vector2(b.Start.X, b.Start.Y), new Vector2(b.FirstControlPoint.X, b.FirstControlPoint.Y), new Vector2(b.SecondControlPoint.X, b.SecondControlPoint.Y), new Vector2(b.End.X, b.End.Y)); /*curve = new CubicBezier( * new Vector2(100, 500), new Vector2(150, 100), new Vector2(500, 150), new Vector2(350, 350));*/ var biarcs = Algorithm.ApproxCubicBezier(curve, 10, 1); foreach (var biarc in biarcs) { int r1 = (int)Math.Ceiling((biarc.A1.startAngle * 180.0f / (float)Math.PI) > 360 ? -(biarc.A1.startAngle * 180.0f / (float)Math.PI) : 360 - (biarc.A1.startAngle * 180.0f / (float)Math.PI)); int r2 = (int)Math.Ceiling(biarc.A1.sweepAngle * 180.0f / (float)Math.PI); int r3 = (int)Math.Ceiling((biarc.A2.startAngle * 180.0f / (float)Math.PI) > 360 ? -(biarc.A2.startAngle * 180.0f / (float)Math.PI) : 360 - (biarc.A2.startAngle * 180.0f / (float)Math.PI)); int r4 = (int)Math.Ceiling(biarc.A2.sweepAngle * 180.0f / (float)Math.PI); sb.AppendLine(string.Format("Draw.Arc({0}, {1}, {2}, {3}, {4}, {5}, red)", (int)(biarc.A1.C.X), (int)(gH - biarc.A1.C.Y), (int)(biarc.A1.r), (int)(biarc.A1.r), (int)(r1), (int)(r1 + r2))); sb.AppendLine(string.Format("Draw.Arc({0}, {1}, {2}, {3}, {4}, {5}, red)", (int)(biarc.A2.C.X), (int)(gH - biarc.A2.C.Y), (int)(biarc.A2.r), (int)(biarc.A2.r), (int)(r3), (int)(r3 + r4))); } //End foreach } //End if #endregion } //End foreach } //End if #endregion #region Ellipse else if (c is SvgEllipse) { var e = c as SvgEllipse; var sColor = e.Stroke as SvgColourServer; var fColor = e.Fill as SvgColourServer; if ((sColor.ToString() != "none") && !isTuringColor(sColor.Colour.ToString())) { if (cacheC1 != string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sColor.Colour.R / 255f, sColor.Colour.G / 255f, sColor.Colour.B / 255f)) { cacheC1 = string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sColor.Colour.R / 255f, sColor.Colour.G / 255f, sColor.Colour.B / 255f); sb.AppendLine(cacheC1); } } if ((fColor.ToString() != "none") && !isTuringColor(fColor.Colour.ToString())) { if (cacheC2 != string.Format("c2 := RGB.AddColour({0}, {1}, {2})", fColor.Colour.R / 255f, fColor.Colour.G / 255f, fColor.Colour.B / 255f)) { cacheC2 = string.Format("c2 := RGB.AddColour({0}, {1}, {2})", fColor.Colour.R / 255f, fColor.Colour.G / 255f, fColor.Colour.B / 255f); sb.AppendLine(cacheC2); } } // Check and apply the stroke if ((e.StrokeWidth.Value > 0) && (sColor.ToString() != "none") && (!sColor.Colour.IsEmpty) && (sColor.Colour.A > 0)) { int width = (int)e.StrokeWidth.Value; sb.AppendLine(drawFillOval((int)e.CenterX, gH - (int)e.CenterY, (int)e.RadiusX + width, (int)e.RadiusY + width, getColor(sColor.Colour.ToString(), false))); } // Check if it is filled if ((fColor.ToString() != "none") && (!fColor.Colour.IsEmpty) && (fColor.Colour.A > 0)) { sb.AppendLine(drawFillOval((int)e.CenterX.Value, gH - (int)e.CenterY.Value, (int)e.RadiusX.Value, (int)e.RadiusY.Value, getColor(fColor.Colour.Name, true))); } else { sb.AppendLine(drawOval((int)e.CenterX.Value, gH - (int)e.CenterY.Value, (int)e.RadiusX.Value, (int)e.RadiusY.Value, getColor(sColor.Colour.Name, false))); } } #endregion #region Circles else if (c is SvgCircle) { var e = c as SvgCircle; var sColor = e.Stroke as SvgColourServer; var fColor = e.Fill as SvgColourServer; if ((sColor.ToString() != "none") && !isTuringColor(sColor.Colour.ToString())) { if (cacheC1 != string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sColor.Colour.R / 255f, sColor.Colour.G / 255f, sColor.Colour.B / 255f)) { cacheC1 = string.Format("c1 := RGB.AddColour({0}, {1}, {2})", sColor.Colour.R / 255f, sColor.Colour.G / 255f, sColor.Colour.B / 255f); sb.AppendLine(cacheC1); } } if ((fColor.ToString() != "none") && !isTuringColor(fColor.Colour.ToString())) { if (cacheC2 != string.Format("c2 := RGB.AddColour({0}, {1}, {2})", fColor.Colour.R / 255f, fColor.Colour.G / 255f, fColor.Colour.B / 255f)) { cacheC2 = string.Format("c2 := RGB.AddColour({0}, {1}, {2})", fColor.Colour.R / 255f, fColor.Colour.G / 255f, fColor.Colour.B / 255f); sb.AppendLine(cacheC2); } } // Check and apply the stroke if ((e.StrokeWidth.Value > 0) && (sColor.ToString() != "none") && (!sColor.Colour.IsEmpty) && (sColor.Colour.A > 0)) { int width = (int)e.StrokeWidth.Value; sb.AppendLine(drawFillOval((int)e.CenterX, gH - (int)e.CenterY, (int)e.Radius + width, (int)e.Radius + width, getColor(sColor.Colour.ToString(), false))); } //Check if it is filled if ((fColor.ToString() != "none") && (!fColor.Colour.IsEmpty) && (fColor.Colour.A > 0)) { sb.AppendLine(drawFillOval((int)e.CenterX.Value, gH - (int)e.CenterY.Value, (int)e.Radius.Value, (int)e.Radius.Value, getColor(fColor.Colour.Name, true))); } else { sb.AppendLine(drawOval((int)e.CenterX.Value, gH - (int)e.CenterY.Value, (int)e.Radius.Value, (int)e.Radius.Value, getColor(sColor.Colour.Name, false))); } } //End if #endregion } // End foreach //Return return(sb.ToString()); }