Category talk:Wren-ellipse

From Rosetta Code
Revision as of 17:10, 22 August 2021 by PureFox (talk | contribs) (Added source code for new 'Wren-ellipse' module.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Source code

<lang ecmascript>/* Module "ellipse.wren" */

import "graphics" for Canvas, Color import "math" for Math, Point import "./polygon" for Polygon

/* Ellipse represents an ellipse in 2 dimensional space. */ class Ellipse {

   // Constructs a new Ellipse object with center (cx, cy),
   // horizontal radius rx and vertical radius ry.
   construct new(cx, cy, rx, ry) {
       if (!((cx is Num) && (cy is Num) && (rx is Num) && (ry is Num))) {
           Fiber.abort("All arguments must be numbers.")
       }
       _cx = cx
       _cy = cy
       _rx = rx
       _ry = ry
   }
   // Properties
   cx { _cx }
   cy { _cy }
   rx { _rx }
   ry { _ry }
   center        { Point.new(_cx, _cy) }
   horizDiameter { 2 * _rx }
   vertiDiameter { 2 * _ry }
   circumference { Num.pi * (3*(_rx + _ry) - ((3*_rx + _ry)*(_rx + 3*_ry)).sqrt) } // approx
   area          { Num.pi * _rx * _ry }
   // Private helper method to determine status of a point (x, y) relative to current instance.
   det_(x, y) { (x - _cx) * (x - _cx) / (_rx * _rx) + (y - _cy) * (y - _cy) / (_ry * _ry) }
   // Returns whether the current instance contains a point (x, y).
   // Points on the boundary are considered to be contained by the ellipse.
   contains(x, y) { det_(x, y) <= 1 }
   // Returns whether a point (x, y) is on the boundary of the current instance.
   hasEdgePoint(x, y) { det_(x,y) == 1 }
   // Returns whether a point (x, y) is an interior point of the current instance.
   // Points on the boundary are not considered to be interior ponts.
   hasInteriorPoint(x, y) { det_(x, y) < 1 }
   // Draws the current instance in a given color.
   draw(c) { Canvas.ellipse(_cx - _rx, _cy - _ry, _cx + _rx, _cy + _ry, c) }
   // Draws the current instance using a given fill color and border color.
   drawfill(fillColor, borderColor) {
       Canvas.ellipsefill(_cx - _rx, _cy - _ry, _cx + _rx, _cy + _ry, fillColor)
       draw(borderColor)
   }
   // Convenience version of drawfill which uses the same fill and border colors.
   drawfill(c) { Canvas.ellipsefill(_cx - _rx, _cy - _ry, _cx + _rx, _cy + _ry, c) }
   // Draws an arc of the current instance in a given color from startAngle to endAngle in degrees.
   // Angles are measured clockwise from the radius drawn to the current instance's rightmost point.
   drawArc(c, startAngle, endAngle) {
       var t = startAngle
       if (endAngle < startAngle) {
           while (endAngle < startAngle) endAngle = endAngle + 360
       }
       var step = (endAngle - startAngle) / 1080
       if (step == 0) return
       while (t <= endAngle) {
           var a = t * Num.pi / 180
           var x = Math.cos(a) * _rx + _cx
           var y = Math.sin(a) * _ry + _cy
           Canvas.pset(x, y, c)
           t = t + step
       }
   }
   // Draws a segment of the current instance in a given color from
   // startAngle to endAngle in degrees. Angles are measured as in drawArc.
   drawSegment(c, startAngle, endAngle) {
       drawArc(c, startAngle, endAngle)
       var sa = startAngle * Num.pi / 180
       var sx = Math.cos(sa) * _rx + _cx
       var sy = Math.sin(sa) * _ry + _cy
       if (endAngle < startAngle) {
           while (endAngle < startAngle) endAngle = endAngle + 360
       }
       var ea = endAngle * Num.pi / 180
       var ex = Math.cos(ea) * _rx + _cx
       var ey = Math.sin(ea) * _ry + _cy
       Canvas.line(sx, sy, ex, ey, c)
   }
   // Draws a segment of the current instance in a given color from
   // startAngle to endAngle in degrees. Angles are measured as in drawArc.
   drawSegmentfill(c, startAngle, endAngle) {
       drawArc(c, startAngle, endAngle)
       var t = startAngle
       if (endAngle < startAngle) {
           while (endAngle < startAngle) endAngle = endAngle + 360
       }
       var step = (endAngle - startAngle) / 1080
       if (step == 0) return
       var u = endAngle
       while (t <= u) {
           var ta = t * Num.pi / 180
           var sx = Math.cos(ta) * _rx + _cx
           var sy = Math.sin(ta) * _ry + _cy
           var ua = u * Num.pi / 180
           var ex = Math.cos(ua) * _rx + _cx
           var ey = Math.sin(ua) * _ry + _cy
           Canvas.line(sx, sy, ex, ey, c, 2)
           t = t + step
           u = u - step
       }
   }
   // Draws a segment of the current instance in a given fill color and border color from
   // startAngle to endAngle in degrees. Angles are measured as in drawArc.
   drawSegmentfill(fillColor, startAngle, endAngle, borderColor) {
       drawSegmentfill(fillColor, startAngle, endAngle)
       drawSegment(borderColor, startAngle, endAngle)
   }
   // Draws a sector of the current instance in a given color from
   // startAngle to endAngle in degrees. Angles are measured as in drawArc.
   drawSector(c, startAngle, endAngle) {
       drawArc(c, startAngle, endAngle)
       var sa = startAngle * Num.pi / 180
       var sx = Math.cos(sa) * _rx + _cx
       var sy = Math.sin(sa) * _ry + _cy
       Canvas.line(_cx, _cy, sx, sy, c)
       if (endAngle < startAngle) {
           while (endAngle < startAngle) endAngle = endAngle + 360
       }
       var ea = endAngle * Num.pi / 180
       var ex = Math.cos(ea) * _rx + _cx
       var ey = Math.sin(ea) * _ry + _cy
       Canvas.line(_cx, _cy, ex, ey, c)
   }
   // Draws a filled sector of the current instance in a given color from
   // startAngle to endAngle in degrees. Angles are measured as in drawArc.
   drawSectorfill(c, startAngle, endAngle) {
       drawSegmentfill(c, startAngle, endAngle)
       var sa = startAngle * Num.pi / 180
       var sx = Math.cos(sa) * _rx + _cx
       var sy = Math.sin(sa) * _ry + _cy
       if (endAngle < startAngle) {
           while (endAngle < startAngle) endAngle = endAngle + 360
       }
       var ea = endAngle * Num.pi / 180
       var ex = Math.cos(ea) * _rx + _cx
       var ey = Math.sin(ea) * _ry + _cy
       var tri = Polygon.quick([[_cx, _cy], [sx, sy], [ex, ey]])
       tri.drawfill(c)
   }
   // Draws a sector of the current instance in a fill color and border color from
   // startAngle to endAngle in degrees. Angles are measured as in drawArc.
   drawSectorfill(fillColor, startAngle, endAngle, borderColor) {
       drawSectorfill(fillColor, startAngle, endAngle)
       drawSector(borderColor, startAngle, endAngle)
   }
   // Draws the current instance as a pie using a list of colors and
   // associated start angles in degrees.
   drawPie(colors, startAngles) {
       var slices = startAngles.count
       for (i in 0...slices-1) {
           var delta = startAngles[i + 1] - startAngles[i]
           if (delta < 0) delta = delta + 360
           if (delta <= 180) {
               drawSectorfill(colors[i], startAngles[i], startAngles[i] + delta)
           } else {
               drawSectorfill(colors[i], startAngles[i], startAngles[i] + 180)
               drawSectorfill(colors[i], startAngles[i] + 180, startAngles[i] + delta)
           }
       }
       var delta = startAngles[0] - startAngles[-1]
       if (delta < 0) delta = delta + 360
       if (delta <= 180) {
           drawSectorfill(colors[-1], startAngles[-1], startAngles[-1] + delta)
       } else {
           drawSectorfill(colors[-1], startAngles[-1], startAngles[-1] + 180)
       }
   }

}

/* Circle represents a circle in 2 dimensional space. */ class Circle is Ellipse {

   // Constructs a new Circle object with center (x, y) and radius r.
   construct new(cx, cy, r) {
       super(cx, cy, r, r)
       _r  = r
   }
   // Properties
   r { _r }
   diameter      { _r * 2 }
   circumference { 2 * Num.pi * _r }
   // Draws the current instance in a given color and thickness.
   draw(c, size) {
       var t = _r - size
       if (t < 0) t = 0
       for (s in _r...t) Canvas.circle(cx, cy, s, c)
   }
   // Convenience version of draw which uses a default thickness of 1 pixel.
   draw(c) { draw(c, 1) }
  // Draws the current instance using a given fill color, border color and border thickness.
   drawfill(fillColor, borderColor, borderSize) {
       Canvas.circlefill(cx, cy, _r, fillColor)
       draw(borderColor, borderSize)
   }
   // Draws the current instance using a given fill color, border color and border thickness of 1.
   drawfill(fillColor, borderColor) {
       Canvas.circlefill(cx, cy, _r, fillColor)
       draw(borderColor)
   }
   // Convenience version of drawfill which uses the same fill and border colors.
   drawfill(c) { Canvas.circlefill(cx, cy, _r, c) }

}

/* Button represents a rectangle with curved corners in 2 dimensional space. */ class Button {

   // Constructs a new elliptical Button object with center (x, y), width w and height h.
   construct new(cx, cy, w, h) {
       if (!((cx is Num) && (cy is Num) && (w is Num) && (h is Num))) {
           Fiber.abort("All arguments must be numbers.")
       }
       _cx = cx
       _cy = cy
       _w  = w
       _h  = h
   }
   // Convenience method which constructs a square button object with center (x, y) and width w.
   static new(cx, cy, w) { new(cx, cy, w, w) }
   // Properties
   cx     { _cx }
   cy     { _cy }
   width  { _w }
   height { _h }
   // Draws the current instance in a given color.
   draw(c) {
       var rx = _w / 3
       var ry = _h / 3
       var cx = _cx - rx/2
       var cy = _cy - ry/2
       var angles   = [[0, 90], [90, 180], [180, 270], [270, 360]]
       var centIncs = [[ rx, ry], [0, ry], [0, 0], [rx, 0]]
       var lineIncs = [[-rx, 0], [0, -ry], [rx, 0], [0, ry]]
       for (i in 0..3) {
           var e = Ellipse.new(cx + centIncs[i][0], cy + centIncs[i][1], rx, ry)
           var startAngle = angles[i][0]
           var endAngle   = angles[i][1]
           e.drawArc(c, startAngle, endAngle)
           var a = endAngle * Num.pi / 180
           var sx = Math.cos(a) * rx + e.cx
           var sy = Math.sin(a) * ry + e.cy
           Canvas.line(sx, sy, sx + lineIncs[i][0], sy + lineIncs[i][1], c)
       }
   }
   // Draws the current instance filled with a given color and with a given border color.
   drawfill(fillColor, borderColor) {
       var rx = _w / 3
       var ry = _h / 3
       var cx = _cx - rx/2
       var cy = _cy - ry/2
       var angles   = [[0, 90], [90, 180], [180, 270], [270, 360]]
       var centIncs = [[ rx, ry], [0, ry], [0, 0], [rx, 0]]
       var lineIncs = [[-rx, 0], [0, -ry], [rx, 0], [0, ry]]
       var rectIncs = [[-rx, -ry], [0, -ry], [0, 0], [-rx, 0]]
       for (i in 0..3) {
           var e = Ellipse.new(cx + centIncs[i][0], cy + centIncs[i][1], rx, ry)
           var startAngle = angles[i][0]
           var endAngle   = angles[i][1]
           e.drawSectorfill(fillColor, startAngle, endAngle)
           e.drawArc(borderColor, startAngle, endAngle)
           var a = endAngle * Num.pi / 180
           var sx = Math.cos(a) * rx + e.cx
           var sy = Math.sin(a) * ry + e.cy
           Canvas.line(sx, sy, sx + lineIncs[i][0], sy + lineIncs[i][1], borderColor, 2)        
           Canvas.rectfill(sx + rectIncs[i][0], sy + rectIncs[i][1], rx, ry, fillColor)
       }
       Canvas.rectfill(cx, cy, rx, ry, fillColor)
   }
   // Convenience version of drawfill method where the fill and border colors are the same.
   drawfill(c) { drawfill(c, c) }

}</lang>