First integration of Spine's BoundingBoxAttachment with Box2D
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
**Runtime**
|
||||
- [BREAK CHANGE] `TextureArrayCpuPolygonSpriteBatch` is now the default batch implementation
|
||||
- [BREAK CHANGE] New `MainItemComponent#uniqueId` management, change from integer to a random string
|
||||
- First integration of Spine's BoundingBoxAttachment with Box2D for skeletal collision detection
|
||||
- Chained Entities and Spine' SkeletonRenderSeparator
|
||||
- Improved PhysicsActions. Add `PhysicsActions#transformTo` and `PhysicsActions#transformBy`
|
||||
- Fixed physic's step
|
||||
|
||||
Submodule h2d-libgdx-spine-extension updated: dc7d17c743...e1fc8e8ae8
Submodule hyperlap2d-runtime-libgdx updated: e45ff31cde...36ae3b7cf8
+3
-2
@@ -6,6 +6,7 @@ import com.badlogic.gdx.utils.IntSet;
|
||||
import games.rednblack.editor.controller.SandboxCommand;
|
||||
import games.rednblack.editor.controller.commands.component.UpdatePolygonVerticesCommand;
|
||||
import games.rednblack.editor.renderer.components.shape.PolygonShapeComponent;
|
||||
import games.rednblack.editor.renderer.utils.poly.PolygonRuntimeUtils;
|
||||
import games.rednblack.editor.utils.poly.PolygonUtils;
|
||||
import games.rednblack.editor.utils.runtime.SandboxComponentRetriever;
|
||||
import games.rednblack.editor.view.ui.followers.PolygonFollower;
|
||||
@@ -50,10 +51,10 @@ public class ChangePolygonVertexPositionCommand extends SandboxCommand {
|
||||
Vector2[] pointsArray = points.toArray();
|
||||
IntSet intersections = PolygonUtils.checkForIntersection(anchor, points, intersectionProblems);
|
||||
if(intersections == null) {
|
||||
if(PolygonUtils.isPolygonCCW(pointsArray)){
|
||||
if(PolygonRuntimeUtils.isPolygonCCW(pointsArray)){
|
||||
points.reverse();
|
||||
}
|
||||
polygonShapeComponent.polygonizedVertices = PolygonUtils.polygonize(polygonShapeComponent.vertices.toArray());
|
||||
polygonShapeComponent.polygonizedVertices = PolygonRuntimeUtils.polygonize(polygonShapeComponent.vertices.toArray());
|
||||
|
||||
UpdatePolygonVerticesCommand.payload(currentCommandPayload, polygonShapeComponent.vertices, polygonShapeComponent.polygonizedVertices);
|
||||
Facade.getInstance().sendNotification(MsgAPI.ACTION_UPDATE_MESH_DATA, currentCommandPayload);
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import com.kotcrab.vis.ui.util.dialog.Dialogs;
|
||||
import games.rednblack.editor.controller.SandboxCommand;
|
||||
import games.rednblack.editor.controller.commands.component.UpdatePolygonVerticesCommand;
|
||||
import games.rednblack.editor.renderer.components.shape.PolygonShapeComponent;
|
||||
import games.rednblack.editor.utils.poly.PolygonUtils;
|
||||
import games.rednblack.editor.renderer.utils.poly.PolygonRuntimeUtils;
|
||||
import games.rednblack.editor.utils.runtime.SandboxComponentRetriever;
|
||||
import games.rednblack.editor.view.ui.followers.PolygonFollower;
|
||||
import games.rednblack.h2d.common.MsgAPI;
|
||||
@@ -47,7 +47,7 @@ public class DeletePolygonVertexCommand extends SandboxCommand {
|
||||
|
||||
polygonShapeComponent.vertices.removeIndex(anchor);
|
||||
follower.setSelectedAnchor(0);
|
||||
polygonShapeComponent.polygonizedVertices = PolygonUtils.polygonize(polygonShapeComponent.vertices.toArray());
|
||||
polygonShapeComponent.polygonizedVertices = PolygonRuntimeUtils.polygonize(polygonShapeComponent.vertices.toArray());
|
||||
|
||||
if(polygonShapeComponent.polygonizedVertices == null) {
|
||||
// restore from backup
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@ package games.rednblack.editor.controller.commands;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import games.rednblack.editor.renderer.components.shape.PolygonShapeComponent;
|
||||
import games.rednblack.editor.utils.poly.PolygonUtils;
|
||||
import games.rednblack.editor.renderer.utils.poly.PolygonRuntimeUtils;
|
||||
import games.rednblack.editor.utils.runtime.EntityUtils;
|
||||
import games.rednblack.editor.utils.runtime.SandboxComponentRetriever;
|
||||
import games.rednblack.h2d.common.MsgAPI;
|
||||
@@ -49,10 +49,10 @@ public class UpdatePolygonDataCommand extends EntityModifyRevertibleCommand {
|
||||
private void checkPolygon(PolygonShapeComponent polygonShapeComponent) {
|
||||
if (!polygonShapeComponent.openEnded) {
|
||||
Array<Vector2> points = polygonShapeComponent.vertices;
|
||||
if(PolygonUtils.isPolygonCCW(points.toArray())){
|
||||
if(PolygonRuntimeUtils.isPolygonCCW(points.toArray())){
|
||||
points.reverse();
|
||||
}
|
||||
polygonShapeComponent.polygonizedVertices = PolygonUtils.polygonize(points.toArray());
|
||||
polygonShapeComponent.polygonizedVertices = PolygonRuntimeUtils.polygonize(points.toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -5,6 +5,7 @@ import com.badlogic.gdx.utils.Array;
|
||||
import games.rednblack.editor.controller.commands.EntityModifyRevertibleCommand;
|
||||
import games.rednblack.editor.renderer.components.TextureRegionComponent;
|
||||
import games.rednblack.editor.renderer.components.shape.PolygonShapeComponent;
|
||||
import games.rednblack.editor.renderer.utils.poly.PolygonRuntimeUtils;
|
||||
import games.rednblack.editor.utils.runtime.EntityUtils;
|
||||
import games.rednblack.editor.utils.runtime.SandboxComponentRetriever;
|
||||
import games.rednblack.h2d.common.MsgAPI;
|
||||
@@ -88,11 +89,7 @@ public class UpdatePolygonVerticesCommand extends EntityModifyRevertibleCommand
|
||||
}
|
||||
|
||||
public static Array<Vector2> cloneData(Array<Vector2> data) {
|
||||
Array<Vector2> clone = new Array<>(true, data.size, Vector2.class);
|
||||
for (Vector2 vector2 : data) {
|
||||
clone.add(vector2.cpy());
|
||||
}
|
||||
return clone;
|
||||
return PolygonRuntimeUtils.cloneData(data);
|
||||
}
|
||||
public static Vector2[][] cloneData(Vector2[][] data) {
|
||||
Vector2[][] newData = new Vector2[data.length][];
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* * Copyright 2015 See AUTHORS file.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package games.rednblack.editor.utils.poly;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import games.rednblack.editor.utils.poly.earclipping.bayazit.BayazitDecomposer;
|
||||
import games.rednblack.editor.utils.poly.earclipping.ewjordan.EwjordanDecomposer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Aurelien Ribon | http://www.aurelienribon.com/
|
||||
*/
|
||||
public class Clipper {
|
||||
public enum Polygonizer {EWJORDAN, BAYAZIT}
|
||||
|
||||
public static Vector2[][] polygonize(Polygonizer polygonizer, Vector2[] points) {
|
||||
Vector2[][] polygons = null;
|
||||
|
||||
if (PolygonUtils.isPolygonCCW(points)) {
|
||||
List<Vector2> vertices = Arrays.asList(points);
|
||||
Collections.reverse(vertices);
|
||||
points = vertices.toArray(new Vector2[0]);
|
||||
}
|
||||
|
||||
switch (polygonizer) {
|
||||
case EWJORDAN:
|
||||
polygons = EwjordanDecomposer.decompose(points);
|
||||
break;
|
||||
|
||||
case BAYAZIT:
|
||||
Array<Vector2> tmpPoints = new Array<Vector2>(points.length);
|
||||
tmpPoints.addAll(points);
|
||||
|
||||
Array<Array<Vector2>> tmpPolygons;
|
||||
|
||||
try {
|
||||
tmpPolygons = BayazitDecomposer.ConvexPartition(tmpPoints);
|
||||
} catch (Exception ex) {
|
||||
tmpPolygons = null;
|
||||
}
|
||||
|
||||
if (tmpPolygons != null) {
|
||||
polygons = new Vector2[tmpPolygons.size][];
|
||||
for (int i = 0; i < tmpPolygons.size; i++) {
|
||||
polygons[i] = new Vector2[tmpPolygons.get(i).size];
|
||||
for (int ii = 0; ii < tmpPolygons.get(i).size; ii++)
|
||||
polygons[i][ii] = new Vector2(tmpPolygons.get(i).get(ii));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (polygons != null) polygons = sliceForMax8Vertices(polygons);
|
||||
return polygons;
|
||||
}
|
||||
|
||||
private static Vector2[][] sliceForMax8Vertices(Vector2[][] polygons) {
|
||||
for (int i = 0; i < polygons.length; i++) {
|
||||
Vector2[] poly = polygons[i];
|
||||
if (poly.length > 8) {
|
||||
int limit = poly.length < 15 ? poly.length / 2 + 1 : 8;
|
||||
Vector2[] newPoly1 = new Vector2[limit];
|
||||
Vector2[] newPoly2 = new Vector2[poly.length - limit + 2];
|
||||
System.arraycopy(poly, 0, newPoly1, 0, limit);
|
||||
System.arraycopy(poly, limit - 1, newPoly2, 0, poly.length - limit + 1);
|
||||
newPoly2[newPoly2.length - 1] = poly[0].cpy();
|
||||
|
||||
Vector2[][] newPolys = new Vector2[polygons.length + 1][];
|
||||
if (i > 0) {
|
||||
System.arraycopy(polygons, 0, newPolys, 0, i);
|
||||
}
|
||||
if (i < polygons.length - 1) {
|
||||
System.arraycopy(polygons, i + 1, newPolys, i + 2, polygons.length - i - 1);
|
||||
}
|
||||
newPolys[i] = newPoly1;
|
||||
newPolys[i + 1] = newPoly2;
|
||||
polygons = newPolys;
|
||||
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
return polygons;
|
||||
}
|
||||
}
|
||||
@@ -24,37 +24,11 @@ import com.badlogic.gdx.utils.*;
|
||||
import games.rednblack.editor.utils.Vector2Pool;
|
||||
import games.rednblack.editor.view.stage.Sandbox;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Aurelien Ribon | http://www.aurelienribon.com/
|
||||
*/
|
||||
public class PolygonUtils {
|
||||
public static float getPolygonSignedArea(Vector2[] points) {
|
||||
if (points.length < 3)
|
||||
return 0;
|
||||
|
||||
float sum = 0;
|
||||
for (int i = 0; i < points.length; i++) {
|
||||
Vector2 p1 = points[i];
|
||||
Vector2 p2 = i != points.length-1 ? points[i+1] : points[0];
|
||||
sum += (p1.x * p2.y) - (p1.y * p2.x);
|
||||
}
|
||||
return 0.5f * sum;
|
||||
}
|
||||
|
||||
public static float getPolygonArea(Vector2[] points) {
|
||||
return Math.abs(getPolygonSignedArea(points));
|
||||
}
|
||||
|
||||
public static boolean isPolygonCCW(Vector2[] points) {
|
||||
return getPolygonSignedArea(points) > 0;
|
||||
}
|
||||
|
||||
public static Vector2[][] polygonize(Vector2[] vertices) {
|
||||
return Clipper.polygonize(Clipper.Polygonizer.EWJORDAN, vertices);
|
||||
}
|
||||
|
||||
public static boolean intersectSegments(Array<Vector2> points, int index1, int index2, int index3, int index4) {
|
||||
Vector2 intersectionPoint = Pools.obtain(Vector2.class).set(points.get(index1));
|
||||
|
||||
-701
@@ -1,701 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* * Copyright 2015 See AUTHORS file.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package games.rednblack.editor.utils.poly.earclipping.bayazit;
|
||||
|
||||
// Taken from BayazitDecomposer.cs (FarseerPhysics.Common.Decomposition.BayazitDecomposer)
|
||||
// at http://farseerphysics.codeplex.com
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import games.rednblack.editor.controller.commands.component.UpdatePolygonVerticesCommand;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
|
||||
/// <summary>
|
||||
/// Convex decomposition algorithm created by Mark Bayazit (http://mnbayazit.com/)
|
||||
/// For more information about this algorithm, see http://mnbayazit.com/406/bayazit
|
||||
/// </summary>
|
||||
public class BayazitDecomposer {
|
||||
|
||||
public static final float Epsilon = 1.192092896e-07f;
|
||||
public static int MaxPolygonVertices = 8;
|
||||
|
||||
public static Vector2 Cross(Vector2 a, float s) {
|
||||
return new Vector2(s * a.y, -s * a.x);
|
||||
}
|
||||
|
||||
private static Vector2 At(int i, Array<Vector2> vertices) {
|
||||
int s = vertices.size;
|
||||
return vertices.get(i < 0 ? s - (-i % s) : i % s);
|
||||
}
|
||||
|
||||
private static Array<Vector2> Copy(int i, int j, Array<Vector2> vertices) {
|
||||
Array<Vector2> p = new Array<Vector2>();
|
||||
while (j < i)
|
||||
j += vertices.size;
|
||||
// p.reserve(j - i + 1);
|
||||
for (; i <= j; ++i) {
|
||||
p.add(At(i, vertices));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public static float GetSignedArea(Array<Vector2> vect) {
|
||||
int i;
|
||||
float area = 0;
|
||||
for (i = 0; i < vect.size; i++) {
|
||||
int j = (i + 1) % vect.size;
|
||||
area += vect.get(i).x * vect.get(j).y;
|
||||
area -= vect.get(i).y * vect.get(j).x;
|
||||
}
|
||||
area /= 2.0f;
|
||||
return area;
|
||||
}
|
||||
|
||||
public static float GetSignedArea(Vector2[] vect) {
|
||||
int i;
|
||||
float area = 0;
|
||||
for (i = 0; i < vect.length; i++) {
|
||||
int j = (i + 1) % vect.length;
|
||||
area += vect[i].x * vect[j].y;
|
||||
area -= vect[i].y * vect[j].x;
|
||||
}
|
||||
area /= 2.0f;
|
||||
return area;
|
||||
}
|
||||
|
||||
public static Boolean IsCounterClockWise(Array<Vector2> vect) {
|
||||
// We just return true for lines
|
||||
if (vect.size < 3)
|
||||
return true;
|
||||
return (GetSignedArea(vect) > 0.0f);
|
||||
}
|
||||
|
||||
public static Boolean IsCounterClockWise(Vector2[] vect) {
|
||||
// We just return true for lines
|
||||
if (vect.length < 3)
|
||||
return true;
|
||||
return (GetSignedArea(vect) > 0.0f);
|
||||
}
|
||||
|
||||
// / <summary>
|
||||
// / Decompose the polygon into several smaller non-concave polygon.
|
||||
// / If the polygon is already convex, it will return the original polygon,
|
||||
// unless it is over Settings.MaxPolygonVertices.
|
||||
// / Precondition: Counter Clockwise polygon
|
||||
// / </summary>
|
||||
// / <param name="vertices"></param>
|
||||
// / <returns></returns>
|
||||
public static Array<Array<Vector2>> ConvexPartition(Array<Vector2> vertices) {
|
||||
// We force it to CCW as it is a precondition in this algorithm.
|
||||
// vertices.ForceCounterClockWise();
|
||||
if (!IsCounterClockWise(vertices)) {
|
||||
// Collections.reverse(vertices);
|
||||
vertices.reverse();
|
||||
// Array<Vector2> reversed = new Array<Vector2>(vertices.size);
|
||||
// for (int i = vertices.size - 1; i <= 0; i--) {
|
||||
// reversed.add(vertices.get(i));
|
||||
// }
|
||||
// vertices = reversed;
|
||||
}
|
||||
Array<Array<Vector2>> list = new Array<Array<Vector2>>();
|
||||
float d, lowerDist, upperDist;
|
||||
Vector2 p;
|
||||
Vector2 lowerInt = new Vector2();
|
||||
Vector2 upperInt = new Vector2(); // intersection points
|
||||
int lowerIndex = 0, upperIndex = 0;
|
||||
Array<Vector2> lowerPoly, upperPoly;
|
||||
for (int i = 0; i < vertices.size; ++i) {
|
||||
if (Reflex(i, vertices)) {
|
||||
lowerDist = upperDist = Float.MAX_VALUE; // std::numeric_limits<qreal>::max();
|
||||
for (int j = 0; j < vertices.size; ++j) {
|
||||
// if line intersects with an edge
|
||||
if (Left(At(i - 1, vertices), At(i, vertices),
|
||||
At(j, vertices))
|
||||
&& RightOn(At(i - 1, vertices), At(i, vertices),
|
||||
At(j - 1, vertices))) {
|
||||
// find the point of intersection
|
||||
p = LineIntersect(At(i - 1, vertices), At(i, vertices),
|
||||
At(j, vertices), At(j - 1, vertices));
|
||||
if (Right(At(i + 1, vertices), At(i, vertices), p)) {
|
||||
// make sure it's inside the poly
|
||||
d = SquareDist(At(i, vertices), p);
|
||||
if (d < lowerDist) {
|
||||
// keep only the closest intersection
|
||||
lowerDist = d;
|
||||
lowerInt = p;
|
||||
lowerIndex = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Left(At(i + 1, vertices), At(i, vertices),
|
||||
At(j + 1, vertices))
|
||||
&& RightOn(At(i + 1, vertices), At(i, vertices),
|
||||
At(j, vertices))) {
|
||||
p = LineIntersect(At(i + 1, vertices), At(i, vertices),
|
||||
At(j, vertices), At(j + 1, vertices));
|
||||
if (Left(At(i - 1, vertices), At(i, vertices), p)) {
|
||||
d = SquareDist(At(i, vertices), p);
|
||||
if (d < upperDist) {
|
||||
upperDist = d;
|
||||
upperIndex = j;
|
||||
upperInt = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if there are no vertices to connect to, choose a point in the
|
||||
// middle
|
||||
if (lowerIndex == (upperIndex + 1) % vertices.size) {
|
||||
Vector2 sp = new Vector2((lowerInt.x + upperInt.x) / 2,
|
||||
(lowerInt.y + upperInt.y) / 2);
|
||||
lowerPoly = Copy(i, upperIndex, vertices);
|
||||
lowerPoly.add(sp);
|
||||
upperPoly = Copy(lowerIndex, i, vertices);
|
||||
upperPoly.add(sp);
|
||||
} else {
|
||||
double highestScore = 0, bestIndex = lowerIndex;
|
||||
while (upperIndex < lowerIndex)
|
||||
upperIndex += vertices.size;
|
||||
for (int j = lowerIndex; j <= upperIndex; ++j) {
|
||||
if (CanSee(i, j, vertices)) {
|
||||
double score = 1 / (SquareDist(At(i, vertices),
|
||||
At(j, vertices)) + 1);
|
||||
if (Reflex(j, vertices)) {
|
||||
if (RightOn(At(j - 1, vertices),
|
||||
At(j, vertices), At(i, vertices))
|
||||
&& LeftOn(At(j + 1, vertices),
|
||||
At(j, vertices),
|
||||
At(i, vertices))) {
|
||||
score += 3;
|
||||
} else {
|
||||
score += 2;
|
||||
}
|
||||
} else {
|
||||
score += 1;
|
||||
}
|
||||
if (score > highestScore) {
|
||||
bestIndex = j;
|
||||
highestScore = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
lowerPoly = Copy(i, (int) bestIndex, vertices);
|
||||
upperPoly = Copy((int) bestIndex, i, vertices);
|
||||
}
|
||||
list.addAll(ConvexPartition(lowerPoly));
|
||||
list.addAll(ConvexPartition(upperPoly));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
// polygon is already convex
|
||||
if (vertices.size > MaxPolygonVertices) {
|
||||
lowerPoly = Copy(0, vertices.size / 2, vertices);
|
||||
upperPoly = Copy(vertices.size / 2, 0, vertices);
|
||||
list.addAll(ConvexPartition(lowerPoly));
|
||||
list.addAll(ConvexPartition(upperPoly));
|
||||
} else
|
||||
list.add(vertices);
|
||||
// The polygons are not guaranteed to be with collinear points. We
|
||||
// remove
|
||||
// them to be sure.
|
||||
for (int i = 0; i < list.size; i++) {
|
||||
list.set(i, SimplifyTools.CollinearSimplify(list.get(i), 0));
|
||||
}
|
||||
// Remove empty vertice collections
|
||||
for (int i = list.size - 1; i >= 0; i--) {
|
||||
if (list.get(i).size == 0)
|
||||
list.removeIndex(i);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Boolean CanSee(int i, int j, Array<Vector2> vertices) {
|
||||
if (Reflex(i, vertices)) {
|
||||
if (LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices))
|
||||
&& RightOn(At(i, vertices), At(i + 1, vertices),
|
||||
At(j, vertices)))
|
||||
return false;
|
||||
} else {
|
||||
if (RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices))
|
||||
|| LeftOn(At(i, vertices), At(i - 1, vertices),
|
||||
At(j, vertices)))
|
||||
return false;
|
||||
}
|
||||
if (Reflex(j, vertices)) {
|
||||
if (LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices))
|
||||
&& RightOn(At(j, vertices), At(j + 1, vertices),
|
||||
At(i, vertices)))
|
||||
return false;
|
||||
} else {
|
||||
if (RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices))
|
||||
|| LeftOn(At(j, vertices), At(j - 1, vertices),
|
||||
At(i, vertices)))
|
||||
return false;
|
||||
}
|
||||
for (int k = 0; k < vertices.size; ++k) {
|
||||
if ((k + 1) % vertices.size == i || k == i
|
||||
|| (k + 1) % vertices.size == j || k == j) {
|
||||
continue; // ignore incident edges
|
||||
}
|
||||
Vector2 intersectionPoint = new Vector2();
|
||||
if (LineIntersect(At(i, vertices), At(j, vertices),
|
||||
At(k, vertices), At(k + 1, vertices), true, true,
|
||||
intersectionPoint)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Vector2 LineIntersect(Vector2 p1, Vector2 p2, Vector2 q1,
|
||||
Vector2 q2) {
|
||||
Vector2 i = new Vector2();
|
||||
float a1 = p2.y - p1.y;
|
||||
float b1 = p1.x - p2.x;
|
||||
float c1 = a1 * p1.x + b1 * p1.y;
|
||||
float a2 = q2.y - q1.y;
|
||||
float b2 = q1.x - q2.x;
|
||||
float c2 = a2 * q1.x + b2 * q1.y;
|
||||
float det = a1 * b2 - a2 * b1;
|
||||
if (!FloatEquals(det, 0)) {
|
||||
// lines are not parallel
|
||||
i.x = (b2 * c1 - b1 * c2) / det;
|
||||
i.y = (a1 * c2 - a2 * c1) / det;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static Boolean FloatEquals(float value1, float value2) {
|
||||
return Math.abs(value1 - value2) <= Epsilon;
|
||||
}
|
||||
|
||||
// / <summary>
|
||||
// / This method detects if two line segments (or lines) intersect,
|
||||
// / and, if so, the point of intersection. Use the
|
||||
// <paramname="firstIsSegment"/> and
|
||||
// / <paramname="secondIsSegment"/> parameters to set whether the
|
||||
// intersection point
|
||||
// / must be on the first and second line segments. Setting these
|
||||
// / both to true means you are doing a line-segment to line-segment
|
||||
// / intersection. Setting one of them to true means you are doing a
|
||||
// / line to line-segment intersection test, and so on.
|
||||
// / Note: If two line segments are coincident, then
|
||||
// / no intersection is detected (there are actually
|
||||
// / infinite intersection points).
|
||||
// / Author: Jeremy Bell
|
||||
// / </summary>
|
||||
// / <param name="point1">The first point of the first line segment.</param>
|
||||
// / <param name="point2">The second point of the first line
|
||||
// segment.</param>
|
||||
// / <param name="point3">The first point of the second line
|
||||
// segment.</param>
|
||||
// / <param name="point4">The second point of the second line
|
||||
// segment.</param>
|
||||
// / <param name="point">This is set to the intersection
|
||||
// / point if an intersection is detected.</param>
|
||||
// / <param name="firstIsSegment">Set this to true to require that the
|
||||
// / intersection point be on the first line segment.</param>
|
||||
// / <param name="secondIsSegment">Set this to true to require that the
|
||||
// / intersection point be on the second line segment.</param>
|
||||
// / <returns>True if an intersection is detected, false
|
||||
// otherwise.</returns>
|
||||
public static Boolean LineIntersect(Vector2 point1, Vector2 point2,
|
||||
Vector2 point3, Vector2 point4, Boolean firstIsSegment,
|
||||
Boolean secondIsSegment, Vector2 point) {
|
||||
point = new Vector2();
|
||||
// these are reused later.
|
||||
// each lettered sub-calculation is used twice, except
|
||||
// for b and d, which are used 3 times
|
||||
float a = point4.y - point3.y;
|
||||
float b = point2.x - point1.x;
|
||||
float c = point4.x - point3.x;
|
||||
float d = point2.y - point1.y;
|
||||
// denominator to solution of linear system
|
||||
float denom = (a * b) - (c * d);
|
||||
// if denominator is 0, then lines are parallel
|
||||
if (!(denom >= -Epsilon && denom <= Epsilon)) {
|
||||
float e = point1.y - point3.y;
|
||||
float f = point1.x - point3.x;
|
||||
float oneOverDenom = 1.0f / denom;
|
||||
// numerator of first equation
|
||||
float ua = (c * e) - (a * f);
|
||||
ua *= oneOverDenom;
|
||||
// check if intersection point of the two lines is on line segment 1
|
||||
if (!firstIsSegment || ua >= 0.0f && ua <= 1.0f) {
|
||||
// numerator of second equation
|
||||
float ub = (b * e) - (d * f);
|
||||
ub *= oneOverDenom;
|
||||
// check if intersection point of the two lines is on line
|
||||
// segment 2
|
||||
// means the line segments intersect, since we know it is on
|
||||
// segment 1 as well.
|
||||
if (!secondIsSegment || ub >= 0.0f && ub <= 1.0f) {
|
||||
// check if they are coincident (no collision in this case)
|
||||
if (ua != 0f || ub != 0f) {
|
||||
// There is an intersection
|
||||
point.x = point1.x + ua * b;
|
||||
point.y = point1.y + ua * d;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// precondition: ccw
|
||||
private static Boolean Reflex(int i, Array<Vector2> vertices) {
|
||||
return Right(i, vertices);
|
||||
}
|
||||
|
||||
private static Boolean Right(int i, Array<Vector2> vertices) {
|
||||
return Right(At(i - 1, vertices), At(i, vertices), At(i + 1, vertices));
|
||||
}
|
||||
|
||||
private static Boolean Left(Vector2 a, Vector2 b, Vector2 c) {
|
||||
return Area(a, b, c) > 0;
|
||||
}
|
||||
|
||||
private static Boolean LeftOn(Vector2 a, Vector2 b, Vector2 c) {
|
||||
return Area(a, b, c) >= 0;
|
||||
}
|
||||
|
||||
private static Boolean Right(Vector2 a, Vector2 b, Vector2 c) {
|
||||
return Area(a, b, c) < 0;
|
||||
}
|
||||
|
||||
private static Boolean RightOn(Vector2 a, Vector2 b, Vector2 c) {
|
||||
return Area(a, b, c) <= 0;
|
||||
}
|
||||
|
||||
public static float Area(Vector2 a, Vector2 b, Vector2 c) {
|
||||
return a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y);
|
||||
}
|
||||
|
||||
private static float SquareDist(Vector2 a, Vector2 b) {
|
||||
float dx = b.x - a.x;
|
||||
float dy = b.y - a.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
}
|
||||
|
||||
class SimplifyTools {
|
||||
private static Boolean[] _usePt;
|
||||
private static double _distanceTolerance;
|
||||
|
||||
// / <summary>
|
||||
// / Removes all collinear points on the polygon.
|
||||
// / </summary>
|
||||
// / <param name="vertices">The polygon that needs simplification.</param>
|
||||
// / <param name="collinearityTolerance">The collinearity tolerance.</param>
|
||||
// / <returns>A simplified polygon.</returns>
|
||||
public static Array<Vector2> CollinearSimplify(Array<Vector2> vertices,
|
||||
float collinearityTolerance) {
|
||||
// We can't simplify polygons under 3 vertices
|
||||
if (vertices.size < 3)
|
||||
return vertices;
|
||||
Array<Vector2> simplified = new Array<Vector2>();
|
||||
for (int i = 0; i < vertices.size; i++) {
|
||||
int prevId = i - 1;
|
||||
if (prevId < 0)
|
||||
prevId = vertices.size - 1;
|
||||
int nextId = i + 1;
|
||||
if (nextId >= vertices.size)
|
||||
nextId = 0;
|
||||
Vector2 prev = vertices.get(prevId);
|
||||
Vector2 current = vertices.get(i);
|
||||
Vector2 next = vertices.get(nextId);
|
||||
// If they collinear, continue
|
||||
if (Collinear(prev, current, next, collinearityTolerance))
|
||||
continue;
|
||||
simplified.add(current);
|
||||
}
|
||||
return simplified;
|
||||
}
|
||||
|
||||
public static Boolean Collinear(Vector2 a, Vector2 b, Vector2 c,
|
||||
float tolerance) {
|
||||
return FloatInRange(BayazitDecomposer.Area(a, b, c), -tolerance,
|
||||
tolerance);
|
||||
}
|
||||
|
||||
public static Boolean FloatInRange(float value, float min, float max) {
|
||||
return (value >= min && value <= max);
|
||||
}
|
||||
|
||||
// / <summary>
|
||||
// / Removes all collinear points on the polygon.
|
||||
// / Has a default bias of 0
|
||||
// / </summary>
|
||||
// / <param name="vertices">The polygon that needs simplification.</param>
|
||||
// / <returns>A simplified polygon.</returns>
|
||||
public static Array<Vector2> CollinearSimplify(Array<Vector2> vertices) {
|
||||
return CollinearSimplify(vertices, 0);
|
||||
}
|
||||
|
||||
// / <summary>
|
||||
// / Ramer-Douglas-Peucker polygon simplification algorithm. This is the
|
||||
// general recursive version that does not use the
|
||||
// / speed-up technique by using the Melkman convex hull.
|
||||
// /
|
||||
// / If you pass in 0, it will remove all collinear points
|
||||
// / </summary>
|
||||
// / <returns>The simplified polygon</returns>
|
||||
public static Array<Vector2> DouglasPeuckerSimplify(
|
||||
Array<Vector2> vertices, float distanceTolerance) {
|
||||
_distanceTolerance = distanceTolerance;
|
||||
_usePt = new Boolean[vertices.size];
|
||||
for (int i = 0; i < vertices.size; i++)
|
||||
_usePt[i] = true;
|
||||
SimplifySection(vertices, 0, vertices.size - 1);
|
||||
Array<Vector2> result = new Array<Vector2>();
|
||||
for (int i = 0; i < vertices.size; i++)
|
||||
if (_usePt[i])
|
||||
result.add(vertices.get(i));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void SimplifySection(Array<Vector2> vertices, int i, int j) {
|
||||
if ((i + 1) == j)
|
||||
return;
|
||||
Vector2 A = vertices.get(i);
|
||||
Vector2 B = vertices.get(j);
|
||||
double maxDistance = -1.0;
|
||||
int maxIndex = i;
|
||||
for (int k = i + 1; k < j; k++) {
|
||||
double distance = DistancePointLine(vertices.get(k), A, B);
|
||||
if (distance > maxDistance) {
|
||||
maxDistance = distance;
|
||||
maxIndex = k;
|
||||
}
|
||||
}
|
||||
if (maxDistance <= _distanceTolerance)
|
||||
for (int k = i + 1; k < j; k++)
|
||||
_usePt[k] = false;
|
||||
else {
|
||||
SimplifySection(vertices, i, maxIndex);
|
||||
SimplifySection(vertices, maxIndex, j);
|
||||
}
|
||||
}
|
||||
|
||||
private static double DistancePointPoint(Vector2 p, Vector2 p2) {
|
||||
double dx = p.x - p2.x;
|
||||
double dy = p.y - p2.x;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
private static double DistancePointLine(Vector2 p, Vector2 A, Vector2 B) {
|
||||
// if start == end, then use point-to-point distance
|
||||
if (A.x == B.x && A.y == B.y)
|
||||
return DistancePointPoint(p, A);
|
||||
// otherwise use comp.graphics.algorithms Frequently Asked Questions
|
||||
// method
|
||||
/*
|
||||
* (1) AC dot AB r = --------- ||AB||^2 r has the following meaning: r=0
|
||||
* Point = A r=1 Point = B r<0 Point is on the backward extension of AB
|
||||
* r>1 Point is on the forward extension of AB 0<r<1 Point is interior
|
||||
* to AB
|
||||
*/
|
||||
double r = ((p.x - A.x) * (B.x - A.x) + (p.y - A.y) * (B.y - A.y))
|
||||
/ ((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
|
||||
if (r <= 0.0)
|
||||
return DistancePointPoint(p, A);
|
||||
if (r >= 1.0)
|
||||
return DistancePointPoint(p, B);
|
||||
/*
|
||||
* (2) (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) s = -----------------------------
|
||||
* Curve^2 Then the distance from C to Point = |s|*Curve.
|
||||
*/
|
||||
double s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y))
|
||||
/ ((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
|
||||
return Math.abs(s)
|
||||
* Math.sqrt(((B.x - A.x) * (B.x - A.x) + (B.y - A.y)
|
||||
* (B.y - A.y)));
|
||||
}
|
||||
|
||||
// From physics2d.net
|
||||
public static Array<Vector2> ReduceByArea(Array<Vector2> vertices,
|
||||
float areaTolerance) {
|
||||
if (vertices.size <= 3)
|
||||
return vertices;
|
||||
if (areaTolerance < 0) {
|
||||
throw new InvalidParameterException(
|
||||
"areaTolerance: must be equal to or greater then zero.");
|
||||
}
|
||||
Array<Vector2> result = new Array<Vector2>();
|
||||
Vector2 v1, v2, v3;
|
||||
float old1, old2, new1;
|
||||
v1 = vertices.get(vertices.size - 2);
|
||||
v2 = vertices.get(vertices.size - 1);
|
||||
areaTolerance *= 2;
|
||||
for (int index = 0; index < vertices.size; ++index, v2 = v3) {
|
||||
if (index == vertices.size - 1) {
|
||||
if (result.size == 0) {
|
||||
throw new InvalidParameterException(
|
||||
"areaTolerance: The tolerance is too high!");
|
||||
}
|
||||
v3 = result.get(0);
|
||||
} else {
|
||||
v3 = vertices.get(index);
|
||||
}
|
||||
old1 = Cross(v1, v2);
|
||||
old2 = Cross(v2, v3);
|
||||
new1 = Cross(v1, v3);
|
||||
if (Math.abs(new1 - (old1 + old2)) > areaTolerance) {
|
||||
result.add(v2);
|
||||
v1 = v2;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Float Cross(Vector2 a, Vector2 b) {
|
||||
return a.x * b.y - a.y * b.x;
|
||||
}
|
||||
|
||||
// From Eric Jordan's convex decomposition library
|
||||
// / <summary>
|
||||
// / Merges all parallel edges in the list of vertices
|
||||
// / </summary>
|
||||
// / <param name="vertices">The vertices.</param>
|
||||
// / <param name="tolerance">The tolerance.</param>
|
||||
public static void MergeParallelEdges(Array<Vector2> vertices,
|
||||
float tolerance) {
|
||||
if (vertices.size <= 3)
|
||||
return; // Can't do anything useful here to a triangle
|
||||
Boolean[] mergeMe = new Boolean[vertices.size];
|
||||
int newNVertices = vertices.size;
|
||||
// Gather points to process
|
||||
for (int i = 0; i < vertices.size; ++i) {
|
||||
int lower = (i == 0) ? (vertices.size - 1) : (i - 1);
|
||||
int middle = i;
|
||||
int upper = (i == vertices.size - 1) ? (0) : (i + 1);
|
||||
float dx0 = vertices.get(middle).x - vertices.get(lower).x;
|
||||
float dy0 = vertices.get(middle).y - vertices.get(lower).y;
|
||||
float dx1 = vertices.get(upper).y - vertices.get(middle).x;
|
||||
float dy1 = vertices.get(upper).y - vertices.get(middle).y;
|
||||
float norm0 = (float) Math.sqrt(dx0 * dx0 + dy0 * dy0);
|
||||
float norm1 = (float) Math.sqrt(dx1 * dx1 + dy1 * dy1);
|
||||
if (!(norm0 > 0.0f && norm1 > 0.0f) && newNVertices > 3) {
|
||||
// Merge identical points
|
||||
mergeMe[i] = true;
|
||||
--newNVertices;
|
||||
}
|
||||
dx0 /= norm0;
|
||||
dy0 /= norm0;
|
||||
dx1 /= norm1;
|
||||
dy1 /= norm1;
|
||||
float cross = dx0 * dy1 - dx1 * dy0;
|
||||
float dot = dx0 * dx1 + dy0 * dy1;
|
||||
if (Math.abs(cross) < tolerance && dot > 0 && newNVertices > 3) {
|
||||
mergeMe[i] = true;
|
||||
--newNVertices;
|
||||
} else
|
||||
mergeMe[i] = false;
|
||||
}
|
||||
if (newNVertices == vertices.size || newNVertices == 0)
|
||||
return;
|
||||
int currIndex = 0;
|
||||
// Copy the vertices to a new list and clear the old
|
||||
Array<Vector2> oldVertices = UpdatePolygonVerticesCommand.cloneData(vertices);
|
||||
vertices.clear();
|
||||
for (int i = 0; i < oldVertices.size; ++i) {
|
||||
if (mergeMe[i] || newNVertices == 0 || currIndex == newNVertices)
|
||||
continue;
|
||||
// Debug.Assert(currIndex < newNVertices);
|
||||
vertices.add(oldVertices.get(i));
|
||||
++currIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Misc
|
||||
// / <summary>
|
||||
// / Merges the identical points in the polygon.
|
||||
// / </summary>
|
||||
// / <param name="vertices">The vertices.</param>
|
||||
// / <returns></returns>
|
||||
public static Array<Vector2> MergeIdenticalPoints(Array<Vector2> vertices) {
|
||||
Array<Vector2> results = new Array<Vector2>();
|
||||
for (int i = 0; i < vertices.size; i++) {
|
||||
Vector2 vOriginal = vertices.get(i);
|
||||
|
||||
boolean alreadyExists = false;
|
||||
for (int j = 0; j < results.size; j++) {
|
||||
Vector2 v = results.get(j);
|
||||
if (vOriginal.equals(v)) {
|
||||
alreadyExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!alreadyExists)
|
||||
results.add(vertices.get(i));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// / <summary>
|
||||
// / Reduces the polygon by distance.
|
||||
// / </summary>
|
||||
// / <param name="vertices">The vertices.</param>
|
||||
// / <param name="distance">The distance between points. Points closer than
|
||||
// this will be 'joined'.</param>
|
||||
// / <returns></returns>
|
||||
public static Array<Vector2> ReduceByDistance(Array<Vector2> vertices,
|
||||
float distance) {
|
||||
// We can't simplify polygons under 3 vertices
|
||||
if (vertices.size < 3)
|
||||
return vertices;
|
||||
Array<Vector2> simplified = new Array<Vector2>();
|
||||
for (int i = 0; i < vertices.size; i++) {
|
||||
Vector2 current = vertices.get(i);
|
||||
int ii = i + 1;
|
||||
if (ii >= vertices.size)
|
||||
ii = 0;
|
||||
Vector2 next = vertices.get(ii);
|
||||
Vector2 diff = new Vector2(next.x - current.x, next.y - current.y);
|
||||
// If they are closer than the distance, continue
|
||||
if (diff.len2() <= distance)
|
||||
continue;
|
||||
simplified.add(current);
|
||||
}
|
||||
return simplified;
|
||||
}
|
||||
|
||||
// / <summary>
|
||||
// / Reduces the polygon by removing the Nth vertex in the vertices list.
|
||||
// / </summary>
|
||||
// / <param name="vertices">The vertices.</param>
|
||||
// / <param name="nth">The Nth point to remove. Example: 5.</param>
|
||||
// / <returns></returns>
|
||||
public static Array<Vector2> ReduceByNth(Array<Vector2> vertices, int nth) {
|
||||
// We can't simplify polygons under 3 vertices
|
||||
if (vertices.size < 3)
|
||||
return vertices;
|
||||
if (nth == 0)
|
||||
return vertices;
|
||||
Array<Vector2> result = new Array<Vector2>(vertices.size);
|
||||
for (int i = 0; i < vertices.size; i++) {
|
||||
if (i % nth == 0)
|
||||
continue;
|
||||
result.add(vertices.get(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* * Copyright 2015 See AUTHORS file.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package games.rednblack.editor.utils.poly.earclipping.ewjordan;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
|
||||
/**
|
||||
* Original code from EwJordan (http://www.ewjordan.com/earClip/)
|
||||
*/
|
||||
public class EwjordanDecomposer {
|
||||
public static Vector2[][] decompose(Vector2[] points) {
|
||||
int vNum = points.length;
|
||||
float[] xv = new float[vNum];
|
||||
float[] yv = new float[vNum];
|
||||
|
||||
for (int i = 0; i < vNum; i++) {
|
||||
xv[i] = points[i].x;
|
||||
yv[i] = points[i].y;
|
||||
}
|
||||
|
||||
Triangle[] tempTriangles = triangulatePolygon(xv, yv, vNum);
|
||||
Polygon[] tempPolygons = polygonizeTriangles(tempTriangles);
|
||||
|
||||
if (tempPolygons == null)
|
||||
return null;
|
||||
|
||||
Vector2[][] polygons = new Vector2[tempPolygons.length][];
|
||||
for (int i = 0; i < tempPolygons.length; i++) {
|
||||
polygons[i] = new Vector2[tempPolygons[i].nVertices];
|
||||
for (int ii = 0; ii < tempPolygons[i].nVertices; ii++)
|
||||
polygons[i][ii] = new Vector2(tempPolygons[i].x[ii], tempPolygons[i].y[ii]);
|
||||
}
|
||||
|
||||
return polygons;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private static Triangle[] triangulatePolygon(float[] xv, float[] yv, int vNum) {
|
||||
if (vNum < 3)
|
||||
return null;
|
||||
|
||||
Triangle[] buffer = new Triangle[vNum];
|
||||
int bufferSize = 0;
|
||||
float[] xrem = new float[vNum];
|
||||
float[] yrem = new float[vNum];
|
||||
for (int i = 0; i < vNum; ++i) {
|
||||
xrem[i] = xv[i];
|
||||
yrem[i] = yv[i];
|
||||
}
|
||||
|
||||
while (vNum > 3) {
|
||||
int earIndex = -1;
|
||||
for (int i = 0; i < vNum; ++i) {
|
||||
if (isEar(i, xrem, yrem)) {
|
||||
earIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (earIndex == -1)
|
||||
return null;
|
||||
|
||||
--vNum;
|
||||
float[] newx = new float[vNum];
|
||||
float[] newy = new float[vNum];
|
||||
int currDest = 0;
|
||||
for (int i = 0; i < vNum; ++i) {
|
||||
if (currDest == earIndex) {
|
||||
++currDest;
|
||||
}
|
||||
newx[i] = xrem[currDest];
|
||||
newy[i] = yrem[currDest];
|
||||
++currDest;
|
||||
}
|
||||
|
||||
int under = (earIndex == 0) ? (xrem.length - 1) : (earIndex - 1);
|
||||
int over = (earIndex == xrem.length - 1) ? 0 : (earIndex + 1);
|
||||
|
||||
Triangle toAdd = new Triangle(xrem[earIndex], yrem[earIndex], xrem[over], yrem[over], xrem[under], yrem[under]);
|
||||
buffer[bufferSize] = toAdd;
|
||||
++bufferSize;
|
||||
|
||||
xrem = newx;
|
||||
yrem = newy;
|
||||
}
|
||||
Triangle toAdd = new Triangle(xrem[1], yrem[1], xrem[2], yrem[2], xrem[0], yrem[0]);
|
||||
buffer[bufferSize] = toAdd;
|
||||
++bufferSize;
|
||||
|
||||
Triangle[] res = new Triangle[bufferSize];
|
||||
System.arraycopy(buffer, 0, res, 0, bufferSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static Polygon[] polygonizeTriangles(Triangle[] triangulated) {
|
||||
Polygon[] polys;
|
||||
int polyIndex = 0;
|
||||
|
||||
if (triangulated == null)
|
||||
return null;
|
||||
|
||||
polys = new Polygon[triangulated.length];
|
||||
boolean[] covered = new boolean[triangulated.length];
|
||||
for (int i = 0; i < triangulated.length; i++)
|
||||
covered[i] = false;
|
||||
|
||||
boolean notDone = true;
|
||||
|
||||
while (notDone) {
|
||||
int currTri = -1;
|
||||
for (int i = 0; i < triangulated.length; i++) {
|
||||
if (!covered[i]) {
|
||||
currTri = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currTri == -1) {
|
||||
notDone = false;
|
||||
} else {
|
||||
Polygon poly = new Polygon(triangulated[currTri]);
|
||||
covered[currTri] = true;
|
||||
for (int i = 0; i < triangulated.length; i++) {
|
||||
if (covered[i])
|
||||
continue;
|
||||
|
||||
Polygon newP = poly.add(triangulated[i]);
|
||||
if (newP == null)
|
||||
continue;
|
||||
|
||||
if (newP.isConvex()) {
|
||||
poly = newP;
|
||||
covered[i] = true;
|
||||
}
|
||||
}
|
||||
polys[polyIndex] = poly;
|
||||
polyIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
Polygon[] ret = new Polygon[polyIndex];
|
||||
System.arraycopy(polys, 0, ret, 0, polyIndex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static boolean isEar(int i, float[] xv, float[] yv) {
|
||||
float dx0, dy0, dx1, dy1;
|
||||
dx0 = dy0 = dx1 = dy1 = 0;
|
||||
|
||||
if (i >= xv.length || i < 0 || xv.length < 3)
|
||||
return false;
|
||||
|
||||
int upper = i + 1;
|
||||
int lower = i - 1;
|
||||
|
||||
if (i == 0) {
|
||||
dx0 = xv[0] - xv[xv.length - 1];
|
||||
dy0 = yv[0] - yv[yv.length - 1];
|
||||
dx1 = xv[1] - xv[0];
|
||||
dy1 = yv[1] - yv[0];
|
||||
lower = xv.length - 1;
|
||||
} else if (i == xv.length - 1) {
|
||||
dx0 = xv[i] - xv[i - 1];
|
||||
dy0 = yv[i] - yv[i - 1];
|
||||
dx1 = xv[0] - xv[i];
|
||||
dy1 = yv[0] - yv[i];
|
||||
upper = 0;
|
||||
} else {
|
||||
dx0 = xv[i] - xv[i - 1];
|
||||
dy0 = yv[i] - yv[i - 1];
|
||||
dx1 = xv[i + 1] - xv[i];
|
||||
dy1 = yv[i + 1] - yv[i];
|
||||
}
|
||||
|
||||
float cross = dx0 * dy1 - dx1 * dy0;
|
||||
if (cross > 0)
|
||||
return false;
|
||||
|
||||
Triangle myTri = new Triangle(xv[i], yv[i], xv[upper], yv[upper], xv[lower], yv[lower]);
|
||||
for (int j = 0; j < xv.length; ++j) {
|
||||
if (j == i || j == lower || j == upper)
|
||||
continue;
|
||||
if (myTri.isInside(xv[j], yv[j]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* * Copyright 2015 See AUTHORS file.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package games.rednblack.editor.utils.poly.earclipping.ewjordan;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author EwJordan (http://www.ewjordan.com/earClip/)
|
||||
*/
|
||||
public class Polygon {
|
||||
public float[] x;
|
||||
public float[] y;
|
||||
public int nVertices;
|
||||
|
||||
public Polygon(float[] _x, float[] _y) {
|
||||
nVertices = _x.length;
|
||||
x = new float[nVertices];
|
||||
y = new float[nVertices];
|
||||
for (int i = 0; i < nVertices; ++i) {
|
||||
x[i] = _x[i];
|
||||
y[i] = _y[i];
|
||||
}
|
||||
}
|
||||
|
||||
public Polygon(Triangle t) {
|
||||
this(t.x, t.y);
|
||||
}
|
||||
|
||||
public void set(Polygon p) {
|
||||
nVertices = p.nVertices;
|
||||
x = new float[nVertices];
|
||||
y = new float[nVertices];
|
||||
for (int i = 0; i < nVertices; ++i) {
|
||||
x[i] = p.x[i];
|
||||
y[i] = p.y[i];
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConvex() {
|
||||
boolean isPositive = false;
|
||||
for (int i = 0; i < nVertices; ++i) {
|
||||
int lower = (i == 0) ? (nVertices - 1) : (i - 1);
|
||||
int middle = i;
|
||||
int upper = (i == nVertices - 1) ? (0) : (i + 1);
|
||||
float dx0 = x[middle] - x[lower];
|
||||
float dy0 = y[middle] - y[lower];
|
||||
float dx1 = x[upper] - x[middle];
|
||||
float dy1 = y[upper] - y[middle];
|
||||
float cross = dx0 * dy1 - dx1 * dy0;
|
||||
//Cross product should have same sign
|
||||
//for each vertex if poly is convex.
|
||||
boolean newIsP = (cross > 0) ? true : false;
|
||||
if (i == 0) {
|
||||
isPositive = newIsP;
|
||||
} else if (isPositive != newIsP) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to add a triangle to the polygon.
|
||||
* Returns null if it can't connect properly.
|
||||
* Assumes bitwise equality of join vertices.
|
||||
*/
|
||||
public Polygon add(Triangle t) {
|
||||
//First, find vertices that connect
|
||||
int firstP = -1;
|
||||
int firstT = -1;
|
||||
int secondP = -1;
|
||||
int secondT = -1;
|
||||
for (int i = 0; i < nVertices; i++) {
|
||||
if (t.x[0] == x[i] && t.y[0] == y[i]) {
|
||||
if (firstP == -1) {
|
||||
firstP = i;
|
||||
firstT = 0;
|
||||
} else {
|
||||
secondP = i;
|
||||
secondT = 0;
|
||||
}
|
||||
} else if (t.x[1] == x[i] && t.y[1] == y[i]) {
|
||||
if (firstP == -1) {
|
||||
firstP = i;
|
||||
firstT = 1;
|
||||
} else {
|
||||
secondP = i;
|
||||
secondT = 1;
|
||||
}
|
||||
} else if (t.x[2] == x[i] && t.y[2] == y[i]) {
|
||||
if (firstP == -1) {
|
||||
firstP = i;
|
||||
firstT = 2;
|
||||
} else {
|
||||
secondP = i;
|
||||
secondT = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Fix ordering if first should be last vertex of poly
|
||||
if (firstP == 0 && secondP == nVertices - 1) {
|
||||
firstP = nVertices - 1;
|
||||
secondP = 0;
|
||||
}
|
||||
|
||||
//Didn't find it
|
||||
if (secondP == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//Find tip index on triangle
|
||||
int tipT = 0;
|
||||
if (tipT == firstT || tipT == secondT) {
|
||||
tipT = 1;
|
||||
}
|
||||
if (tipT == firstT || tipT == secondT) {
|
||||
tipT = 2;
|
||||
}
|
||||
|
||||
float[] newx = new float[nVertices + 1];
|
||||
float[] newy = new float[nVertices + 1];
|
||||
int currOut = 0;
|
||||
for (int i = 0; i < nVertices; i++) {
|
||||
newx[currOut] = x[i];
|
||||
newy[currOut] = y[i];
|
||||
if (i == firstP) {
|
||||
++currOut;
|
||||
newx[currOut] = t.x[tipT];
|
||||
newy[currOut] = t.y[tipT];
|
||||
}
|
||||
++currOut;
|
||||
}
|
||||
return new Polygon(newx, newy);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* * Copyright 2015 See AUTHORS file.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package games.rednblack.editor.utils.poly.earclipping.ewjordan;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author EwJordan (http://www.ewjordan.com/earClip/)
|
||||
*/
|
||||
public class Triangle {
|
||||
public float[] x;
|
||||
public float[] y;
|
||||
|
||||
public Triangle(float x1, float y1, float x2, float y2, float x3, float y3) {
|
||||
x = new float[3];
|
||||
y = new float[3];
|
||||
float dx1 = x2 - x1;
|
||||
float dx2 = x3 - x1;
|
||||
float dy1 = y2 - y1;
|
||||
float dy2 = y3 - y1;
|
||||
float cross = dx1 * dy2 - dx2 * dy1;
|
||||
boolean ccw = (cross > 0);
|
||||
if (ccw) {
|
||||
x[0] = x1;
|
||||
x[1] = x2;
|
||||
x[2] = x3;
|
||||
y[0] = y1;
|
||||
y[1] = y2;
|
||||
y[2] = y3;
|
||||
} else {
|
||||
x[0] = x1;
|
||||
x[1] = x3;
|
||||
x[2] = x2;
|
||||
y[0] = y1;
|
||||
y[1] = y3;
|
||||
y[2] = y2;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInside(float _x, float _y) {
|
||||
float vx2 = _x - x[0];
|
||||
float vy2 = _y - y[0];
|
||||
float vx1 = x[1] - x[0];
|
||||
float vy1 = y[1] - y[0];
|
||||
float vx0 = x[2] - x[0];
|
||||
float vy0 = y[2] - y[0];
|
||||
|
||||
float dot00 = vx0 * vx0 + vy0 * vy0;
|
||||
float dot01 = vx0 * vx1 + vy0 * vy1;
|
||||
float dot02 = vx0 * vx2 + vy0 * vy2;
|
||||
float dot11 = vx1 * vx1 + vy1 * vy1;
|
||||
float dot12 = vx1 * vx2 + vy1 * vy2;
|
||||
float invDenom = 1f / (dot00 * dot11 - dot01 * dot01);
|
||||
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||
|
||||
return ((u > 0) && (v > 0) && (u + v < 1));
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import games.rednblack.editor.controller.commands.AddComponentToItemCommand;
|
||||
import games.rednblack.editor.controller.commands.RemoveComponentFromItemCommand;
|
||||
import games.rednblack.editor.controller.commands.component.UpdatePolygonVerticesCommand;
|
||||
import games.rednblack.editor.renderer.components.shape.PolygonShapeComponent;
|
||||
import games.rednblack.editor.renderer.utils.poly.PolygonRuntimeUtils;
|
||||
import games.rednblack.editor.utils.KeyBindingsLayout;
|
||||
import games.rednblack.editor.utils.poly.PolygonUtils;
|
||||
import games.rednblack.editor.utils.runtime.SandboxComponentRetriever;
|
||||
@@ -132,10 +133,10 @@ public class PolygonTool extends SelectionTool implements PolygonTransformationL
|
||||
if (!polygonShapeComponent.openEnded) {
|
||||
IntSet intersections = PolygonUtils.checkForIntersection(vertexIndex, points, intersectionProblems);
|
||||
if(intersections == null) {
|
||||
if(PolygonUtils.isPolygonCCW(points.toArray())){
|
||||
if(PolygonRuntimeUtils.isPolygonCCW(points.toArray())){
|
||||
points.reverse();
|
||||
}
|
||||
polygonShapeComponent.polygonizedVertices = PolygonUtils.polygonize(points.toArray());
|
||||
polygonShapeComponent.polygonizedVertices = PolygonRuntimeUtils.polygonize(points.toArray());
|
||||
} else {
|
||||
// restore from backup
|
||||
polygonShapeComponent.vertices = UpdatePolygonVerticesCommand.cloneData(verticesBackup);
|
||||
@@ -156,7 +157,7 @@ public class PolygonTool extends SelectionTool implements PolygonTransformationL
|
||||
|
||||
polygonShapeComponent.vertices.insert(vertexIndex, new Vector2(x, y));
|
||||
if (!polygonShapeComponent.openEnded)
|
||||
polygonShapeComponent.polygonizedVertices = PolygonUtils.polygonize(polygonShapeComponent.vertices.toArray());
|
||||
polygonShapeComponent.polygonizedVertices = PolygonRuntimeUtils.polygonize(polygonShapeComponent.vertices.toArray());
|
||||
follower.update();
|
||||
|
||||
follower.draggingAnchorId = vertexIndex;
|
||||
@@ -195,7 +196,7 @@ public class PolygonTool extends SelectionTool implements PolygonTransformationL
|
||||
// check if any of near lines intersect
|
||||
IntSet intersections = PolygonUtils.checkForIntersection(anchor, points, intersectionProblems);
|
||||
if(intersections == null) {
|
||||
polygonShapeComponent.polygonizedVertices = PolygonUtils.polygonize(points.toArray());
|
||||
polygonShapeComponent.polygonizedVertices = PolygonRuntimeUtils.polygonize(points.toArray());
|
||||
follower.setProblems(null);
|
||||
} else {
|
||||
follower.setProblems(intersections);
|
||||
@@ -252,7 +253,7 @@ public class PolygonTool extends SelectionTool implements PolygonTransformationL
|
||||
follower.setSelectedAnchor(follower.getSelectedAnchorId() - 1);
|
||||
|
||||
if (!polygonShapeComponent.openEnded) {
|
||||
polygonShapeComponent.polygonizedVertices = PolygonUtils.polygonize(polygonShapeComponent.vertices.toArray());
|
||||
polygonShapeComponent.polygonizedVertices = PolygonRuntimeUtils.polygonize(polygonShapeComponent.vertices.toArray());
|
||||
|
||||
if(polygonShapeComponent.polygonizedVertices == null) {
|
||||
// restore from backup
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.badlogic.gdx.utils.Array;
|
||||
import com.kotcrab.vis.ui.util.dialog.Dialogs;
|
||||
import games.rednblack.editor.renderer.components.shape.PolygonShapeComponent;
|
||||
import games.rednblack.editor.renderer.components.TextureRegionComponent;
|
||||
import games.rednblack.editor.utils.poly.PolygonUtils;
|
||||
import games.rednblack.editor.renderer.utils.poly.PolygonRuntimeUtils;
|
||||
import games.rednblack.editor.utils.poly.tracer.Tracer;
|
||||
import games.rednblack.editor.utils.runtime.SandboxComponentRetriever;
|
||||
import games.rednblack.editor.view.stage.Sandbox;
|
||||
@@ -71,7 +71,7 @@ public class AutoTraceDialogMediator extends Mediator<AutoTraceDialog> {
|
||||
.flatMap(Stream::of)
|
||||
.toArray(Vector2[]::new);
|
||||
polygonShapeComponent.vertices = new Array<>(points);
|
||||
polygonShapeComponent.polygonizedVertices = PolygonUtils.polygonize(points);
|
||||
polygonShapeComponent.polygonizedVertices = PolygonRuntimeUtils.polygonize(points);
|
||||
|
||||
FollowersUIMediator followersUIMediator = Facade.getInstance().retrieveMediator(FollowersUIMediator.NAME);
|
||||
BasicFollower follower = followersUIMediator.getFollower(entity);
|
||||
|
||||
Reference in New Issue
Block a user