feat: path util file and test

This commit is contained in:
zhanning.bzn 2019-12-05 15:45:22 +08:00
parent 73223e7946
commit d5d4fd9520
5 changed files with 282 additions and 1 deletions

View File

@ -26,7 +26,7 @@
"clean": "rimraf esm lib dist", "clean": "rimraf esm lib dist",
"lint": "lint-staged", "lint": "lint-staged",
"test": "jest", "test": "jest",
"test-live": "DEBUG_MODE=1 jest --watch", "test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/util",
"coverage": "jest --coverage", "coverage": "jest --coverage",
"ci": "run-s build coverage", "ci": "run-s build coverage",
"doc": "rimraf apis && typedoc", "doc": "rimraf apis && typedoc",

13
src/interface/shape.ts Normal file
View File

@ -0,0 +1,13 @@
export interface IPoint {
x: number;
y: number;
}
export interface IRect extends IPoint {
width: number;
height: number;
}
export interface ICircle extends IPoint {
r: number;
}

52
src/util/math.ts Normal file
View File

@ -0,0 +1,52 @@
import { IPoint } from '../interface/math'
/**
*
* @param {number} value
* @param {number} min
* @param {number} max
* @return {boolean} bool
*/
const isBetween = (value: number, min: number, max: number) => value >= min && value <= max
/**
* 线
* @param {object} p0 线
* @param {object} p1 线
* @param {object} p2 线
* @param {object} p3 线
* @return {object}
*/
export const getLineIntersect = (p0: IPoint, p1: IPoint, p2: IPoint, p3: IPoint): IPoint => {
const tolerance = 0.001;
const E: IPoint = {
x: p2.x - p0.x,
y: p2.y - p0.y
};
const D0: IPoint = {
x: p1.x - p0.x,
y: p1.y - p0.y
};
const D1: IPoint = {
x: p3.x - p2.x,
y: p3.y - p2.y
};
const kross: number = D0.x * D1.y - D0.y * D1.x;
const sqrKross: number = kross * kross;
const sqrLen0: number = D0.x * D0.x + D0.y * D0.y;
const sqrLen1: number = D1.x * D1.x + D1.y * D1.y;
let point: IPoint;
if (sqrKross > tolerance * sqrLen0 * sqrLen1) {
const s = (E.x * D1.y - E.y * D1.x) / kross;
const t = (E.x * D0.y - E.y * D0.x) / kross;
if (isBetween(s, 0, 1) && isBetween(t, 0, 1)) {
point = {
x: p0.x + s * D0.x,
y: p0.y + s * D0.y
};
}
}
return point;
}

100
src/util/path.ts Normal file
View File

@ -0,0 +1,100 @@
import { vec2 } from '@antv/matrix-util'
import { catmullRom2Bezier } from '@antv/path-util'
import { IPoint } from '../interface/math'
/**
*
* @param {String} str
* @param {Object} o json data
*/
const substitute = (str: string, o): string => {
if(!str || !o) {
return str
}
return str.replace(/\\?\{([^{}]+)\}/g, (match: string, name: string) => {
if(match.charAt(0) === '\\') {
return match.slice(1)
}
return o[name] || ''
})
}
/**
* 线 M C
* @param points coordinate set
*/
export const getSpline = (points: IPoint[]) => {
const data: number[] = []
if(points.length < 2) {
console.warn(`point length must largn than 2, now it's ${points.length}`)
}
for(const point of points) {
const { x, y } = point
data.push(x)
data.push(y)
}
const spliePath = catmullRom2Bezier(data)
spliePath.unshift([ 'M', points[0].x, points[0].y ])
return spliePath
}
/**
*
* @param {IPoint} startPoint x,y
* @param {IPoint} endPoint , x,y
* @param {Number} percent , 0-1
* @param {Number} offset
* @return {IPoint} x,y
*/
export const getControlPoint = (
startPoint: IPoint, endPoint: IPoint, percent: number = 0, offset: number = 0): IPoint => {
const point: IPoint = {
x: (1 - percent) * startPoint.x + percent * endPoint.x,
y: (1 - percent) * startPoint.y + percent * endPoint.y
};
let tangent = []
vec2.normalize(tangent, [ endPoint.x - startPoint.x, endPoint.x - startPoint.y ])
if(tangent.length === 0) {
tangent = [ 0, 0 ]
}
const perpendicular = [ -tangent[1] * offset, tangent[0] * offset ]; // 垂直向量
point.x += perpendicular[0];
point.y += perpendicular[1];
return point;
}
/**
* Path多边形
* @param {Array} points
* @param {Boolen} z
* @return {Array} Path
*/
export const pointsToPolygon = (points: IPoint[], z: boolean): string => {
if(!points.length) {
return ''
}
let path = ''
let str = ''
for (let i = 0, length = points.length; i < length; i++) {
const item = points[i];
if (i === 0) {
str = 'M{x} {y}';
} else {
str = 'L{x} {y}';
}
path += substitute(str, item);
}
if (z) {
path += 'Z';
}
return path;
}

View File

@ -0,0 +1,116 @@
import { getControlPoint, getSpline, pointsToPolygon } from '../../../src/util/path'
describe('Path Util Test', () => {
it('getSpline', () => {
const points = [
{
x: 10,
y: 12
},
{
x: 5,
y: 5
},
{
x: 2,
y: 7
}
]
const splinePath = getSpline(points)
expect(splinePath.length).toEqual(3)
const first = splinePath[0]
expect(first.length).toEqual(3)
expect(first[0]).toEqual('M')
expect(first[1]).toEqual(10)
expect(first[2]).toEqual(12)
const second = splinePath[1]
expect(second.length).toEqual(7)
expect(second[0]).toEqual('C')
expect(second[1]).toEqual(9.166666666666666)
expect(second[2]).toEqual(10.833333333333334)
expect(second[5]).toEqual(5)
const three = splinePath[2]
expect(three.length).toEqual(7)
expect(three[2]).toEqual(4.166666666666667)
expect(three[6]).toEqual(7)
})
it('getControlPoint horizontal', () => {
const start = { x: 0, y: 0 };
const end = { x: 100, y: 0 };
const controlPoint = getControlPoint(start, end, 0.5, 10)
expect(controlPoint.x).toEqual(42.928932188134524)
expect(controlPoint.y).toEqual(7.0710678118654755)
const points = getControlPoint(start, end, 0.2, -2)
expect(points).toEqual({ x: 21.414213562373096, y: -1.4142135623730951 })
})
it('getControlPoint vertical', () => {
const start = { x: 0, y: 0 };
const end = { x: 0, y: 100 };
const point = getControlPoint(start, end, 0.5)
expect(point).toEqual({ x: 0, y: 50 })
const point2 = getControlPoint(start, end, 0.2, -2)
console.log(point2)
expect(point2).toEqual({ x: 0, y: 20 })
})
it('getControlPoint 45', () => {
const start = { x: 0, y: 0 };
const end = { x: 100, y: 100 };
const point = getControlPoint(start, end, 0.5, 10);
const sqrt2 = Math.sqrt(2);
expect(point.x).toEqual(50 - sqrt2 * 10 / 2);
expect(point.y).toEqual(50 + sqrt2 * 10 / 2);
})
it('getControlPoint 135', () => {
const start = { x: 100, y: 100 };
const end = { x: 0, y: 0 };
const point = getControlPoint(start, end, 0.5, 10);
const sqrt2 = Math.sqrt(2);
expect(point.x).toEqual(50 + sqrt2 * 10 / 2);
expect(point.y).toEqual(50 - sqrt2 * 10 / 2);
})
it('pointsToPolygon z = false', () => {
const points = [
{
x: 1,
y: 2
},
{
x: 5,
y: 5
}
]
const polygonPoint = pointsToPolygon(points, false)
expect(polygonPoint).toEqual('M1 2L5 5')
})
it('pointsToPolygon z = true', () => {
const points = [
{
x: 1,
y: 2
},
{
x: 5,
y: 5
}
]
const polygonPoint = pointsToPolygon(points, true)
expect(polygonPoint).toEqual('M1 2L5 5Z')
})
})