diff --git a/public/modules/ocean-layers.js b/public/modules/ocean-layers.js deleted file mode 100644 index 281fad0a..00000000 --- a/public/modules/ocean-layers.js +++ /dev/null @@ -1,92 +0,0 @@ -"use strict"; - -window.OceanLayers = (function () { - let cells, vertices, pointsN, used; - - const OceanLayers = function OceanLayers() { - const outline = oceanLayers.attr("layers"); - if (outline === "none") return; - TIME && console.time("drawOceanLayers"); - - lineGen.curve(d3.curveBasisClosed); - (cells = grid.cells), (pointsN = grid.cells.i.length), (vertices = grid.vertices); - const limits = outline === "random" ? randomizeOutline() : outline.split(",").map(s => +s); - - const chains = []; - const opacity = rn(0.4 / limits.length, 2); - used = new Uint8Array(pointsN); // to detect already passed cells - - for (const i of cells.i) { - const t = cells.t[i]; - if (t > 0) continue; - if (used[i] || !limits.includes(t)) continue; - const start = findStart(i, t); - if (!start) continue; - used[i] = 1; - const chain = connectVertices(start, t); // vertices chain to form a path - if (chain.length < 4) continue; - const relax = 1 + t * -2; // select only n-th point - const relaxed = chain.filter((v, i) => !(i % relax) || vertices.c[v].some(c => c >= pointsN)); - if (relaxed.length < 4) continue; - const points = clipPoly( - relaxed.map(v => vertices.p[v]), - 1 - ); - chains.push([t, points]); - } - - for (const t of limits) { - const layer = chains.filter(c => c[0] === t); - let path = layer.map(c => round(lineGen(c[1]))).join(""); - if (path) oceanLayers.append("path").attr("d", path).attr("fill", "#ecf2f9").attr("fill-opacity", opacity); - } - - // find eligible cell vertex to start path detection - function findStart(i, t) { - if (cells.b[i]) return cells.v[i].find(v => vertices.c[v].some(c => c >= pointsN)); // map border cell - return cells.v[i][cells.c[i].findIndex(c => cells.t[c] < t || !cells.t[c])]; - } - - TIME && console.timeEnd("drawOceanLayers"); - }; - - function randomizeOutline() { - const limits = []; - let odd = 0.2; - for (let l = -9; l < 0; l++) { - if (P(odd)) { - odd = 0.2; - limits.push(l); - } else { - odd *= 2; - } - } - return limits; - } - - // connect vertices to chain - function connectVertices(start, t) { - const chain = []; // vertices chain to form a path - for (let i = 0, current = start; i === 0 || (current !== start && i < 10000); i++) { - const prev = chain[chain.length - 1]; // previous vertex in chain - chain.push(current); // add current vertex to sequence - const c = vertices.c[current]; // cells adjacent to vertex - c.filter(c => cells.t[c] === t).forEach(c => (used[c] = 1)); - const v = vertices.v[current]; // neighboring vertices - const c0 = !cells.t[c[0]] || cells.t[c[0]] === t - 1; - const c1 = !cells.t[c[1]] || cells.t[c[1]] === t - 1; - const c2 = !cells.t[c[2]] || cells.t[c[2]] === t - 1; - if (v[0] !== undefined && v[0] !== prev && c0 !== c1) current = v[0]; - else if (v[1] !== undefined && v[1] !== prev && c1 !== c2) current = v[1]; - else if (v[2] !== undefined && v[2] !== prev && c0 !== c2) current = v[2]; - if (current === chain[chain.length - 1]) { - ERROR && console.error("Next vertex is not found"); - break; - } - } - chain.push(chain[0]); // push first vertex as the last one - return chain; - } - - return OceanLayers; -})(); diff --git a/src/index.html b/src/index.html index 0db2395e..6b3a1e33 100644 --- a/src/index.html +++ b/src/index.html @@ -8469,7 +8469,6 @@ - diff --git a/src/modules/index.ts b/src/modules/index.ts index 241974b8..e534f524 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -1,3 +1,4 @@ import "./voronoi"; import "./heightmap-generator"; -import "./features"; \ No newline at end of file +import "./features"; +import "./ocean-layers"; \ No newline at end of file diff --git a/src/modules/ocean-layers.ts b/src/modules/ocean-layers.ts new file mode 100644 index 00000000..16dfc59b --- /dev/null +++ b/src/modules/ocean-layers.ts @@ -0,0 +1,121 @@ +import { line, curveBasisClosed } from 'd3'; +import type { Selection } from 'd3'; +import { clipPoly,P,rn,round } from '../utils'; + +declare global { + interface Window { + OceanLayers: any; + } + + var TIME: boolean; + var ERROR: boolean; + var grid: any; + var oceanLayers: Selection; +} +class OceanModule { + private cells: any; + private vertices: any; + private pointsN: any; + private used: any; + private lineGen = line().curve(curveBasisClosed); + private oceanLayers: Selection; + + + constructor(oceanLayers: Selection) { + this.oceanLayers = oceanLayers; + } + + get grid(): any { + return grid; + } + + randomizeOutline() { + const limits = []; + let odd = 0.2; + for (let l = -9; l < 0; l++) { + if (P(odd)) { + odd = 0.2; + limits.push(l); + } else { + odd *= 2; + } + } + return limits; + } + + // connect vertices to chain + connectVertices(start: number, t: number) { + const chain = []; // vertices chain to form a path + for (let i = 0, current = start; i === 0 || (current !== start && i < 10000); i++) { + const prev = chain[chain.length - 1]; // previous vertex in chain + chain.push(current); // add current vertex to sequence + const c = this.vertices.c[current]; // cells adjacent to vertex + c.filter((c: number) => this.cells.t[c] === t).forEach((c: number) => (this.used[c] = 1)); + const v = this.vertices.v[current]; // neighboring vertices + const c0 = !this.cells.t[c[0]] || this.cells.t[c[0]] === t - 1; + const c1 = !this.cells.t[c[1]] || this.cells.t[c[1]] === t - 1; + const c2 = !this.cells.t[c[2]] || this.cells.t[c[2]] === t - 1; + if (v[0] !== undefined && v[0] !== prev && c0 !== c1) current = v[0]; + else if (v[1] !== undefined && v[1] !== prev && c1 !== c2) current = v[1]; + else if (v[2] !== undefined && v[2] !== prev && c0 !== c2) current = v[2]; + if (current === chain[chain.length - 1]) { + ERROR && console.error("Next vertex is not found"); + break; + } + } + chain.push(chain[0]); // push first vertex as the last one + return chain; + } + + // find eligible cell vertex to start path detection + findStart(i: number, t: number) { + if (this.cells.b[i]) return this.cells.v[i].find((v: number) => this.vertices.c[v].some((c: number) => c >= this.pointsN)); // map border cell + return this.cells.v[i][this.cells.c[i].findIndex((c: number)=> this.cells.t[c] < t || !this.cells.t[c])]; + } + + draw() { + const outline = this.oceanLayers.attr("layers"); + if (outline === "none") return; + TIME && console.time("drawOceanLayers"); + this.cells = grid.cells; + this.pointsN = grid.cells.i.length; + this.vertices = grid.vertices; + const limits = outline === "random" ? this.randomizeOutline() : outline.split(",").map((s: string) => +s); + + const chains: [number, any[]][] = []; + const opacity = rn(0.4 / limits.length, 2); + this.used = new Uint8Array(this.pointsN); // to detect already passed cells + + for (const i of this.cells.i) { + const t = this.cells.t[i]; + if (t > 0) continue; + if (this.used[i] || !limits.includes(t)) continue; + const start = this.findStart(i, t); + if (!start) continue; + this.used[i] = 1; + const chain = this.connectVertices(start, t); // vertices chain to form a path + if (chain.length < 4) continue; + const relax = 1 + t * -2; // select only n-th point + const relaxed = chain.filter((v, i) => !(i % relax) || this.vertices.c[v].some((c: number) => c >= this.pointsN)); + if (relaxed.length < 4) continue; + + const points = clipPoly( + relaxed.map(v => this.vertices.p[v]), + undefined, + undefined, + 1 + ); + chains.push([t, points]); + } + + for (const t of limits) { + const layer = chains.filter((c: [number, any[]]) => c[0] === t); + let path = layer.map((c: [number, any[]]) => round(this.lineGen(c[1]) || "")).join(""); + if (path) this.oceanLayers.append("path").attr("d", path).attr("fill", "#ecf2f9").attr("fill-opacity", opacity); + } + + TIME && console.timeEnd("drawOceanLayers"); + } +} + +window.OceanLayers = () => new OceanModule(oceanLayers).draw();