diff --git a/boxline2.js b/boxline2.js
index 16bd44e..2fdec05 100644
--- a/boxline2.js
+++ b/boxline2.js
@@ -32,10 +32,15 @@ function edgeName(link) {
return `link-${link.index}`
}
-export function renderBoxGraph({nodes, links}, ignore, callback) {
+export function renderBoxGraph({nodes, links}, direction, ignore, callback) {
let [itemColors, recipeColors] = getColorMaps(nodes, links)
+ if (direction === "down") {
+ direction = "TB"
+ } else {
+ direction = "LR"
+ }
let g = new dagre.graphlib.Graph({multigraph: true})
- g.setGraph({rankdir: "TB"})
+ g.setGraph({rankdir: direction})
g.setDefaultEdgeLabel(() => {})
let testSVG = d3.select("body").append("svg").classed("test", true)
@@ -181,7 +186,7 @@ export function renderBoxGraph({nodes, links}, ignore, callback) {
.data(nodes)
.join("g")
.classed("node", true)
- renderNode(rects, boxlineNodeMargin, recipeColors, ignore)
+ renderNode(rects, boxlineNodeMargin, "left", recipeColors, ignore)
svg.append("g")
.classed("overlay", true)
diff --git a/calc.html b/calc.html
index dd6cd19..149cb4e 100644
--- a/calc.html
+++ b/calc.html
@@ -26,7 +26,7 @@
var handlers = {}
@@ -115,6 +116,16 @@
+
+ Graph direction:
+
+
diff --git a/events.js b/events.js
index 6079b18..54f7adc 100644
--- a/events.js
+++ b/events.js
@@ -93,6 +93,8 @@ export function setVisualizerType(vt) {
export function changeVisType(event) {
visualizerType = event.target.value
+ visualizerDirection = getDefaultVisDirection()
+ d3.select(`#${visualizerDirection}_direction`).property("checked", true)
spec.display()
}
@@ -109,6 +111,29 @@ export function changeVisRender(event) {
spec.display()
}
+export let visualizerDirection
+
+export function getDefaultVisDirection() {
+ if (visualizerType === "sankey") {
+ return "right"
+ } else {
+ return "down"
+ }
+}
+
+export function isDefaultVisDirection() {
+ return visualizerDirection === getDefaultVisDirection()
+}
+
+export function setVisualizerDirection(vd) {
+ visualizerDirection = vd
+}
+
+export function changeVisDir(event) {
+ visualizerDirection = event.target.value
+ spec.display()
+}
+
// Number of SVG coordinate points per zoom level.
const ZOOM_SCALE = 100
// Number of distinct zoom "steps."
diff --git a/fragment.js b/fragment.js
index b7873b7..42e97a1 100644
--- a/fragment.js
+++ b/fragment.js
@@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
import { DEFAULT_RATE, DEFAULT_RATE_PRECISION, DEFAULT_COUNT_PRECISION, DEFAULT_FORMAT } from "./align.js"
-import { DEFAULT_TAB, currentTab, DEFAULT_VISUALIZER, visualizerType, DEFAULT_RENDER, visualizerRender } from "./events.js"
+import { DEFAULT_TAB, currentTab, DEFAULT_VISUALIZER, visualizerType, DEFAULT_RENDER, visualizerRender, isDefaultVisDirection, visualizerDirection } from "./events.js"
import { spec, DEFAULT_BELT, DEFAULT_FUEL } from "./factory.js"
import { Rational } from "./rational.js"
import { currentMod, DEFAULT_TITLE, DEFAULT_COLOR_SCHEME, colorScheme } from "./settings.js"
@@ -102,6 +102,9 @@ export function formatSettings(excludeTitle, overrideTab, targets) {
if (visualizerRender !== DEFAULT_RENDER) {
settings += "vr=" + visualizerRender + "&"
}
+ if (!isDefaultVisDirection()) {
+ settings += "vd=" + visualizerDirection + "&"
+ }
settings += "items="
let targetStrings = []
diff --git a/graph.js b/graph.js
index 6b4a73e..5f75f6b 100644
--- a/graph.js
+++ b/graph.js
@@ -103,7 +103,14 @@ export function imageViewBox(obj) {
return `${x1} ${y1} ${PX_WIDTH-1} ${PX_HEIGHT-1}`
}
-export function renderNode(rects, nodeMargin, recipeColors, ignore) {
+export function renderNode(rects, nodeMargin, justification, recipeColors, ignore) {
+ rects.each(d => {
+ if (justification === "left") {
+ d.labelX = d.x0
+ } else {
+ d.labelX = (d.x0 + d.x1)/2 - d.width/2
+ }
+ })
// main rect
rects.append("rect")
.attr("x", d => d.x0)
@@ -125,7 +132,7 @@ export function renderNode(rects, nodeMargin, recipeColors, ignore) {
// recipe icon
labeledNode.append("svg")
.attr("viewBox", d => imageViewBox(d.recipe))
- .attr("x", d => d.x0 + nodeMargin + 0.5)
+ .attr("x", d => d.labelX + nodeMargin + 0.5)
.attr("y", d => (d.y0 + d.y1) / 2 - iconSize/2 + 0.5)
.attr("width", iconSize)
.attr("height", iconSize)
@@ -136,7 +143,7 @@ export function renderNode(rects, nodeMargin, recipeColors, ignore) {
.attr("height", sheetHeight)
// node text (building count, or plain rate if no building)
labeledNode.append("text")
- .attr("x", d => d.x0 + nodeMargin + iconSize + (d.building === null ? 0 : colonWidth + iconSize) /*+ 5*/)
+ .attr("x", d => d.labelX + nodeMargin + iconSize + (d.building === null ? 0 : colonWidth + iconSize) /*+ 5*/)
.attr("y", d => (d.y0 + d.y1) / 2)
.attr("dy", "0.35em")
.text(d => d.text())
@@ -144,18 +151,18 @@ export function renderNode(rects, nodeMargin, recipeColors, ignore) {
// colon
buildingNode.append("circle")
.classed("colon", true)
- .attr("cx", d => d.x0 + nodeMargin + iconSize + colonWidth/2)
+ .attr("cx", d => d.labelX + nodeMargin + iconSize + colonWidth/2)
.attr("cy", d => (d.y0 + d.y1) / 2 - 4)
.attr("r", 1)
buildingNode.append("circle")
.classed("colon", true)
- .attr("cx", d => d.x0 + nodeMargin + iconSize + colonWidth/2)
+ .attr("cx", d => d.labelX + nodeMargin + iconSize + colonWidth/2)
.attr("cy", d => (d.y0 + d.y1) / 2 + 4)
.attr("r", 1)
// building icon
buildingNode.append("svg")
.attr("viewBox", d => imageViewBox(d.building))
- .attr("x", d => d.x0 + iconSize + colonWidth + nodeMargin + 0.5)
+ .attr("x", d => d.labelX + iconSize + colonWidth + nodeMargin + 0.5)
.attr("y", d => (d.y0 + d.y1) / 2 - iconSize/2 + 0.5)
.attr("width", iconSize)
.attr("height", iconSize)
diff --git a/sankey.js b/sankey.js
index 62ab88d..a918ffa 100644
--- a/sankey.js
+++ b/sankey.js
@@ -95,7 +95,7 @@ function linkPath(d) {
return makeCurve(1, 0, x0, y0, x1, y1, d.width)
}
-export function renderSankey(data, ignore) {
+export function renderSankey(data, direction, ignore) {
let maxNodeWidth = 0
let testSVG = d3.select("body").append("svg")
.classed("sankey test", true)
@@ -110,9 +110,17 @@ export function renderSankey(data, ignore) {
text.remove()
testSVG.remove()
+ let nw, np
+ if (direction === "down") {
+ nw = nodePadding
+ np = maxNodeWidth
+ } else if (direction === "right") {
+ nw = maxNodeWidth
+ np = nodePadding
+ }
let sankey = d3sankey.sankey()
- .nodeWidth(maxNodeWidth)
- .nodePadding(nodePadding)
+ .nodeWidth(nw)
+ .nodePadding(np)
.nodeAlign(d3sankey.sankeyRight)
.maxNodeHeight(maxNodeHeight)
.linkLength(columnWidth)
@@ -121,6 +129,9 @@ export function renderSankey(data, ignore) {
for (let link of links) {
link.curve = linkPath(link)
+ if (direction === "down") {
+ link.curve = link.curve.transpose()
+ }
let belts = []
if (link.beltCount !== null) {
let dy = link.width / link.beltCount.toFloat()
@@ -136,6 +147,13 @@ export function renderSankey(data, ignore) {
link.belts = belts
}
+ if (direction === "down") {
+ for (let node of nodes) {
+ [node.x0, node.y0] = [node.y0, node.x0];
+ [node.x1, node.y1] = [node.y1, node.x1];
+ }
+ }
+
let svg = d3.select("svg#graph")
.classed("sankey", true)
svg.selectAll("g").remove()
@@ -148,7 +166,11 @@ export function renderSankey(data, ignore) {
.join("g")
.classed("node", true)
- renderNode(rects, sankeyNodeMargin, recipeColors, ignore)
+ let nodeJust = "left"
+ if (direction === "down") {
+ nodeJust = "center"
+ }
+ renderNode(rects, sankeyNodeMargin, nodeJust, recipeColors, ignore)
// Link paths
let link = svg.append("g")
@@ -190,23 +212,38 @@ export function renderSankey(data, ignore) {
.attr("stroke-width", 1)
link.append("title")
.text(d => `${d.source.name} \u2192 ${d.target.name}\n${spec.format.rate(d.rate)}`)
- link.filter(d => d.extra)
+ let linkIcon = link.filter(d => d.extra)
.append("svg")
.attr("viewBox", d => imageViewBox(d.item))
.attr("x", d => d.source.x1 + 2.25)
.attr("y", d => d.y0 - iconSize/4 + 0.25)
.attr("width", iconSize/2)
.attr("height", iconSize/2)
- .append("image")
- .attr("xlink:href", "images/sprite-sheet-" + sheetHash + ".png")
- .attr("width", sheetWidth)
- .attr("height", sheetHeight)
- link.append("text")
+ linkIcon.append("image")
+ .attr("xlink:href", "images/sprite-sheet-" + sheetHash + ".png")
+ .attr("width", sheetWidth)
+ .attr("height", sheetHeight)
+ if (direction === "down") {
+ linkIcon
+ .attr("x", d => d.y0 - iconSize/4 + 0.25)
+ .attr("y", d => d.source.y1 + 2.25)
+ }
+ let linkLabel = link.append("text")
.attr("x", d => d.source.x1 + 2 + (d.extra ? iconSize/2 : 0))
.attr("y", d => d.y0)
.attr("dy", "0.35em")
.attr("text-anchor", "start")
.text(d => (d.extra ? "\u00d7 " : "") + spec.format.rate(d.rate) + "/" + spec.format.rateName)
+ if (direction === "down") {
+ linkLabel
+ .attr("x", null)
+ .attr("y", null)
+ .attr("transform", d => {
+ let x = d.y0
+ let y = d.source.y1 + 2 + (d.extra ? 16 : 0)
+ return `translate(${x},${y}) rotate(90)`
+ })
+ }
// Overlay transparent rect on top of each node, for click events.
let rectElements = svg.selectAll("g.node rect").nodes()
diff --git a/settings.js b/settings.js
index 52fe422..82c586c 100644
--- a/settings.js
+++ b/settings.js
@@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.*/
import { DEFAULT_RATE, DEFAULT_RATE_PRECISION, DEFAULT_COUNT_PRECISION, DEFAULT_FORMAT, longRateNames } from "./align.js"
import { colorSchemes } from "./color.js"
-import { DEFAULT_TAB, clickTab, DEFAULT_VISUALIZER, visualizerType, setVisualizerType, DEFAULT_RENDER, visualizerRender, setVisualizerRender } from "./events.js"
+import { DEFAULT_TAB, clickTab, DEFAULT_VISUALIZER, visualizerType, setVisualizerType, DEFAULT_RENDER, visualizerRender, setVisualizerRender, visualizerDirection, getDefaultVisDirection, setVisualizerDirection } from "./events.js"
import { spec, DEFAULT_BELT, DEFAULT_FUEL, buildingSort } from "./factory.js"
import { getRecipeGroups } from "./groups.js"
import { changeMod } from "./init.js"
@@ -40,7 +40,7 @@ export let MODIFICATIONS = new Map([
//["space-age", new Modification("Space Age 2.0.6", "space-age-2.0.6.json", false)],
])
-let DEFAULT_MODIFICATION = "2-0-6"
+let DEFAULT_MODIFICATION = "2-0-7"
// Ideally we'd write this as a generalized function, but for now we can hard-
// code these version upgrades.
@@ -525,6 +525,12 @@ function renderVisualizer(settings) {
setVisualizerRender(DEFAULT_RENDER)
}
d3.select(`#${visualizerRender}_render`).property("checked", true)
+ if (settings.has("vd")) {
+ setVisualizerDirection(settings.get("vd"))
+ } else {
+ setVisualizerDirection(getDefaultVisDirection())
+ }
+ d3.select(`#${visualizerDirection}_direction`).property("checked", true)
}
// default module
diff --git a/visualize.js b/visualize.js
index 3b8cf69..17a000c 100644
--- a/visualize.js
+++ b/visualize.js
@@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
import { renderBoxGraph } from "./boxline2.js"
-import { visualizerType, visualizerRender, installSVGEvents } from "./events.js"
+import { visualizerType, visualizerRender, visualizerDirection, installSVGEvents } from "./events.js"
import { spec } from "./factory.js"
import { iconSize, colonWidth } from "./graph.js"
import { zero, one } from "./rational.js"
@@ -247,9 +247,9 @@ export function renderTotals(totals, ignore) {
}
if (visualizerType === "sankey") {
- renderSankey(data, ignore)
+ renderSankey(data, visualizerDirection, ignore)
callback()
} else {
- renderBoxGraph(data, ignore, callback)
+ renderBoxGraph(data, visualizerDirection, ignore, callback)
}
}