mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 03:51:23 +01:00
feat: routes - create route dialog
This commit is contained in:
parent
a3a858d609
commit
f6f7beb793
8 changed files with 318 additions and 124 deletions
51
index.html
51
index.html
|
|
@ -2221,7 +2221,7 @@
|
||||||
>
|
>
|
||||||
River
|
River
|
||||||
</button>
|
</button>
|
||||||
<button id="addRoute" data-tip="Click on map to place a route" data-shortcut="Shift + 4">Route</button>
|
<button id="addRoute" data-tip="Open route creation dialog" data-shortcut="Shift + 4">Route</button>
|
||||||
<button
|
<button
|
||||||
id="addMarker"
|
id="addMarker"
|
||||||
data-tip="Click on map to place a marker. Hold Shift to add multiple"
|
data-tip="Click on map to place a marker. Hold Shift to add multiple"
|
||||||
|
|
@ -2661,7 +2661,7 @@
|
||||||
data-tip="Provide a name for the new group"
|
data-tip="Provide a name for the new group"
|
||||||
style="display: none; width: 10em"
|
style="display: none; width: 10em"
|
||||||
/>
|
/>
|
||||||
<span id="labelGroupNew" data-tip="Create new group for this label" class="icon-plus pointer"></span>
|
<span id="labelGroupNew" data-tip="Create a new group for this label" class="icon-plus pointer"></span>
|
||||||
<span
|
<span
|
||||||
id="labelGroupRemove"
|
id="labelGroupRemove"
|
||||||
data-tip="Remove the Group with all labels"
|
data-tip="Remove the Group with all labels"
|
||||||
|
|
@ -2774,7 +2774,7 @@
|
||||||
<div id="riverBottom">
|
<div id="riverBottom">
|
||||||
<button
|
<button
|
||||||
id="riverCreateSelectingCells"
|
id="riverCreateSelectingCells"
|
||||||
data-tip="Create new river selecting river cells"
|
data-tip="Create a new river selecting river cells"
|
||||||
class="icon-map-pin"
|
class="icon-map-pin"
|
||||||
></button>
|
></button>
|
||||||
<button id="riverEditStyle" data-tip="Edit style for all rivers in Style Editor" class="icon-brush"></button>
|
<button id="riverEditStyle" data-tip="Edit style for all rivers in Style Editor" class="icon-brush"></button>
|
||||||
|
|
@ -2818,7 +2818,7 @@
|
||||||
<div data-tip="Type to change lake type (group)">
|
<div data-tip="Type to change lake type (group)">
|
||||||
<div class="label" style="width: 4.8em">Type:</div>
|
<div class="label" style="width: 4.8em">Type:</div>
|
||||||
<span id="lakeGroupRemove" data-tip="Remove the group" class="icon-trash-empty pointer"></span>
|
<span id="lakeGroupRemove" data-tip="Remove the group" class="icon-trash-empty pointer"></span>
|
||||||
<span id="lakeGroupAdd" data-tip="Create new type (group) for the lake" class="icon-plus pointer"></span>
|
<span id="lakeGroupAdd" data-tip="Create a new type (group) for the lake" class="icon-plus pointer"></span>
|
||||||
<select id="lakeGroup" data-tip="Select lake type (group)"></select>
|
<select id="lakeGroup" data-tip="Select lake type (group)"></select>
|
||||||
<input
|
<input
|
||||||
id="lakeGroupName"
|
id="lakeGroupName"
|
||||||
|
|
@ -2922,6 +2922,7 @@
|
||||||
<div class="label">Group:</div>
|
<div class="label">Group:</div>
|
||||||
<select id="routeGroup"></select>
|
<select id="routeGroup"></select>
|
||||||
<span id="routeGroupEdit" data-tip="Edit route groups" class="icon-pencil pointer"></span>
|
<span id="routeGroupEdit" data-tip="Edit route groups" class="icon-pencil pointer"></span>
|
||||||
|
<span id="routeEditStyle" data-tip="Edit style for the route group" class="icon-brush pointer"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-tip="Route length in selected units">
|
<div data-tip="Route length in selected units">
|
||||||
|
|
@ -2933,10 +2934,9 @@
|
||||||
<div id="routeBottom">
|
<div id="routeBottom">
|
||||||
<button
|
<button
|
||||||
id="routeCreateSelectingCells"
|
id="routeCreateSelectingCells"
|
||||||
data-tip="Create new route selecting route cells"
|
data-tip="Create a new route selecting route cells"
|
||||||
class="icon-map-pin"
|
class="icon-map-pin"
|
||||||
></button>
|
></button>
|
||||||
<button id="routeEditStyle" data-tip="Edit style for all routes in Style Editor" class="icon-brush"></button>
|
|
||||||
<button
|
<button
|
||||||
id="routeElevationProfile"
|
id="routeElevationProfile"
|
||||||
data-tip="Show the elevation profile for the route"
|
data-tip="Show the elevation profile for the route"
|
||||||
|
|
@ -2952,6 +2952,27 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="routeCreator" class="dialog" style="display: none">
|
||||||
|
<div>Click on map to add/remove route cells</div>
|
||||||
|
<ol id="routeCreatorBody" class="table" style="margin: 0; padding: 0.3em 0 0.3em 2em"></ol>
|
||||||
|
<div id="routeCreatorBottom">
|
||||||
|
<button id="routeCreatorComplete" data-tip="Complete route creation" class="icon-check"></button>
|
||||||
|
<button id="routeCreatorCancel" data-tip="Cancel the creation" class="icon-cancel"></button>
|
||||||
|
<div style="display: inline-block">
|
||||||
|
Group:
|
||||||
|
<select id="routeCreatorGroupSelect"></select>
|
||||||
|
<span id="routeCreatorGroupEdit" data-tip="Edit route groups" class="icon-pencil pointer"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="routeGroupsEditor" class="dialog" style="display: none">
|
||||||
|
<div id="routeGroupsEditorBody" class="table" style="padding: 0.3em 0; width: 100%"></div>
|
||||||
|
<div id="routeGroupsEditorBottom">
|
||||||
|
<button id="routeGroupsEditorAdd" data-tip="Add route group" class="icon-plus"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="iceEditor" class="dialog" style="display: none">
|
<div id="iceEditor" class="dialog" style="display: none">
|
||||||
<button id="iceEditStyle" data-tip="Edit style in Style Editor" class="icon-brush"></button>
|
<button id="iceEditStyle" data-tip="Edit style in Style Editor" class="icon-brush"></button>
|
||||||
<button id="iceRandomize" data-tip="Randomize Iceberg shape" class="icon-shuffle"></button>
|
<button id="iceRandomize" data-tip="Randomize Iceberg shape" class="icon-shuffle"></button>
|
||||||
|
|
@ -2976,7 +2997,11 @@
|
||||||
data-tip="Provide a name for the new group"
|
data-tip="Provide a name for the new group"
|
||||||
style="display: none; width: 9em"
|
style="display: none; width: 9em"
|
||||||
/>
|
/>
|
||||||
<span id="coastlineGroupAdd" data-tip="Create new group for this coastline" class="icon-plus pointer"></span>
|
<span
|
||||||
|
id="coastlineGroupAdd"
|
||||||
|
data-tip="Create a new group for this coastline"
|
||||||
|
class="icon-plus pointer"
|
||||||
|
></span>
|
||||||
<span id="coastlineGroupRemove" data-tip="Remove the group" class="icon-trash-empty pointer"></span>
|
<span id="coastlineGroupRemove" data-tip="Remove the group" class="icon-trash-empty pointer"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -3447,10 +3472,10 @@
|
||||||
<input
|
<input
|
||||||
id="burgInputGroup"
|
id="burgInputGroup"
|
||||||
placeholder="new group name"
|
placeholder="new group name"
|
||||||
data-tip="Create new Group for the Burg"
|
data-tip="Create a new Group for the Burg"
|
||||||
style="display: none; width: 10em"
|
style="display: none; width: 10em"
|
||||||
/>
|
/>
|
||||||
<i id="burgAddGroup" data-tip="Create new group for the burg" class="icon-plus pointer"></i>
|
<i id="burgAddGroup" data-tip="Create a new group for the burg" class="icon-plus pointer"></i>
|
||||||
<i id="burgRemoveGroup" data-tip="Remove selected burg group" class="icon-trash pointer"></i>
|
<i id="burgRemoveGroup" data-tip="Remove selected burg group" class="icon-trash pointer"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -3599,7 +3624,7 @@
|
||||||
|
|
||||||
<div id="regimentBottom">
|
<div id="regimentBottom">
|
||||||
<button id="regimentAttack" data-tip="Attack foreign regiment" class="icon-target"></button>
|
<button id="regimentAttack" data-tip="Attack foreign regiment" class="icon-target"></button>
|
||||||
<button id="regimentAdd" data-tip="Create new regiment or fleet" class="icon-user-plus"></button>
|
<button id="regimentAdd" data-tip="Create a new regiment or fleet" class="icon-user-plus"></button>
|
||||||
<button id="regimentSplit" data-tip="Split regiment into 2 separate ones" class="icon-half"></button>
|
<button id="regimentSplit" data-tip="Split regiment into 2 separate ones" class="icon-half"></button>
|
||||||
<button
|
<button
|
||||||
id="regimentAttach"
|
id="regimentAttach"
|
||||||
|
|
@ -5446,7 +5471,7 @@
|
||||||
|
|
||||||
<div id="routesBottom">
|
<div id="routesBottom">
|
||||||
<button id="routesOverviewRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
<button id="routesOverviewRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
||||||
<button id="routeCreateNew" data-tip="Create new route selecting route cells" class="icon-map-pin"></button>
|
<button id="routeCreateNew" data-tip="Create a new route selecting route cells" class="icon-map-pin"></button>
|
||||||
<button
|
<button
|
||||||
id="routesExport"
|
id="routesExport"
|
||||||
data-tip="Save routes-related data as a text file (.csv)"
|
data-tip="Save routes-related data as a text file (.csv)"
|
||||||
|
|
@ -5502,7 +5527,7 @@
|
||||||
data-tip="Automatically add river starting from clicked cell. Hold Shift to add multiple"
|
data-tip="Automatically add river starting from clicked cell. Hold Shift to add multiple"
|
||||||
class="icon-plus"
|
class="icon-plus"
|
||||||
></button>
|
></button>
|
||||||
<button id="riverCreateNew" data-tip="Create new river selecting river cells" class="icon-map-pin"></button>
|
<button id="riverCreateNew" data-tip="Create a new river selecting river cells" class="icon-map-pin"></button>
|
||||||
<button id="riversBasinHighlight" data-tip="Toggle basin highlight mode" class="icon-sitemap"></button>
|
<button id="riversBasinHighlight" data-tip="Toggle basin highlight mode" class="icon-sitemap"></button>
|
||||||
<button
|
<button
|
||||||
id="riversExport"
|
id="riversExport"
|
||||||
|
|
@ -8128,6 +8153,8 @@
|
||||||
<script defer src="modules/ui/elevation-profile.js?v=1.97.10"></script>
|
<script defer src="modules/ui/elevation-profile.js?v=1.97.10"></script>
|
||||||
<script defer src="modules/ui/temperature-graph.js?v=1.90.03"></script>
|
<script defer src="modules/ui/temperature-graph.js?v=1.90.03"></script>
|
||||||
<script defer src="modules/ui/routes-editor.js?v=1.89.04"></script>
|
<script defer src="modules/ui/routes-editor.js?v=1.89.04"></script>
|
||||||
|
<script defer src="modules/ui/routes-creator.js"></script>
|
||||||
|
<script defer src="modules/ui/route-group-editor.js"></script>
|
||||||
<script defer src="modules/ui/ice-editor.js?v=1.89.08"></script>
|
<script defer src="modules/ui/ice-editor.js?v=1.89.08"></script>
|
||||||
<script defer src="modules/ui/lakes-editor.js?v=1.87.10"></script>
|
<script defer src="modules/ui/lakes-editor.js?v=1.87.10"></script>
|
||||||
<script defer src="modules/ui/coastline-editor.js"></script>
|
<script defer src="modules/ui/coastline-editor.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -549,6 +549,58 @@ window.Routes = (function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function preparePointsArray() {
|
||||||
|
const {cells, burgs} = pack;
|
||||||
|
return cells.p.map(([x, y], cellId) => {
|
||||||
|
const burgId = cells.burg[cellId];
|
||||||
|
if (burgId) return [burgs[burgId].x, burgs[burgId].y];
|
||||||
|
return [x, y];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPoints(route, points) {
|
||||||
|
if (route.points) return route.points;
|
||||||
|
const routePoints = route.cells.map(cellId => points[cellId]);
|
||||||
|
|
||||||
|
if (route.group !== "searoutes2") {
|
||||||
|
for (let i = 1; i < route.cells.length - 1; i++) {
|
||||||
|
const cellId = route.cells[i];
|
||||||
|
if (pack.cells.burg[cellId]) continue;
|
||||||
|
|
||||||
|
const [prevX, prevY] = routePoints[i - 1];
|
||||||
|
const [currX, currY] = routePoints[i];
|
||||||
|
const [nextX, nextY] = routePoints[i + 1];
|
||||||
|
|
||||||
|
const dAx = prevX - currX;
|
||||||
|
const dAy = prevY - currY;
|
||||||
|
const dBx = nextX - currX;
|
||||||
|
const dBy = nextY - currY;
|
||||||
|
const angle = Math.abs((Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy) * 180) / Math.PI);
|
||||||
|
|
||||||
|
if (angle < ROUTES_SHARP_ANGLE) {
|
||||||
|
const middleX = (prevX + nextX) / 2;
|
||||||
|
const middleY = (prevY + nextY) / 2;
|
||||||
|
let newX, newY;
|
||||||
|
|
||||||
|
if (angle < ROUTES_VERY_SHARP_ANGLE) {
|
||||||
|
newX = rn((currX + middleX * 2) / 3, 2);
|
||||||
|
newY = rn((currY + middleY * 2) / 3, 2);
|
||||||
|
} else {
|
||||||
|
newX = rn((currX + middleX) / 2, 2);
|
||||||
|
newY = rn((currY + middleY) / 2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (findCell(newX, newY) === cellId) {
|
||||||
|
routePoints[i] = [newX, newY];
|
||||||
|
points[cellId] = routePoints[i]; // change cell coordinate for all routes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return routePoints;
|
||||||
|
}
|
||||||
|
|
||||||
function remove(route) {
|
function remove(route) {
|
||||||
const routes = pack.cells.routes;
|
const routes = pack.cells.routes;
|
||||||
|
|
||||||
|
|
@ -568,5 +620,16 @@ window.Routes = (function () {
|
||||||
.remove();
|
.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {generate, isConnected, areConnected, getRoute, hasRoad, isCrossroad, generateName, remove};
|
return {
|
||||||
|
generate,
|
||||||
|
isConnected,
|
||||||
|
areConnected,
|
||||||
|
getRoute,
|
||||||
|
hasRoad,
|
||||||
|
isCrossroad,
|
||||||
|
generateName,
|
||||||
|
preparePointsArray,
|
||||||
|
getPoints,
|
||||||
|
remove
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ function handleKeyup(event) {
|
||||||
else if (key === "!") toggleAddBurg();
|
else if (key === "!") toggleAddBurg();
|
||||||
else if (key === "@") toggleAddLabel();
|
else if (key === "@") toggleAddLabel();
|
||||||
else if (key === "#") toggleAddRiver();
|
else if (key === "#") toggleAddRiver();
|
||||||
else if (key === "$") toggleAddRoute();
|
else if (key === "$") createRoute();
|
||||||
else if (key === "%") toggleAddMarker();
|
else if (key === "%") toggleAddMarker();
|
||||||
else if (alt && code === "KeyB") console.table(pack.burgs);
|
else if (alt && code === "KeyB") console.table(pack.burgs);
|
||||||
else if (alt && code === "KeyS") console.table(pack.states);
|
else if (alt && code === "KeyS") console.table(pack.states);
|
||||||
|
|
|
||||||
|
|
@ -1645,17 +1645,12 @@ function drawRoutes() {
|
||||||
const routePaths = {};
|
const routePaths = {};
|
||||||
const lineGen = d3.line();
|
const lineGen = d3.line();
|
||||||
|
|
||||||
const {cells, burgs} = pack;
|
let points = Routes.preparePointsArray();
|
||||||
let points = cells.p.map(([x, y], cellId) => {
|
|
||||||
const burgId = cells.burg[cellId];
|
|
||||||
if (burgId) return [burgs[burgId].x, burgs[burgId].y];
|
|
||||||
return [x, y];
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const route of pack.routes) {
|
for (const route of pack.routes) {
|
||||||
const {i, group} = route;
|
const {i, group} = route;
|
||||||
lineGen.curve(ROUTE_CURVES[group] || ROUTE_CURVES.default);
|
lineGen.curve(ROUTE_CURVES[group] || ROUTE_CURVES.default);
|
||||||
const routePoints = getRoutePoints(route, points);
|
const routePoints = Routes.getPoints(route, points);
|
||||||
const path = round(lineGen(routePoints), 1);
|
const path = round(lineGen(routePoints), 1);
|
||||||
|
|
||||||
if (!routePaths[group]) routePaths[group] = [];
|
if (!routePaths[group]) routePaths[group] = [];
|
||||||
|
|
@ -1673,49 +1668,6 @@ function drawRoutes() {
|
||||||
const ROUTES_SHARP_ANGLE = 135;
|
const ROUTES_SHARP_ANGLE = 135;
|
||||||
const ROUTES_VERY_SHARP_ANGLE = 115;
|
const ROUTES_VERY_SHARP_ANGLE = 115;
|
||||||
|
|
||||||
function getRoutePoints(route, points) {
|
|
||||||
if (route.points) return route.points;
|
|
||||||
const routePoints = route.cells.map(cellId => points[cellId]);
|
|
||||||
|
|
||||||
if (route.group !== "searoutes2") {
|
|
||||||
for (let i = 1; i < route.cells.length - 1; i++) {
|
|
||||||
const cellId = route.cells[i];
|
|
||||||
if (pack.cells.burg[cellId]) continue;
|
|
||||||
|
|
||||||
const [prevX, prevY] = routePoints[i - 1];
|
|
||||||
const [currX, currY] = routePoints[i];
|
|
||||||
const [nextX, nextY] = routePoints[i + 1];
|
|
||||||
|
|
||||||
const dAx = prevX - currX;
|
|
||||||
const dAy = prevY - currY;
|
|
||||||
const dBx = nextX - currX;
|
|
||||||
const dBy = nextY - currY;
|
|
||||||
const angle = Math.abs((Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy) * 180) / Math.PI);
|
|
||||||
|
|
||||||
if (angle < ROUTES_SHARP_ANGLE) {
|
|
||||||
const middleX = (prevX + nextX) / 2;
|
|
||||||
const middleY = (prevY + nextY) / 2;
|
|
||||||
let newX, newY;
|
|
||||||
|
|
||||||
if (angle < ROUTES_VERY_SHARP_ANGLE) {
|
|
||||||
newX = rn((currX + middleX * 2) / 3, 2);
|
|
||||||
newY = rn((currY + middleY * 2) / 3, 2);
|
|
||||||
} else {
|
|
||||||
newX = rn((currX + middleX) / 2, 2);
|
|
||||||
newY = rn((currY + middleY) / 2, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (findCell(newX, newY) === cellId) {
|
|
||||||
routePoints[i] = [newX, newY];
|
|
||||||
points[cellId] = routePoints[i]; // change cell coordinate for all routes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return routePoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawRoute() {}
|
function drawRoute() {}
|
||||||
|
|
||||||
function toggleMilitary() {
|
function toggleMilitary() {
|
||||||
|
|
|
||||||
83
modules/ui/route-group-editor.js
Normal file
83
modules/ui/route-group-editor.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function editRouteGroups() {
|
||||||
|
if (customization) return;
|
||||||
|
if (!layerIsOn("toggleRoutes")) toggleRoutes();
|
||||||
|
|
||||||
|
addLines();
|
||||||
|
|
||||||
|
$("#routeGroupsEditor").dialog({
|
||||||
|
title: "Edit Route groups",
|
||||||
|
resizable: false,
|
||||||
|
position: {my: "left top", at: "left+10 top+140", of: "#map"}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (modules.editRouteGroups) return;
|
||||||
|
modules.editRouteGroups = true;
|
||||||
|
|
||||||
|
// add listeners
|
||||||
|
byId("routeGroupsEditorAdd").addEventListener("click", addGroup);
|
||||||
|
byId("routeGroupsEditorBody").on("click", ev => {
|
||||||
|
const group = ev.target.parentNode.dataset.id;
|
||||||
|
if (ev.target.classList.contains("editStyle")) editStyle("routes", group);
|
||||||
|
else if (ev.target.classList.contains("removeGroup")) removeGroup(group);
|
||||||
|
});
|
||||||
|
|
||||||
|
function addLines() {
|
||||||
|
byId("routeGroupsEditorBody").innerHTML = "";
|
||||||
|
|
||||||
|
const lines = Array.from(routes.selectAll("g")._groups[0]).map(el => {
|
||||||
|
const count = el.children.length;
|
||||||
|
return /* html */ `<div data-id="${el.id}" class="states" style="display: flex; justify-content: space-between;">
|
||||||
|
<span>${el.id} (${count})</span>
|
||||||
|
<div style="width: auto; display: flex; gap: 0.4em;">
|
||||||
|
<span data-tip="Edit style" class="editStyle icon-brush pointer" style="font-size: smaller;"></span>
|
||||||
|
<span data-tip="Remove group" class="removeGroup icon-trash pointer"></span>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
byId("routeGroupsEditorBody").innerHTML = lines.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_GROUPS = ["roads", "trails", "searoutes"];
|
||||||
|
|
||||||
|
function addGroup() {
|
||||||
|
prompt("Type group name", {default: "route-group-new"}, v => {
|
||||||
|
let group = v
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/ /g, "_")
|
||||||
|
.replace(/[^\w\s]/gi, "");
|
||||||
|
|
||||||
|
if (!group) return tip("Invalid group name", false, "error");
|
||||||
|
if (!group.startsWith("route-")) group = "route-" + group;
|
||||||
|
if (byId(group)) return tip("Element with this name already exists. Provide a unique name", false, "error");
|
||||||
|
if (Number.isFinite(+group.charAt(0))) return tip("Group name should start with a letter", false, "error");
|
||||||
|
|
||||||
|
routes
|
||||||
|
.append("g")
|
||||||
|
.attr("id", group)
|
||||||
|
.attr("stroke", "#000000")
|
||||||
|
.attr("stroke-width", 0.5)
|
||||||
|
.attr("stroke-dasharray", "1 0.5")
|
||||||
|
.attr("stroke-linecap", "butt");
|
||||||
|
byId("routeGroup")?.options.add(new Option(group, group));
|
||||||
|
addLines();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeGroup(group) {
|
||||||
|
confirmationDialog({
|
||||||
|
title: "Remove route group",
|
||||||
|
message:
|
||||||
|
"Are you sure you want to remove the entire route group? All routes in this group will be removed. This action can't be reverted.",
|
||||||
|
confirm: "Remove",
|
||||||
|
onConfirm: () => {
|
||||||
|
const routes = pack.routes.filter(r => r.group === group);
|
||||||
|
routes.forEach(r => Routes.remove(r));
|
||||||
|
if (DEFAULT_GROUPS.includes(group)) routes.select(`#${group}`).remove();
|
||||||
|
addLines();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
120
modules/ui/routes-creator.js
Normal file
120
modules/ui/routes-creator.js
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function createRoute(defaultGroup) {
|
||||||
|
if (customization) return;
|
||||||
|
closeDialogs();
|
||||||
|
if (!layerIsOn("toggleRoutes")) toggleRoutes();
|
||||||
|
|
||||||
|
byId("toggleCells").dataset.forced = +!layerIsOn("toggleCells");
|
||||||
|
if (!layerIsOn("toggleCells")) toggleCells();
|
||||||
|
|
||||||
|
tip("Click to add route point, click again to remove", true);
|
||||||
|
debug.append("g").attr("id", "controlCells");
|
||||||
|
viewbox.style("cursor", "crosshair").on("click", onCellClick);
|
||||||
|
|
||||||
|
createRoute.cells = [];
|
||||||
|
const body = byId("routeCreatorBody");
|
||||||
|
|
||||||
|
// update route groups
|
||||||
|
byId("routeCreatorGroupSelect").innerHTML = Array.from(routes.selectAll("g")._groups[0]).map(el => {
|
||||||
|
return `<option value="${el.id}" ${el.id === defaultGroup ? "selected" : ""}>${el.id}</option>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#routeCreator").dialog({
|
||||||
|
title: "Create Route",
|
||||||
|
resizable: false,
|
||||||
|
position: {my: "left top", at: "left+10 top+10", of: "#map"},
|
||||||
|
close: closeRouteCreator
|
||||||
|
});
|
||||||
|
|
||||||
|
if (modules.createRoute) return;
|
||||||
|
modules.createRoute = true;
|
||||||
|
|
||||||
|
// add listeners
|
||||||
|
byId("routeCreatorGroupEdit").on("click", editRouteGroups);
|
||||||
|
byId("routeCreatorComplete").on("click", completeCreation);
|
||||||
|
byId("routeCreatorCancel").on("click", () => $("#routeCreator").dialog("close"));
|
||||||
|
body.on("click", ev => {
|
||||||
|
if (ev.target.classList.contains("icon-trash-empty")) removeCell(+ev.target.parentNode.dataset.cell);
|
||||||
|
});
|
||||||
|
|
||||||
|
function onCellClick() {
|
||||||
|
const cell = findCell(...d3.mouse(this));
|
||||||
|
|
||||||
|
if (createRoute.cells.includes(cell)) removeCell(cell);
|
||||||
|
else addCell(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCell(cell) {
|
||||||
|
createRoute.cells.push(cell);
|
||||||
|
drawCells(createRoute.cells);
|
||||||
|
|
||||||
|
body.innerHTML += `<li class="editorLine" data-cell="${cell}">
|
||||||
|
<span>Cell ${cell}</span>
|
||||||
|
<span data-tip="Remove the cell" class="icon-trash-empty pointer"></span>
|
||||||
|
</li>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeCell(cell) {
|
||||||
|
createRoute.cells = createRoute.cells.filter(c => c !== cell);
|
||||||
|
drawCells(createRoute.cells);
|
||||||
|
body.querySelector(`[data-cell='${cell}']`)?.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCells(cells) {
|
||||||
|
debug
|
||||||
|
.select("#controlCells")
|
||||||
|
.selectAll("polygon")
|
||||||
|
.data(cells)
|
||||||
|
.join("polygon")
|
||||||
|
.attr("points", getPackPolygon)
|
||||||
|
.attr("class", "current");
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeCreation() {
|
||||||
|
const routeCells = createRoute.cells;
|
||||||
|
if (routeCells.length < 2) return tip("Add at least 2 cells", false, "error");
|
||||||
|
|
||||||
|
const routeId = Math.max(...pack.routes.map(route => route.i)) + 1;
|
||||||
|
const group = byId("routeCreatorGroupSelect").value;
|
||||||
|
const feature = pack.cells.f[routeCells[0]];
|
||||||
|
const route = {cells: routeCells, group, feature, i: routeId};
|
||||||
|
pack.routes.push(route);
|
||||||
|
|
||||||
|
const links = pack.cells.routes;
|
||||||
|
for (let i = 0; i < routeCells.length; i++) {
|
||||||
|
const cellId = routeCells[i];
|
||||||
|
const nextCellId = routeCells[i + 1];
|
||||||
|
if (nextCellId) {
|
||||||
|
if (!links[cellId]) links[cellId] = {};
|
||||||
|
links[cellId][nextCellId] = routeId;
|
||||||
|
|
||||||
|
if (!links[nextCellId]) links[nextCellId] = {};
|
||||||
|
links[nextCellId][cellId] = routeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineGen = d3.line();
|
||||||
|
lineGen.curve(ROUTE_CURVES[group] || ROUTE_CURVES.default);
|
||||||
|
const routePoints = Routes.getPoints(route, Routes.preparePointsArray());
|
||||||
|
const path = round(lineGen(routePoints), 1);
|
||||||
|
routes
|
||||||
|
.select("#" + group)
|
||||||
|
.append("path")
|
||||||
|
.attr("d", path)
|
||||||
|
.attr("id", "route" + routeId);
|
||||||
|
|
||||||
|
editRoute("route" + routeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeRouteCreator() {
|
||||||
|
body.innerHTML = "";
|
||||||
|
debug.select("#controlCells").remove();
|
||||||
|
restoreDefaultEvents();
|
||||||
|
clearMainTip();
|
||||||
|
|
||||||
|
const forced = +byId("toggleCells").dataset.forced;
|
||||||
|
byId("toggleCells").dataset.forced = 0;
|
||||||
|
if (forced && layerIsOn("toggleCells")) toggleCells();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,7 +20,8 @@ function editRoute(id) {
|
||||||
|
|
||||||
updateRouteData();
|
updateRouteData();
|
||||||
|
|
||||||
drawControlPoints(getPoints());
|
const route = getRoute();
|
||||||
|
drawControlPoints(Routes.getPoints(route, Routes.preparePointsArray()));
|
||||||
drawCells();
|
drawCells();
|
||||||
|
|
||||||
$("#routeEditor").dialog({
|
$("#routeEditor").dialog({
|
||||||
|
|
@ -34,13 +35,14 @@ function editRoute(id) {
|
||||||
modules.editRoute = true;
|
modules.editRoute = true;
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
byId("routeCreateSelectingCells").on("click", createRoute);
|
byId("routeCreateSelectingCells").on("click", showCreationDialog);
|
||||||
byId("routeEditStyle").on("click", editRouteGroupStyle);
|
|
||||||
byId("routeElevationProfile").on("click", showRouteElevationProfile);
|
byId("routeElevationProfile").on("click", showRouteElevationProfile);
|
||||||
byId("routeLegend").on("click", editRouteLegend);
|
byId("routeLegend").on("click", editRouteLegend);
|
||||||
byId("routeRemove").on("click", removeRoute);
|
byId("routeRemove").on("click", removeRoute);
|
||||||
byId("routeName").on("input", changeName);
|
byId("routeName").on("input", changeName);
|
||||||
byId("routeGroup").on("input", changeGroup);
|
byId("routeGroup").on("input", changeGroup);
|
||||||
|
byId("routeGroupEdit").on("click", editRouteGroups);
|
||||||
|
byId("routeEditStyle").on("click", editRouteGroupStyle);
|
||||||
byId("routeGenerateName").on("click", generateName);
|
byId("routeGenerateName").on("click", generateName);
|
||||||
|
|
||||||
function getRoute() {
|
function getRoute() {
|
||||||
|
|
@ -49,17 +51,6 @@ function editRoute(id) {
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPoints() {
|
|
||||||
const {cells, burgs} = pack;
|
|
||||||
let points = cells.p.map(([x, y], cellId) => {
|
|
||||||
const burgId = cells.burg[cellId];
|
|
||||||
if (burgId) return [burgs[burgId].x, burgs[burgId].y];
|
|
||||||
return [x, y];
|
|
||||||
});
|
|
||||||
|
|
||||||
return getRoutePoints(getRoute(), points);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateRouteData() {
|
function updateRouteData() {
|
||||||
const route = getRoute();
|
const route = getRoute();
|
||||||
|
|
||||||
|
|
@ -181,23 +172,9 @@ function editRoute(id) {
|
||||||
redrawRoute();
|
redrawRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawConnections() {
|
function showCreationDialog() {
|
||||||
debug.selectAll("line").remove();
|
const route = getRoute();
|
||||||
for (const [fromCellId, connections] of Object.entries(pack.cells.routes)) {
|
createRoute(route.group);
|
||||||
const from = pack.cells.p[fromCellId];
|
|
||||||
for (const toCellId of Object.keys(connections)) {
|
|
||||||
const to = pack.cells.p[toCellId];
|
|
||||||
debug
|
|
||||||
.append("line")
|
|
||||||
.attr("x1", from[0])
|
|
||||||
.attr("y1", from[1])
|
|
||||||
.attr("x2", to[0])
|
|
||||||
.attr("y2", to[1])
|
|
||||||
.attr("stroke", "red")
|
|
||||||
.attr("stroke-width", 0.4)
|
|
||||||
.attr("opacity", 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeConnection(from, to) {
|
function removeConnection(from, to) {
|
||||||
|
|
@ -254,10 +231,6 @@ function editRoute(id) {
|
||||||
editStyle("routes", group);
|
editStyle("routes", group);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRoute() {
|
|
||||||
// TODO: white the code :)
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeRoute() {
|
function removeRoute() {
|
||||||
alertMessage.innerHTML = "Are you sure you want to remove the route";
|
alertMessage.innerHTML = "Are you sure you want to remove the route";
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ toolsContent.addEventListener("click", function (event) {
|
||||||
if (button === "addLabel") toggleAddLabel();
|
if (button === "addLabel") toggleAddLabel();
|
||||||
else if (button === "addBurgTool") toggleAddBurg();
|
else if (button === "addBurgTool") toggleAddBurg();
|
||||||
else if (button === "addRiver") toggleAddRiver();
|
else if (button === "addRiver") toggleAddRiver();
|
||||||
else if (button === "addRoute") toggleAddRoute();
|
else if (button === "addRoute") createRoute();
|
||||||
else if (button === "addMarker") toggleAddMarker();
|
else if (button === "addMarker") toggleAddMarker();
|
||||||
// click to create a new map buttons
|
// click to create a new map buttons
|
||||||
else if (button === "openSubmapMenu") UISubmap.openSubmapMenu();
|
else if (button === "openSubmapMenu") UISubmap.openSubmapMenu();
|
||||||
|
|
@ -775,30 +775,6 @@ function addRiverOnClick() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleAddRoute() {
|
|
||||||
const pressed = document.getElementById("addRoute").classList.contains("pressed");
|
|
||||||
if (pressed) {
|
|
||||||
unpressClickToAddButton();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addFeature.querySelectorAll("button.pressed").forEach(b => b.classList.remove("pressed"));
|
|
||||||
addRoute.classList.add("pressed");
|
|
||||||
closeDialogs(".stable");
|
|
||||||
viewbox.style("cursor", "crosshair").on("click", addRouteOnClick);
|
|
||||||
tip("Click on map to add a first control point", true);
|
|
||||||
if (!layerIsOn("toggleRoutes")) toggleRoutes();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRouteOnClick() {
|
|
||||||
unpressClickToAddButton();
|
|
||||||
const [x, y] = d3.mouse(this);
|
|
||||||
|
|
||||||
const id = getNextId("route");
|
|
||||||
routes.select("g").append("path").attr("id", id).attr("data-new", 1).attr("d", `M${x},${y}`);
|
|
||||||
editRoute(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleAddMarker() {
|
function toggleAddMarker() {
|
||||||
const pressed = document.getElementById("addMarker")?.classList.contains("pressed");
|
const pressed = document.getElementById("addMarker")?.classList.contains("pressed");
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue