/**
 * @module Shape
 * @submodule Custom Shapes
 * @for p5
 * @requires core
 * @requires constants
 */

function vertex(p5, fn){
  /**
   * Begins adding vertices to a custom shape.
   *
   * The `beginShape()` and <a href="#/p5/endShape">endShape()</a> functions
   * allow for creating custom shapes in 2D or 3D. `beginShape()` begins adding
   * vertices to a custom shape and <a href="#/p5/endShape">endShape()</a> stops
   * adding them.
   *
   * The parameter, `kind`, sets the kind of shape to make. The available kinds are:
   *
   * - `PATH` (the default) to draw shapes by tracing out the path along their edges.
   * - `POINTS` to draw a series of points.
   * - `LINES` to draw a series of unconnected line segments.
   * - `TRIANGLES` to draw a series of separate triangles.
   * - `TRIANGLE_FAN` to draw a series of connected triangles sharing the first vertex in a fan-like fashion.
   * - `TRIANGLE_STRIP` to draw a series of connected triangles in strip fashion.
   * - `QUADS` to draw a series of separate quadrilaterals (quads).
   * - `QUAD_STRIP` to draw quad strip using adjacent edges to form the next quad.
   *
   * After calling `beginShape()`, shapes can be built by calling
   * <a href="#/p5/vertex">vertex()</a>,
   * <a href="#/p5/bezierVertex">bezierVertex()</a>, and/or
   * <a href="#/p5/splineVertex">splineVertex()</a>. Calling
   * <a href="#/p5/endShape">endShape()</a> will stop adding vertices to the
   * shape. Each shape will be outlined with the current stroke color and filled
   * with the current fill color.
   *
   * Transformations such as <a href="#/p5/translate">translate()</a>,
   * <a href="#/p5/rotate">rotate()</a>, and
   * <a href="#/p5/scale">scale()</a> don't work between `beginShape()` and
   * <a href="#/p5/endShape">endShape()</a>. It's also not possible to use
   * other shapes, such as <a href="#/p5/ellipse">ellipse()</a> or
   * <a href="#/p5/rect">rect()</a>, between `beginShape()` and
   * <a href="#/p5/endShape">endShape()</a>.
   *
   * @method beginShape
   * @param  {(POINTS|LINES|TRIANGLES|TRIANGLE_FAN|TRIANGLE_STRIP|QUADS|QUAD_STRIP|PATH)} [kind=PATH] either POINTS, LINES, TRIANGLES, TRIANGLE_FAN
   *                                TRIANGLE_STRIP, QUADS, QUAD_STRIP or PATH. Defaults to PATH.
   * @chainable
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   beginShape();
   *
   *   // Add vertices.
   *   vertex(30, 20);
   *   vertex(85, 20);
   *   vertex(85, 75);
   *   vertex(30, 75);
   *
   *   // Stop drawing the shape.
   *   endShape(CLOSE);
   *
   *   describe('A white square on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   // Only draw the vertices (points).
   *   beginShape(POINTS);
   *
   *   // Add vertices.
   *   vertex(30, 20);
   *   vertex(85, 20);
   *   vertex(85, 75);
   *   vertex(30, 75);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('Four black dots that form a square are drawn on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   // Only draw lines between alternating pairs of vertices.
   *   beginShape(LINES);
   *
   *   // Add vertices.
   *   vertex(30, 20);
   *   vertex(85, 20);
   *   vertex(85, 75);
   *   vertex(30, 75);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('Two horizontal black lines on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Style the shape.
   *   noFill();
   *
   *   // Start drawing the shape.
   *   beginShape();
   *
   *   // Add vertices.
   *   vertex(30, 20);
   *   vertex(85, 20);
   *   vertex(85, 75);
   *   vertex(30, 75);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('Three black lines form a sideways U shape on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Style the shape.
   *   noFill();
   *
   *   // Start drawing the shape.
   *   beginShape();
   *
   *   // Add vertices.
   *   vertex(30, 20);
   *   vertex(85, 20);
   *   vertex(85, 75);
   *   vertex(30, 75);
   *
   *   // Stop drawing the shape.
   *   // Connect the first and last vertices.
   *   endShape(CLOSE);
   *
   *   describe('A black outline of a square drawn on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   // Draw a series of triangles.
   *   beginShape(TRIANGLES);
   *
   *   // Left triangle.
   *   vertex(30, 75);
   *   vertex(40, 20);
   *   vertex(50, 75);
   *
   *   // Right triangle.
   *   vertex(60, 20);
   *   vertex(70, 75);
   *   vertex(80, 20);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('Two white triangles drawn on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   // Draw a series of triangles.
   *   beginShape(TRIANGLE_STRIP);
   *
   *   // Add vertices.
   *   vertex(30, 75);
   *   vertex(40, 20);
   *   vertex(50, 75);
   *   vertex(60, 20);
   *   vertex(70, 75);
   *   vertex(80, 20);
   *   vertex(90, 75);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('Five white triangles that are interleaved drawn on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   // Draw a series of triangles that share their first vertex.
   *   beginShape(TRIANGLE_FAN);
   *
   *   // Add vertices.
   *   vertex(57.5, 50);
   *   vertex(57.5, 15);
   *   vertex(92, 50);
   *   vertex(57.5, 85);
   *   vertex(22, 50);
   *   vertex(57.5, 15);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('Four white triangles form a square are drawn on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   // Draw a series of quadrilaterals.
   *   beginShape(QUADS);
   *
   *   // Left rectangle.
   *   vertex(30, 20);
   *   vertex(30, 75);
   *   vertex(50, 75);
   *   vertex(50, 20);
   *
   *   // Right rectangle.
   *   vertex(65, 20);
   *   vertex(65, 75);
   *   vertex(85, 75);
   *   vertex(85, 20);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('Two white rectangles drawn on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   // Draw a series of quadrilaterals.
   *   beginShape(QUAD_STRIP);
   *
   *   // Add vertices.
   *   vertex(30, 20);
   *   vertex(30, 75);
   *   vertex(50, 20);
   *   vertex(50, 75);
   *   vertex(65, 20);
   *   vertex(65, 75);
   *   vertex(85, 20);
   *   vertex(85, 75);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('Three white rectangles that share edges are drawn on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100, WEBGL);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   // Draw a series of quadrilaterals.
   *   beginShape(PATH);
   *
   *   // Add the vertices.
   *   vertex(-30, -30, 0);
   *   vertex(30, -30, 0);
   *   vertex(30, -10, 0);
   *   vertex(-10, -10, 0);
   *   vertex(-10, 10, 0);
   *   vertex(30, 10, 0);
   *   vertex(30, 30, 0);
   *   vertex(-30, 30, 0);
   *
   *   // Stop drawing the shape.
   *   // Connect the first and last vertices.
   *   endShape(CLOSE);
   *
   *   describe('A blocky C shape drawn in white on a gray background.');
   * }
   *
   * @example
   * // Click and drag with the mouse to view the scene from different angles.
   *
   * function setup() {
   *   createCanvas(100, 100, WEBGL);
   *
   *   describe('A blocky C shape drawn in red, blue, and green on a gray background.');
   * }
   *
   * function draw() {
   *   background(200);
   *
   *   // Enable orbiting with the mouse.
   *   orbitControl();
   *
   *   // Start drawing the shape.
   *   // Draw a series of quadrilaterals.
   *   beginShape(PATH);
   *
   *   // Add the vertices.
   *   fill('red');
   *   stroke('red');
   *   vertex(-30, -30, 0);
   *   vertex(30, -30, 0);
   *   vertex(30, -10, 0);
   *   fill('green');
   *   stroke('green');
   *   vertex(-10, -10, 0);
   *   vertex(-10, 10, 0);
   *   vertex(30, 10, 0);
   *   fill('blue');
   *   stroke('blue');
   *   vertex(30, 30, 0);
   *   vertex(-30, 30, 0);
   *
   *   // Stop drawing the shape.
   *   // Connect the first and last vertices.
   *   endShape(CLOSE);
   * }
   */
  fn.beginShape = function(kind) {
    // p5._validateParameters('beginShape', arguments);
    this._renderer.beginShape(...arguments);
  };

  /**
   * Adds a Bézier curve segment to a custom shape.
   *
   * `bezierVertex()` adds a curved segment to custom shapes. The Bézier curves
   * it creates are defined like those made by the
   * <a href="#/p5/bezier">bezier()</a> function. `bezierVertex()` must be
   * called between the
   * <a href="#/p5/beginShape">beginShape()</a> and
   * <a href="#/p5/endShape">endShape()</a> functions.
   * Bézier need a starting point. Building a shape
   * only with Bézier curves needs one initial
   * call to <a href="#/p5/vertex">bezierVertex()</a>, before
   * a number of `bezierVertex()` calls that is a multiple of the parameter
   * set by <a href="#/p5/bezierOrder">bezierOrder(...)</a> (default 3).
   * But shapes can mix different types of vertices, so if there
   * are some previous vertices, then the initial anchor is not needed,
   * only the multiples of 3 (or the Bézier order) calls to
   *  `bezierVertex` for each curve.
   *
   * Each curve of order 3 requires three calls to `bezierVertex`, so
   * 2 curves would need 7 calls to `bezierVertex()`:
   * (1 one initial anchor point, two sets of 3 curves describing the curves)
   * With `bezierOrder(2)`, two curves would need 5 calls: 1 + 2 + 2.
   *
   * Bézier curves can also be drawn in 3D using WebGL mode.
   *
   * Note: `bezierVertex()` won’t work when an argument is passed to
   * <a href="#/p5/beginShape">beginShape()</a>.
   *
   * @method bezierVertex
   * @param  {Number} x x-coordinate of the first control point.
   * @param  {Number} y y-coordinate of the first control point.
   * @param  {Number} [u]
   * @param  {Number} [v]
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Style the shape.
   *   noFill();
   *
   *   // Start drawing the shape.
   *   beginShape();
   *
   *   // Add the first anchor point.
   *   bezierVertex(30, 20);
   *
   *   // Add the Bézier vertex.
   *   bezierVertex(80, 0);
   *   bezierVertex(80, 75);
   *   bezierVertex(30, 75);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('A black C curve on a gray background.');
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Draw the anchor points in black.
   *   stroke(0);
   *   strokeWeight(5);
   *   point(30, 20);
   *   point(30, 75);
   *
   *   // Draw the control points in red.
   *   stroke(255, 0, 0);
   *   point(80, 0);
   *   point(80, 75);
   *
   *   // Style the shape.
   *   noFill();
   *   stroke(0);
   *   strokeWeight(1);
   *
   *   // Start drawing the shape.
   *   beginShape();
   *
   *   // Add the first anchor point.
   *   bezierVertex(30, 20);
   *
   *   // Add the Bézier vertex.
   *   bezierVertex(80, 0);
   *   bezierVertex(80, 75);
   *   bezierVertex(30, 75);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   // Draw red lines from the anchor points to the control points.
   *   stroke(255, 0, 0);
   *   line(30, 20, 80, 0);
   *   line(30, 75, 80, 75);
   *
   *   describe(
   *     'A gray square with three curves. A black curve has two straight, red lines that extend from its ends. The endpoints of all the curves are marked with dots.'
   *   );
   * }
   *
   * @example
   * // Click the mouse near the red dot in the top-right corner
   * // and drag to change the curve's shape.
   *
   * let x2 = 80;
   * let y2 = 0;
   * let isChanging = false;
   *
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   describe(
   *     'A gray square with three curves. A black curve has two straight, red lines that extend from its ends. The endpoints of all the curves are marked with dots.'
   *   );
   * }
   *
   * function draw() {
   *   background(200);
   *
   *   // Draw the anchor points in black.
   *   stroke(0);
   *   strokeWeight(5);
   *   point(30, 20);
   *   point(30, 75);
   *
   *   // Draw the control points in red.
   *   stroke(255, 0, 0);
   *   point(x2, y2);
   *   point(80, 75);
   *
   *   // Style the shape.
   *   noFill();
   *   stroke(0);
   *   strokeWeight(1);
   *
   *   // Start drawing the shape.
   *   beginShape();
   *
   *   // Add the first anchor point.
   *   bezierVertex(30, 20);
   *
   *   // Add the Bézier vertex.
   *   bezierVertex(x2, y2);
   *   bezierVertex(80, 75);
   *   bezierVertex(30, 75);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   // Draw red lines from the anchor points to the control points.
   *   stroke(255, 0, 0);
   *   line(30, 20, x2, y2);
   *   line(30, 75, 80, 75);
   * }
   *
   * // Start changing the first control point if the user clicks near it.
   * function mousePressed() {
   *   if (dist(mouseX, mouseY, x2, y2) < 20) {
   *     isChanging = true;
   *   }
   * }
   *
   * // Stop changing the first control point when the user releases the mouse.
   * function mouseReleased() {
   *   isChanging = false;
   * }
   *
   * // Update the first control point while the user drags the mouse.
   * function mouseDragged() {
   *   if (isChanging === true) {
   *     x2 = mouseX;
   *     y2 = mouseY;
   *   }
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Start drawing the shape.
   *   beginShape();
   *
   *   // Add the first anchor point.
   *   bezierVertex(30, 20);
   *
   *   // Add the Bézier vertices.
   *   bezierVertex(80, 0);
   *   bezierVertex(80, 75);
   *   bezierVertex(30, 75);
   *
   *   bezierVertex(50, 80);
   *   bezierVertex(60, 25);
   *   bezierVertex(30, 20);
   *
   *   // Stop drawing the shape.
   *   endShape();
   *
   *   describe('A crescent moon shape drawn in white on a gray background.');
   * }
   *
   * @example
   * // Click and drag the mouse to view the scene from different angles.
   *
   * function setup() {
   *   createCanvas(100, 100, WEBGL);
   *
   *   describe('A crescent moon shape drawn in white on a blue background. When the user drags the mouse, the scene rotates and a second moon is revealed.');
   * }
   *
   * function draw() {
   *   background('midnightblue');
   *
   *   // Enable orbiting with the mouse.
   *   orbitControl();
   *
   *   // Style the moons.
   *   noStroke();
   *   fill('lemonchiffon');
   *
   *   // Draw the first moon.
   *   beginShape();
   *   bezierVertex(-20, -30, 0);
   *
   *   bezierVertex(30, -50, 0);
   *   bezierVertex(30, 25, 0);
   *   bezierVertex(-20, 25, 0);
   *
   *   bezierVertex(0, 30, 0);
   *   bezierVertex(10, -25, 0);
   *   bezierVertex(-20, -30, 0);
   *   endShape();
   *
   *   // Draw the second moon.
   *   beginShape();
   *
   *   bezierVertex(-20, -30, -20);
   *
   *   bezierVertex(30, -50, -20);
   *   bezierVertex(30, 25, -20);
   *   bezierVertex(-20, 25, -20);
   *
   *   bezierVertex(0, 30, -20);
   *   bezierVertex(10, -25, -20);
   *   bezierVertex(-20, -30, -20);
   *
   *   endShape();
   * }
   */
  /**
   * @method bezierVertex
   * @param  {Number} x
   * @param  {Number} y
   * @param  {Number} z
   * @param  {Number} [u]
   * @param  {Number} [v]
   */
  fn.bezierVertex = function(...args) {
    this._renderer.bezierVertex(...args);
  };

  /**
   * Concludes the vertices of a custom shape.
   *
   * The <a href="#/p5/beginShape">beginShape()</a> and `endShape()` functions
   * allow for creating custom shapes in 2D or 3D.
   * <a href="#/p5/beginShape">beginShape()</a> begins adding vertices to a
   * custom shape and `endShape()` stops adding them.
   *
   * The first parameter, `mode`, is optional. By default, the first and last
   * vertices of a shape aren't connected. If the constant `CLOSE` is passed, as
   * in `endShape(CLOSE)`, then the first and last vertices will be connected.
   * When CLOSE mode is used for splines (with `splineVeertex()`), the shape is ended smoothly.
   *
   *
   * The second parameter, `count`, is also optional. In WebGL mode, it’s more
   * efficient to draw many copies of the same shape using a technique called
   * <a href="https://webglfundamentals.org/webgl/lessons/webgl-instanced-drawing.html" target="_blank">instancing</a>.
   * The `count` parameter tells WebGL mode how many copies to draw. For
   * example, calling `endShape(CLOSE, 400)` after drawing a custom shape will
   * make it efficient to draw 400 copies. This feature requires
   * <a href="https://p5js.org/tutorials/intro-to-shaders/" target="_blank">writing a custom shader</a>.
   *
   * After calling <a href="#/p5/beginShape">beginShape()</a>, shapes can be
   * built by calling <a href="#/p5/vertex">vertex()</a>,
   * <a href="#/p5/bezierVertex">bezierVertex()</a> and/or
   * <a href="#/p5/splineVertex">splineVertex()</a>. Calling
   * `endShape()` will stop adding vertices to the
   * shape. Each shape will be outlined with the current stroke color and filled
   * with the current fill color.
   *
   * Transformations such as <a href="#/p5/translate">translate()</a>,
   * <a href="#/p5/rotate">rotate()</a>, and
   * <a href="#/p5/scale">scale()</a> don't work between
   * <a href="#/p5/beginShape">beginShape()</a> and `endShape()`. It's also not
   * possible to use other shapes, such as <a href="#/p5/ellipse">ellipse()</a> or
   * <a href="#/p5/rect">rect()</a>, between
   * <a href="#/p5/beginShape">beginShape()</a> and `endShape()`.
   *
   * @method endShape
   * @param  {CLOSE} [mode] use CLOSE to close the shape
   * @param  {Integer} [count] number of times you want to draw/instance the shape (for WebGL mode).
   * @chainable
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *
   *   background(200);
   *
   *   // Style the shapes.
   *   noFill();
   *
   *   // Left triangle.
   *   beginShape();
   *   vertex(20, 20);
   *   vertex(45, 20);
   *   vertex(45, 80);
   *   endShape(CLOSE);
   *
   *   // Right triangle.
   *   beginShape();
   *   vertex(50, 20);
   *   vertex(75, 20);
   *   vertex(75, 80);
   *   endShape();
   *
   *   describe(
   *     'Two sets of black lines drawn on a gray background. The three lines on the left form a right triangle. The two lines on the right form a right angle.'
   *   );
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(200, 100);
   *
   *   background(240);
   *
   *   noFill();
   *   stroke(0);
   *
   *   // Open shape (left)
   *   beginShape();
   *   vertex(20, 20);
   *   vertex(80, 20);
   *   vertex(80, 80);
   *   endShape();  // Not closed
   *
   *   // Closed shape (right)
   *   beginShape();
   *   vertex(120, 20);
   *   vertex(180, 20);
   *   vertex(180, 80);
   *   endShape(CLOSE);  // Closed
   *
   *   describe(
   *     'Two right-angled shapes on a light gray background. The left shape is open with three lines. The right shape is closed, forming a triangle.'
   *   );
   * }
   *
   * @example
   * function setup() {
   *   createCanvas(100, 100);
   *   background(200);
   *
   *   beginShape();
   *
   *   splineVertex(32, 91);
   *   splineVertex(21, 17);
   *   splineVertex(68, 19);
   *   splineVertex(82, 91);
   *
   *   endShape(CLOSE);
   *
   *   describe(
   *     'A curvy four-sided slightly lopsided blob.'
   *   );
   * }
   *
   * @example
   * // Note: A "uniform" is a global variable within a shader program.
   *
   * // Create a string with the vertex shader program.
   * // The vertex shader is called for each vertex.
   * let vertSrc = `#version 300 es
   *
   * precision mediump float;
   *
   * in vec3 aPosition;
   * flat out int instanceID;
   *
   * uniform mat4 uModelViewMatrix;
   * uniform mat4 uProjectionMatrix;
   *
   * void main() {
   *
   *   // Copy the instance ID to the fragment shader.
   *   instanceID = gl_InstanceID;
   *   vec4 positionVec4 = vec4(aPosition, 1.0);
   *
   *   // gl_InstanceID represents a numeric value for each instance.
   *   // Using gl_InstanceID allows us to move each instance separately.
   *   // Here we move each instance horizontally by ID * 23.
   *   float xOffset = float(gl_InstanceID) * 23.0;
   *
   *   // Apply the offset to the final position.
   *   gl_Position = uProjectionMatrix * uModelViewMatrix * (positionVec4 -
   *     vec4(xOffset, 0.0, 0.0, 0.0));
   * }
   * `;
   *
   * // Create a string with the fragment shader program.
   * // The fragment shader is called for each pixel.
   * let fragSrc = `#version 300 es
   *
   * precision mediump float;
   *
   * out vec4 outColor;
   * flat in int instanceID;
   * uniform float numInstances;
   *
   * void main() {
   *   vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
   *   vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
   *
   *   // Normalize the instance ID.
   *   float normId = float(instanceID) / numInstances;
   *
   *   // Mix between two colors using the normalized instance ID.
   *   outColor = mix(red, blue, normId);
   * }
   * `;
   *
   * function setup() {
   *   createCanvas(100, 100, WEBGL);
   *
   *   // Create a p5.Shader object.
   *   let myShader = createShader(vertSrc, fragSrc);
   *
   *   background(220);
   *
   *   // Compile and apply the p5.Shader.
   *   shader(myShader);
   *
   *   // Set the numInstances uniform.
   *   myShader.setUniform('numInstances', 4);
   *
   *   // Translate the origin to help align the drawing.
   *   translate(25, -10);
   *
   *   // Style the shapes.
   *   noStroke();
   *
   *   // Draw the shapes.
   *   beginShape();
   *   vertex(0, 0);
   *   vertex(0, 20);
   *   vertex(20, 20);
   *   vertex(20, 0);
   *   vertex(0, 0);
   *   endShape(CLOSE, 4);
   *
   *   describe('A row of four squares. Their colors transition from purple on the left to red on the right');
   * }
   */
  fn.endShape = function(mode, count = 1) {
    // p5._validateParameters('endShape', arguments);
    if (count < 1) {
      console.log('🌸 p5.js says: You can not have less than one instance');
      count = 1;
    }

    this._renderer.endShape(mode, count);
  };

  /**
   * Sets the normal vector for vertices in a custom 3D shape.
   *
   * 3D shapes created with <a href="#/p5/beginShape">beginShape()</a> and
   * <a href="#/p5/endShape">endShape()</a> are made by connecting sets of
   * points called vertices. Each vertex added with
   * <a href="#/p5/vertex">vertex()</a> has a normal vector that points away
   * from it. The normal vector controls how light reflects off the shape.
   *
   * `normal()` can be called two ways with different parameters to define the
   * normal vector's components.
   *
   * The first way to call `normal()` has three parameters, `x`, `y`, and `z`.
   * If `Number`s are passed, as in `normal(1, 2, 3)`, they set the x-, y-, and
   * z-components of the normal vector.
   *
   * The second way to call `normal()` has one parameter, `vector`. If a
   * <a href="#/p5.Vector">p5.Vector</a> object is passed, as in
   * `normal(myVector)`, its components will be used to set the normal vector.
   *
   * `normal()` changes the normal vector of vertices added to a custom shape
   * with <a href="#/p5/vertex">vertex()</a>. `normal()` must be called between
   * the <a href="#/p5/beginShape">beginShape()</a> and
   * <a href="#/p5/endShape">endShape()</a> functions, just like
   * <a href="#/p5/vertex">vertex()</a>. The normal vector set by calling
   * `normal()` will affect all following vertices until `normal()` is called
   * again:
   *
   * ```js
   * beginShape();
   *
   * // Set the vertex normal.
   * normal(-0.4, -0.4, 0.8);
   *
   * // Add a vertex.
   * vertex(-30, -30, 0);
   *
   * // Set the vertex normal.
   * normal(0, 0, 1);
   *
   * // Add vertices.
   * vertex(30, -30, 0);
   * vertex(30, 30, 0);
   *
   * // Set the vertex normal.
   * normal(0.4, -0.4, 0.8);
   *
   * // Add a vertex.
   * vertex(-30, 30, 0);
   *
   * endShape();
   * ```
   *
   * @method normal
   * @param  {p5.Vector} vector vertex normal as a <a href="#/p5.Vector">p5.Vector</a> object.
   * @chainable
   *
   * @example
   * // Click the and drag the mouse to view the scene from a different angle.
   *
   * function setup() {
   *   createCanvas(100, 100, WEBGL);
   *
   *   describe(
   *     'A colorful square on a black background. The square changes color and rotates when the user drags the mouse. Parts of its surface reflect light in different directions.'
   *   );
   * }
   *
   * function draw() {
   *   background(0);
   *
   *   // Enable orbiting with the mouse.
   *   orbitControl();
   *
   *   // Style the shape.
   *   normalMaterial();
   *   noStroke();
   *
   *   // Draw the shape.
   *   beginShape();
   *   vertex(-30, -30, 0);
   *   vertex(30, -30, 0);
   *   vertex(30, 30, 0);
   *   vertex(-30, 30, 0);
   *   endShape();
   * }
   *
   * @example
   * // Click the and drag the mouse to view the scene from a different angle.
   *
   * function setup() {
   *   createCanvas(100, 100, WEBGL);
   *
   *   describe(
   *     'A colorful square on a black background. The square changes color and rotates when the user drags the mouse. Parts of its surface reflect light in different directions.'
   *   );
   * }
   *
   * function draw() {
   *   background(0);
   *
   *   // Enable orbiting with the mouse.
   *   orbitControl();
   *
   *   // Style the shape.
   *   normalMaterial();
   *   noStroke();
   *
   *   // Draw the shape.
   *   // Use normal() to set vertex normals.
   *   beginShape();
   *   normal(-0.4, -0.4, 0.8);
   *   vertex(-30, -30, 0);
   *
   *   normal(0, 0, 1);
   *   vertex(30, -30, 0);
   *   vertex(30, 30, 0);
   *
   *   normal(0.4, -0.4, 0.8);
   *   vertex(-30, 30, 0);
   *   endShape();
   * }
   *
   * @example
   * // Click the and drag the mouse to view the scene from a different angle.
   *
   * function setup() {
   *   createCanvas(100, 100, WEBGL);
   *
   *   describe(
   *     'A colorful square on a black background. The square changes color and rotates when the user drags the mouse. Parts of its surface reflect light in different directions.'
   *   );
   * }
   *
   * function draw() {
   *   background(0);
   *
   *   // Enable orbiting with the mouse.
   *   orbitControl();
   *
   *   // Style the shape.
   *   normalMaterial();
   *   noStroke();
   *
   *   // Create p5.Vector objects.
   *   let n1 = createVector(-0.4, -0.4, 0.8);
   *   let n2 = createVector(0, 0, 1);
   *   let n3 = createVector(0.4, -0.4, 0.8);
   *
   *   // Draw the shape.
   *   // Use normal() to set vertex normals.
   *   beginShape();
   *   normal(n1);
   *   vertex(-30, -30, 0);
   *
   *   normal(n2);
   *   vertex(30, -30, 0);
   *   vertex(30, 30, 0);
   *
   *   normal(n3);
   *   vertex(-30, 30, 0);
   *   endShape();
   * }
   */
  /**
   * @method normal
   * @param  {Number} x x-component of the vertex normal.
   * @param  {Number} y y-component of the vertex normal.
   * @param  {Number} z z-component of the vertex normal.
   * @chainable
   */
  fn.normal = function(x, y, z) {
    this._assert3d('normal');
    // p5._validateParameters('normal', arguments);
    this._renderer.normal(...arguments);

    return this;
  };

  /**
   * Sets the shader's vertex property or attribute variables.
   *
   * A vertex property, or vertex attribute, is a variable belonging to a vertex in a shader. p5.js provides some
   * default properties, such as `aPosition`, `aNormal`, `aVertexColor`, etc. These are
   * set using <a href="#/p5/vertex">vertex()</a>, <a href="#/p5/normal">normal()</a>
   * and <a href="#/p5/fill">fill()</a> respectively. Custom properties can also
   * be defined within <a href="#/p5/beginShape">beginShape()</a> and
   * <a href="#/p5/endShape">endShape()</a>.
   *
   * The first parameter, `propertyName`, is a string with the property's name.
   * This is the same variable name which should be declared in the shader, such as
   * `in vec3 aProperty`, similar to .`setUniform()`.
   *
   * The second parameter, `data`, is the value assigned to the shader variable. This
   * value will be applied to subsequent vertices created with
   * <a href="#/p5/vertex">vertex()</a>. It can be a Number or an array of numbers,
   * and in the shader program the type can be declared according to the WebGL
   * specification. Common types include `float`, `vec2`, `vec3`, `vec4` or matrices.
   *
   * See also the <a href="#/p5/vertexProperty">vertexProperty()</a> method on
   * <a href="#/p5/Geometry">Geometry</a> objects.
   *
   * @method vertexProperty
   * @for p5
   * @param {String} attributeName the name of the vertex attribute.
   * @param {Number|Number[]} data the data tied to the vertex attribute.
   *
   * @example
   * const vertSrc = `#version 300 es
   *  precision mediump float;
   *  uniform mat4 uModelViewMatrix;
   *  uniform mat4 uProjectionMatrix;
   *
   *  in vec3 aPosition;
   *  in vec2 aOffset;
   *
   *  void main(){
   *    vec4 positionVec4 = vec4(aPosition.xyz, 1.0);
   *    positionVec4.xy += aOffset;
   *    gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
   *  }
   * `;
   *
   * const fragSrc = `#version 300 es
   *  precision mediump float;
   *  out vec4 outColor;
   *  void main(){
   *    outColor = vec4(0.0, 1.0, 1.0, 1.0);
   *  }
   * `;
   *
   * function setup(){
   *   createCanvas(100, 100, WEBGL);
   *
   *   // Create and use the custom shader.
   *   const myShader = createShader(vertSrc, fragSrc);
   *   shader(myShader);
   *
   *   describe('A wobbly, cyan circle on a gray background.');
   * }
   *
   * function draw(){
   *   // Set the styles
   *   background(125);
   *   noStroke();
   *
   *   // Draw the circle.
   *   beginShape();
   *   for (let i = 0; i < 30; i++){
   *     const x = 40 * cos(i/30 * TWO_PI);
   *     const y = 40 * sin(i/30 * TWO_PI);
   *
   *     // Apply some noise to the coordinates.
   *     const xOff = 10 * noise(x + millis()/1000) - 5;
   *     const yOff = 10 * noise(y + millis()/1000) - 5;
   *
   *     // Apply these noise values to the following vertex.
   *     vertexProperty('aOffset', [xOff, yOff]);
   *     vertex(x, y);
   *   }
   *   endShape(CLOSE);
   * }
   *
   * @example
   * let myShader;
   * const cols = 10;
   * const rows = 10;
   * const cellSize = 6;
   *
   * const vertSrc = `#version 300 es
   *   precision mediump float;
   *   uniform mat4 uProjectionMatrix;
   *   uniform mat4 uModelViewMatrix;
   *
   *   in vec3 aPosition;
   *   in vec3 aNormal;
   *   in vec3 aVertexColor;
   *   in float aDistance;
   *
   *   out vec3 vVertexColor;
   *
   *   void main(){
   *     vec4 positionVec4 = vec4(aPosition, 1.0);
   *     positionVec4.xyz += aDistance * aNormal * 2.0;;
   *     vVertexColor = aVertexColor;
   *     gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
   *   }
   * `;
   *
   * const fragSrc = `#version 300 es
   *   precision mediump float;
   *
   *   in vec3 vVertexColor;
   *   out vec4 outColor;
   *
   *   void main(){
   *     outColor = vec4(vVertexColor, 1.0);
   *   }
   * `;
   *
   * function setup(){
   *   createCanvas(100, 100, WEBGL);
   *
   *   // Create and apply the custom shader.
   *   myShader = createShader(vertSrc, fragSrc);
   *   shader(myShader);
   *   noStroke();
   *   describe('A blue grid, which moves away from the mouse position, on a gray background.');
   * }
   *
   * function draw(){
   *   background(200);
   *
   *   // Draw the grid in the middle of the screen.
   *   translate(-cols*cellSize/2, -rows*cellSize/2);
   *   beginShape(QUADS);
   *   for (let i = 0; i < cols; i++) {
   *     for (let j = 0; j < rows; j++) {
   *
   *       // Calculate the cell position.
   *       let x = i * cellSize;
   *       let y = j * cellSize;
   *
   *       fill(j/rows*255, j/cols*255, 255);
   *
   *       // Calculate the distance from the corner of each cell to the mouse.
   *       let distance = dist(x, y, mouseX, mouseY);
   *
   *       // Send the distance to the shader.
   *       vertexProperty('aDistance', min(distance, 100));
   *
   *       vertex(x, y);
   *       vertex(x + cellSize, y);
   *       vertex(x + cellSize, y + cellSize);
   *       vertex(x, y + cellSize);
   *     }
   *   }
   *   endShape();
   * }
   */
  fn.vertexProperty = function(attributeName, data){
    // this._assert3d('vertexProperty');
    // p5._validateParameters('vertexProperty', arguments);
    this._renderer.vertexProperty(attributeName, data);
  };
}

if(typeof p5 !== 'undefined'){
  vertex(p5, p5.prototype);
}

export { vertex as default };
