Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rhombic & deltoidal #60

Merged
merged 9 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,19 @@ The cubic projection.

[<img src="https://raw.githubusercontent.com/d3/d3-geo-polygon/main/test/snapshots/dodecahedral.png" width="480" height="250">](https://observablehq.com/@fil/dodecahedral-projection)

The dodecahedral projection.
The pentagonal dodecahedral projection.

<a href="#geoRhombic" name="geoRhombic">#</a> d3.<b>geoRhombic</b>() · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/polyhedral/rhombic.js), [Examples](https://observablehq.com/d/881a8431e638b408)

[<img src="https://raw.githubusercontent.com/d3/d3-geo-polygon/main/test/snapshots/rhombic.png" width="480" height="250">](https://observablehq.com/d/881a8431e638b408)

The rhombic dodecahedral projection.

<a href="#geoDeltoidal" name="geoDeltoidal">#</a> d3.<b>geoDeltoidal</b>() · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/polyhedral/deltoidal.js), [Examples](https://observablehq.com/d/881a8431e638b408)

[<img src="https://raw.githubusercontent.com/d3/d3-geo-polygon/main/test/snapshots/deltoidal.png" width="480" height="250">](https://observablehq.com/d/881a8431e638b408)

The deltoidal hexecontahedral projection.

<a href="#geoIcosahedral" name="geoIcosahedral">#</a> d3.<b>geoIcosahedral</b>() · [Source](https://github.com/d3/d3-geo-polygon/blob/main/src/icosahedral.js), [Examples](https://observablehq.com/@fil/icosahedral-projections)

Expand Down
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
{
"name": "Philippe Rivière",
"url": "https://visionscarto.net"
},
{
"name": "Enrico Spinielli",
"url": "https://enrico.spinielli.net/"
},
{
"name": "Ronnie Bathoorn",
"url": "https://github.com/bathoorn"
}
],
"type": "module",
Expand Down
6 changes: 3 additions & 3 deletions src/polyhedral/butterfly.js → src/butterfly.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {geoCentroid as centroid, geoGnomonic as gnomonic} from "d3-geo";
import {pi} from "../math.js";
import polyhedral from "./index.js";
import octahedron from "./octahedron.js";
import {pi} from "./math.js";
import polyhedral from "./polyhedral/index.js";
import octahedron from "./polyhedral/octahedron.js";

export default function(faceProjection = ((face) => {
const c = centroid({type: "MultiPoint", coordinates: face});
Expand Down
5 changes: 2 additions & 3 deletions src/cartesian.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ export function cartesianScale(vector, k) {
return [vector[0] * k, vector[1] * k, vector[2] * k];
}

// TODO return d
export function cartesianNormalizeInPlace(d) {
export function cartesianNormalize(d) {
const l = hypot(d[0], d[1], d[2]);
d[0] /= l, d[1] /= l, d[2] /= l;
return [d[0] / l, d[1] / l, d[2] / l];
}

export function cartesianEqual(a, b) {
Expand Down
13 changes: 7 additions & 6 deletions src/clip/polygon.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ function clipLine(segments, pointVisible) {
point: (lambda, phi, close) => {
if (cos(lambda) == -1) lambda -= sign(sin(lambda)) * 1e-5; // move away from -180/180 https://github.com/d3/d3-geo/pull/108#issuecomment-323798937
if (close) (lambda = lambda00), (phi = phi00);
let point = cartesian([lambda, phi]);
let point = cartesian([lambda * 0.9999999999, phi + 1e-14]);
let v = v0;
if (point0) {
const intersections = [];
Expand All @@ -178,7 +178,7 @@ function clipLine(segments, pointVisible) {
) {
const t = 1e-4;
lambda = ((lambda + 3 * pi + randsign(i, j) * t) % (2 * pi)) - pi;
phi = min(pi / 2 - 1e-4, max(1e-4 - pi / 2, phi + randsign(i, j) * t));
phi = min(pi / 2 - t, max(t - pi / 2, phi + randsign(i, j) * t));
segment = new intersectSegment(point0, (point = cartesian([lambda, phi])));
(i = -1), --j;
intersections.length = 0;
Expand All @@ -188,9 +188,9 @@ function clipLine(segments, pointVisible) {
intersection.distance = clipPolygonDistance(point0, intersection);
intersection.index = i;
intersection.t = clipPolygonDistance(s.from, intersection);
(intersection[0] = sph[0]),
(intersection[1] = sph[1]),
intersection.pop();
intersection[0] = sph[0];
intersection[1] = sph[1];
delete intersection[2];
intersections.push(intersection);
}
}
Expand Down Expand Up @@ -237,7 +237,8 @@ function clipLine(segments, pointVisible) {
v00 = v = pointVisible((lambda00 = lambda), (phi00 = phi));
if (v) line = [], line.push([lambda, phi]);
}
(point0 = point), (v0 = v);
point0 = point;
v0 = v;
},
// Rejoin first and last segments if there were intersections and the first
// and last points were visible.
Expand Down
37 changes: 36 additions & 1 deletion src/collignon.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// code duplicated from d3-geo-projection
import {geoCentroid as centroid, geoProjection as projection} from "d3-geo";
import {asin, pi, sin, sqrt, sqrtPi} from "./math.js";
import polyhedral from "./polyhedral/index.js";
import octahedron from "./polyhedral/octahedron.js";

// code duplicated from d3-geo-projection
export function collignonRaw(lambda, phi) {
const alpha = sqrt(1 - sin(phi));
return [(2 / sqrtPi) * lambda * alpha, sqrtPi * (1 - alpha)];
Expand All @@ -10,3 +13,35 @@ collignonRaw.invert = function(x, y) {
const lambda = (y / sqrtPi - 1);
return [lambda ? x * sqrt(pi) / lambda / 2 : 0, asin(1 - lambda ** 2)];
};


const kx = 2 / sqrt(3);

function collignonK(a, b) {
const p = collignonRaw(a, b);
return [p[0] * kx, p[1]];
}

collignonK.invert = (x,y) => collignonRaw.invert(x / kx, y);

export default function(faceProjection = (face) => {
const c = centroid({type: "MultiPoint", coordinates: face});
return projection(collignonK).translate([0, 0]).scale(1).rotate(c[1] > 0 ? [-c[0], 0] : [180 - c[0], 180]);
}) {
const faces = octahedron.map((face) => ({face, project: faceProjection(face)}));

[-1, 0, 0, 1, 0, 1, 4, 5].forEach((d, i) => {
const node = faces[d];
node && (node.children || (node.children = [])).push(faces[i]);
});

return polyhedral(
faces[0],
(lambda, phi) => faces[lambda < -pi / 2 ? phi < 0 ? 6 : 4
: lambda < 0 ? phi < 0 ? 2 : 0
: lambda < pi / 2 ? phi < 0 ? 3 : 1
: phi < 0 ? 7 : 5])
.angle(-30)
.scale(121.906)
.center([0, 48.5904]);
}
16 changes: 15 additions & 1 deletion src/cubic.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,21 @@
*
*/
import voronoi from "./polyhedral/voronoi.js";
import { default as cube } from "./polyhedral/cube.js";
import {atan, degrees, sqrt1_2} from "./math.js";

const phi1 = atan(sqrt1_2) * degrees;
const cube1 = [
[0, phi1], [90, phi1], [180, phi1], [-90, phi1],
[0, -phi1], [90, -phi1], [180, -phi1], [-90, -phi1]
];
const cube = [
[0, 3, 2, 1], // N
[0, 1, 5, 4],
[1, 2, 6, 5],
[2, 3, 7, 6],
[3, 0, 4, 7],
[4, 5, 6, 7] // S
].map((face) => face.map((i) => cube1[i]));

export default function() {
const polygons = {
Expand Down
149 changes: 149 additions & 0 deletions src/deltoidal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Deltoidal Hexecontahedron map
*
* Implemented for D3.js by Ronnie Bathoorn (2024),
* based on Icosahedron map by Jason Davies (2013)
* Enrico Spinielli (2017) and Philippe Rivière (2017, 2018)
*
*/
import { atan, degrees } from "./math.js";
import voronoi from "./polyhedral/voronoi.js";
import { geoCentroid, geoInterpolate } from "d3-geo";

export default function () {
const theta = atan(0.5) * degrees;

// construction inspired by
// https://en.wikipedia.org/wiki/Regular_icosahedron#Spherical_coordinates
const vertices = [[0, 90], [0, -90]].concat(
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => [
((i * 36 + 180) % 360) - 180,
i & 1 ? theta : -theta
])
);

// icosahedron
const polyhedron = [
[0, 3, 11],
[0, 5, 3],
[0, 7, 5],
[0, 9, 7],
[0, 11, 9], // North
[2, 11, 3],
[3, 4, 2],
[4, 3, 5],
[5, 6, 4],
[6, 5, 7],
[7, 8, 6],
[8, 7, 9],
[9, 10, 8],
[10, 9, 11],
[11, 2, 10], // Equator
[1, 2, 4],
[1, 4, 6],
[1, 6, 8],
[1, 8, 10],
[1, 10, 2], // South
].map((face) => {
const t = face.map((i) => vertices[i]);
// create 3 polygons from these using centroid and midpoints
const a0 = geoInterpolate(t[1], t[2])(0.5);
const a1 = geoInterpolate(t[0], t[2])(0.5);
const a2 = geoInterpolate(t[0], t[1])(0.5);
const c = geoCentroid({ type: "MultiPoint", coordinates: t });
return [
[t[0], a2, c, a1],
[t[1], a0, c, a2],
[t[2], a1, c, a0]
];
});

const polygons = {
type: "FeatureCollection",
features: polyhedron.flat().map((face) => ({
type: "Feature",
properties: {
sitecoordinates: geoCentroid({
type: "MultiPoint",
coordinates: face,
}),
},
geometry: {
type: "Polygon",
coordinates: [[...face, face[0]]],
},
}))
};

const parents = [
-1, // 0
2, // 1
0, // 2
5, // 3
5, // 4
22, // 5
8, // 6
8, // 7
28, // 8
11, // 9
11, // 10
34, // 11
14, // 12
14, // 13
40, // 14
16, // 15
2, // 16
16, // 17
17, // 18
18, // 19
18, // 20
19, // 21
21, // 22
21, // 23
23, // 24
24, // 25
24, // 26
25, // 27
27, // 28
27, // 29
29, // 30
30, // 31
30, // 32
31, // 33
33, // 34
33, // 35
35, // 36
36, // 37
36, // 38
37, // 39
39, // 40
39, // 41
41, // 42
42, // 43
42, // 44
46, // 45
20, // 46
46, // 47
49, // 48
26, // 49
49, // 50
52, // 51
32, // 52
52, // 53
55, // 54
38, // 55
55, // 56
58, // 57
44, // 58
58, // 59
];

//return polygons;
return voronoi()
.parents(parents)
.polygons(polygons)
.angle(3)
.rotate([108, 0])
.translate([72, 252])
.scale(136.67);
}
4 changes: 2 additions & 2 deletions src/polyhedral/dodecahedral.js → src/dodecahedral.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {acos, asin, degrees, sqrt} from "../math.js";
import voronoi from "./voronoi.js";
import {acos, asin, degrees, sqrt} from "./math.js";
import voronoi from "./polyhedral/voronoi.js";

export default function() {
const A0 = asin(1/sqrt(3)) * degrees;
Expand Down
10 changes: 6 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
export {default as geoClipPolygon} from "./clip/polygon.js";
export {default as geoIntersectArc} from "./intersect.js";
export {default as geoPolyhedral} from "./polyhedral/index.js";
export {default as geoPolyhedralButterfly} from "./polyhedral/butterfly.js";
export {default as geoPolyhedralCollignon} from "./polyhedral/collignon.js";
export {default as geoPolyhedralWaterman} from "./polyhedral/waterman.js";
export {default as geoPolyhedralButterfly} from "./butterfly.js";
export {default as geoPolyhedralCollignon} from "./collignon.js";
export {default as geoPolyhedralWaterman} from "./waterman.js";
export {default as geoPolyhedralVoronoi} from "./polyhedral/voronoi.js";
export {default as geoDodecahedral} from "./polyhedral/dodecahedral.js";
export {default as geoDodecahedral} from "./dodecahedral.js";
export {default as geoCox, coxRaw as geoCoxRaw} from "./cox.js";
export {default as geoTetrahedralLee, leeRaw as geoLeeRaw} from "./tetrahedralLee.js";
export {default as geoGrayFullerRaw} from "./grayfuller.js";
export {default as geoAirocean} from "./airocean.js";
export {default as geoIcosahedral} from "./icosahedral.js";
export {default as geoImago, imagoBlock as geoImagoBlock, imagoRaw as geoImagoRaw} from "./imago.js";
export {default as geoCubic} from "./cubic.js";
export {default as geoRhombic} from "./rhombic.js";
export {default as geoDeltoidal} from "./deltoidal.js";
export {default as geoCahillKeyes, cahillKeyesRaw as geoCahillKeyesRaw} from "./cahillKeyes.js";
export {default as geoComplexLog, complexLogRaw as geoComplexLogRaw} from "./complexLog.js";
Loading
Loading