diff --git a/README.md b/README.md
index 4ce223969b..ac73f8e372 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ The Source Academy is a gamified platform designed to teach students coding whil
### Installation
1. Install a stable version of NodeJS (tested: Node 10.15.0).
2. Run `npm install` to install dependencies.
-3. Copy the `.env.example` file as `.env` and set the necessary variables (refer to docs/contributing for more information)
+3. Copy the `.env.example` file as `.env` and set the necessary variables (refer below for more information)
4. Run `npm start` to start the server at `localhost:8075`.
### Setting up your environment
diff --git a/public/externalLibs/graphics/CURVES_README.md b/public/externalLibs/graphics/CURVES_README.md
new file mode 100644
index 0000000000..cd4de82c98
--- /dev/null
+++ b/public/externalLibs/graphics/CURVES_README.md
@@ -0,0 +1,3 @@
+## Graphic Library
+
+Curve-producing library for Source Academy.
\ No newline at end of file
diff --git a/public/externalLibs/graphics/README.md b/public/externalLibs/graphics/README.md
deleted file mode 100644
index c065a56dd6..0000000000
--- a/public/externalLibs/graphics/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## Graphic Library
-
-this is the graphics library
\ No newline at end of file
diff --git a/public/externalLibs/graphics/RUNES_README.md b/public/externalLibs/graphics/RUNES_README.md
new file mode 100644
index 0000000000..d600a15818
--- /dev/null
+++ b/public/externalLibs/graphics/RUNES_README.md
@@ -0,0 +1,15 @@
+RUNES is a library for realising the picture language of
+section
+2.2.4 Example: A Picture Language
+of the textbook
+Structure and Interpretation
+of Computer Programs, JavaScript Adaptation (SICP JS).
+It provides primitive values, called runes, such
+as `heart` and `circle`, operations such as `beside`
+to construct more complex runes from simpler ones, and ways
+to display runes, in two-dimensional and three-dimensional
+space.
+
+On the right, you see all predeclared names of the RUNES library,
+in alphabetical
+order. Click on a name to see how it is defined and used.
diff --git a/public/externalLibs/graphics/gl-matrix.js b/public/externalLibs/graphics/gl-matrix.js
index 0146d11207..eafb095d0b 100644
--- a/public/externalLibs/graphics/gl-matrix.js
+++ b/public/externalLibs/graphics/gl-matrix.js
@@ -1,4 +1,4 @@
-/**
+/*
* @fileoverview gl-matrix - High performance matrix and vector operations
* @author Brandon Jones
* @author Colin MacKenzie IV
diff --git a/public/externalLibs/graphics/webGLcurve.js b/public/externalLibs/graphics/webGLcurve.js
index 87717e2e80..9ce3c77318 100644
--- a/public/externalLibs/graphics/webGLcurve.js
+++ b/public/externalLibs/graphics/webGLcurve.js
@@ -64,50 +64,143 @@ function generateCurve(scaleMode, drawMode, numPoints, func, isFullView) {
return new ShapeDrawn()
}
+/**
+ * returns a function that turns a given Curve into a Drawing,
+ * by sampling the Curve at num
sample points
+ * and connecting each pair with a line.
+ * When a program evaluates to a Drawing, the Source system
+ * displays it graphically, in a window, instead of textually.
+ * The parts between (0,0) and (1,1) of the resulting Drawing
+ * are shown in the window.
+ * @param {number} num - determines the number of points to be
+ * sampled. Including 0 and 1,
+ * there are num + 1
evenly spaced sample points.
+ * @return {function} function from Curves to Drawings.
+ */
function draw_connected(num) {
return function(func) {
return generateCurve('none', 'lines', num, func)
}
}
+/**
+ * returns a function that turns a given Curve into a Drawing,
+ * by sampling the Curve at num
sample points.
+ * The Drawing consists of isolated points, and does not connect them.
+ * When a program evaluates to a Drawing, the Source system
+ * displays it graphically, in a window, instead of textually.
+ * The parts between (0,0) and (1,1) of the resulting Drawing
+ * are shown in the window.
+ * @param {number} num - determines the number of points to be
+ * sampled. Including 0 and 1,
+ * there are num + 1
evenly spaced sample points.
+ * @return {function} function from Curves to Drawings.
+ */
function draw_points_on(num) {
- return function(func) {
- return generateCurve('none', 'points', num, func)
- }
+ return curve =>
+ generateCurve('none', 'points', num, curve)
}
+/**
+ * returns a function that turns a given Curve into a Drawing,
+ * by sampling the Curve at num
sample points.
+ * The Drawing consists of isolated points, and does not connect them.
+ * When a program evaluates to a Drawing, the Source system
+ * displays it graphically, in a window, instead of textually.
+ * The Drawing is squeezed such that all its parts are shown in the
+ * window.
+ * @param {number} num - determines the number of points to be
+ * sampled. Including 0 and 1,
+ * there are num + 1
evenly spaced sample points.
+ * @return {function} function from Curves to Drawings.
+ */
function draw_points_squeezed_to_window(num) {
return function(func) {
return generateCurve('fit', 'points', num, func)
}
}
+/**
+ * returns a function that turns a given Curve into a Drawing,
+ * by sampling the Curve at num
sample points
+ * and connecting each pair with a line.
+ * When a program evaluates to a Drawing, the Source system
+ * displays it graphically, in a window, instead of textually.
+ * The Drawing is squeezed such that all its parts are shown in the
+ * window.
+ * @param {number} num - determines the number of points to be
+ * sampled. Including 0 and 1,
+ * there are num + 1
evenly spaced sample points.
+ * @return {function} function from Curves to Drawings.
+ */
function draw_connected_squeezed_to_window(num) {
return function(func) {
return generateCurve('fit', 'lines', num, func)
}
}
+/**
+ * returns a function that turns a given Curve into a Drawing,
+ * by sampling the Curve at num
sample points
+ * and connecting each pair with a line.
+ * When a program evaluates to a Drawing, the Source system
+ * displays it graphically, in a window, instead of textually.
+ * The Drawing is stretched or shrunk
+ * to show the full curve and maximize its width and height.
+ * @param {number} num - determines the number of points to be
+ * sampled. Including 0 and 1,
+ * there are num + 1
evenly spaced sample points.
+ * @return {function} function from Curves to Drawings.
+ */
function draw_connected_full_view(num) {
return function(func) {
return generateCurve('stretch', 'lines', num, func, true)
}
}
+/**
+ * returns a function that turns a given Curve into a Drawing,
+ * by sampling the Curve at num
sample points
+ * and connecting each pair with a line.
+ * When a program evaluates to a Drawing, the Source system
+ * displays it graphically, in a window, instead of textually.
+ * The Drawing is scaled proportionally to show the full curve
+ * and maximize its size.
+ * @param {number} num - determines the number of points to be
+ * sampled. Including 0 and 1,
+ * there are num + 1
evenly spaced sample points.
+ * @return {function} function from Curves to Drawings.
+ */
function draw_connected_full_view_proportional(num) {
return function(func) {
return generateCurve('fit', 'lines', num, func, true)
}
}
+/**
+ * makes a Point with given x and y coordinates
+ * @param {number} x - x-coordinate of new point
+ * @param {number} y - y-coordinate of new point
+ * @returns {Point} with x and y as coordinates
+ */
function make_point(x, y) {
return [x, y]
}
+/**
+ * retrieves the x-coordinate a given Point
+ * @param {Point} p - given point
+ * @returns {number} x-coordinate of the Point
+ */
function x_of(pt) {
return pt[0]
}
+/**
+ * retrieves the y-coordinate a given Point
+ * @param {Point} p - given point
+ * @returns {number} y-coordinate of the Point
+ */
function y_of(pt) {
return pt[1]
}
diff --git a/public/externalLibs/graphics/webGLgraphics.js b/public/externalLibs/graphics/webGLgraphics.js
index 6cff619653..ff4b9b8541 100644
--- a/public/externalLibs/graphics/webGLgraphics.js
+++ b/public/externalLibs/graphics/webGLgraphics.js
@@ -267,7 +267,7 @@ function resetCanvas() {
}
}
-/**
+/*
* Gets the WebGL object (gl) ready for usage. Use this
* to reset the mode of rendering i.e to change from 2d to 3d runes.
*
diff --git a/public/externalLibs/graphics/webGLhi_graph.js b/public/externalLibs/graphics/webGLhi_graph.js
index 550903ff84..c9b65143d9 100644
--- a/public/externalLibs/graphics/webGLhi_graph.js
+++ b/public/externalLibs/graphics/webGLhi_graph.js
@@ -30,25 +30,65 @@ function square(x) {
// SOME CURVES
+/**
+ * this function is a curve: a function from a
+ * fraction t to a point. The points lie on the
+ * unit circle. They start at Point (1,0) when
+ * t is 0. When t is 0.25, they reach Point (0,1),
+ * when t is 0.5, they reach Point (-1, 0), etc.
+ *
+ * @param {number} t - fraction between 0 and 1
+ * @returns {Point} Point in the line at t
+ */
function unit_circle(t) {
- return make_point(Math.sin(2 * Math.PI * t), Math.cos(2 * Math.PI * t))
+ return make_point(Math.sin(2 * Math.PI * t),
+ Math.cos(2 * Math.PI * t))
}
+/**
+ * this function is a curve: a function from a
+ * fraction t to a point. The x-coordinate at
+ * franction t is t, and the y-coordinate is 0.
+ *
+ * @param {number} t - fraction between 0 and 1
+ * @returns {Point} Point in the line at t
+ */
function unit_line(t) {
return make_point(t, 0)
}
+/**
+ * this function is a Curve generator: it takes
+ * a number and returns a horizontal curve. The number
+ * is a y-coordinate, and the Curve generates
+ * only points with the given y-coordinate.
+ *
+ * @param {number} t - fraction between 0 and 1
+ * @returns {curve} horizontal Curve
+ */
function unit_line_at(y) {
return function(t) {
return make_point(t, y)
}
}
+// alternative_unit_circle: undocumented feature
+
function alternative_unit_circle(t) {
- return make_point(Math.sin(2 * Math.PI * square(t)), Math.cos(2 * Math.PI * square(t)))
+ return make_point(Math.sin(2 * Math.PI * square(t)),
+ Math.cos(2 * Math.PI * square(t)))
}
-// made available for Mission 6
+/**
+ * this function is a curve: a function from a
+ * fraction t to a point. The points lie on the
+ * right half of the unit circle. They start at Point (0,1) when
+ * t is 0. When t is 0.5, they reach Point (1,0),
+ * when t is 1, they reach Point (0, -1).
+ *
+ * @param {number} t - fraction between 0 and 1
+ * @returns {Point} Point in the line at t
+ */
function arc(t) {
return make_point(Math.sin(Math.PI * t), Math.cos(Math.PI * t))
}
@@ -57,34 +97,72 @@ function arc(t) {
// SOME CURVE-TRANSFORMS
+/**
+ * this function is a unary Curve operator: a function from a
+ * Curve to a Curve. The points of the result Curve are
+ * the same points as the points of the original Curve, but
+ * in reverse: The result Curve applied to 0 is the original Curve
+ * applied to 1 and vice versa.
+ *
+ * @param {Curve} original - original Curve
+ * @returns {Curve} result Curve
+ */
function invert(curve) {
return function(t) {
return curve(1 - t)
}
}
+/**
+ * this function is a unary Curve operator: a function from a
+ * Curve to a Curve. The result Curve is the original Curve
+ * rotated by 90 degrees in counterclockwise direction around
+ * the origin.
+ * @param {Curve} original - original Curve
+ * @returns {Curve} result Curve
+ */
function rotate_pi_over_2(curve) {
- return function(t) {
- var ct = curve(t)
- return make_point(-y_of(ct), x_of(ct))
- }
+ return t => {
+ var ct = curve(t)
+ return make_point(-y_of(ct), x_of(ct))
+ }
}
// CONSTRUCTORS OF CURVE-TRANSFORMS
// TRANSLATE is of type (JS-Num, JS-Num --> Curve-Transform)
+/**
+ * this function returns a unary Curve operator:
+ * It takes an x-value x0 and a y-value y0 as arguments and returns a
+ * unary Curve operator that
+ * takes a Curve as argument and returns
+ * a new Curve, by translating the original by x0 in x-direction
+ * and by y0 in y-direction.
+ *
+ * @param {number} x0 - x-value
+ * @param {number} y0 - y-value
+ * @returns {function} unary Curve operator
+ */
function translate(x0, y0) {
- return function(curve) {
- return function(t) {
- var ct = curve(t)
- return make_point(x0 + x_of(ct), y0 + y_of(ct))
- }
- }
+ return curve =>
+ (t) => {
+ var ct = curve(t)
+ return make_point(x0 + x_of(ct), y0 + y_of(ct))
+ }
}
// ROTATE-AROUND-ORIGIN is of type (JS-Num --> Curve-Transform)
-
+/**
+ * this function
+ * takes an angle theta as parameter and returns a unary Curve operator:
+ * a function that takes
+ * a Curve a argument and returns
+ * a new Curve, which is the original Curve rotated by the given angle
+ * around the origin, in counter-clockwise direction.
+ * @param {Curve} Curve - given Curve
+ * @returns {Curve} result Curve
+ */
function rotate_around_origin(theta) {
var cth = Math.cos(theta)
var sth = Math.sin(theta)
@@ -109,25 +187,46 @@ function deriv_t(n) {
}
}
+/**
+ * this function returns a unary Curve operator:
+ * It takes scaling factors a
and b
as arguments
+ * and returns a unary Curve operator that
+ * scales a given Curve by a
in x-direction and by b
+ * in y-direction.
+ *
+ * @param {number} a - scaling factor in x-direction
+ * @param {number} b - scaling factor in y-direction
+ * @returns {function} unary Curve operator
+ */
function scale_x_y(a, b) {
- return function(curve) {
- return function(t) {
- var ct = curve(t)
- return make_point(a * x_of(ct), b * y_of(ct))
- }
- }
+ return curve =>
+ t => {
+ var ct = curve(t)
+ return make_point(a * x_of(ct), b * y_of(ct))
+ }
}
+/**
+ * this function returns a unary Curve operator:
+ * It takes a scaling factor s argument and returns a
+ * unary Curve operator that
+ * scales a given Curve by s in x and y direction.
+ *
+ * @param {number} s - scaling factor
+ * @returns {function} unary Curve operator
+ */
function scale(s) {
return scale_x_y(s, s)
}
// SQUEEZE-RECTANGULAR-PORTION translates and scales a curve
-// so the portion of the curve in the rectangle
+// so the portion of the Curve in the rectangle
// with corners xlo xhi ylo yhi will appear in a display window
// which has x, y coordinates from 0 to 1.
// It is of type (JS-Num, JS-Num, JS-Num, JS-Num --> Curve-Transform).
+// squeeze_rectangular_portion: undocumented feature
+
function squeeze_rectangular_portion(xlo, xhi, ylo, yhi) {
var width = xhi - xlo
var height = yhi - ylo
@@ -138,11 +237,13 @@ function squeeze_rectangular_portion(xlo, xhi, ylo, yhi) {
}
}
-// SQUEEZE-FULL-VIEW translates and scales a curve such that
+// SQUEEZE-FULL-VIEW translates and scales a Curve such that
// the ends are fully visible.
// It is very similar to the squeeze-rectangular-portion procedure
// only that that procedure does not allow the edges to be easily seen
+// squeeze_full_view: undocumented feature
+
function squeeze_full_view(xlo, xhi, ylo, yhi) {
var width = xhi - xlo
var height = yhi - ylo
@@ -156,7 +257,7 @@ function squeeze_full_view(xlo, xhi, ylo, yhi) {
}
}
-// FULL-VIEW
+// full_view_proportional: undocumented feature
function full_view_proportional(xlo, xhi, ylo, yhi) {
var width = xhi - xlo
@@ -175,12 +276,27 @@ function full_view_proportional(xlo, xhi, ylo, yhi) {
}
// PUT-IN-STANDARD-POSITION is a Curve-Transform.
-// A curve is in "standard position" if it starts at (0,0) ends at (1,0).
-// A curve is PUT-IN-STANDARD-POSITION by rigidly translating it so its
-// start point is at the origin, then rotating it about the origin to put
+// A Curve is in "standard position" if it starts at (0,0) ends at (1,0).
+// A Curve is PUT-IN-STANDARD-POSITION by rigidly translating it so its
+// start Point is at the origin, then rotating it about the origin to put
// its endpoint on the x axis, then scaling it to put the endpoint at (1,0).
// Behavior is unspecified on closed curves (with start-point = end-point).
+/**
+ * this function is a unary Curve operator: It
+ * takes a Curve as argument and returns
+ * a new Curve, as follows.
+ * A Curve is in standard position if it starts at (0,0) ends at (1,0).
+ * This function puts the given Curve in standard position by
+ * rigidly translating it so its
+ * start Point is at the origin (0,0), then rotating it about the origin to put
+ * its endpoint on the x axis, then scaling it to put the endpoint at (1,0).
+ * Behavior is unspecified on closed Curves where start-point equal end-point.
+ *
+ * @param {Curve} curve - given Curve
+ * @returns {Curve} result Curve
+ */
+
function put_in_standard_position(curve) {
var start_point = curve(0)
var curve_started_at_origin = translate(-x_of(start_point), -y_of(start_point))(curve)
@@ -193,8 +309,22 @@ function put_in_standard_position(curve) {
// Binary-transform = (Curve,Curve --> Curve)
-// CONNECT-RIGIDLY makes a curve consisting of curve1 followed by curve2.
-
+// CONNECT-RIGIDLY makes a Curve consisting of curve1 followed by curve2.
+
+/**
+ * this function is a binary Curve operator: It
+ * takes two Curves as arguments and returns
+ * a new Curve. The two Curves are combined
+ * by using the full first Curve for the first portion
+ * of the result and by using the full second Curve for the
+ * second portion of the result.
+ * The second Curve is not changed, and therefore
+ * there might be a big jump in the middle of the
+ * result Curve.
+ * @param {Curve} curve1 - first Curve
+ * @param {Curve} curve2 - second Curve
+ * @returns {Curve} result Curve
+ */
function connect_rigidly(curve1, curve2) {
return function(t) {
if (t < 1 / 2) {
@@ -205,10 +335,35 @@ function connect_rigidly(curve1, curve2) {
}
}
-// CONNECT-ENDS makes a curve consisting of curve1 followed by
+// CONNECT-ENDS makes a Curve consisting of curve1 followed by
// a copy of curve2 starting at the end of curve1
-// < redacted >
+/**
+ * this function is a binary Curve operator: It
+ * takes two Curves as arguments and returns
+ * a new Curve. The two Curves are combined
+ * by using the full first Curve for the first portion
+ * of the result and by using the full second Curve for the second
+ * portion of the result.
+ * The second Curve is translated such that its point
+ * at fraction 0 is the same as the Point of the first
+ * Curve at fraction 1.
+ *
+ * @param {Curve} curve1 - first Curve
+ * @param {Curve} curve2 - second Curve
+ * @returns {Curve} result Curve
+ */
+function connect_ends(curve1, curve2) {
+ const start_point_of_curve2 = curve2(0);
+ const end_point_of_curve1 = curve1(1);
+ return connect_rigidly(curve1,
+ (translate(x_of(end_point_of_curve1) -
+ x_of(start_point_of_curve2),
+ y_of(end_point_of_curve1) -
+ y_of(start_point_of_curve2)))
+ (curve2));
+}
+
// function connect_ends(curve1, curve2) {...}
@@ -216,6 +371,15 @@ function connect_rigidly(curve1, curve2) {
// GOSPERIZE is a Curve-Transform
+
+/**
+ * this function is a unary Curve operator: It
+ * takes a Curve as argument and returns
+ * a new Curve, according to the Gosper operation.
+ *
+ * @param {Curve} curve - given Curve
+ * @returns {Curve} result Curve
+ */
function gosperize(curve) {
var scaled_curve = scale(Math.sqrt(2) / 2)(curve)
return connect_rigidly(
@@ -226,16 +390,40 @@ function gosperize(curve) {
// GOSPER-CURVE is of type (JS-Num --> Curve)
+/**
+ * returns a gosper Curve, that results from
+ * apply gosperize as many times to the unit line
+ * as given by the parameter level
of
+ * the function gosper_curve
+ *
+ * @param {number} level - number of repeated applications
+ * @returns {curve_operator}
+ */
function gosper_curve(level) {
return repeated(gosperize, level)(unit_line)
}
// DRAWING GOSPER CURVES
+/**
+ * shows a gosper Curve of given level, by drawing it
+ * with suitable parameters
+ *
+ * @param {number} level - number of repeated applications of gosperize to the unit line
+ * @returns {curve_operator}
+ */
function show_connected_gosper(level) {
return draw_connected(200)(squeeze_rectangular_portion(-0.5, 1.5, -0.5, 1.5)(gosper_curve(level)))
}
+/**
+ * returns a Curve that results from gosperizing the unit line
+ * as often as given by the level parameter, each time
+ * applying an angle given by the given angle-producing function
+ * @param {number} level - number of repeated applications of gosperize to the curve
+ * @param {function} angle_at - function that determines the angle at each level
+ * @returns {curve}
+ */
function param_gosper(level, angle_at) {
if (level === 0) {
return unit_line
@@ -244,6 +432,16 @@ function param_gosper(level, angle_at) {
}
}
+/**
+ * this function returns a unary Curve operator:
+ * It takes an angle theta and returns a Curve operator:
+ * A function that takes a Curve as argument and returns
+ * a new Curve, according to the Gosper operation, modified
+ * with the given angle
+ *
+ * @param {Curve} curve - given Curve
+ * @returns {Curve} result Curve
+ */
function param_gosperize(theta) {
return function(curve) {
var scale_factor = 1 / Math.cos(theta) / 2
diff --git a/public/externalLibs/graphics/webGLrune.js b/public/externalLibs/graphics/webGLrune.js
index 95716248f6..abae1e5c0d 100644
--- a/public/externalLibs/graphics/webGLrune.js
+++ b/public/externalLibs/graphics/webGLrune.js
@@ -4,48 +4,48 @@ var viewport_size = 512 // This is the height of the viewport
var maxArcLength = 20
/*-----------------------Some class definitions----------------------*/
-function PrimaryShape(first, count) {
- this.isPrimary = true // this is a primary shape
+function PrimaryRune(first, count) {
+ this.isPrimary = true // this is a primary rune
this.first = first // the first index in the index buffer
- // that belongs to this shape
- this.count = count // number of indices to draw the shape
+ // that belongs to this rune
+ this.count = count // number of indices to draw the rune
}
-function Shape() {
+function Rune() {
this.isPrimary = false
this.transMatrix = mat4.create()
- this.shapes = []
+ this.runes = []
this.color = undefined
}
-// set the transformation matrix related to the shape
-Shape.prototype.setM = function(matrix) {
+// set the transformation matrix related to the rune
+Rune.prototype.setM = function(matrix) {
this.transMatrix = matrix
}
-// get the transformation matrix related to the shape
-Shape.prototype.getM = function() {
+// get the transformation matrix related to the rune
+Rune.prototype.getM = function() {
return this.transMatrix
}
-// get the sub-shapes (array) of the shape
-Shape.prototype.getS = function() {
- return this.shapes
+// get the sub-runes (array) of the rune
+Rune.prototype.getS = function() {
+ return this.runes
}
-Shape.prototype.setS = function(shapes) {
- this.shapes = shapes
+Rune.prototype.setS = function(runes) {
+ this.runes = runes
}
-Shape.prototype.addS = function(shape) {
- this.shapes.push(shape)
+Rune.prototype.addS = function(rune) {
+ this.runes.push(rune)
}
-Shape.prototype.getColor = function() {
+Rune.prototype.getColor = function() {
return this.color
}
-Shape.prototype.setColor = function(color) {
+Rune.prototype.setColor = function(color) {
this.color = color
}
@@ -180,7 +180,7 @@ function makeCircle() {
}
indices.push(centerVerInd, firstVer, firstVer + numPoints - 1)
var count = 3 * numPoints
- return new PrimaryShape(firstInd, count)
+ return new PrimaryRune(firstInd, count)
}
function makeHeart() {
@@ -220,7 +220,7 @@ function makeHeart() {
indices.push(bottomMidInd, i, i + 1)
}
var count = 3 * 2 * numPoints
- return new PrimaryShape(firstInd, count)
+ return new PrimaryRune(firstInd, count)
}
function makePentagram() {
@@ -243,7 +243,7 @@ function makePentagram() {
indices.push(0, firstVer + i, firstVer + (i + 2) % 5)
}
- return new PrimaryShape(firstInd, 15)
+ return new PrimaryRune(firstInd, 15)
}
function makeRibbon() {
@@ -270,62 +270,62 @@ function makeRibbon() {
indices.push(i, i + 1, i + 2)
}
- return new PrimaryShape(firstInd, 3 * totalPoints - 6)
+ return new PrimaryRune(firstInd, 3 * totalPoints - 6)
}
/**
- * primitive rune in the shape of a full square
+ * primitive Rune in the rune of a full square
**/
-var square = new PrimaryShape(0, 6)
+var square = new PrimaryRune(0, 6)
/**
- * primitive rune in the shape of a blank square
+ * primitive Rune in the rune of a blank square
**/
-var blank = new PrimaryShape(0, 0)
+var blank = new PrimaryRune(0, 0)
/**
- * primitive rune in the shape of a
+ * primitive Rune in the rune of a
* smallsquare inside a large square,
* each diagonally split into a
* black and white half
**/
-var rcross = new PrimaryShape(6, 15)
+var rcross = new PrimaryRune(6, 15)
/**
- * primitive rune in the shape of a sail
+ * primitive Rune in the rune of a sail
**/
-var sail = new PrimaryShape(21, 3)
+var sail = new PrimaryRune(21, 3)
/**
- * primitive rune with black triangle,
+ * primitive Rune with black triangle,
* filling upper right corner
**/
-var corner = new PrimaryShape(24, 3)
+var corner = new PrimaryRune(24, 3)
/**
- * primitive rune in the shape of two overlapping
+ * primitive Rune in the rune of two overlapping
* triangles, residing in the upper half
* of
**/
-var nova = new PrimaryShape(27, 6)
+var nova = new PrimaryRune(27, 6)
/**
- * primitive rune in the shape of a circle
+ * primitive Rune in the rune of a circle
**/
var circle = makeCircle()
/**
- * primitive rune in the shape of a heart
+ * primitive Rune in the rune of a heart
**/
var heart = makeHeart()
/**
- * primitive rune in the shape of a pentagram
+ * primitive Rune in the rune of a pentagram
**/
var pentagram = makePentagram()
/**
- * primitive rune in the shape of a ribbon
+ * primitive Rune in the rune of a ribbon
* winding outwards in an anticlockwise spiral
**/
var ribbon = makeRibbon()
@@ -335,10 +335,10 @@ vertices = new Float32Array(vertices)
indices = new Uint16Array(indices)
/*-----------------------Drawing functions----------------------*/
-function generateFlattenedShapeList(shape) {
+function generateFlattenedRuneList(rune) {
var matStack = []
var matrix = mat4.create()
- var shape_list = {}
+ var rune_list = {}
function pushMat() {
matStack.push(mat4.clone(matrix))
}
@@ -349,30 +349,30 @@ function generateFlattenedShapeList(shape) {
matrix = matStack.pop()
}
}
- function helper(shape, color) {
- if (shape.isPrimary) {
- if (shape.count === 0) {
+ function helper(rune, color) {
+ if (rune.isPrimary) {
+ if (rune.count === 0) {
// this is blank, do nothing
return
}
- if (!shape_list[shape.first]) {
- shape_list[shape.first] = {
- shape: shape,
+ if (!rune_list[rune.first]) {
+ rune_list[rune.first] = {
+ rune: rune,
matrices: [],
colors: []
}
}
- shape_list[shape.first].matrices.push(matrix)
- shape_list[shape.first].colors.push(color || [0, 0, 0, 1])
+ rune_list[rune.first].matrices.push(matrix)
+ rune_list[rune.first].colors.push(color || [0, 0, 0, 1])
} else {
- if (color === undefined && shape.getColor() !== undefined) {
- color = shape.getColor()
+ if (color === undefined && rune.getColor() !== undefined) {
+ color = rune.getColor()
}
pushMat()
- mat4.multiply(matrix, matrix, shape.getM())
- var childShapes = shape.getS()
- for (var i = 0; i < childShapes.length; i++) {
- helper(childShapes[i], color)
+ mat4.multiply(matrix, matrix, rune.getM())
+ var childRunes = rune.getS()
+ for (var i = 0; i < childRunes.length; i++) {
+ helper(childRunes[i], color)
}
popMat()
}
@@ -385,82 +385,83 @@ function generateFlattenedShapeList(shape) {
}
return instanceArray
}
- helper(shape)
- var flattened_shape_list = []
+ helper(rune)
+ var flattened_rune_list = []
// draw a white square background first
- flattened_shape_list.push({
- shape: square,
+ flattened_rune_list.push({
+ rune: square,
instanceArray: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -1, 1, 1, 1, 1, 1])
})
- for (var key in shape_list) {
- if (shape_list.hasOwnProperty(key)) {
- var shape = shape_list[key].shape
- var instanceArray = flatten(shape_list[key].matrices, shape_list[key].colors)
- flattened_shape_list.push({ shape: shape, instanceArray: instanceArray })
+ for (var key in rune_list) {
+ if (rune_list.hasOwnProperty(key)) {
+ var rune = rune_list[key].rune
+ var instanceArray = flatten(rune_list[key].matrices, rune_list[key].colors)
+ flattened_rune_list.push({ rune: rune, instanceArray: instanceArray })
}
}
- return flattened_shape_list
+ return flattened_rune_list
}
-function drawWithWebGL(flattened_shape_list, drawFunction) {
- for (var i = 0; i < flattened_shape_list.length; i++) {
- var shape = flattened_shape_list[i].shape
- var instanceArray = flattened_shape_list[i].instanceArray
- drawFunction(shape.first, shape.count, instanceArray)
+function drawWithWebGL(flattened_rune_list, drawFunction) {
+ for (var i = 0; i < flattened_rune_list.length; i++) {
+ var rune = flattened_rune_list[i].rune
+ var instanceArray = flattened_rune_list[i].instanceArray
+ drawFunction(rune.first, rune.count, instanceArray)
}
}
/**
- * stores a given Rune into the REPL picture frame
- * @param {Rune} shape - given Rune
+ * turns a given Rune into a two-dimensional Picture
+ * @param {Rune} rune - given Rune
* @return {Picture}
- * the picture frame. If the result of evaluating a program is the
- * REPL picture frame, the REPL displays it graphically, instead of
- * using text.
+ * If the result of evaluating a program is a Picture,
+ * the REPL displays it graphically, instead of textually.
*/
-function show(shape) {
+function show(rune) {
clear_viewport()
- var flattened_shape_list = generateFlattenedShapeList(shape)
- drawWithWebGL(flattened_shape_list, drawRune)
+ var flattened_rune_list = generateFlattenedRuneList(rune)
+ drawWithWebGL(flattened_rune_list, drawRune)
return new ShapeDrawn()
}
/**
* turns a given Rune into an Anaglyph
- * @param {Rune} shape - given Rune
+ * @param {Rune} rune - given Rune
* @return {Picture}
* If the result of evaluating a program is an Anaglyph,
* the REPL displays it graphically, using anaglyph
- * technology, instead of using text. Use your 3D-glasses
+ * technology, instead of textually. Use your 3D-glasses
* to view the Anaglyph.
*/
-function anaglyph(shape) {
+function anaglyph(rune) {
clear_viewport()
clearAnaglyphFramebuffer()
- var flattened_shape_list = generateFlattenedShapeList(shape)
- drawWithWebGL(flattened_shape_list, drawAnaglyph)
+ var flattened_rune_list = generateFlattenedRuneList(rune)
+ drawWithWebGL(flattened_rune_list, drawAnaglyph)
return new ShapeDrawn()
}
var hollusionTimeout
-/**
+/* // to view documentation, put two * in this line
+ * // currently, this function is not documented;
+ * // animation not working
* turns a given Rune into Hollusion
- * @param {Rune} shape - given Rune
+ * @param {Rune} rune - given Rune
* @return {Picture}
* If the result of evaluating a program is a Hollusion,
* the REPL displays it graphically, using hollusion
- * technology, instead of using text.
+ * technology, instead of textually.
*/
-function hollusion(shape, num) {
+function hollusion(rune, num) {
clear_viewport()
var num = num > 3 ? num : 3
- var flattened_shape_list = generateFlattenedShapeList(shape)
+ var flattened_rune_list = generateFlattenedRuneList(rune)
var frame_list = []
for (var j = 0; j < num; j++) {
var frame = open_pixmap('frame' + j, viewport_size, viewport_size, false)
- for (var i = 0; i < flattened_shape_list.length; i++) {
- var shape = flattened_shape_list[i].shape
- var instanceArray = flattened_shape_list[i].instanceArray
+ for (var i = 0; i < flattened_rune_list.length; i++) {
+ var rune = flattened_rune_list[i].rune
+ var instanceArray = flattened_rune_list[i].instanceArray
var cameraMatrix = mat4.create()
mat4.lookAt(
cameraMatrix,
@@ -468,7 +469,7 @@ function hollusion(shape, num) {
vec3.fromValues(0, 0, -0.4),
vec3.fromValues(0, 1, 0)
)
- draw3D(shape.first, shape.count, instanceArray, cameraMatrix, [1, 1, 1, 1], null, true)
+ draw3D(rune.first, rune.count, instanceArray, cameraMatrix, [1, 1, 1, 1], null, true)
}
gl.finish()
copy_viewport(gl.canvas, frame)
@@ -494,42 +495,42 @@ function clearHollusion() {
}
/*-----------------------Transformation functions----------------------*/
-function scale_independent(ratio_x, ratio_y, shape) {
+function scale_independent(ratio_x, ratio_y, rune) {
var scaleVec = vec3.fromValues(ratio_x, ratio_y, 1)
var scaleMat = mat4.create()
mat4.scale(scaleMat, scaleMat, scaleVec)
- var wrapper = new Shape()
- wrapper.addS(shape)
+ var wrapper = new Rune()
+ wrapper.addS(rune)
wrapper.setM(scaleMat)
return wrapper
}
-function scale(ratio, shape) {
- return scale_independent(ratio, ratio, shape)
+function scale(ratio, rune) {
+ return scale_independent(ratio, ratio, rune)
}
-function translate(x, y, shape) {
+function translate(x, y, rune) {
var translateVec = vec3.fromValues(x, -y, 0)
var translateMat = mat4.create()
mat4.translate(translateMat, translateMat, translateVec)
- var wrapper = new Shape()
- wrapper.addS(shape)
+ var wrapper = new Rune()
+ wrapper.addS(rune)
wrapper.setM(translateMat)
return wrapper
}
/**
- * rotates a given rune by a given angle,
+ * rotates a given Rune by a given angle,
* given in radians, in anti-clockwise direction
* @param {number} rad - fraction between 0 and 1
- * @param {Rune} shape - given Rune
- * @return {Rune} - rotated Rune
+ * @param {Rune} rune - given Rune
+ * @return {Rune} rotated Rune
*/
-function rotate(rad, shape) {
+function rotate(rad, rune) {
var rotateMat = mat4.create()
mat4.rotateZ(rotateMat, rotateMat, rad)
- var wrapper = new Shape()
- wrapper.addS(shape)
+ var wrapper = new Rune()
+ wrapper.addS(rune)
wrapper.setM(rotateMat)
return wrapper
}
@@ -540,15 +541,15 @@ function rotate(rad, shape) {
* such that the first one occupies frac
* portion of the height of the result and
* the second the rest
- * @param {Rune} shape1 - given Rune
- * @param {Rune} shape2 - given Rune
* @param {number} frac - fraction between 0 and 1
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune1 - given Rune
+ * @param {Rune} rune2 - given Rune
+ * @return {Rune} resulting Rune
*/
-function stack_frac(frac, shape1, shape2) {
- var upper = translate(0, -(1 - frac), scale_independent(1, frac, shape1))
- var lower = translate(0, frac, scale_independent(1, 1 - frac, shape2))
- var combined = new Shape()
+function stack_frac(frac, rune1, rune2) {
+ var upper = translate(0, -(1 - frac), scale_independent(1, frac, rune1))
+ var lower = translate(0, frac, scale_independent(1, 1 - frac, rune2))
+ var combined = new Rune()
combined.setS([upper, lower])
return combined
}
@@ -558,26 +559,26 @@ function stack_frac(frac, shape1, shape2) {
* placing the first on top of the second, each
* occupying equal parts of the height of the
* result
- * @param {Rune} shape1 - given Rune
- * @param {Rune} shape2 - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune1 - given Rune
+ * @param {Rune} rune2 - given Rune
+ * @return {Rune} resulting Rune
*/
-function stack(shape1, shape2) {
- return stack_frac(1 / 2, shape1, shape2)
+function stack(rune1, rune2) {
+ return stack_frac(1 / 2, rune1, rune2)
}
/**
* makes a new Rune from a given Rune
* by vertically stacking n copies of it
* @param {number} n - positive integer
- * @param {Rune} shape - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune - given Rune
+ * @return {Rune} resulting Rune
*/
-function stackn(n, shape) {
+function stackn(n, rune) {
if (n === 1) {
- return shape
+ return rune
} else {
- return stack_frac(1 / n, shape, stackn(n - 1, shape))
+ return stack_frac(1 / n, rune, stackn(n - 1, rune))
}
}
@@ -585,32 +586,32 @@ function stackn(n, shape) {
* makes a new Rune from a given Rune
* by turning it a quarter-turn in
* clockwise direction
- * @param {Rune} shape - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune - given Rune
+ * @return {Rune} resulting Rune
*/
-function quarter_turn_right(shape) {
- return rotate(-Math.PI / 2, shape)
+function quarter_turn_right(rune) {
+ return rotate(-Math.PI / 2, rune)
}
/**
* makes a new Rune from a given Rune
* by turning it a quarter-turn in
* anti-clockwise direction
- * @param {Rune} shape - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune - given Rune
+ * @return {Rune} resulting Rune
*/
-function quarter_turn_left(shape) {
- return rotate(Math.PI / 2, shape)
+function quarter_turn_left(rune) {
+ return rotate(Math.PI / 2, rune)
}
/**
* makes a new Rune from a given Rune
* by turning it upside-down
- * @param {Rune} shape - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune - given Rune
+ * @return {Rune} resulting Rune
*/
-function turn_upside_down(shape) {
- return rotate(Math.PI, shape)
+function turn_upside_down(rune) {
+ return rotate(Math.PI, rune)
}
/**
@@ -619,15 +620,15 @@ function turn_upside_down(shape) {
* such that the first one occupies frac
* portion of the width of the result and
* the second the rest
- * @param {Rune} shape1 - given Rune
- * @param {Rune} shape2 - given Rune
* @param {number} frac - fraction between 0 and 1
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune1 - given Rune
+ * @param {Rune} rune2 - given Rune
+ * @return {Rune} resulting Rune
*/
-function beside_frac(frac, shape1, shape2) {
- var left = translate(-(1 - frac), 0, scale_independent(frac, 1, shape1))
- var right = translate(frac, 0, scale_independent(1 - frac, 1, shape2))
- var combined = new Shape()
+function beside_frac(frac, rune1, rune2) {
+ var left = translate(-(1 - frac), 0, scale_independent(frac, 1, rune1))
+ var right = translate(frac, 0, scale_independent(1 - frac, 1, rune2))
+ var combined = new Rune()
combined.setS([left, right])
return combined
}
@@ -637,63 +638,63 @@ function beside_frac(frac, shape1, shape2) {
* placing the first on the left of the second,
* both occupying equal portions of the width
* of the result
- * @param {Rune} shape1 - given Rune
- * @param {Rune} shape2 - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune1 - given Rune
+ * @param {Rune} rune2 - given Rune
+ * @return {Rune} resulting Rune
*/
-function beside(shape1, shape2) {
- return beside_frac(1 / 2, shape1, shape2)
+function beside(rune1, rune2) {
+ return beside_frac(1 / 2, rune1, rune2)
}
/**
* makes a new Rune from a given Rune by
* flipping it around a horizontal axis,
* turning it upside down
- * @param {Rune} shape - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune - given Rune
+ * @return {Rune} resulting Rune
*/
-function flip_vert(shape) {
- return scale_independent(1, -1, shape)
+function flip_vert(rune) {
+ return scale_independent(1, -1, rune)
}
/**
* makes a new Rune from a given Rune by
* flipping it around a vertical axis,
* creating a mirror image
- * @param {Rune} shape - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune - given Rune
+ * @return {Rune} resulting Rune
*/
-function flip_horiz(shape) {
- return scale_independent(-1, 1, shape)
+function flip_horiz(rune) {
+ return scale_independent(-1, 1, rune)
}
/**
* makes a new Rune from a given Rune by
* arranging into a square for copies of the
* given Rune in different orientations
- * @param {Rune} shape - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune - given Rune
+ * @return {Rune} resulting Rune
*/
-function make_cross(shape) {
+function make_cross(rune) {
return stack(
- beside(quarter_turn_right(shape), rotate(Math.PI, shape)),
- beside(shape, rotate(Math.PI / 2, shape))
+ beside(quarter_turn_right(rune), rotate(Math.PI, rune)),
+ beside(rune, rotate(Math.PI / 2, rune))
)
}
/**
- * applies a given function n times to an argument
+ * applies a given function n times to an initial value
* @param {number} n - a non-negative integer
* @param {function} f - unary function from t to t
- * @param {t} shape - argument
+ * @param {t} initial - argument
* @return {t} - result of n times application of
- * f to shape: f(f(...f(f(shape))...))
+ * f to rune: f(f(...f(f(rune))...))
*/
-function repeat_pattern(n, pattern, shape) {
+function repeat_pattern(n, pattern, initial) {
if (n === 0) {
- return shape
+ return initial
} else {
- return pattern(repeat_pattern(n - 1, pattern, shape))
+ return pattern(repeat_pattern(n - 1, pattern, initial))
}
}
@@ -709,45 +710,45 @@ function hexToColor(hex) {
}
/**
- * adds color to shape by specifying
+ * adds color to rune by specifying
* the red, green, blue (RGB) value.
* Opacity is kept at default value of 1. (Full opacity)
- * @param {Rune} shape - the shape to add color to
+ * @param {Rune} rune - the rune to add color to
* @param {number} r - red value (0-255)
* @param {number} g - green value (0-255)
* @param {number} b - blue value (0-255)
*/
-function color(shape, r, g, b) {
- var wrapper = new Shape()
- wrapper.addS(shape)
+function color(rune, r, g, b) {
+ var wrapper = new Rune()
+ wrapper.addS(rune)
var color = [r, g, b, 1]
wrapper.setColor(color)
return wrapper
}
-function addColorFromHex(shape, hex) {
- var wrapper = new Shape()
- wrapper.addS(shape)
+function addColorFromHex(rune, hex) {
+ var wrapper = new Rune()
+ wrapper.addS(rune)
wrapper.setColor(hexToColor(hex))
return wrapper
}
/**
- * Gives random color to the given shape.
+ * Gives random color to the given rune.
* The color is chosen randomly from the nine given
* colors below, where black and white are excluded.
- * @param {Rune} shape - the shape to color
+ * @param {Rune} rune - the rune to color
*/
-function random_color(shape) {
- var wrapper = new Shape()
- wrapper.addS(shape)
+function random_color(rune) {
+ var wrapper = new Rune()
+ wrapper.addS(rune)
var randomColor = hexToColor(colorPalette[Math.floor(Math.random() * colorPalette.length)])
wrapper.setColor(randomColor)
return wrapper
}
// black and white not included because they are boring colors
-// colorPalette is used in generateFlattenedShapeList to generate a random color
+// colorPalette is used in generateFlattenedRuneList to generate a random color
var colorPalette = [
'#F44336',
'#E91E63',
@@ -761,91 +762,91 @@ var colorPalette = [
]
/**
- * colors the give given shape red.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune red.
+ * @param {Rune} rune - the rune to color
*/
-function red(shape) {
- return addColorFromHex(shape, '#F44336')
+function red(rune) {
+ return addColorFromHex(rune, '#F44336')
}
/**
- * colors the give given shape pink.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune pink.
+ * @param {Rune} rune - the rune to color
*/
-function pink(shape) {
- return addColorFromHex(shape, '#E91E63')
+function pink(rune) {
+ return addColorFromHex(rune, '#E91E63')
}
/**
- * colors the give given shape purple.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune purple.
+ * @param {Rune} rune - the rune to color
*/
-function purple(shape) {
- return addColorFromHex(shape, '#AA00FF')
+function purple(rune) {
+ return addColorFromHex(rune, '#AA00FF')
}
/**
- * colors the give given shape indigo.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune indigo.
+ * @param {Rune} rune - the rune to color
*/
-function indigo(shape) {
- return addColorFromHex(shape, '#3F51B5')
+function indigo(rune) {
+ return addColorFromHex(rune, '#3F51B5')
}
/**
- * colors the give given shape blue.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune blue.
+ * @param {Rune} rune - the rune to color
*/
-function blue(shape) {
- return addColorFromHex(shape, '#2196F3')
+function blue(rune) {
+ return addColorFromHex(rune, '#2196F3')
}
/**
- * colors the give given shape green.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune green.
+ * @param {Rune} rune - the rune to color
*/
-function green(shape) {
- return addColorFromHex(shape, '#4CAF50')
+function green(rune) {
+ return addColorFromHex(rune, '#4CAF50')
}
/**
- * colors the give given shape yellow.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune yellow.
+ * @param {Rune} rune - the rune to color
*/
-function yellow(shape) {
- return addColorFromHex(shape, '#FFEB3B')
+function yellow(rune) {
+ return addColorFromHex(rune, '#FFEB3B')
}
/**
- * colors the give given shape orange.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune orange.
+ * @param {Rune} rune - the rune to color
*/
-function orange(shape) {
- return addColorFromHex(shape, '#FF9800')
+function orange(rune) {
+ return addColorFromHex(rune, '#FF9800')
}
/**
- * colors the give given shape brown.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune brown.
+ * @param {Rune} rune - the rune to color
*/
-function brown(shape) {
- return addColorFromHex(shape, '#795548')
+function brown(rune) {
+ return addColorFromHex(rune, '#795548')
}
/**
- * colors the give given shape black.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune black.
+ * @param {Rune} rune - the rune to color
*/
-function black(shape) {
- return addColorFromHex(shape, '#000000')
+function black(rune) {
+ return addColorFromHex(rune, '#000000')
}
/**
- * colors the give given shape white.
- * @param {Rune} shape - the shape to color
+ * colors the give given rune white.
+ * @param {Rune} rune - the rune to color
*/
-function white(shape) {
- return addColorFromHex(shape, '#FFFFFF')
+function white(rune) {
+ return addColorFromHex(rune, '#FFFFFF')
}
/**
@@ -854,20 +855,20 @@ function white(shape) {
* such that the first one occupies frac
* portion of the depth of the 3D result
* and the second the rest
- * @param {Rune} shape1 - given Rune
- * @param {Rune} shape2 - given Rune
* @param {number} frac - fraction between 0 and 1
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune1 - given Rune
+ * @param {Rune} rune2 - given Rune
+ * @return {Rune} resulting Rune
*/
-function overlay_frac(frac, shape1, shape2) {
- var front = new Shape()
- front.addS(shape1)
+function overlay_frac(frac, rune1, rune2) {
+ var front = new Rune()
+ front.addS(rune1)
var frontMat = front.getM()
// z: scale by frac
mat4.scale(frontMat, frontMat, vec3.fromValues(1, 1, frac))
- var back = new Shape()
- back.addS(shape2)
+ var back = new Rune()
+ back.addS(rune2)
var backMat = back.getM()
// z: scale by (1-frac), translate by -frac
mat4.scale(
@@ -876,7 +877,7 @@ function overlay_frac(frac, shape1, shape2) {
vec3.fromValues(1, 1, 1 - frac)
)
- var combined = new Shape()
+ var combined = new Rune()
combined.setS([front, back]) // render front first to avoid redrawing
return combined
}
@@ -886,24 +887,24 @@ function overlay_frac(frac, shape1, shape2) {
* overlaying the first with the second, each
* occupying equal parts of the depth of the
* result
- * @param {Rune} shape1 - given Rune
- * @param {Rune} shape2 - given Rune
- * @return {Rune} - resulting Rune
+ * @param {Rune} rune1 - given Rune
+ * @param {Rune} rune2 - given Rune
+ * @return {Rune} resulting Rune
*/
-function overlay(shape1, shape2) {
- return overlay_frac(0.5, shape1, shape2)
+function overlay(rune1, rune2) {
+ return overlay_frac(0.5, rune1, rune2)
}
/*
-function stereogram(shape) {
+function stereogram(rune) {
clear_viewport()
- var flattened_shape_list = generateFlattenedShapeList(shape)
+ var flattened_rune_list = generateFlattenedRuneList(rune)
var depth_map = open_pixmap('depth_map', viewport_size, viewport_size, true)
// draw the depth map
- for (var i = 0; i < flattened_shape_list.length; i++) {
- var shape = flattened_shape_list[i].shape
- var instanceArray = flattened_shape_list[i].instanceArray
- drawRune(shape.first, shape.count, instanceArray)
+ for (var i = 0; i < flattened_rune_list.length; i++) {
+ var rune = flattened_rune_list[i].rune
+ var instanceArray = flattened_rune_list[i].instanceArray
+ drawRune(rune.first, rune.count, instanceArray)
}
gl.finish()
copy_viewport(gl.canvas, depth_map)
diff --git a/public/externalLibs/sound/README.md b/public/externalLibs/sound/README.md
index 3b6bc2ba73..68e38ce06b 100644
--- a/public/externalLibs/sound/README.md
+++ b/public/externalLibs/sound/README.md
@@ -1,63 +1 @@
-__**Documentation For Sound 1920**__
-=====================================
-
-**Removal of Sourcesound & Sound Distinction**
-
-Previously, 2 types of abstractions for sound existed, __Sourcesound__ and __Sound__. They are defined as shown below.
-
-A __Sourcesound__ is a pair(wave, duration). Wave is a mathematical function that accepts time t and returns amplitude of the wave at that time. Duration is the duration of the __Sourcesound__.
-
-A __Sound__ is a discretized sample of audio, stored in an array.
-
-In Sound 1920, all abstractions operate on __Sourcesound__ **only**. __Sourcesounds__ are converted to __Sound__ **only upon playing**. As __Sounds__ are never used outside of the play() function, this distinction has become obsolete. Henceforth, all __Sourcesounds__ will be renamed to __Sound__, with the following definition.
-
-A __Sound__ is a pair(wave, duration). Wave is a mathematical function that accepts time t and returns amplitude of the wave at that time. Duration is the duration of the __Sound__.
-
-
-**Improved runtime & play() function**
-
-Previously, sound manipulation would involve many back and forth conversions between __Sourcesound__ and __Sound__, which proved to be very time-costly. As stated above, this conversion now only occurs when play() is called, resulting in a large speed up.
-
-However, the process of converting __Sourcesounds__ into discrete samples (discretization) is inherently time-costly and thus play() may still result in an unacceptably long latency between running and playback, especially for long or complex sounds.
-
-The new play() function utilises __WebAudio__ abstractions to solve this issue. Two __AudioBuffers__ of size 0.1 seconds are initialised. Then, the first 0.1s of the input sound is discretized and fed into the first buffer and then played by creating an __AudioBufferSourceNode__ using that buffer. While the first sample is playing, the next 0.1s sample is discretized and fed into the second buffer, then scheduled to play after the current sample ends. This implementation is reliant on the fact that second sample will be computed and ready to play before the first sample ends. Upon the end of the current sample, an __eventlistener__ triggers the discretization of the next 0.1s into the buffer of the ended sample and schedules it to play after the current sample. To maintain space-efficiency, only two buffers are created and utilised however a new __AudioBufferSourceNode__ is necessarily created for each sample as is intended by the __WebAudio__ API. This buffering method results in virtually zero wait time between running and playback.
-
-
-**Introduction of play_safe()**
-
-While the new play() function solves the latency issue, it is unsuitable for extremely complex sounds, where the processing time (discretization time) is longer than the length of the sample. The sound becomes "choppy" and begins to "stutter". A check could be added to detect this and prompt the user to use play_safe() instead to play their sound.
-
-The play_safe() function discretizes the whole sound before playback (similar to the older implementation of play()). This circumvents the above issue but reintroduces the previous issue of latency between running and playback. However, more testing is required to determine if this is still an issue in the Native implementation of the Source Academy which features massively improved performance.
-
-**Microphone Recording**
-
-Recording of sound input from student's microphone is available in Sound 1920. This section details the usage of the abstractions available.
-
-To begin microphone recording, init_record() must be called to grant access to user's microphone. record_for(duration) begins a sound recording that lasts for the specified duration. This
-sound is saved to a global 'recorded_sound' variable. Alternatively, for more precise control of exactly when to start and stop and recording, start_record() and stop_record() may be called.
-
-
-**Known Bugs & Issues**
-
-The following are known issues with the current implementation of Sound 1920 as of 19 April 2019.
-
--Both play() and play_safe() return undefined instead of throwing an error when given a non-sound list or pair.
-
--Sound 1920 appears to be buggy for Mac OS / Safari Browser. More testing required. Recommend Firefox / Chrome instead.
-
--Microphone sound is not working on Chrome on Mac OS and Windows. It is suspected to be due to a security issue.
-
--Multiple calls to record_for() in the Source Academy has unknown behaviour. It must be called one by one on the REPL instead.
-
-
-**Future Plans / Improvements**
-
--Prompt user to use play_safe() when sound is too complex for normal play().
-
--Prompt user to save their work before pressing play_safe().
-
--play_safe() "remembers" and immediately plays back the input sound if no changes were made since last run, in order to save processing time.
-
--Prompt user to run init_record if microphone abstractions are called.
-
--Further improvement of discretize function by using workers or multi-threading.
+Sound processing library for Source Academy.
diff --git a/public/externalLibs/sound/README_internal.md b/public/externalLibs/sound/README_internal.md
new file mode 100644
index 0000000000..3b6bc2ba73
--- /dev/null
+++ b/public/externalLibs/sound/README_internal.md
@@ -0,0 +1,63 @@
+__**Documentation For Sound 1920**__
+=====================================
+
+**Removal of Sourcesound & Sound Distinction**
+
+Previously, 2 types of abstractions for sound existed, __Sourcesound__ and __Sound__. They are defined as shown below.
+
+A __Sourcesound__ is a pair(wave, duration). Wave is a mathematical function that accepts time t and returns amplitude of the wave at that time. Duration is the duration of the __Sourcesound__.
+
+A __Sound__ is a discretized sample of audio, stored in an array.
+
+In Sound 1920, all abstractions operate on __Sourcesound__ **only**. __Sourcesounds__ are converted to __Sound__ **only upon playing**. As __Sounds__ are never used outside of the play() function, this distinction has become obsolete. Henceforth, all __Sourcesounds__ will be renamed to __Sound__, with the following definition.
+
+A __Sound__ is a pair(wave, duration). Wave is a mathematical function that accepts time t and returns amplitude of the wave at that time. Duration is the duration of the __Sound__.
+
+
+**Improved runtime & play() function**
+
+Previously, sound manipulation would involve many back and forth conversions between __Sourcesound__ and __Sound__, which proved to be very time-costly. As stated above, this conversion now only occurs when play() is called, resulting in a large speed up.
+
+However, the process of converting __Sourcesounds__ into discrete samples (discretization) is inherently time-costly and thus play() may still result in an unacceptably long latency between running and playback, especially for long or complex sounds.
+
+The new play() function utilises __WebAudio__ abstractions to solve this issue. Two __AudioBuffers__ of size 0.1 seconds are initialised. Then, the first 0.1s of the input sound is discretized and fed into the first buffer and then played by creating an __AudioBufferSourceNode__ using that buffer. While the first sample is playing, the next 0.1s sample is discretized and fed into the second buffer, then scheduled to play after the current sample ends. This implementation is reliant on the fact that second sample will be computed and ready to play before the first sample ends. Upon the end of the current sample, an __eventlistener__ triggers the discretization of the next 0.1s into the buffer of the ended sample and schedules it to play after the current sample. To maintain space-efficiency, only two buffers are created and utilised however a new __AudioBufferSourceNode__ is necessarily created for each sample as is intended by the __WebAudio__ API. This buffering method results in virtually zero wait time between running and playback.
+
+
+**Introduction of play_safe()**
+
+While the new play() function solves the latency issue, it is unsuitable for extremely complex sounds, where the processing time (discretization time) is longer than the length of the sample. The sound becomes "choppy" and begins to "stutter". A check could be added to detect this and prompt the user to use play_safe() instead to play their sound.
+
+The play_safe() function discretizes the whole sound before playback (similar to the older implementation of play()). This circumvents the above issue but reintroduces the previous issue of latency between running and playback. However, more testing is required to determine if this is still an issue in the Native implementation of the Source Academy which features massively improved performance.
+
+**Microphone Recording**
+
+Recording of sound input from student's microphone is available in Sound 1920. This section details the usage of the abstractions available.
+
+To begin microphone recording, init_record() must be called to grant access to user's microphone. record_for(duration) begins a sound recording that lasts for the specified duration. This
+sound is saved to a global 'recorded_sound' variable. Alternatively, for more precise control of exactly when to start and stop and recording, start_record() and stop_record() may be called.
+
+
+**Known Bugs & Issues**
+
+The following are known issues with the current implementation of Sound 1920 as of 19 April 2019.
+
+-Both play() and play_safe() return undefined instead of throwing an error when given a non-sound list or pair.
+
+-Sound 1920 appears to be buggy for Mac OS / Safari Browser. More testing required. Recommend Firefox / Chrome instead.
+
+-Microphone sound is not working on Chrome on Mac OS and Windows. It is suspected to be due to a security issue.
+
+-Multiple calls to record_for() in the Source Academy has unknown behaviour. It must be called one by one on the REPL instead.
+
+
+**Future Plans / Improvements**
+
+-Prompt user to use play_safe() when sound is too complex for normal play().
+
+-Prompt user to save their work before pressing play_safe().
+
+-play_safe() "remembers" and immediately plays back the input sound if no changes were made since last run, in order to save processing time.
+
+-Prompt user to run init_record if microphone abstractions are called.
+
+-Further improvement of discretize function by using workers or multi-threading.
diff --git a/public/externalLibs/sound/microphone.js b/public/externalLibs/sound/microphone.js
index da76692858..5125c51c6d 100644
--- a/public/externalLibs/sound/microphone.js
+++ b/public/externalLibs/sound/microphone.js
@@ -2,12 +2,6 @@
// // Microphone Functionality
// // ---------------------------------------------
-/**
- * @class {test} some class description
- *
- */
-class Microphone {}
-
// permission initially undefined
// set to true by granting microphone permission
// set to false by denying microphone permission
@@ -65,15 +59,29 @@ function play_recording_signal() {
play(sine_sound(500, recording_signal_duration_ms / 1000));
}
-const buffer_ms = 40;
-
-function record() {
+/**
+ * takes a buffer
duration (in seconds) as argument, and
+ * returns a nullary stop function stop
. A call
+ * stop()
returns a sound promise: a nullary function
+ * that returns a sound. Example:
init_record();
+ * const stop = record(0.5);
+ * // record after 0.5 seconds. Then in next query:
+ * const promise = stop();
+ * // In next query, you can play the promised sound, by
+ * // applying the promise:
+ * play(promise());
+ * @param {number} buffer - pause before recording, in seconds
+ * @returns {function} nullary stop
function;
+ * stop()
stops the recording and
+ * returns a sound promise: a nullary function that returns the recorded sound
+ */
+function record(buffer) {
check_permission();
const mediaRecorder = new MediaRecorder(globalStream);
play_recording_signal();
setTimeout(() => {
start_recording(mediaRecorder);
- }, recording_signal_duration_ms + buffer_ms);
+ }, recording_signal_duration_ms + buffer * 1000);
return () => {
mediaRecorder.stop();
play_recording_signal();
@@ -87,9 +95,22 @@ function record() {
};
}
-function record_for(duration_s) {
+/**
+ * Records a sound of given duration
in seconds, after
+ * a buffer
also in seconds, and
+ * returns a sound promise: a nullary function
+ * that returns a sound. Example: init_record();
+ * const promise = record_for(2, 0.5);
+ * // In next query, you can play the promised sound, by
+ * // applying the promise:
+ * play(promise());
+ * @param {number} duration - duration in seconds
+ * @param {number} buffer - pause before recording, in seconds
+ * @returns {function} promise
: nullary function which returns the recorded sound
+ */
+function record_for(duration, buffer) {
recorded_sound = undefined;
- const duration_ms = duration_s * 1000;
+ const duration_ms = duration * 1000;
check_permission();
const mediaRecorder = new MediaRecorder(globalStream);
play_recording_signal();
@@ -99,7 +120,7 @@ function record_for(duration_s) {
mediaRecorder.stop();
play_recording_signal();
}, duration_ms);
- }, recording_signal_duration_ms + buffer_ms);
+ }, recording_signal_duration_ms + buffer * 1000);
return () => {
if (recorded_sound === undefined) {
throw new Error("recording still being processed")
@@ -130,8 +151,8 @@ function convertToArrayBuffer(blob) {
function save(audioBuffer) {
const array = audioBuffer.getChannelData(0);
const duration = array.length / FS;
- recorded_sound = autocut_sound(
- make_sound(function(t) {
+ recorded_sound =
+ make_sound( t => {
const index = t * FS
const lowerIndex = Math.floor(index)
const upperIndex = lowerIndex + 1
@@ -139,6 +160,5 @@ function save(audioBuffer) {
const upper = array[upperIndex] ? array[upperIndex] : 0
const lower = array[lowerIndex] ? array[lowerIndex] : 0
return lower * (1 - ratio) + upper * ratio
- }, duration)
- );
+ }, duration);
}
diff --git a/public/externalLibs/sound/soundToneMatrix.js b/public/externalLibs/sound/soundToneMatrix.js
index 6d7df8754a..dcbbb0c16c 100644
--- a/public/externalLibs/sound/soundToneMatrix.js
+++ b/public/externalLibs/sound/soundToneMatrix.js
@@ -489,11 +489,25 @@ function exponential_decay(decay_period) {
}
}
+/**
+ * Returns an envelope: a function from sound to sound.
+ * When the envelope is applied to a sound, it returns
+ * a new sound that results from applying ADSR to
+ * the given sound. The Attack duration, Sustain duration and
+ * Release duration are given in the first, second and fourth
+ * arguments in seconds, and the Sustain level is given in
+ * the third argument as a fraction between 0 and 1.
+ * @param {number} attack_time - duration of attack phase in seconds
+ * @param {number} decay_time - duration of decay phase in seconds
+ * @param {number} sustain_level - sustain level between 0 and 1
+ * @param {number} release_time - duration of release phase in seconds
+ * @returns {function} envelope: function from sound to sound
+ */
function adsr(attack_time, decay_time, sustain_level, release_time) {
- return function (sound) {
+ return sound => {
var wave = get_wave(sound);
var duration = get_duration(sound);
- return make_sound(function (x) {
+ return make_sound( x => {
if (x < attack_time) {
return wave(x) * (x / attack_time);
} else if (x < attack_time + decay_time) {
@@ -510,9 +524,23 @@ function adsr(attack_time, decay_time, sustain_level, release_time) {
}
// waveform is a function that accepts freq, dur and returns sound
-function stacking_adsr(waveform, base_frequency, duration, list_of_envelope) {
+/**
+ * Returns a sound that results from applying a list of envelopes
+ * to a given wave form. The wave form should be a sound generator that
+ * takes a frequency and a duration as arguments and produces a
+ * sound with the given frequency and duration. Each evelope is
+ * applied to a harmonic: the first harmonic has the given frequency,
+ * the second has twice the frequency, the third three times the
+ * frequency etc.
+ * @param {function} waveform - function from frequency and duration to sound
+ * @param {number} base_frequency - frequency of the first harmonic
+ * @param {number} duration - duration of the produced sound, in seconds
+ * @param {list_of_envelope} envelopes - each a function from sound to sound
+ * @returns {sound} resulting sound
+ */
+function stacking_adsr(waveform, base_frequency, duration, envelopes) {
function zip(lst, n) {
- if (is_empty_list(lst)) {
+ if (is_null(lst)) {
return lst;
} else {
return pair(pair(n, head(lst)), zip(tail(lst), n + 1));
@@ -520,22 +548,35 @@ function stacking_adsr(waveform, base_frequency, duration, list_of_envelope) {
}
return simultaneously(accumulate(
- function (x, y) {
- return pair((tail(x))
- (waveform(base_frequency * head(x), duration))
- , y);
- }
- , []
- , zip(list_of_envelope, 1)));
+ (x, y) => pair((tail(x))
+ (waveform(base_frequency * head(x), duration))
+ , y)
+ , []
+ , zip(envelopes, 1)));
}
// instruments for students
+
+/**
+ * returns a sound that is reminiscent of a trombone, playing
+ * a given note for a given duration
of seconds
+ * @param {number} note - midi note
+ * @param {number} duration - duration in seconds
+ * @returns {function} stop
to stop recording,
+ */
function trombone(note, duration) {
return stacking_adsr(square_sound, midi_note_to_frequency(note), duration,
list(adsr(0.4, 0, 1, 0),
adsr(0.6472, 1.2, 0, 0)));
}
+/**
+ * returns a sound that is reminiscent of a piano, playing
+ * a given note for a given duration
of seconds
+ * @param {number} note - midi note
+ * @param {number} duration - duration in seconds
+ * @returns {function} stop
to stop recording,
+ */
function piano(note, duration) {
return stacking_adsr(triangle_sound, midi_note_to_frequency(note), duration,
list(adsr(0, 1.03, 0, 0),
@@ -543,6 +584,13 @@ function piano(note, duration) {
adsr(0, 0.4, 0, 0)));
}
+/**
+ * returns a sound that is reminiscent of a bell, playing
+ * a given note for a given duration
of seconds
+ * @param {number} note - midi note
+ * @param {number} duration - duration in seconds
+ * @returns {function} stop
to stop recording,
+ */
function bell(note, duration) {
return stacking_adsr(square_sound, midi_note_to_frequency(note), duration,
list(adsr(0, 1.2, 0, 0),
@@ -551,6 +599,13 @@ function bell(note, duration) {
adsr(0, 1.8142, 0, 0)));
}
+/**
+ * returns a sound that is reminiscent of a violin, playing
+ * a given note for a given duration
of seconds
+ * @param {number} note - midi note
+ * @param {number} duration - duration in seconds
+ * @returns {function} stop
to stop recording,
+ */
function violin(note, duration) {
return stacking_adsr(sawtooth_sound, midi_note_to_frequency(note), duration,
list(adsr(0.7, 0, 1, 0.3),
@@ -559,6 +614,13 @@ function violin(note, duration) {
adsr(0.9, 0, 1, 0.3)));
}
+/**
+ * returns a sound that is reminiscent of a cello, playing
+ * a given note for a given duration
of seconds
+ * @param {number} note - midi note
+ * @param {number} duration - duration in seconds
+ * @returns {function} stop
to stop recording,
+ */
function cello(note, duration) {
return stacking_adsr(square_sound, midi_note_to_frequency(note), duration,
list(adsr(0.1, 0, 1, 0.2),
diff --git a/public/externalLibs/sound/sounds.js b/public/externalLibs/sound/sounds.js
index ce77dac468..6b3d179c04 100644
--- a/public/externalLibs/sound/sounds.js
+++ b/public/externalLibs/sound/sounds.js
@@ -145,22 +145,49 @@ function raw_to_audio(_data) {
// duration: real value in seconds 0 < x < Infinity
// sound: (time -> amplitude) x duration
+/**
+ * Makes a sound from a wave and a duration.
+ * The wave is a function from time (in seconds)
+ * to an amplitude value that should lie between
+ * -1 and 1. The duration is given in seconds.
+ * @param {function} wave - given wave function
+ * @param {number} duration - in seconds
+ * @returns {sound}
+ */
function make_sound(wave, duration) {
return pair(t => t >= duration ? 0 : wave(t), duration);
}
+/**
+ * Accesses the wave of a sound.
+ * The wave is a function from time (in seconds)
+ * to an amplitude value that should lie between
+ * -1 and 1.
+ * @param {sound} sound - given sound
+ * @returns {function} wave function of the sound
+ */
function get_wave(sound) {
return head(sound);
}
+/**
+ * Accesses the duration of a sound, in seconds.
+ * @param {sound} sound - given sound
+ * @returns {number} duration in seconds
+ */
function get_duration(sound) {
return tail(sound);
}
-function is_sound(sound) {
- return is_pair(sound) &&
- ((typeof get_wave(sound)) === 'function') &&
- ((typeof get_duration(sound)) === 'number');
+/**
+ * Checks if a given value is a sound
+ * @param {value} x - given value
+ * @returns {boolean} whether x
is a sound
+ */
+function is_sound(x) {
+ return is_pair(x) &&
+ ((typeof get_wave(x)) === 'function') &&
+ ((typeof get_duration(x)) === 'number');
}
// Keeps track of whether play() is currently running,
@@ -274,6 +301,11 @@ function play_unsafe(sound) {
var _safeplaying = false;
var _safeaudio = null;
+/**
+ * plays a given sound using your computer's sound device
+ * @param {sound} sound - given sound
+ * @returns {undefined} undefined
+ */
function play(sound) {
// If a sound is already playing, terminate execution.
if (_safeplaying || _playing) return;
@@ -301,6 +333,10 @@ function string_to_sound(str) {
}
*/
+/**
+ * Stops playing the current sound
+ * @returns {undefined} undefined
+ */
function stop() {
// If using normal play()
if (_playing) {
@@ -316,6 +352,14 @@ function stop() {
}
// Concats a list of sounds
+/**
+ * makes a new sound by combining the sounds in a given
+ * list so that
+ * they play consecutively, each next sound starting when the
+ * previous sound ends
+ * @param {list_of_sounds} sounds - given list of sounds
+ * @returns {sound} resulting sound
+ */
function consecutively(list_of_sounds) {
function consec_two(ss1, ss2) {
var wave1 = head(ss1);
@@ -329,6 +373,14 @@ function consecutively(list_of_sounds) {
}
// Mushes a list of sounds together
+/**
+ * makes a new sound by combining the sounds in a given
+ * list so that
+ * they play simutaneously, all starting at the beginning of the
+ * resulting sound
+ * @param {list_of_sounds} sounds - given list of sounds
+ * @returns {sound} resulting sound
+ */
function simultaneously(list_of_sounds) {
function musher(ss1, ss2) {
var wave1 = head(ss1);
@@ -349,19 +401,45 @@ function simultaneously(list_of_sounds) {
return pair(normalised_wave, highest_duration);
}
+/**
+ * makes a sound of a given duration by randomly
+ * generating amplitude values
+ * @param {number} duration - duration of result sound, in seconds
+ * @returns {sound} resulting noise sound
+ */
function noise_sound(duration) {
return make_sound(t => Math.random() * 2 - 1, duration);
}
+/**
+ * makes a sine wave sound with given frequency and a given duration
+ * @param {number} freq - frequency of result sound, in Hz
+ * @param {number} duration - duration of result sound, in seconds
+ * @returns {sound} resulting sine sound
+ */
function sine_sound(freq, duration) {
return make_sound(t => Math.sin(2 * Math.PI * t * freq), duration);
}
+/**
+ * makes a silence sound with a given duration
+ * @param {number} duration - duration of result sound, in seconds
+ * @returns {sound} resulting silence sound
+ */
function silence_sound(duration) {
return make_sound(t => 0, duration);
}
// for mission 14
+
+/**
+ * converts a letter name str
to corresponding midi note.
+ * Examples for letter names are "A5"
, "B3"
, "D#4"
.
+ * See mapping from
+ * letter name to midi notes
+ * @param {string} str - given letter name
+ * @returns {number} midi value of the corresponding note
+ */
function letter_name_to_midi_note(note) {
// we don't consider double flat/ double sharp
var note = note.split("");
@@ -417,14 +495,34 @@ function letter_name_to_midi_note(note) {
return res;
}
+
+/**
+ * converts a letter name str
to corresponding frequency.
+ * First converts str
to a note using letter_name_to_midi_note
+ * and then to a frequency using midi_note_to_frequency
+ * @param {string} str - given letter name
+ * @returns {number} frequency of corresponding note in Hz
+ */
function letter_name_to_frequency(note) {
return midi_note_to_frequency(note_to_midi_note(note));
}
+/**
+ * converts a midi note n
to corresponding frequency.
+ * The note is given as an integer number.
+ * @param {number} n - given midi note
+ * @returns {number} frequency of the note in Hz
+ */
function midi_note_to_frequency(note) {
return 8.1757989156 * Math.pow(2, (note / 12));
}
+/**
+ * makes a square wave sound with given frequency and a given duration
+ * @param {number} freq - frequency of result sound, in Hz
+ * @param {number} duration - duration of result sound, in seconds
+ * @returns {sound} resulting square sound
+ */
function square_sound(freq, duration) {
function fourier_expansion_square(t) {
var answer = 0;
@@ -441,6 +539,12 @@ function square_sound(freq, duration) {
duration);
}
+/**
+ * makes a triangle wave sound with given frequency and a given duration
+ * @param {number} freq - frequency of result sound, in Hz
+ * @param {number} duration - duration of result sound, in seconds
+ * @returns {sound} resulting triangle sound
+ */
function triangle_sound(freq, duration) {
function fourier_expansion_triangle(t) {
var answer = 0;
@@ -458,6 +562,12 @@ function triangle_sound(freq, duration) {
duration);
}
+/**
+ * makes a sawtooth wave sound with given frequency and a given duration
+ * @param {number} freq - frequency of result sound, in Hz
+ * @param {number} duration - duration of result sound, in seconds
+ * @returns {sound} resulting sawtooth sound
+ */
function sawtooth_sound(freq, duration) {
function fourier_expansion_sawtooth(t) {
var answer = 0;
diff --git a/public/externalLibs/tree.js b/public/externalLibs/tree.js
index 7b412e3c08..cbfeebb7b8 100644
--- a/public/externalLibs/tree.js
+++ b/public/externalLibs/tree.js
@@ -1,50 +1,80 @@
// tree.js: Binary Tree abstraction for M5
// requires list.js
-// Author: Joel Lee
+// Authors: Joel Lee, Martin Henz
-// make_empty_binary_tree returns an empty list
-function make_empty_binary_tree() {
+/**
+ * returns an empty binary tree, represented by the empty list null
+ * @return {binarytree} empty binary tree
+ */
+
+function make_empty_tree() {
return null
}
-// checks if a given list is a valid binary tree, according to the abstraction
-function is_binary_tree(t) {
+/**
+ * checks whether a given value is a valid binary tree, according to the abstraction
+ * @param {value} x - given value
+ * @returns {boolean}
+ */
+function is_tree(t) {
return (
- is_empty_binary_tree(t) ||
+ is_empty_tree(t) ||
(length(t) === 3 &&
- is_binary_tree(left_subtree_of(t)) &&
- is_binary_tree(right_subtree_of(t)))
+ is_tree(left_branch(t)) &&
+ is_tree(right_branch(t)))
);
}
-// make_binary_tree_node returns a binary tree node composed of the
-// three elements passed in
-function make_binary_tree_node(left, value, right) {
- if (!is_binary_tree(left)) {
+/**
+ * returns a binary tree node composed of the
+ * three components passed in
+ * @param {value} val - value of the node
+ * @param {binary_tree} left - left subtree
+ * @param {binary_tree} right - right subtree
+ * @returns {boolean}
+ */
+function make_tree(value, left, right) {
+ if (!is_tree(left)) {
throw new Error('Left subtree is not a valid binary tree')
- } else if (!is_binary_tree(right)) {
+ } else if (!is_tree(right)) {
throw new Error('Right subtree is not a valid binary tree')
}
- return list(left, value, right)
+ return list(value, left, right)
}
-// is_empty_binary_tree checks if given binary tree node is an empty list
-function is_empty_binary_tree(t) {
+/**
+ * checks whether given binary tree t
is empty
+ * @param {binary_tree} t - given binary tree
+ * @returns {boolean}
+ */
+function is_empty_tree(t) {
return is_null(t)
}
-// left_subtree_of returns the left subtree of a given binary tree node
-function left_subtree_of(t) {
+/**
+ * returns the value of a given binary tree
+ * @param {binary_tree} t - given binary tree
+ * @returns {value} value of t
+ */
+function entry(t) {
return list_ref(t, 0)
}
-// value_of returns the value of a given binary tree node
-function value_of(t) {
+/**
+ * returns the left branch of a given binary tree
+ * @param {binary_tree} t - given binary tree
+ * @returns {binary_tree} left branch of t
+ */
+function left_branch(t) {
return list_ref(t, 1)
}
-// right_subtree_of returns the right subtree of a given binary tree node
-function right_subtree_of(t) {
+/**
+ * returns the right branch of a given binary tree
+ * @param {binary_tree} t - given binary tree
+ * @returns {binary_tree} right branch of t
+ */
+function right_branch(t) {
return list_ref(t, 2)
}
diff --git a/src/reducers/externalLibraries.ts b/src/reducers/externalLibraries.ts
index 8d9cec1b0f..3b8cf9e102 100644
--- a/src/reducers/externalLibraries.ts
+++ b/src/reducers/externalLibraries.ts
@@ -53,10 +53,10 @@ const libEntries: Array<[ExternalLibraryName, string[]]> = [
'heart',
'pentagram',
'ribbon',
- 'hollusion',
'anaglyph',
'overlay_frac',
- 'overlay'
+ 'overlay',
+ 'hollusion' // currently not documented; animation not working
]
],
[
@@ -77,13 +77,9 @@ const libEntries: Array<[ExternalLibraryName, string[]]> = [
'connect_rigidly',
'connect_ends',
'put_in_standard_position',
- 'full_view_proportional',
- 'squeeze_full_view',
- 'squeeze_rectangular_portion',
'translate',
'scale',
/** Contest functions */
- 'alternative_unit_circle',
'rotate_pi_over_2',
'scale_x_y',
'gosperize',
@@ -94,7 +90,11 @@ const libEntries: Array<[ExternalLibraryName, string[]]> = [
'param_gosperize',
'rotate_around_origin',
'arc', // used in GOSPERIZE
- 'invert' // used in DRAGONIZE
+ 'invert', // used in DRAGONIZE
+ 'alternative_unit_circle', // undocumented
+ 'full_view_proportional', // undocumented
+ 'squeeze_full_view', // undocumented
+ 'squeeze_rectangular_portion' // undocumented
]
],
[
@@ -134,13 +134,13 @@ const libEntries: Array<[ExternalLibraryName, string[]]> = [
[
ExternalLibraryNames.BINARYTREES,
[
- 'make_empty_binary_tree',
- 'is_binary_tree',
- 'make_binary_tree_node',
- 'is_empty_binary_tree',
- 'left_subtree_of',
- 'value_of',
- 'right_subtree_of'
+ 'make_empty_tree',
+ 'is_tree',
+ 'make_tree',
+ 'is_empty_tree',
+ 'entry',
+ 'left_branch',
+ 'right_branch'
]
]
];