summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go')
-rw-r--r--Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go581
1 files changed, 581 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go
new file mode 100644
index 000000000..2a19e7355
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go
@@ -0,0 +1,581 @@
+// Copyright 2011 The draw2d Authors. All rights reserved.
+// created: 27/05/2011 by Laurent Le Goff
+package raster
+
+const (
+ POLYGON_CLIP_NONE = iota
+ POLYGON_CLIP_LEFT
+ POLYGON_CLIP_RIGHT
+ POLYGON_CLIP_TOP
+ POLYGON_CLIP_BOTTOM
+)
+
+type Polygon []float64
+
+type PolygonEdge struct {
+ X, Slope float64
+ FirstLine, LastLine int
+ Winding int16
+}
+
+//! A more optimized representation of a polygon edge.
+type PolygonScanEdge struct {
+ FirstLine, LastLine int
+ Winding int16
+ X Fix
+ Slope Fix
+ SlopeFix Fix
+ NextEdge *PolygonScanEdge
+}
+
+//! Calculates the edges of the polygon with transformation and clipping to edges array.
+/*! \param startIndex the index for the first vertex.
+ * \param vertexCount the amount of vertices to convert.
+ * \param edges the array for result edges. This should be able to contain 2*aVertexCount edges.
+ * \param tr the transformation matrix for the polygon.
+ * \param aClipRectangle the clip rectangle.
+ * \return the amount of edges in the result.
+ */
+func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [6]float64, clipBound [4]float64) int {
+ startIndex = startIndex * 2
+ endIndex := startIndex + vertexCount*2
+ if endIndex > len(p) {
+ endIndex = len(p)
+ }
+
+ x := p[startIndex]
+ y := p[startIndex+1]
+ // inline transformation
+ prevX := x*tr[0] + y*tr[2] + tr[4]
+ prevY := x*tr[1] + y*tr[3] + tr[5]
+
+ //! Calculates the clip flags for a point.
+ prevClipFlags := POLYGON_CLIP_NONE
+ if prevX < clipBound[0] {
+ prevClipFlags |= POLYGON_CLIP_LEFT
+ } else if prevX >= clipBound[2] {
+ prevClipFlags |= POLYGON_CLIP_RIGHT
+ }
+
+ if prevY < clipBound[1] {
+ prevClipFlags |= POLYGON_CLIP_TOP
+ } else if prevY >= clipBound[3] {
+ prevClipFlags |= POLYGON_CLIP_BOTTOM
+ }
+
+ edgeCount := 0
+ var k, clipFlags, clipSum, clipUnion int
+ var xleft, yleft, xright, yright, oldY, maxX, minX float64
+ var swapWinding int16
+ for n := startIndex; n < endIndex; n = n + 2 {
+ k = (n + 2) % len(p)
+ x = p[k]*tr[0] + p[k+1]*tr[2] + tr[4]
+ y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5]
+
+ //! Calculates the clip flags for a point.
+ clipFlags = POLYGON_CLIP_NONE
+ if prevX < clipBound[0] {
+ clipFlags |= POLYGON_CLIP_LEFT
+ } else if prevX >= clipBound[2] {
+ clipFlags |= POLYGON_CLIP_RIGHT
+ }
+ if prevY < clipBound[1] {
+ clipFlags |= POLYGON_CLIP_TOP
+ } else if prevY >= clipBound[3] {
+ clipFlags |= POLYGON_CLIP_BOTTOM
+ }
+
+ clipSum = prevClipFlags | clipFlags
+ clipUnion = prevClipFlags & clipFlags
+
+ // Skip all edges that are either completely outside at the top or at the bottom.
+ if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 {
+ if clipUnion&POLYGON_CLIP_RIGHT != 0 {
+ // Both clip to right, edge is a vertical line on the right side
+ if getVerticalEdge(prevY, y, clipBound[2], &edges[edgeCount], clipBound) {
+ edgeCount++
+ }
+ } else if clipUnion&POLYGON_CLIP_LEFT != 0 {
+ // Both clip to left, edge is a vertical line on the left side
+ if getVerticalEdge(prevY, y, clipBound[0], &edges[edgeCount], clipBound) {
+ edgeCount++
+ }
+ } else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 {
+ // No clipping in the horizontal direction
+ if getEdge(prevX, prevY, x, y, &edges[edgeCount], clipBound) {
+ edgeCount++
+ }
+ } else {
+ // Clips to left or right or both.
+
+ if x < prevX {
+ xleft, yleft = x, y
+ xright, yright = prevX, prevY
+ swapWinding = -1
+ } else {
+ xleft, yleft = prevX, prevY
+ xright, yright = x, y
+ swapWinding = 1
+ }
+
+ slope := (yright - yleft) / (xright - xleft)
+
+ if clipSum&POLYGON_CLIP_RIGHT != 0 {
+ // calculate new position for the right vertex
+ oldY = yright
+ maxX = clipBound[2]
+
+ yright = yleft + (maxX-xleft)*slope
+ xright = maxX
+
+ // add vertical edge for the overflowing part
+ if getVerticalEdge(yright, oldY, maxX, &edges[edgeCount], clipBound) {
+ edges[edgeCount].Winding *= swapWinding
+ edgeCount++
+ }
+ }
+
+ if clipSum&POLYGON_CLIP_LEFT != 0 {
+ // calculate new position for the left vertex
+ oldY = yleft
+ minX = clipBound[0]
+
+ yleft = yleft + (minX-xleft)*slope
+ xleft = minX
+
+ // add vertical edge for the overflowing part
+ if getVerticalEdge(oldY, yleft, minX, &edges[edgeCount], clipBound) {
+ edges[edgeCount].Winding *= swapWinding
+ edgeCount++
+ }
+ }
+
+ if getEdge(xleft, yleft, xright, yright, &edges[edgeCount], clipBound) {
+ edges[edgeCount].Winding *= swapWinding
+ edgeCount++
+ }
+ }
+ }
+
+ prevClipFlags = clipFlags
+ prevX = x
+ prevY = y
+ }
+
+ return edgeCount
+}
+
+//! Creates a polygon edge between two vectors.
+/*! Clips the edge vertically to the clip rectangle. Returns true for edges that
+ * should be rendered, false for others.
+ */
+func getEdge(x0, y0, x1, y1 float64, edge *PolygonEdge, clipBound [4]float64) bool {
+ var startX, startY, endX, endY float64
+ var winding int16
+
+ if y0 <= y1 {
+ startX = x0
+ startY = y0
+ endX = x1
+ endY = y1
+ winding = 1
+ } else {
+ startX = x1
+ startY = y1
+ endX = x0
+ endY = y0
+ winding = -1
+ }
+
+ // Essentially, firstLine is floor(startY + 1) and lastLine is floor(endY).
+ // These are refactored to integer casts in order to avoid function
+ // calls. The difference with integer cast is that numbers are always
+ // rounded towards zero. Since values smaller than zero get clipped away,
+ // only coordinates between 0 and -1 require greater attention as they
+ // also round to zero. The problems in this range can be avoided by
+ // adding one to the values before conversion and subtracting after it.
+
+ firstLine := int(startY + 1)
+ lastLine := int(endY+1) - 1
+
+ minClip := int(clipBound[1])
+ maxClip := int(clipBound[3])
+
+ // If start and end are on the same line, the edge doesn't cross
+ // any lines and thus can be ignored.
+ // If the end is smaller than the first line, edge is out.
+ // If the start is larger than the last line, edge is out.
+ if firstLine > lastLine || lastLine < minClip || firstLine >= maxClip {
+ return false
+ }
+
+ // Adjust the start based on the target.
+ if firstLine < minClip {
+ firstLine = minClip
+ }
+
+ if lastLine >= maxClip {
+ lastLine = maxClip - 1
+ }
+ edge.Slope = (endX - startX) / (endY - startY)
+ edge.X = startX + (float64(firstLine)-startY)*edge.Slope
+ edge.Winding = winding
+ edge.FirstLine = firstLine
+ edge.LastLine = lastLine
+
+ return true
+}
+
+//! Creates a vertical polygon edge between two y values.
+/*! Clips the edge vertically to the clip rectangle. Returns true for edges that
+ * should be rendered, false for others.
+ */
+func getVerticalEdge(startY, endY, x float64, edge *PolygonEdge, clipBound [4]float64) bool {
+ var start, end float64
+ var winding int16
+ if startY < endY {
+ start = startY
+ end = endY
+ winding = 1
+ } else {
+ start = endY
+ end = startY
+ winding = -1
+ }
+
+ firstLine := int(start + 1)
+ lastLine := int(end+1) - 1
+
+ minClip := int(clipBound[1])
+ maxClip := int(clipBound[3])
+
+ // If start and end are on the same line, the edge doesn't cross
+ // any lines and thus can be ignored.
+ // If the end is smaller than the first line, edge is out.
+ // If the start is larger than the last line, edge is out.
+ if firstLine > lastLine || lastLine < minClip || firstLine >= maxClip {
+ return false
+ }
+
+ // Adjust the start based on the clip rect.
+ if firstLine < minClip {
+ firstLine = minClip
+ }
+ if lastLine >= maxClip {
+ lastLine = maxClip - 1
+ }
+
+ edge.Slope = 0
+ edge.X = x
+ edge.Winding = winding
+ edge.FirstLine = firstLine
+ edge.LastLine = lastLine
+
+ return true
+}
+
+type VertexData struct {
+ X, Y float64
+ ClipFlags int
+ Line int
+}
+
+//! Calculates the edges of the polygon with transformation and clipping to edges array.
+/*! Note that this may return upto three times the amount of edges that the polygon has vertices,
+ * in the unlucky case where both left and right side get clipped for all edges.
+ * \param edges the array for result edges. This should be able to contain 2*aVertexCount edges.
+ * \param aTransformation the transformation matrix for the polygon.
+ * \param aClipRectangle the clip rectangle.
+ * \return the amount of edges in the result.
+ */
+func (p Polygon) getScanEdges(edges []PolygonScanEdge, tr [6]float64, clipBound [4]float64) int {
+ var n int
+ vertexData := make([]VertexData, len(p)/2+1)
+ for n = 0; n < len(vertexData)-1; n = n + 1 {
+ k := n * 2
+ vertexData[n].X = p[k]*tr[0] + p[k+1]*tr[2] + tr[4]
+ vertexData[n].Y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5]
+ // Calculate clip flags for all vertices.
+ vertexData[n].ClipFlags = POLYGON_CLIP_NONE
+ if vertexData[n].X < clipBound[0] {
+ vertexData[n].ClipFlags |= POLYGON_CLIP_LEFT
+ } else if vertexData[n].X >= clipBound[2] {
+ vertexData[n].ClipFlags |= POLYGON_CLIP_RIGHT
+ }
+ if vertexData[n].Y < clipBound[1] {
+ vertexData[n].ClipFlags |= POLYGON_CLIP_TOP
+ } else if vertexData[n].Y >= clipBound[3] {
+ vertexData[n].ClipFlags |= POLYGON_CLIP_BOTTOM
+ }
+
+ // Calculate line of the vertex. If the vertex is clipped by top or bottom, the line
+ // is determined by the clip rectangle.
+ if vertexData[n].ClipFlags&POLYGON_CLIP_TOP != 0 {
+ vertexData[n].Line = int(clipBound[1])
+ } else if vertexData[n].ClipFlags&POLYGON_CLIP_BOTTOM != 0 {
+ vertexData[n].Line = int(clipBound[3] - 1)
+ } else {
+ vertexData[n].Line = int(vertexData[n].Y+1) - 1
+ }
+ }
+
+ // Copy the data from 0 to the last entry to make the data to loop.
+ vertexData[len(vertexData)-1] = vertexData[0]
+
+ // Transform the first vertex; store.
+ // Process mVertexCount - 1 times, next is n+1
+ // copy the first vertex to
+ // Process 1 time, next is n
+
+ edgeCount := 0
+ for n = 0; n < len(vertexData)-1; n++ {
+ clipSum := vertexData[n].ClipFlags | vertexData[n+1].ClipFlags
+ clipUnion := vertexData[n].ClipFlags & vertexData[n+1].ClipFlags
+
+ if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 &&
+ vertexData[n].Line != vertexData[n+1].Line {
+ var startIndex, endIndex int
+ var winding int16
+ if vertexData[n].Y < vertexData[n+1].Y {
+ startIndex = n
+ endIndex = n + 1
+ winding = 1
+ } else {
+ startIndex = n + 1
+ endIndex = n
+ winding = -1
+ }
+
+ firstLine := vertexData[startIndex].Line + 1
+ lastLine := vertexData[endIndex].Line
+
+ if clipUnion&POLYGON_CLIP_RIGHT != 0 {
+ // Both clip to right, edge is a vertical line on the right side
+ edges[edgeCount].FirstLine = firstLine
+ edges[edgeCount].LastLine = lastLine
+ edges[edgeCount].Winding = winding
+ edges[edgeCount].X = Fix(clipBound[2] * FIXED_FLOAT_COEF)
+ edges[edgeCount].Slope = 0
+ edges[edgeCount].SlopeFix = 0
+
+ edgeCount++
+ } else if clipUnion&POLYGON_CLIP_LEFT != 0 {
+ // Both clip to left, edge is a vertical line on the left side
+ edges[edgeCount].FirstLine = firstLine
+ edges[edgeCount].LastLine = lastLine
+ edges[edgeCount].Winding = winding
+ edges[edgeCount].X = Fix(clipBound[0] * FIXED_FLOAT_COEF)
+ edges[edgeCount].Slope = 0
+ edges[edgeCount].SlopeFix = 0
+
+ edgeCount++
+ } else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 {
+ // No clipping in the horizontal direction
+ slope := (vertexData[endIndex].X -
+ vertexData[startIndex].X) /
+ (vertexData[endIndex].Y -
+ vertexData[startIndex].Y)
+
+ // If there is vertical clip (for the top) it will be processed here. The calculation
+ // should be done for all non-clipping edges as well to determine the accurate position
+ // where the edge crosses the first scanline.
+ startx := vertexData[startIndex].X +
+ (float64(firstLine)-vertexData[startIndex].Y)*slope
+
+ edges[edgeCount].FirstLine = firstLine
+ edges[edgeCount].LastLine = lastLine
+ edges[edgeCount].Winding = winding
+ edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
+ edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
+
+ if lastLine-firstLine >= SLOPE_FIX_STEP {
+ edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
+ edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
+ } else {
+ edges[edgeCount].SlopeFix = 0
+ }
+
+ edgeCount++
+ } else {
+ // Clips to left or right or both.
+ slope := (vertexData[endIndex].X -
+ vertexData[startIndex].X) /
+ (vertexData[endIndex].Y -
+ vertexData[startIndex].Y)
+
+ // The edge may clip to both left and right.
+ // The clip results in one or two new vertices, and one to three segments.
+ // The rounding for scanlines may produce a result where any of the segments is
+ // ignored.
+
+ // The start is always above the end. Calculate the clip positions to clipVertices.
+ // It is possible that only one of the vertices exist. This will be detected from the
+ // clip flags of the vertex later, so they are initialized here.
+ var clipVertices [2]VertexData
+
+ if vertexData[startIndex].X <
+ vertexData[endIndex].X {
+ clipVertices[0].X = clipBound[0]
+ clipVertices[1].X = clipBound[2]
+ clipVertices[0].ClipFlags = POLYGON_CLIP_LEFT
+ clipVertices[1].ClipFlags = POLYGON_CLIP_RIGHT
+ } else {
+ clipVertices[0].X = clipBound[2]
+ clipVertices[1].X = clipBound[0]
+ clipVertices[0].ClipFlags = POLYGON_CLIP_RIGHT
+ clipVertices[1].ClipFlags = POLYGON_CLIP_LEFT
+ }
+
+ var p int
+ for p = 0; p < 2; p++ {
+ // Check if either of the vertices crosses the edge marked for the clip vertex
+ if clipSum&clipVertices[p].ClipFlags != 0 {
+ // The the vertex is required, calculate it.
+ clipVertices[p].Y = vertexData[startIndex].Y +
+ (clipVertices[p].X-
+ vertexData[startIndex].X)/slope
+
+ // If there is clipping in the vertical direction, the new vertex may be clipped.
+ if clipSum&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) != 0 {
+ if clipVertices[p].Y < clipBound[1] {
+ clipVertices[p].ClipFlags = POLYGON_CLIP_TOP
+ clipVertices[p].Line = int(clipBound[1])
+ } else if clipVertices[p].Y > clipBound[3] {
+ clipVertices[p].ClipFlags = POLYGON_CLIP_BOTTOM
+ clipVertices[p].Line = int(clipBound[3] - 1)
+ } else {
+ clipVertices[p].ClipFlags = 0
+ clipVertices[p].Line = int(clipVertices[p].Y+1) - 1
+ }
+ } else {
+ clipVertices[p].ClipFlags = 0
+ clipVertices[p].Line = int(clipVertices[p].Y+1) - 1
+ }
+ }
+ }
+
+ // Now there are three or four vertices, in the top-to-bottom order of start, clip0, clip1,
+ // end. What kind of edges are required for connecting these can be determined from the
+ // clip flags.
+ // -if clip vertex has horizontal clip flags, it doesn't exist. No edge is generated.
+ // -if start vertex or end vertex has horizontal clip flag, the edge to/from the clip vertex is vertical
+ // -if the line of two vertices is the same, the edge is not generated, since the edge doesn't
+ // cross any scanlines.
+
+ // The alternative patterns are:
+ // start - clip0 - clip1 - end
+ // start - clip0 - end
+ // start - clip1 - end
+
+ var topClipIndex, bottomClipIndex int
+ if (clipVertices[0].ClipFlags|clipVertices[1].ClipFlags)&
+ (POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) == 0 {
+ // Both sides are clipped, the order is start-clip0-clip1-end
+ topClipIndex = 0
+ bottomClipIndex = 1
+
+ // Add the edge from clip0 to clip1
+ // Check that the line is different for the vertices.
+ if clipVertices[0].Line != clipVertices[1].Line {
+ firstClipLine := clipVertices[0].Line + 1
+
+ startx := vertexData[startIndex].X +
+ (float64(firstClipLine)-vertexData[startIndex].Y)*slope
+
+ edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
+ edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
+ edges[edgeCount].FirstLine = firstClipLine
+ edges[edgeCount].LastLine = clipVertices[1].Line
+ edges[edgeCount].Winding = winding
+
+ if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
+ edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
+ edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
+ } else {
+ edges[edgeCount].SlopeFix = 0
+ }
+
+ edgeCount++
+ }
+ } else {
+ // Clip at either side, check which side. The clip flag is on for the vertex
+ // that doesn't exist, i.e. has not been clipped to be inside the rect.
+ if clipVertices[0].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
+ topClipIndex = 1
+ bottomClipIndex = 1
+ } else {
+ topClipIndex = 0
+ bottomClipIndex = 0
+ }
+ }
+
+ // Generate the edges from start - clip top and clip bottom - end
+ // Clip top and clip bottom may be the same vertex if there is only one
+ // clipped vertex.
+
+ // Check that the line is different for the vertices.
+ if vertexData[startIndex].Line != clipVertices[topClipIndex].Line {
+ edges[edgeCount].FirstLine = firstLine
+ edges[edgeCount].LastLine = clipVertices[topClipIndex].Line
+ edges[edgeCount].Winding = winding
+
+ // If startIndex is clipped, the edge is a vertical one.
+ if vertexData[startIndex].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
+ edges[edgeCount].X = Fix(clipVertices[topClipIndex].X * FIXED_FLOAT_COEF)
+ edges[edgeCount].Slope = 0
+ edges[edgeCount].SlopeFix = 0
+ } else {
+ startx := vertexData[startIndex].X +
+ (float64(firstLine)-vertexData[startIndex].Y)*slope
+
+ edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
+ edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
+
+ if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
+ edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
+ edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
+ } else {
+ edges[edgeCount].SlopeFix = 0
+ }
+ }
+
+ edgeCount++
+ }
+
+ // Check that the line is different for the vertices.
+ if clipVertices[bottomClipIndex].Line != vertexData[endIndex].Line {
+ firstClipLine := clipVertices[bottomClipIndex].Line + 1
+
+ edges[edgeCount].FirstLine = firstClipLine
+ edges[edgeCount].LastLine = lastLine
+ edges[edgeCount].Winding = winding
+
+ // If endIndex is clipped, the edge is a vertical one.
+ if vertexData[endIndex].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
+ edges[edgeCount].X = Fix(clipVertices[bottomClipIndex].X * FIXED_FLOAT_COEF)
+ edges[edgeCount].Slope = 0
+ edges[edgeCount].SlopeFix = 0
+ } else {
+ startx := vertexData[startIndex].X +
+ (float64(firstClipLine)-vertexData[startIndex].Y)*slope
+
+ edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
+ edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
+
+ if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
+ edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
+ edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
+ } else {
+ edges[edgeCount].SlopeFix = 0
+ }
+ }
+
+ edgeCount++
+ }
+
+ }
+ }
+ }
+
+ return edgeCount
+}