diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index d0475c0f8..4fc2aa7ad 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -874,20 +874,23 @@ public class Animation { } static public class PathConstraintTimeline extends CurveTimeline { - static private final int PREV_TIME = -4; - static private final int PREV_POSITION = -3; - static private final int PREV_ROTATE_MIX = -2; - static private final int PREV_TRANSLATE_MIX = -1; + static private final int PREV_TIME = -5; + static private final int PREV_POSITION = -4; + static private final int PREV_ROTATE_MIX = -3; + static private final int PREV_TRANSLATE_MIX = -2; + static private final int PREV_SCALE_MIX = -1; static private final int POSITION = 1; static private final int ROTATE_MIX = 2; static private final int TRANSLATE_MIX = 3; + static private final int SCALE_MIX = 4; int pathConstraintIndex; + private final float[] frames; // time, rotate mix, translate mix, scale mix, shear mix, ... public PathConstraintTimeline (int frameCount) { super(frameCount); - frames = new float[frameCount * 4]; + frames = new float[frameCount * 5]; } public void setPathConstraintIndex (int index) { @@ -903,12 +906,13 @@ public class Animation { } /** Sets the time, position, and mixes of the specified keyframe. */ - public void setFrame (int frameIndex, float time, float position, float rotateMix, float translateMix) { - frameIndex *= 4; + public void setFrame (int frameIndex, float time, float position, float rotateMix, float translateMix, float scaleMix) { + frameIndex *= 5; frames[frameIndex] = time; frames[frameIndex + 1] = position; frames[frameIndex + 2] = rotateMix; frames[frameIndex + 3] = translateMix; + frames[frameIndex + 4] = scaleMix; } public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { @@ -917,27 +921,30 @@ public class Animation { PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex); - if (time >= frames[frames.length - 4]) { // Time is after last frame. + if (time >= frames[frames.length - 5]) { // Time is after last frame. int i = frames.length - 1; - constraint.position += (frames[i - 2] - constraint.position) * alpha; - constraint.rotateMix += (frames[i - 1] - constraint.rotateMix) * alpha; - constraint.translateMix += (frames[i] - constraint.translateMix) * alpha; + constraint.position += (frames[i - 3] - constraint.position) * alpha; + constraint.rotateMix += (frames[i - 2] - constraint.rotateMix) * alpha; + constraint.translateMix += (frames[i - 1] - constraint.translateMix) * alpha; + constraint.scaleMix += (frames[i] - constraint.scaleMix) * alpha; return; } // Interpolate between the previous frame and the current frame. - int frame = binarySearch(frames, time, 4); + int frame = binarySearch(frames, time, 5); float frameTime = frames[frame]; float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); - percent = getCurvePercent(frame / 4 - 1, percent); + percent = getCurvePercent(frame / 5 - 1, percent); float position = frames[frame + PREV_POSITION]; float rotate = frames[frame + PREV_ROTATE_MIX]; float translate = frames[frame + PREV_TRANSLATE_MIX]; + float scale = frames[frame + PREV_SCALE_MIX]; constraint.position += (position + (frames[frame + POSITION] - position) * percent - constraint.position) * alpha; constraint.rotateMix += (rotate + (frames[frame + ROTATE_MIX] - rotate) * percent - constraint.rotateMix) * alpha; constraint.translateMix += (translate + (frames[frame + TRANSLATE_MIX] - translate) * percent - constraint.translateMix) * alpha; + constraint.scaleMix += (scale + (frames[frame + SCALE_MIX] - scale) * percent - constraint.scaleMix) * alpha; } } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java index 6eecc75f0..459148127 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java @@ -3,7 +3,6 @@ package com.esotericsoftware.spine; import static com.badlogic.gdx.math.MathUtils.*; -import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.FloatArray; import com.esotericsoftware.spine.attachments.Attachment; @@ -13,15 +12,16 @@ public class PathConstraint implements Updatable { final PathConstraintData data; final Array bones; Slot target; - float position, rotateMix, translateMix; + float position, rotateMix, translateMix, scaleMix; final FloatArray lengths = new FloatArray(), positions = new FloatArray(); - final Vector2 temp = new Vector2(); + final FloatArray worldVertices = new FloatArray(), temp = new FloatArray(); public PathConstraint (PathConstraintData data, Skeleton skeleton) { this.data = data; position = data.position; rotateMix = data.rotateMix; translateMix = data.translateMix; + scaleMix = data.scaleMix; bones = new Array(data.bones.size); for (BoneData boneData : data.bones) @@ -40,6 +40,7 @@ public class PathConstraint implements Updatable { position = constraint.position; rotateMix = constraint.rotateMix; translateMix = constraint.translateMix; + scaleMix = constraint.scaleMix; } public void apply () { @@ -50,12 +51,12 @@ public class PathConstraint implements Updatable { Attachment attachment = target.getAttachment(); if (!(attachment instanceof PathAttachment)) return; - float translateMix = this.translateMix, rotateMix = this.rotateMix; - boolean translate = translateMix > 0, rotate = rotateMix > 0; + float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix; + boolean translate = translateMix > 0, rotate = rotateMix > 0, scale = scaleMix > 0; if (!translate && !rotate) return; PathAttachment path = (PathAttachment)attachment; - FloatArray lengths = this.lengths, positions = this.positions; + FloatArray lengths = this.lengths; lengths.clear(); lengths.add(0); positions.clear(); @@ -63,7 +64,7 @@ public class PathConstraint implements Updatable { Array bones = this.bones; int boneCount = bones.size; if (boneCount == 1) { - path.computeWorldPositions(target, position, lengths, positions, rotate); + computeWorldPositions(path, rotate); Bone bone = bones.first(); bone.worldX += (positions.first() - bone.worldX) * translateMix; bone.worldY += (positions.get(1) - bone.worldY) * translateMix; @@ -85,22 +86,39 @@ public class PathConstraint implements Updatable { for (int i = 0; i < boneCount; i++) lengths.add(bones.get(i).data.length); - path.computeWorldPositions(target, position, lengths, positions, false); + computeWorldPositions(path, false); - Vector2 temp = this.temp; - float boneX = positions.first(), boneY = positions.get(1); + float[] positions = this.positions.items; + float boneX = positions[0], boneY = positions[1]; for (int i = 0, p = 2; i < boneCount; i++, p += 2) { Bone bone = bones.get(i); + float x = positions[p], y = positions[p + 1]; + + if (scale) { + float dx = boneX - x, dy = boneY - y, d = (float)Math.sqrt(dx * dx + dy * dy); + // BOZO - Length not transformed by bone matrix. + float sx = bone.scaleX + (d / bone.data.length - bone.scaleX) * scaleMix; + bone.a *= sx; + bone.c *= sx; + } + bone.worldX += (boneX - bone.worldX) * translateMix; bone.worldY += (boneY - bone.worldY) * translateMix; - float x = positions.get(p), y = positions.get(p + 1); + float r = atan2(y - boneY, x - boneX) + data.offsetRotation * degRad; + if (data.offsetRotation != 0) { + boneX = x; + boneY = y; + } else { + // BOZO - Doesn't transform by bone matrix. + float cos = cos(r), sin = sin(r); + float length = bone.data.length, mix = rotateMix * (1 - scaleMix); + boneX = x + (length * cos + boneX - x) * mix; + boneY = y + (length * sin + boneY - y) * mix; + } if (rotate) { - // Scale. - // float dist = (float)Math.sqrt((boneX - x) * (boneX - x) + (boneY - y) * (boneY - y)); - // bone.scaleX = dist / bone.data.length; float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - float r = atan2(y - boneY, x - boneX) - atan2(c, a) + data.offsetRotation * degRad; + r -= atan2(c, a); if (r > PI) r -= PI2; else if (r < -PI) r += PI2; @@ -110,24 +128,216 @@ public class PathConstraint implements Updatable { bone.b = cos * b - sin * d; bone.c = sin * a + cos * c; bone.d = sin * b + cos * d; - if (data.offsetRotation == 0 && rotateMix == 1) { - // Place at tip. - bone.localToWorld(temp.set(bone.data.length, 0)); - boneX = temp.x; - boneY = temp.y; - } else { - // Place on path. - boneX = x; - boneY = y; - } - } else { - // Place on path. - boneX = x; - boneY = y; } } } + private void computeWorldPositions (PathAttachment path, boolean tangents) { + Slot slot = target; + float position = this.position; + FloatArray out = positions; + FloatArray lengths = this.lengths; + + int verticesLength = path.getWorldVerticesLength(), curves = verticesLength / 6; + boolean closed = path.getClosed(); + float[] vertices; + + if (!path.getConstantSpeed()) { + if (!closed) curves--; + float pathLength = path.getLength(); + vertices = worldVertices.setSize(8); + for (int i = 0, n = lengths.size; i < n; i++) { + position += lengths.get(i) / pathLength; + if (closed) { + position %= 1; + if (position < 0) position += 1; + } else if (position < 0 || position > 1) { + path.computeWorldVertices(slot, 0, 4, vertices, 0); + path.computeWorldVertices(slot, verticesLength - 4, 4, vertices, 4); + addOutsidePoint(position * pathLength, vertices, 8, pathLength, out, tangents); + continue; + } + int curve = position < 1 ? (int)(curves * position) : curves - 1; + if (closed && curve == curves - 1) { + path.computeWorldVertices(slot, verticesLength - 4, 4, vertices, 0); + path.computeWorldVertices(slot, 0, 4, vertices, 4); + } else + path.computeWorldVertices(slot, curve * 6 + 2, 8, vertices, 0); + addCurvePoint(vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], vertices[6], vertices[7], + (position - curve / (float)curves) * curves, tangents, out); + } + return; + } + + if (closed) { + verticesLength += 2; + vertices = worldVertices.setSize(verticesLength); + path.computeWorldVertices(slot, 2, verticesLength - 4, vertices, 0); + path.computeWorldVertices(slot, 0, 2, vertices, verticesLength - 4); + vertices[verticesLength - 2] = vertices[0]; + vertices[verticesLength - 1] = vertices[1]; + } else { + verticesLength -= 4; + vertices = worldVertices.setSize(verticesLength); + path.computeWorldVertices(slot, 2, verticesLength, vertices, 0); + } + + // Curve lengths. + temp.setSize(10 + curves); // BOZO - Combine with worldVertices? + float[] temp = this.temp.items; + float pathLength = 0, x1 = vertices[0], y1 = vertices[1], cx1, cy1, cx2, cy2, x2, y2; + float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy; + for (int i = 10, w = 2; w < verticesLength; i++, w += 6) { + cx1 = vertices[w]; + cy1 = vertices[w + 1]; + cx2 = vertices[w + 2]; + cy2 = vertices[w + 3]; + x2 = vertices[w + 4]; + y2 = vertices[w + 5]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f; + pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[i] = pathLength; + x1 = x2; + y1 = y2; + } + position *= pathLength; + + for (int i = 0, n = lengths.size; i < n; i++) { + position += lengths.get(i); + float p = position; + + if (closed) { + p %= pathLength; + if (p < 0) p += pathLength; + } else if (p < 0 || p > pathLength) { + addOutsidePoint(p, vertices, verticesLength, pathLength, out, tangents); + continue; + } + + // Determine curve containing position. + int curve; + float length = temp[10]; + if (p <= length) { + curve = 0; + p /= length; + } else { + for (curve = 11;; curve++) { + length = temp[curve]; + if (p <= length) { + float prev = temp[curve - 1]; + p = (p - prev) / (length - prev); + break; + } + } + curve = (curve - 10) * 6; + } + + // Curve segment lengths. + x1 = vertices[curve]; + y1 = vertices[curve + 1]; + cx1 = vertices[curve + 2]; + cy1 = vertices[curve + 3]; + cx2 = vertices[curve + 4]; + cy2 = vertices[curve + 5]; + x2 = vertices[curve + 6]; + y2 = vertices[curve + 7]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.03f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; + length = (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[0] = length; + for (int ii = 1; ii < 8; ii++) { + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[ii] = length; + } + dfx += ddfx; + dfy += ddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[8] = length; + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[9] = length; + + // Weight by segment length. + p *= length; + length = temp[0]; + if (p <= length) + p = 0.1f * p / length; + else { + for (int ii = 1;; ii++) { + length = temp[ii]; + if (p <= length) { + float prev = temp[ii - 1]; + p = 0.1f * (ii + (p - prev) / (length - prev)); + break; + } + } + } + + addCurvePoint(x1, y1, cx1, cy1, cx2, cy2, x2, y2, p, tangents, out); + } + } + + private void addOutsidePoint (float position, float[] vertices, int verticesLength, float pathLength, FloatArray out, + boolean tangents) { + float x1, y1, x2, y2; + if (position < 0) { + x1 = vertices[0]; + y1 = vertices[1]; + x2 = vertices[2] - x1; + y2 = vertices[3] - y1; + } else { + x1 = vertices[verticesLength - 2]; + y1 = vertices[verticesLength - 1]; + x2 = x1 - vertices[verticesLength - 4]; + y2 = y1 - vertices[verticesLength - 3]; + position -= pathLength; + } + float r = atan2(y2, x2); + out.add(x1 + position * cos(r)); + out.add(y1 + position * sin(r)); + if (tangents) out.add(r + PI); + } + + private void addCurvePoint (float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float position, + boolean tangents, FloatArray out) { + if (position == 0) position = 0.0001f; + float tt = position * position, ttt = tt * position, u = 1 - position, uu = u * u, uuu = uu * u; + float ut = u * position, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * position; + float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + out.add(x); + out.add(y); + if (tangents) out.add(atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt))); + } + public float getPosition () { return position; } @@ -152,6 +362,14 @@ public class PathConstraint implements Updatable { this.translateMix = translateMix; } + public float getScaleMix () { + return scaleMix; + } + + public void setScaleMix (float scaleMix) { + this.scaleMix = scaleMix; + } + public Array getBones () { return bones; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java index 4df2952ea..c4e4d4f03 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java @@ -7,7 +7,7 @@ public class PathConstraintData { final String name; final Array bones = new Array(); SlotData target; - float position, rotateMix, translateMix; + float position, rotateMix, translateMix, scaleMix; float offsetRotation; public PathConstraintData (String name) { @@ -50,6 +50,14 @@ public class PathConstraintData { this.translateMix = translateMix; } + public float getScaleMix () { + return scaleMix; + } + + public void setScaleMix (float scaleMix) { + this.scaleMix = scaleMix; + } + public float getOffsetRotation () { return offsetRotation; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index 823da165d..32ff7a6da 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -227,6 +227,7 @@ public class Skeleton { constraint.position = data.position; constraint.rotateMix = data.rotateMix; constraint.translateMix = data.translateMix; + constraint.scaleMix = data.scaleMix; } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index becad6226..1bddff0d3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -217,6 +217,7 @@ public class SkeletonBinary { data.position = input.readFloat(); data.rotateMix = input.readFloat(); data.translateMix = input.readFloat(); + data.scaleMix = input.readFloat(); skeletonData.pathConstraints.add(data); } @@ -588,11 +589,12 @@ public class SkeletonBinary { PathConstraintTimeline timeline = new PathConstraintTimeline(frameCount); timeline.pathConstraintIndex = index; for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat()); + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), + input.readFloat()); if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[frameCount * 4 - 4]); + duration = Math.max(duration, timeline.getFrames()[frameCount * 5 - 5]); } // Deform timelines. diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index ed6d1f0b9..4c7ecf0c3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -199,6 +199,7 @@ public class SkeletonJson { data.position = constraintMap.getFloat("position", 0); data.rotateMix = constraintMap.getFloat("rotateMix", 1); data.translateMix = constraintMap.getFloat("translateMix", 1); + data.scaleMix = constraintMap.getFloat("scaleMix", 1); skeletonData.pathConstraints.add(data); } @@ -506,12 +507,12 @@ public class SkeletonJson { int frameIndex = 0; for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("scaleMix", 1), - valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1)); + valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1), valueMap.getFloat("scaleMix", 1)); readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 4 - 4]); + duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 5 - 5]); } // Deform timelines. diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java index 4c4ef8786..4683221b4 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java @@ -31,19 +31,16 @@ package com.esotericsoftware.spine.attachments; -import static com.badlogic.gdx.math.MathUtils.*; - import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.utils.FloatArray; import com.esotericsoftware.spine.Slot; public class PathAttachment extends VertexAttachment { + float length; + boolean closed, constantSpeed; + // Nonessential. final Color color = new Color(1, 0.5f, 0, 1); - float[] worldVertices, temp; - boolean closed, constantSpeed; - public PathAttachment (String name) { super(name); } @@ -52,201 +49,8 @@ public class PathAttachment extends VertexAttachment { super.computeWorldVertices(slot, worldVertices); } - public void computeWorldPositions (Slot slot, float position, FloatArray lengths, FloatArray out, boolean tangents) { - float[] vertices = this.worldVertices; - int verticesLength = worldVerticesLength; - int curves = verticesLength / 6; - - if (!constantSpeed) { - for (int i = 0, n = lengths.size; i < n; i++) { - // BOZO - Wrong. Use path length property to give !constantSpeed paths support for oob and multiple bones? - position += lengths.get(i); - if (closed) { - position %= 1; - if (position < 0) position += 1; - } else { - position = clamp(position, 0, 1); - curves--; - } - int curve = position < 1 ? (int)(curves * position) : curves - 1; - position = (position - curve / (float)curves) * curves; - - if (closed && curve == curves - 1) { - super.computeWorldVertices(slot, curves * 6 - 4, 4, vertices, 0); - super.computeWorldVertices(slot, 0, 4, vertices, 4); - } else - super.computeWorldVertices(slot, curve * 6 + 2, 8, vertices, 0); - - addPoint(vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], vertices[6], vertices[7], - position, tangents, out); - } - return; - } - - if (closed) { - super.computeWorldVertices(slot, 2, verticesLength - 2, vertices, 0); - super.computeWorldVertices(slot, 0, 2, vertices, verticesLength - 2); - vertices[verticesLength] = vertices[0]; - vertices[verticesLength + 1] = vertices[1]; - verticesLength += 2; - } else { - verticesLength -= 4; - super.computeWorldVertices(slot, 2, verticesLength, vertices, 0); - } - - // Curve lengths. - float[] temp = this.temp; - float pathLength = 0, x1 = vertices[0], y1 = vertices[1], cx1, cy1, cx2, cy2, x2, y2; - float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy; - for (int i = 10, w = 2; w < verticesLength; i++, w += 6) { - cx1 = vertices[w]; - cy1 = vertices[w + 1]; - cx2 = vertices[w + 2]; - cy2 = vertices[w + 3]; - x2 = vertices[w + 4]; - y2 = vertices[w + 5]; - tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f; - tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; - dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f; - dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f; - ddfx = tmpx * 2 + dddfx; - ddfy = tmpy * 2 + dddfy; - dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f; - dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f; - pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); - dfx += ddfx; - dfy += ddfy; - ddfx += dddfx; - ddfy += dddfy; - pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); - dfx += ddfx; - dfy += ddfy; - pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); - dfx += ddfx + dddfx; - dfy += ddfy + dddfy; - pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); - temp[i] = pathLength; - x1 = x2; - y1 = y2; - } - position *= pathLength; - - for (int i = 0, n = lengths.size; i < n; i++) { - position += lengths.get(i); - float p = position; - - if (closed) { - p %= pathLength; - if (p < 0) p += pathLength; - } else if (p < 0 || p > pathLength) { - // Outside path. - if (p < 0) { - x1 = vertices[0]; - y1 = vertices[1]; - cx1 = vertices[2] - x1; - cy1 = vertices[3] - y1; - } else { - x1 = vertices[verticesLength - 2]; - y1 = vertices[verticesLength - 1]; - cx1 = x1 - vertices[verticesLength - 4]; - cy1 = y1 - vertices[verticesLength - 3]; - p -= pathLength; - } - float r = atan2(cy1, cx1); - out.add(x1 + p * cos(r)); - out.add(y1 + p * sin(r)); - if (tangents) out.add(r + PI); - continue; - } - - // Determine curve containing position. - int curve; - float length = temp[10]; - if (p <= length) { - curve = 0; - p /= length; - } else { - for (curve = 11;; curve++) { - length = temp[curve]; - if (p <= length) { - float prev = temp[curve - 1]; - p = (p - prev) / (length - prev); - break; - } - } - curve = (curve - 10) * 6; - } - - // Curve segment lengths. - x1 = vertices[curve]; - y1 = vertices[curve + 1]; - cx1 = vertices[curve + 2]; - cy1 = vertices[curve + 3]; - cx2 = vertices[curve + 4]; - cy2 = vertices[curve + 5]; - x2 = vertices[curve + 6]; - y2 = vertices[curve + 7]; - tmpx = (x1 - cx1 * 2 + cx2) * 0.03f; - tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; - dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f; - dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f; - ddfx = tmpx * 2 + dddfx; - ddfy = tmpy * 2 + dddfy; - dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f; - dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; - length = (float)Math.sqrt(dfx * dfx + dfy * dfy); - // BOZO! - Don't overwrite curve lengths with segment lengths. - temp[0] = length; - for (int ii = 1; ii < 8; ii++) { - dfx += ddfx; - dfy += ddfy; - ddfx += dddfx; - ddfy += dddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - temp[ii] = length; - } - dfx += ddfx; - dfy += ddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - temp[8] = length; - dfx += ddfx + dddfx; - dfy += ddfy + dddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - temp[9] = length; - - // Weight by segment length. - p *= length; - length = temp[0]; - if (p <= length) - p = 0.1f * p / length; - else { - for (int ii = 1;; ii++) { - length = temp[ii]; - if (p <= length) { - float prev = temp[ii - 1]; - p = 0.1f * (ii + (p - prev) / (length - prev)); - break; - } - } - } - - addPoint(x1, y1, cx1, cy1, cx2, cy2, x2, y2, p, tangents, out); - } - } - - private void addPoint (float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float position, - boolean tangents, FloatArray out) { - if (position == 0) position = 0.0001f; - float tt = position * position, ttt = tt * position, u = 1 - position, uu = u * u, uuu = uu * u; - float ut = u * position, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * position; - float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; - out.add(x); - out.add(y); - if (tangents) out.add(atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt))); - } - - public Color getColor () { - return color; + public void computeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset) { + super.computeWorldVertices(slot, start, count, worldVertices, offset); } public boolean getClosed () { @@ -265,10 +69,15 @@ public class PathAttachment extends VertexAttachment { this.constantSpeed = constantSpeed; } - public void setWorldVerticesLength (int worldVerticesLength) { - super.setWorldVerticesLength(worldVerticesLength); - // BOZO! - Share working memory? - worldVertices = new float[Math.max(2, worldVerticesLength + 4)]; - temp = new float[10 + worldVerticesLength / 6]; + public float getLength () { + return length; + } + + public void setLength (float length) { + this.length = length; + } + + public Color getColor () { + return color; } }