import { cache } from 'react';
import * as THREE from 'three';
export const sortByDate = (
{ date: a }: { date: string },
{ date: b }: { date: string },
) => {
if (a < b) {
return 1;
} else if (a > b) {
return -1;
} else {
return 0;
}
};
/**
* Read post head to mark TOC
* @param source
* @returns
*/
export const getHeadings = (source: string) => {
const regex = /
(.*?)<\/h2>/g;
const linkRegx = /id="(.*?)"/;
return source.match(regex)?.map((heading) => {
const headingText = heading
.replace(//, '')
.replace(/<\/h2>/, '');
const link = '#' + heading.match(linkRegx)?.[1];
return {
text: headingText,
link,
};
});
};
export type SingleToc = {
level: number;
head: string;
link: string;
children: SingleToc[];
};
/**
* Generate post toc
* @param source
* @returns
*/
export const generateToc = cache((source: string) => {
const regex = /^#{2,3}(?!#)(.*)/gm;
let lastH2: SingleToc | null = null;
const toc: SingleToc[] = [];
source.match(regex)?.map((h) => {
const heading = h.split(' ');
const level = heading[0].length;
const head = h.substring(level + 1);
const link = `#${head
.toLocaleLowerCase()
.replace(/ /g, '-')
.replace(/\./g, '')}`;
switch (level) {
case 2: {
lastH2 = {
level,
head,
link,
children: [],
};
toc.push(lastH2);
break;
}
case 3: {
lastH2?.children.push({
level,
head,
link,
children: [],
});
break;
}
}
});
return toc;
});
/**
* Get mouse or touch position on screen.
* @param e Mouse or Touch event.
* @returns
*/
export const getMousePosition = (e: MouseEvent | globalThis.TouchEvent) => {
return e instanceof MouseEvent
? {
x: e.clientX,
y: e.clientY,
}
: {
x: e.touches[0].clientX,
y: e.touches[0].clientY,
};
};
type Debounce = {
(
fn: (...arg: T) => R,
ms: number,
): (this: unknown, ...arg: T) => void;
};
export const debounce: Debounce = (fn, ms) => {
let timer: NodeJS.Timeout;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, ms);
};
};
export const frameArea = (
sizeToFitOnScreen: number,
boxSize: number,
boxCenter: THREE.Vector3,
camera: THREE.PerspectiveCamera,
) => {
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
const halfFovY = THREE.MathUtils.degToRad(camera.fov * 0.5);
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
// compute a unit vector that points in the direction the camera is now
// in the xz plane from the center of the box
const direction = new THREE.Vector3()
.subVectors(camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
// move the camera to a position distance units way from the center
// in whatever direction the camera was from the center already
camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
// pick some near and far values for the frustum that
// will contain the box.
camera.near = boxSize / 100;
camera.far = boxSize * 100;
camera.updateProjectionMatrix();
// point the camera to look at the center of the box
camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
};
export const isUrl = (url: string) => {
const urlRegex = new RegExp(
'^(http:\\/\\/www\\.|https:\\/\\/www\\.|http:\\/\\/|https:\\/\\/)?[a-z0-9]+([\\-.]{1}[a-z0-9]+)*\\.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$',
);
return urlRegex.test(url);
};