Skip to content

Commit

Permalink
feat: initial implementation of isPointInFill
Browse files Browse the repository at this point in the history
  • Loading branch information
msand committed Oct 3, 2019
1 parent b6ee11a commit 203e53b
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 3 deletions.
76 changes: 76 additions & 0 deletions android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/


package com.horcrux.svg;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;

import javax.annotation.Nonnull;

class RNSVGRenderableManager extends ReactContextBaseJavaModule {
RNSVGRenderableManager(ReactApplicationContext reactContext) {
super(reactContext);
}

@Nonnull
@Override
public String getName() {
return "RNSVGRenderableManager";
}

private static void isPointInFill(final int tag, final float[] src, final Callback successCallback, final int attempt) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);

if (svg == null) {
if (attempt < 1) {
RenderableViewManager.runWhenViewIsAvailable(tag, new Runnable() {
@Override
public void run() {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) { // Should never happen
successCallback.invoke(false);
return;
}
isPointInFill(tag, src, successCallback, attempt + 1);
}
});
} else {
successCallback.invoke(false);
}
return;
} else {
float scale = svg.mScale;
src[0] *= scale;
src[1] *= scale;
int i = svg.hitTest(src);
successCallback.invoke(i != -1);
}
}
}
);
}

@SuppressWarnings("unused")
@ReactMethod
public void isPointInFill(int tag, ReadableMap options, Callback successCallback) {
float x = (float)options.getDouble("x");
float y = (float)options.getDouble("y");
float[] src = new float[] { x, y };
isPointInFill(tag, src, successCallback, 0);
}
}
6 changes: 6 additions & 0 deletions android/src/main/java/com/horcrux/svg/RenderableView.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ abstract public class RenderableView extends VirtualView {

private static final Pattern regex = Pattern.compile("[0-9.-]+");

@Override
public void setId(int id) {
super.setId(id);
RenderableViewManager.setRenderableView(id, this);
}

@ReactProp(name = "vectorEffect")
public void setVectorEffect(int vectorEffect) {
this.vectorEffect = vectorEffect;
Expand Down
79 changes: 78 additions & 1 deletion android/src/main/java/com/horcrux/svg/RenderableViewManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package com.horcrux.svg;

import android.graphics.Matrix;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;

Expand Down Expand Up @@ -41,7 +42,57 @@
import static com.facebook.react.uimanager.MatrixMathHelper.v3Dot;
import static com.facebook.react.uimanager.MatrixMathHelper.v3Length;
import static com.facebook.react.uimanager.MatrixMathHelper.v3Normalize;
import static com.facebook.react.uimanager.ViewProps.*;
import static com.facebook.react.uimanager.ViewProps.ALIGN_CONTENT;
import static com.facebook.react.uimanager.ViewProps.ALIGN_ITEMS;
import static com.facebook.react.uimanager.ViewProps.ALIGN_SELF;
import static com.facebook.react.uimanager.ViewProps.BORDER_BOTTOM_WIDTH;
import static com.facebook.react.uimanager.ViewProps.BORDER_END_WIDTH;
import static com.facebook.react.uimanager.ViewProps.BORDER_LEFT_WIDTH;
import static com.facebook.react.uimanager.ViewProps.BORDER_RIGHT_WIDTH;
import static com.facebook.react.uimanager.ViewProps.BORDER_START_WIDTH;
import static com.facebook.react.uimanager.ViewProps.BORDER_TOP_WIDTH;
import static com.facebook.react.uimanager.ViewProps.BORDER_WIDTH;
import static com.facebook.react.uimanager.ViewProps.BOTTOM;
import static com.facebook.react.uimanager.ViewProps.COLLAPSABLE;
import static com.facebook.react.uimanager.ViewProps.DISPLAY;
import static com.facebook.react.uimanager.ViewProps.END;
import static com.facebook.react.uimanager.ViewProps.FLEX;
import static com.facebook.react.uimanager.ViewProps.FLEX_BASIS;
import static com.facebook.react.uimanager.ViewProps.FLEX_DIRECTION;
import static com.facebook.react.uimanager.ViewProps.FLEX_GROW;
import static com.facebook.react.uimanager.ViewProps.FLEX_SHRINK;
import static com.facebook.react.uimanager.ViewProps.FLEX_WRAP;
import static com.facebook.react.uimanager.ViewProps.HEIGHT;
import static com.facebook.react.uimanager.ViewProps.JUSTIFY_CONTENT;
import static com.facebook.react.uimanager.ViewProps.LEFT;
import static com.facebook.react.uimanager.ViewProps.MARGIN;
import static com.facebook.react.uimanager.ViewProps.MARGIN_BOTTOM;
import static com.facebook.react.uimanager.ViewProps.MARGIN_END;
import static com.facebook.react.uimanager.ViewProps.MARGIN_HORIZONTAL;
import static com.facebook.react.uimanager.ViewProps.MARGIN_LEFT;
import static com.facebook.react.uimanager.ViewProps.MARGIN_RIGHT;
import static com.facebook.react.uimanager.ViewProps.MARGIN_START;
import static com.facebook.react.uimanager.ViewProps.MARGIN_TOP;
import static com.facebook.react.uimanager.ViewProps.MARGIN_VERTICAL;
import static com.facebook.react.uimanager.ViewProps.MAX_HEIGHT;
import static com.facebook.react.uimanager.ViewProps.MAX_WIDTH;
import static com.facebook.react.uimanager.ViewProps.MIN_HEIGHT;
import static com.facebook.react.uimanager.ViewProps.MIN_WIDTH;
import static com.facebook.react.uimanager.ViewProps.OVERFLOW;
import static com.facebook.react.uimanager.ViewProps.PADDING;
import static com.facebook.react.uimanager.ViewProps.PADDING_BOTTOM;
import static com.facebook.react.uimanager.ViewProps.PADDING_END;
import static com.facebook.react.uimanager.ViewProps.PADDING_HORIZONTAL;
import static com.facebook.react.uimanager.ViewProps.PADDING_LEFT;
import static com.facebook.react.uimanager.ViewProps.PADDING_RIGHT;
import static com.facebook.react.uimanager.ViewProps.PADDING_START;
import static com.facebook.react.uimanager.ViewProps.PADDING_TOP;
import static com.facebook.react.uimanager.ViewProps.PADDING_VERTICAL;
import static com.facebook.react.uimanager.ViewProps.POSITION;
import static com.facebook.react.uimanager.ViewProps.RIGHT;
import static com.facebook.react.uimanager.ViewProps.START;
import static com.facebook.react.uimanager.ViewProps.TOP;
import static com.facebook.react.uimanager.ViewProps.WIDTH;
import static com.horcrux.svg.RenderableView.CAP_ROUND;
import static com.horcrux.svg.RenderableView.FILL_RULE_NONZERO;
import static com.horcrux.svg.RenderableView.JOIN_ROUND;
Expand Down Expand Up @@ -1250,4 +1301,30 @@ protected VirtualView createViewInstance(@Nonnull ThemedReactContext reactContex
throw new IllegalStateException("Unexpected type " + svgClass.toString());
}
}

private static final SparseArray<RenderableView> mTagToRenderableView = new SparseArray<>();
private static final SparseArray<Runnable> mTagToRunnable = new SparseArray<>();

static void setRenderableView(int tag, RenderableView svg) {
mTagToRenderableView.put(tag, svg);
Runnable task = mTagToRunnable.get(tag);
if (task != null) {
task.run();
mTagToRunnable.delete(tag);
}
}

static void runWhenViewIsAvailable(int tag, Runnable task) {
mTagToRunnable.put(tag, task);
}

static @Nullable RenderableView getRenderableViewByTag(int tag) {
return mTagToRenderableView.get(tag);
}

@Override
public void onDropViewInstance(@Nonnull VirtualView view) {
super.onDropViewInstance(view);
mTagToRenderableView.remove(view.getId());
}
}
5 changes: 4 additions & 1 deletion android/src/main/java/com/horcrux/svg/SvgPackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext rea
@Nonnull
@Override
public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
return Collections.<NativeModule>singletonList(new SvgViewModule(reactContext));
return Arrays.<NativeModule>asList(
new SvgViewModule(reactContext),
new RNSVGRenderableManager(reactContext)
);
}

@SuppressWarnings("unused")
Expand Down
52 changes: 52 additions & 0 deletions ios/ViewManagers/RNSVGRenderableManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
* LICENSE file in the root directory of this source tree.
*/

#import <React/RCTBridge.h>
#import <React/RCTUIManager.h>
#import <React/RCTUIManagerUtils.h>
#import "RNSVGRenderableManager.h"

#import "RCTConvert+RNSVG.h"
Expand Down Expand Up @@ -34,4 +37,53 @@ - (RNSVGRenderable *)node
RCT_EXPORT_VIEW_PROPERTY(vectorEffect, int)
RCT_EXPORT_VIEW_PROPERTY(propList, NSArray<NSString *>)

- (void)isPointInFill:(nonnull NSNumber *)reactTag point:(CGPoint)point callback:(RCTResponseSenderBlock)callback attempt:(int)attempt {
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
__kindof UIView *view = viewRegistry[reactTag];
UIView *target;
if (!view) {
if (attempt < 1) {
void (^retryBlock)(void) = ^{
[self isPointInFill:reactTag point:point callback:callback attempt:(attempt + 1)];
};
RCTExecuteOnUIManagerQueue(retryBlock);
} else {
callback(@[[NSNumber numberWithBool:false]]);
}
return;
}
if ([view isKindOfClass:[RNSVGRenderable class]]) {
RNSVGRenderable *svg = view;
target = [svg hitTest:point withEvent:nil];
BOOL hit = target != nil;
callback(@[[NSNumber numberWithBool:hit]]);
} else {
RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
callback(@[[NSNumber numberWithBool:false]]);
}
}];
}

RCT_EXPORT_METHOD(isPointInFill:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback)
{
if (options == nil) {
RCTLogError(@"Invalid options given to isPointInFill, got: %@", options);
callback(@[[NSNumber numberWithBool:false]]);
return;
}
id xo = [options objectForKey:@"x"];
id yo = [options objectForKey:@"y"];
if (![xo isKindOfClass:NSNumber.class] ||
![yo isKindOfClass:NSNumber.class]) {
RCTLogError(@"Invalid x or y given to isPointInFill");
callback(@[[NSNumber numberWithBool:false]]);
return;
}
CGFloat x = (CGFloat)[xo floatValue];
CGFloat y = (CGFloat)[yo floatValue];
CGPoint point = CGPointMake(x, y);
[self isPointInFill:reactTag point:point callback:callback attempt:0];
}


@end
70 changes: 69 additions & 1 deletion src/elements/Shape.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
import { Component } from 'react';
import SvgTouchableMixin from '../lib/SvgTouchableMixin';
import { NativeMethodsMixinStatic } from 'react-native';
import {
NativeModules,
findNodeHandle,
NativeMethodsMixinStatic,
} from 'react-native';
import { TransformProps } from '../lib/extract/types';

const RNSVGRenderableManager = NativeModules.RNSVGRenderableManager;
const { touchableGetInitialState } = SvgTouchableMixin;
const touchKeys = Object.keys(SvgTouchableMixin);
const touchVals = touchKeys.map(key => SvgTouchableMixin[key]);
const numTouchKeys = touchKeys.length;

interface SVGBoundingBoxOptions {
fill: boolean;
stroke: boolean;
markers: boolean;
clipped: boolean;
}

interface DOMPointInit {
x?: number;
y?: number;
z?: number;
w?: number;
}

export default class Shape<P> extends Component<P> {
[x: string]: unknown;
root: (Shape<P> & NativeMethodsMixinStatic) | null = null;
Expand Down Expand Up @@ -36,4 +55,53 @@ export default class Shape<P> extends Component<P> {
) => {
this.root && this.root.setNativeProps(props);
};
getBBox = (callback: () => void, options?: SVGBoundingBoxOptions) => {
if (!callback) {
return;
}
const handle = findNodeHandle(this.root as Component);
RNSVGRenderableManager.getBBox(handle, options, callback); // TODO
};
getCTM = (callback: () => void) => {
if (!callback) {
return;
}
const handle = findNodeHandle(this.root as Component);
RNSVGRenderableManager.getCTM(handle, callback); // TODO
};
getScreenCTM = (callback: () => void) => {
if (!callback) {
return;
}
const handle = findNodeHandle(this.root as Component);
RNSVGRenderableManager.getScreenCTM(handle, callback); // TODO
};
isPointInFill = (callback: () => void, options: DOMPointInit) => {
if (!callback) {
return;
}
const handle = findNodeHandle(this.root as Component);
RNSVGRenderableManager.isPointInFill(handle, options, callback);
};
isPointInStroke = (callback: () => void, options: DOMPointInit) => {
if (!callback) {
return;
}
const handle = findNodeHandle(this.root as Component);
RNSVGRenderableManager.isPointInStroke(handle, options, callback); // TODO
};
getTotalLength = (callback: () => void) => {
if (!callback) {
return;
}
const handle = findNodeHandle(this.root as Component);
RNSVGRenderableManager.getTotalLength(handle, callback); // TODO
};
getPointAtLength = (callback: () => void, options: Object) => {
if (!callback) {
return;
}
const handle = findNodeHandle(this.root as Component);
RNSVGRenderableManager.getPointAtLength(handle, options, callback); // TODO
};
}

0 comments on commit 203e53b

Please sign in to comment.