Is there an easy way to measure the distance between a point and a VectorDrawable?

In my Android application, I want to know the distance between the point the user clicks on and a specific VectorDrawable group.

I need the distance to a group like blue in VectorDrawable:

 <vector android:height="24dp" android:viewportHeight="1052.3622" android:viewportWidth="744.0945" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> <path android:fillColor="#ff0000" android:name="blue" android:pathData="M182.9,349.5m-74.7,0a74.7,74.7 0,1 1,149.3 0a74.7,74.7 0,1 1,-149.3 0" android:strokeAlpha="1" android:strokeColor="#000000" android:strokeWidth="4.23501825"/> <path android:fillColor="#00ff00" android:name="red" android:pathData="M474.3,392.4a84.3,102.9 0,1 0,168.6 0a84.3,102.9 0,1 0,-168.6 0z" android:strokeAlpha="1" android:strokeColor="#000000" android:strokeWidth="5"/>> </vector> 

Is there an easy way to calculate this distance in Android?

+6
source share
2 answers

I am not sure that an easy way to solve the problem exists, but it can be done as follows:

Parse vector XML so that you have all of these variables at runtime. Parsing is not considered here, suppose you have the following data structure, which we will work with later:

 private static class VectorData { private int width = 24; private int height = 24; private double viewportHeight = 1052.3622; private double viewportWidth = 744.0945; private String path = "M182.9,349.5m-74.7,0a74.7,74.7 0,1 1,149.3 0a74.7,74.7 0,1 1,-149.3 0"; private double scaleVectorX(Context context) { return dpToPx(context, width) / viewportWidth; } private double scaleVectorY(Context context) { return dpToPx(context, height) / viewportHeight; } private static float dpToPx(Context context, float dp) { return dp * context.getResources().getDisplayMetrics().density; } } 

As you can see, all fields are hard-coded for simplicity.

The next step is to analyze the data of the vector path, converting them to android.graphics.Path:

 android.graphics.Path path = android.util.PathParser.createPathFromPathData(vectorData.path); 

android.util.PathParser is not included, but you can find the source here: https://android.googlesource.com/platform/frameworks/base/+/17e64ffd852f8fe23b8e2e2ff1b62ee742af17a6/core/java/android/util/PathParser.java . Not sure how legal it is to copy and use it, though.

Along the way, we need to find N of our points (coordinates). More points - a more accurate result will be a slower processing:

 final Collection<Point> points = getPoints(path, iv.getX(), iv.getY(), vectorData); private static class Point { private float x; private float y; Point(float x, float y) { this.x = x; this.y = y; } @Override public String toString() { return "Point{" + "x=" + x + ", y=" + y + '}'; } } private Collection<Point> getPoints(Path path, float viewX, float viewY, VectorData vectorData) { Collection<Point> points = new ArrayList<>(); PathMeasure pm = new PathMeasure(path, false); float length = pm.getLength(); float distance = 0f; int size = N; float speed = length / size; int counter = 0; float[] aCoordinates = new float[2]; while ((distance < length) && (counter < size)) { // get point from the path pm.getPosTan(distance, aCoordinates, null); float pathX = aCoordinates[0]; float pathY = aCoordinates[1]; float x = (float) (vectorData.scaleVectorX(this) * pathX) + viewX; float y = (float) (vectorData.scaleVectorY(this) * pathY) + viewY; points.add(new Point(x, y)); counter++; distance = distance + speed; } return points; } 

path is our path that we get before that, iv is a vector container (for example, ImageView), we need it to adjust the coordinates of the points. vectorData is the structure we got before parsing our vector.

Now we need to define the area to handle the case when the path is closed, and we want to process the click inside the path as the distance 0:

 final Region region = new Region(); RectF rectF = new RectF(); path.computeBounds(rectF, true); region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom)); 

To calculate the minimum distance, use the following method:

 private int getMinDistance(float eventX, float eventY, Collection<Point> pathPoints, Region pathRegion, VectorData vectorData) { int minDistance = Integer.MAX_VALUE; boolean contains = pathRegion.contains((int) (eventX / vectorData.scaleVectorX(this)), (int) (eventY / vectorData.scaleVectorY(this))); if (contains) { minDistance = 0; } else { for (Point point : pathPoints) { int distance = getDistanceBetweenPoints((int) eventX, (int) eventY, (int) point.x, (int) point.y); if (distance < minDistance) { minDistance = distance; } } } return minDistance; } private int getDistanceBetweenPoints(int x, int y, int x1, int y1) { return (int) Math.sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y)); } 
+2
source

first get the coordinates of the point where the touches are used:

 @Override public boolean onTouch(View v, MotionEvent event) { float x1 = event.getX(); float y1 = event.getY(); return true; } 

since you know where the output will be displayed on the screen, you can assign some values ​​to the positioning cords (x2, y2) of your drawing.

or use a figurative image of your drawing with view.getLocationOnScreen(int\[\]) , for example:

 int[] posiXY = new int[2]; yourDrawablesImageView.getLocationOnScreen(posiXY); int x2 = posiXY[0]; int y2 = posiXY[1]; 

then if you just apply the distance formula:

 float distance=sqrt((x2βˆ’x1)*(x2βˆ’x1)+(y2βˆ’y1)*(y2βˆ’y1)); 

You will get the required distance.

0
source

Source: https://habr.com/ru/post/1013918/


All Articles