diff --git a/vue-solo/src/components/FantasyMapGenerator.js b/vue-solo/src/components/FantasyMapGenerator.js index d9dcc5b6..ac9702c3 100644 --- a/vue-solo/src/components/FantasyMapGenerator.js +++ b/vue-solo/src/components/FantasyMapGenerator.js @@ -1,5 +1,8 @@ /* eslint-disable */ +import {drawOcean} from '../draw/drawOcean.js'; +import {drawCoastline} from '../draw/drawCoastline.js'; + export default { name: 'FantasyMapGenerator', mounted (){ @@ -301,14 +304,14 @@ export default { drawScaleBar(); defineHeightmap(); markFeatures(); - drawOcean(); + drawOcean(cells,rn,diagram,lineGen,getContinuousLine,oceanLayers); elevateLakes(); resolveDepressionsPrimary(); reGraph(); resolveDepressionsSecondary(); flux(); addLakes(); - drawCoastline(); + drawCoastline(cells,rn,features,lineGen,seed,defs,graphWidth,land,diagram,getContinuousLine,coastline,lakes,landmass,graphHeight,drawDefaultRuler); drawRelief(); generateCultures(); manorsAndRegions(); @@ -1365,6 +1368,7 @@ export default { console.timeEnd("reduceClosedLakes"); } + /* moved into the draw folder function drawOcean() { console.time("drawOcean"); let limits = []; @@ -1412,7 +1416,7 @@ export default { } console.timeEnd("drawOcean"); } - + */ // recalculate Voronoi Graph to pack cells function reGraph() { console.time("reGraph"); @@ -1600,6 +1604,7 @@ export default { updateHistory(); } + /* moved into the draw folder // Detect and draw the coasline function drawCoastline() { console.time('drawCoastline'); @@ -1679,7 +1684,7 @@ export default { drawDefaultRuler(minXedge, maxXedge); console.timeEnd('drawCoastline'); } - + */ // draw default scale bar function drawScaleBar() { if ($("#scaleBar").hasClass("hidden")) return; // no need to re-draw hidden element @@ -6105,7 +6110,7 @@ export default { exitCustomization(); console.time("TOTAL"); markFeatures(); - drawOcean(); + drawOcean(cells,rn,diagram,lineGen,getContinuousLine,oceanLayers); elevateLakes(); resolveDepressionsPrimary(); reGraph(); diff --git a/vue-solo/src/draw/drawCoastline.js b/vue-solo/src/draw/drawCoastline.js new file mode 100644 index 00000000..e4153d3e --- /dev/null +++ b/vue-solo/src/draw/drawCoastline.js @@ -0,0 +1,93 @@ +// Detect and draw the coasline +export function drawCoastline(cells, + rn, + features, + lineGen, + seed, + defs, + graphWidth, + land, + diagram, + getContinuousLine, + coastline, + lakes, + landmass, + graphHeight, + drawDefaultRuler) { + console.time('drawCoastline'); + Math.seedrandom(seed); // reset seed to get the same result on heightmap edit + const shape = defs.append("mask").attr("id", "shape").attr("fill", "black").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%"); + $("#landmass").empty(); + let minX = graphWidth, maxX = 0; // extreme points + let minXedge, maxXedge; // extreme edges + const oceanEdges = [],lakeEdges = []; + for (let i=0; i < land.length; i++) { + const id = land[i].index, cell = diagram.cells[id]; + const f = land[i].fn; + land[i].height = Math.trunc(land[i].height); + if (!oceanEdges[f]) {oceanEdges[f] = []; lakeEdges[f] = [];} + cell.halfedges.forEach(function(e) { + const edge = diagram.edges[e]; + const start = edge[0].join(" "); + const end = edge[1].join(" "); + if (edge.left && edge.right) { + const ea = edge.left.index === id ? edge.right.index : edge.left.index; + cells[ea].height = Math.trunc(cells[ea].height); + if (cells[ea].height < 20) { + cells[ea].ctype = -1; + if (land[i].ctype !== 1) { + land[i].ctype = 1; // mark coastal land cells + // move cell point closer to coast + const x = (land[i].data[0] + cells[ea].data[0]) / 2; + const y = (land[i].data[1] + cells[ea].data[1]) / 2; + land[i].haven = ea; // harbor haven (oposite water cell) + land[i].coastX = rn(x + (land[i].data[0] - x) * 0.1, 1); + land[i].coastY = rn(y + (land[i].data[1] - y) * 0.1, 1); + land[i].data[0] = rn(x + (land[i].data[0] - x) * 0.5, 1); + land[i].data[1] = rn(y + (land[i].data[1] - y) * 0.5, 1); + } + if (features[cells[ea].fn].border) { + oceanEdges[f].push({start, end}); + // island extreme points + if (edge[0][0] < minX) {minX = edge[0][0]; minXedge = edge[0]} + if (edge[1][0] < minX) {minX = edge[1][0]; minXedge = edge[1]} + if (edge[0][0] > maxX) {maxX = edge[0][0]; maxXedge = edge[0]} + if (edge[1][0] > maxX) {maxX = edge[1][0]; maxXedge = edge[1]} + } else { + const l = cells[ea].fn; + if (!lakeEdges[f][l]) lakeEdges[f][l] = []; + lakeEdges[f][l].push({start, end}); + } + } + } else { + oceanEdges[f].push({start, end}); + } + }); + } + + for (let f = 0; f < features.length; f++) { + if (!oceanEdges[f]) continue; + if (!oceanEdges[f].length && lakeEdges[f].length) { + const m = lakeEdges[f].indexOf(d3.max(lakeEdges[f])); + oceanEdges[f] = lakeEdges[f][m]; + lakeEdges[f][m] = []; + } + lineGen.curve(d3.curveCatmullRomClosed.alpha(0.1)); + const oceanCoastline = getContinuousLine(oceanEdges[f],3, 0); + if (oceanCoastline) { + shape.append("path").attr("d", oceanCoastline).attr("fill", "white"); // draw the mask + coastline.append("path").attr("d", oceanCoastline); // draw the coastline + } + lineGen.curve(d3.curveBasisClosed); + lakeEdges[f].forEach(function(l) { + const lakeCoastline = getContinuousLine(l, 3, 0); + if (lakeCoastline) { + shape.append("path").attr("d", lakeCoastline).attr("fill", "black"); // draw the mask + lakes.append("path").attr("d", lakeCoastline); // draw the lakes + } + }); + } + landmass.append("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight); // draw the landmass + drawDefaultRuler(minXedge, maxXedge); + console.timeEnd('drawCoastline'); +} diff --git a/vue-solo/src/draw/drawOcean.js b/vue-solo/src/draw/drawOcean.js new file mode 100644 index 00000000..67dbc549 --- /dev/null +++ b/vue-solo/src/draw/drawOcean.js @@ -0,0 +1,52 @@ +export function drawOcean(cells, + rn, + diagram, + lineGen, + getContinuousLine, + oceanLayers) { + console.time("drawOcean"); + let limits = []; + let odd = 0.8; // initial odd for ocean layer is 80% + // Define type of ocean cells based on cell distance form land + let frontier = $.grep(cells, function(e) {return e.ctype === -1;}); + if (Math.random() < odd) {limits.push(-1); odd = 0.2;} + for (let c = -2; frontier.length > 0 && c > -10; c--) { + if (Math.random() < odd) {limits.unshift(c); odd = 0.2;} else {odd += 0.2;} + frontier.map(function(i) { + i.neighbors.forEach(function(e) { + if (!cells[e].ctype) cells[e].ctype = c; + }); + }); + frontier = $.grep(cells, function(e) {return e.ctype === c;}); + } + if (outlineLayersInput.value === "none") return; + if (outlineLayersInput.value !== "random") limits = outlineLayersInput.value.split(","); + // Define area edges + const opacity = rn(0.4 / limits.length, 2); + for (let l=0; l < limits.length; l++) { + const edges = []; + const lim = +limits[l]; + for (let i = 0; i < cells.length; i++) { + if (cells[i].ctype < lim || cells[i].ctype === undefined) continue; + if (cells[i].ctype > lim && cells[i].type !== "border") continue; + const cell = diagram.cells[i]; + cell.halfedges.forEach(function(e) { + const edge = diagram.edges[e]; + const start = edge[0].join(" "); + const end = edge[1].join(" "); + if (edge.left && edge.right) { + const ea = edge.left.index === i ? edge.right.index : edge.left.index; + if (cells[ea].ctype < lim) edges.push({start, end}); + } else { + edges.push({start, end}); + } + }); + } + lineGen.curve(d3.curveBasis); + let relax = 0.8 - l / 10; + if (relax < 0.2) relax = 0.2; + const line = getContinuousLine(edges, 0, relax); + oceanLayers.append("path").attr("d", line).attr("fill", "#ecf2f9").style("opacity", opacity); + } + console.timeEnd("drawOcean"); +}