Category talk:Wren-polygon
Source code
<lang ecmascript>/* Module "polygon.wren" */
import "graphics" for Canvas, Color import "math" for Point
/* Selectable is an abstract class representing the interface needed for an identifiable
object to be selectable by mouse click or otherwise */
class Selectable {
tag { "" } tag=(t) {}
selected { false } selected=(s) {}
contains(x, y) { false }
}
/* Polygon represents a polygon in 2 dimensional space. */ class Polygon is Selectable {
// Constructs a new Polygon object from its vertices and tag. construct new(vertices, tag) { if (vertices.count < 3) Fiber.abort("Number of vertices cannot be less than 3.") if (vertices[0].type != List || vertices[0].count != 2 || vertices[0][0].type != Num) { Fiber.abort("Each vertex must be a pair of numbers.") } _v = vertices.toList _tag = tag _selected = false }
// Constructs a new Polygon object from its vertices and tag without checking or copying them. construct quick(vertices, tag) { _v = vertices _tag = tag _selected = false }
// Constructs a new regular Polygon object with n sides, centered at (cx, cy), radius r from the // center to each vertex and start angle sa in degrees measured clockwise from the horizonal line // drawn to the polygon's rightmost point. static regular(n, cx, cy, r, sa, tag) { var vertices = List.filled(n, null) var inc = 360 / n var angle = sa for (i in 0...n) { var a = angle * Num.pi / 180 var x = a.cos * r + cx var y = a.sin * r + cy vertices[i] = [x, y] angle = angle + inc } return Polygon.new(vertices, tag) }
// Convenience versions of the above constructors for objects with an empty tag. static new(vertices) { new(vertices, "") }
static quick(vertices) { quick(vertices, "") }
static regular(n, cx, cy, r, sa) { regular(n, cx, cy, r, sa, "") }
// Properties sides { _v.count } // returns the number of sides of the curent instance vertices { _v.toList } // returns a shallow copy of the current instance's vertices tag { _tag } // gets the tag of the current instance tag=(t) { _tag = t } // sets the tag of the current instance selected { _selected } // gets whether the current instance is selected or not. selected=(s) { _selected = s } // sets whether the current instance is selected or not.
// Returns what kind of polygon this instance is. kind { return (sides < 11) ? ["triangle", "quadrilateral", "pentagon", "hexagon", "octagon", "nonagon", "decagon"][sides-3] : "%(sides)-sided polygon" }
// Returns whether the current instance contains a point (x, y) using the 'even-odd' rule. // Vertices or points on the boundary are considered to be contained by the polygon. contains(x, y) { var n = sides var j = n - 1 var contained = false for (i in 0...n) { if (x == _v[i][0] && y == _v[i][1]) return true // vertex if ((_v[i][1] > y) != (_v[j][1] > y)) { var slope = (x-_v[i][0])*(_v[j][1]-_v[i][1]) - (_v[j][0]-_v[i][0])*(y-_v[i][1]) if (slope == 0) return true // point on boundary if ((slope < 0) != (_v[j][1] < _v[i][1])) contained = !contained } j = i } return contained }
// Returns whether a point (x, y) is a vertex of the current instance. hasVertex(x, y) { for (i in 0...sides) { if (x == _v[i][0] && y == _v[i][1]) return true } return false }
// Returns whether a point (x, y) is a vertex or on the boundary of the current instance. hasEdgePoint(x, y) { var n = sides var j = n - 1 for (i in 0...n) { if (x == _v[i][0] && y == _v[i][1]) return true // vertex if ((_v[i][1] > y) != (_v[j][1] > y)) { var slope = (x-_v[i][0])*(_v[j][1]-_v[i][1]) - (_v[j][0]-_v[i][0])*(y-_v[i][1]) if (slope == 0) return true // point on boundary } j = i } return false }
// Returns whether a point (x, y) is an interior point of the current instance. // Vertices or points on the boundary are not considered to be interior ponts. hasInteriorPoint(x, y) { var n = sides var j = n - 1 var contained = false for (i in 0...n) { if (x == _v[i][0] && y == _v[i][1]) return false // vertex if ((_v[i][1] > y) != (_v[j][1] > y)) { var slope = (x-_v[i][0])*(_v[j][1]-_v[i][1]) - (_v[j][0]-_v[i][0])*(y-_v[i][1]) if (slope == 0) return false // point on boundary if ((slope < 0) != (_v[j][1] < _v[i][1])) contained = !contained } j = i } return contained }
// Draws the current instance in a given color and pixel size. draw(col, size) { if (col.type != Color) Fiber.abort("First argument must be a color.") if (size.type != Num || !size.isInteger || size < 1) { Fiber.abort("Size must be a positive integer.") } var j = sides - 1 for (i in 0...j) { Canvas.line(_v[i][0], _v[i][1], _v[i+1][0], _v[i+1][1], col, size) } Canvas.line(_v[j][0], _v[j][1], _v[0][0], _v[0][1], col, size) }
// Draws the current instance in a given color and pixel size of 1. draw(col) { draw(col, 1) }
// Draws the current instance filled with a given color. drawfill(col) { if (col.type != Color) Fiber.abort("First argument must be a color.") // get coordinates of bounding rectangle var vx = _v.map { |v| v[0] } var vy = _v.map { |v| v[1] } var xmin = vx.reduce { |acc, x| (x < acc) ? x : acc } var xmax = vx.reduce { |acc, x| (x > acc) ? x : acc } var ymin = vy.reduce { |acc, y| (y < acc) ? y : acc } var ymax = vy.reduce { |acc, y| (y > acc) ? y : acc } for (x in xmin..xmax) { for (y in ymin..ymax) { if (contains(x, y)) Canvas.pset(x, y, col) } } }
// Draws the current instance using a given fill color, border color and border thickness. drawfill(fillColor, borderColor, borderSize) { drawfill(fillColor) draw(borderColor, borderSize) }
// Draws the current instance using a given fill color, border color and border thickness of 1. drawfill(fillColor, borderColor) { drawfill(fillColor) draw(borderColor) }
// Returns the string representation of the current instance. toString { _vertices.toString }
}
/* Rectangle represents a rectangle in 2 dimensional space. */ class Rectangle is Polygon {
// Constructs a new Rectangle object with top left corner (x, y), width w and height h. construct new (x, y, w, h, tag) { if (!((x is Num) && (y is Num) && (w is Num) && (h is Num))) { Fiber.abort("First four arguments must be numbers.") } super([[x, y], [x + w, y], [x + w, y + h] , [x, y + h]], tag) _x = x _y = y _w = w _h = h }
// Constructs a new Rectangle object with center (cx, cy), width w and height h. static fromCenter(cx, cy, w, h, tag) { new(cx - w/2, cy - h/2, w, h, tag) }
// Convenience versions of the above constructors for objects with an empty tag. static new(x, y, w, h) { new(x, y, w, h, "") }
static fromCenter(cx, cy, w, h) { fromCenter(cx, cy, w, h, "") } // Properties x { _x } y { _y } cx { _x + _w/2 } cy { _y + _h/2 } width { _w } height { _h }
topLeft { Point.new(_x, _y) } center { Point.new(cx, cy) } perimeter { 2 * (_w + _h) } area { _w * _h }
}
/* Square represents a square in 2 dimensional space. */ class Square is Rectangle {
// Constructs a new Square object with top left corner (x, y) and side s. construct new (x, y, s, tag) { super(x, y, s, s, tag) _s = s }
// Constructs a new Square object with center (cx, cy) and side s. static fromCenter(cx, cy, s, tag) { new(cx - s/2, cy - s/2, s, tag) }
// Convenience versions of the above constructors for objects with an empty tag. static new(x, y, s) { new(x, y, s, "") }
static fromCenter(cx, cy, s) { fromCenter(cx, cy, s, "") }
// Properties. side { _s }
}</lang>