目录
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)
}
}