UNPKG

transitive-js

Version:

A tool for generating dynamic stylized transit maps that are easy to understand.

219 lines (182 loc) 6.62 kB
import { forEach } from 'lodash' import Renderer from './renderer' /** * A Renderer subclass for the default network rendering engine. * * @param {Object} the main Transitive object */ export default class DefaultRenderer extends Renderer { render() { super.render() const display = this.transitive.display const network = this.transitive.network display.styler = this.transitive.styler // TODO: restore legend? // Process vertices/points const focusedPoints = [] const unfocusedPoints = [] forEach(network.graph.vertices, (vertex) => { vertex.point.clearRenderData() if (!vertex.point.isFocused()) unfocusedPoints.push(vertex.point) else focusedPoints.push(vertex.point) }) forEach(network.renderedEdges, (rEdge) => { rEdge.refreshRenderData(display) }) // Create arrays of focused, unfocused RenderedSegments const focusedSegments = [] const unfocusedSegments = [] forEach(network.paths, (path) => { forEach(path.segments, (pathSegment) => { forEach(pathSegment.renderedSegments, (renderedSegment) => { if (renderedSegment.isFocused()) focusedSegments.push(renderedSegment) else unfocusedSegments.push(renderedSegment) }) }) }) // Render elements in the following order: // 1. Unfocused segments unfocusedSegments.forEach((rs) => rs.render(display)) // 2. Unfocused points forEach(unfocusedPoints, (pt) => pt.render(display)) // 3. Focused segments focusedSegments.forEach((rs) => rs.render(display)) // 4. Focused points forEach(focusedPoints, (pt) => pt.render(display)) // TODO: draw the edge-based points /* forEach(network.graph.edges, edge => { forEach(edge.pointArray, point => { point.render(display) }) }) */ // 5. Labels const labels = this.transitive.labeler.doLayout() forEach(labels.pointLabels, (label) => label.render(display)) forEach(labels.segmentLabels, (label) => label.render(display)) // Keep internal collection of rendered segments this.renderedSegments = focusedSegments.concat(unfocusedSegments) } refreshSegmentRenderData() { forEach(this.transitive.network.renderedEdges, (rEdge) => { rEdge.refreshRenderData(this.transitive.display) }) // try intersecting adjacent rendered edges to create a smooth transition const isectKeys = [] // keep track of edge-edge intersections we've already computed forEach(this.transitive.network.paths, (path) => { forEach(path.segments, (pathSegment) => { forEach(pathSegment.renderedSegments, (rSegment) => { for (let s = 0; s < rSegment.renderedEdges.length - 1; s++) { const rEdge1 = rSegment.renderedEdges[s] const rEdge2 = rSegment.renderedEdges[s + 1] const key = rEdge1.getId() + '_' + rEdge2.getId() if (isectKeys.indexOf(key) !== -1) continue if (rEdge1.graphEdge.isInternal && rEdge2.graphEdge.isInternal) { rEdge1.intersect(rEdge2) } isectKeys.push(key) } }) }) }) } /** * sortElements */ sortElements() { this.renderedSegments.sort(function (a, b) { return a.compareTo(b) }) const focusBaseZIndex = 100000 forEach(this.renderedSegments, (rSegment, index) => { rSegment.zIndex = index * 10 + (rSegment.isFocused() ? focusBaseZIndex : 0) }) forEach(this.transitive.network.graph.vertices, (vertex) => { const point = vertex.point point.zIndex = point.zIndex + (point.isFocused() ? focusBaseZIndex : 0) }) this.transitive.display.svg .selectAll('.transitive-sortable') .sort(function (a, b) { const aIndex = typeof a.getZIndex === 'function' ? a.getZIndex() : a.owner.getZIndex() const bIndex = typeof b.getZIndex === 'function' ? b.getZIndex() : b.owner.getZIndex() return aIndex - bIndex }) } /** * focusPath */ focusPath(path) { let pathRenderedSegments = [] const graph = this.transitive.network.graph if (path) { // if we're focusing a specific path pathRenderedSegments = path.getRenderedSegments() // un-focus all internal points forEach(graph.edges, (edge) => { edge.pointArray.forEach(function (point, i) { point.setAllPatternsFocused(false) }) }) } else { // if we're returning to 'all-focused' mode // re-focus all internal points forEach(graph.edges, (edge) => { forEach(edge.pointArray, (point, i) => { point.setAllPatternsFocused(true) }) }) } // Keep track of changed segments / points for transition animation (currently disabled) const focusChangeSegments = [] const focusedVertexPoints = [] forEach(this.renderedSegments, (rSegment) => { if (path && pathRenderedSegments.indexOf(rSegment) === -1) { if (rSegment.isFocused()) focusChangeSegments.push(rSegment) rSegment.setFocused(false) } else { if (!rSegment.isFocused()) focusChangeSegments.push(rSegment) rSegment.setFocused(true) focusedVertexPoints.push(rSegment.pathSegment.startVertex().point) focusedVertexPoints.push(rSegment.pathSegment.endVertex().point) } }) const focusChangePoints = [] forEach(graph.vertices, (vertex) => { const point = vertex.point if (focusedVertexPoints.indexOf(point) !== -1) { if (!point.isFocused()) focusChangePoints.push(point) point.setFocused(true) } else { if (point.isFocused()) focusChangePoints.push(point) point.setFocused(false) } }) // bring the focused elements to the front for the transition // if (path) this.sortElements(); // TODO: restore transitions /* // create a transition callback function that invokes refresh() after all transitions complete var n = 0 var refreshOnEnd = (transition, callback) => { transition .each(() => { ++n }) .on('end', () => { if (!--n) this.transitive.refresh() }) } // run the transitions on the affected elements forEach(focusChangeSegments, segment => { segment.runFocusTransition(this.transitive.display, refreshOnEnd) }) forEach(focusChangePoints, point => { point.runFocusTransition(this.transitive.display, refreshOnEnd) }) */ this.transitive.render() } }