Skip to content

Commit

Permalink
fix: fix native methods spec conformance
Browse files Browse the repository at this point in the history
return SVGMatrix and SVGPoint from native methods
expose Matrix and Point helpers, types and classes
  • Loading branch information
msand committed Oct 5, 2019
1 parent e6eda84 commit ecedb21
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 16 deletions.
2 changes: 1 addition & 1 deletion ios/Text/RNSVGText.m
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ - (void)setRotate:(NSArray<RNSVGLength *> *)rotate

- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
{
[self clip:context];
CGContextSaveGState(context);
[self clip:context];
[self setupGlyphContext:context];
[self pushGlyphContext];
[super renderGroupTo:context rect:rect];
Expand Down
251 changes: 236 additions & 15 deletions src/elements/Shape.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 => {
Expand All @@ -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 => {
Expand All @@ -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 => {
Expand All @@ -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;

0 comments on commit ecedb21

Please sign in to comment.