Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify bezierVertex() and quadraticVertex() so that curFillColor becomes an interpolated value #5920

Merged
merged 10 commits into from
Jan 3, 2023
223 changes: 176 additions & 47 deletions src/webgl/3d_primitives.js
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,8 @@ p5.RendererGL.prototype.bezierVertex = function(...args) {
let w_x = [];
let w_y = [];
let w_z = [];
let t, _x, _y, _z, i;
let t, _x, _y, _z, i, k, m;
// variable i for bezierPoints, k for components, and m for anchor points.
const argLength = args.length;

t = 0;
Expand Down Expand Up @@ -1588,25 +1589,68 @@ p5.RendererGL.prototype.bezierVertex = function(...args) {

const LUTLength = this._lookUpTableBezier.length;

// fillColors[0]: start point color
// fillColors[1],[2]: control point color
// fillColors[3]: end point color
const fillColors = [];
for (m = 0; m < 4; m++) fillColors.push([]);
fillColors[0] = this.immediateMode.geometry.vertexColors.slice(-4);
fillColors[3] = this.curFillColor.slice();

// Do the same for strokeColor.
const strokeColors = [];
for (m = 0; m < 4; m++) strokeColors.push([]);
strokeColors[0] = this.immediateMode.geometry.lineVertexColors.slice(-4);
strokeColors[3] = this.curStrokeColor.slice();

if (argLength === 6) {
this.isBezier = true;

w_x = [this.immediateMode._bezierVertex[0], args[0], args[2], args[4]];
w_y = [this.immediateMode._bezierVertex[1], args[1], args[3], args[5]];
// The ratio of the distance between the start point, the two control-
// points, and the end point determines the intermediate color.
let d0 = Math.hypot(w_x[0]-w_x[1], w_y[0]-w_y[1]);
let d1 = Math.hypot(w_x[1]-w_x[2], w_y[1]-w_y[2]);
let d2 = Math.hypot(w_x[2]-w_x[3], w_y[2]-w_y[3]);
const totalLength = d0 + d1 + d2;
d0 /= totalLength;
d2 /= totalLength;
for (k = 0; k < 4; k++) {
fillColors[1].push(
fillColors[0][k] * (1-d0) + fillColors[3][k] * d0
);
fillColors[2].push(
fillColors[0][k] * d2 + fillColors[3][k] * (1-d2)
);
strokeColors[1].push(
strokeColors[0][k] * (1-d0) + strokeColors[3][k] * d0
);
strokeColors[2].push(
strokeColors[0][k] * d2 + strokeColors[3][k] * (1-d2)
);
}

for (i = 0; i < LUTLength; i++) {
_x =
w_x[0] * this._lookUpTableBezier[i][0] +
w_x[1] * this._lookUpTableBezier[i][1] +
w_x[2] * this._lookUpTableBezier[i][2] +
w_x[3] * this._lookUpTableBezier[i][3];
_y =
w_y[0] * this._lookUpTableBezier[i][0] +
w_y[1] * this._lookUpTableBezier[i][1] +
w_y[2] * this._lookUpTableBezier[i][2] +
w_y[3] * this._lookUpTableBezier[i][3];
// Interpolate colors using control points
this.curFillColor = [0, 0, 0, 0];
this.curStrokeColor = [0, 0, 0, 0];
_x = _y = 0;
for (m = 0; m < 4; m++) {
for (k = 0; k < 4; k++) {
this.curFillColor[k] +=
this._lookUpTableBezier[i][m] * fillColors[m][k];
this.curStrokeColor[k] +=
this._lookUpTableBezier[i][m] * strokeColors[m][k];
}
_x += w_x[m] * this._lookUpTableBezier[i][m];
_y += w_y[m] * this._lookUpTableBezier[i][m];
}
this.vertex(_x, _y);
}
// so that we leave currentColor with the last value the user set it to
this.curFillColor = fillColors[3];
this.curStrokeColor = strokeColors[3];
this.immediateMode._bezierVertex[0] = args[4];
this.immediateMode._bezierVertex[1] = args[5];
} else if (argLength === 9) {
Expand All @@ -1615,24 +1659,49 @@ p5.RendererGL.prototype.bezierVertex = function(...args) {
w_x = [this.immediateMode._bezierVertex[0], args[0], args[3], args[6]];
w_y = [this.immediateMode._bezierVertex[1], args[1], args[4], args[7]];
w_z = [this.immediateMode._bezierVertex[2], args[2], args[5], args[8]];
// The ratio of the distance between the start point, the two control-
// points, and the end point determines the intermediate color.
let d0 = Math.hypot(w_x[0]-w_x[1], w_y[0]-w_y[1], w_z[0]-w_z[1]);
let d1 = Math.hypot(w_x[1]-w_x[2], w_y[1]-w_y[2], w_z[1]-w_z[2]);
let d2 = Math.hypot(w_x[2]-w_x[3], w_y[2]-w_y[3], w_z[2]-w_z[3]);
const totalLength = d0 + d1 + d2;
d0 /= totalLength;
d2 /= totalLength;
for (k = 0; k < 4; k++) {
fillColors[1].push(
fillColors[0][k] * (1-d0) + fillColors[3][k] * d0
);
fillColors[2].push(
fillColors[0][k] * d2 + fillColors[3][k] * (1-d2)
);
strokeColors[1].push(
strokeColors[0][k] * (1-d0) + strokeColors[3][k] * d0
);
strokeColors[2].push(
strokeColors[0][k] * d2 + strokeColors[3][k] * (1-d2)
);
}
for (i = 0; i < LUTLength; i++) {
_x =
w_x[0] * this._lookUpTableBezier[i][0] +
w_x[1] * this._lookUpTableBezier[i][1] +
w_x[2] * this._lookUpTableBezier[i][2] +
w_x[3] * this._lookUpTableBezier[i][3];
_y =
w_y[0] * this._lookUpTableBezier[i][0] +
w_y[1] * this._lookUpTableBezier[i][1] +
w_y[2] * this._lookUpTableBezier[i][2] +
w_y[3] * this._lookUpTableBezier[i][3];
_z =
w_z[0] * this._lookUpTableBezier[i][0] +
w_z[1] * this._lookUpTableBezier[i][1] +
w_z[2] * this._lookUpTableBezier[i][2] +
w_z[3] * this._lookUpTableBezier[i][3];
// Interpolate colors using control points
this.curFillColor = [0, 0, 0, 0];
this.curStrokeColor = [0, 0, 0, 0];
_x = _y = _z = 0;
for (m = 0; m < 4; m++) {
for (k = 0; k < 4; k++) {
this.curFillColor[k] +=
this._lookUpTableBezier[i][m] * fillColors[m][k];
this.curStrokeColor[k] +=
this._lookUpTableBezier[i][m] * strokeColors[m][k];
}
_x += w_x[m] * this._lookUpTableBezier[i][m];
_y += w_y[m] * this._lookUpTableBezier[i][m];
_z += w_z[m] * this._lookUpTableBezier[i][m];
}
this.vertex(_x, _y, _z);
}
// so that we leave currentColor with the last value the user set it to
this.curFillColor = fillColors[3];
this.curStrokeColor = strokeColors[3];
this.immediateMode._bezierVertex[0] = args[6];
this.immediateMode._bezierVertex[1] = args[7];
this.immediateMode._bezierVertex[2] = args[8];
Expand All @@ -1647,7 +1716,8 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) {
let w_x = [];
let w_y = [];
let w_z = [];
let t, _x, _y, _z, i;
let t, _x, _y, _z, i, k, m;
// variable i for bezierPoints, k for components, and m for anchor points.
const argLength = args.length;

t = 0;
Expand Down Expand Up @@ -1679,24 +1749,62 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) {

const LUTLength = this._lookUpTableQuadratic.length;

// fillColors[0]: start point color
// fillColors[1]: control point color
// fillColors[2]: end point color
const fillColors = [];
for (m = 0; m < 3; m++) fillColors.push([]);
fillColors[0] = this.immediateMode.geometry.vertexColors.slice(-4);
fillColors[2] = this.curFillColor.slice();

// Do the same for strokeColor.
const strokeColors = [];
for (m = 0; m < 3; m++) strokeColors.push([]);
strokeColors[0] = this.immediateMode.geometry.lineVertexColors.slice(-4);
strokeColors[2] = this.curStrokeColor.slice();

if (argLength === 4) {
this.isQuadratic = true;

w_x = [this.immediateMode._quadraticVertex[0], args[0], args[2]];
w_y = [this.immediateMode._quadraticVertex[1], args[1], args[3]];

// The ratio of the distance between the start point, the control-
// point, and the end point determines the intermediate color.
let d0 = Math.hypot(w_x[0]-w_x[1], w_y[0]-w_y[1]);
let d1 = Math.hypot(w_x[1]-w_x[2], w_y[1]-w_y[2]);
const totalLength = d0 + d1;
d0 /= totalLength;
for (k = 0; k < 4; k++) {
fillColors[1].push(
fillColors[0][k] * (1-d0) + fillColors[2][k] * d0
);
strokeColors[1].push(
strokeColors[0][k] * (1-d0) + strokeColors[2][k] * d0
);
}

for (i = 0; i < LUTLength; i++) {
_x =
w_x[0] * this._lookUpTableQuadratic[i][0] +
w_x[1] * this._lookUpTableQuadratic[i][1] +
w_x[2] * this._lookUpTableQuadratic[i][2];
_y =
w_y[0] * this._lookUpTableQuadratic[i][0] +
w_y[1] * this._lookUpTableQuadratic[i][1] +
w_y[2] * this._lookUpTableQuadratic[i][2];
// Interpolate colors using control points
this.curFillColor = [0, 0, 0, 0];
this.curStrokeColor = [0, 0, 0, 0];
_x = _y = 0;
for (m = 0; m < 3; m++) {
for (k = 0; k < 4; k++) {
this.curFillColor[k] +=
this._lookUpTableQuadratic[i][m] * fillColors[m][k];
this.curStrokeColor[k] +=
this._lookUpTableQuadratic[i][m] * strokeColors[m][k];
}
_x += w_x[m] * this._lookUpTableQuadratic[i][m];
_y += w_y[m] * this._lookUpTableQuadratic[i][m];
}
this.vertex(_x, _y);
}

// so that we leave currentColor with the last value the user set it to
this.curFillColor = fillColors[2];
this.curStrokeColor = strokeColors[2];
this.immediateMode._quadraticVertex[0] = args[2];
this.immediateMode._quadraticVertex[1] = args[3];
} else if (argLength === 6) {
Expand All @@ -1706,22 +1814,43 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) {
w_y = [this.immediateMode._quadraticVertex[1], args[1], args[4]];
w_z = [this.immediateMode._quadraticVertex[2], args[2], args[5]];

// The ratio of the distance between the start point, the control-
// point, and the end point determines the intermediate color.
let d0 = Math.hypot(w_x[0]-w_x[1], w_y[0]-w_y[1], w_z[0]-w_z[1]);
let d1 = Math.hypot(w_x[1]-w_x[2], w_y[1]-w_y[2], w_z[1]-w_z[2]);
const totalLength = d0 + d1;
d0 /= totalLength;
for (k = 0; k < 4; k++) {
fillColors[1].push(
fillColors[0][k] * (1-d0) + fillColors[2][k] * d0
);
strokeColors[1].push(
strokeColors[0][k] * (1-d0) + strokeColors[2][k] * d0
);
}

for (i = 0; i < LUTLength; i++) {
_x =
w_x[0] * this._lookUpTableQuadratic[i][0] +
w_x[1] * this._lookUpTableQuadratic[i][1] +
w_x[2] * this._lookUpTableQuadratic[i][2];
_y =
w_y[0] * this._lookUpTableQuadratic[i][0] +
w_y[1] * this._lookUpTableQuadratic[i][1] +
w_y[2] * this._lookUpTableQuadratic[i][2];
_z =
w_z[0] * this._lookUpTableQuadratic[i][0] +
w_z[1] * this._lookUpTableQuadratic[i][1] +
w_z[2] * this._lookUpTableQuadratic[i][2];
// Interpolate colors using control points
this.curFillColor = [0, 0, 0, 0];
this.curStrokeColor = [0, 0, 0, 0];
_x = _y = _z = 0;
for (m = 0; m < 3; m++) {
for (k = 0; k < 4; k++) {
this.curFillColor[k] +=
this._lookUpTableQuadratic[i][m] * fillColors[m][k];
this.curStrokeColor[k] +=
this._lookUpTableQuadratic[i][m] * strokeColors[m][k];
}
_x += w_x[m] * this._lookUpTableQuadratic[i][m];
_y += w_y[m] * this._lookUpTableQuadratic[i][m];
_z += w_z[m] * this._lookUpTableQuadratic[i][m];
}
this.vertex(_x, _y, _z);
}

// so that we leave currentColor with the last value the user set it to
this.curFillColor = fillColors[2];
this.curStrokeColor = strokeColors[2];
this.immediateMode._quadraticVertex[0] = args[3];
this.immediateMode._quadraticVertex[1] = args[4];
this.immediateMode._quadraticVertex[2] = args[5];
Expand Down
36 changes: 36 additions & 0 deletions test/unit/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,42 @@ suite('p5.RendererGL', function() {
assert.deepEqual(myp5.get(256, 2), [142, 136, 140, 255]);
assert.deepEqual(myp5.get(511, 2), [42, 36, 240, 255]);

done();
});
test('bezierVertex() should interpolate curFillColor', function(done) {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);

// start color: (255, 255, 255)
// end color: (255, 0, 0)
// Intermediate values are expected to be approximately half the value.

renderer.beginShape();
renderer.fill(255);
renderer.vertex(-128, -128);
renderer.fill(255, 0, 0);
renderer.bezierVertex(128, -128, 128, 128, -128, 128);
renderer.endShape();

assert.deepEqual(myp5.get(128, 128), [255, 129, 129, 255]);

done();
});
test('quadraticVertex() should interpolate curFillColor', function(done) {
const renderer = myp5.createCanvas(256, 256, myp5.WEBGL);

// start color: (255, 255, 255)
// end color: (255, 0, 0)
// Intermediate values are expected to be approximately half the value.

renderer.beginShape();
renderer.fill(255);
renderer.vertex(-128, -128);
renderer.fill(255, 0, 0);
renderer.quadraticVertex(256, 0, -128, 128);
renderer.endShape();

assert.deepEqual(myp5.get(128, 128), [255, 128, 128, 255]);

done();
});
});
Expand Down