Jump to content

Convex hull and triangle hovering for near perfect model bounds.


PolishCivil

Recommended Posts

Hey, before i start I've seen this one already on forums, as i remember our BrainFree mentioned it.

Because my engrish is really bad, i will try to provide links instead writing how it works.

So here it is one more time:


Convex hull is generally tightest area of point set.
So what we want to do is to calculate 2D points of our object, entity and make convex hull for it for perfect bounding polygon, it will be really helpfull on fighter scripts or any script that needs to interact alot, it will fail less time.

Bounding box you were using:

 

2014-06-17_04-46-29.png

 

What we want to accomplish:

 

2014-06-17_04-44-36.png

 

 

As you see we have some blind spots on basic rect bounding box:

 

2014-06-17_04-48-52.png

 

Red - hulling will reduce them.
Yellow - hulling will not reduce them (actually the one i want to show)

It is not really necessary to remove yellow ones so w/e.
The thing is we want to shink area a little bit to be more precise when interacting.

 

So the algorithm is like this:

  /**
     * Making convex hull around points.
     *
     * @param points - the points
     * @return - the convex hull points points.
     */
    public static short[][] hull(short[][] points) {
        int upperSize = 2;
        int lowerSize = 2;
        int pointsSize = points.length;

        short[][] lUpper = new short[pointsSize][2];
        short[][] lLower = new short[pointsSize][2];

        short[][] xSorted = points.clone();
        Arrays.sort(xSorted, new Comparator<short[]>() {
            @Override
            public int compare(short[] o1, short[] o2) {
                return Integer.compare(o1[0], o2[0]);
            }
        });
        lUpper[0] = xSorted[0];
        lUpper[1] = xSorted[1];
        lLower[0] = xSorted[pointsSize - 1];
        lLower[1] = xSorted[pointsSize - 2];


        for (int i = 2; i < pointsSize; i++) {
            lUpper[upperSize] = xSorted[i];
            upperSize++;

            while (upperSize > 2 && !rightTurn(lUpper[upperSize - 3], lUpper[upperSize - 2], lUpper[upperSize - 1])) {
                lUpper[upperSize - 2] = lUpper[upperSize - 1];
                upperSize--;
            }
        }


        for (int i = pointsSize - 3; i >= 0; i--) {
            lLower[lowerSize] = xSorted[i];
            lowerSize++;

            while (lowerSize > 2 && !rightTurn(lLower[lowerSize - 3], lLower[lowerSize - 2], lLower[lowerSize - 1])) {
                lLower[lowerSize - 2] = lLower[lowerSize - 1];
                lowerSize--;
            }
        }
        short[][] result = new short[upperSize + lowerSize - 1][2];
        System.arraycopy(lUpper, 0, result, 0, upperSize);
        System.arraycopy(lLower, 0, result, upperSize, lowerSize - 1);
        return result;
    }

    /**
     * Checks if points turns right.
     *
     * @param a  - the 'a' point.
     * @param b  - the 'b' point.
     * @param c  - the 'c' point.
     * @return
     */
    private static boolean rightTurn(short[] a, short[] b, short[] c) {
        return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]) > 0;
    }

(I ddnt made it as i remember but i cant find real author)

What do you need to read to understand this algorithm:

http://en.wikipedia.org/wiki/Convex_hull

en.wikipedia.org/wiki/Graham_scan

    /**
     * Converting points to polygon.
     *
     * @param points - the - the points.
     * @return the polygon based on points.
     */
    public static Polygon toPolygon(short[][] points) {
        Polygon result = new Polygon();
        for (short[] point : points) {
            result.addPoint(point[0], point[1]);
        }
        return result;
    }

How to implement :

 

First we need to get screen coords:

 

http://osbot.org/osbot2_api/org/osbot/rs07/api/util/GraphicUtilities.html#getScreenCoordinates%28org.osbot.rs07.Bot,%20int,%20int,%20int,%20org.osbot.rs07.api.model.Model%29

 

Then we make hull based on points:

hull(screenCoordinates);

Example for dumbs:

            Entity litara = npcs.closest("Litara");
            short[][] screenCoordinates = Calculations.getScreenCoordinates(this, litara.getGridX(), litara.getGridY(), litara.getZ(), litara.getModel());
            short[][] hull = Calculations.hull(screenCoordinates);
            g.draw(Calculations.toPolygon(hull));
Edited by PolishCivil
  • Like 4
Link to comment
Share on other sites

Don't they also need a custom mouse handler? SInce osbot api mouse.move only goes to a rectangle.

 

Ye, something like

    class PolygonDest extends RectangleDestination {
        private Polygon polygon;

        public PolygonDest(Bot bot, Polygon polygon) {
            super(bot, polygon.getBounds());
            this.polygon = polygon;
        }

        @Override
        public Area getArea() {
            return new Area(polygon);
        }
    }

It will work cuz they are actually using area to check if mouse position is inside it.

 

    @Override
    public int execute() throws InterruptedException {
        if (!this.destination.isVisible()) {
            final boolean b = false;
            this.setFailed();
            return b ? 1 : 0;
        }
        final Point position = this.mouse.getPosition();
        int n = 0;
        if ((((this.interactionEvent == null) ? this.destination.getArea().contains(position) : this.destination.evaluate(this.interactionEvent, this.destination.getBoundingBox())) ? (n = 1) : n) == 0 && MethodProvider.gRandom(3, 2.0) == 0) {
            n = 1;
        }
Link to comment
Share on other sites

If I recall, Jagex only checks if the mouse is over one of the triangles in the model, so isn't this a bit excessive for just clicking a model? Especially when you can be more accurate with just triangles. If you can achieve full control over click accuracy, then you can add in a degree of inaccuracy later. Not to mention the added expense of the extra calculations.

  • Like 1
Link to comment
Share on other sites

If I recall, Jagex only checks if the mouse is over one of the triangles in the model, so isn't this a bit excessive for just clicking a model? Especially when you can be more accurate with just triangles. If you can achieve full control over click accuracy, then you can add in a degree of inaccuracy later. Not to mention the added expense of the extra calculations.

 

Ye, but with just rect u can fail some times cuz mouse will not be on any triangle, this thing reduces such thing, but yh making it checking for triangles should be less pain and more precise.

Actually tbh i just wanted to show ppl that this thing exists and can be used :D

Edited by PolishCivil
Link to comment
Share on other sites

If I recall, Jagex only checks if the mouse is over one of the triangles in the model, so isn't this a bit excessive for just clicking a model? Especially when you can be more accurate with just triangles. If you can achieve full control over click accuracy, then you can add in a degree of inaccuracy later. Not to mention the added expense of the extra calculations.

 

I use something similar to that, I found moving entities or the player's movement itself cause enough inaccuracy where nothing more is needed. I also got better results when only a small portion of the entity was visible or entities with holes in them. Some modification is still needed to the points for some entities, such as fire, or ladders, objects that are half submerged underground. Mine removes any points that are below the position it's on.

 

Jq81CKO.png

Edited by Th3
  • Like 1
Link to comment
Share on other sites

@Proryan thing:

2014-06-17_11-00-07.png

    /**
     * Checks if we are hovering this entity.
     *
     * @param atMethodProvider - the atMethodProvider instance.
     * @param entity           - the entity.
     * @return whether we are.
     */
    public static boolean isHovering(ATMethodProvider atMethodProvider, Entity entity) {

        Model model = entity.getModel();
        short[][] screenCoordinates = GraphicUtilities.getScreenCoordinates(atMethodProvider.bot, entity.getGridX(), entity.getGridY(), entity.getZ(), model);
        for (int triangleId = 0; triangleId < model.getTriangleCount(); triangleId++) {
            int triangleA = model.getVertexXIndices()[triangleId];
            int triangleB = model.getVertexYIndices()[triangleId];
            int triangleC = model.getVertexZIndices()[triangleId];

            short[] pointA = screenCoordinates[triangleA];
            short[] pointB = screenCoordinates[triangleB];
            short[] pointC = screenCoordinates[triangleC];
            short[][] points = new short[][]{pointA, pointB, pointC};
            Point position = atMethodProvider.mouse.getPosition();
            if (Calculations.contains(points, position.x, position.y)) {
                return true;
            }
        }
        return false;
    }

@Contains is from Polygon.contains

 /**
     * Checks if point is inside polygon.
     *
     * @param points - the polygon points.
     * @param x      - the point x to check.
     * @param y      - the point y to check.
     * @return whether it is.
     */
    public static boolean contains(short[][] points, double x, double y) {
        int hits = 0;
        int npoints = points.length;

        int lastx = points[npoints - 1][0];
        int lasty = points[npoints - 1][1];
        int curx, cury;

        // Walk the edges of the polygon
        for (int i = 0; i < npoints; lastx = curx, lasty = cury, i++) {
            curx = points[i][0];
            cury = points[i][1];

            if (cury == lasty) {
                continue;
            }

            int leftx;
            if (curx < lastx) {
                if (x >= lastx) {
                    continue;
                }
                leftx = curx;
            } else {
                if (x >= curx) {
                    continue;
                }
                leftx = lastx;
            }

            double test1, test2;
            if (cury < lasty) {
                if (y < cury || y >= lasty) {
                    continue;
                }
                if (x < leftx) {
                    hits++;
                    continue;
                }
                test1 = x - curx;
                test2 = y - cury;
            } else {
                if (y < lasty || y >= cury) {
                    continue;
                }
                if (x < leftx) {
                    hits++;
                    continue;
                }
                test1 = x - lastx;
                test2 = y - lasty;
            }

            if (test1 < (test2 / (lasty - cury) * (lastx - curx))) {
                hits++;
            }
        }

        return ((hits & 1) != 0);
    }
Edited by PolishCivil
  • Like 1
Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...