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:

  1. sort 3 points by Y coordinate and get P1, P2 and P3
  2. find out the point (Pz) on the edge between P1 and P3 that have the same Y with P2
  3. draw top triangle P1-P2-Pz by filling lines between P1-P2 and P2-Pz slopes
  4. draw bottom triangle P2-Pz-P3 by filling lines between P2-Pz and P3-Pz slopes

Corner cases:

  1. P2.y equal to P1.y → Pz = P1; skip step 3
  2. 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 ); };