fix: fix native methods spec conformance

return SVGMatrix and SVGPoint from native methods
expose Matrix and Point helpers, types and classes
This commit is contained in:
Mikael Sand
2019-10-05 14:46:40 +03:00
parent e6eda84513
commit ecedb21bac
2 changed files with 237 additions and 16 deletions
+236 -15
View File
@@ -13,20 +13,221 @@ const touchKeys = Object.keys(SvgTouchableMixin);
const touchVals = touchKeys.map(key => SvgTouchableMixin[key]);
const numTouchKeys = touchKeys.length;
interface SVGBoundingBoxOptions {
export interface SVGBoundingBoxOptions {
fill?: boolean;
stroke?: boolean;
markers?: boolean;
clipped?: boolean;
}
interface DOMPointInit {
export interface DOMPointInit {
x?: number;
y?: number;
z?: number;
w?: number;
}
export interface Point {
x: number;
y: number;
}
export interface SVGPoint extends Point {
constructor(point?: Point): SVGPoint;
matrixTransform(matrix: SVGMatrix): SVGPoint;
}
export interface Matrix {
a: number;
b: number;
c: number;
d: number;
e: number;
f: number;
}
export interface SVGMatrix extends Matrix {
constructor(matrix?: Matrix): SVGMatrix;
multiply(secondMatrix: SVGMatrix): SVGMatrix;
inverse(): SVGMatrix;
translate(x: number, y: number): SVGMatrix;
scale(scaleFactor: number): SVGMatrix;
scaleNonUniform(scaleFactorX: number, scaleFactorY: number): SVGMatrix;
rotate(angle: number): SVGMatrix;
rotateFromVector(x: number, y: number): SVGMatrix;
flipX(): SVGMatrix;
flipY(): SVGMatrix;
skewX(angle: number): SVGMatrix;
skewY(angle: number): SVGMatrix;
}
export function multiply_matrices(l: Matrix, r: Matrix): Matrix {
const { a: al, b: bl, c: cl, d: dl, e: el, f: fl } = l;
const { a: ar, b: br, c: cr, d: dr, e: er, f: fr } = r;
const a = al * ar + cl * br;
const c = al * cr + cl * dr;
const e = al * er + cl * fr + el;
const b = bl * ar + dl * br;
const d = bl * cr + dl * dr;
const f = bl * er + dl * fr + fl;
return { a, c, e, b, d, f };
}
export function invert({ a, b, c, d, e, f }: Matrix) {
const n = a * d - b * c;
return {
a: d / n,
b: -b / n,
c: -c / n,
d: a / n,
e: (c * f - d * e) / n,
f: -(a * f - b * e) / n,
};
}
const deg2rad = Math.PI / 180;
export class SVGMatrix implements SVGMatrix {
constructor(matrix?: Matrix) {
if (matrix) {
const { a, b, c, d, e, f } = matrix;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
this.f = f;
} else {
this.a = 1;
this.b = 0;
this.c = 0;
this.d = 1;
this.e = 0;
this.f = 0;
}
}
multiply(secondMatrix: Matrix): SVGMatrix {
return new SVGMatrix(multiply_matrices(this, secondMatrix));
}
inverse(): SVGMatrix {
return new SVGMatrix(invert(this));
}
translate(x: number, y: number): SVGMatrix {
return new SVGMatrix(
multiply_matrices(this, { a: 1, b: 0, c: 0, d: 1, e: x, f: y }),
);
}
scale(scaleFactor: number): SVGMatrix {
return new SVGMatrix(
multiply_matrices(this, {
a: scaleFactor,
b: 0,
c: 0,
d: scaleFactor,
e: 0,
f: 0,
}),
);
}
scaleNonUniform(scaleFactorX: number, scaleFactorY: number): SVGMatrix {
return new SVGMatrix(
multiply_matrices(this, {
a: scaleFactorX,
b: 0,
c: 0,
d: scaleFactorY,
e: 0,
f: 0,
}),
);
}
rotate(angle: number): SVGMatrix {
const cos = Math.cos(deg2rad * angle);
const sin = Math.sin(deg2rad * angle);
return new SVGMatrix(
multiply_matrices(this, { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 }),
);
}
rotateFromVector(x: number, y: number): SVGMatrix {
const angle = Math.atan2(y, x);
const cos = Math.cos(deg2rad * angle);
const sin = Math.sin(deg2rad * angle);
return new SVGMatrix(
multiply_matrices(this, { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 }),
);
}
flipX(): SVGMatrix {
return new SVGMatrix(
multiply_matrices(this, { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 }),
);
}
flipY(): SVGMatrix {
return new SVGMatrix(
multiply_matrices(this, { a: 1, b: 0, c: 0, d: -1, e: 0, f: 0 }),
);
}
skewX(angle: number): SVGMatrix {
return new SVGMatrix(
multiply_matrices(this, {
a: 1,
b: 0,
c: Math.tan(deg2rad * angle),
d: 1,
e: 0,
f: 0,
}),
);
}
skewY(angle: number): SVGMatrix {
return new SVGMatrix(
multiply_matrices(this, {
a: 1,
b: Math.tan(deg2rad * angle),
c: 0,
d: 1,
e: 0,
f: 0,
}),
);
}
}
export function matrixTransform(matrix: Matrix, point: SVGPoint): Point {
const { a, b, c, d, e, f } = matrix;
const { x, y } = point;
return {
x: a * x + c * y + e,
y: b * x + d * y + f,
};
}
export class SVGPoint implements SVGPoint {
constructor(point?: Point) {
if (point) {
const { x, y } = point;
this.x = x;
this.y = y;
} else {
this.x = 0;
this.y = 0;
}
}
matrixTransform(matrix: SVGMatrix): SVGPoint {
return new SVGPoint(matrixTransform(matrix, this));
}
}
export const ownerSVGElement = {
createSVGPoint(): SVGPoint {
return new SVGPoint();
},
createSVGMatrix(): SVGMatrix {
return new SVGMatrix();
},
};
export default class Shape<P> extends Component<P> {
[x: string]: unknown;
root: (Shape<P> & NativeMethodsMixinStatic) | null = null;
@@ -55,7 +256,7 @@ export default class Shape<P> extends Component<P> {
) => {
this.root && this.root.setNativeProps(props);
};
getBBox = (callback?: () => void, options?: SVGBoundingBoxOptions) => {
getBBox = (options?: SVGBoundingBoxOptions, callback?: () => void) => {
const { fill = true, stroke = true, markers = true, clipped = true } =
options || {};
const handle = findNodeHandle(this.root as Component);
@@ -85,27 +286,35 @@ export default class Shape<P> extends Component<P> {
);
return undefined;
};
getCTM = (callback: () => void) => {
getCTM = (callback: (screenCTM: SVGMatrix) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.getCTM(handle, resolve);
RNSVGRenderableManager.getCTM(handle, (matrix: Matrix) =>
resolve(new SVGMatrix(matrix)),
);
});
}
RNSVGRenderableManager.getCTM(handle, callback);
RNSVGRenderableManager.getCTM(handle, (matrix: Matrix) =>
callback(new SVGMatrix(matrix)),
);
return undefined;
};
getScreenCTM = (callback: () => void) => {
getScreenCTM = (callback: (screenCTM: SVGMatrix) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.getScreenCTM(handle, resolve);
RNSVGRenderableManager.getScreenCTM(handle, (matrix: Matrix) =>
resolve(new SVGMatrix(matrix)),
);
});
}
RNSVGRenderableManager.getScreenCTM(handle, callback);
RNSVGRenderableManager.getScreenCTM(handle, (matrix: Matrix) =>
callback(new SVGMatrix(matrix)),
);
return undefined;
};
isPointInFill = (options: DOMPointInit, callback: () => void) => {
isPointInFill = (options: DOMPointInit, callback: (res: boolean) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
@@ -115,7 +324,10 @@ export default class Shape<P> extends Component<P> {
RNSVGRenderableManager.isPointInFill(handle, options, callback);
return undefined;
};
isPointInStroke = (options: DOMPointInit, callback?: () => void) => {
isPointInStroke = (
options: DOMPointInit,
callback?: (res: boolean) => void,
) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
@@ -125,7 +337,7 @@ export default class Shape<P> extends Component<P> {
RNSVGRenderableManager.isPointInStroke(handle, options, callback);
return undefined;
};
getTotalLength = (callback?: () => void) => {
getTotalLength = (callback?: (length: number) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
@@ -135,14 +347,23 @@ export default class Shape<P> extends Component<P> {
RNSVGRenderableManager.getTotalLength(handle, callback);
return undefined;
};
getPointAtLength = (length: number, callback: () => void) => {
getPointAtLength = (length: number, callback: (point: SVGPoint) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.getPointAtLength(handle, { length }, resolve);
RNSVGRenderableManager.getPointAtLength(
handle,
{ length },
(point: Point) => resolve(new SVGPoint(point)),
);
});
}
RNSVGRenderableManager.getPointAtLength(handle, { length }, callback);
RNSVGRenderableManager.getPointAtLength(
handle,
{ length },
(point: Point) => callback(new SVGPoint(point)),
);
return undefined;
};
}
Shape.prototype.ownerSVGElement = ownerSVGElement;