mirror of
				https://github.com/skishore/makemeahanzi.git
				synced 2025-10-31 19:07:07 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			99 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			99 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const svgPathUtils = require('point-at-length');
 | |
| 
 | |
| const dist = (p1, p2) => Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
 | |
| const norm = (vect) => dist(vect, {x: 0, y: 0});
 | |
| const subtract = (p1, p2) => ({x: p1.x - p2.x, y: p1.y - p2.y});
 | |
| const ptEq = (p1, p2) => p1.x === p2.x && p1.y === p2.y;
 | |
| 
 | |
| const getOutlinePoints = (pathString, count = 1000) => {
 | |
|   const path = svgPathUtils(pathString);
 | |
|   const delta = path.length() / count;
 | |
|   const outline = [];
 | |
|   for (let i = 0; i < count; i += 1) {
 | |
|     const svgPoint = path.at(i * delta);
 | |
|     outline.push({x: svgPoint[0], y: svgPoint[1]});
 | |
|   }
 | |
|   return outline;
 | |
| };
 | |
| 
 | |
| // get the intersection point of 2 lines defined by 2 points each
 | |
| // from https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
 | |
| const getLinesIntersectPoint = (l1p1, l1p2, l2p1, l2p2) => {
 | |
|   const x1 = l1p1.x;
 | |
|   const x2 = l1p2.x;
 | |
|   const x3 = l2p1.x;
 | |
|   const x4 = l2p2.x;
 | |
|   const y1 = l1p1.y;
 | |
|   const y2 = l1p2.y;
 | |
|   const y3 = l2p1.y;
 | |
|   const y4 = l2p2.y;
 | |
|   const xNumerator = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
 | |
|   const yNumerator = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
 | |
|   const denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
 | |
|   return {x: xNumerator / denominator, y: yNumerator / denominator};
 | |
| };
 | |
| 
 | |
| const getPointIndex = (point, pathOutline) => {
 | |
|   const dists = pathOutline.map(outlinePoint => dist(point, outlinePoint));
 | |
|   const min = Math.min(...dists);
 | |
|   return dists.indexOf(min);
 | |
| };
 | |
| 
 | |
| const getIndexAtDelta = (index, delta, pathOutline) => {
 | |
|   return (pathOutline.length + index + delta) % pathOutline.length;
 | |
| };
 | |
| 
 | |
| const getCosSimAroundPoint = (point, pathOutline) => {
 | |
|   // if this is 1, the point is on a flat line.
 | |
|   const pointIndex = getPointIndex(point, pathOutline);
 | |
|   const preIndex = getIndexAtDelta(pointIndex, -3, pathOutline);
 | |
|   const postIndex = getIndexAtDelta(pointIndex, 3, pathOutline);
 | |
|   const vect1 = subtract(pathOutline[pointIndex], pathOutline[preIndex]);
 | |
|   const vect2 = subtract(pathOutline[postIndex], pathOutline[pointIndex]);
 | |
|   return (vect1.x * vect2.x + vect1.y * vect2.y) / (norm(vect1) * norm(vect2));
 | |
| };
 | |
| 
 | |
| // return a new point, p3, which is on the same line as p1 and p2, but distance away
 | |
| // from p2. p1, p2, p3 will always lie on the line in that order
 | |
| const extendPointOnLine = (p1, p2, distance) => {
 | |
|   const vect = subtract(p2, p1);
 | |
|   const mag = distance / norm(vect);
 | |
|   return {x: p2.x + mag * vect.x, y: p2.y + mag * vect.y};
 | |
| };
 | |
| 
 | |
| const distToPath = (point, pathOutline) => {
 | |
|   const dists = pathOutline.map(outlinePoint => dist(point, outlinePoint));
 | |
|   return Math.min(...dists);
 | |
| };
 | |
| 
 | |
| const roundPathPoints = (pathString) => {
 | |
|   const floats = pathString.match(/\d+\.\d+/ig);
 | |
|   if (!floats) return pathString;
 | |
|   let fixedPathString = pathString;
 | |
|   floats.forEach(float => {
 | |
|     fixedPathString = fixedPathString.replace(float, Math.round(parseFloat(float)));
 | |
|   });
 | |
|   return fixedPathString;
 | |
| };
 | |
| 
 | |
| const estimateTanPoints = (pathOutline, clipPoints) => {
 | |
|   const cpIndex0 = getPointIndex(clipPoints[0], pathOutline);
 | |
|   const cpIndex1 = getPointIndex(clipPoints[1], pathOutline);
 | |
|   return [
 | |
|     pathOutline[getIndexAtDelta(cpIndex0, -15, pathOutline)],
 | |
|     pathOutline[getIndexAtDelta(cpIndex1, 15, pathOutline)],
 | |
|   ];
 | |
| };
 | |
| 
 | |
| module.exports = {
 | |
|   distToPath,
 | |
|   getCosSimAroundPoint,
 | |
|   getOutlinePoints,
 | |
|   getLinesIntersectPoint,
 | |
|   extendPointOnLine,
 | |
|   estimateTanPoints,
 | |
|   dist,
 | |
|   ptEq,
 | |
|   roundPathPoints,
 | |
| };
 | 
