UNPKG

twreporter-react

Version:

React-Redux site for The Reporter Foundation in Taiwan

156 lines (154 loc) 5.66 kB
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Arc3</title> <style> svg { position: absolute; top: 0; left: 0; } </style> <script src="dist/snap.svg-min.js"></script> </head> <body> <script> function intersect(x1, y1, x2, y2, x3, y3, x4, y4) { var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); if (!denominator) { return; } var px = nx / denominator, py = ny / denominator; return {x: px, y: py}; } function calc_bisect_perp(x1, y1, x2, y2) { var dx = x2 - x1, dy = y2 - y1; if (dy == 0) { return [x1 + dx / 2, 0, x1 + dx / 2, 1]; } else if (dx == 0) { return [0, y1 + dy / 2, 1, y1 + dy / 2]; } else { var m, b, x3, y3; m = -dx / dy; x3 = x1 + dx / 2; y3 = y1 + dy / 2; b = y3 - m * x3; return [0, b, 1, m + b]; } } function len(x1, y1, x2, y2) { return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); } function angle(x1, y1, x2, y2) { if (x1 == x2) { if (y2 < y1) { return 270; } else { return 90; } } else if (y1 == y2) { if (x2 < x1) { return 180; } else { return 0; } } else { var angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI; if (angle < 0) { angle += 360; } return angle; } } function arc3(x1, y1, x2, y2, x3, y3) { var out = {}; if (x1 == x2 && y1 == y2 || x3 == x2 && y3 == y2) { out.path = "L" + [x3, y3]; return out; } if (x1 == x3 && y1 == y3) { r = len(x1, y1, x2, y2) / 2; out.path = "A" + [r, r, 0, 0, 0, x2, y2] + "A" + [r, r, 0, 0, 0, x1, y1]; return out; } var bp1 = calc_bisect_perp(x1, y1, x2, y2), bp2 = calc_bisect_perp(x2, y2, x3, y3), inter = intersect(bp1[0], bp1[1], bp1[2], bp1[3], bp2[0], bp2[1], bp2[2], bp2[3]), ang_start = inter && angle(inter.x, inter.y, x1, y1), ang_int = inter && angle(inter.x, inter.y, x2, y2), ang_end = inter && angle(inter.x, inter.y, x3, y3), angl = ang_end - ang_start; if (ang_int < ang_start) { if (ang_start < ang_end) { angl -= 360; } else if (ang_int < ang_end) { angl += 360; } } else { if (ang_end < ang_start) { angl += 360; } else if (ang_end < ang_int) { angl -= 360; } } if (inter) { var r = len(x1, y1, inter.x, inter.y); out.cx = inter.x; out.cy = inter.y; out.a1 = ang_start; out.a2 = ang_end; out.r = r; out.f1 = +(Math.abs(angl) > 180); out.f2 = +(angl > 0); out.path = "A" + [safeRound(r), safeRound(r), 0, out.f1, out.f2, x3, y3]; } else { out.path = "L" + [x3, y3]; } return out; } function safeRound(n) { var match = (n + "").match(/^\d*\.[^0]*(?=0|$)/); return match ? +match[0] : n; } function findDotAtBezierSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { var t1 = 1 - t, pow = Math.pow; return { x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y }; } function asArc(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { var m = findDotAtBezierSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, .5), arc = arc3(p1x, p1y, m.x, m.y, p2x, p2y); for (var i = 1; i < 10; i++) { if (i != 5) { var dot = findDotAtBezierSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / 10); if (Math.abs(len(arc.cx, arc.cy, dot.x, dot.y) - arc.r) > .5) { return null; } } } return arc.path; } var r = Snap(800, 600); r.path("M400,100c55.22,0,100,44.78,100,100").attr({ fill: "none", stroke: "#000" }); r.path("M400,100" + asArc(400, 100, 455.22, 100, 500, 144.78, 500, 200)).attr({ fill: "none", stroke: "#c00", strokeWidth: 4, strokeOpacity: .5 }); console.log(asArc(400, 100, 455.22, 100, 500, 144.78, 500, 200)); console.log("M400,100c55.22,0,100,44.78,100,100"); </script> </body> </html>