まずはポリゴン・法線ベクトルで調べるべし.
https://www.google.co.jp/search?client=ubuntu&channel=fs&q=%E3%83%9D%E3%83%AA%E3%82%B4%E3%83%B3+%E6%B3%95%E7%B7%9A%E3%83%99%E3%82%AF%E3%83%88%E3%83%AB&ie=utf-8&oe=utf-8&gfe_rd=cr&dcr=0&ei=R4HOWtu5O6_98Ae4wKLACQ
三点から得られたポリゴン面に垂直に立つ法線ベクトルを求め, それを真上から射影すればその方向と傾きが得られる.
例えばここを参考としてスクリプトを組んで見る.
http://www.dstorm.co.jp/dsproducts/developers/Technical_Data/CalcNormal.html
HTML
1<canvas id="canvas" width="400" height="400" style="background-color:yellow;"></canvas>
JavaScript
1"use strict";
2{
3 function getNormal(a, b){
4 const vec = [
5 a[1] * b[2] - a[2] * b[1],
6 a[2] * b[0] - a[0] * b[2],
7 a[0] * b[1] - a[1] * b[0]
8 ];
9 const norm = Math.sqrt(vec.reduce((prev, val) => prev + val * val, 0));
10 return vec.map((val, i) => vec[i] /= norm);
11 }
12 function plot(table){
13 const ctx = canvas.getContext("2d");
14 ctx.strokeStyle = ctx.fillStyle = "red";
15 ctx.lineWidth = 2;
16 const unit = 100, arrow = 50;
17 const marker = new Path2D("M0,-5L5,0 0 5z");
18 const rows = table.length, cols = table[0].length;
19 for(let y = 0; y < rows-1; y++){
20 for(let x = 0; x < cols-1; x++){
21 ctx.save();
22 //ベクトルの描画基準
23 ctx.translate(unit * (x + 0.5), unit * (y + 0.5));
24 //上半三角
25 const normalT = getNormal(
26 [1, 0, table[y][x+1] - table[y][x]],
27 [0, 1, table[y+1][x] - table[y][x]]
28 );
29 //下半三角
30 const normalB = getNormal(
31 [-1, 0, table[y+1][x] - table[y+1][x+1]],
32 [0, -1, table[y][x+1] - table[y+1][x+1]]
33 );
34 //平均
35 const normal = normalT.map((val, i) => (val + normalB[i])/2)
36 ctx.beginPath();
37 ctx.moveTo(0,0);
38 ctx.lineTo(normal[0] * arrow, normal[1] * arrow);
39 ctx.stroke();
40 //marker
41 ctx.translate(normal[0] * arrow, normal[1] * arrow);
42 ctx.rotate(Math.atan2(normal[1], normal[0]));
43 ctx.fill(marker);
44 ctx.restore();
45 }
46 }
47 }
48 plot([
49 [0, 0, 1, 0, 0],
50 [1, 1, 1, 1, 1],
51 [2, 3, 2, 4, 2],
52 [3, 3, 3, 3, 3],
53 [4, 3, 5, 6, 7]
54 ]);
55}
NOTE:
途中で求めた法線ベクトル二つを足して簡易的に2で割っているけれど, 本当はもっと適した算出法があるかもしれない.(この操作が引き起こす実用上の問題は不明)