Calculating all triangle pixels. It would be one of classical polygon filling algorithm.
Real task is inversed — find out used in the triangle colors, but it's the same.
Polygon drawing
Used a simple approach that it is easy to fill pixels between left and right bounds, so solution would be:
- sort 3 points by Y coordinate and get P1, P2 and P3
- find out the point (Pz) on the edge between P1 and P3 that have the same Y with P2
- draw top triangle P1-P2-Pz by filling lines between P1-P2 and P2-Pz slopes
- draw bottom triangle P2-Pz-P3 by filling lines between P2-Pz and P3-Pz slopes
Corner cases:
- P2.y equal to P1.y → Pz = P1; skip step 3
- P2.y equal to P3.y → Pz = P3; skip step 4
Combined solution
var p1 = new Point(30,30);
var p2 = new Point(130,40);
var p3 = new Point(50, 90);
var triangle = function(p1, p2, p3){
[p1, p2, p3] = [p1,p2,p3].sort((a,b)=>a.y-b.y);
if (p2.y === p3.y) {
flatBottom(p1, p2, p3, 0x009933);
} else if (p1.y === p2.y) {
flatTop(p1, p2, p3, 0x003399);
} else {
var p4y = p1.x + (p2.y - p1.y)/(p3.y - p1.y)*(p3.x - p1.x);
var p4 = new Point(p4y, p2.y);
flatBottom(p1, p2, p4, 0x00bb99);
flatTop(p2, p4, p3, 0xbb6600);
}
};
var flatBottom = function(p1, p2, p3, color){
var invslope1 = (p2.x - p1.x) / (p2.y - p1.y),
invslope2 = (p3.x - p1.x) / (p3.y - p1.y),
curx1 = p1.x,
curx2 = p1.x,
scanlineY = p1.y,
scanlineTo = p2.y;
for (; scanlineY <= scanlineTo; scanlineY++){
line(curx1|0, scanlineY, curx2|0, scanlineY, color);
curx1 += invslope1;
curx2 += invslope2;
}
},
flatTop = function(p1, p2, p3, color){
var invslope1 = (p3.x - p1.x) / (p3.y - p1.y),
invslope2 = (p3.x - p2.x) / (p3.y - p2.y),
curx1 = p3.x,
curx2 = p3.x,
scanlineY = p3.y,
scanlineTo = p1.y;
for (; scanlineY > scanlineTo; scanlineY--){
line(curx1|0, scanlineY, curx2|0, scanlineY, color);
curx1 -= invslope1;
curx2 -= invslope2;
}
};
var c = 0;
move = function(){
c+=0.01;
clear();
var center = new Point(w/2,h/2);
p1 = mouse.clamp(0);
p2 = new Point(0, 40).rotate(c).add(center).clamp(0);
p3 = new Point(0, 60).rotate(c/2+3).add(center).clamp(0);
triangle(p1, p2, p3, 0x999900);
[p1,p2,p3].forEach((p, i)=>{
point(p.x, p.y, 0xff0000);
circle(p.x, p.y, 2, 0xff0000)
print(i+1 +' '+p.toString(0), p.x-8,p.y-5, 0x007700)
});
};
Colorized
Moved this solution to my pixel drawing library and give it ability to take color as function. Just look at this fluffy kitten.
var p1 = new Point(30,30);
var p2 = new Point(130,40);
var p3 = new Point(50, 90);
var c = 0;
move = function(){
c+=0.01;
clear();
var center = new Point(w/2,h/2);
p1 = mouse.clamp(0);
p2 = new Point(0, 40).rotate(c).add(center).clamp(0);
p3 = new Point(0, 60).rotate(c/2+3).add(center).clamp(0);
p1.maxDistance = Math.max( p1.distance(p2), p1.distance(p3) );
p2.maxDistance = Math.max( p2.distance(p1), p2.distance(p3) );
p3.maxDistance = Math.max( p3.distance(p1), p3.distance(p2) );
var tmp = new Point();
var clr = function(x, y, p){
tmp.x = x; tmp.y = y;
return (tmp.distance(p)*256/p.maxDistance) |0;
};
triangle(p1, p2, p3, (x,y) => color(clr(x,y,p1), clr(x,y,p2), clr(x,y,p3)));
var center = p1.addClone(p2).add(p3).div(3);
eye(center.subClone(5,0));
eye(center.addClone(5,0));
mouth(center.addClone(0,6));
//nose(center.addClone(0,3));
[p1,p2,p3].forEach((p, i)=>{
point(p.x, p.y, 0xff0000);
circle(p.x, p.y, 2, 0xff0000)
print(i+1, 0x007700)
});
};
var eye = function(p){
p.clamp();
circle(p.x, p.y, 1,0xffffff);
circle(p.x, p.y, 2,0xffffff);
circle(p.x, p.y, 3,0x000001);
var lookDirection = new Point(1.5,0).rotate(p.getAngle(mouse)),
lookAt1 = p.addClone(lookDirection);
for(var i = 0.5; i < 2; i+=0.5)
circle(lookAt1.x, lookAt1.y, i, 0x000001);
};
var mouth = function(p){
bitmap(
`
# #
# #
# # #
### ###
`, p.x-3, p.y, 0x000001
);
};