@euphrasiologist/lwphylo 1.2.26 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -6
- package/dist/index.cjs +80 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +80 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
A lightweight, low level javascript library to plot phylogenies from a Newick file. It uses no dependencies on any other package, but is designed to be given to the D3 library for visualisation.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Website
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Visit https://euphrasiologist.github.io/lwPhylo/ to see examples and live rendering of trees. Can even paste your own in.
|
|
8
8
|
|
|
9
|
-
###
|
|
9
|
+
### Functionality
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
It goes over the three tree layout functions, and hopefully is all quite straightforward.
|
|
11
|
+
Newick trees can be parsed using the `readTree()` function. This object can then be wrapped in three main functions; `rectangleLayout()` to produce a "regular" phylogenetic tree, `radialLayout()` to produce a circular phylogeny, and `unrooted()` to produce an unrooted tree via the equal angle layout algorithm.
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
Need a tree to experiment with? `randomTree(nTips, { maxBranchLength, labelPrefix, seed })` generates a random bifurcating tree in the same node shape as `readTree()`, ready to pass straight into any of the layout functions.
|
|
15
14
|
|
|
16
15
|
### Acknowledgements
|
|
17
16
|
|
package/dist/index.cjs
CHANGED
|
@@ -959,6 +959,85 @@ function parentFisheye(d, data) {
|
|
|
959
959
|
return parent ? { px: parent.fisheye.x, py: parent.fisheye.y } : null;
|
|
960
960
|
}
|
|
961
961
|
|
|
962
|
+
/**
|
|
963
|
+
* Generate a random bifurcating tree with `nTips` tips, in the same
|
|
964
|
+
* parent/children node shape produced by readTree().
|
|
965
|
+
*
|
|
966
|
+
* Topology is grown by repeatedly picking a random extant lineage to split
|
|
967
|
+
* (a Yule/coalescent-style process), so internal branching order is random
|
|
968
|
+
* rather than a fixed balanced/caterpillar shape. Branch lengths are drawn
|
|
969
|
+
* uniformly from [0, maxBranchLength).
|
|
970
|
+
*/
|
|
971
|
+
|
|
972
|
+
function randomTree(nTips = 10, {
|
|
973
|
+
maxBranchLength = 1,
|
|
974
|
+
labelPrefix = 't',
|
|
975
|
+
seed = null
|
|
976
|
+
} = {}) {
|
|
977
|
+
if (!Number.isInteger(nTips) || nTips < 1) {
|
|
978
|
+
throw new Error("nTips must be a positive integer");
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
const random = seed == null ? Math.random : mulberry32(seed);
|
|
982
|
+
|
|
983
|
+
let nodeId = 0;
|
|
984
|
+
const makeNode = (parent) => ({
|
|
985
|
+
parent,
|
|
986
|
+
children: [],
|
|
987
|
+
id: nodeId++,
|
|
988
|
+
label: '',
|
|
989
|
+
branchLength: null
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
const root = makeNode(null);
|
|
993
|
+
|
|
994
|
+
if (nTips === 1) {
|
|
995
|
+
root.label = `${labelPrefix}1`;
|
|
996
|
+
return root;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// start with two lineages hanging off the root
|
|
1000
|
+
let lineages = [makeNode(root), makeNode(root)];
|
|
1001
|
+
root.children.push(...lineages);
|
|
1002
|
+
|
|
1003
|
+
// repeatedly split a random lineage until we have nTips of them
|
|
1004
|
+
while (lineages.length < nTips) {
|
|
1005
|
+
const i = Math.floor(random() * lineages.length);
|
|
1006
|
+
const parent = lineages[i];
|
|
1007
|
+
const left = makeNode(parent);
|
|
1008
|
+
const right = makeNode(parent);
|
|
1009
|
+
parent.children.push(left, right);
|
|
1010
|
+
lineages.splice(i, 1, left, right);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// assign branch lengths to every non-root node, and tip labels in
|
|
1014
|
+
// left-to-right order
|
|
1015
|
+
let tipIndex = 0;
|
|
1016
|
+
const assign = (node) => {
|
|
1017
|
+
for (const child of node.children) {
|
|
1018
|
+
child.branchLength = random() * maxBranchLength;
|
|
1019
|
+
assign(child);
|
|
1020
|
+
}
|
|
1021
|
+
if (node.children.length === 0) {
|
|
1022
|
+
node.label = `${labelPrefix}${++tipIndex}`;
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
assign(root);
|
|
1026
|
+
|
|
1027
|
+
return root;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// small deterministic PRNG so `seed` gives reproducible trees
|
|
1031
|
+
function mulberry32(seed) {
|
|
1032
|
+
let a = seed >>> 0;
|
|
1033
|
+
return function () {
|
|
1034
|
+
a |= 0; a = (a + 0x6D2B79F5) | 0;
|
|
1035
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
1036
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
1037
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
962
1041
|
/**
|
|
963
1042
|
* Parse a Newick tree string into a doubly-linked list of JS Objects.
|
|
964
1043
|
* Assigns labels, branch lengths, and node IDs (tips before internals if input emits them that way).
|
|
@@ -1862,6 +1941,7 @@ exports.parentFisheye = parentFisheye;
|
|
|
1862
1941
|
exports.phisheye = phisheye;
|
|
1863
1942
|
exports.polarToCartesian = polarToCartesian;
|
|
1864
1943
|
exports.radialLayout = radialLayout;
|
|
1944
|
+
exports.randomTree = randomTree;
|
|
1865
1945
|
exports.readTree = readTree;
|
|
1866
1946
|
exports.rectangleLayout = rectangleLayout;
|
|
1867
1947
|
exports.subTree = subTree;
|