source: node_modules/d3-geo/src/clip/circle.js@ ba17441

Last change on this file since ba17441 was e4c61dd, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Prototype 1.1

  • Property mode set to 100644
File size: 5.7 KB
Line 
1import {cartesian, cartesianAddInPlace, cartesianCross, cartesianDot, cartesianScale, spherical} from "../cartesian.js";
2import {circleStream} from "../circle.js";
3import {abs, cos, epsilon, pi, radians, sqrt} from "../math.js";
4import pointEqual from "../pointEqual.js";
5import clip from "./index.js";
6
7export default function(radius) {
8 var cr = cos(radius),
9 delta = 2 * radians,
10 smallRadius = cr > 0,
11 notHemisphere = abs(cr) > epsilon; // TODO optimise for this common case
12
13 function interpolate(from, to, direction, stream) {
14 circleStream(stream, radius, delta, direction, from, to);
15 }
16
17 function visible(lambda, phi) {
18 return cos(lambda) * cos(phi) > cr;
19 }
20
21 // Takes a line and cuts into visible segments. Return values used for polygon
22 // clipping: 0 - there were intersections or the line was empty; 1 - no
23 // intersections 2 - there were intersections, and the first and last segments
24 // should be rejoined.
25 function clipLine(stream) {
26 var point0, // previous point
27 c0, // code for previous point
28 v0, // visibility of previous point
29 v00, // visibility of first point
30 clean; // no intersections
31 return {
32 lineStart: function() {
33 v00 = v0 = false;
34 clean = 1;
35 },
36 point: function(lambda, phi) {
37 var point1 = [lambda, phi],
38 point2,
39 v = visible(lambda, phi),
40 c = smallRadius
41 ? v ? 0 : code(lambda, phi)
42 : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
43 if (!point0 && (v00 = v0 = v)) stream.lineStart();
44 if (v !== v0) {
45 point2 = intersect(point0, point1);
46 if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2))
47 point1[2] = 1;
48 }
49 if (v !== v0) {
50 clean = 0;
51 if (v) {
52 // outside going in
53 stream.lineStart();
54 point2 = intersect(point1, point0);
55 stream.point(point2[0], point2[1]);
56 } else {
57 // inside going out
58 point2 = intersect(point0, point1);
59 stream.point(point2[0], point2[1], 2);
60 stream.lineEnd();
61 }
62 point0 = point2;
63 } else if (notHemisphere && point0 && smallRadius ^ v) {
64 var t;
65 // If the codes for two points are different, or are both zero,
66 // and there this segment intersects with the small circle.
67 if (!(c & c0) && (t = intersect(point1, point0, true))) {
68 clean = 0;
69 if (smallRadius) {
70 stream.lineStart();
71 stream.point(t[0][0], t[0][1]);
72 stream.point(t[1][0], t[1][1]);
73 stream.lineEnd();
74 } else {
75 stream.point(t[1][0], t[1][1]);
76 stream.lineEnd();
77 stream.lineStart();
78 stream.point(t[0][0], t[0][1], 3);
79 }
80 }
81 }
82 if (v && (!point0 || !pointEqual(point0, point1))) {
83 stream.point(point1[0], point1[1]);
84 }
85 point0 = point1, v0 = v, c0 = c;
86 },
87 lineEnd: function() {
88 if (v0) stream.lineEnd();
89 point0 = null;
90 },
91 // Rejoin first and last segments if there were intersections and the first
92 // and last points were visible.
93 clean: function() {
94 return clean | ((v00 && v0) << 1);
95 }
96 };
97 }
98
99 // Intersects the great circle between a and b with the clip circle.
100 function intersect(a, b, two) {
101 var pa = cartesian(a),
102 pb = cartesian(b);
103
104 // We have two planes, n1.p = d1 and n2.p = d2.
105 // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
106 var n1 = [1, 0, 0], // normal
107 n2 = cartesianCross(pa, pb),
108 n2n2 = cartesianDot(n2, n2),
109 n1n2 = n2[0], // cartesianDot(n1, n2),
110 determinant = n2n2 - n1n2 * n1n2;
111
112 // Two polar points.
113 if (!determinant) return !two && a;
114
115 var c1 = cr * n2n2 / determinant,
116 c2 = -cr * n1n2 / determinant,
117 n1xn2 = cartesianCross(n1, n2),
118 A = cartesianScale(n1, c1),
119 B = cartesianScale(n2, c2);
120 cartesianAddInPlace(A, B);
121
122 // Solve |p(t)|^2 = 1.
123 var u = n1xn2,
124 w = cartesianDot(A, u),
125 uu = cartesianDot(u, u),
126 t2 = w * w - uu * (cartesianDot(A, A) - 1);
127
128 if (t2 < 0) return;
129
130 var t = sqrt(t2),
131 q = cartesianScale(u, (-w - t) / uu);
132 cartesianAddInPlace(q, A);
133 q = spherical(q);
134
135 if (!two) return q;
136
137 // Two intersection points.
138 var lambda0 = a[0],
139 lambda1 = b[0],
140 phi0 = a[1],
141 phi1 = b[1],
142 z;
143
144 if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
145
146 var delta = lambda1 - lambda0,
147 polar = abs(delta - pi) < epsilon,
148 meridian = polar || delta < epsilon;
149
150 if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;
151
152 // Check that the first point is between a and b.
153 if (meridian
154 ? polar
155 ? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon ? phi0 : phi1)
156 : phi0 <= q[1] && q[1] <= phi1
157 : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
158 var q1 = cartesianScale(u, (-w + t) / uu);
159 cartesianAddInPlace(q1, A);
160 return [q, spherical(q1)];
161 }
162 }
163
164 // Generates a 4-bit vector representing the location of a point relative to
165 // the small circle's bounding box.
166 function code(lambda, phi) {
167 var r = smallRadius ? radius : pi - radius,
168 code = 0;
169 if (lambda < -r) code |= 1; // left
170 else if (lambda > r) code |= 2; // right
171 if (phi < -r) code |= 4; // below
172 else if (phi > r) code |= 8; // above
173 return code;
174 }
175
176 return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
177}
Note: See TracBrowser for help on using the repository browser.