aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/App.vue5
-rw-r--r--src/components/AncestryBar.vue2
-rw-r--r--src/components/SearchModal.vue2
-rw-r--r--src/components/SettingsModal.vue2
-rw-r--r--src/components/Tile.vue2
-rw-r--r--src/components/TileInfoModal.vue2
-rw-r--r--src/components/TutorialPane.vue2
-rw-r--r--src/layout.ts2
-rw-r--r--src/lib.ts139
-rw-r--r--src/util.ts135
10 files changed, 146 insertions, 147 deletions
diff --git a/src/App.vue b/src/App.vue
index eae0a16..a8c98f8 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -14,13 +14,12 @@ import SearchIcon from './components/icon/SearchIcon.vue';
import PlayIcon from './components/icon/PlayIcon.vue';
import SettingsIcon from './components/icon/SettingsIcon.vue';
import HelpIcon from './components/icon/HelpIcon.vue';
-// Classes and types
+// Other
// Note: Import paths lack a .ts or .js extension because .ts makes vue-tsc complain, and .js makes vite complain
import {TolNode, TolMap, UiOptions, Action} from './lib';
import {LayoutNode, LayoutOptions, LayoutTreeChg} from './layout';
-// Functions
-import {arraySum, randWeightedChoice, getScrollBarWidth, getBreakpoint} from './lib';
import {initLayoutTree, initLayoutMap, tryLayout} from './layout';
+import {arraySum, randWeightedChoice, getScrollBarWidth, getBreakpoint} from './util';
// Type representing auto-mode actions
type AutoAction = 'move across' | 'move down' | 'move up' | Action;
diff --git a/src/components/AncestryBar.vue b/src/components/AncestryBar.vue
index 85a8f99..c973195 100644
--- a/src/components/AncestryBar.vue
+++ b/src/components/AncestryBar.vue
@@ -1,8 +1,8 @@
<script lang="ts">
import {defineComponent, PropType} from 'vue';
import Tile from './Tile.vue'
-import {LayoutNode, LayoutOptions} from '../layout';
import {TolMap, UiOptions} from '../lib';
+import {LayoutNode, LayoutOptions} from '../layout';
// Displays a sequence of nodes, representing ancestors from a tree-of-life root to a currently-active root
export default defineComponent({
diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue
index 7b0c23e..1a3ef27 100644
--- a/src/components/SearchModal.vue
+++ b/src/components/SearchModal.vue
@@ -3,8 +3,8 @@ import {defineComponent, PropType} from 'vue';
import SearchIcon from './icon/SearchIcon.vue';
import LogInIcon from './icon/LogInIcon.vue';
import InfoIcon from './icon/InfoIcon.vue';
-import {LayoutNode} from '../layout';
import {TolMap, SearchSugg, SearchSuggResponse, UiOptions} from '../lib';
+import {LayoutNode} from '../layout';
// Displays a search box, and sends search requests
export default defineComponent({
diff --git a/src/components/SettingsModal.vue b/src/components/SettingsModal.vue
index 36e3564..5be06b7 100644
--- a/src/components/SettingsModal.vue
+++ b/src/components/SettingsModal.vue
@@ -2,8 +2,8 @@
import {defineComponent, PropType} from 'vue';
import CloseIcon from './icon/CloseIcon.vue';
import RButton from './RButton.vue';
-import {LayoutOptions} from '../layout';
import {UiOptions} from '../lib';
+import {LayoutOptions} from '../layout';
// Displays configurable options, and sends option-change requests
export default defineComponent({
diff --git a/src/components/Tile.vue b/src/components/Tile.vue
index 22a7333..e03f13e 100644
--- a/src/components/Tile.vue
+++ b/src/components/Tile.vue
@@ -3,7 +3,7 @@ import {defineComponent, PropType} from 'vue';
import InfoIcon from './icon/InfoIcon.vue';
import {LayoutNode, LayoutOptions} from '../layout';
import {TolNode, TolMap, UiOptions} from '../lib';
-import {capitalizeWords} from '../lib';
+import {capitalizeWords} from '../util';
// Displays one, or a hierarchy of, tree-of-life nodes, as a 'tile'
export default defineComponent({
diff --git a/src/components/TileInfoModal.vue b/src/components/TileInfoModal.vue
index 6fcb023..423a2f5 100644
--- a/src/components/TileInfoModal.vue
+++ b/src/components/TileInfoModal.vue
@@ -4,7 +4,7 @@ import CloseIcon from './icon/CloseIcon.vue';
import Tile from './Tile.vue'
import {LayoutNode, LayoutOptions} from '../layout';
import {TolNode, TolMap, UiOptions, DescInfo, ImgInfo, TileInfoResponse} from '../lib';
-import {capitalizeWords} from '../lib';
+import {capitalizeWords} from '../util';
// Displays information about a tree-of-life node
export default defineComponent({
diff --git a/src/components/TutorialPane.vue b/src/components/TutorialPane.vue
index 0bbe8e9..6fa7bac 100644
--- a/src/components/TutorialPane.vue
+++ b/src/components/TutorialPane.vue
@@ -1,8 +1,8 @@
<script lang="ts">
import {defineComponent, PropType} from 'vue';
import CloseIcon from './icon/CloseIcon.vue';
-import RButton from './RButton.vue';
import {Action, UiOptions} from '../lib';
+import RButton from './RButton.vue';
export default defineComponent({
props: {
diff --git a/src/layout.ts b/src/layout.ts
index 883192f..9b4a58f 100644
--- a/src/layout.ts
+++ b/src/layout.ts
@@ -7,7 +7,7 @@
*/
import {TolMap} from './lib';
-import {range, arraySum, linspace, limitVals, updateAscSeq} from './lib';
+import {range, arraySum, linspace, limitVals, updateAscSeq} from './util';
// Represents a node/tree that holds layout data for a TolNode node/tree
export class LayoutNode {
diff --git a/src/lib.ts b/src/lib.ts
index e210e97..4fd999b 100644
--- a/src/lib.ts
+++ b/src/lib.ts
@@ -1,7 +1,8 @@
/*
- * Types/classes
+ * Project-wide types/classes
*/
+
// Used for tree-of-life representation
// Maps tree-of-life node names to node objects
export type TolMap = Map<string, TolNode>;
@@ -87,139 +88,3 @@ export type UiOptions = {
disabledActions: Set<Action>,
scrollGap: number, // Size of scroll bar, in px
};
-
-/*
- * General utility functions
- */
-
-export type Breakpoint = 'sm' | 'md' | 'lg'; // These represent screen sizes
-export function getBreakpoint(): Breakpoint {
- let w = window.innerWidth;
- if (w < 768){
- return 'sm';
- } else if (w < 1024){
- return 'md';
- } else {
- return 'lg';
- }
-}
-
-// Returns [0 ... len]
-export function range(len: number): number[] {
- return [...Array(len).keys()];
-}
-// Returns sum of array values
-export function arraySum(array: number[]): number {
- return array.reduce((x,y) => x+y);
-}
-// Returns an array of increasing evenly-spaced numbers from 'start' to 'end' with size 'size'
-export function linspace(start: number, end: number, size: number): number[] {
- let step = (end - start) / (size - 1);
- let ar = [];
- for (let i = 0; i < size; i++){
- ar.push(start + step * i);
- }
- return ar;
-}
-// Returns array copy with vals clipped to within [min,max], redistributing to compensate
-// Returns null on failure
-export function limitVals(arr: number[], min: number, max: number): number[] | null {
- let vals = [...arr];
- let clipped = new Array(vals.length).fill(false);
- let owedChg = 0; // Stores total change made after clipping values
- while (true){
- // Clip values
- for (let i = 0; i < vals.length; i++){
- if (clipped[i]){
- continue;
- }
- if (vals[i] < min){
- owedChg += vals[i] - min;
- vals[i] = min;
- clipped[i] = true;
- } else if (vals[i] > max){
- owedChg += vals[i] - max;
- vals[i] = max;
- clipped[i] = true;
- }
- }
- if (Math.abs(owedChg) < Number.EPSILON){
- return vals;
- }
- // Compensate for changes made
- let indicesToUpdate = (owedChg > 0) ?
- range(vals.length).filter(idx => vals[idx] < max) :
- range(vals.length).filter(idx => vals[idx] > min);
- if (indicesToUpdate.length == 0){
- return null;
- }
- for (let i of indicesToUpdate){
- vals[i] += owedChg / indicesToUpdate.length;
- }
- owedChg = 0;
- }
-}
-// Usable to iterate through possible int arrays with ascending values in the range 0 to maxLen-1, starting with [0]
- // eg: With maxLen 3, updates [0] to [0,1], then to [0,2], then [0,1,2]
-// Returns false when there is no next array
-export function updateAscSeq(seq: number[], maxLen: number): boolean {
- // Try increasing last element, then preceding elements, then extending the array
- let i = seq.length - 1;
- while (true){
- if (i > 0 && seq[i] < (maxLen - 1) - (seq.length - 1 - i)){
- seq[i]++;
- return true;
- } else if (i > 0){
- i--;
- } else {
- if (seq.length < maxLen){
- seq.push(0);
- seq.splice(0, seq.length, ...range(seq.length));
- return true;
- } else {
- return false;
- }
- }
- }
-}
-// Given a non-empty array of non-negative weights, returns an array index chosen with weighted pseudorandomness
-// Returns null if array contains all zeros
-export function randWeightedChoice(weights: number[]): number | null {
- let thresholds = Array(weights.length);
- let sum = 0;
- for (let i = 0; i < weights.length; i++){
- sum += weights[i];
- thresholds[i] = sum;
- }
- let rand = Math.random();
- for (let i = 0; i < weights.length; i++){
- if (rand <= thresholds[i] / sum){
- return i;
- }
- }
- return null;
-}
-// Returns a string with words first-letter capitalised
-export function capitalizeWords(str: string){
- str = str.replace(/\b\w/g, x => x.toUpperCase()); // '\b' matches word boundary, '\w' is like [a-zA-Z0-9_]
- str = str.replace(/(\w)'S/, '$1\'s'); // Avoid cases like "traveler's tree" -> "Traveler'S Tree"
- return str;
-}
-// Dynamically obtains scroll bar width
-// From stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript
-export function getScrollBarWidth(){
- // Create hidden outer div
- let outer = document.createElement('div');
- outer.style.visibility = 'hidden';
- outer.style.overflow = 'scroll';
- document.body.appendChild(outer);
- // Create inner div
- let inner = document.createElement('div');
- outer.appendChild(inner);
- // Get width difference
- let scrollBarWidth = outer.offsetWidth - inner.offsetWidth;
- // Remove temporary divs
- outer.parentNode!.removeChild(outer);
- //
- return scrollBarWidth;
-}
diff --git a/src/util.ts b/src/util.ts
new file mode 100644
index 0000000..139e668
--- /dev/null
+++ b/src/util.ts
@@ -0,0 +1,135 @@
+/*
+ * General utility functions
+ */
+
+export type Breakpoint = 'sm' | 'md' | 'lg'; // These represent screen sizes
+export function getBreakpoint(): Breakpoint {
+ let w = window.innerWidth;
+ if (w < 768){
+ return 'sm';
+ } else if (w < 1024){
+ return 'md';
+ } else {
+ return 'lg';
+ }
+}
+
+// Returns [0 ... len]
+export function range(len: number): number[] {
+ return [...Array(len).keys()];
+}
+// Returns sum of array values
+export function arraySum(array: number[]): number {
+ return array.reduce((x,y) => x+y);
+}
+// Returns an array of increasing evenly-spaced numbers from 'start' to 'end' with size 'size'
+export function linspace(start: number, end: number, size: number): number[] {
+ let step = (end - start) / (size - 1);
+ let ar = [];
+ for (let i = 0; i < size; i++){
+ ar.push(start + step * i);
+ }
+ return ar;
+}
+// Returns array copy with vals clipped to within [min,max], redistributing to compensate
+// Returns null on failure
+export function limitVals(arr: number[], min: number, max: number): number[] | null {
+ let vals = [...arr];
+ let clipped = new Array(vals.length).fill(false);
+ let owedChg = 0; // Stores total change made after clipping values
+ while (true){
+ // Clip values
+ for (let i = 0; i < vals.length; i++){
+ if (clipped[i]){
+ continue;
+ }
+ if (vals[i] < min){
+ owedChg += vals[i] - min;
+ vals[i] = min;
+ clipped[i] = true;
+ } else if (vals[i] > max){
+ owedChg += vals[i] - max;
+ vals[i] = max;
+ clipped[i] = true;
+ }
+ }
+ if (Math.abs(owedChg) < Number.EPSILON){
+ return vals;
+ }
+ // Compensate for changes made
+ let indicesToUpdate = (owedChg > 0) ?
+ range(vals.length).filter(idx => vals[idx] < max) :
+ range(vals.length).filter(idx => vals[idx] > min);
+ if (indicesToUpdate.length == 0){
+ return null;
+ }
+ for (let i of indicesToUpdate){
+ vals[i] += owedChg / indicesToUpdate.length;
+ }
+ owedChg = 0;
+ }
+}
+// Usable to iterate through possible int arrays with ascending values in the range 0 to maxLen-1, starting with [0]
+ // eg: With maxLen 3, updates [0] to [0,1], then to [0,2], then [0,1,2]
+// Returns false when there is no next array
+export function updateAscSeq(seq: number[], maxLen: number): boolean {
+ // Try increasing last element, then preceding elements, then extending the array
+ let i = seq.length - 1;
+ while (true){
+ if (i > 0 && seq[i] < (maxLen - 1) - (seq.length - 1 - i)){
+ seq[i]++;
+ return true;
+ } else if (i > 0){
+ i--;
+ } else {
+ if (seq.length < maxLen){
+ seq.push(0);
+ seq.splice(0, seq.length, ...range(seq.length));
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+}
+// Given a non-empty array of non-negative weights, returns an array index chosen with weighted pseudorandomness
+// Returns null if array contains all zeros
+export function randWeightedChoice(weights: number[]): number | null {
+ let thresholds = Array(weights.length);
+ let sum = 0;
+ for (let i = 0; i < weights.length; i++){
+ sum += weights[i];
+ thresholds[i] = sum;
+ }
+ let rand = Math.random();
+ for (let i = 0; i < weights.length; i++){
+ if (rand <= thresholds[i] / sum){
+ return i;
+ }
+ }
+ return null;
+}
+// Returns a string with words first-letter capitalised
+export function capitalizeWords(str: string){
+ str = str.replace(/\b\w/g, x => x.toUpperCase()); // '\b' matches word boundary, '\w' is like [a-zA-Z0-9_]
+ str = str.replace(/(\w)'S/, '$1\'s'); // Avoid cases like "traveler's tree" -> "Traveler'S Tree"
+ return str;
+}
+// Dynamically obtains scroll bar width
+// From stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript
+export function getScrollBarWidth(){
+ // Create hidden outer div
+ let outer = document.createElement('div');
+ outer.style.visibility = 'hidden';
+ outer.style.overflow = 'scroll';
+ document.body.appendChild(outer);
+ // Create inner div
+ let inner = document.createElement('div');
+ outer.appendChild(inner);
+ // Get width difference
+ let scrollBarWidth = outer.offsetWidth - inner.offsetWidth;
+ // Remove temporary divs
+ outer.parentNode!.removeChild(outer);
+ //
+ return scrollBarWidth;
+}