目录
desmos绘制
webgl绘制
将线段坐标生成三角化坐标
处理斜接线段
处理圆角
尖角
先在desmos上面完成线条lineJoin绘制的,再将代码和公式转到js用webgl绘制.
desmos绘制
示例
desmos计角斜接角时,需要用到的一些函数。在desmos定义成公共函数,
这里填充线条的模式也是采用类似webgl中的三角形模式
webgl绘制
将线段坐标生成三角化坐标
/*** @param {number} lineWidth* @param {"bevel" | "miter" | "round"} lineJoin* @param {"butt" | "round" | "square"} lineCap*/function generateLineVertices(positions, lineWidth, lineJoin = 'miter', lineCap = 'butt', miterLimit = 10) {const vertices = [];const halfWidth = lineWidth / 2;for (let i = 0; i < positions.length - 1; i++) {const p1 = positions[i];const p2 = positions[i + 1];// 计算线段法线const dx = p2[0] - p1[0];const dy = p2[1] - p1[1];const len = Math.sqrt(dx * dx + dy * dy);const nx = -dy / len;const ny = dx / len;// 计算顶点偏移const offsetX = nx * halfWidth;const offsetY = ny * halfWidth;// 两条边const left1 = [p1[0] - offsetX, p1[1] - offsetY]; const right1 = [p1[0] + offsetX, p1[1] + offsetY];const left2 = [p2[0] - offsetX, p2[1] - offsetY]; const right2 = [p2[0] + offsetX, p2[1] + offsetY];// 将带状三角形的顶点加入数组vertices.push(...left1, ...right1, ...left2, ...right2);// 处理线端样式if (i === 0) {generateCapVertices(p1,p2, halfWidth, vertices, true, lineCap); // 起点圆头} if (i === positions.length - 2) {generateCapVertices(p2,p1, halfWidth, vertices, false, lineCap); // 终点圆头}// 处理线段连接样式if (i < positions.length - 2) {const p3 = positions[i + 2];handleLineJoin(p2, p1, p3, lineWidth, lineJoin, miterLimit, vertices);}}return new Float32Array(vertices);}
处理斜接线段
// 处理线段连接function handleLineJoin(current, prev, next, lineWidth, lineJoin, miterLimit, vertices) {// 根据 lineJoin 类型处理拐角连接if (lineJoin === 'miter') {handleMiterJoin(current, prev, next, lineWidth, vertices)} else if (lineJoin === 'bevel') {// handleBevelJoin(current, prev, next, lineWidth, vertices);} else if (lineJoin === 'round') {handleRoundJoin(current, prev, next, lineWidth, vertices);}}
处理圆角
// 处理圆角连接function handleRoundJoin(current, prev, next, lineWidth, vertices) {const normal1 = computeDirection(prev, current);const normal2 = computeDirection(next, current);const cos=computeDot(normal1,normal2)if(cos==1){return}const normal1_perpendicular=[-normal1[1],normal1[0]]const normal2_perpendicular=[-normal2[1],normal2[0]]let angle_1=Math.atan2(normal1_perpendicular[1],normal1_perpendicular[0])const halfWidth = lineWidth / 2;const steps = 10; const angleStep =Math.PI / steps; let startAngle = angle_1//-(Math.PI*0.5);let prevX,prevY;// 圆弧生成点for (let i = 0; i <= steps; i++) {const angle = startAngle + i * angleStep;const x = current[0] + Math.cos(angle) * halfWidth;const y = current[1] + Math.sin(angle) * halfWidth;if (i > 0) {vertices.push(current[0], current[1], prevX, prevY, x, y);}prevX = x;prevY = y;}}
尖角
// 尖角
function handleMiterJoin(current, prev, next, lineWidth, vertices) {const halfWidth = lineWidth / 2;const l1=subtract(prev,current)const l2=subtract(next,current)const d=computeCross(l1,l2)if(d===0){return}const l1_n=normalize(l1)const l2_n=normalize(l2)const lineAngle=computeDot(l1_n,l2_n)if(lineAngle==1){return}//const sign=const bisector=normalize(add(l1_n,l2_n))const ab_n=computeNormal(prev,current)const cos=computeDot(bisector,ab_n)if(cos===1){return}const miterLength=halfWidth/cos;const b1=add(current,multiplyScalar(bisector,miterLength))const b2=add(current,multiplyScalar(bisector,-miterLength))if(d<0){vertices.push(...b1)}else{vertices.push(...b2)}}