Use the range slider to transition to an isometric projection and back again.
See also Isometric Grid Transition.
| license: gpl-3.0 |
Use the range slider to transition to an isometric projection and back again.
See also Isometric Grid Transition.
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <style> | |
| body { | |
| margin: 0; | |
| } | |
| svg { | |
| display: table; | |
| margin: 0 auto; | |
| } | |
| line { | |
| stroke-width: 4px; | |
| } | |
| path { | |
| fill: none; | |
| } | |
| .cell { | |
| stroke: #eee; | |
| } | |
| .outline { | |
| stroke: #000; | |
| stroke-width: 2px; | |
| } | |
| .input { | |
| display: table; | |
| margin: 0 auto; | |
| } | |
| .input label { | |
| font-family: sans-serif; | |
| margin-right: 8px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="input"> | |
| <label>Rotation</label> | |
| <input type="range" min="0" max="30" value="30"> | |
| </div> | |
| <div class="grid"></div> | |
| <script type="module"> | |
| import { cross, range } from "https://cdn.skypack.dev/d3-array@3"; | |
| import { geoEquirectangular, geoPath } from "https://cdn.skypack.dev/d3-geo@3"; | |
| import { randomUniform } from "https://cdn.skypack.dev/d3-random@3"; | |
| import { interpolateSinebow } from "https://cdn.skypack.dev/d3-scale-chromatic@3"; | |
| import { select } from "https://cdn.skypack.dev/d3-selection@3"; | |
| import { interval } from "https://cdn.skypack.dev/d3-timer@3"; | |
| import { transition } from "https://cdn.skypack.dev/d3-transition@3"; | |
| const { cos, PI, round, sin } = Math; | |
| const i = 0.4; | |
| const r = range(-1, 1, i); | |
| const cells = { | |
| type: "FeatureCollection", | |
| features: cross(r, r) | |
| .map(([lon, lat]) => ({ | |
| type: "Feature", | |
| geometry: { | |
| type: "Polygon", | |
| coordinates: [ | |
| [ | |
| [lon, lat], | |
| [lon, lat + i], | |
| [lon + i, lat + i], | |
| [lon + i, lat], | |
| [lon, lat] | |
| ] | |
| ] | |
| } | |
| })) | |
| }; | |
| const x = n => round(cos(n)); | |
| const y = n => round(sin(n)); | |
| const random = randomUniform(-1, 1); | |
| const data = Array.from({ length: 250 }).map(d => ([random(), random()])); | |
| const square = [ range(1, PI * 2.5, PI * 0.25).map(n => [x(n), y(n)]) ]; | |
| const margin = {left: 1, right: 1, top: 50, bottom: 1}; | |
| const size = 400; | |
| const projection = geoEquirectangular() | |
| .reflectY(true) | |
| .fitSize([size, size], cells); | |
| const path = geoPath(projection); | |
| const svg = select(".grid").append("svg") | |
| .attr("width", size + margin.left + margin.right) | |
| .attr("height", size + margin.top + margin.bottom); | |
| const g = svg.append("g") | |
| .attr("transform", `translate(${[margin.left, margin.top]})`); | |
| const cell = g.selectAll(".cell") | |
| .data(cells.features) | |
| .join("path") | |
| .attr("class", "cell"); | |
| const outline = g.append("path") | |
| .attr("class", "outline") | |
| .datum({ | |
| type: "Polygon", | |
| coordinates: square | |
| }); | |
| const line = g.selectAll("line") | |
| .data(data) | |
| .join("line") | |
| .attr("stroke", (d, i, e) => interpolateSinebow(i / e.length)) | |
| .attr("y2", -margin.top + 15); | |
| draw([30, -60, 0]); | |
| select("input").on("input", function(){ | |
| draw([this.value, -this.value * 2, 0]); | |
| }); | |
| function draw(rotation){ | |
| projection | |
| .rotate(rotation) | |
| .fitSize([size, size], cells); | |
| cell | |
| .attr("d", path); | |
| outline | |
| .attr("d", path); | |
| line | |
| .attr("transform", d => `translate(${projection(d)})`); | |
| } | |
| </script> | |
| </body> | |
| </html> |