public void Line(double x0, double y0, double x1, double y1, Color4 color) { var v0 = new LineArtVertex(new Vector2d(x0, y0), color: color); var v1 = new LineArtVertex(new Vector2d(x1, y1), color: color); SubmitOpenPath(new LineSegment(v0, v1)); }
RawJoin RawJoinAt(LineArtVertex v) { var along = (V1.Pos - V0.Pos).Normalized(); var side = along.PerpendicularRight; // OPT: vector math return(new RawJoin(side * v.StrokeWidth, along, side * -v.StrokeWidth, along)); }
public PathSegment(LineArtVertex v0, LineArtVertex v1) { V0 = v0; V1 = v1; // These are normally set by Path below Before = null; After = null; Closing = false; }
// TODO: These don't have a way to specify line thickness... public void Circle(double x, double y, double radius, Color4 color, int?numSegments = null) { var vertex = new LineArtVertex(new Vector2d(x + radius, y), color); var verts = new PathSegment[] { new ArcSegment(vertex, vertex, new Vector2d(x, y), true, numSegments) }; SubmitClosedPath(verts); }
public ArcSegment(LineArtVertex v0, LineArtVertex v1, Vector2d center, bool?clockwise = null, int?requestedSegments = null) : base(v0, v1) { Center = center; Clockwise = clockwise ?? SMath.CrossZ(V0.Pos - Center, V1.Pos - Center) < 0; // XXX: Might be better to come up with something based on RADIANS_PER_PIECE // and the angle span of the arc. RequestedSegments = requestedSegments ?? DEFAULT_SEGMENTS; }
public void PolyLineUniform(IEnumerable <Vector2d> positions, Color4 color) { var verts = new List <LineArtVertex>(); foreach (var pos in positions) { var v = new LineArtVertex(pos, color: color); verts.Add(v); } PolyLine(verts); }
public void Arc(double cx, double cy, double radius, double sweep, Color4 color, double startAngle = 0.0, int?numSegments = null) { var pos0 = SMath.Rotate(Vector2d.UnitX, startAngle) * radius; var pos1 = SMath.Rotate(Vector2d.UnitX, startAngle + sweep) * radius; //Log.Message("Start angle {0}, sweep {1}, radius {2}, pos0 {3}, pos1 {4}, length1 {5}, length2 {6}", // startAngle, sweep, radius, pos0, pos1, pos0.Length, pos1.Length); var v0 = new LineArtVertex(pos0, color: color); var v1 = new LineArtVertex(pos1, color: color); SubmitOpenPath(new ArcSegment(v1, v0, new Vector2d(cx, cy), true, numSegments)); }
public RawJoin RawJoinAt(LineArtVertex v) { var side = (v.Pos - Center).Normalized(); if (Clockwise) { side = -side; } var along = -side.PerpendicularRight; return(new RawJoin(side * v.StrokeHalfWidth, along, side * -v.StrokeHalfWidth, along)); }
public override void TesselateArc(ArcSegment arc) { AdvanceToSegmentIn(arc); foreach (var pt in arc.GenerateInteriorPoints()) { var interior = pt.Item1; var sideR = pt.Item2; var sideL = pt.Item3; var verts = new LineArtVertex[] { new LineArtVertex(interior.Pos + sideR, color: interior.Color), new LineArtVertex(interior.Pos + sideL, color: interior.Color) }; AdvanceTo(verts); } AdvanceToSegmentOut(arc); }
void AdvanceToSegmentOut(PathSegment seg) { if (seg.After == null) { var rjOut = seg.RawJoinOut(); var nextVerts = new LineArtVertex[] { new LineArtVertex(seg.Pos1 + rjOut.SideR, color: seg.V1.Color), new LineArtVertex(seg.Pos1 + rjOut.SideL, color: seg.V1.Color), }; if (seg.Cap) { var cap = SquarishCap(rjOut, +1.0); var cap0 = cap.Item1; var cap1 = cap.Item2; var endVerts = new LineArtVertex[] { new LineArtVertex(seg.Pos1 + cap0, color: seg.V1.Color), new LineArtVertex(seg.Pos1 + cap1, color: seg.V1.Color) }; AdvanceTo(nextVerts); AdvanceTo(endVerts); } else { AdvanceTo(nextVerts); } } else if (seg.Closing) { CloseRoad(); } else { var jn = MiterishJoin(seg.RawJoinOut(), seg.After.RawJoinIn()); var sideR = jn.Item1; var sideL = jn.Item2; var nextVerts = new LineArtVertex[] { new LineArtVertex(seg.Pos1 + sideR, color: seg.V1.Color), new LineArtVertex(seg.Pos1 + sideL, color: seg.V1.Color) }; AdvanceTo(nextVerts); } }
void AdvanceToSegmentIn(PathSegment seg) { if (seg.Before == null) { var rjIn = seg.RawJoinIn(); var nextVerts = new LineArtVertex[] { new LineArtVertex(seg.Pos0 + rjIn.SideR, color: seg.V0.Color), new LineArtVertex(seg.Pos0 + rjIn.SideL, color: seg.V0.Color), }; if (seg.Cap) { var cap = SquarishCap(rjIn, -1.0); var cap0 = cap.Item1; var cap1 = cap.Item2; var beginVerts = new LineArtVertex[] { new LineArtVertex(seg.Pos0 + cap0, color: seg.V0.Color), new LineArtVertex(seg.Pos0 + cap1, color: seg.V0.Color), }; BeginRoad(beginVerts); AdvanceTo(nextVerts); } else { // No cap BeginRoad(nextVerts); } } else if (seg.Before.Closing || (lastIndices == null)) { // The RHS of the disjunction is in case we start tessellating in the middle of a // path somehow. That feels like it might be useful later. var jn = MiterishJoin(seg.Before.RawJoinOut(), seg.RawJoinIn()); var sideR = jn.Item1; var sideL = jn.Item2; var verts = new LineArtVertex[] { new LineArtVertex(seg.Pos0 + sideR, color: seg.V0.Color), new LineArtVertex(seg.Pos0 + sideL, color: seg.V0.Color), }; BeginRoad(verts); } }
public void AddVertex(LineArtVertex v) { Vertexes.Add(v); }
GenerateInteriorPoints() { // Everything is in radians in this function because converting to degrees and back would // be rather silly. //var offset0 = V0.Pos - Center; //var offset1 = V1.Pos - Center; var angle0 = Math.Atan2(V0.Pos.Y - Center.Y, V0.Pos.X - Center.X); var angle1 = Math.Atan2(V1.Pos.Y - Center.Y, V1.Pos.X - Center.X); if (Clockwise && (angle0 <= angle1)) { angle1 -= SMath.TAU; } else if ((!Clockwise) && (angle0 >= angle1)) { angle1 += SMath.TAU; } var angleStep = (angle1 - angle0) / RequestedSegments; var radius0 = (V0.Pos - Center).Length; var radius1 = (V1.Pos - Center).Length; //Log.Message("Radius 0 {0}, Radius 1 {1}", radius0, radius1); var radiusStep = (radius1 - radius0) / RequestedSegments; var strokeHalfWidth0 = V0.StrokeHalfWidth; var strokeHalfWidth1 = V1.StrokeHalfWidth; var strokeHalfWidthStep = strokeHalfWidth1 - strokeHalfWidth0; // This is alpha not in terms of color blending but rather in terms of // how far we are through a lerp. var alphaStep = 1.0 / RequestedSegments; var currentAlpha = 0.0; var currentAngle = angle0; var currentRadius = radius0; var currentX = Math.Cos(currentAngle) * radius0; var currentY = Math.Sin(currentAngle) * radius0; var currentStrokeHalfWidth = strokeHalfWidth0; for (int i = 1; i < RequestedSegments; i++) { currentAlpha += alphaStep; currentAngle += angleStep; currentRadius += radiusStep; currentX = Math.Cos(currentAngle) * currentRadius; currentY = Math.Sin(currentAngle) * currentRadius; currentStrokeHalfWidth += strokeHalfWidthStep; //Console.WriteLine("Generating point at x {0}, y {1}, radius {2}, angle {3}", currentX, currentY, currentRadius, currentAngle); var currentOffset = new Vector2d(currentX, currentY); var currentPosition = currentOffset + Center; var currentVertex = LineArtVertex.Lerp(V0, V1, currentAlpha, pos: currentPosition); var currentNormal = currentOffset.Normalized(); var inner = currentNormal * currentStrokeHalfWidth; var outer = -inner; //Log.Message("Current angle: {0}, X: {1}, Y: {2}, center: {3}, alpha: {4}, angle0: {5}, angle1: {6}", // currentAngle, currentX, currentY, Center, currentAlpha, angle0, angle1); yield return(new Tuple <LineArtVertex, Vector2d, Vector2d>(currentVertex, outer, inner)); } /* * var rel0 = V0.Pos - Center; * var rel1 = V1.Pos - Center; * var radius0 = rel0.Length; * var radius1 = rel1.Length; * var angle0 = Math.Atan2(V0.Pos.Y - Center.Y, V0.Pos.X - Center.X); * var angle1 = Math.Atan2(V1.Pos.Y - Center.Y, V1.Pos.X - Center.X); * if (Clockwise && angle0 <= angle1) { * angle1 -= SMath.TAU; * } else if ((!Clockwise) && (angle0 >= angle1)) { * angle1 += SMath.TAU; * } * var nPieces = RequestedSegments ?? Math.Max(3, (int)Math.Ceiling(Math.Abs(angle1 - angle0) / RADIANS_PER_PIECE)); * var deltaAlpha = 1.0 / nPieces; * var deltaRadius = (radius1 - radius0) * deltaAlpha; * var deltaAngle = (angle1 - angle0) * deltaAlpha; * var deltaStrokeHalf = (V1.StrokeHalfWidth - V0.StrokeHalfWidth) * deltaAlpha; * * var sinDA = Math.Sin(deltaAngle); * var cosDA = Math.Cos(deltaAngle); * var rel0Unit = rel0.Normalized(); * * var unitX = rel0Unit.X; * var unitY = rel0Unit.Y; * var curRadius = radius0; * var curStrokeHalf = V0.StrokeHalfWidth; * if (Clockwise) { * curStrokeHalf = -curStrokeHalf; * deltaStrokeHalf = -deltaStrokeHalf; * } * // We only need to generate nPieces-1 points because the start and end points are both excluded. * // This might be more numerically stable than the tan/cos method and still doesn't take a sin/cos * // every iteration. We shouldn't be doing this very often anyway. * for (int i = 1; i < nPieces; i++) { * curStrokeHalf += deltaStrokeHalf; * curRadius += deltaRadius; * unitX = unitX * cosDA - unitY * sinDA; * unitY = unitX * sinDA - unitY * cosDA; * var interiorPos = new Vector2d(Center.X + unitX * curRadius, Center.Y + unitY * curRadius); * var interior = LineArtVertex.Lerp(V0, V1, i * deltaAlpha, pos: interiorPos); * var sideR = new Vector2d(unitX * curStrokeHalf, unitY * curStrokeHalf); * yield return new Tuple<LineArtVertex, Vector2d, Vector2d>(interior, sideR, -sideR); * } * yield break; */ }
public LineSegment(LineArtVertex v0, LineArtVertex v1) : base(v0, v1) { Cap = true; }