processing / processing/p5.js

Refactor 2d_primitives.js

Open
#6,686 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Area:Core Enhancement
Dominant language
JavaScript
Stars
24k
Forks
3.8k
Avg merge
3d 16h
Merged PRs (30d)
25

Description

Increasing Access

It would make the code shorter and more readable

Most appropriate sub-area of p5.js?
  • Accessibility
  • Color
  • Core/Environment/Rendering
  • Data
  • DOM
  • Events
  • Image
  • IO
  • Math
  • Typography
  • Utilities
  • WebGL
  • Build Process
  • Unit Testing
  • Internalization
  • Friendly Errors
  • Other (specify if possible)
Feature enhancement details

I think that 2d_primitives.js could be refactored a bit.
These are some suggestions:

_normalizeArcAngles signature

Problems:

  • function has too many arguments
  • function has boolean argument, which requires, when reading the code, to see the function signature

From this:

p5.prototype._normalizeArcAngles = (
  start,
  stop,
  width,
  height,
  correctForScaling
)

To this:

p5.prototype._normalizeArcAngles = (
  size, //(width, height),
  drawRegion, //(startAngle, stopAngle)
  angleBehaviourOnScale //(angleBehaviourOnScale.Stretch, angleBehaviourOnScale.KeepRelative)
)

_normalizeArcAngles angle constraint

Code:

  // 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);

Problems:

  • contains many levels of abstractions,
  • code should expain itself

Proposal:

  drawRegion = ConstrainDrawRegionToOneCircumference(drawRegion);

_normalizeArcAngles abs

Code:

separation = Math.min(
    Math.abs(start - stop),
    constants.TWO_PI - Math.abs(start - stop)
  );

Problem:

  • contains many levels of abstractions
  • makes the function too long

Proposal:

arcAngle = getArchAngle(drawRegion);

_normalizeArcAngles angle scaling

Code:

// 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;
   }
 }

Problems:

  • contains repeated code,
  • contains numeric literals,
  • contains a redundant comment,
  • contains many levels of abstractions,
  • is too long

Proposal:

  if(angleBehaviourOnScale === angleBehaviourOnScale.KeepRelative)
    makeDrawRegionRelative(drawRegion);
   
makeDrawRegionRelative(region){
  region.start = makeAngleRelative(region.start);
  region.stop = makeAngleRelative(region.stop);
  return region;
}

//I don't understand the math behind completely so I'm not able to simplify it further, 
// this method is actually too long but it's still an improvement
//TODO: simplify this method
makeAngleRelative(angle){
  const THREE_PI = 3 * constants.HALF_PI;
  let offsetAngle = 0;
  
  if(angle < constants.PI)
    offsetAngle = 0;
  else if (angle <= THREE_PI)
    offsetAngle = constants.PI;
  else 
    offsetAngle = constants.TWO_PI;

  return Math.atan(width / height * Math.tan(angle)) + offsetAngle;
}

_normalizeArcAngles angle last check

Code:

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

Problem:

  • function contains many levels of abstractions
    Proposal:
drawRegion = makeStopGreaterThanStart(drawRegion);

But I would leave the comment inside of the function because it's not obvious

arc function signature

Problem:

  • function has too many arguments but it's a public API so it's better to just group them and call another function

Proposal:

p5.prototype.arc = function(x, y, w, h, start, stop, mode, detail) {
  p5._validateParameters('arc', arguments);
  position: {x:x,y:y}
  size: {width:w, height: h}
  drawRegion: {start: start, stop: stop}
  return arc(position, size, drawRegion, mode, detail);
}

arc function premature return

Code:

// 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;
}

Problem:

  • Code should expain itself so the comment should not be necessary

Proposal:

if(isArcNotVisible(this, drawRegion))
  return this;

arc function size abs

Code:

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

Problem:

  • a function should only contain one level of abstraction

Proposal:

size = flipNegativeDimensions(size);

circle inline documentation

Problem:

  • I don't think it is necessary to explain what a circle is.
 /* Draws a circle to the canvas. A circle is a round shape. Every point on the
 * edge of a circle is the same distance from its center. By default, the first
 * two parameters set the location of the center of the circle. The third
 * parameter sets the shape's width and height (diameter).
 */

circle function

Code:

  p5.prototype.circle = function() {
    p5._validateParameters('circle', arguments);
    const args = Array.prototype.slice.call(arguments, 0, 2);
    args.push(arguments[2]);
    args.push(arguments[2]);
    return this._renderEllipse(...args);
  }

Problem:

  • code could be simplified

Proposal:

  p5.prototype.circle = function(x, y, diameter) {
   p5._validateParameters('circle', arguments);   
   return this._renderEllipse(x, y, diameter, diameter, diameter);
  }

_renderEllipse signature

Code:

p5.prototype._renderEllipse = function(x, y, w, h, detailX){
}

Problem:

  • too many arguments

Proposal:

p5.prototype._renderEllipse = function(position, size, detailX){
}

_renderEllipse code

Code:

// p5 supports negative width and heights for rects
  if (w < 0) {
    w = Math.abs(w);
  }

  if (typeof h === 'undefined') {
    // Duplicate 3rd argument if only 3 given.
    h = w;
  } else if (h < 0) {
    h = Math.abs(h);
  }

##Opinion:

  • I don't think this function should be so elastic to be able to accept different counts of arguments. It's only used internally after all.

Proposal:

size = flipNegativeDimensions(size);

I noticed that in many functions x and y, width and height are separate arguments but I think it's preferable to use position and size instead.

I would like to be assigned to this issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading 2d_primitives.js, especially _normalizeArcAngles, arc, circle, and _renderEllipse, and compare the proposed signature and abstraction changes with their current callers. Before changing code, establish which refactors are in scope and verify that existing rendering behavior and public APIs remain intact when the work is complete.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
computer-graphics
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.