/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.catroid.physics.shapebuilder; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.PolygonShape; import com.badlogic.gdx.physics.box2d.Shape; import org.catrobat.catroid.physics.PhysicsWorldConverter; import java.util.ArrayList; import java.util.List; import java.util.Stack; public final class PhysicsShapeBuilderStrategyFastHull implements PhysicsShapeBuilderStrategy { private static final int MINIMUM_PIXEL_ALPHA_VALUE = 1; @Override public Shape[] build(Pixmap pixmap, float scale) { if (pixmap == null) { return null; } int width = pixmap.getWidth(); int height = pixmap.getHeight(); float coordinateAdjustmentValue = 1.0f; Stack<Vector2> convexHull = new Stack<Vector2>(); Vector2 point = new Vector2(width, height); for (int y = 0; y < height; y++) { for (int x = 0; x < point.x; x++) { if ((pixmap.getPixel(x, y) & 0xff) >= MINIMUM_PIXEL_ALPHA_VALUE) { point = new Vector2(x, y); addPoint(convexHull, point); break; } } } if (convexHull.isEmpty()) { return null; } for (int x = (int) point.x; x < width; x++) { for (int y = height - 1; y > point.y; y--) { if ((pixmap.getPixel(x, y) & 0xff) >= MINIMUM_PIXEL_ALPHA_VALUE) { point = new Vector2(x, y); addPoint(convexHull, new Vector2(x, y + coordinateAdjustmentValue)); break; } } } Vector2 firstPoint = convexHull.firstElement(); for (int y = (int) point.y; y >= firstPoint.y; y--) { for (int x = width - 1; x > point.x; x--) { if ((pixmap.getPixel(x, y) & 0xff) >= MINIMUM_PIXEL_ALPHA_VALUE) { point = new Vector2(x, y); addPoint(convexHull, new Vector2(x + coordinateAdjustmentValue, y + coordinateAdjustmentValue)); break; } } } for (int x = (int) point.x; x > firstPoint.x; x--) { for (int y = (int) firstPoint.y; y < point.y; y++) { if ((pixmap.getPixel(x, y) & 0xff) >= MINIMUM_PIXEL_ALPHA_VALUE) { point = new Vector2(x, y); addPoint(convexHull, new Vector2(x + coordinateAdjustmentValue, y)); break; } } } if (convexHull.size() > 2) { removeNonConvexPoints(convexHull, firstPoint); } return devideShape(convexHull.toArray(new Vector2[convexHull.size()]), width, height); } private void addPoint(Stack<Vector2> convexHull, Vector2 point) { removeNonConvexPoints(convexHull, point); convexHull.add(point); } private void removeNonConvexPoints(Stack<Vector2> convexHull, Vector2 newTop) { while (convexHull.size() > 1) { Vector2 top = convexHull.peek(); Vector2 secondTop = convexHull.get(convexHull.size() - 2); if (leftTurn(secondTop, top, newTop)) { break; } if (top.y > newTop.y && top.y > secondTop.y) { break; } convexHull.pop(); } } private boolean leftTurn(Vector2 a, Vector2 b, Vector2 c) { return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) < 0; } private Shape[] devideShape(Vector2[] convexpoints, int width, int height) { for (int index = 0; index < convexpoints.length; index++) { Vector2 point = convexpoints[index]; point.x -= width / 2.0f; point.y = height / 2.0f - point.y; convexpoints[index] = PhysicsWorldConverter.convertCatroidToBox2dVector(point); } if (convexpoints.length < 9) { PolygonShape polygon = new PolygonShape(); polygon.set(convexpoints); return new Shape[] { polygon }; } List<Shape> shapes = new ArrayList<Shape>(convexpoints.length / 6 + 1); List<Vector2> pointsPerShape = new ArrayList<Vector2>(8); Vector2 rome = convexpoints[0]; int index = 1; while (index < convexpoints.length - 1) { int k = index + 7; int remainingPointsCount = convexpoints.length - index; if (remainingPointsCount > 7 && remainingPointsCount < 9) { k -= 3; } pointsPerShape.add(rome); for (; index < k && index < convexpoints.length; index++) { pointsPerShape.add(convexpoints[index]); } PolygonShape polygon = new PolygonShape(); polygon.set(pointsPerShape.toArray(new Vector2[pointsPerShape.size()])); shapes.add(polygon); pointsPerShape.clear(); index--; } return shapes.toArray(new Shape[shapes.size()]); } }