refactor(es modules): modulize utils

This commit is contained in:
Azgaar 2022-06-26 20:44:29 +03:00
parent 12e1c9f334
commit 7ccebec048
54 changed files with 168 additions and 134 deletions

174
src/utils/languageUtils.js Normal file
View file

@ -0,0 +1,174 @@
import {P} from "@/utils/probabilityUtils";
// chars that serve as vowels
const VOWELS = `aeiouyɑ'əøɛœæɶɒɨɪɔɐʊɤɯаоиеёэыуюяàèìòùỳẁȁȅȉȍȕáéíóúýẃőűâêîôûŷŵäëïöüÿẅãẽĩõũỹąęįǫųāēīōūȳăĕĭŏŭǎěǐǒǔȧėȯẏẇạẹịọụỵẉḛḭṵṳ`;
function vowel(c) {
return VOWELS.includes(c);
}
// remove vowels from the end of the string
function trimVowels(string, minLength = 3) {
while (string.length > minLength && vowel(string.at(-1))) {
string = string.slice(0, -1);
}
return string;
}
const adjectivizationRules = [
{name: "guo", probability: 1, condition: new RegExp(" Guo$"), action: noun => noun.slice(0, -4)},
{
name: "orszag",
probability: 1,
condition: new RegExp("orszag$"),
action: noun => (noun.length < 9 ? noun + "ian" : noun.slice(0, -6))
},
{
name: "stan",
probability: 1,
condition: new RegExp("stan$"),
action: noun => (noun.length < 9 ? noun + "i" : trimVowels(noun.slice(0, -4)))
},
{
name: "land",
probability: 1,
condition: new RegExp("land$"),
action: noun => {
if (noun.length > 9) return noun.slice(0, -4);
const root = trimVowels(noun.slice(0, -4), 0);
if (root.length < 3) return noun + "ic";
if (root.length < 4) return root + "lish";
return root + "ish";
}
},
{
name: "que",
probability: 1,
condition: new RegExp("que$"),
action: noun => noun.replace(/que$/, "can")
},
{
name: "a",
probability: 1,
condition: new RegExp("a$"),
action: noun => noun + "n"
},
{
name: "o",
probability: 1,
condition: new RegExp("o$"),
action: noun => noun.replace(/o$/, "an")
},
{
name: "u",
probability: 1,
condition: new RegExp("u$"),
action: noun => noun + "an"
},
{
name: "i",
probability: 1,
condition: new RegExp("i$"),
action: noun => noun + "an"
},
{
name: "e",
probability: 1,
condition: new RegExp("e$"),
action: noun => noun + "an"
},
{
name: "ay",
probability: 1,
condition: new RegExp("ay$"),
action: noun => noun + "an"
},
{
name: "os",
probability: 1,
condition: new RegExp("os$"),
action: noun => {
const root = trimVowels(noun.slice(0, -2), 0);
if (root.length < 4) return noun.slice(0, -1);
return root + "ian";
}
},
{
name: "es",
probability: 1,
condition: new RegExp("es$"),
action: noun => {
const root = trimVowels(noun.slice(0, -2), 0);
if (root.length > 7) return noun.slice(0, -1);
return root + "ian";
}
},
{
name: "l",
probability: 0.8,
condition: new RegExp("l$"),
action: noun => noun + "ese"
},
{
name: "n",
probability: 0.8,
condition: new RegExp("n$"),
action: noun => noun + "ese"
},
{
name: "ad",
probability: 0.8,
condition: new RegExp("ad$"),
action: noun => noun + "ian"
},
{
name: "an",
probability: 0.8,
condition: new RegExp("an$"),
action: noun => noun + "ian"
},
{
name: "ish",
probability: 0.25,
condition: new RegExp("^[a-zA-Z]{6}$"),
action: noun => trimVowels(noun.slice(0, -1)) + "ish"
},
{
name: "an",
probability: 0.5,
condition: new RegExp("^[a-zA-Z]{0-7}$"),
action: noun => trimVowels(noun) + "an"
}
];
// get adjective form from noun
function getAdjective(noun) {
for (const rule of adjectivizationRules) {
if (P(rule.probability) && rule.condition.test(noun)) {
return rule.action(noun);
}
}
return noun; // no rule applied, return noun as is
}
// get ordinal from integer: 1 => 1st
const nth = n => n + (["st", "nd", "rd"][((((n + 90) % 100) - 10) % 10) - 1] || "th");
// get two-letters code (abbreviation) from string
function abbreviate(name, restricted = []) {
const parsed = name.replace("Old ", "O ").replace(/[()]/g, ""); // remove Old prefix and parentheses
const words = parsed.split(" ");
const letters = words.join("");
let code = words.length === 2 ? words[0][0] + words[1][0] : letters.slice(0, 2);
for (let i = 1; i < letters.length - 1 && restricted.includes(code); i++) {
code = letters[0] + letters[i].toUpperCase();
}
return code;
}
// conjunct array: [A,B,C] => "A, B and C"
function list(array) {
if (!Intl.ListFormat) return array.join(", ");
const conjunction = new Intl.ListFormat(window.lang || "en", {style: "long", type: "conjunction"});
return conjunction.format(array);
}

7
src/utils/nodeUtils.ts Normal file
View file

@ -0,0 +1,7 @@
import {byId} from "./shorthands";
// get next unused id
export function getNextId(core: string, index = 1) {
while (byId(core + index)) index++;
return core + index;
}

View file

@ -0,0 +1,84 @@
import {ERROR} from "@/config/logging";
import {minmax, rn} from "./numberUtils";
const d3 = window.d3;
// random number in range
export function rand(min: number, max: number) {
if (min === undefined && max === undefined) return Math.random();
if (max === undefined) {
max = min;
min = 0;
}
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// probability shorthand
export function P(probability: number) {
if (probability >= 1) return true;
if (probability <= 0) return false;
return Math.random() < probability;
}
export function each(n: number) {
return (i: number) => i % n === 0;
}
// random number using normal distribution
export function gauss(expected = 100, deviation = 30, min = 0, max = 300, round = 0) {
const randomValue = d3.randomNormal(expected, deviation);
const clamped = minmax(randomValue(), min, max);
return rn(clamped, round);
}
// probability shorthand for floats
export function Pint(float: number) {
return ~~float + +P(float % 1);
}
// return random value from the array
export function ra<T>(array: T[]) {
return array[Math.floor(Math.random() * array.length)];
}
// return random value from weighted array
export function rw(object: {[key: string]: number}) {
const weightedArray = Object.entries(object)
.map(([choise, weight]) => new Array(weight).fill(choise))
.flat();
return ra(weightedArray);
}
// return a random integer from min to max biased towards one end based on exponent distribution (the bigger ex the higher bias towards min)
export function biased(min: number, max: number, ex: number) {
return Math.round(min + (max - min) * Math.pow(Math.random(), ex));
}
// get number from string in format "1-3" or "2" or "0.5"
export function getNumberInRange(rangeString: string) {
if (typeof rangeString !== "string") {
ERROR && console.error("The value should be a string", rangeString);
return 0;
}
const rangeNumber = Number(rangeString);
if (!isNaN(rangeNumber)) return Pint(rangeNumber);
const negative = rangeString.startsWith("-");
const sign = negative ? -1 : 1;
if (negative) rangeString = rangeString.substring(1);
const [min, max] = rangeString.split("-");
const count = rand(sign * +min, +max);
if (isNaN(count) || count < 0) {
ERROR && console.error("Cannot parse number. Check the format", rangeString);
return 0;
}
return count;
}
export function generateSeed() {
return String(Math.floor(Math.random() * 1e9));
}

56
src/utils/stringUtils.ts Normal file
View file

@ -0,0 +1,56 @@
import {rn} from "@/utils/numberUtils";
// round numbers in string to d decimals
export function round(str: string, d = 1) {
return str.replace(/[\d\.-][\d\.e-]*/g, n => String(rn(+n, d)));
}
// return string with 1st char capitalized
export function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// split string into 2 almost equal parts not breaking words
export function splitInTwo(str: string) {
const half = str.length / 2;
const ar = str.split(" ");
if (ar.length < 2) return ar; // only one word
let first = "",
last = "",
middle = "",
rest = "";
ar.forEach((w, d) => {
if (d + 1 !== ar.length) w += " ";
rest += w;
if (!first || rest.length < half) first += w;
else if (!middle) middle = w;
else last += w;
});
if (!last) return [first, middle];
if (first.length < last.length) return [first + middle, last];
return [first, middle + last];
}
// transform string to array [translateX,translateY,rotateDeg,rotateX,rotateY,scale]
export function parseTransform(str: string) {
if (!str) return [0, 0, 0, 0, 0, 1];
const a = str
.replace(/[a-z()]/g, "")
.replace(/[ ]/g, ",")
.split(",");
return [a[0] || 0, a[1] || 0, a[2] || 0, a[3] || 0, a[4] || 0, a[5] || 1];
}
// check if string is a valid for JSON parse
export const isJsonValid = (str: string) => {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
};

37
src/utils/unitUtils.js Normal file
View file

@ -0,0 +1,37 @@
import {rn} from "/src/utils/numberUtils";
// conver temperature from °C to other scales
const temperatureConversionMap = {
"°C": temp => rn(temp) + "°C",
"°F": temp => rn((temp * 9) / 5 + 32) + "°F",
K: temp => rn(temp + 273.15) + "K",
"°R": temp => rn(((temp + 273.15) * 9) / 5) + "°R",
"°De": temp => rn(((100 - temp) * 3) / 2) + "°De",
"°N": temp => rn((temp * 33) / 100) + "°N",
"°Ré": temp => rn((temp * 4) / 5) + "°Ré",
"°Rø": temp => rn((temp * 21) / 40 + 7.5) + "°Rø"
};
function convertTemperature(temp) {
const scale = temperatureScale.value || "°C";
return temperatureConversionMap[scale](temp);
}
// corvent number to short string with SI postfix
function si(n) {
if (n >= 1e9) return rn(n / 1e9, 1) + "B";
if (n >= 1e8) return rn(n / 1e6) + "M";
if (n >= 1e6) return rn(n / 1e6, 1) + "M";
if (n >= 1e4) return rn(n / 1e3) + "K";
if (n >= 1e3) return rn(n / 1e3, 1) + "K";
return rn(n);
}
// getInteger number from user input data
function getInteger(value) {
const metric = value.slice(-1);
if (metric === "K") return parseInt(value.slice(0, -1) * 1e3);
if (metric === "M") return parseInt(value.slice(0, -1) * 1e6);
if (metric === "B") return parseInt(value.slice(0, -1) * 1e9);
return parseInt(value);
}