// Copyright 2010 The draw2d Authors. All rights reserved. // created: 06/12/2010 by Laurent Le Goff package draw2d import ( "math" ) type PathConverter struct { converter VertexConverter ApproximationScale, AngleTolerance, CuspLimit float64 startX, startY, x, y float64 } func NewPathConverter(converter VertexConverter) *PathConverter { return &PathConverter{converter, 1, 0, 0, 0, 0, 0, 0} } func (c *PathConverter) Convert(paths ...*PathStorage) { for _, path := range paths { j := 0 for _, cmd := range path.commands { j = j + c.ConvertCommand(cmd, path.vertices[j:]...) } c.converter.NextCommand(VertexStopCommand) } } func (c *PathConverter) ConvertCommand(cmd PathCmd, vertices ...float64) int { switch cmd { case MoveTo: c.x, c.y = vertices[0], vertices[1] c.startX, c.startY = c.x, c.y c.converter.NextCommand(VertexStopCommand) c.converter.NextCommand(VertexStartCommand) c.converter.Vertex(c.x, c.y) return 2 case LineTo: c.x, c.y = vertices[0], vertices[1] if c.startX == c.x && c.startY == c.y { c.converter.NextCommand(VertexCloseCommand) } c.converter.Vertex(c.x, c.y) c.converter.NextCommand(VertexJoinCommand) return 2 case QuadCurveTo: quadraticBezier(c.converter, c.x, c.y, vertices[0], vertices[1], vertices[2], vertices[3], c.ApproximationScale, c.AngleTolerance) c.x, c.y = vertices[2], vertices[3] if c.startX == c.x && c.startY == c.y { c.converter.NextCommand(VertexCloseCommand) } c.converter.Vertex(c.x, c.y) return 4 case CubicCurveTo: cubicBezier(c.converter, c.x, c.y, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale, c.AngleTolerance, c.CuspLimit) c.x, c.y = vertices[4], vertices[5] if c.startX == c.x && c.startY == c.y { c.converter.NextCommand(VertexCloseCommand) } c.converter.Vertex(c.x, c.y) return 6 case ArcTo: c.x, c.y = arc(c.converter, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale) if c.startX == c.x && c.startY == c.y { c.converter.NextCommand(VertexCloseCommand) } c.converter.Vertex(c.x, c.y) return 6 case Close: c.converter.NextCommand(VertexCloseCommand) c.converter.Vertex(c.startX, c.startY) return 0 } return 0 } func (c *PathConverter) MoveTo(x, y float64) *PathConverter { c.x, c.y = x, y c.startX, c.startY = c.x, c.y c.converter.NextCommand(VertexStopCommand) c.converter.NextCommand(VertexStartCommand) c.converter.Vertex(c.x, c.y) return c } func (c *PathConverter) RMoveTo(dx, dy float64) *PathConverter { c.MoveTo(c.x+dx, c.y+dy) return c } func (c *PathConverter) LineTo(x, y float64) *PathConverter { c.x, c.y = x, y if c.startX == c.x && c.startY == c.y { c.converter.NextCommand(VertexCloseCommand) } c.converter.Vertex(c.x, c.y) c.converter.NextCommand(VertexJoinCommand) return c } func (c *PathConverter) RLineTo(dx, dy float64) *PathConverter { c.LineTo(c.x+dx, c.y+dy) return c } func (c *PathConverter) QuadCurveTo(cx, cy, x, y float64) *PathConverter { quadraticBezier(c.converter, c.x, c.y, cx, cy, x, y, c.ApproximationScale, c.AngleTolerance) c.x, c.y = x, y if c.startX == c.x && c.startY == c.y { c.converter.NextCommand(VertexCloseCommand) } c.converter.Vertex(c.x, c.y) return c } func (c *PathConverter) RQuadCurveTo(dcx, dcy, dx, dy float64) *PathConverter { c.QuadCurveTo(c.x+dcx, c.y+dcy, c.x+dx, c.y+dy) return c } func (c *PathConverter) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathConverter { cubicBezier(c.converter, c.x, c.y, cx1, cy1, cx2, cy2, x, y, c.ApproximationScale, c.AngleTolerance, c.CuspLimit) c.x, c.y = x, y if c.startX == c.x && c.startY == c.y { c.converter.NextCommand(VertexCloseCommand) } c.converter.Vertex(c.x, c.y) return c } func (c *PathConverter) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) *PathConverter { c.CubicCurveTo(c.x+dcx1, c.y+dcy1, c.x+dcx2, c.y+dcy2, c.x+dx, c.y+dy) return c } func (c *PathConverter) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathConverter { endAngle := startAngle + angle clockWise := true if angle < 0 { clockWise = false } // normalize if clockWise { for endAngle < startAngle { endAngle += math.Pi * 2.0 } } else { for startAngle < endAngle { startAngle += math.Pi * 2.0 } } startX := cx + math.Cos(startAngle)*rx startY := cy + math.Sin(startAngle)*ry c.MoveTo(startX, startY) c.x, c.y = arc(c.converter, cx, cy, rx, ry, startAngle, angle, c.ApproximationScale) if c.startX == c.x && c.startY == c.y { c.converter.NextCommand(VertexCloseCommand) } c.converter.Vertex(c.x, c.y) return c } func (c *PathConverter) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *PathConverter { c.ArcTo(c.x+dcx, c.y+dcy, rx, ry, startAngle, angle) return c } func (c *PathConverter) Close() *PathConverter { c.converter.NextCommand(VertexCloseCommand) c.converter.Vertex(c.startX, c.startY) return c }