图形学 三次Hermite曲线绘制实现代码 javascript:es6+h5:canvas

2019/11/20UPD:更正起点终点切矢量的取均值

 

期末要考三次hermite曲线绘制编程题,熟悉一下,(虽然考的是opengl版本

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 
  4 <head>
  5     <meta charset="UTF-8">
  6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
  8     <link rel="stylesheet" type="text/css" href="2d.css">
  9     <script src="jquery.min.js"></script>
 10     <title>三次hermite曲线绘制</title>
 11     <style>
 12     </style>
 13 </head>
 14 
 15 <body>
 16     <canvas id="chart"></canvas>
 17     <script>
 18 
 19         class Point {
 20             constructor(x, y) {
 21                 this.x = Math.round(x) || 0;
 22                 this.y = Math.round(y) || 0;
 23             }
 24             set(x, y) {
 25                 this.x = Math.round(x) || 0;
 26                 this.y = Math.round(y) || 0;
 27             }
 28         }
 29         class Line {
 30             constructor(start, end) {
 31                 this.start = (start === undefined) ? new Point() : start;
 32                 this.end = (end === undefined) ? new Point() : end;
 33             }
 34             set(start, end) {
 35                 this.start = (start === undefined) ? new Point() : start;
 36                 this.end = (end === undefined) ? new Point() : end;
 37             }
 38         }
 39 
 40         let chart = document.getElementById("chart");
 41         let gridStep = 2;//网格大小,未画出
 42         let bigStep = 5;
 43         let gIsLButtonDown = false;
 44         let change = -1;//更改下标
 45         let p = new Array();//原始点
 46         let h = new Array();//基函数
 47         let pre_point = new Point();//拖动点
 48         const num = 1000;//每段直线画点个数
 49 
 50 
 51 
 52         function lpTodp(x) {
 53             return x * gridStep;
 54         }
 55 
 56         function dpTolp(x) {
 57             return Math.trunc(x / gridStep);
 58         }
 59 
 60         function drawPoint(ctx, x, y, color, step) {
 61             if (color)
 62                 ctx.fillStyle = color;
 63             if (!step)
 64                 step = gridStep;
 65             ctx.fillRect(lpTodp(x), lpTodp(y), step, step);
 66         }
 67 
 68         function drawLine(ctx, line) {//bresenham
 69             let dx, dy, upInc, downInc, x, y, d, isSwap = false;
 70             let tx, ty, e, dx2, dy2;
 71             let x1 = line.end.x, y1 = line.end.y;
 72             let x0 = line.start.x, y0 = line.start.y;
 73             dx = Math.abs(x1 - x0), dy = Math.abs(y1 - y0);
 74             if (dx < dy) {
 75                 [x0, y0] = [y0, x0];
 76                 [x1, y1] = [y1, x1];
 77                 [dx, dy] = [dy, dx];
 78                 isSwap = true;
 79             }
 80             tx = x0 <= x1 ? 1 : -1;
 81             ty = y0 <= y1 ? 1 : -1;
 82             e = -dx, x = x0, y = y0;
 83             dx2 = dx * 2, dy2 = dy * 2;
 84             while (x != x1) {
 85                 isSwap ? drawPoint(ctx, y, x) : drawPoint(ctx, x, y);
 86                 x += tx, e += dy2;
 87                 if (e > 0) {
 88                     y += ty;
 89                     e -= dx2;
 90                 }
 91             }
 92             isSwap ? drawPoint(ctx, y, x) : drawPoint(ctx, x, y);
 93         }
 94         function draw() {
 95             if (!chart.getContext) return;
 96             const ctx = chart.getContext("2d");
 97             //开始代码
 98             ctx.fillStyle = "rgb(255, 255, 255)";
 99             //绘制矩形
100 
101             const w = $("#chart").width();
102             const h = $("#chart").height();
103 
104             ctx.fillRect(0, 0, w, h);
105 
106             for (let q of p)
107                 drawPoint(ctx, q.x, q.y, "#ff0000", bigStep);
108             ctx.fillStyle = "rgba(0, 0, 0,0.5)";
109             drawHermite();
110         }
111 
112 
113         function getBaseFunction(t) {//基函数计算
114             const t2 = t * t;
115             const t3 = t2 * t;
116             let cur = new Array(4);
117             cur[0] = 2 * t3 - 3 * t2 + 1;
118             cur[1] = -2 * t3 + 3 * t2;
119             cur[2] = t3 - 2 * t2 + t;
120             cur[3] = t3 - t2;
121             return cur;
122         }
123 
124         function drawHermite() {
125             if (p.length < 4)
126                 return;
127             h = new Array();//置空
128             const delta = 1.0 / num;
129             const ctx = chart.getContext("2d");
130             for (let i = 1; i < num; i++) {
131                 const t = i * 1.0 / num;
132                 h.push(getBaseFunction(t));
133             }
134             const m0 = new Point(p[1].x - p[0].x, p[1].y - p[0].y);
135             const m1 = new Point(p[2].x - p[1].x, p[2].y - p[1].y);
136             const m2 = new Point(p[3].x - p[2].x, p[3].y - p[2].y);
137             const k0 = new Point((m0.x + m1.x) / 2, (m0.y + m1.y) / 2);//取均值作为切线
138             const k1 = new Point((m1.x + m2.x) / 2, (m1.y + m2.y) / 2);
139             drawLine(ctx, new Line(p[0], p[1]));
140             let pre = undefined;
141             for (let i = 0; i < num - 1; i++) {
142                 const x = p[1].x * h[i][0] + p[2].x * h[i][1] + k0.x * h[i][2] + k1.x * h[i][3];
143                 const y = p[1].y * h[i][0] + p[2].y * h[i][1] + k0.y * h[i][2] + k1.y * h[i][3];
144                 const now = new Point(x, y);
145                 if (pre)
146                     drawLine(ctx, new Line(pre, now));
147                 pre = now;
148             }
149             drawLine(ctx, new Line(pre, p[2]));
150             drawLine(ctx, new Line(p[2], p[3]));
151         }
152 
153         function getPoint(x, y) {//获取坐标
154             const cur = new Point(x, y);
155             if (change == -1) {//获取拖动坐标
156                 for (let i = 0; i < p.length; i++)
157                     if (p[i].x == cur.x && p[i].y == cur.y) {
158                         pre_point = p[i];
159                         change = i;
160                     }
161             }
162             if (p.length == 4)
163                 return;
164             p.push(cur);
165         }
166         function movePoint(x, y, f) {
167             if (change != -1)
168                 pre_point.set(x, y);
169             if (f)
170                 change = -1;
171         }
172         function resizeCanvas() {
173             // 将chart的大小设置为窗口大小, 但是为了避免偶尔溢出, 略微减去4个像素
174             $('#chart').attr("width", $(window).get(0).innerWidth - 4);
175             $('#chart').attr("height", $(window).get(0).innerHeight - 4);
176             draw();
177         };
178 
179         $(function () {
180             $(window).resize(resizeCanvas);
181             resizeCanvas();
182 
183             $('canvas').mousedown(function (e) {
184                 var x = e.originalEvent.x || e.originalEvent.layerX || 0;
185                 var y = e.originalEvent.y || e.originalEvent.layerY || 0;
186                 x = dpTolp(x);
187                 y = dpTolp(y);
188                 gIsLButtonDown = true;
189                 getPoint(x, y);
190                 draw();
191             });
192 
193             $('canvas').mousemove(function (e) {
194                 if (gIsLButtonDown) {
195                     let x = e.originalEvent.x || e.originalEvent.layerX || 0;
196                     let y = e.originalEvent.y || e.originalEvent.layerY || 0;
197                     x = dpTolp(x);
198                     y = dpTolp(y);
199                     movePoint(x, y);
200                     draw();
201                 }
202             });
203 
204             $('canvas').mouseup(function (e) {
205                 if (gIsLButtonDown) {
206                     let x = e.originalEvent.x || e.originalEvent.layerX || 0;
207                     let y = e.originalEvent.y || e.originalEvent.layerY || 0;
208                     x = dpTolp(x);
209                     y = dpTolp(y);
210                     movePoint(x, y, 1);
211                     draw();
212                     gIsLButtonDown = false;
213                 }
214             });
215         });
216     </script>
217 </body>
218 
219 </html>
View Code

 

实现效果(可拖拽端点)

posted @ 2019-11-18 23:11  mool  阅读(659)  评论(0编辑  收藏  举报