basant307/AI_Governance_Project
048
1/**2 * Explained by Blindman67 at https://stackoverflow.com/a/44856925/110288283 */4 5 6// Declare reused variable to avoid reallocating variables every time the function is called7let x, y, v1 = {}, v2 = {}, sinA, sinA90, radDirection, drawDirection, angle, halfAngle, cRadius, lenOut, radius, limit;8let startX, startY, stopX, stopY;9let lastPoint;10 11// convert 2 points into vector form, polar form, and normalised12const asVec = function (p, pp, v) {13 v.x = pp.x - p.x;14 v.y = pp.y - p.y;15 v.len = Math.sqrt(v.x * v.x + v.y * v.y);16 v.nx = v.x / v.len;17 v.ny = v.y / v.len;18 v.ang = Math.atan2(v.ny, v.nx);19};20 21const invertVec = function (originalV, invertedV) {22 invertedV.x = originalV.x * -1;23 invertedV.y = originalV.y * -1;24 invertedV.nx = originalV.nx * -1;25 invertedV.ny = originalV.ny * -1;26 invertedV.ang = originalV.ang > 0 ? -(Math.PI - originalV.ang) : Math.PI + originalV.ang;27};28 29const calcCornerArc = (previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius) => {30 //-----------------------------------------31 // Part 132 previousPoint !== lastPoint ? asVec(currentPoint, previousPoint, v1) : invertVec(v2, v1); // Avoid recalculating vec if it is the invert of the last one calculated33 asVec(currentPoint, nextPoint, v2);34 sinA = v1.nx * v2.ny - v1.ny * v2.nx;35 sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny;36 angle = Math.asin(Math.max(-1, Math.min(1, sinA)));37 if (Math.abs(angle) < 1e-6) {38 x = currentPoint.x;39 y = currentPoint.y;40 cRadius = radius = 0;41 return;42 }43 //-----------------------------------------44 radDirection = 1;45 drawDirection = false;46 if (sinA90 < 0) {47 if (angle < 0) {48 angle = Math.PI + angle;49 } else {50 angle = Math.PI - angle;51 radDirection = -1;52 drawDirection = true;53 }54 } else {55 if (angle > 0) {56 radDirection = -1;57 drawDirection = true;58 }59 }60 if (currentPoint.radius !== undefined) {61 radius = currentPoint.radius;62 } else {63 radius = radiusMax;64 }65 //-----------------------------------------66 // Part 267 halfAngle = angle / 2;68 //-----------------------------------------69 70 71 limit = Math.min(v1.len / 2, v2.len / 2);72 73 if (isArcRadius) {74 //-----------------------------------------75 // Part 376 lenOut = Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle));77 78 //-----------------------------------------79 // Special part A80 if (lenOut > limit) {81 lenOut = limit;82 cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));83 } else {84 cRadius = radius;85 }86 } else {87 lenOut = Math.min(limit, radius);88 cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));89 }90 //-----------------------------------------91 92 93 94 //-----------------------------------------95 // Part 496 stopX = currentPoint.x + v2.nx * lenOut;97 stopY = currentPoint.y + v2.ny * lenOut;98 //-----------------------------------------99 // Part 5100 x = stopX - v2.ny * cRadius * radDirection;101 y = stopY + v2.nx * cRadius * radDirection;102 //-----------------------------------------103 // Additional Part : calculate start point E104 startX = currentPoint.x + v1.nx * lenOut;105 startY = currentPoint.y + v1.ny * lenOut;106 107 // Save last point to avoid recalculating vector when not needed108 lastPoint = currentPoint;109};110 111 112/**113 * Draw round corner from a point and its previous and next neighbours in a path114 *115 * @param ctx :CanvasRenderingContext2D116 * @param previousPoint {{x: number, y:number, radius: number?}}117 * @param currentPoint {{x: number, y:number, radius: number?}}118 * @param nextPoint {{x: number, y:number, radius: number?}}119 * @param radiusMax :number120 * @param isArcRadius :boolean121 */122export function drawRoundCorner(ctx, previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius) {123 calcCornerArc(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius);124 if (cRadius === 0) ctx.lineTo(currentPoint.x, currentPoint.y);125 else ctx.arc(x, y, cRadius, v1.ang + Math.PI / 2 * radDirection, v2.ang - Math.PI / 2 * radDirection, drawDirection);126 127}128 129/**130 * Draw corner provided by {@link getRoundCorner}131 *132 * @param ctx :CanvasRenderingContext2D133 * @param roundCorner {{cx:number, cy:number, radius:number, endAngle: number, startAngle: number, counterClockwise: boolean}}134 */135export function drawPreparedRoundCorner(ctx, roundCorner) {136 if (roundCorner.radius === 0) ctx.lineTo(roundCorner.cx, roundCorner.cy);137 else ctx.arc(roundCorner.cx, roundCorner.cy, roundCorner.radius, roundCorner.startAngle, roundCorner.endAngle, roundCorner.counterClockwise);138}139 140/**141 * Get round corner from a point and its previous and next neighbours in a path142 *143 * @param previousPoint {{x: number, y:number, radius: number?}}144 * @param currentPoint {{x: number, y:number, radius: number?}}145 * @param nextPoint {{x: number, y:number, radius: number?}}146 * @param radiusMax :number147 * @param isArcRadius :boolean148 * @return {{149 * cx:number, cy:number, radius:number,150 * startX:number, startY:number,151 * stopX:number, stopY: number,152 * endAngle: number, startAngle: number, counterClockwise: boolean153 * }}154 */155export function getRoundCorner(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius = true) {156 if (radiusMax === 0 || currentPoint.radius === 0) return {157 cx: currentPoint.x,158 cy: currentPoint.y,159 radius: 0,160 startX: currentPoint.x,161 startY: currentPoint.y,162 stopX: currentPoint.x,163 stopY: currentPoint.y,164 startAngle: undefined,165 endAngle: undefined,166 counterClockwise: undefined167 };168 169 calcCornerArc(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius);170 return {171 cx: x, cy: y, radius: cRadius,172 startX, startY,173 stopX, stopY,174 startAngle: v1.ang + Math.PI / 2 * radDirection,175 endAngle: v2.ang - Math.PI / 2 * radDirection,176 counterClockwise: drawDirection177 };178}179 