<commit_before>/**
 * @module Shape
 * @submodule 2D Primitives
 * @for p5
 * @requires core
 * @requires constants
 */

import p5 from '../main';
import * as constants from '../constants';
import canvas from '../helpers';
import '../friendly_errors/fes_core';
import '../friendly_errors/file_errors';
import '../friendly_errors/validate_params';

/**
 * This function does 3 things:
 *
 *   1. Bounds the desired start/stop angles for an arc (in radians) so that:
 *
 *          0 <= start < TWO_PI ;    start <= stop < start + TWO_PI
 *
 *      This means that the arc rendering functions don't have to be concerned
 *      with what happens if stop is smaller than start, or if the arc 'goes
 *      round more than once', etc.: they can just start at start and increase
 *      until stop and the correct arc will be drawn.
 *
 *   2. Optionally adjusts the angles within each quadrant to counter the naive
 *      scaling of the underlying ellipse up from the unit circle.  Without
 *      this, the angles become arbitrary when width != height: 45 degrees
 *      might be drawn at 5 degrees on a 'wide' ellipse, or at 85 degrees on
 *      a 'tall' ellipse.
 *
 *   3. Flags up when start and stop correspond to the same place on the
 *      underlying ellipse.  This is useful if you want to do something special
 *      there (like rendering a whole ellipse instead).
 */
p5.prototype._normalizeArcAngles = (
  start,
  stop,
  width,
  height,
  correctForScaling
) => {
  const epsilon = 0.00001; // Smallest visible angle on displays up to 4K.
  let separation;

  // The order of the steps is important here: each one builds upon the
  // adjustments made in the steps that precede it.

  // Constrain both start and stop to [0,TWO_PI).
  start = start - constants.TWO_PI * Math.floor(start / constants.TWO_PI);
  stop = stop - constants.TWO_PI * Math.floor(stop / constants.TWO_PI);

  // Get the angular separation between the requested start and stop points.
  //
  // Technically this separation only matches what gets drawn if
  // correctForScaling is enabled.  We could add a more complicated calculation
  // for when the scaling is uncorrected (in which case the drawn points could
  // end up pushed together or pulled apart quite dramatically relative to what
  // was requested), but it would make things more opaque for little practical
  // benefit.
  //
  // (If you do disable correctForScaling and find that correspondToSamePoint
  // is set too aggressively, the easiest thing to do is probably to just make
  // epsilon smaller...)
  separation = Math.min(
    Math.abs(start - stop),
    constants.TWO_PI - Math.abs(start - stop)
  );

  // Optionally adjust the angles to counter linear scaling.
  if (correctForScaling) {
    if (start <= constants.HALF_PI) {
      start = Math.atan(width / height * Math.tan(start));
    } else if (start > constants.HALF_PI && start <= 3 * constants.HALF_PI) {
      start = Math.atan(width / height * Math.tan(start)) + constants.PI;
    } else {
      start = Math.atan(width / height * Math.tan(start)) + constants.TWO_PI;
    }
    if (stop <= constants.HALF_PI) {
      stop = Math.atan(width / height * Math.tan(stop));
    } else if (stop > constants.HALF_PI && stop <= 3 * constants.HALF_PI) {
      stop = Math.atan(width / height * Math.tan(stop)) + constants.PI;
    } else {
      stop = Math.atan(width / height * Math.tan(stop)) + constants.TWO_PI;
    }
  }

  // Ensure that start <= stop < start + TWO_PI.
  if (start > stop) {
    stop += constants.TWO_PI;
  }

  return {
    start,
    stop,
    correspondToSamePoint: separation < epsilon
  };
};

/**
 * Draw an arc to the screen. If called with only x, y, w, h, start and stop,
 * the arc will be drawn and filled as an open pie segment. If a mode parameter
 * is provided, the arc will be filled like an open semi-circle (OPEN), a closed
 * semi-circle (CHORD), or as a closed pie segment (PIE). The origin may be changed
 * with the <a href="#/p5/ellipseMode">ellipseMode()</a> function.
 *
 * The arc is always drawn clockwise from wherever start falls to wherever stop
 * falls on the ellipse.Adding or subtracting TWO_PI to either angle does not
 * change where they fall. If both start and stop fall at the same place, a full
 * ellipse will be drawn. Be aware that the y-axis increases in the downward
 * direction, therefore angles are measured clockwise from the positive
 * x-direction ("3 o'clock").
 *
 * @method arc
 * @param  {Number} x      x-coordinate of the arc's ellipse
 * @param  {Number} y      y-coordinate of the arc's ellipse
 * @param  {Number} w      width of the arc's ellipse by default
 * @param  {Number} h      height of the arc's ellipse by default
 * @param  {Number} start  angle to start the arc, specified in radians
 * @param  {Number} stop   angle to stop the arc, specified in radians
 * @param  {Constant} [mode] optional parameter to determine the way of drawing
 *                         the arc. either CHORD, PIE or OPEN
 * @param  {Number} [detail] optional parameter for WebGL mode only. This is to
 *                         specify the number of vertices that makes up the
 *                         perimeter of the arc. Default value is 25.
 * @chainable
 *
 * @example
 * <div>
 * <code>
 * arc(50, 55, 50, 50, 0, HALF_PI);
 * noFill();
 * arc(50, 55, 60, 60, HALF_PI, PI);
 * arc(50, 55, 70, 70, PI, PI + QUARTER_PI);
 * arc(50, 55, 80, 80, PI + QUARTER_PI, TWO_PI);
 * </code>
 * </div>
 *
 * <div>
 * <code>
 * arc(50, 50, 80, 80, 0, PI + QUARTER_PI);
 * </code>
 * </div>
 *
 * <div>
 * <code>
 * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, OPEN);
 * </code>
 * </div>
 *
 * <div>
 * <code>
 * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, CHORD);
 * </code>
 * </div>
 *
 * <div>
 * <code>
 * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, PIE);
 * </code>
 * </div>
 *
 * @alt
 *shattered outline of an ellipse with a quarter of a white circle bottom-right.
 *white ellipse with top right quarter missing.
 *white ellipse with black outline with top right missing.
 *white ellipse with top right missing with black outline around shape.
 *white ellipse with top right quarter missing with black outline around the shape.
 */
p5.prototype.arc = function(x, y, w, h, start, stop, mode, detail) {
  p5._validateParameters('arc', arguments);

  // if the current stroke and fill settings wouldn't result in something
  // visible, exit immediately
  if (!this._renderer._doStroke && !this._renderer._doFill) {
    return this;
  }

  start = this._toRadians(start);
  stop = this._toRadians(stop);

  // p5 supports negative width and heights for ellipses
  w = Math.abs(w);
  h = Math.abs(h);

  const vals = canvas.modeAdjust(x, y, w, h, this._renderer._ellipseMode);
  const angles = this._normalizeArcAngles(start, stop, vals.w, vals.h, true);

  if (angles.correspondToSamePoint) {
    // If the arc starts and ends at (near enough) the same place, we choose to
    // draw an ellipse instead.  This is preferable to faking an ellipse (by
    // making stop ever-so-slightly less than start + TWO_PI) because the ends
    // join up to each other rather than at a vertex at the centre (leaving<commit_msg>fix inconsistent arc behavior closes #4664<commit_after>/**
 * @module Shape
 * @submodule 2D Primitives
 * @for p5
 * @requires core
 * @requires constants
 */

import p5 from '../main';
import * as constants from '../constants';
import canvas from '../helpers';
import '../friendly_errors/fes_core';
import '../friendly_errors/file_errors';
import '../friendly_errors/validate_params';

/**
 * This function does 3 things:
 *
 *   1. Bounds the desired start/stop angles for an arc (in radians) so that:
 *
 *          0 <= start < TWO_PI ;    start <= stop < start + TWO_PI
 *
 *      This means that the arc rendering functions don't have to be concerned
 *      with what happens if stop is smaller than start, or if the arc 'goes
 *      round more than once', etc.: they can just start at start and increase
 *      until stop and the correct arc will be drawn.
 *
 *   2. Optionally adjusts the angles within each quadrant to counter the naive
 *      scaling of the underlying ellipse up from the unit circle.  Without
 *      this, the angles become arbitrary when width != height: 45 degrees
 *      might be drawn at 5 degrees on a 'wide' ellipse, or at 85 degrees on
 *      a 'tall' ellipse.
 *
 *   3. Flags up when start and stop correspond to the same place on the
 *      underlying ellipse.  This is useful if you want to do something special
 *      there (like rendering a whole ellipse instead).
 */
p5.prototype._normalizeArcAngles = (
  start,
  stop,
  width,
  height,
  correctForScaling
) => {
  const epsilon = 0.00001; // Smallest visible angle on displays up to 4K.
  let separation;

  // The order of the steps is important here: each one builds upon the
  // adjustments made in the steps that precede it.

  // Constrain both start and stop to [0,TWO_PI).
  start = start - constants.TWO_PI * Math.floor(start / constants.TWO_PI);
  stop = stop - constants.TWO_PI * Math.floor(stop / constants.TWO_PI);

  // Get the angular separation between the requested start and stop points.
  //
  // Technically this separation only matches what gets drawn if
  // correctForScaling is enabled.  We could add a more complicated calculation
  // for when the scaling is uncorrected (in which case the drawn points could
  // end up pushed together or pulled apart quite dramatically relative to what
  // was requested), but it would make things more opaque for little practical
  // benefit.
  //
  // (If you do disable correctForScaling and find that correspondToSamePoint
  // is set too aggressively, the easiest thing to do is probably to just make
  // epsilon smaller...)
  separation = Math.min(
    Math.abs(start - stop),
    constants.TWO_PI - Math.abs(start - stop)
  );

  // Optionally adjust the angles to counter linear scaling.
  if (correctForScaling) {
    if (start <= constants.HALF_PI) {
      start = Math.atan(width / height * Math.tan(start));
    } else if (start > constants.HALF_PI && start <= 3 * constants.HALF_PI) {
      start = Math.atan(width / height * Math.tan(start)) + constants.PI;
    } else {
      start = Math.atan(width / height * Math.tan(start)) + constants.TWO_PI;
    }
    if (stop <= constants.HALF_PI) {
      stop = Math.atan(width / height * Math.tan(stop));
    } else if (stop > constants.HALF_PI && stop <= 3 * constants.HALF_PI) {
      stop = Math.atan(width / height * Math.tan(stop)) + constants.PI;
    } else {
      stop = Math.atan(width / height * Math.tan(stop)) + constants.TWO_PI;
    }
  }

  // Ensure that start <= stop < start + TWO_PI.
  if (start > stop) {
    stop += constants.TWO_PI;
  }

  return {
    start,
    stop,
    correspondToSamePoint: separation < epsilon
  };
};

/**
 * Draw an arc to the screen. If called with only x, y, w, h, start and stop,
 * the arc will be drawn and filled as an open pie segment. If a mode parameter
 * is provided, the arc will be filled like an open semi-circle (OPEN), a closed
 * semi-circle (CHORD), or as a closed pie segment (PIE). The origin may be changed
 * with the <a href="#/p5/ellipseMode">ellipseMode()</a> function.
 *
 * The arc is always drawn clockwise from wherever start falls to wherever stop
 * falls on the ellipse.Adding or subtracting TWO_PI to either angle does not
 * change where they fall. If both start and stop fall at the same place, a full
 * ellipse will be drawn. Be aware that the y-axis increases in the downward
 * direction, therefore angles are measured clockwise from the positive
 * x-direction ("3 o'clock").
 *
 * @method arc
 * @param  {Number} x      x-coordinate of the arc's ellipse
 * @param  {Number} y      y-coordinate of the arc's ellipse
 * @param  {Number} w      width of the arc's ellipse by default
 * @param  {Number} h      height of the arc's ellipse by default
 * @param  {Number} start  angle to start the arc, specified in radians
 * @param  {Number} stop   angle to stop the arc, specified in radians
 * @param  {Constant} [mode] optional parameter to determine the way of drawing
 *                         the arc. either CHORD, PIE or OPEN
 * @param  {Number} [detail] optional parameter for WebGL mode only. This is to
 *                         specify the number of vertices that makes up the
 *                         perimeter of the arc. Default value is 25.
 * @chainable
 *
 * @example
 * <div>
 * <code>
 * arc(50, 55, 50, 50, 0, HALF_PI);
 * noFill();
 * arc(50, 55, 60, 60, HALF_PI, PI);
 * arc(50, 55, 70, 70, PI, PI + QUARTER_PI);
 * arc(50, 55, 80, 80, PI + QUARTER_PI, TWO_PI);
 * </code>
 * </div>
 *
 * <div>
 * <code>
 * arc(50, 50, 80, 80, 0, PI + QUARTER_PI);
 * </code>
 * </div>
 *
 * <div>
 * <code>
 * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, OPEN);
 * </code>
 * </div>
 *
 * <div>
 * <code>
 * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, CHORD);
 * </code>
 * </div>
 *
 * <div>
 * <code>
 * arc(50, 50, 80, 80, 0, PI + QUARTER_PI, PIE);
 * </code>
 * </div>
 *
 * @alt
 *shattered outline of an ellipse with a quarter of a white circle bottom-right.
 *white ellipse with top right quarter missing.
 *white ellipse with black outline with top right missing.
 *white ellipse with top right missing with black outline around shape.
 *white ellipse with top right quarter missing with black outline around the shape.
 */
p5.prototype.arc = function(x, y, w, h, start, stop, mode, detail) {
  p5._validateParameters('arc', arguments);

  // if the current stroke and fill settings wouldn't result in something
  // visible, exit immediately
  if (!this._renderer._doStroke && !this._renderer._doFill) {
    return this;
  }

  if (start === stop) {
    return this;
  }

  start = this._toRadians(start);
  stop = this._toRadians(stop);

  // p5 supports negative width and heights for ellipses
  w = Math.abs(w);
  h = Math.abs(h);

  const vals = canvas.modeAdjust(x, y, w, h, this._renderer._ellipseMode);
  const angles = this._normalizeArcAngles(start, stop, vals.w, vals.h, true);

  if (angles.correspondToSamePoint) {
    // If the arc starts and ends at (near enough) the same place, we choose to
    // draw an ellipse instead.  This is preferable to faking an ellipse (by
    // making stop ever-so-slightly less than start + TWO_PI) because the ends
    // join up to each other rather than at a vertex at the centre (leaving
{'fraction_non_alphanumeric': 0.10685386264610278, 'fraction_numerical': 0.014108859533854348, 'mean_word_length': 2.9389812040315992, 'pattern_counts': {'":': 0, '<': 69, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7126839', 'n_tokens_mistral': 4652, 'n_tokens_neox': 4360, 'n_words': 1825}