fix: Formatting + avg => geomMean
This commit is contained in:
parent
e610d798d3
commit
5ec31fa365
530
lib/findPaths.js
530
lib/findPaths.js
|
@ -1,285 +1,328 @@
|
||||||
/* Imports*/
|
/* Imports*/
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { numToAlphabeticalString, formatLargeOrSmall, avg } from "../lib/utils.js"
|
import {
|
||||||
|
numToAlphabeticalString,
|
||||||
|
formatLargeOrSmall,
|
||||||
|
avg,
|
||||||
|
geomMean,
|
||||||
|
} from "../lib/utils.js";
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
|
|
||||||
const pathPlusLink = (pathSoFar, link) => {
|
const pathPlusLink = (pathSoFar, link) => {
|
||||||
return [...pathSoFar, link]
|
return [...pathSoFar, link];
|
||||||
// previously: pathSoFar.concat(link).flat()
|
// previously: pathSoFar.concat(link).flat()
|
||||||
// Note that concat is not destructive
|
// Note that concat is not destructive
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
|
||||||
}
|
};
|
||||||
|
|
||||||
async function findPathsWithoutPrunning({
|
async function findPathsWithoutPrunning({
|
||||||
sourceElementId, targetElementId,
|
sourceElementId,
|
||||||
maxLengthOfPath, pathSoFar,
|
targetElementId,
|
||||||
links, nodes
|
maxLengthOfPath,
|
||||||
|
pathSoFar,
|
||||||
|
links,
|
||||||
|
nodes,
|
||||||
}) {
|
}) {
|
||||||
// This is an un-used function which might make findPaths more understandable
|
// This is an un-used function which might make findPaths more understandable
|
||||||
// It uses the same recursive functionality
|
// It uses the same recursive functionality
|
||||||
// but has no path prunning
|
// but has no path prunning
|
||||||
let paths = []
|
let paths = [];
|
||||||
|
|
||||||
/* Path traversing */
|
/* Path traversing */
|
||||||
if (maxLengthOfPath > 0) {
|
if (maxLengthOfPath > 0) {
|
||||||
for (let link of links) { // vs let link of linksNow in findPaths
|
for (let link of links) {
|
||||||
if (
|
// vs let link of linksNow in findPaths
|
||||||
((link.source == sourceElementId) && (link.target == targetElementId)) ||
|
if (
|
||||||
((link.source == targetElementId) && (link.target == sourceElementId))
|
(link.source == sourceElementId && link.target == targetElementId) ||
|
||||||
) { // direct Path
|
(link.source == targetElementId && link.target == sourceElementId)
|
||||||
let newPath = pathPlusLink(pathSoFar, link)
|
) {
|
||||||
paths.push(newPath)
|
// direct Path
|
||||||
} else if ((link.source == sourceElementId)) {
|
let newPath = pathPlusLink(pathSoFar, link);
|
||||||
let newPaths = await findPaths({
|
paths.push(newPath);
|
||||||
pathSoFar: pathPlusLink(pathSoFar, link),
|
} else if (link.source == sourceElementId) {
|
||||||
maxLengthOfPath: (maxLengthOfPath - 1),
|
let newPaths = await findPaths({
|
||||||
sourceElementId: link.target,
|
pathSoFar: pathPlusLink(pathSoFar, link),
|
||||||
targetElementId,
|
maxLengthOfPath: maxLengthOfPath - 1,
|
||||||
links: links, // vs let link of linksInner in findPaths
|
sourceElementId: link.target,
|
||||||
nodes
|
targetElementId,
|
||||||
})
|
links: links, // vs let link of linksInner in findPaths
|
||||||
if (newPaths.length != 0) {
|
nodes,
|
||||||
paths.push(...newPaths)
|
});
|
||||||
}
|
if (newPaths.length != 0) {
|
||||||
} else if ((link.target == sourceElementId)) {
|
paths.push(...newPaths);
|
||||||
let newPaths = await findPaths({
|
|
||||||
pathSoFar: pathPlusLink(pathSoFar, link),
|
|
||||||
maxLengthOfPath: (maxLengthOfPath - 1),
|
|
||||||
sourceElementId: link.source,
|
|
||||||
targetElementId,
|
|
||||||
links: links, // vs let link of linksInner in findPaths
|
|
||||||
nodes
|
|
||||||
})
|
|
||||||
if (newPaths.length != 0) {
|
|
||||||
paths.push(...newPaths)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if (link.target == sourceElementId) {
|
||||||
|
let newPaths = await findPaths({
|
||||||
|
pathSoFar: pathPlusLink(pathSoFar, link),
|
||||||
|
maxLengthOfPath: maxLengthOfPath - 1,
|
||||||
|
sourceElementId: link.source,
|
||||||
|
targetElementId,
|
||||||
|
links: links, // vs let link of linksInner in findPaths
|
||||||
|
nodes,
|
||||||
|
});
|
||||||
|
if (newPaths.length != 0) {
|
||||||
|
paths.push(...newPaths);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return paths
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findPaths({
|
async function findPaths({
|
||||||
sourceElementId, sourceElementPosition,
|
sourceElementId,
|
||||||
targetElementId, targetElementPosition,
|
sourceElementPosition,
|
||||||
maxLengthOfPath, pathSoFar,
|
targetElementId,
|
||||||
links, nodes
|
targetElementPosition,
|
||||||
|
maxLengthOfPath,
|
||||||
|
pathSoFar,
|
||||||
|
links,
|
||||||
|
nodes,
|
||||||
}) {
|
}) {
|
||||||
// This is the key path finding function
|
// This is the key path finding function
|
||||||
// It finds the path from one element to another, recursively
|
// It finds the path from one element to another, recursively
|
||||||
// It used to be very computationally expensive until I added
|
// It used to be very computationally expensive until I added
|
||||||
// the path prunning step: Instead of traversing all links,
|
// the path prunning step: Instead of traversing all links,
|
||||||
// traverse only those which are between the origin and target links
|
// traverse only those which are between the origin and target links
|
||||||
// this requires us to have a notion of "between"
|
// this requires us to have a notion of "between"
|
||||||
|
|
||||||
let paths = []
|
let paths = [];
|
||||||
|
|
||||||
/* Path prunning*/
|
/* Path prunning*/
|
||||||
let minPos = Math.min(sourceElementPosition, targetElementPosition)
|
let minPos = Math.min(sourceElementPosition, targetElementPosition);
|
||||||
let maxPos = Math.max(sourceElementPosition, targetElementPosition)
|
let maxPos = Math.max(sourceElementPosition, targetElementPosition);
|
||||||
let linksInner = links.filter(link =>
|
let linksInner = links.filter(
|
||||||
(minPos <= link.sourceElementPosition && link.sourceElementPosition <= maxPos) &&
|
(link) =>
|
||||||
(minPos <= link.targetElementPosition && link.targetElementPosition <= maxPos)
|
minPos <= link.sourceElementPosition &&
|
||||||
)
|
link.sourceElementPosition <= maxPos &&
|
||||||
let linksNow = linksInner.filter(link => (link.source == sourceElementId || link.target == sourceElementId))
|
minPos <= link.targetElementPosition &&
|
||||||
|
link.targetElementPosition <= maxPos
|
||||||
|
);
|
||||||
|
let linksNow = linksInner.filter(
|
||||||
|
(link) => link.source == sourceElementId || link.target == sourceElementId
|
||||||
|
);
|
||||||
|
|
||||||
/* Path traversing */
|
/* Path traversing */
|
||||||
if (maxLengthOfPath > 0) {
|
if (maxLengthOfPath > 0) {
|
||||||
for (let link of linksNow) {
|
for (let link of linksNow) {
|
||||||
if (
|
if (
|
||||||
((link.source == sourceElementId) && (link.target == targetElementId)) ||
|
(link.source == sourceElementId && link.target == targetElementId) ||
|
||||||
((link.source == targetElementId) && (link.target == sourceElementId))
|
(link.source == targetElementId && link.target == sourceElementId)
|
||||||
) { // direct Path
|
) {
|
||||||
let newPath = pathPlusLink(pathSoFar, link)
|
// direct Path
|
||||||
paths.push(newPath)
|
let newPath = pathPlusLink(pathSoFar, link);
|
||||||
} else if ((link.source == sourceElementId)) {
|
paths.push(newPath);
|
||||||
let newPaths = await findPaths({
|
} else if (link.source == sourceElementId) {
|
||||||
pathSoFar: pathPlusLink(pathSoFar, link),
|
let newPaths = await findPaths({
|
||||||
maxLengthOfPath: (maxLengthOfPath - 1),
|
pathSoFar: pathPlusLink(pathSoFar, link),
|
||||||
sourceElementPosition: link.sourceElementPosition,
|
maxLengthOfPath: maxLengthOfPath - 1,
|
||||||
sourceElementId: link.target,
|
sourceElementPosition: link.sourceElementPosition,
|
||||||
targetElementId, targetElementPosition,
|
sourceElementId: link.target,
|
||||||
links: linksInner,
|
targetElementId,
|
||||||
nodes
|
targetElementPosition,
|
||||||
})
|
links: linksInner,
|
||||||
if (newPaths.length != 0) {
|
nodes,
|
||||||
paths.push(...newPaths)
|
});
|
||||||
}
|
if (newPaths.length != 0) {
|
||||||
} else if ((link.target == sourceElementId)) {
|
paths.push(...newPaths);
|
||||||
let newPaths = await findPaths({
|
|
||||||
pathSoFar: pathPlusLink(pathSoFar, link),
|
|
||||||
maxLengthOfPath: (maxLengthOfPath - 1),
|
|
||||||
sourceElementPosition: link.sourceElementPosition,
|
|
||||||
sourceElementId: link.source,
|
|
||||||
targetElementPosition,
|
|
||||||
targetElementId,
|
|
||||||
links: linksInner,
|
|
||||||
nodes
|
|
||||||
})
|
|
||||||
if (newPaths.length != 0) {
|
|
||||||
paths.push(...newPaths)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if (link.target == sourceElementId) {
|
||||||
|
let newPaths = await findPaths({
|
||||||
|
pathSoFar: pathPlusLink(pathSoFar, link),
|
||||||
|
maxLengthOfPath: maxLengthOfPath - 1,
|
||||||
|
sourceElementPosition: link.sourceElementPosition,
|
||||||
|
sourceElementId: link.source,
|
||||||
|
targetElementPosition,
|
||||||
|
targetElementId,
|
||||||
|
links: linksInner,
|
||||||
|
nodes,
|
||||||
|
});
|
||||||
|
if (newPaths.length != 0) {
|
||||||
|
paths.push(...newPaths);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return paths
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findDistance({
|
async function findDistance({
|
||||||
sourceElementId, sourceElementPosition,
|
sourceElementId,
|
||||||
targetElementId, targetElementPosition,
|
sourceElementPosition,
|
||||||
nodes, links
|
targetElementId,
|
||||||
|
targetElementPosition,
|
||||||
|
nodes,
|
||||||
|
links,
|
||||||
}) {
|
}) {
|
||||||
// This function gets all possible paths using findPaths
|
// This function gets all possible paths using findPaths
|
||||||
// then orders them correctly in the for loop
|
// then orders them correctly in the for loop
|
||||||
// (by flipping the distance to 1/distance when necessary)
|
// (by flipping the distance to 1/distance when necessary)
|
||||||
// and then gets the array of weights for the different paths.
|
// and then gets the array of weights for the different paths.
|
||||||
console.log(`findDistance@findPaths.js from ${sourceElementPosition} to ${targetElementPosition}`)
|
console.log(
|
||||||
|
`findDistance@findPaths.js from ${sourceElementPosition} to ${targetElementPosition}`
|
||||||
|
);
|
||||||
|
|
||||||
let maxLengthOfPath = Math.abs(sourceElementPosition - targetElementPosition)
|
let maxLengthOfPath = Math.abs(sourceElementPosition - targetElementPosition);
|
||||||
let paths = await findPaths({
|
let paths = await findPaths({
|
||||||
sourceElementId, sourceElementPosition,
|
sourceElementId,
|
||||||
targetElementId, targetElementPosition,
|
sourceElementPosition,
|
||||||
links, nodes,
|
targetElementId,
|
||||||
maxLengthOfPath, pathSoFar: []
|
targetElementPosition,
|
||||||
});
|
links,
|
||||||
|
nodes,
|
||||||
|
maxLengthOfPath,
|
||||||
|
pathSoFar: [],
|
||||||
|
});
|
||||||
|
|
||||||
let weights = []
|
let weights = [];
|
||||||
for (let path of paths) {
|
for (let path of paths) {
|
||||||
let currentSource = sourceElementId
|
let currentSource = sourceElementId;
|
||||||
let weight = 1
|
let weight = 1;
|
||||||
for (let element of path) {
|
for (let element of path) {
|
||||||
let distance = 0
|
let distance = 0;
|
||||||
if (element.source == currentSource) {
|
if (element.source == currentSource) {
|
||||||
distance = element.distance
|
distance = element.distance;
|
||||||
currentSource = element.target
|
currentSource = element.target;
|
||||||
} else if (element.target == currentSource) {
|
} else if (element.target == currentSource) {
|
||||||
distance = 1 / Number(element.distance)
|
distance = 1 / Number(element.distance);
|
||||||
currentSource = element.source
|
currentSource = element.source;
|
||||||
}
|
}
|
||||||
weight = weight * distance
|
weight = weight * distance;
|
||||||
|
|
||||||
}
|
|
||||||
weights.push(weight)
|
|
||||||
}
|
}
|
||||||
return weights
|
weights.push(weight);
|
||||||
|
}
|
||||||
|
return weights;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findDistancesForAllElements({ nodes, links }) {
|
async function findDistancesForAllElements({ nodes, links }) {
|
||||||
// Simple wrapper function around findDistance
|
// Simple wrapper function around findDistance
|
||||||
// Needs to find the reference point first
|
// Needs to find the reference point first
|
||||||
console.log("findDistancesForAllElements@findPaths.js")
|
console.log("findDistancesForAllElements@findPaths.js");
|
||||||
/* Get or build reference element */
|
/* Get or build reference element */
|
||||||
let referenceElements = nodes.filter(x => x.isReferenceValue)
|
let referenceElements = nodes.filter((x) => x.isReferenceValue);
|
||||||
let midpoint = Math.round(nodes.length / 2)
|
let midpoint = Math.round(nodes.length / 2);
|
||||||
let referenceElement = referenceElements.length > 0 ? referenceElements[0] : nodes[midpoint]
|
let referenceElement =
|
||||||
console.log(`referenceElement.position: ${referenceElement.position}`)
|
referenceElements.length > 0 ? referenceElements[0] : nodes[midpoint];
|
||||||
|
console.log(`referenceElement.position: ${referenceElement.position}`);
|
||||||
|
|
||||||
/* Get distances. */
|
/* Get distances. */
|
||||||
let distances = nodes.map(node => {
|
let distances = nodes.map((node) => {
|
||||||
if (node.isReferenceValue || (node.id == referenceElement.id)) {
|
if (node.isReferenceValue || node.id == referenceElement.id) {
|
||||||
return [1]
|
return [1];
|
||||||
} else {
|
} else {
|
||||||
console.log("node")
|
console.log("node");
|
||||||
console.log(node)
|
console.log(node);
|
||||||
let distance = findDistance({
|
let distance = findDistance({
|
||||||
sourceElementId: referenceElement.id,
|
sourceElementId: referenceElement.id,
|
||||||
sourceElementPosition: referenceElement.position,
|
sourceElementPosition: referenceElement.position,
|
||||||
targetElementId: node.id,
|
targetElementId: node.id,
|
||||||
targetElementPosition: node.position,
|
targetElementPosition: node.position,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
links: links,
|
links: links,
|
||||||
})
|
});
|
||||||
return distance
|
return distance;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
distances = await Promise.all(distances)
|
distances = await Promise.all(distances);
|
||||||
return distances
|
return distances;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function buildRows({ isListOrdered, orderedList, listOfElements, links, rows, setTableRows }) {
|
export async function buildRows({
|
||||||
console.log("buildRows@findPaths.js")
|
isListOrdered,
|
||||||
// This function is used in pages/comparisonView.js to create the rows that will be displayed.
|
orderedList,
|
||||||
// it is in there because it needs to be deployed after isListOrdered becomes true,
|
listOfElements,
|
||||||
// and using an useEffect inside CreateTable was too messy.
|
links,
|
||||||
if (isListOrdered && !(orderedList.length < listOfElements.length) && rows.length == 0) {
|
rows,
|
||||||
let nodes = []
|
setTableRows,
|
||||||
let positionDictionary = ({})
|
}) {
|
||||||
orderedList.forEach((id, pos) => {
|
console.log("buildRows@findPaths.js");
|
||||||
nodes.push({ ...listOfElements[id], position: pos })
|
// This function is used in pages/comparisonView.js to create the rows that will be displayed.
|
||||||
positionDictionary[id] = pos
|
// it is in there because it needs to be deployed after isListOrdered becomes true,
|
||||||
})
|
// and using an useEffect inside CreateTable was too messy.
|
||||||
links = links.map(link => ({
|
if (
|
||||||
...link,
|
isListOrdered &&
|
||||||
sourceElementPosition: positionDictionary[link.source],
|
!(orderedList.length < listOfElements.length) &&
|
||||||
targetElementPosition: positionDictionary[link.target]
|
rows.length == 0
|
||||||
}))
|
) {
|
||||||
|
let nodes = [];
|
||||||
|
let positionDictionary = {};
|
||||||
|
orderedList.forEach((id, pos) => {
|
||||||
|
nodes.push({ ...listOfElements[id], position: pos });
|
||||||
|
positionDictionary[id] = pos;
|
||||||
|
});
|
||||||
|
links = links.map((link) => ({
|
||||||
|
...link,
|
||||||
|
sourceElementPosition: positionDictionary[link.source],
|
||||||
|
targetElementPosition: positionDictionary[link.target],
|
||||||
|
}));
|
||||||
|
|
||||||
let distances = await findDistancesForAllElements({ nodes, links })
|
let distances = await findDistancesForAllElements({ nodes, links });
|
||||||
rows = nodes.map((element, i) => ({
|
rows = nodes.map((element, i) => ({
|
||||||
id: numToAlphabeticalString(element.position),
|
id: numToAlphabeticalString(element.position),
|
||||||
position: element.position,
|
position: element.position,
|
||||||
name: element.name,
|
name: element.name,
|
||||||
distances: distances[i]
|
distances: distances[i],
|
||||||
}))
|
}));
|
||||||
console.log(rows)
|
console.log(rows);
|
||||||
setTableRows(rows)
|
setTableRows(rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CreateTable({ tableRows }) {
|
export function CreateTable({ tableRows }) {
|
||||||
/* This function receives a list of rows, and displays them nicely. */
|
/* This function receives a list of rows, and displays them nicely. */
|
||||||
function abridgeArrayAndDisplay(array) {
|
function abridgeArrayAndDisplay(array) {
|
||||||
let newArray
|
let newArray;
|
||||||
let formatForDisplay
|
let formatForDisplay;
|
||||||
if (array.length > 10) {
|
if (array.length > 10) {
|
||||||
newArray = array.slice(0, 9)
|
newArray = array.slice(0, 9);
|
||||||
formatForDisplay = newArray.map(d => formatLargeOrSmall(d))
|
formatForDisplay = newArray.map((d) => formatLargeOrSmall(d));
|
||||||
formatForDisplay[9] = "..."
|
formatForDisplay[9] = "...";
|
||||||
} else {
|
} else {
|
||||||
newArray = array
|
newArray = array;
|
||||||
formatForDisplay = newArray.map(d => formatLargeOrSmall(d))
|
formatForDisplay = newArray.map((d) => formatLargeOrSmall(d));
|
||||||
}
|
|
||||||
let result = JSON.stringify(formatForDisplay, null, 2).replaceAll(`"`, "")
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
return (
|
let result = JSON.stringify(formatForDisplay, null, 2).replaceAll(`"`, "");
|
||||||
<div>
|
return result;
|
||||||
<table className="w-full">
|
}
|
||||||
<thead>
|
return (
|
||||||
<tr >
|
<div>
|
||||||
<th >Id</th>
|
<table className="w-full">
|
||||||
<th> </th>
|
<thead>
|
||||||
<th >Position</th>
|
<tr>
|
||||||
<th> </th>
|
<th>Id</th>
|
||||||
<th >Element</th>
|
<th> </th>
|
||||||
<th> </th>
|
<th>Position</th>
|
||||||
<th >Possible relative values</th>
|
<th> </th>
|
||||||
<th> </th>
|
<th>Element</th>
|
||||||
<th >Average relative value</th>
|
<th> </th>
|
||||||
</tr>
|
<th>Possible relative values</th>
|
||||||
</thead>
|
<th> </th>
|
||||||
<tbody>
|
<th>Average relative value</th>
|
||||||
{tableRows.map(row => <tr key={row.id}>
|
</tr>
|
||||||
<td className="" >{row.id}</td>
|
</thead>
|
||||||
<td> </td>
|
<tbody>
|
||||||
<td className="" >{row.position}</td>
|
{tableRows.map((row) => (
|
||||||
<td> </td>
|
<tr key={row.id}>
|
||||||
<td className="">{row.name}</td>
|
<td className="">{row.id}</td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td className="">{abridgeArrayAndDisplay(row.distances)}</td>
|
<td className="">{row.position}</td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td className="">{formatLargeOrSmall(avg(row.distances))}</td>
|
<td className="">{row.name}</td>
|
||||||
</tr>
|
<td> </td>
|
||||||
)}
|
<td className="">{abridgeArrayAndDisplay(row.distances)}</td>
|
||||||
</tbody>
|
<td> </td>
|
||||||
</table>
|
<td className="">
|
||||||
</div>
|
{formatLargeOrSmall(geomMean(row.distances))}
|
||||||
)
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Testing */
|
/* Testing */
|
||||||
|
@ -300,4 +343,5 @@ console.log(JSON.stringify(paths, null, 2))
|
||||||
/*
|
/*
|
||||||
let distances = findDistance({sourceElementId:2, targetElementId:4, links, nodes})
|
let distances = findDistance({sourceElementId:2, targetElementId:4, links, nodes})
|
||||||
console.log(distances)
|
console.log(distances)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -1,145 +1,171 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import * as d3 from 'd3';
|
import * as d3 from "d3";
|
||||||
import { toLocale, truncateValueForDisplay, numToAlphabeticalString, formatLargeOrSmall } from "./utils.js"
|
import {
|
||||||
|
toLocale,
|
||||||
|
truncateValueForDisplay,
|
||||||
|
numToAlphabeticalString,
|
||||||
|
formatLargeOrSmall,
|
||||||
|
} from "./utils.js";
|
||||||
|
|
||||||
let getlength = (number) => number.toString().length;
|
let getlength = (number) => number.toString().length;
|
||||||
|
|
||||||
export function removeOldSvg() {
|
export function removeOldSvg() {
|
||||||
d3.select("#graph").select("svg").remove();
|
d3.select("#graph").select("svg").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawGraphInner({ nodes, links }) {
|
function drawGraphInner({ nodes, links }) {
|
||||||
|
// List of node ids for convenience
|
||||||
|
var nodeids = nodes.map((node) => node.id);
|
||||||
|
var positionById = {};
|
||||||
|
nodeids.forEach((nodeid, i) => (positionById[nodeid] = i));
|
||||||
|
console.log("NodeIds/positionById");
|
||||||
|
console.log(nodeids);
|
||||||
|
console.log(positionById);
|
||||||
|
|
||||||
// List of node ids for convenience
|
// Calculate the dimensions
|
||||||
var nodeids = nodes.map(node => node.id)
|
// let margin = { top: 0, right: 30, bottom: 20, left: 30 };
|
||||||
var positionById = {}
|
// let width = 900 - margin.left - margin.right;
|
||||||
nodeids.forEach((nodeid, i) => positionById[nodeid] = i)
|
|
||||||
console.log("NodeIds/positionById")
|
|
||||||
console.log(nodeids)
|
|
||||||
console.log(positionById)
|
|
||||||
|
|
||||||
// Calculate the dimensions
|
let initialWindowWidth = window.innerWidth;
|
||||||
// let margin = { top: 0, right: 30, bottom: 20, left: 30 };
|
let margin = { top: 0, right: 10, bottom: 30, left: 10 };
|
||||||
// let width = 900 - margin.left - margin.right;
|
let width = initialWindowWidth * 0.8 - margin.left - margin.right;
|
||||||
|
|
||||||
let initialWindowWidth = window.innerWidth
|
|
||||||
let margin = { top: 0, right: 10, bottom: 30, left: 10 };
|
|
||||||
let width = initialWindowWidth*0.7 - margin.left - margin.right;
|
|
||||||
|
|
||||||
var x = d3.scalePoint()
|
|
||||||
.range([0, width])
|
|
||||||
.domain(nodeids)
|
|
||||||
|
|
||||||
let heights = links.map(link => {
|
var x = d3.scalePoint().range([0, width]).domain(nodeids);
|
||||||
let start = x(positionById[link.source])
|
|
||||||
let end = x(positionById[link.target])
|
let heights = links.map((link) => {
|
||||||
return Math.abs(start - end) / 2 + 70 // Magic constant.
|
let start = x(positionById[link.source]);
|
||||||
|
let end = x(positionById[link.target]);
|
||||||
|
return Math.abs(start - end) / 2 + 70; // Magic constant.
|
||||||
|
});
|
||||||
|
console.log(heights);
|
||||||
|
let maxheight = Math.max(...heights);
|
||||||
|
let height = maxheight - margin.top - margin.bottom;
|
||||||
|
console.log(`height: ${height}`);
|
||||||
|
|
||||||
|
// Build the d3 graph
|
||||||
|
|
||||||
|
removeOldSvg();
|
||||||
|
var svg = d3
|
||||||
|
.select("#graph")
|
||||||
|
.append("svg")
|
||||||
|
.attr("width", width + margin.left + margin.right)
|
||||||
|
.attr("height", height + margin.top + margin.bottom)
|
||||||
|
.append("g")
|
||||||
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||||
|
|
||||||
|
// A linear scale to position the nodes on the X axis
|
||||||
|
|
||||||
|
// Add the circle for the nodes
|
||||||
|
svg
|
||||||
|
.selectAll("mynodes")
|
||||||
|
.data(nodes)
|
||||||
|
.enter()
|
||||||
|
.append("circle")
|
||||||
|
.attr("cx", function (d) {
|
||||||
|
return x(d.id);
|
||||||
})
|
})
|
||||||
console.log(heights)
|
.attr("cy", height - 30)
|
||||||
let maxheight = Math.max(...heights)
|
.attr("r", 8)
|
||||||
let height = maxheight - margin.top - margin.bottom;
|
.style("fill", "#69b3a2");
|
||||||
console.log(`height: ${height}`)
|
|
||||||
|
|
||||||
// Build the d3 graph
|
// And give them a label
|
||||||
|
svg
|
||||||
|
.selectAll("mylabels")
|
||||||
|
.data(nodes)
|
||||||
|
.enter()
|
||||||
|
.append("text")
|
||||||
|
.attr("x", function (d) {
|
||||||
|
return x(d.id);
|
||||||
|
})
|
||||||
|
.attr("y", height - 10)
|
||||||
|
.text(function (d) {
|
||||||
|
return numToAlphabeticalString(d.position);
|
||||||
|
})
|
||||||
|
.style("text-anchor", "middle");
|
||||||
|
|
||||||
removeOldSvg()
|
// Add the links
|
||||||
var svg = d3.select("#graph")
|
svg
|
||||||
.append("svg")
|
.selectAll("mylinks")
|
||||||
.attr("width", width + margin.left + margin.right)
|
.data(links)
|
||||||
.attr("height", height + margin.top + margin.bottom)
|
.enter()
|
||||||
.append("g")
|
.append("path")
|
||||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
.attr("d", function (d) {
|
||||||
|
let start = x(d.source);
|
||||||
|
// X position of start node on the X axis
|
||||||
|
let end = x(d.target);
|
||||||
|
// X position of end node
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
"M",
|
||||||
|
start,
|
||||||
|
height - 30,
|
||||||
|
// the arc starts at the coordinate x=start, y=height-30 (where the starting node is)
|
||||||
|
"A",
|
||||||
|
// This means we're gonna build an elliptical arc
|
||||||
|
(start - end) / 2,
|
||||||
|
",",
|
||||||
|
// Next 2 lines are the coordinates of the inflexion point. Height of this point is proportional with start - end distance
|
||||||
|
(start - end) / 2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
",",
|
||||||
|
start < end ? 1 : 0,
|
||||||
|
end,
|
||||||
|
",",
|
||||||
|
height - 30,
|
||||||
|
]
|
||||||
|
// We always want the arc on top. So if end is before start, putting 0 here turn the arc upside down.
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.style("fill", "none")
|
||||||
|
.attr("stroke", "black");
|
||||||
|
|
||||||
// A linear scale to position the nodes on the X axis
|
// labels for links
|
||||||
|
svg
|
||||||
|
.selectAll("mylinks")
|
||||||
// Add the circle for the nodes
|
.data(links)
|
||||||
svg
|
.enter()
|
||||||
.selectAll("mynodes")
|
.append("text")
|
||||||
.data(nodes)
|
.attr("x", function (d) {
|
||||||
.enter()
|
let start = x(d.source);
|
||||||
.append("circle")
|
// X position of start node on the X axis
|
||||||
.attr("cx", function (d) { return (x(d.id)) })
|
let end = x(d.target);
|
||||||
.attr("cy", height - 30)
|
// X position of end node
|
||||||
.attr("r", 8)
|
return start + (end - start) / 2; //-4*getlength(d.distance)
|
||||||
.style("fill", "#69b3a2")
|
})
|
||||||
|
.attr("y", function (d) {
|
||||||
// And give them a label
|
let start = x(d.source);
|
||||||
svg
|
// X position of start node on the X axis
|
||||||
.selectAll("mylabels")
|
let end = x(d.target);
|
||||||
.data(nodes)
|
// X position of end node
|
||||||
.enter()
|
return height - 32 - Math.abs(start - end) / 2; //height-30
|
||||||
.append("text")
|
})
|
||||||
.attr("x", function (d) { return (x(d.id)) })
|
.text(function (d) {
|
||||||
.attr("y", height - 10)
|
return formatLargeOrSmall(Number(d.distance));
|
||||||
.text(function (d) { return numToAlphabeticalString(d.position) })
|
// return (truncateValueForDisplay(Number(d.distance)))
|
||||||
.style("text-anchor", "middle")
|
//return(Number(d.distance).toPrecision(2).toString())
|
||||||
|
})
|
||||||
// Add the links
|
.style("text-anchor", "middle");
|
||||||
svg
|
|
||||||
.selectAll('mylinks')
|
|
||||||
.data(links)
|
|
||||||
.enter()
|
|
||||||
.append('path')
|
|
||||||
.attr('d', function (d) {
|
|
||||||
let start = x(d.source)
|
|
||||||
// X position of start node on the X axis
|
|
||||||
let end = x(d.target)
|
|
||||||
// X position of end node
|
|
||||||
return ['M',
|
|
||||||
start,
|
|
||||||
height - 30,
|
|
||||||
// the arc starts at the coordinate x=start, y=height-30 (where the starting node is)
|
|
||||||
'A',
|
|
||||||
// This means we're gonna build an elliptical arc
|
|
||||||
(start - end) / 2, ',',
|
|
||||||
// Next 2 lines are the coordinates of the inflexion point. Height of this point is proportional with start - end distance
|
|
||||||
(start - end) / 2, 0, 0, ',',
|
|
||||||
start < end ? 1 : 0, end, ',', height - 30]
|
|
||||||
// We always want the arc on top. So if end is before start, putting 0 here turn the arc upside down.
|
|
||||||
.join(' ');
|
|
||||||
})
|
|
||||||
.style("fill", "none")
|
|
||||||
.attr("stroke", "black")
|
|
||||||
|
|
||||||
// labels for links
|
|
||||||
svg
|
|
||||||
.selectAll('mylinks')
|
|
||||||
.data(links)
|
|
||||||
.enter()
|
|
||||||
.append("text")
|
|
||||||
.attr("x", function (d) {
|
|
||||||
let start = x(d.source)
|
|
||||||
// X position of start node on the X axis
|
|
||||||
let end = x(d.target)
|
|
||||||
// X position of end node
|
|
||||||
return start + (end - start) / 2 //-4*getlength(d.distance)
|
|
||||||
})
|
|
||||||
.attr("y", function (d) {
|
|
||||||
let start = x(d.source)
|
|
||||||
// X position of start node on the X axis
|
|
||||||
let end = x(d.target)
|
|
||||||
// X position of end node
|
|
||||||
return height - 32 - (Math.abs(start - end) / 2)//height-30
|
|
||||||
})
|
|
||||||
.text(function (d) {
|
|
||||||
return formatLargeOrSmall(Number(d.distance))
|
|
||||||
// return (truncateValueForDisplay(Number(d.distance)))
|
|
||||||
//return(Number(d.distance).toPrecision(2).toString())
|
|
||||||
})
|
|
||||||
.style("text-anchor", "middle")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DrawGraph({ isListOrdered, orderedList, listOfElements, links }) {
|
export function DrawGraph({
|
||||||
if (isListOrdered) {
|
isListOrdered,
|
||||||
let nodes = orderedList.map((id, pos) => ({ ...listOfElements[id], position: pos }))
|
orderedList,
|
||||||
drawGraphInner({ nodes, links });
|
listOfElements,
|
||||||
}
|
links,
|
||||||
return (
|
}) {
|
||||||
<div>
|
if (isListOrdered) {
|
||||||
<div id="graph">
|
let nodes = orderedList.map((id, pos) => ({
|
||||||
</div>
|
...listOfElements[id],
|
||||||
</div>
|
position: pos,
|
||||||
);
|
}));
|
||||||
|
drawGraphInner({ nodes, links });
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div id="graph"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
134
lib/utils.js
134
lib/utils.js
|
@ -1,78 +1,94 @@
|
||||||
import crypto from "crypto"
|
import crypto from "crypto";
|
||||||
|
|
||||||
export const hashString = (string) => crypto.createHash('md5').update(string).digest('hex');
|
export const hashString = (string) =>
|
||||||
const id = x => x
|
crypto.createHash("md5").update(string).digest("hex");
|
||||||
export const transformSliderValueToActualValue = id
|
const id = (x) => x;
|
||||||
export const transformSliderValueToPracticalValue = id
|
export const transformSliderValueToActualValue = id;
|
||||||
|
export const transformSliderValueToPracticalValue = id;
|
||||||
|
|
||||||
export const _transformSliderValueToActualValue = value => 10 ** value //>= 2 ? Math.round(10 ** value) : Math.round(10 * 10 ** value) / 10
|
export const _transformSliderValueToActualValue = (value) => 10 ** value; //>= 2 ? Math.round(10 ** value) : Math.round(10 * 10 ** value) / 10
|
||||||
export const toLocale = x => Number(x).toLocaleString()
|
export const toLocale = (x) => Number(x).toLocaleString();
|
||||||
export const truncateValueForDisplay = value => {
|
export const truncateValueForDisplay = (value) => {
|
||||||
if (value > 10) {
|
if (value > 10) {
|
||||||
return Number(Math.round(value).toPrecision(2))
|
return Number(Math.round(value).toPrecision(2));
|
||||||
} else if (value > 1) {
|
} else if (value > 1) {
|
||||||
return Math.round(value * 10) / 10
|
return Math.round(value * 10) / 10;
|
||||||
} else if (value < 1) {
|
} else if (value < 1) {
|
||||||
|
}
|
||||||
}
|
};
|
||||||
}
|
export const _transformSliderValueToPracticalValue = (value) =>
|
||||||
export const _transformSliderValueToPracticalValue = value => truncateValueForDisplay(transformSliderValueToActualValue(value))
|
truncateValueForDisplay(transformSliderValueToActualValue(value));
|
||||||
|
|
||||||
export function sleep(ms) {
|
export function sleep(ms) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function numToAlphabeticalString(num) {
|
export function numToAlphabeticalString(num) {
|
||||||
// https://stackoverflow.com/questions/45787459/convert-number-to-alphabet-string-javascript/45787487
|
// https://stackoverflow.com/questions/45787459/convert-number-to-alphabet-string-javascript/45787487
|
||||||
num = num + 1
|
num = num + 1;
|
||||||
var s = '', t;
|
var s = "",
|
||||||
while (num > 0) {
|
t;
|
||||||
t = (num - 1) % 26;
|
while (num > 0) {
|
||||||
s = String.fromCharCode(65 + t) + s;
|
t = (num - 1) % 26;
|
||||||
num = (num - t) / 26 | 0;
|
s = String.fromCharCode(65 + t) + s;
|
||||||
}
|
num = ((num - t) / 26) | 0;
|
||||||
return `#${s}` || undefined;
|
}
|
||||||
|
return `#${s}` || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatLargeOrSmall(num) {
|
export function formatLargeOrSmall(num) {
|
||||||
if (num > 1) {
|
if (num > 1) {
|
||||||
return toLocale(truncateValueForDisplay(num))
|
return toLocale(truncateValueForDisplay(num));
|
||||||
} else if (num > 0) {
|
} else if (num > 0) {
|
||||||
return num.toFixed(-Math.floor(Math.log(num) / Math.log(10)) + 1);
|
return num.toFixed(-Math.floor(Math.log(num) / Math.log(10)) + 1);
|
||||||
} else if (num < -1) {
|
} else if (num < -1) {
|
||||||
return num.toFixed(-Math.floor(Math.log(-num) / Math.log(10)) + 1);
|
return num.toFixed(-Math.floor(Math.log(-num) / Math.log(10)) + 1);
|
||||||
} else {
|
} else {
|
||||||
return toLocale(num)//return "~0"
|
return toLocale(num); //return "~0"
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const firstFewMaxMergeSortSequence = [0, 0, 1, 3, 5, 8, 11, 14, 17, 21, 25, 29, 33, 37, 41, 45, 49, 54, 59, 64, 69, 74, 79, 84, 89, 94, 99, 104, 109, 114, 119, 124, 129, 135, 141, 147, 153, 159, 165, 171, 177, 183, 189, 195, 201, 207, 213, 219, 225, 231, 237, 243, 249, 255, 261, 267, 273, 279, 285]
|
const firstFewMaxMergeSortSequence = [
|
||||||
|
0, 0, 1, 3, 5, 8, 11, 14, 17, 21, 25, 29, 33, 37, 41, 45, 49, 54, 59, 64, 69,
|
||||||
|
74, 79, 84, 89, 94, 99, 104, 109, 114, 119, 124, 129, 135, 141, 147, 153, 159,
|
||||||
|
165, 171, 177, 183, 189, 195, 201, 207, 213, 219, 225, 231, 237, 243, 249,
|
||||||
|
255, 261, 267, 273, 279, 285,
|
||||||
|
];
|
||||||
|
|
||||||
export function maxMergeSortSteps(n) {
|
export function maxMergeSortSteps(n) {
|
||||||
if (n < firstFewMaxMergeSortSequence.length) {
|
if (n < firstFewMaxMergeSortSequence.length) {
|
||||||
return firstFewMaxMergeSortSequence[n]
|
return firstFewMaxMergeSortSequence[n];
|
||||||
} else {
|
} else {
|
||||||
return maxMergeSortSteps(Math.floor(n / 2)) + maxMergeSortSteps(Math.ceil(n / 2)) + n - 1
|
return (
|
||||||
}
|
maxMergeSortSteps(Math.floor(n / 2)) +
|
||||||
|
maxMergeSortSteps(Math.ceil(n / 2)) +
|
||||||
|
n -
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function expectedNumMergeSortSteps(n) {
|
export function expectedNumMergeSortSteps(n) {
|
||||||
// https://cs.stackexchange.com/questions/82862/expected-number-of-comparisons-in-a-merge-step
|
// https://cs.stackexchange.com/questions/82862/expected-number-of-comparisons-in-a-merge-step
|
||||||
// n-2 for each step, so (n-2) + (n-2)/2 + (n-2)/4 + ...
|
// n-2 for each step, so (n-2) + (n-2)/2 + (n-2)/4 + ...
|
||||||
// ~ 2*(n-2) -1 = 2*n - 3
|
// ~ 2*(n-2) -1 = 2*n - 3
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return 0
|
return 0;
|
||||||
} else if (n == 1) {
|
} else if (n == 1) {
|
||||||
return 0
|
return 0;
|
||||||
} else if (n == 2) {
|
} else if (n == 2) {
|
||||||
return 1
|
return 1;
|
||||||
} else if (n == 3) {
|
} else if (n == 3) {
|
||||||
return 2
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return Math.ceil((n ** 2) / (n + 2)) + expectedNumMergeSortSteps(Math.ceil(n / 2))
|
return (
|
||||||
}
|
Math.ceil(n ** 2 / (n + 2)) + expectedNumMergeSortSteps(Math.ceil(n / 2))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const avg = arr => arr.reduce((a, b) => (a + b), 0) / arr.length
|
export const avg = (arr) => arr.reduce((a, b) => a + b, 0) / arr.length;
|
||||||
|
|
||||||
export const increasingList = (n) => Array.from(Array(n).keys())
|
export const geomMean = (arr) =>
|
||||||
|
arr.reduce((a, b) => a * b, 1) ^ (1 / arr.length);
|
||||||
|
|
||||||
|
export const increasingList = (n) => Array.from(Array(n).keys());
|
||||||
|
|
Loading…
Reference in New Issue
Block a user