Start of multi bone path constraints.

This commit is contained in:
NathanSweet 2016-05-28 20:30:12 +02:00
parent e50b591558
commit 05b3e66dc0
8 changed files with 268 additions and 199 deletions

View File

@ -233,6 +233,15 @@ public class Bone implements Updatable {
appliedScaleY = scaleY; appliedScaleY = scaleY;
} }
public void rotateWorld (float degrees) {
float a = this.a, b = this.b, c = this.c, d = this.d;
float cos = cosDeg(degrees), sin = sinDeg(degrees);
this.a = cos * a - sin * c;
this.b = cos * b - sin * d;
this.c = sin * a + cos * c;
this.d = sin * b + cos * d;
}
public void setToSetupPose () { public void setToSetupPose () {
BoneData data = this.data; BoneData data = this.data;
x = data.x; x = data.x;

View File

@ -56,14 +56,14 @@ public class IkConstraint implements Updatable {
} }
/** Copy constructor. */ /** Copy constructor. */
public IkConstraint (IkConstraint ikConstraint, Skeleton skeleton) { public IkConstraint (IkConstraint constraint, Skeleton skeleton) {
data = ikConstraint.data; data = constraint.data;
bones = new Array(ikConstraint.bones.size); bones = new Array(constraint.bones.size);
for (Bone bone : ikConstraint.bones) for (Bone bone : constraint.bones)
bones.add(skeleton.bones.get(bone.data.index)); bones.add(skeleton.bones.get(bone.data.index));
target = skeleton.bones.get(ikConstraint.target.data.index); target = skeleton.bones.get(constraint.target.data.index);
mix = ikConstraint.mix; mix = constraint.mix;
bendDirection = ikConstraint.bendDirection; bendDirection = constraint.bendDirection;
} }
public void apply () { public void apply () {

View File

@ -4,29 +4,38 @@ package com.esotericsoftware.spine;
import static com.badlogic.gdx.math.MathUtils.*; import static com.badlogic.gdx.math.MathUtils.*;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.FloatArray;
import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.PathAttachment; import com.esotericsoftware.spine.attachments.PathAttachment;
public class PathConstraint implements Updatable { public class PathConstraint implements Updatable {
final PathConstraintData data; final PathConstraintData data;
Bone bone; final Array<Bone> bones;
Slot target; Slot target;
float position, rotateMix, translateMix; float position, rotateMix, translateMix;
final Vector2 worldPosition = new Vector2(), tangent = new Vector2(); final FloatArray lengths = new FloatArray(), positions = new FloatArray();
final Vector2 temp = new Vector2();
public PathConstraint (PathConstraintData data, Skeleton skeleton) { public PathConstraint (PathConstraintData data, Skeleton skeleton) {
this.data = data; this.data = data;
position = data.position; position = data.position;
rotateMix = data.rotateMix; rotateMix = data.rotateMix;
translateMix = data.translateMix; translateMix = data.translateMix;
bone = skeleton.findBone(data.bone.name);
bones = new Array(data.bones.size);
for (BoneData boneData : data.bones)
bones.add(skeleton.findBone(boneData.name));
target = skeleton.findSlot(data.target.name); target = skeleton.findSlot(data.target.name);
} }
/** Copy constructor. */ /** Copy constructor. */
public PathConstraint (PathConstraint constraint, Skeleton skeleton) { public PathConstraint (PathConstraint constraint, Skeleton skeleton) {
data = constraint.data; data = constraint.data;
bone = skeleton.bones.get(constraint.bone.data.index); bones = new Array(constraint.bones.size);
for (Bone bone : constraint.bones)
bones.add(skeleton.bones.get(bone.data.index));
target = skeleton.slots.get(constraint.target.data.index); target = skeleton.slots.get(constraint.target.data.index);
position = constraint.position; position = constraint.position;
rotateMix = constraint.rotateMix; rotateMix = constraint.rotateMix;
@ -40,31 +49,82 @@ public class PathConstraint implements Updatable {
public void update () { public void update () {
Attachment attachment = target.getAttachment(); Attachment attachment = target.getAttachment();
if (!(attachment instanceof PathAttachment)) return; if (!(attachment instanceof PathAttachment)) return;
PathAttachment path = (PathAttachment)attachment;
Vector2 worldPosition = this.worldPosition;
Bone bone = this.bone;
float translateMix = this.translateMix, rotateMix = this.rotateMix; float translateMix = this.translateMix, rotateMix = this.rotateMix;
if (translateMix > 0) { boolean translate = translateMix > 0, rotate = rotateMix > 0;
path.computeWorldPosition(target, position, worldPosition, rotateMix > 0 ? tangent : null); if (!translate && !rotate) return;
bone.worldX += (worldPosition.x - bone.worldX) * translateMix;
bone.worldY += (worldPosition.y - bone.worldY) * translateMix; PathAttachment path = (PathAttachment)attachment;
FloatArray lengths = this.lengths, positions = this.positions;
lengths.clear();
lengths.add(0);
positions.clear();
Array<Bone> bones = this.bones;
int boneCount = bones.size;
if (boneCount == 1) {
path.computeWorldPositions(target, position, lengths, positions, rotate);
Bone bone = bones.first();
bone.worldX += (positions.first() - bone.worldX) * translateMix;
bone.worldY += (positions.get(1) - bone.worldY) * translateMix;
if (rotate) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float r = positions.get(2) - atan2(c, a) + data.offsetRotation * degRad;
if (r > PI)
r -= PI2;
else if (r < -PI) r += PI2;
r *= rotateMix;
float cos = cos(r), sin = sin(r);
bone.a = cos * a - sin * c;
bone.b = cos * b - sin * d;
bone.c = sin * a + cos * c;
bone.d = sin * b + cos * d;
}
return;
} }
if (rotateMix > 0) { for (int i = 0; i < boneCount; i++)
if (translateMix == 0) path.computeWorldPosition(target, position, worldPosition, tangent); lengths.add(bones.get(i).data.length);
float a = bone.a, b = bone.b, c = bone.c, d = bone.d; path.computeWorldPositions(target, position, lengths, positions, false);
float r = atan2(worldPosition.y - tangent.y, worldPosition.x - tangent.x) - atan2(c, a) + data.offsetRotation * degRad;
if (r > PI) Vector2 temp = this.temp;
r -= PI2; float boneX = positions.first(), boneY = positions.get(1);
else if (r < -PI) r += PI2; for (int i = 0, p = 2; i < boneCount; i++, p += 2) {
r *= rotateMix; Bone bone = bones.get(i);
float cos = cos(r), sin = sin(r); bone.worldX += (boneX - bone.worldX) * translateMix;
bone.a = cos * a - sin * c; bone.worldY += (boneY - bone.worldY) * translateMix;
bone.b = cos * b - sin * d;
bone.c = sin * a + cos * c; float x = positions.get(p), y = positions.get(p + 1);
bone.d = sin * b + cos * d; 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;
if (r > PI)
r -= PI2;
else if (r < -PI) r += PI2;
r *= rotateMix;
float cos = cos(r), sin = sin(r);
bone.a = cos * a - sin * c;
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;
}
} }
} }
@ -92,12 +152,8 @@ public class PathConstraint implements Updatable {
this.translateMix = translateMix; this.translateMix = translateMix;
} }
public Bone getBone () { public Array<Bone> getBones () {
return bone; return bones;
}
public void setBone (Bone bone) {
this.bone = bone;
} }
public Slot getTarget () { public Slot getTarget () {

View File

@ -1,9 +1,11 @@
package com.esotericsoftware.spine; package com.esotericsoftware.spine;
import com.badlogic.gdx.utils.Array;
public class PathConstraintData { public class PathConstraintData {
final String name; final String name;
BoneData bone; final Array<BoneData> bones = new Array();
SlotData target; SlotData target;
float position, rotateMix, translateMix; float position, rotateMix, translateMix;
float offsetRotation; float offsetRotation;
@ -12,12 +14,8 @@ public class PathConstraintData {
this.name = name; this.name = name;
} }
public BoneData getBone () { public Array<BoneData> getBones () {
return bone; return bones;
}
public void setBone (BoneData bone) {
this.bone = bone;
} }
public SlotData getTarget () { public SlotData getTarget () {

View File

@ -162,7 +162,8 @@ public class Skeleton {
for (int i = 0, n = pathConstraints.size; i < n; i++) { for (int i = 0, n = pathConstraints.size; i < n; i++) {
PathConstraint pathConstraint = pathConstraints.get(i); PathConstraint pathConstraint = pathConstraints.get(i);
Bone bone = pathConstraint.bone; // BOZO! - Fix update order for multiple bones.
Bone bone = pathConstraint.bones.peek();
for (int ii = updateCache.size - 1; ii >= 0; ii--) { for (int ii = updateCache.size - 1; ii >= 0; ii--) {
if (updateCache.get(ii) == bone) { if (updateCache.get(ii) == bone) {
updateCache.insert(ii + 1, pathConstraint); updateCache.insert(ii + 1, pathConstraint);

View File

@ -210,7 +210,8 @@ public class SkeletonBinary {
// Path constraints. // Path constraints.
for (int i = 0, n = input.readInt(true); i < n; i++) { for (int i = 0, n = input.readInt(true); i < n; i++) {
PathConstraintData data = new PathConstraintData(input.readString()); PathConstraintData data = new PathConstraintData(input.readString());
data.bone = skeletonData.bones.get(input.readInt(true)); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
data.bones.add(skeletonData.bones.get(input.readInt(true)));
data.target = skeletonData.slots.get(input.readInt(true)); data.target = skeletonData.slots.get(input.readInt(true));
data.offsetRotation = input.readFloat(); data.offsetRotation = input.readFloat();
data.position = input.readFloat(); data.position = input.readFloat();

View File

@ -184,9 +184,12 @@ public class SkeletonJson {
for (JsonValue constraintMap = root.getChild("path"); constraintMap != null; constraintMap = constraintMap.next) { for (JsonValue constraintMap = root.getChild("path"); constraintMap != null; constraintMap = constraintMap.next) {
PathConstraintData data = new PathConstraintData(constraintMap.getString("name")); PathConstraintData data = new PathConstraintData(constraintMap.getString("name"));
String boneName = constraintMap.getString("bone"); for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
data.bone = skeletonData.findBone(boneName); String boneName = boneMap.asString();
if (data.bone == null) throw new SerializationException("Bone not found: " + boneName); BoneData bone = skeletonData.findBone(boneName);
if (bone == null) throw new SerializationException("Path bone not found: " + boneName);
data.bones.add(bone);
}
String targetName = constraintMap.getString("target"); String targetName = constraintMap.getString("target");
data.target = skeletonData.findSlot(targetName); data.target = skeletonData.findSlot(targetName);

View File

@ -31,16 +31,17 @@
package com.esotericsoftware.spine.attachments; package com.esotericsoftware.spine.attachments;
import static com.badlogic.gdx.math.MathUtils.*;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.math.Vector2;
import com.esotericsoftware.spine.Slot; import com.esotericsoftware.spine.Slot;
public class PathAttachment extends VertexAttachment { public class PathAttachment extends VertexAttachment {
// Nonessential. // Nonessential.
final Color color = new Color(1, 0.5f, 0, 1); final Color color = new Color(1, 0.5f, 0, 1);
float[] worldVertices, lengths; float[] worldVertices, temp;
boolean closed, constantSpeed; boolean closed, constantSpeed;
public PathAttachment (String name) { public PathAttachment (String name) {
@ -51,144 +52,140 @@ public class PathAttachment extends VertexAttachment {
super.computeWorldVertices(slot, worldVertices); super.computeWorldVertices(slot, worldVertices);
} }
public void computeWorldPosition (Slot slot, float position, Vector2 worldPosition, Vector2 tangent) { public void computeWorldPositions (Slot slot, float position, FloatArray lengths, FloatArray out, boolean tangents) {
float x1, y1, cx1, cy1, cx2, cy2, x2, y2; float[] vertices = this.worldVertices;
int verticesLength = worldVerticesLength;
int curves = verticesLength / 6;
if (!constantSpeed) { if (!constantSpeed) {
int curves = worldVerticesLength / 6; for (int i = 0, n = lengths.size; i < n; i++) {
if (closed) { // BOZO - Wrong. Use path length property to give !constantSpeed paths support for oob and multiple bones?
position = position % 1; position += lengths.get(i);
if (position < 0) position += 1; if (closed) {
} else { position %= 1;
position = MathUtils.clamp(position, 0, 1); if (position < 0) position += 1;
curves--;
}
int curve = position < 1 ? (int)(curves * position) : curves - 1;
position = (position - curve / (float)curves) * curves;
float[] worldVertices = this.worldVertices;
if (closed && curve == curves - 1) {
super.computeWorldVertices(slot, curves * 6 - 4, 4, worldVertices, 0);
super.computeWorldVertices(slot, 0, 4, worldVertices, 4);
} else
super.computeWorldVertices(slot, curve * 6 + 2, 8, worldVertices, 0);
x1 = worldVertices[0];
y1 = worldVertices[1];
cx1 = worldVertices[2];
cy1 = worldVertices[3];
cx2 = worldVertices[4];
cy2 = worldVertices[5];
x2 = worldVertices[6];
y2 = worldVertices[7];
} else {
float[] worldVertices = this.worldVertices;
int verticesLength;
if (closed) {
verticesLength = worldVerticesLength;
super.computeWorldVertices(slot, 2, verticesLength - 2, worldVertices, 0);
super.computeWorldVertices(slot, 0, 2, worldVertices, verticesLength - 2);
worldVertices[verticesLength] = worldVertices[0];
worldVertices[verticesLength + 1] = worldVertices[1];
verticesLength += 2;
} else {
verticesLength = worldVerticesLength - 4;
super.computeWorldVertices(slot, 2, verticesLength, worldVertices, 0);
}
// Curve lengths.
float[] lengths = this.lengths;
float length = 0;
x1 = worldVertices[0];
y1 = worldVertices[1];
float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
for (int i = 0, w = 2; w < verticesLength; i++, w += 6) {
cx1 = worldVertices[w];
cy1 = worldVertices[w + 1];
cx2 = worldVertices[w + 2];
cy2 = worldVertices[w + 3];
x2 = worldVertices[w + 4];
y2 = worldVertices[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;
length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx;
dfy += ddfy;
length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx + dddfx;
dfy += ddfy + dddfy;
length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
lengths[i] = length;
x1 = x2;
y1 = y2;
}
position *= length;
if (closed) {
position = position % length;
if (position < 0) position += length;
} else if (position < 0 || position > length) {
// Outside curve.
if (position < 0) {
x1 = worldVertices[0];
y1 = worldVertices[1];
cx1 = worldVertices[2] - x1;
cy1 = worldVertices[3] - y1;
} else { } else {
x1 = worldVertices[verticesLength - 2]; position = clamp(position, 0, 1);
y1 = worldVertices[verticesLength - 1]; curves--;
cx1 = x1 - worldVertices[verticesLength - 4];
cy1 = y1 - worldVertices[verticesLength - 3];
position -= length;
} }
float r = MathUtils.atan2(cy1, cx1); int curve = position < 1 ? (int)(curves * position) : curves - 1;
float cos = MathUtils.cos(r), sin = MathUtils.sin(r); position = (position - curve / (float)curves) * curves;
worldPosition.x = x1 + position * cos;
worldPosition.y = y1 + position * sin; if (closed && curve == curves - 1) {
if (tangent != null) { super.computeWorldVertices(slot, curves * 6 - 4, 4, vertices, 0);
tangent.x = worldPosition.x - cos; super.computeWorldVertices(slot, 0, 4, vertices, 4);
tangent.y = worldPosition.y - sin; } 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;
} }
return; 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. // Determine curve containing position.
int curve; int curve;
length = lengths[0]; float length = temp[10];
if (position <= length) { if (p <= length) {
curve = 0; curve = 0;
position /= length; p /= length;
} else { } else {
for (curve = 1;; curve++) { for (curve = 11;; curve++) {
length = lengths[curve]; length = temp[curve];
if (position <= length) { if (p <= length) {
float prev = lengths[curve - 1]; float prev = temp[curve - 1];
position = (position - prev) / (length - prev); p = (p - prev) / (length - prev);
break; break;
} }
} }
curve *= 6; curve = (curve - 10) * 6;
} }
// Curve segment lengths. // Curve segment lengths.
x1 = worldVertices[curve]; x1 = vertices[curve];
y1 = worldVertices[curve + 1]; y1 = vertices[curve + 1];
cx1 = worldVertices[curve + 2]; cx1 = vertices[curve + 2];
cy1 = worldVertices[curve + 3]; cy1 = vertices[curve + 3];
cx2 = worldVertices[curve + 4]; cx2 = vertices[curve + 4];
cy2 = worldVertices[curve + 5]; cy2 = vertices[curve + 5];
x2 = worldVertices[curve + 6]; x2 = vertices[curve + 6];
y2 = worldVertices[curve + 7]; y2 = vertices[curve + 7];
tmpx = (x1 - cx1 * 2 + cx2) * 0.03f; tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f; dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
@ -198,51 +195,54 @@ public class PathAttachment extends VertexAttachment {
dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f; dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
length = (float)Math.sqrt(dfx * dfx + dfy * dfy); length = (float)Math.sqrt(dfx * dfx + dfy * dfy);
lengths[0] = length; // BOZO! - Don't overwrite curve lengths with segment lengths.
for (int i = 1; i < 8; i++) { temp[0] = length;
for (int ii = 1; ii < 8; ii++) {
dfx += ddfx; dfx += ddfx;
dfy += ddfy; dfy += ddfy;
ddfx += dddfx; ddfx += dddfx;
ddfy += dddfy; ddfy += dddfy;
length += (float)Math.sqrt(dfx * dfx + dfy * dfy); length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
lengths[i] = length; temp[ii] = length;
} }
dfx += ddfx; dfx += ddfx;
dfy += ddfy; dfy += ddfy;
length += (float)Math.sqrt(dfx * dfx + dfy * dfy); length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
lengths[8] = length; temp[8] = length;
dfx += ddfx + dddfx; dfx += ddfx + dddfx;
dfy += ddfy + dddfy; dfy += ddfy + dddfy;
length += (float)Math.sqrt(dfx * dfx + dfy * dfy); length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
lengths[9] = length; temp[9] = length;
// Weight by segment length. // Weight by segment length.
position *= length; p *= length;
length = lengths[0]; length = temp[0];
if (position <= length) if (p <= length)
position = 0.1f * position / length; p = 0.1f * p / length;
else { else {
for (int i = 1;; i++) { for (int ii = 1;; ii++) {
length = lengths[i]; length = temp[ii];
if (position <= length) { if (p <= length) {
float prev = lengths[i - 1]; float prev = temp[ii - 1];
position = 0.1f * (i + (position - prev) / (length - prev)); p = 0.1f * (ii + (p - prev) / (length - prev));
break; break;
} }
} }
} }
}
// Calculate point and tangent. addPoint(x1, y1, cx1, cy1, cx2, cy2, x2, y2, p, tangents, out);
position += 0.0001f; }
}
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 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 ut = u * position, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * position;
worldPosition.x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt; float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
worldPosition.y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; out.add(x);
if (tangent != null) { out.add(y);
tangent.x = x1 * uu + cx1 * ut * 2 + cx2 * tt; if (tangents) out.add(atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)));
tangent.y = y1 * uu + cy1 * ut * 2 + cy2 * tt;
}
} }
public Color getColor () { public Color getColor () {
@ -267,7 +267,8 @@ public class PathAttachment extends VertexAttachment {
public void setWorldVerticesLength (int worldVerticesLength) { public void setWorldVerticesLength (int worldVerticesLength) {
super.setWorldVerticesLength(worldVerticesLength); super.setWorldVerticesLength(worldVerticesLength);
// BOZO! - Share working memory?
worldVertices = new float[Math.max(2, worldVerticesLength + 4)]; worldVertices = new float[Math.max(2, worldVerticesLength + 4)];
lengths = new float[Math.max(10, worldVerticesLength / 6)]; temp = new float[10 + worldVerticesLength / 6];
} }
} }