Streamline KBox2D api by removing unnecessary interfaces
This commit is contained in:
parent
721d2a2029
commit
27a870fcf0
@ -148,353 +148,3 @@ enum class BodyFlags(val bitmask: Int) {
|
||||
return other and bitmask.inv()
|
||||
}
|
||||
}
|
||||
|
||||
interface IBody {
|
||||
/** Creates a fixture and attach it to this body. Use this function if you need
|
||||
* to set some fixture parameters, like friction. Otherwise you can create the
|
||||
* fixture directly from a shape.
|
||||
* If the density is non-zero, this function automatically updates the mass of the body.
|
||||
* Contacts are not created until the next time step.
|
||||
* @param def the fixture definition.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun createFixture(def: FixtureDef): IFixture
|
||||
|
||||
/**
|
||||
* Creates a fixture from a shape and attach it to this body.
|
||||
* This is a convenience function. Use b2FixtureDef if you need to set parameters
|
||||
* like friction, restitution, user data, or filtering.
|
||||
* If the density is non-zero, this function automatically updates the mass of the body.
|
||||
* @param shape the shape to be cloned.
|
||||
* @param density the shape density (set to zero for static bodies).
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun createFixture(shape: IShape<*>, density: Double): IFixture {
|
||||
return createFixture(
|
||||
FixtureDef(
|
||||
shape = shape,
|
||||
density = density
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a fixture. This removes the fixture from the broad-phase and
|
||||
* destroys all contacts associated with this fixture. This will
|
||||
* automatically adjust the mass of the body if the body is dynamic and the
|
||||
* fixture has positive density.
|
||||
* All fixtures attached to a body are implicitly destroyed when the body is destroyed.
|
||||
* @param fixture the fixture to be removed.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun destroyFixture(fixture: IFixture)
|
||||
|
||||
/**
|
||||
* Set the position of the body's origin and rotation.
|
||||
* Manipulating a body's transform may cause non-physical behavior.
|
||||
* Note: contacts are updated on the next call to b2World::Step.
|
||||
* @param position the world position of the body's local origin.
|
||||
* @param angle the world rotation in radians.
|
||||
*/
|
||||
fun setTransform(position: Vector2d, angle: Double)
|
||||
|
||||
/**
|
||||
* Get the body transform for the body's origin.
|
||||
* @return the world transform of the body's origin.
|
||||
*/
|
||||
val transform: Transform
|
||||
|
||||
/**
|
||||
* Get the world body origin position.
|
||||
* @return the world position of the body's origin.
|
||||
*/
|
||||
val position: Vector2d
|
||||
|
||||
/**
|
||||
* Get the angle in radians.
|
||||
* @return the current world rotation angle in radians.
|
||||
*/
|
||||
val angle: Double
|
||||
|
||||
/**
|
||||
* Get the world position of the center of mass.
|
||||
*/
|
||||
val worldCenter: Vector2d
|
||||
|
||||
/**
|
||||
* Get the local position of the center of mass.
|
||||
*/
|
||||
val localCenter: Vector2d
|
||||
|
||||
/**
|
||||
* The linear velocity of the body's origin in world co-ordinates.
|
||||
*/
|
||||
var linearVelocity: Vector2d
|
||||
|
||||
/**
|
||||
* The angular velocity of the body.
|
||||
*/
|
||||
var angularVelocity: Double
|
||||
|
||||
/**
|
||||
* Apply a force at a world point. If the force is not
|
||||
* applied at the center of mass, it will generate a torque and
|
||||
* affect the angular velocity. This wakes up the body.
|
||||
* @param force the world force vector, usually in Newtons (N).
|
||||
* @param point the world position of the point of application.
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyForce(force: Vector2d, point: Vector2d, wake: Boolean = true)
|
||||
|
||||
/**
|
||||
* Apply a force to the center of mass. This wakes up the body.
|
||||
* @param force the world force vector, usually in Newtons (N).
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyForceToCenter(force: Vector2d, wake: Boolean = true)
|
||||
|
||||
/**
|
||||
* Apply a torque. This affects the angular velocity
|
||||
* without affecting the linear velocity of the center of mass.
|
||||
* @param torque about the z-axis (out of the screen), usually in N-m.
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyTorque(torque: Double, wake: Boolean = true)
|
||||
|
||||
/**
|
||||
* Apply an impulse at a point. This immediately modifies the velocity.
|
||||
* It also modifies the angular velocity if the point of application
|
||||
* is not at the center of mass. This wakes up the body.
|
||||
* @param impulse the world impulse vector, usually in N-seconds or kg-m/s.
|
||||
* @param point the world position of the point of application.
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyLinearImpulse(impulse: Vector2d, point: Vector2d, wake: Boolean = true)
|
||||
|
||||
/**
|
||||
* Apply an impulse to the center of mass. This immediately modifies the velocity.
|
||||
* @param impulse the world impulse vector, usually in N-seconds or kg-m/s.
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyLinearImpulseToCenter(impulse: Vector2d, wake: Boolean = true)
|
||||
|
||||
/**
|
||||
* Apply an angular impulse.
|
||||
* @param impulse the angular impulse in units of kg*m*m/s
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyAngularImpulse(impulse: Double, wake: Boolean = true)
|
||||
|
||||
/**
|
||||
* Get the total mass of the body.
|
||||
* @return the mass, usually in kilograms (kg).
|
||||
*/
|
||||
val mass: Double
|
||||
|
||||
/**
|
||||
* Get the rotational inertia of the body about the local origin.
|
||||
* @return the rotational inertia, usually in kg-m^2.
|
||||
*/
|
||||
val inertia: Double
|
||||
|
||||
/**
|
||||
* Get the mass data of the body.
|
||||
* @return a struct containing the mass, inertia and center of the body.
|
||||
* Set the mass properties to override the mass properties of the fixtures.
|
||||
* Note that this changes the center of mass position.
|
||||
* Note that creating or destroying fixtures can also alter the mass.
|
||||
* This function has no effect if the body isn't dynamic.
|
||||
* @param data the mass properties.
|
||||
*/
|
||||
var massData: MassData
|
||||
|
||||
/**
|
||||
* This resets the mass properties to the sum of the mass properties of the fixtures.
|
||||
* This normally does not need to be called unless you called SetMassData to override
|
||||
* the mass and you later want to reset the mass.
|
||||
*/
|
||||
fun resetMassData()
|
||||
|
||||
/**
|
||||
* Get the world coordinates of a point given the local coordinates.
|
||||
* @param localPoint a point on the body measured relative the the body's origin.
|
||||
* @return the same point expressed in world coordinates.
|
||||
*/
|
||||
fun getWorldPoint(localPoint: Vector2d): Vector2d
|
||||
|
||||
/**
|
||||
* Get the world coordinates of a vector given the local coordinates.
|
||||
* @param localVector a vector fixed in the body.
|
||||
* @return the same vector expressed in world coordinates.
|
||||
*/
|
||||
fun getWorldVector(localPoint: Vector2d): Vector2d
|
||||
|
||||
/**
|
||||
* Gets a local point relative to the body's origin given a world point.
|
||||
* @param worldPoint a point in world coordinates.
|
||||
* @return the corresponding local point relative to the body's origin.
|
||||
*/
|
||||
fun getLocalPoint(worldPoint: Vector2d): Vector2d
|
||||
|
||||
/**
|
||||
* Gets a local vector given a world vector.
|
||||
* @param worldVector a vector in world coordinates.
|
||||
* @return the corresponding local vector.
|
||||
*/
|
||||
fun getLocalVector(worldVector: Vector2d): Vector2d
|
||||
|
||||
/**
|
||||
* Get the world linear velocity of a world point attached to this body.
|
||||
* @param worldPoint a point in world coordinates.
|
||||
* @return the world velocity of a point.
|
||||
*/
|
||||
fun getLinearVelocityFromWorldPoint(worldPoint: Vector2d): Vector2d
|
||||
|
||||
/**
|
||||
* Get the world velocity of a local point.
|
||||
* @param localPoint a point in local coordinates.
|
||||
* @return the world velocity of a point.
|
||||
*/
|
||||
fun getLinearVelocityFromLocalPoint(localPoint: Vector2d): Vector2d
|
||||
|
||||
var linearDamping: Double
|
||||
var angularDamping: Double
|
||||
var gravityScale: Double
|
||||
|
||||
/**
|
||||
* Set the type of this body. This may alter the mass and velocity.
|
||||
*/
|
||||
var type: BodyType
|
||||
|
||||
/**
|
||||
* Should this body be treated like a bullet for continuous collision detection?
|
||||
*/
|
||||
var isBullet: Boolean
|
||||
|
||||
/**
|
||||
* You can disable sleeping on this body. If you disable sleeping, the
|
||||
* body will be woken.
|
||||
*/
|
||||
var allowAutoSleep: Boolean
|
||||
|
||||
/**
|
||||
* Set the sleep state of the body. A sleeping body has very
|
||||
* low CPU cost.
|
||||
* @param flag set to true to wake the body, false to put it to sleep.
|
||||
*/
|
||||
var isAwake: Boolean
|
||||
|
||||
/**
|
||||
* Allow a body to be disabled. A disabled body is not simulated and cannot
|
||||
* be collided with or woken up.
|
||||
*
|
||||
* If you pass a flag of true, all fixtures will be added to the broad-phase.
|
||||
*
|
||||
* If you pass a flag of false, all fixtures will be removed from the
|
||||
* broad-phase and all contacts will be destroyed.
|
||||
*
|
||||
* Fixtures and joints are otherwise unaffected. You may continue
|
||||
* to create/destroy fixtures and joints on disabled bodies.
|
||||
*
|
||||
* Fixtures on a disabled body are implicitly disabled and will
|
||||
* not participate in collisions, ray-casts, or queries.
|
||||
*
|
||||
* Joints connected to a disabled body are implicitly disabled.
|
||||
*
|
||||
* An diabled body is still owned by a b2World object and remains
|
||||
* in the body list.
|
||||
*/
|
||||
var isEnabled: Boolean
|
||||
|
||||
/**
|
||||
* Set this body to have fixed rotation. This causes the mass to be reset.
|
||||
*/
|
||||
var isFixedRotation: Boolean
|
||||
|
||||
/**
|
||||
* Get the list of all fixtures attached to this body.
|
||||
*
|
||||
* Kotlin: This is not a list, but, as in C++ impl, a custom
|
||||
* linked list.
|
||||
*/
|
||||
val fixtureList: IFixture?
|
||||
|
||||
val fixtureIterator: Iterator<IFixture> get() {
|
||||
return object : Iterator<IFixture> {
|
||||
private var node = fixtureList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): IFixture {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all joints attached to this body.
|
||||
*
|
||||
* Kotlin: This is not a list, but, as in C++ impl, a custom
|
||||
* linked list.
|
||||
*/
|
||||
val jointList: JointEdge?
|
||||
|
||||
val jointIterator: Iterator<JointEdge> get() {
|
||||
return object : Iterator<JointEdge> {
|
||||
private var node = jointList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): JointEdge {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all contacts attached to this body.
|
||||
* @warning this list changes during the time step and you may
|
||||
* miss some collisions if you don't use b2ContactListener.
|
||||
*
|
||||
* Kotlin: This is not a list, but, as in C++ impl, a custom
|
||||
* linked list.
|
||||
*/
|
||||
val contactEdge: ContactEdge?
|
||||
|
||||
val contactEdgeIterator: Iterator<ContactEdge> get() {
|
||||
return object : Iterator<ContactEdge> {
|
||||
private var node = contactEdge
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): ContactEdge {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val next: IBody?
|
||||
val prev: IBody?
|
||||
|
||||
val userData: Any?
|
||||
|
||||
/**
|
||||
* Get the parent world of this body.
|
||||
*/
|
||||
val world: IB2World
|
||||
|
||||
/// Dump this body to a file
|
||||
fun dump()
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
typealias b2Pair = Pair<Int, Int>
|
||||
|
||||
const val e_nullProxy = -1
|
||||
|
||||
/**
|
||||
* The broad-phase is used for computing pairs and performing volume queries and ray casts.
|
||||
* This broad-phase does not persist pairs. Instead, this reports potentially new pairs.
|
||||
* It is up to the client to consume the new pairs and to track subsequent overlap.
|
||||
*/
|
||||
interface IBroadPhase : IProxieable, IMovable {
|
||||
/**
|
||||
* Call to trigger a re-processing of it's pairs on the next call to UpdatePairs.
|
||||
*/
|
||||
fun touchProxy(proxyID: Int)
|
||||
|
||||
/**
|
||||
* Test overlap of fat AABBs.
|
||||
*/
|
||||
fun testOverlap(proxyIDA: Int, proxyIDB: Int): Boolean
|
||||
|
||||
/**
|
||||
* Get the number of proxies.
|
||||
*/
|
||||
val proxyCount: Int
|
||||
|
||||
/**
|
||||
* Update the pairs. This results in pair callbacks. This can only add pairs.
|
||||
*/
|
||||
fun updatePairs(callback: (Any?, Any?) -> Unit)
|
||||
|
||||
/**
|
||||
* Get the height of the embedded tree.
|
||||
*/
|
||||
val treeHeight: Int
|
||||
|
||||
/**
|
||||
* Get the balance of the embedded tree.
|
||||
*/
|
||||
val treeBalance: Int
|
||||
|
||||
/**
|
||||
* Get the quality metric of the embedded tree.
|
||||
*/
|
||||
val treeQuality: Double
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kbox2d.dynamics.Body
|
||||
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
|
||||
import kotlin.math.sqrt
|
||||
|
||||
/// Friction mixing law. The idea is to allow either fixture to drive the friction to zero.
|
||||
@ -31,8 +33,8 @@ data class ContactRegister(
|
||||
* nodes, one for each attached body.
|
||||
*/
|
||||
data class ContactEdge(
|
||||
val other: ru.dbotthepony.kbox2d.api.IBody, ///< provides quick access to the other body attached.
|
||||
val contact: IContact, ///< the contact
|
||||
val other: Body, ///< provides quick access to the other body attached.
|
||||
val contact: AbstractContact, ///< the contact
|
||||
var prev: ContactEdge? = null, ///< the previous contact edge in the body's contact list
|
||||
var next: ContactEdge? = null, ///< the next contact edge in the body's contact list
|
||||
)
|
||||
@ -68,79 +70,3 @@ enum class ContactFlags(val bitmask: Int) {
|
||||
return other and bitmask.inv()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The class manages contact between two shapes. A contact exists for each overlapping
|
||||
* AABB in the broad-phase (except if filtered). Therefore a contact object may exist
|
||||
* that has no contact points.
|
||||
*/
|
||||
interface IContact {
|
||||
/// Get the contact manifold. Do not modify the manifold unless you understand the
|
||||
/// internals of Box2D.
|
||||
val manifold: Manifold
|
||||
|
||||
/// Get the world manifold.
|
||||
val worldManifold: IWorldManifold
|
||||
|
||||
/// Is this contact touching?
|
||||
val isTouching: Boolean
|
||||
|
||||
/// Enable/disable this contact. This can be used inside the pre-solve
|
||||
/// contact listener. The contact is only disabled for the current
|
||||
/// time step (or sub-step in continuous collisions).
|
||||
var isEnabled: Boolean
|
||||
|
||||
/// Get the next contact in the world's contact list.
|
||||
val next: IContact?
|
||||
val prev: IContact?
|
||||
|
||||
/// Get fixture A in this contact.
|
||||
val fixtureA: IFixture
|
||||
|
||||
/// Get the child primitive index for fixture A.
|
||||
val childIndexA: Int
|
||||
|
||||
/// Get fixture B in this contact.
|
||||
val fixtureB: IFixture
|
||||
|
||||
/// Get the child primitive index for fixture B.
|
||||
val childIndexB: Int
|
||||
|
||||
/// Override the default friction mixture. You can call this in b2ContactListener::PreSolve.
|
||||
/// This value persists until set or reset.
|
||||
/// Get the friction.
|
||||
var friction: Double
|
||||
|
||||
/// Reset the friction mixture to the default value.
|
||||
fun resetFriction() {
|
||||
friction = b2MixFriction(fixtureA.friction, fixtureB.friction)
|
||||
}
|
||||
|
||||
fun flagForFiltering()
|
||||
|
||||
/// Override the default restitution mixture. You can call this in b2ContactListener::PreSolve.
|
||||
/// The value persists until you set or reset.
|
||||
var restitution: Double
|
||||
|
||||
/// Reset the restitution to the default value.
|
||||
fun resetRestitution() {
|
||||
restitution = b2MixRestitution(fixtureA.restitution, fixtureB.restitution)
|
||||
}
|
||||
|
||||
/// Override the default restitution velocity threshold mixture. You can call this in b2ContactListener::PreSolve.
|
||||
/// The value persists until you set or reset.
|
||||
/// Get the restitution threshold.
|
||||
var restitutionThreshold: Double
|
||||
|
||||
/// Reset the restitution threshold to the default value.
|
||||
fun resetRestitutionThreshold() {
|
||||
restitutionThreshold = b2MixRestitutionThreshold(fixtureA.restitutionThreshold, fixtureB.restitutionThreshold)
|
||||
}
|
||||
|
||||
/// Set the desired tangent speed for a conveyor belt behavior. In meters per second.
|
||||
/// Get the desired tangent speed. In meters per second.
|
||||
var tangentSpeed: Double
|
||||
|
||||
/// Evaluate this contact with your own manifold and transforms.
|
||||
fun evaluate(xfA: Transform, xfB: Transform): Manifold
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
/**
|
||||
* Delegate of b2World.
|
||||
*/
|
||||
interface IContactManager {
|
||||
fun addPair(proxyUserDataA: Any?, proxyUserDataB: Any?)
|
||||
|
||||
fun findNewContacts()
|
||||
|
||||
fun destroy(contact: IContact)
|
||||
|
||||
fun collide()
|
||||
|
||||
val broadPhase: IBroadPhase
|
||||
var contactList: IContact?
|
||||
|
||||
val contactListIterator: Iterator<IContact> get() {
|
||||
return object : Iterator<IContact> {
|
||||
private var node = contactList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): IContact {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val contactCount: Int
|
||||
var contactFilter: IContactFilter?
|
||||
var contactListener: IContactListener?
|
||||
}
|
@ -1,26 +1,8 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kbox2d.collision.DistanceProxy
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
/**
|
||||
* A distance proxy is used by the GJK algorithm.
|
||||
* It encapsulates any shape.
|
||||
*/
|
||||
interface IDistanceProxy {
|
||||
val vertices: List<Vector2d>
|
||||
val radius: Double
|
||||
|
||||
/**
|
||||
* Get the supporting vertex index in the given direction.
|
||||
*/
|
||||
fun getSupport(d: Vector2d): Int
|
||||
|
||||
/**
|
||||
* Get the supporting vertex in the given direction.
|
||||
*/
|
||||
fun getSupportVertex(d: Vector2d): Vector2d
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to warm start b2Distance.
|
||||
* Set count to zero on first call.
|
||||
@ -38,8 +20,8 @@ data class SimplexCache(
|
||||
* in the computation. Even
|
||||
*/
|
||||
data class DistanceInput(
|
||||
var proxyA: IDistanceProxy,
|
||||
var proxyB: IDistanceProxy,
|
||||
var proxyA: DistanceProxy,
|
||||
var proxyB: DistanceProxy,
|
||||
var transformA: Transform = Transform(),
|
||||
var transformB: Transform = Transform(),
|
||||
var useRadii: Boolean = false
|
||||
@ -57,8 +39,8 @@ data class DistanceOutput(
|
||||
)
|
||||
|
||||
data class ShapeCastInput(
|
||||
var proxyA: IDistanceProxy,
|
||||
var proxyB: IDistanceProxy,
|
||||
var proxyA: DistanceProxy,
|
||||
var proxyB: DistanceProxy,
|
||||
var transformA: Transform,
|
||||
var transformB: Transform,
|
||||
var translationB: Vector2d,
|
||||
|
@ -1,58 +0,0 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kbox2d.collision.DynamicTree
|
||||
import ru.dbotthepony.kbox2d.collision.b2_nullNode
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
|
||||
private const val DEBUG_CYCLIC_REFERENCES = true
|
||||
|
||||
/// A node in the dynamic tree. The client does not interact with this directly.
|
||||
data class TreeNode(
|
||||
val tree: DynamicTree,
|
||||
/// Enlarged AABB
|
||||
var aabb: AABB? = null,
|
||||
|
||||
var child1: Int = 0,
|
||||
var child2: Int = 0,
|
||||
|
||||
// leaf = 0, free node = -1
|
||||
var height: Int = 0,
|
||||
|
||||
var moved: Boolean = false,
|
||||
|
||||
var userData: Any? = null,
|
||||
) {
|
||||
val isLeaf get() = child1 == b2_nullNode
|
||||
|
||||
// union
|
||||
private var _union: Int = b2_nullNode
|
||||
|
||||
var parent by this::_union
|
||||
var next by this::_union
|
||||
}
|
||||
|
||||
interface IDynamicTree : IProxieable, IMovable {
|
||||
fun wasMoved(proxyID: Int): Boolean
|
||||
fun clearMoved(proxyID: Int)
|
||||
|
||||
/**
|
||||
* Validate this tree. For testing.
|
||||
*/
|
||||
fun validate()
|
||||
|
||||
/**
|
||||
* Compute the height of the binary tree in O(N) time. Should not be
|
||||
* called often.
|
||||
*/
|
||||
val height: Int
|
||||
|
||||
/// Get the maximum balance of an node in the tree. The balance is the difference
|
||||
/// in height of the two children of a node.
|
||||
val maxBalance: Int
|
||||
|
||||
/// Get the ratio of the sum of the node areas to the root area.
|
||||
val getAreaRatio: Double
|
||||
|
||||
/// Build an optimal tree. Very expensive. For testing.
|
||||
fun rebuildBottomUp()
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kbox2d.collision.e_nullProxy
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
@ -40,26 +42,38 @@ data class ImmutableFilter(
|
||||
) : IFilter
|
||||
|
||||
data class FixtureDef(
|
||||
/// The shape, this must be set. The shape will be cloned.
|
||||
/**
|
||||
* The shape, this must be set. The shape will be cloned.
|
||||
*/
|
||||
var shape: IShape<*>? = null,
|
||||
|
||||
var friction: Double = 0.2,
|
||||
|
||||
/// The restitution (elasticity) usually in the range [0,1].
|
||||
/**
|
||||
* The restitution (elasticity) usually in the range [0,1].
|
||||
*/
|
||||
var restitution: Double = 0.0,
|
||||
|
||||
/// Restitution velocity threshold, usually in m/s. Collisions above this
|
||||
/// speed have restitution applied (will bounce).
|
||||
/**
|
||||
* Restitution velocity threshold, usually in m/s. Collisions above this
|
||||
* speed have restitution applied (will bounce).
|
||||
*/
|
||||
var restitutionThreshold: Double = 1.0 * b2_lengthUnitsPerMeter,
|
||||
|
||||
/// The density, usually in kg/m^2.
|
||||
/**
|
||||
* The density, usually in kg/m^2.
|
||||
*/
|
||||
var density: Double = 0.0,
|
||||
|
||||
/// A sensor shape collects contact information but never generates a collision
|
||||
/// response.
|
||||
/**
|
||||
* A sensor shape collects contact information but never generates a collision
|
||||
* response.
|
||||
*/
|
||||
var isSensor: Boolean = false,
|
||||
|
||||
/// Use this to store application specific fixture data.
|
||||
/**
|
||||
* Use this to store application specific fixture data.
|
||||
*/
|
||||
var userData: Any? = null,
|
||||
|
||||
val filter: Filter = Filter()
|
||||
@ -67,7 +81,7 @@ data class FixtureDef(
|
||||
|
||||
data class FixtureProxy(
|
||||
var aabb: AABB,
|
||||
val fixture: IFixture,
|
||||
val fixture: Fixture,
|
||||
val childIndex: Int,
|
||||
) {
|
||||
private var setProxyID = false
|
||||
@ -83,99 +97,3 @@ data class FixtureProxy(
|
||||
throw IllegalStateException("FixtureProxy should be immutable (tried to set value $value, already having $field)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fixture is used to attach a shape to a body for collision detection. A fixture
|
||||
* inherits its transform from its parent. Fixtures hold additional non-geometric data
|
||||
* such as friction, collision filters, etc.
|
||||
* Fixtures are created via b2Body::CreateFixture.
|
||||
* @warning you cannot reuse fixtures.
|
||||
*/
|
||||
interface IFixture {
|
||||
/// Get the type of the child shape. You can use this to down cast to the concrete shape.
|
||||
/// @return the shape type.
|
||||
val type: IShape.Type
|
||||
|
||||
/// Get the child shape. You can modify the child shape, however you should not change the
|
||||
/// number of vertices because this will crash some collision caching mechanisms.
|
||||
/// Manipulating the shape may lead to non-physical behavior.
|
||||
val shape: IShape<*>
|
||||
|
||||
/// Set if this fixture is a sensor.
|
||||
/// Is this fixture a sensor (non-solid)?
|
||||
/// @return the true if the shape is a sensor.
|
||||
var isSensor: Boolean
|
||||
|
||||
/// Set the contact filtering data. This will not update contacts until the next time
|
||||
/// step when either parent body is active and awake.
|
||||
/// This automatically calls Refilter.
|
||||
var filter: IFilter
|
||||
|
||||
/// Call this if you want to establish collision that was previously disabled by b2ContactFilter::ShouldCollide.
|
||||
fun refilter()
|
||||
|
||||
/// Get the parent body of this fixture. This is nullptr if the fixture is not attached.
|
||||
/// @return the parent body.
|
||||
val body: ru.dbotthepony.kbox2d.api.IBody?
|
||||
|
||||
/// Get the next fixture in the parent body's fixture list.
|
||||
/// @return the next shape.
|
||||
val next: IFixture?
|
||||
|
||||
/// Get the user data that was assigned in the fixture definition. Use this to
|
||||
/// store your application specific data.
|
||||
val userData: Any?
|
||||
|
||||
/// Test a point for containment in this fixture.
|
||||
/// @param p a point in world coordinates.
|
||||
fun testPoint(p: Vector2d): Boolean {
|
||||
return shape.testPoint(checkNotNull(body) { "Tried to use detached fixture" }.transform, p)
|
||||
}
|
||||
|
||||
/// Cast a ray against this shape.
|
||||
/// @param output the ray-cast results.
|
||||
/// @param input the ray-cast input parameters.
|
||||
/// @param childIndex the child shape index (e.g. edge index)
|
||||
fun rayCast(input: RayCastInput, childIndex: Int): RayCastOutput {
|
||||
return shape.rayCast(input, checkNotNull(body) { "Tried to use detached fixture" }.transform, childIndex)
|
||||
}
|
||||
|
||||
/// Get the mass data for this fixture. The mass data is based on the density and
|
||||
/// the shape. The rotational inertia is about the shape's origin. This operation
|
||||
/// may be expensive.
|
||||
fun getMassData(): MassData {
|
||||
return shape.computeMass(density)
|
||||
}
|
||||
|
||||
/// Set the density of this fixture. This will _not_ automatically adjust the mass
|
||||
/// of the body. You must call b2Body::ResetMassData to update the body's mass.
|
||||
/// Get the density of this fixture.
|
||||
var density: Double
|
||||
|
||||
/// Get the coefficient of friction.
|
||||
/// Set the coefficient of friction. This will _not_ change the friction of
|
||||
/// existing contacts.
|
||||
var friction: Double
|
||||
|
||||
/// Get the coefficient of restitution.
|
||||
/// Set the coefficient of restitution. This will _not_ change the restitution of
|
||||
/// existing contacts.
|
||||
var restitution: Double
|
||||
|
||||
/// Get the restitution velocity threshold.
|
||||
/// Set the restitution threshold. This will _not_ change the restitution threshold of
|
||||
/// existing contacts.
|
||||
var restitutionThreshold: Double
|
||||
|
||||
/// Get the fixture's AABB. This AABB may be enlarge and/or stale.
|
||||
/// If you need a more accurate AABB, compute it using the shape and
|
||||
/// the body transform.
|
||||
fun getAABB(childIndex: Int): AABB {
|
||||
return proxies[childIndex].aabb
|
||||
}
|
||||
|
||||
/// Dump this fixture to the log file. (Log4J)
|
||||
fun dump(childIndex: Int) { }
|
||||
|
||||
val proxies: List<FixtureProxy>
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import ru.dbotthepony.kbox2d.collision.DynamicTree
|
||||
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
||||
|
||||
fun interface ProxyQueryCallback {
|
||||
fun invoke(nodeId: Int, userData: Any?): Boolean
|
||||
@ -11,33 +13,33 @@ fun interface ProxyRayCastCallback {
|
||||
fun invoke(subInput: RayCastInput, nodeId: Int, userData: Any?): Double
|
||||
}
|
||||
|
||||
sealed interface IProxieable {
|
||||
interface IProxieable {
|
||||
/**
|
||||
* For [IBroadPhase]:
|
||||
* For [BroadPhase]:
|
||||
* Create a proxy with an initial AABB. Pairs are not reported until
|
||||
* UpdatePairs is called.
|
||||
*
|
||||
* For [IDynamicTree]:
|
||||
* For [DynamicTree]:
|
||||
* Create a proxy. Provide a tight fitting AABB and a userData pointer.
|
||||
*/
|
||||
fun createProxy(aabb: AABB, userData: Any?): Int
|
||||
|
||||
/**
|
||||
* For [IBroadPhase]:
|
||||
* For [BroadPhase]:
|
||||
* Destroy a proxy. It is up to the client to remove any pairs.
|
||||
*
|
||||
* For [IDynamicTree]:
|
||||
* For [DynamicTree]:
|
||||
* Destroy a proxy. This asserts if the id is invalid.
|
||||
*/
|
||||
fun destroyProxy(proxyID: Int)
|
||||
|
||||
/**
|
||||
* For [IBroadPhase]:
|
||||
* For [BroadPhase]:
|
||||
* Call MoveProxy as many times as you like, then when you are done
|
||||
* call UpdatePairs to finalized the proxy pairs (for your time step).
|
||||
* @return true
|
||||
*
|
||||
* For [IDynamicTree]:
|
||||
* For [DynamicTree]:
|
||||
* Move a proxy with a swepted AABB. If the proxy has moved outside of its fattened AABB,
|
||||
* then the proxy is removed from the tree and re-inserted. Otherwise
|
||||
* the function returns immediately.
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kbox2d.dynamics.Body
|
||||
import ru.dbotthepony.kbox2d.dynamics.joint.AbstractJoint
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import kotlin.math.PI
|
||||
@ -12,8 +13,8 @@ data class StiffnessResult(val stiffness: Double, val damping: Double)
|
||||
fun b2LinearStiffness(
|
||||
frequencyHertz: Double,
|
||||
dampingRatio: Double,
|
||||
bodyA: IBody?,
|
||||
bodyB: IBody?,
|
||||
bodyA: Body?,
|
||||
bodyB: Body?,
|
||||
): StiffnessResult {
|
||||
val massA = bodyA?.mass ?: 0.0
|
||||
val massB = bodyB?.mass ?: 0.0
|
||||
@ -41,8 +42,8 @@ fun b2LinearStiffness(
|
||||
fun b2AngularStiffness(
|
||||
frequencyHertz: Double,
|
||||
dampingRatio: Double,
|
||||
bodyA: IBody?,
|
||||
bodyB: IBody?,
|
||||
bodyA: Body?,
|
||||
bodyB: Body?,
|
||||
): StiffnessResult {
|
||||
val inertiaA = bodyA?.inertia ?: 0.0
|
||||
val inertiaB = bodyB?.inertia ?: 0.0
|
||||
@ -91,13 +92,13 @@ data class Jacobian(
|
||||
* nodes, one for each attached body.
|
||||
*/
|
||||
class JointEdge(
|
||||
other: IBody?, ///< provides quick access to the other body attached.
|
||||
val joint: IJoint, ///< the joint
|
||||
other: Body?, ///< provides quick access to the other body attached.
|
||||
val joint: AbstractJoint, ///< the joint
|
||||
var prev: JointEdge? = null, ///< the previous joint edge in the body's joint list
|
||||
var next: JointEdge? = null ///< the next joint edge in the body's joint list
|
||||
) {
|
||||
val otherNullable: IBody? = other
|
||||
val other: IBody get() = checkNotNull(otherNullable) { "Other body is not present" }
|
||||
val otherNullable: Body? = other
|
||||
val other: Body get() = checkNotNull(otherNullable) { "Other body is not present" }
|
||||
}
|
||||
|
||||
sealed interface IJointDef {
|
||||
@ -109,12 +110,12 @@ sealed interface IJointDef {
|
||||
/**
|
||||
* The first attached body.
|
||||
*/
|
||||
val bodyA: IBody?
|
||||
val bodyA: Body?
|
||||
|
||||
/**
|
||||
* The second attached body.
|
||||
*/
|
||||
val bodyB: IBody?
|
||||
val bodyB: Body?
|
||||
|
||||
/**
|
||||
* Set this flag to true if the attached bodies should collide.
|
||||
@ -128,8 +129,8 @@ sealed interface IJointDef {
|
||||
}
|
||||
|
||||
class DistanceJointDef(
|
||||
b1: IBody,
|
||||
b2: IBody,
|
||||
b1: Body,
|
||||
b2: Body,
|
||||
anchor1: Vector2d,
|
||||
anchor2: Vector2d
|
||||
) : IJointDef {
|
||||
@ -160,8 +161,8 @@ class DistanceJointDef(
|
||||
*/
|
||||
val damping: Double = 0.0
|
||||
|
||||
override var bodyA: IBody = b1
|
||||
override var bodyB: IBody = b2
|
||||
override var bodyA: Body = b1
|
||||
override var bodyB: Body = b2
|
||||
|
||||
override var collideConnected: Boolean = false
|
||||
override var userData: Any? = null
|
||||
@ -185,13 +186,13 @@ class DistanceJointDef(
|
||||
}
|
||||
|
||||
class RevoluteJointDef(
|
||||
b1: IBody,
|
||||
b2: IBody,
|
||||
b1: Body,
|
||||
b2: Body,
|
||||
anchor: Vector2d,
|
||||
) : IJointDef {
|
||||
override val type: JointType = JointType.REVOLUTE
|
||||
override var bodyA: IBody = b1
|
||||
override var bodyB: IBody = b2
|
||||
override var bodyA: Body = b1
|
||||
override var bodyB: Body = b2
|
||||
override var collideConnected: Boolean = false
|
||||
override var userData: Any? = null
|
||||
|
||||
@ -244,14 +245,14 @@ class RevoluteJointDef(
|
||||
}
|
||||
|
||||
class PrismaticJointDef(
|
||||
b1: IBody,
|
||||
b2: IBody,
|
||||
b1: Body,
|
||||
b2: Body,
|
||||
anchor: Vector2d,
|
||||
axis: Vector2d,
|
||||
) : IJointDef {
|
||||
override val type: JointType = JointType.PRISMATIC
|
||||
override var bodyA: IBody = b1
|
||||
override var bodyB: IBody = b2
|
||||
override var bodyA: Body = b1
|
||||
override var bodyB: Body = b2
|
||||
override var collideConnected: Boolean = false
|
||||
override var userData: Any? = null
|
||||
|
||||
@ -311,8 +312,8 @@ class PrismaticJointDef(
|
||||
* two dynamic body anchor points, and a pulley ratio.
|
||||
*/
|
||||
class PulleyJointDef(
|
||||
b1: IBody,
|
||||
b2: IBody,
|
||||
b1: Body,
|
||||
b2: Body,
|
||||
|
||||
/**
|
||||
* The first ground anchor in world coordinates. This point never moves.
|
||||
@ -329,8 +330,8 @@ class PulleyJointDef(
|
||||
ratio: Double,
|
||||
) : IJointDef {
|
||||
override val type: JointType = JointType.PULLEY
|
||||
override var bodyA: IBody = b1
|
||||
override var bodyB: IBody = b2
|
||||
override var bodyA: Body = b1
|
||||
override var bodyB: Body = b2
|
||||
override var collideConnected: Boolean = false
|
||||
override var userData: Any? = null
|
||||
|
||||
@ -375,8 +376,8 @@ class PulleyJointDef(
|
||||
* @warning bodyB on the input joints must both be dynamic
|
||||
*/
|
||||
class GearJointDef(
|
||||
override var bodyA: IBody,
|
||||
override var bodyB: IBody,
|
||||
override var bodyA: Body,
|
||||
override var bodyB: Body,
|
||||
|
||||
/**
|
||||
* The first revolute/prismatic joint attached to the gear joint.
|
||||
@ -416,8 +417,8 @@ class MouseJointDef(
|
||||
*/
|
||||
var target: Vector2d,
|
||||
|
||||
override var bodyB: IBody,
|
||||
override val bodyA: IBody? = null,
|
||||
override var bodyB: Body,
|
||||
override val bodyA: Body? = null,
|
||||
|
||||
/**
|
||||
* The maximum constraint force that can be exerted
|
||||
@ -436,8 +437,8 @@ class MouseJointDef(
|
||||
fun linearStiffness(
|
||||
frequencyHertz: Double,
|
||||
dampingRatio: Double,
|
||||
bodyA: IBody?,
|
||||
bodyB: IBody?
|
||||
bodyA: Body?,
|
||||
bodyB: Body?
|
||||
): MouseJointDef {
|
||||
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||
this.stiffness = stiffness
|
||||
@ -448,8 +449,8 @@ class MouseJointDef(
|
||||
fun angularStiffness(
|
||||
frequencyHertz: Double,
|
||||
dampingRatio: Double,
|
||||
bodyA: IBody?,
|
||||
bodyB: IBody?
|
||||
bodyA: Body?,
|
||||
bodyB: Body?
|
||||
): MouseJointDef {
|
||||
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||
this.stiffness = stiffness
|
||||
@ -467,8 +468,8 @@ class MouseJointDef(
|
||||
* anchors and a local axis helps when saving and loading a game.
|
||||
*/
|
||||
class WheelJointDef(
|
||||
override var bodyA: IBody,
|
||||
override var bodyB: IBody,
|
||||
override var bodyA: Body,
|
||||
override var bodyB: Body,
|
||||
anchor: Vector2d,
|
||||
axis: Vector2d,
|
||||
|
||||
@ -534,8 +535,8 @@ class WheelJointDef(
|
||||
fun linearStiffness(
|
||||
frequencyHertz: Double,
|
||||
dampingRatio: Double,
|
||||
bodyA: IBody = this.bodyA,
|
||||
bodyB: IBody = this.bodyB
|
||||
bodyA: Body = this.bodyA,
|
||||
bodyB: Body = this.bodyB
|
||||
): WheelJointDef {
|
||||
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||
this.stiffness = stiffness
|
||||
@ -546,8 +547,8 @@ class WheelJointDef(
|
||||
fun angularStiffness(
|
||||
frequencyHertz: Double,
|
||||
dampingRatio: Double,
|
||||
bodyA: IBody = this.bodyA,
|
||||
bodyB: IBody = this.bodyB
|
||||
bodyA: Body = this.bodyA,
|
||||
bodyB: Body = this.bodyB
|
||||
): WheelJointDef {
|
||||
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||
this.stiffness = stiffness
|
||||
@ -562,8 +563,8 @@ class WheelJointDef(
|
||||
* of the anchor points is important for computing the reaction torque.
|
||||
*/
|
||||
class WeldJointDef(
|
||||
override var bodyA: IBody,
|
||||
override var bodyB: IBody,
|
||||
override var bodyA: Body,
|
||||
override var bodyB: Body,
|
||||
anchor: Vector2d,
|
||||
|
||||
/**
|
||||
@ -598,8 +599,8 @@ class WeldJointDef(
|
||||
fun linearStiffness(
|
||||
frequencyHertz: Double,
|
||||
dampingRatio: Double,
|
||||
bodyA: IBody = this.bodyA,
|
||||
bodyB: IBody = this.bodyB
|
||||
bodyA: Body = this.bodyA,
|
||||
bodyB: Body = this.bodyB
|
||||
): WeldJointDef {
|
||||
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||
this.stiffness = stiffness
|
||||
@ -610,8 +611,8 @@ class WeldJointDef(
|
||||
fun angularStiffness(
|
||||
frequencyHertz: Double,
|
||||
dampingRatio: Double,
|
||||
bodyA: IBody = this.bodyA,
|
||||
bodyB: IBody = this.bodyB
|
||||
bodyA: Body = this.bodyA,
|
||||
bodyB: Body = this.bodyB
|
||||
): WeldJointDef {
|
||||
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||
this.stiffness = stiffness
|
||||
@ -624,8 +625,8 @@ class WeldJointDef(
|
||||
* Friction joint definition.
|
||||
*/
|
||||
class FrictionJointDef(
|
||||
override var bodyA: IBody,
|
||||
override var bodyB: IBody,
|
||||
override var bodyA: Body,
|
||||
override var bodyB: Body,
|
||||
anchor: Vector2d,
|
||||
|
||||
/**
|
||||
@ -655,8 +656,8 @@ class FrictionJointDef(
|
||||
}
|
||||
|
||||
class MotorJointDef(
|
||||
override var bodyA: IBody,
|
||||
override var bodyB: IBody,
|
||||
override var bodyA: Body,
|
||||
override var bodyB: Body,
|
||||
|
||||
/**
|
||||
* The maximum motor force in N.
|
||||
@ -692,77 +693,3 @@ class MotorJointDef(
|
||||
field = value
|
||||
}
|
||||
}
|
||||
|
||||
interface IJoint : IMovable {
|
||||
/**
|
||||
* Get the type of the concrete joint.
|
||||
*/
|
||||
val type: JointType
|
||||
|
||||
/**
|
||||
* Get the first body attached to this joint.
|
||||
*/
|
||||
val bodyA: IBody
|
||||
|
||||
/**
|
||||
* Get the second body attached to this joint.
|
||||
*/
|
||||
val bodyB: IBody
|
||||
|
||||
/**
|
||||
* Get the anchor point on bodyA in world coordinates.
|
||||
*/
|
||||
val anchorA: Vector2d
|
||||
|
||||
/**
|
||||
* Get the anchor point on bodyB in world coordinates.
|
||||
*/
|
||||
val anchorB: Vector2d
|
||||
|
||||
/**
|
||||
* Get the reaction force on bodyB at the joint anchor in Newtons.
|
||||
*/
|
||||
fun getReactionForce(inv_dt: Double): Vector2d
|
||||
|
||||
/**
|
||||
* Get the reaction torque on bodyB in N*m.
|
||||
*/
|
||||
fun getReactionTorque(inv_dt: Double): Double
|
||||
|
||||
/**
|
||||
* Get the next joint the world joint list.
|
||||
*
|
||||
* Kotlin: This is not a list, but, as in C++ impl, a custom
|
||||
* linked list.
|
||||
*/
|
||||
val next: IJoint?
|
||||
val prev: IJoint?
|
||||
|
||||
fun destroy() {
|
||||
bodyA.world.destroyJoint(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Short-cut function to determine if either body is enabled.
|
||||
*/
|
||||
val isEnabled: Boolean get() = bodyA.isEnabled || bodyB.isEnabled
|
||||
|
||||
/**
|
||||
* Get collide connected.
|
||||
* Note: modifying the collide connect flag won't work correctly because
|
||||
* the flag is only checked when fixture AABBs begin to overlap.
|
||||
*/
|
||||
val collideConnected: Boolean
|
||||
|
||||
/**
|
||||
* Dump this joint to the log file.
|
||||
*/
|
||||
fun dump() { }
|
||||
|
||||
/**
|
||||
* Debug draw this joint
|
||||
*/
|
||||
fun draw(draw: IDebugDraw) { }
|
||||
|
||||
var userData: Any?
|
||||
}
|
||||
|
@ -9,9 +9,12 @@ data class MassData(
|
||||
val inertia: Double,
|
||||
)
|
||||
|
||||
/// A shape is used for collision detection. You can create a shape however you like.
|
||||
/// Shapes used for simulation in b2World are created automatically when a b2Fixture
|
||||
/// is created. Shapes may encapsulate a one or more child shapes.
|
||||
|
||||
/**
|
||||
* A shape is used for collision detection. You can create a shape however you like.
|
||||
* Shapes used for simulation in b2World are created automatically when a b2Fixture
|
||||
* is created. Shapes may encapsulate a one or more child shapes.
|
||||
*/
|
||||
interface IShape<S : IShape<S>> {
|
||||
enum class Type {
|
||||
CIRCLE,
|
||||
@ -20,41 +23,58 @@ interface IShape<S : IShape<S>> {
|
||||
CHAIN,
|
||||
}
|
||||
|
||||
/// Clone the concrete shape.
|
||||
/**
|
||||
* Clone the concrete shape.
|
||||
*/
|
||||
fun copy(): S
|
||||
|
||||
/// Get the type of this shape. You can use this to down cast to the concrete shape.
|
||||
/// @return the shape type.
|
||||
/**
|
||||
* Get the type of this shape. You can use this to down cast to the concrete shape.
|
||||
* @return the shape type.
|
||||
*/
|
||||
val type: Type
|
||||
|
||||
/// Get the number of child primitives.
|
||||
/**
|
||||
* Get the number of child primitives.
|
||||
*/
|
||||
val childCount: Int
|
||||
|
||||
/// Test a point for containment in this shape. This only works for convex shapes.
|
||||
/// @param xf the shape world transform.
|
||||
/// @param p a point in world coordinates.
|
||||
|
||||
/**
|
||||
* Test a point for containment in this shape. This only works for convex shapes.
|
||||
* @param xf the shape world transform.
|
||||
* @param p a point in world coordinates.
|
||||
*/
|
||||
fun testPoint(transform: Transform, p: Vector2d): Boolean { return false }
|
||||
|
||||
/// Cast a ray against a child shape.
|
||||
/// @param output the ray-cast results.
|
||||
/// @param input the ray-cast input parameters.
|
||||
/// @param transform the transform to be applied to the shape.
|
||||
/// @param childIndex the child shape index
|
||||
/**
|
||||
* Cast a ray against a child shape.
|
||||
* @param output the ray-cast results.
|
||||
* @param input the ray-cast input parameters.
|
||||
* @param transform the transform to be applied to the shape.
|
||||
* @param childIndex the child shape index
|
||||
*/
|
||||
fun rayCast(input: RayCastInput, transform: Transform, childIndex: Int): RayCastOutput
|
||||
|
||||
/// Given a transform, compute the associated axis aligned bounding box for a child shape.
|
||||
/// @param aabb returns the axis aligned box.
|
||||
/// @param xf the world transform of the shape.
|
||||
/// @param childIndex the child shape
|
||||
/**
|
||||
* Given a transform, compute the associated axis aligned bounding box for a child shape.
|
||||
* @param aabb returns the axis aligned box.
|
||||
* @param xf the world transform of the shape.
|
||||
* @param childIndex the child shape
|
||||
*/
|
||||
fun computeAABB(transform: Transform, childIndex: Int): AABB
|
||||
|
||||
/// Compute the mass properties of this shape using its dimensions and density.
|
||||
/// The inertia tensor is computed about the local origin.
|
||||
/// @param massData returns the mass data for this shape.
|
||||
/// @param density the density in kilograms per meter squared.
|
||||
/**
|
||||
* Compute the mass properties of this shape using its dimensions and density.
|
||||
* The inertia tensor is computed about the local origin.
|
||||
* @param massData returns the mass data for this shape.
|
||||
* @param density the density in kilograms per meter squared.
|
||||
*/
|
||||
fun computeMass(density: Double): MassData
|
||||
|
||||
/// Radius of a shape. For polygonal shapes this must be b2_polygonRadius. There is no support for
|
||||
/// making rounded polygons.
|
||||
/**
|
||||
* Radius of a shape. For polygonal shapes this must be b2_polygonRadius. There is no support for
|
||||
* making rounded polygons.
|
||||
*/
|
||||
var radius: Double
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
@ -1,11 +1,13 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kbox2d.collision.DistanceProxy
|
||||
|
||||
/**
|
||||
* Input parameters for b2TimeOfImpact
|
||||
*/
|
||||
data class TOIInput(
|
||||
var proxyA: IDistanceProxy,
|
||||
var proxyB: IDistanceProxy,
|
||||
var proxyA: DistanceProxy,
|
||||
var proxyB: DistanceProxy,
|
||||
var sweepA: Sweep,
|
||||
var sweepB: Sweep,
|
||||
var tMax: Double, // defines sweep interval [0, tMax]
|
||||
|
@ -1,245 +0,0 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
/**
|
||||
* The world class manages all physics entities, dynamic simulation,
|
||||
* and asynchronous queries. The world also contains efficient memory
|
||||
* management facilities.
|
||||
*/
|
||||
interface IB2World : IMovable {
|
||||
/**
|
||||
* Register a destruction listener. The listener is owned by you and must
|
||||
* remain in scope.
|
||||
*/
|
||||
var destructionListener: IDestructionListener?
|
||||
|
||||
/**
|
||||
* Register a contact filter to provide specific control over collision.
|
||||
* Otherwise the default filter is used (b2_defaultFilter). The listener is
|
||||
* owned by you and must remain in scope.
|
||||
*/
|
||||
var contactFilter: IContactFilter?
|
||||
|
||||
/**
|
||||
* Register a contact event listener. The listener is owned by you and must
|
||||
* remain in scope.
|
||||
*/
|
||||
var contactListener: IContactListener?
|
||||
|
||||
/**
|
||||
* Register a routine for debug drawing. The debug draw functions are called
|
||||
* inside with b2World::DebugDraw method. The debug draw object is owned
|
||||
* by you and must remain in scope.
|
||||
*/
|
||||
var debugDraw: IDebugDraw?
|
||||
|
||||
/**
|
||||
* Create a rigid body given a definition. No reference to the definition
|
||||
* is retained.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun createBody(bodyDef: ru.dbotthepony.kbox2d.api.BodyDef): ru.dbotthepony.kbox2d.api.IBody
|
||||
|
||||
/**
|
||||
* Destroy a rigid body given a definition. No reference to the definition
|
||||
* is retained. This function is locked during callbacks.
|
||||
* @warning This automatically deletes all associated shapes and joints.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun destroyBody(body: ru.dbotthepony.kbox2d.api.IBody)
|
||||
|
||||
/**
|
||||
* Create a joint to constrain bodies together. No reference to the definition
|
||||
* is retained. This may cause the connected bodies to cease colliding.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun createJoint(jointDef: IJointDef): IJoint
|
||||
|
||||
/**
|
||||
* Destroy a joint. This may cause the connected bodies to begin colliding.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun destroyJoint(joint: IJoint)
|
||||
|
||||
/**
|
||||
* Take a time step. This performs collision detection, integration,
|
||||
* and constraint solution.
|
||||
* @param dt the amount of time to simulate, this should not vary.
|
||||
* @param velocityIterations for the velocity constraint solver.
|
||||
* @param positionIterations for the position constraint solver.
|
||||
*/
|
||||
fun step(dt: Double, velocityIterations: Int, positionIterations: Int)
|
||||
|
||||
/**
|
||||
* Manually clear the force buffer on all bodies. By default, forces are cleared automatically
|
||||
* after each call to Step. The default behavior is modified by calling SetAutoClearForces.
|
||||
* The purpose of this function is to support sub-stepping. Sub-stepping is often used to maintain
|
||||
* a fixed sized time step under a variable frame-rate.
|
||||
* When you perform sub-stepping you will disable auto clearing of forces and instead call
|
||||
* ClearForces after all sub-steps are complete in one pass of your game loop.
|
||||
* @see SetAutoClearForces
|
||||
*/
|
||||
fun clearForces()
|
||||
|
||||
/**
|
||||
* Call this to draw shapes and other debug draw data. This is intentionally non-const.
|
||||
*/
|
||||
fun debugDraw()
|
||||
|
||||
/**
|
||||
* Query the world for all fixtures that potentially overlap the
|
||||
* provided AABB.
|
||||
* @param callback a user implemented callback class.
|
||||
* @param aabb the query box.
|
||||
*/
|
||||
fun queryAABB(aabb: AABB, callback: IQueryCallback)
|
||||
|
||||
/**
|
||||
* Ray-cast the world for all fixtures in the path of the ray. Your callback
|
||||
* controls whether you get the closest point, any point, or n-points.
|
||||
* The ray-cast ignores shapes that contain the starting point.
|
||||
* @param callback a user implemented callback class.
|
||||
* @param point1 the ray starting point
|
||||
* @param point2 the ray ending point
|
||||
*/
|
||||
fun rayCast(point1: Vector2d, point2: Vector2d, callback: IRayCastCallback)
|
||||
|
||||
/**
|
||||
* Get the world body list. With the returned body, use b2Body::GetNext to get
|
||||
* the next body in the world list. A nullptr body indicates the end of the list.
|
||||
* @return the head of the world body list.
|
||||
*/
|
||||
val bodyList: ru.dbotthepony.kbox2d.api.IBody?
|
||||
|
||||
val bodyListIterator: Iterator<ru.dbotthepony.kbox2d.api.IBody> get() {
|
||||
return object : Iterator<ru.dbotthepony.kbox2d.api.IBody> {
|
||||
private var node = bodyList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): ru.dbotthepony.kbox2d.api.IBody {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
check(node != old) { "Hard loop detected at $old" }
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world joint list. With the returned joint, use b2Joint::GetNext to get
|
||||
* the next joint in the world list. A nullptr joint indicates the end of the list.
|
||||
* @return the head of the world joint list.
|
||||
*/
|
||||
val jointList: IJoint?
|
||||
|
||||
val jointListIterator: Iterator<IJoint> get() {
|
||||
return object : Iterator<IJoint> {
|
||||
private var node = jointList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): IJoint {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
check(node != old) { "Hard loop detected at $old" }
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world contact list. With the returned contact, use b2Contact::GetNext to get
|
||||
* the next contact in the world list. A nullptr contact indicates the end of the list.
|
||||
* @return the head of the world contact list.
|
||||
* @warning contacts are created and destroyed in the middle of a time step.
|
||||
* Use b2ContactListener to avoid missing contacts.
|
||||
*/
|
||||
val contactList: IContact? get() = contactManager.contactList
|
||||
|
||||
val contactListIterator: Iterator<IContact> get() {
|
||||
return object : Iterator<IContact> {
|
||||
private var node = contactList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): IContact {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
check(node != old) { "Hard loop detected at $old" }
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable sleep.
|
||||
*/
|
||||
var allowAutoSleep: Boolean
|
||||
|
||||
/**
|
||||
* Enable/disable warm starting. For testing.
|
||||
*/
|
||||
var warmStarting: Boolean
|
||||
|
||||
/**
|
||||
* Enable/disable continuous physics. For testing.
|
||||
*/
|
||||
var continuousPhysics: Boolean
|
||||
|
||||
/**
|
||||
* Enable/disable single stepped continuous physics. For testing.
|
||||
*/
|
||||
var enableSubStepping: Boolean
|
||||
|
||||
val proxyCount: Int get() = contactManager.broadPhase.proxyCount
|
||||
val bodyCount: Int
|
||||
val jointCount: Int
|
||||
val contactCount: Int get() = contactManager.contactCount
|
||||
val treeHeight: Int get() = contactManager.broadPhase.treeHeight
|
||||
val treeBalance: Int get() = contactManager.broadPhase.treeBalance
|
||||
|
||||
/**
|
||||
* Get the quality metric of the dynamic tree. The smaller the better.
|
||||
* The minimum is 1.
|
||||
*/
|
||||
val treeQuality: Double get() = contactManager.broadPhase.treeQuality
|
||||
|
||||
/**
|
||||
* Change the global gravity vector.
|
||||
*/
|
||||
var gravity: Vector2d
|
||||
|
||||
/**
|
||||
* Is the world locked (in the middle of a time step).
|
||||
*/
|
||||
val isLocked: Boolean
|
||||
|
||||
/**
|
||||
* Set flag to control automatic clearing of forces after each time step.
|
||||
*/
|
||||
var autoClearForces: Boolean
|
||||
|
||||
/**
|
||||
* Get the current profile.
|
||||
*/
|
||||
val profileData: ru.dbotthepony.kbox2d.api.IProfileData
|
||||
|
||||
/**
|
||||
* Dump the world into the log file.
|
||||
* @warning this should be called outside of a time step.
|
||||
*/
|
||||
fun dump()
|
||||
|
||||
val contactManager: IContactManager
|
||||
|
||||
fun notifyNewContacts()
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
package ru.dbotthepony.kbox2d.api
|
||||
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
|
||||
import ru.dbotthepony.kbox2d.dynamics.joint.AbstractJoint
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
/**
|
||||
@ -11,7 +14,7 @@ interface IContactFilter {
|
||||
* Return true if contact calculations should be performed between these two shapes.
|
||||
* @warning for performance reasons this is only called when the AABBs begin to overlap.
|
||||
*/
|
||||
fun shouldCollide(fixtureA: IFixture, fixtureB: IFixture): Boolean
|
||||
fun shouldCollide(fixtureA: Fixture, fixtureB: Fixture): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@ -24,13 +27,13 @@ interface IDestructionListener {
|
||||
* Called when any joint is about to be destroyed due
|
||||
* to the destruction of one of its attached bodies.
|
||||
*/
|
||||
fun sayGoodbye(joint: IJoint)
|
||||
fun sayGoodbye(joint: AbstractJoint)
|
||||
|
||||
/**
|
||||
* Called when any fixture is about to be destroyed due
|
||||
* to the destruction of its parent body.
|
||||
*/
|
||||
fun sayGoodbye(fixture: IFixture)
|
||||
fun sayGoodbye(fixture: Fixture)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,12 +63,12 @@ interface IContactListener {
|
||||
/**
|
||||
* Called when two fixtures begin to touch.
|
||||
*/
|
||||
fun beginContact(contact: IContact)
|
||||
fun beginContact(contact: AbstractContact)
|
||||
|
||||
/**
|
||||
* Called when two fixtures cease to touch.
|
||||
*/
|
||||
fun endContact(contact: IContact)
|
||||
fun endContact(contact: AbstractContact)
|
||||
|
||||
/**
|
||||
* This is called after a contact is updated. This allows you to inspect a
|
||||
@ -84,7 +87,7 @@ interface IContactListener {
|
||||
* get an EndContact callback. However, you may get a BeginContact callback
|
||||
* the next step.
|
||||
*/
|
||||
fun preSolve(contact: IContact, oldManifold: Manifold)
|
||||
fun preSolve(contact: AbstractContact, oldManifold: Manifold)
|
||||
|
||||
/**
|
||||
* This lets you inspect a contact after the solver is finished. This is useful
|
||||
@ -96,7 +99,7 @@ interface IContactListener {
|
||||
*
|
||||
* Note: this is only called for contacts that are touching, solid, and awake.
|
||||
*/
|
||||
fun postSolve(contact: IContact, impulse: ContactImpulse)
|
||||
fun postSolve(contact: AbstractContact, impulse: ContactImpulse)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,7 +111,7 @@ fun interface IQueryCallback {
|
||||
* Called for each fixture found in the query AABB.
|
||||
* @return false to terminate the query.
|
||||
*/
|
||||
fun reportFixture(fixture: IFixture): Boolean
|
||||
fun reportFixture(fixture: Fixture): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,5 +138,5 @@ fun interface IRayCastCallback {
|
||||
* @return -1 to filter, 0 to terminate, fraction to clip the ray for
|
||||
* closest hit, 1 to continue
|
||||
*/
|
||||
fun reportFixture(fixture: IFixture, point: Vector2d, normal: Vector2d, fraction: Double): Double
|
||||
fun reportFixture(fixture: Fixture, point: Vector2d, normal: Vector2d, fraction: Double): Double
|
||||
}
|
||||
|
@ -4,6 +4,10 @@ import ru.dbotthepony.kbox2d.api.*
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
typealias b2Pair = Pair<Int, Int>
|
||||
|
||||
const val e_nullProxy = -1
|
||||
|
||||
private class IntArrayList {
|
||||
private var buffer = IntArray(16)
|
||||
var size: Int = 0
|
||||
@ -33,17 +37,28 @@ private class IntArrayList {
|
||||
}
|
||||
}
|
||||
|
||||
class BroadPhase : IBroadPhase {
|
||||
/**
|
||||
* The broad-phase is used for computing pairs and performing volume queries and ray casts.
|
||||
* This broad-phase does not persist pairs. Instead, this reports potentially new pairs.
|
||||
* It is up to the client to consume the new pairs and to track subsequent overlap.
|
||||
*/
|
||||
class BroadPhase : IProxieable, IMovable {
|
||||
private val moveBuffer = IntArrayList()
|
||||
private val pairBuffer = ArrayList<b2Pair>()
|
||||
private var moveCount = 0
|
||||
|
||||
private val tree = DynamicTree()
|
||||
|
||||
override var proxyCount: Int = 0
|
||||
/**
|
||||
* Get the number of proxies.
|
||||
*/
|
||||
var proxyCount: Int = 0
|
||||
private set
|
||||
|
||||
override fun touchProxy(proxyID: Int) {
|
||||
/**
|
||||
* Call to trigger a re-processing of it's pairs on the next call to UpdatePairs.
|
||||
*/
|
||||
fun touchProxy(proxyID: Int) {
|
||||
bufferMove(proxyID)
|
||||
}
|
||||
|
||||
@ -107,16 +122,28 @@ class BroadPhase : IBroadPhase {
|
||||
return tree.getFatAABB(proxyID)
|
||||
}
|
||||
|
||||
override val treeHeight: Int
|
||||
/**
|
||||
* Get the height of the embedded tree.
|
||||
*/
|
||||
val treeHeight: Int
|
||||
get() = tree.height
|
||||
|
||||
override val treeBalance: Int
|
||||
/**
|
||||
* Get the balance of the embedded tree.
|
||||
*/
|
||||
val treeBalance: Int
|
||||
get() = tree.maxBalance
|
||||
|
||||
override val treeQuality: Double
|
||||
/**
|
||||
* Get the quality metric of the embedded tree.
|
||||
*/
|
||||
val treeQuality: Double
|
||||
get() = tree.getAreaRatio
|
||||
|
||||
override fun testOverlap(proxyIDA: Int, proxyIDB: Int): Boolean {
|
||||
/**
|
||||
* Test overlap of fat AABBs.
|
||||
*/
|
||||
fun testOverlap(proxyIDA: Int, proxyIDB: Int): Boolean {
|
||||
return tree.getFatAABB(proxyIDA).intersect(tree.getFatAABB(proxyIDB))
|
||||
}
|
||||
|
||||
@ -143,7 +170,10 @@ class BroadPhase : IBroadPhase {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun updatePairs(callback: (Any?, Any?) -> Unit) {
|
||||
/**
|
||||
* Update the pairs. This results in pair callbacks. This can only add pairs.
|
||||
*/
|
||||
fun updatePairs(callback: (Any?, Any?) -> Unit) {
|
||||
pairBuffer.clear()
|
||||
|
||||
for (i in 0 until moveCount) {
|
||||
|
@ -17,9 +17,13 @@ var b2_gjkIters = 0
|
||||
var b2_gjkMaxIters = 0
|
||||
private set
|
||||
|
||||
class DistanceProxy : IDistanceProxy {
|
||||
override val vertices: List<Vector2d>
|
||||
override val radius: Double
|
||||
/**
|
||||
* A distance proxy is used by the GJK algorithm.
|
||||
* It encapsulates any shape.
|
||||
*/
|
||||
class DistanceProxy {
|
||||
val vertices: List<Vector2d>
|
||||
val radius: Double
|
||||
|
||||
constructor(shape: IShape<*>, index: Int) {
|
||||
when (shape.type) {
|
||||
@ -63,7 +67,10 @@ class DistanceProxy : IDistanceProxy {
|
||||
this.radius = radius
|
||||
}
|
||||
|
||||
override fun getSupport(d: Vector2d): Int {
|
||||
/**
|
||||
* Get the supporting vertex index in the given direction.
|
||||
*/
|
||||
fun getSupport(d: Vector2d): Int {
|
||||
var bestIndex = 0
|
||||
var bestValue = b2Dot(vertices[0], d)
|
||||
|
||||
@ -79,7 +86,10 @@ class DistanceProxy : IDistanceProxy {
|
||||
return bestIndex
|
||||
}
|
||||
|
||||
override fun getSupportVertex(d: Vector2d): Vector2d {
|
||||
/**
|
||||
* Get the supporting vertex in the given direction.
|
||||
*/
|
||||
fun getSupportVertex(d: Vector2d): Vector2d {
|
||||
return vertices[getSupport(d)]
|
||||
}
|
||||
}
|
||||
@ -116,9 +126,9 @@ class Simplex() {
|
||||
|
||||
constructor(
|
||||
cache: SimplexCache,
|
||||
proxyA: IDistanceProxy,
|
||||
proxyA: DistanceProxy,
|
||||
transformA: Transform,
|
||||
proxyB: IDistanceProxy,
|
||||
proxyB: DistanceProxy,
|
||||
transformB: Transform,
|
||||
) : this() {
|
||||
check(cache.count <= 3)
|
||||
@ -413,8 +423,8 @@ private const val k_maxIters = 20
|
||||
*/
|
||||
fun b2Distance(
|
||||
cache: SimplexCache,
|
||||
proxyA: IDistanceProxy,
|
||||
proxyB: IDistanceProxy,
|
||||
proxyA: DistanceProxy,
|
||||
proxyB: DistanceProxy,
|
||||
transformA: Transform,
|
||||
transformB: Transform,
|
||||
useRadii: Boolean = false
|
||||
@ -553,8 +563,8 @@ private const val gjk_tolerance = 0.5 * b2_linearSlop
|
||||
*/
|
||||
fun b2ShapeCast(
|
||||
output: ShapeCastOutput,
|
||||
proxyA: IDistanceProxy,
|
||||
proxyB: IDistanceProxy,
|
||||
proxyA: DistanceProxy,
|
||||
proxyB: DistanceProxy,
|
||||
xfA: Transform,
|
||||
xfB: Transform,
|
||||
r: Vector2d,
|
||||
|
@ -5,9 +5,36 @@ import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
/**
|
||||
* A node in the dynamic tree. The client does not interact with this directly.
|
||||
*/
|
||||
private data class TreeNode(
|
||||
val tree: DynamicTree,
|
||||
/// Enlarged AABB
|
||||
var aabb: AABB? = null,
|
||||
|
||||
var child1: Int = 0,
|
||||
var child2: Int = 0,
|
||||
|
||||
// leaf = 0, free node = -1
|
||||
var height: Int = 0,
|
||||
|
||||
var moved: Boolean = false,
|
||||
|
||||
var userData: Any? = null,
|
||||
) {
|
||||
val isLeaf get() = child1 == b2_nullNode
|
||||
|
||||
// union
|
||||
private var _union: Int = b2_nullNode
|
||||
|
||||
var parent by this::_union
|
||||
var next by this::_union
|
||||
}
|
||||
|
||||
const val b2_nullNode = -1
|
||||
|
||||
class DynamicTree : IDynamicTree {
|
||||
class DynamicTree : IProxieable, IMovable {
|
||||
private var root: Int = b2_nullNode
|
||||
|
||||
private val nodeCapacity get() = nodes.size
|
||||
@ -15,7 +42,7 @@ class DynamicTree : IDynamicTree {
|
||||
private var freeList = 0
|
||||
private var insertionCount = 0
|
||||
|
||||
internal var nodes = Array(16) { TreeNode(this) }
|
||||
private var nodes = Array(16) { TreeNode(this) }
|
||||
|
||||
init {
|
||||
// Build a linked list for the free list.
|
||||
@ -162,11 +189,11 @@ class DynamicTree : IDynamicTree {
|
||||
return nodes[proxyID].userData
|
||||
}
|
||||
|
||||
override fun wasMoved(proxyID: Int): Boolean {
|
||||
fun wasMoved(proxyID: Int): Boolean {
|
||||
return nodes[proxyID].moved
|
||||
}
|
||||
|
||||
override fun clearMoved(proxyID: Int) {
|
||||
fun clearMoved(proxyID: Int) {
|
||||
nodes[proxyID].moved = false
|
||||
}
|
||||
|
||||
@ -476,9 +503,16 @@ class DynamicTree : IDynamicTree {
|
||||
return iA
|
||||
}
|
||||
|
||||
override val height: Int get() = if (root == b2_nullNode) 0 else nodes[root].height
|
||||
/**
|
||||
* Compute the height of the binary tree in O(N) time. Should not be
|
||||
* called often.
|
||||
*/
|
||||
val height: Int get() = if (root == b2_nullNode) 0 else nodes[root].height
|
||||
|
||||
override val getAreaRatio: Double get() {
|
||||
/**
|
||||
* Get the ratio of the sum of the node areas to the root area.
|
||||
*/
|
||||
val getAreaRatio: Double get() {
|
||||
if (root == b2_nullNode)
|
||||
return 0.0
|
||||
|
||||
@ -562,7 +596,10 @@ class DynamicTree : IDynamicTree {
|
||||
validateMetrics(child2)
|
||||
}
|
||||
|
||||
override fun validate() {
|
||||
/**
|
||||
* Validate this tree. For testing.
|
||||
*/
|
||||
fun validate() {
|
||||
validateStructure(root)
|
||||
validateMetrics(root)
|
||||
|
||||
@ -578,7 +615,11 @@ class DynamicTree : IDynamicTree {
|
||||
check(nodeCount + freeCount == nodeCapacity)
|
||||
}
|
||||
|
||||
override val maxBalance: Int get() {
|
||||
/**
|
||||
* Get the maximum balance of an node in the tree. The balance is the difference
|
||||
* in height of the two children of a node.
|
||||
*/
|
||||
val maxBalance: Int get() {
|
||||
var maxBalance = 0
|
||||
|
||||
for (node in nodes) {
|
||||
@ -596,7 +637,10 @@ class DynamicTree : IDynamicTree {
|
||||
return maxBalance
|
||||
}
|
||||
|
||||
override fun rebuildBottomUp() {
|
||||
/**
|
||||
* Build an optimal tree. Very expensive. For testing.
|
||||
*/
|
||||
fun rebuildBottomUp() {
|
||||
TODO("Not Yet Implemented")
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,8 @@ private data class MinSeparationResult(
|
||||
|
||||
private class SeparationFunction(
|
||||
cache: SimplexCache,
|
||||
val proxyA: IDistanceProxy,
|
||||
val proxyB: IDistanceProxy,
|
||||
val proxyA: DistanceProxy,
|
||||
val proxyB: DistanceProxy,
|
||||
val sweepA: Sweep,
|
||||
val sweepB: Sweep,
|
||||
t1: Double
|
||||
@ -228,8 +228,8 @@ const val k_maxIterations = 20
|
||||
* Note: use [b2Distance] to compute the contact point and normal at the time of impact.
|
||||
*/
|
||||
fun b2TimeOfImpact(
|
||||
proxyA: IDistanceProxy,
|
||||
proxyB: IDistanceProxy,
|
||||
proxyA: DistanceProxy,
|
||||
proxyB: DistanceProxy,
|
||||
_sweepA: Sweep,
|
||||
_sweepB: Sweep,
|
||||
tMax: Double, // defines sweep interval [0, tMax]
|
||||
|
@ -1,3 +1,6 @@
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package ru.dbotthepony.kbox2d.dynamics
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.*
|
||||
@ -15,30 +18,141 @@ import ru.dbotthepony.kvector.vector.Color
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import ru.dbotthepony.kvector.vector.ndouble.times
|
||||
|
||||
class B2World(override var gravity: Vector2d) : IB2World {
|
||||
override var bodyCount: Int = 0
|
||||
/**
|
||||
* The world class manages all physics entities, dynamic simulation,
|
||||
* and asynchronous queries. The world also contains efficient memory
|
||||
* management facilities.
|
||||
*/
|
||||
class B2World(
|
||||
/**
|
||||
* Change the global gravity vector.
|
||||
*/
|
||||
var gravity: Vector2d
|
||||
) : IMovable {
|
||||
var bodyCount: Int = 0
|
||||
private set
|
||||
override var jointCount: Int = 0
|
||||
var jointCount: Int = 0
|
||||
private set
|
||||
|
||||
override val contactManager: IContactManager = ContactManager()
|
||||
val contactManager = ContactManager()
|
||||
|
||||
override var destructionListener: IDestructionListener? = null
|
||||
override var contactFilter: IContactFilter? by contactManager::contactFilter
|
||||
override var contactListener: IContactListener? by contactManager::contactListener
|
||||
/**
|
||||
* Get the world contact list. With the returned contact, use b2Contact::GetNext to get
|
||||
* the next contact in the world list. A nullptr contact indicates the end of the list.
|
||||
* @return the head of the world contact list.
|
||||
* @warning contacts are created and destroyed in the middle of a time step.
|
||||
* Use b2ContactListener to avoid missing contacts.
|
||||
*/
|
||||
val contactList: AbstractContact? get() = contactManager.contactList
|
||||
val contactListIterator: Iterator<AbstractContact> get() = contactManager.contactListIterator
|
||||
|
||||
override var debugDraw: IDebugDraw? = null
|
||||
/**
|
||||
* Register a destruction listener. The listener is owned by you and must
|
||||
* remain in scope.
|
||||
*/
|
||||
var destructionListener: IDestructionListener? = null
|
||||
|
||||
override var bodyList: IBody? = null
|
||||
private set
|
||||
override var jointList: IJoint? = null
|
||||
/**
|
||||
* Register a contact filter to provide specific control over collision.
|
||||
* Otherwise the default filter is used (b2_defaultFilter). The listener is
|
||||
* owned by you and must remain in scope.
|
||||
*/
|
||||
var contactFilter: IContactFilter? by contactManager::contactFilter
|
||||
|
||||
/**
|
||||
* Register a contact event listener. The listener is owned by you and must
|
||||
* remain in scope.
|
||||
*/
|
||||
var contactListener: IContactListener? by contactManager::contactListener
|
||||
|
||||
|
||||
/**
|
||||
* Register a routine for debug drawing. The debug draw functions are called
|
||||
* inside with b2World::DebugDraw method. The debug draw object is owned
|
||||
* by you and must remain in scope.
|
||||
*/
|
||||
var debugDraw: IDebugDraw? = null
|
||||
|
||||
/**
|
||||
* Get the world body list. With the returned body, use b2Body::GetNext to get
|
||||
* the next body in the world list. A nullptr body indicates the end of the list.
|
||||
* @return the head of the world body list.
|
||||
*/
|
||||
var bodyList: Body? = null
|
||||
private set
|
||||
|
||||
override var warmStarting: Boolean = true
|
||||
override var continuousPhysics: Boolean = true
|
||||
override var enableSubStepping: Boolean = false
|
||||
/**
|
||||
* Get the world joint list. With the returned joint, use b2Joint::GetNext to get
|
||||
* the next joint in the world list. A nullptr joint indicates the end of the list.
|
||||
* @return the head of the world joint list.
|
||||
*/
|
||||
var jointList: AbstractJoint? = null
|
||||
private set
|
||||
|
||||
override var allowAutoSleep: Boolean = true
|
||||
val bodyListIterator: Iterator<Body> get() {
|
||||
return object : Iterator<Body> {
|
||||
private var node = bodyList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): Body {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
check(node != old) { "Hard loop detected at $old" }
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jointListIterator: Iterator<AbstractJoint> get() {
|
||||
return object : Iterator<AbstractJoint> {
|
||||
private var node = jointList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): AbstractJoint {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
check(node != old) { "Hard loop detected at $old" }
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable warm starting. For testing.
|
||||
*/
|
||||
var warmStarting: Boolean = true
|
||||
|
||||
/**
|
||||
* Enable/disable continuous physics. For testing.
|
||||
*/
|
||||
var continuousPhysics: Boolean = true
|
||||
|
||||
/**
|
||||
* Enable/disable single stepped continuous physics. For testing.
|
||||
*/
|
||||
var enableSubStepping: Boolean = false
|
||||
|
||||
/**
|
||||
* Get the quality metric of the dynamic tree. The smaller the better.
|
||||
* The minimum is 1.
|
||||
*/
|
||||
val treeQuality: Double get() = contactManager.broadPhase.treeQuality
|
||||
|
||||
val proxyCount: Int get() = contactManager.broadPhase.proxyCount
|
||||
val contactCount: Int get() = contactManager.contactCount
|
||||
val treeHeight: Int get() = contactManager.broadPhase.treeHeight
|
||||
val treeBalance: Int get() = contactManager.broadPhase.treeBalance
|
||||
|
||||
/**
|
||||
* Enable/disable sleep.
|
||||
*/
|
||||
var allowAutoSleep: Boolean = true
|
||||
set(value) {
|
||||
if (value == field)
|
||||
return
|
||||
@ -52,40 +166,57 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
}
|
||||
}
|
||||
|
||||
override var isLocked: Boolean = false
|
||||
/**
|
||||
* Is the world locked (in the middle of a time step).
|
||||
*/
|
||||
var isLocked: Boolean = false
|
||||
private set
|
||||
|
||||
override var autoClearForces: Boolean = true
|
||||
/**
|
||||
* Set flag to control automatic clearing of forces after each time step.
|
||||
*/
|
||||
var autoClearForces: Boolean = true
|
||||
|
||||
private var stepComplete = true
|
||||
private var newContacts = false
|
||||
|
||||
private val profile = ProfileData()
|
||||
|
||||
override fun notifyNewContacts() {
|
||||
fun notifyNewContacts() {
|
||||
newContacts = true
|
||||
}
|
||||
|
||||
override fun createBody(bodyDef: BodyDef): IBody {
|
||||
/**
|
||||
* Create a rigid body given a definition. No reference to the definition
|
||||
* is retained.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun createBody(bodyDef: BodyDef): Body {
|
||||
if (isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
|
||||
val body = Body(bodyDef, this)
|
||||
|
||||
body.next = bodyList
|
||||
(bodyList as Body?)?.prev = body
|
||||
bodyList?.prev = body
|
||||
bodyList = body
|
||||
bodyCount++
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
override fun destroyBody(body: IBody) {
|
||||
/**
|
||||
* Destroy a rigid body given a definition. No reference to the definition
|
||||
* is retained. This function is locked during callbacks.
|
||||
* @warning This automatically deletes all associated shapes and joints.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun destroyBody(body: Body) {
|
||||
if (isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
|
||||
check(body.world == this) { "$body does not belong to $this" }
|
||||
check(bodyCount > 0) { "I have ${bodyCount} bodies, can't remove one" }
|
||||
check(bodyCount > 0) { "I have $bodyCount bodies, can't remove one" }
|
||||
|
||||
// Delete the attached joints.
|
||||
for (jointEdge in body.jointIterator) {
|
||||
@ -101,12 +232,12 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
// Delete the attached fixtures. This destroys broad-phase proxies.
|
||||
for (fixture in body.fixtureIterator) {
|
||||
destructionListener?.sayGoodbye(fixture)
|
||||
(fixture as Fixture).destroyProxies(contactManager.broadPhase)
|
||||
fixture.destroyProxies(contactManager.broadPhase)
|
||||
}
|
||||
|
||||
// Remove world body list.
|
||||
val prev = body.prev as Body?
|
||||
val next = body.next as Body?
|
||||
val prev = body.prev
|
||||
val next = body.next
|
||||
|
||||
prev?.next = next
|
||||
next?.prev = prev
|
||||
@ -116,10 +247,15 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
}
|
||||
|
||||
bodyCount--
|
||||
(body as Body).unlink()
|
||||
body.unlink()
|
||||
}
|
||||
|
||||
override fun createJoint(jointDef: IJointDef): AbstractJoint {
|
||||
/**
|
||||
* Create a joint to constrain bodies together. No reference to the definition
|
||||
* is retained. This may cause the connected bodies to cease colliding.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun createJoint(jointDef: IJointDef): AbstractJoint {
|
||||
if (isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
|
||||
@ -127,7 +263,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
// Connect to the world list.
|
||||
joint.next = jointList
|
||||
(jointList as AbstractJoint?)?.prev = joint
|
||||
jointList?.prev = joint
|
||||
jointList = joint
|
||||
jointCount++
|
||||
|
||||
@ -150,12 +286,15 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
return joint
|
||||
}
|
||||
|
||||
override fun destroyJoint(joint: IJoint) {
|
||||
/**
|
||||
* Destroy a joint. This may cause the connected bodies to begin colliding.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun destroyJoint(joint: AbstractJoint) {
|
||||
if (isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
|
||||
check(jointCount > 0) { "No joints tracked to remove" }
|
||||
joint as AbstractJoint
|
||||
require((joint.nullableBodyA?.world == this || joint.nullableBodyA == null) && (joint.nullableBodyB?.world == this || joint.nullableBodyB == null)) { "$joint does not belong to $this" }
|
||||
|
||||
if (!joint.isValid) {
|
||||
@ -164,8 +303,8 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
// Remove from the doubly linked list.
|
||||
this.run {
|
||||
val prev = joint.prev as AbstractJoint?
|
||||
val next = joint.next as AbstractJoint?
|
||||
val prev = joint.prev
|
||||
val next = joint.next
|
||||
|
||||
prev?.next = next
|
||||
next?.prev = prev
|
||||
@ -241,25 +380,20 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
// Clear all the island flags.
|
||||
for (body in bodyListIterator) {
|
||||
body as Body
|
||||
body.isOnIsland = false
|
||||
}
|
||||
|
||||
for (contact in contactListIterator) {
|
||||
contact as AbstractContact
|
||||
contact.isOnIsland = false
|
||||
}
|
||||
|
||||
for (joint in jointListIterator) {
|
||||
joint as AbstractJoint
|
||||
check(joint.isValid) { "$joint is no longer valid, but present in linked list" }
|
||||
joint.isOnIsland = false
|
||||
}
|
||||
|
||||
// Build and simulate all awake islands.
|
||||
for (seed in bodyListIterator) {
|
||||
seed as Body
|
||||
|
||||
if (seed.type == BodyType.STATIC || !seed.isAwake || !seed.isEnabled || seed.isOnIsland) {
|
||||
continue
|
||||
}
|
||||
@ -287,7 +421,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
// Search all contacts connected to this body.
|
||||
for (ce in body.contactEdgeIterator) {
|
||||
val contact = ce.contact as AbstractContact
|
||||
val contact = ce.contact
|
||||
|
||||
// Has this contact already been added to an island?
|
||||
if (contact.isOnIsland)
|
||||
@ -304,7 +438,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
island.add(contact)
|
||||
contact.isOnIsland = true
|
||||
|
||||
val other = ce.other as Body
|
||||
val other = ce.other
|
||||
|
||||
// Was the other body already added to this island?
|
||||
if (!other.isOnIsland) {
|
||||
@ -315,13 +449,13 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
// Search all joints connect to this body.
|
||||
for (je in body.jointIterator) {
|
||||
val joint = je.joint as AbstractJoint
|
||||
val joint = je.joint
|
||||
check(joint.isValid) { "$joint is no longer valid, but present in linked list of $body" }
|
||||
|
||||
if (joint.isOnIsland)
|
||||
continue
|
||||
|
||||
val other = je.otherNullable as Body?
|
||||
val other = je.otherNullable
|
||||
|
||||
// Don't simulate joints connected to disabled bodies.
|
||||
if (other != null && !other.isEnabled)
|
||||
@ -357,8 +491,6 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
// Synchronize fixtures, check for out of range bodies.
|
||||
for (body in bodyListIterator) {
|
||||
body as Body
|
||||
|
||||
// If a body was not in an island then it did not move.
|
||||
if (!body.isOnIsland || body.type == BodyType.STATIC) {
|
||||
continue
|
||||
@ -381,14 +513,12 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
if (stepComplete) {
|
||||
for (body in bodyListIterator) {
|
||||
body as Body
|
||||
body.isOnIsland = false
|
||||
body.sweep.alpha0 = 0.0
|
||||
}
|
||||
|
||||
for (c in contactManager.contactListIterator) {
|
||||
// Invalidate TOI
|
||||
c as AbstractContact
|
||||
c.isOnIsland = false
|
||||
c.toiFlag = false
|
||||
c.toiCount = 0
|
||||
@ -406,13 +536,11 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
continue
|
||||
}
|
||||
|
||||
c as AbstractContact
|
||||
|
||||
if (c.toiCount > b2_maxSubSteps) {
|
||||
continue
|
||||
}
|
||||
|
||||
var alpha = 1.0
|
||||
var alpha: Double
|
||||
|
||||
if (c.toiFlag) {
|
||||
// This contact has a valid cached TOI.
|
||||
@ -550,7 +678,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
if (body.type == BodyType.DYNAMIC) {
|
||||
for (ce in body.contactEdgeIterator) {
|
||||
val contact = ce.contact as AbstractContact
|
||||
val contact = ce.contact
|
||||
|
||||
// Has this contact already been added to the island?
|
||||
if (contact.isOnIsland) {
|
||||
@ -569,8 +697,6 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
continue
|
||||
}
|
||||
|
||||
other as Body
|
||||
|
||||
// Tentatively advance the body to the TOI.
|
||||
val backup = other.sweep
|
||||
if (!other.isOnIsland) {
|
||||
@ -639,7 +765,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
// Invalidate all contact TOIs on this displaced body.
|
||||
for (ce in body.contactEdgeIterator) {
|
||||
val contact = ce.contact as AbstractContact
|
||||
val contact = ce.contact
|
||||
contact.toiFlag = false
|
||||
contact.isOnIsland = false
|
||||
}
|
||||
@ -658,8 +784,15 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
private var m_inv_dt0 = 0.0
|
||||
|
||||
override fun step(dt: Double, velocityIterations: Int, positionIterations: Int) {
|
||||
var stepTimer = System.nanoTime()
|
||||
/**
|
||||
* Take a time step. This performs collision detection, integration,
|
||||
* and constraint solution.
|
||||
* @param dt the amount of time to simulate, this should not vary.
|
||||
* @param velocityIterations for the velocity constraint solver.
|
||||
* @param positionIterations for the position constraint solver.
|
||||
*/
|
||||
fun step(dt: Double, velocityIterations: Int, positionIterations: Int) {
|
||||
val stepTimer = System.nanoTime()
|
||||
|
||||
// If new fixtures were added, we need to find the new contacts.
|
||||
if (newContacts) {
|
||||
@ -724,19 +857,41 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
profile.step = System.nanoTime() - stepTimer
|
||||
}
|
||||
|
||||
override fun clearForces() {
|
||||
/**
|
||||
* Manually clear the force buffer on all bodies. By default, forces are cleared automatically
|
||||
* after each call to Step. The default behavior is modified by calling SetAutoClearForces.
|
||||
* The purpose of this function is to support sub-stepping. Sub-stepping is often used to maintain
|
||||
* a fixed sized time step under a variable frame-rate.
|
||||
* When you perform sub-stepping you will disable auto clearing of forces and instead call
|
||||
* ClearForces after all sub-steps are complete in one pass of your game loop.
|
||||
* @see autoClearForces
|
||||
*/
|
||||
fun clearForces() {
|
||||
for (body in bodyListIterator) {
|
||||
body as Body
|
||||
body.force = Vector2d.ZERO
|
||||
body.torque = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
override fun queryAABB(aabb: AABB, callback: IQueryCallback) {
|
||||
/**
|
||||
* Query the world for all fixtures that potentially overlap the
|
||||
* provided AABB.
|
||||
* @param callback a user implemented callback class.
|
||||
* @param aabb the query box.
|
||||
*/
|
||||
fun queryAABB(aabb: AABB, callback: IQueryCallback) {
|
||||
contactManager.broadPhase.query(aabb) { nodeId, userData -> callback.reportFixture((userData as FixtureProxy).fixture) }
|
||||
}
|
||||
|
||||
override fun rayCast(point1: Vector2d, point2: Vector2d, callback: IRayCastCallback) {
|
||||
/**
|
||||
* Ray-cast the world for all fixtures in the path of the ray. Your callback
|
||||
* controls whether you get the closest point, any point, or n-points.
|
||||
* The ray-cast ignores shapes that contain the starting point.
|
||||
* @param callback a user implemented callback class.
|
||||
* @param point1 the ray starting point
|
||||
* @param point2 the ray ending point
|
||||
*/
|
||||
fun rayCast(point1: Vector2d, point2: Vector2d, callback: IRayCastCallback) {
|
||||
val input = RayCastInput(point1, point2, 1.0)
|
||||
|
||||
contactManager.broadPhase.rayCast(input, object : ProxyRayCastCallback {
|
||||
@ -756,7 +911,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
})
|
||||
}
|
||||
|
||||
private fun drawShape(fixture: IFixture, xf: Transform, color: Color) {
|
||||
private fun drawShape(fixture: Fixture, xf: Transform, color: Color) {
|
||||
when (fixture.type) {
|
||||
IShape.Type.CIRCLE -> {
|
||||
val circle = fixture.shape as CircleShape
|
||||
@ -799,7 +954,10 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
}
|
||||
}
|
||||
|
||||
override fun debugDraw() {
|
||||
/**
|
||||
* Call this to draw shapes and other debug draw data. This is intentionally non-const.
|
||||
*/
|
||||
fun debugDraw() {
|
||||
val debugDraw = debugDraw ?: return
|
||||
|
||||
if (debugDraw.drawShapes) {
|
||||
@ -872,17 +1030,6 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BAD_BODY_COLOR = Color(1f, 0f, 0f)
|
||||
private val DISABLED_BODY_COLOR = Color(0.5f, 0.5f, 0.3f)
|
||||
private val STATIC_BODY_COLOR = Color(0.5f, 0.9f, 0.5f)
|
||||
private val KINEMATIC_BODY_COLOR = Color(0.5f, 0.5f, 0.9f)
|
||||
private val SLEEPING_BODY_COLOR = Color(0.6f, 0.6f, 0.6f)
|
||||
private val NORMAL_BODY_COLOR = Color(0.9f, 0.7f, 0.7f)
|
||||
private val PAIR_COLOR = Color(0.3f, 0.9f, 0.9f)
|
||||
private val AABB_COLOR = Color(0.9f, 0.3f, 0.9f)
|
||||
}
|
||||
|
||||
override fun shiftOrigin(newOrigin: Vector2d) {
|
||||
if (isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
@ -891,7 +1038,6 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
|
||||
try {
|
||||
for (body in bodyListIterator) {
|
||||
body as Body
|
||||
body.transform.p -= newOrigin
|
||||
body.sweep.c0 -= newOrigin
|
||||
body.sweep.c -= newOrigin
|
||||
@ -907,10 +1053,29 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
||||
}
|
||||
}
|
||||
|
||||
override val profileData: IProfileData
|
||||
|
||||
/**
|
||||
* Get the current profile.
|
||||
*/
|
||||
val profileData: IProfileData
|
||||
get() = profile.snapshot()
|
||||
|
||||
override fun dump() {
|
||||
/**
|
||||
* Dump the world into the log file.
|
||||
* @warning this should be called outside of a time step.
|
||||
*/
|
||||
fun dump() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BAD_BODY_COLOR = Color(1f, 0f, 0f)
|
||||
private val DISABLED_BODY_COLOR = Color(0.5f, 0.5f, 0.3f)
|
||||
private val STATIC_BODY_COLOR = Color(0.5f, 0.9f, 0.5f)
|
||||
private val KINEMATIC_BODY_COLOR = Color(0.5f, 0.5f, 0.9f)
|
||||
private val SLEEPING_BODY_COLOR = Color(0.6f, 0.6f, 0.6f)
|
||||
private val NORMAL_BODY_COLOR = Color(0.9f, 0.7f, 0.7f)
|
||||
private val PAIR_COLOR = Color(0.3f, 0.9f, 0.9f)
|
||||
private val AABB_COLOR = Color(0.9f, 0.3f, 0.9f)
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,13 @@ package ru.dbotthepony.kbox2d.dynamics
|
||||
import ru.dbotthepony.kbox2d.api.*
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
|
||||
open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
private var _world: IB2World? = world
|
||||
class Body(def: BodyDef, world: B2World) {
|
||||
private var _world: B2World? = world
|
||||
|
||||
final override val world: IB2World
|
||||
/**
|
||||
* Get the parent world of this body.
|
||||
*/
|
||||
val world: B2World
|
||||
get() = _world ?: throw IllegalStateException("Tried to use removed body")
|
||||
|
||||
internal var flags: Int = 0
|
||||
@ -43,21 +46,37 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
|
||||
internal val sweep: Sweep = Sweep()
|
||||
|
||||
final override val transform: Transform = Transform(def.position, def.angle)
|
||||
/**
|
||||
* Get the body transform for the body's origin.
|
||||
* @return the world transform of the body's origin.
|
||||
*/
|
||||
val transform: Transform = Transform(def.position, def.angle)
|
||||
val xf by this::transform
|
||||
|
||||
override val position: Vector2d
|
||||
/**
|
||||
* Get the world body origin position.
|
||||
* @return the world position of the body's origin.
|
||||
*/
|
||||
val position: Vector2d
|
||||
get() = transform.position
|
||||
override val angle: Double
|
||||
|
||||
/**
|
||||
* Get the angle in radians.
|
||||
* @return the current world rotation angle in radians.
|
||||
*/
|
||||
val angle: Double
|
||||
get() = sweep.a
|
||||
|
||||
override val userData: Any? = def.userData
|
||||
val userData: Any? = def.userData
|
||||
|
||||
internal var sleepTime: Double = 0.0
|
||||
internal var torque: Double = 0.0
|
||||
internal var force: Vector2d = Vector2d.ZERO
|
||||
|
||||
override var linearVelocity: Vector2d = def.linearVelocity
|
||||
/**
|
||||
* The linear velocity of the body's origin in world co-ordinates.
|
||||
*/
|
||||
var linearVelocity: Vector2d = def.linearVelocity
|
||||
set(value) {
|
||||
if (type == BodyType.STATIC)
|
||||
return
|
||||
@ -68,7 +87,10 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
field = value
|
||||
}
|
||||
|
||||
override var angularVelocity: Double = def.angularVelocity
|
||||
/**
|
||||
* The angular velocity of the body.
|
||||
*/
|
||||
var angularVelocity: Double = def.angularVelocity
|
||||
set(value) {
|
||||
if (type == BodyType.STATIC)
|
||||
return
|
||||
@ -79,27 +101,95 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
field = value
|
||||
}
|
||||
|
||||
override var fixtureList: Fixture? = null
|
||||
var fixtureList: Fixture? = null
|
||||
protected set
|
||||
|
||||
protected var fixtureCount: Int = 0
|
||||
var fixtureCount: Int = 0
|
||||
private set
|
||||
|
||||
override var jointList: JointEdge? = null
|
||||
internal set
|
||||
override var contactEdge: ContactEdge? = null
|
||||
/**
|
||||
* Get the list of all joints attached to this body.
|
||||
*
|
||||
* Kotlin: This is not a list, but, as in C++ impl, a custom
|
||||
* linked list.
|
||||
*/
|
||||
var jointList: JointEdge? = null
|
||||
internal set
|
||||
|
||||
override var next: IBody? = null
|
||||
internal set
|
||||
override var prev: IBody? = null
|
||||
/**
|
||||
* Get the list of all contacts attached to this body.
|
||||
* @warning this list changes during the time step and you may
|
||||
* miss some collisions if you don't use b2ContactListener.
|
||||
*
|
||||
* Kotlin: This is not a list, but, as in C++ impl, a custom
|
||||
* linked list.
|
||||
*/
|
||||
var contactEdge: ContactEdge? = null
|
||||
internal set
|
||||
|
||||
override var linearDamping: Double = def.linearDamping
|
||||
override var angularDamping: Double = def.angularDamping
|
||||
override var gravityScale: Double = def.gravityScale
|
||||
val contactEdgeIterator: Iterator<ContactEdge> get() {
|
||||
return object : Iterator<ContactEdge> {
|
||||
private var node = contactEdge
|
||||
|
||||
override var mass: Double = 0.0
|
||||
protected set
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): ContactEdge {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jointIterator: Iterator<JointEdge> get() {
|
||||
return object : Iterator<JointEdge> {
|
||||
private var node = jointList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): JointEdge {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var next: Body? = null
|
||||
internal set
|
||||
var prev: Body? = null
|
||||
internal set
|
||||
|
||||
val fixtureIterator: Iterator<Fixture> get() {
|
||||
return object : Iterator<Fixture> {
|
||||
private var node = fixtureList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): Fixture {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var linearDamping: Double = def.linearDamping
|
||||
var angularDamping: Double = def.angularDamping
|
||||
var gravityScale: Double = def.gravityScale
|
||||
|
||||
/**
|
||||
* Get the total mass of the body.
|
||||
* @return the mass, usually in kilograms (kg).
|
||||
*/
|
||||
var mass: Double = 0.0
|
||||
private set
|
||||
|
||||
internal var invMass: Double = 0.0
|
||||
|
||||
@ -116,11 +206,19 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
internal var I by this::rotInertia
|
||||
internal var invI by this::rotInertiaInv
|
||||
|
||||
override var isBullet: Boolean
|
||||
/**
|
||||
* Should this body be treated like a bullet for continuous collision detection?
|
||||
*/
|
||||
var isBullet: Boolean
|
||||
get() = BodyFlags.BULLET.isit(flags)
|
||||
set(value) { flags = BodyFlags.BULLET.update(flags, value) }
|
||||
|
||||
override var isAwake: Boolean
|
||||
/**
|
||||
* Set the sleep state of the body. A sleeping body has very
|
||||
* low CPU cost.
|
||||
* @param flag set to true to wake the body, false to put it to sleep.
|
||||
*/
|
||||
var isAwake: Boolean
|
||||
get() = BodyFlags.AWAKE.isit(flags)
|
||||
set(value) {
|
||||
if (type == BodyType.STATIC || BodyFlags.AWAKE.isit(flags) == value)
|
||||
@ -139,7 +237,27 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override var isEnabled: Boolean
|
||||
/**
|
||||
* Allow a body to be disabled. A disabled body is not simulated and cannot
|
||||
* be collided with or woken up.
|
||||
*
|
||||
* If you pass a flag of true, all fixtures will be added to the broad-phase.
|
||||
*
|
||||
* If you pass a flag of false, all fixtures will be removed from the
|
||||
* broad-phase and all contacts will be destroyed.
|
||||
*
|
||||
* Fixtures and joints are otherwise unaffected. You may continue
|
||||
* to create/destroy fixtures and joints on disabled bodies.
|
||||
*
|
||||
* Fixtures on a disabled body are implicitly disabled and will
|
||||
* not participate in collisions, ray-casts, or queries.
|
||||
*
|
||||
* Joints connected to a disabled body are implicitly disabled.
|
||||
*
|
||||
* An diabled body is still owned by a b2World object and remains
|
||||
* in the body list.
|
||||
*/
|
||||
var isEnabled: Boolean
|
||||
get() = BodyFlags.ENABLED.isit(flags)
|
||||
set(value) {
|
||||
if (world.isLocked)
|
||||
@ -178,7 +296,10 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override var isFixedRotation: Boolean
|
||||
/**
|
||||
* Set this body to have fixed rotation. This causes the mass to be reset.
|
||||
*/
|
||||
var isFixedRotation: Boolean
|
||||
get() = BodyFlags.FIXED_ROTATION.isit(flags)
|
||||
set(value) {
|
||||
if (value == isFixedRotation)
|
||||
@ -190,7 +311,11 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
resetMassData()
|
||||
}
|
||||
|
||||
override var allowAutoSleep: Boolean
|
||||
/**
|
||||
* You can disable sleeping on this body. If you disable sleeping, the
|
||||
* body will be woken.
|
||||
*/
|
||||
var allowAutoSleep: Boolean
|
||||
get() = BodyFlags.AUTO_SLEEP.isit(flags)
|
||||
set(value) {
|
||||
flags = BodyFlags.AUTO_SLEEP.update(flags, value)
|
||||
@ -200,7 +325,10 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override var type: BodyType = def.type
|
||||
/**
|
||||
* Set the type of this body. This may alter the mass and velocity.
|
||||
*/
|
||||
var type: BodyType = def.type
|
||||
set(value) {
|
||||
if (world.isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
@ -234,7 +362,7 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
contactEdge = null
|
||||
|
||||
val broadPhase = world.contactManager.broadPhase
|
||||
var f: IFixture? = fixtureList
|
||||
var f: Fixture? = fixtureList
|
||||
|
||||
while (f != null) {
|
||||
for (proxy in f.proxies) {
|
||||
@ -245,7 +373,15 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override fun createFixture(def: FixtureDef): IFixture {
|
||||
/** Creates a fixture and attach it to this body. Use this function if you need
|
||||
* to set some fixture parameters, like friction. Otherwise you can create the
|
||||
* fixture directly from a shape.
|
||||
* If the density is non-zero, this function automatically updates the mass of the body.
|
||||
* Contacts are not created until the next time step.
|
||||
* @param def the fixture definition.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun createFixture(def: FixtureDef): Fixture {
|
||||
if (world.isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
|
||||
@ -271,21 +407,48 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
return fixture
|
||||
}
|
||||
|
||||
override fun destroyFixture(fixture: IFixture) {
|
||||
/**
|
||||
* Creates a fixture from a shape and attach it to this body.
|
||||
* This is a convenience function. Use b2FixtureDef if you need to set parameters
|
||||
* like friction, restitution, user data, or filtering.
|
||||
* If the density is non-zero, this function automatically updates the mass of the body.
|
||||
* @param shape the shape to be cloned.
|
||||
* @param density the shape density (set to zero for static bodies).
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun createFixture(shape: IShape<*>, density: Double): Fixture {
|
||||
return createFixture(
|
||||
FixtureDef(
|
||||
shape = shape,
|
||||
density = density
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a fixture. This removes the fixture from the broad-phase and
|
||||
* destroys all contacts associated with this fixture. This will
|
||||
* automatically adjust the mass of the body if the body is dynamic and the
|
||||
* fixture has positive density.
|
||||
* All fixtures attached to a body are implicitly destroyed when the body is destroyed.
|
||||
* @param fixture the fixture to be removed.
|
||||
* @warning This function is locked during callbacks.
|
||||
*/
|
||||
fun destroyFixture(fixture: Fixture) {
|
||||
if (world.isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
|
||||
require(fixture.body == this) { "$fixture does not belong to $this (belongs to ${fixture.body})" }
|
||||
check(fixtureCount > 0) { "Having no tracked fixtures, but $fixture belongs to us" }
|
||||
|
||||
var node: IFixture? = fixtureList
|
||||
var node: Fixture? = fixtureList
|
||||
var found = false
|
||||
var previous: IFixture? = null
|
||||
var previous: Fixture? = null
|
||||
|
||||
while (node != null) {
|
||||
if (node == fixture) {
|
||||
// TODO: Это должно работать
|
||||
(previous as Fixture?)?.next = node.next
|
||||
previous?.next = node.next
|
||||
found = true
|
||||
break
|
||||
} else {
|
||||
@ -330,7 +493,12 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetMassData() {
|
||||
/**
|
||||
* This resets the mass properties to the sum of the mass properties of the fixtures.
|
||||
* This normally does not need to be called unless you called SetMassData to override
|
||||
* the mass and you later want to reset the mass.
|
||||
*/
|
||||
fun resetMassData() {
|
||||
// Compute mass data from shapes. Each shape has its own density.
|
||||
mass = 0.0
|
||||
invMass = 0.0
|
||||
@ -387,7 +555,16 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
linearVelocity += b2Cross(angularVelocity, sweep.c - oldCenter)
|
||||
}
|
||||
|
||||
override var massData: MassData
|
||||
/**
|
||||
* Get the mass data of the body.
|
||||
* @return a struct containing the mass, inertia and center of the body.
|
||||
* Set the mass properties to override the mass properties of the fixtures.
|
||||
* Note that this changes the center of mass position.
|
||||
* Note that creating or destroying fixtures can also alter the mass.
|
||||
* This function has no effect if the body isn't dynamic.
|
||||
* @param data the mass properties.
|
||||
*/
|
||||
var massData: MassData
|
||||
get() = MassData(
|
||||
mass = mass,
|
||||
inertia = inertia,
|
||||
@ -428,7 +605,7 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
linearVelocity += b2Cross(angularVelocity, sweep.c - oldCenter)
|
||||
}
|
||||
|
||||
internal fun shouldCollide(other: IBody): Boolean {
|
||||
internal fun shouldCollide(other: Body): Boolean {
|
||||
// At least one body should be dynamic.
|
||||
if (type != BodyType.DYNAMIC && other.type != BodyType.DYNAMIC)
|
||||
return false
|
||||
@ -454,7 +631,14 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
sweep.a = def.angle
|
||||
}
|
||||
|
||||
override fun setTransform(position: Vector2d, angle: Double) {
|
||||
/**
|
||||
* Set the position of the body's origin and rotation.
|
||||
* Manipulating a body's transform may cause non-physical behavior.
|
||||
* Note: contacts are updated on the next call to b2World::Step.
|
||||
* @param position the world position of the body's local origin.
|
||||
* @param angle the world rotation in radians.
|
||||
*/
|
||||
fun setTransform(position: Vector2d, angle: Double) {
|
||||
if (world.isLocked)
|
||||
throw ConcurrentModificationException()
|
||||
|
||||
@ -477,40 +661,88 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
world.notifyNewContacts()
|
||||
}
|
||||
|
||||
override val inertia: Double
|
||||
/**
|
||||
* Get the rotational inertia of the body about the local origin.
|
||||
* @return the rotational inertia, usually in kg-m^2.
|
||||
*/
|
||||
val inertia: Double
|
||||
get() = rotInertia + mass * sweep.localCenter.dot(sweep.localCenter)
|
||||
|
||||
override val localCenter: Vector2d
|
||||
/**
|
||||
* Get the local position of the center of mass.
|
||||
*/
|
||||
val localCenter: Vector2d
|
||||
get() = sweep.localCenter
|
||||
|
||||
override val worldCenter: Vector2d
|
||||
/**
|
||||
* Get the world position of the center of mass.
|
||||
*/
|
||||
val worldCenter: Vector2d
|
||||
get() = sweep.c
|
||||
|
||||
override fun getWorldPoint(localPoint: Vector2d): Vector2d {
|
||||
/**
|
||||
* Get the world coordinates of a point given the local coordinates.
|
||||
* @param localPoint a point on the body measured relative the the body's origin.
|
||||
* @return the same point expressed in world coordinates.
|
||||
*/
|
||||
fun getWorldPoint(localPoint: Vector2d): Vector2d {
|
||||
return transform.times(localPoint)
|
||||
}
|
||||
|
||||
override fun getWorldVector(localPoint: Vector2d): Vector2d {
|
||||
/**
|
||||
* Get the world coordinates of a vector given the local coordinates.
|
||||
* @param localVector a vector fixed in the body.
|
||||
* @return the same vector expressed in world coordinates.
|
||||
*/
|
||||
fun getWorldVector(localPoint: Vector2d): Vector2d {
|
||||
return transform.rotation.times(localPoint)
|
||||
}
|
||||
|
||||
override fun getLocalPoint(worldPoint: Vector2d): Vector2d {
|
||||
/**
|
||||
* Gets a local point relative to the body's origin given a world point.
|
||||
* @param worldPoint a point in world coordinates.
|
||||
* @return the corresponding local point relative to the body's origin.
|
||||
*/
|
||||
fun getLocalPoint(worldPoint: Vector2d): Vector2d {
|
||||
return transform.timesT(worldPoint)
|
||||
}
|
||||
|
||||
override fun getLocalVector(worldVector: Vector2d): Vector2d {
|
||||
/**
|
||||
* Gets a local vector given a world vector.
|
||||
* @param worldVector a vector in world coordinates.
|
||||
* @return the corresponding local vector.
|
||||
*/
|
||||
fun getLocalVector(worldVector: Vector2d): Vector2d {
|
||||
return transform.rotation.timesT(worldVector)
|
||||
}
|
||||
|
||||
override fun getLinearVelocityFromWorldPoint(worldPoint: Vector2d): Vector2d {
|
||||
/**
|
||||
* Get the world linear velocity of a world point attached to this body.
|
||||
* @param worldPoint a point in world coordinates.
|
||||
* @return the world velocity of a point.
|
||||
*/
|
||||
fun getLinearVelocityFromWorldPoint(worldPoint: Vector2d): Vector2d {
|
||||
return linearVelocity + b2Cross(angularVelocity, worldPoint - sweep.c)
|
||||
}
|
||||
|
||||
override fun getLinearVelocityFromLocalPoint(localPoint: Vector2d): Vector2d {
|
||||
/**
|
||||
* Get the world velocity of a local point.
|
||||
* @param localPoint a point in local coordinates.
|
||||
* @return the world velocity of a point.
|
||||
*/
|
||||
fun getLinearVelocityFromLocalPoint(localPoint: Vector2d): Vector2d {
|
||||
return getLinearVelocityFromWorldPoint(getWorldPoint(localPoint))
|
||||
}
|
||||
|
||||
override fun applyForce(force: Vector2d, point: Vector2d, wake: Boolean) {
|
||||
/**
|
||||
* Apply a force at a world point. If the force is not
|
||||
* applied at the center of mass, it will generate a torque and
|
||||
* affect the angular velocity. This wakes up the body.
|
||||
* @param force the world force vector, usually in Newtons (N).
|
||||
* @param point the world position of the point of application.
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyForce(force: Vector2d, point: Vector2d, wake: Boolean) {
|
||||
if (type != BodyType.DYNAMIC)
|
||||
return
|
||||
|
||||
@ -524,7 +756,12 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyForceToCenter(force: Vector2d, wake: Boolean) {
|
||||
/**
|
||||
* Apply a force to the center of mass. This wakes up the body.
|
||||
* @param force the world force vector, usually in Newtons (N).
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyForceToCenter(force: Vector2d, wake: Boolean) {
|
||||
if (type != BodyType.DYNAMIC)
|
||||
return
|
||||
|
||||
@ -537,7 +774,13 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyTorque(torque: Double, wake: Boolean) {
|
||||
/**
|
||||
* Apply a torque. This affects the angular velocity
|
||||
* without affecting the linear velocity of the center of mass.
|
||||
* @param torque about the z-axis (out of the screen), usually in N-m.
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyTorque(torque: Double, wake: Boolean) {
|
||||
if (type != BodyType.DYNAMIC)
|
||||
return
|
||||
|
||||
@ -549,7 +792,15 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyLinearImpulse(impulse: Vector2d, point: Vector2d, wake: Boolean) {
|
||||
/**
|
||||
* Apply an impulse at a point. This immediately modifies the velocity.
|
||||
* It also modifies the angular velocity if the point of application
|
||||
* is not at the center of mass. This wakes up the body.
|
||||
* @param impulse the world impulse vector, usually in N-seconds or kg-m/s.
|
||||
* @param point the world position of the point of application.
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyLinearImpulse(impulse: Vector2d, point: Vector2d, wake: Boolean) {
|
||||
if (type != BodyType.DYNAMIC)
|
||||
return
|
||||
|
||||
@ -562,7 +813,12 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyLinearImpulseToCenter(impulse: Vector2d, wake: Boolean) {
|
||||
/**
|
||||
* Apply an impulse to the center of mass. This immediately modifies the velocity.
|
||||
* @param impulse the world impulse vector, usually in N-seconds or kg-m/s.
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyLinearImpulseToCenter(impulse: Vector2d, wake: Boolean) {
|
||||
if (type != BodyType.DYNAMIC)
|
||||
return
|
||||
|
||||
@ -574,7 +830,12 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyAngularImpulse(impulse: Double, wake: Boolean) {
|
||||
/**
|
||||
* Apply an angular impulse.
|
||||
* @param impulse the angular impulse in units of kg*m*m/s
|
||||
* @param wake also wake up the body
|
||||
*/
|
||||
fun applyAngularImpulse(impulse: Double, wake: Boolean) {
|
||||
if (type != BodyType.DYNAMIC)
|
||||
return
|
||||
|
||||
@ -616,7 +877,7 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
||||
}
|
||||
}
|
||||
|
||||
override fun dump() {
|
||||
fun dump() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
@ -4,18 +4,35 @@ import ru.dbotthepony.kbox2d.api.*
|
||||
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
||||
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
|
||||
|
||||
class ContactManager : IContactManager {
|
||||
override val broadPhase: IBroadPhase = BroadPhase()
|
||||
override var contactList: IContact? = null
|
||||
override var contactCount: Int = 0
|
||||
/**
|
||||
* Delegate of b2World.
|
||||
*/
|
||||
class ContactManager {
|
||||
val broadPhase = BroadPhase()
|
||||
var contactList: AbstractContact? = null
|
||||
var contactCount: Int = 0
|
||||
private set
|
||||
|
||||
override var contactFilter: IContactFilter? = null
|
||||
override var contactListener: IContactListener? = null
|
||||
var contactFilter: IContactFilter? = null
|
||||
var contactListener: IContactListener? = null
|
||||
|
||||
override fun destroy(contact: IContact) {
|
||||
contact as AbstractContact
|
||||
val contactListIterator: Iterator<AbstractContact> get() {
|
||||
return object : Iterator<AbstractContact> {
|
||||
private var node = contactList
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return node != null
|
||||
}
|
||||
|
||||
override fun next(): AbstractContact {
|
||||
val old = node!!
|
||||
node = old.next
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun destroy(contact: AbstractContact) {
|
||||
val fixtureA = contact.fixtureA
|
||||
val fixtureB = contact.fixtureB
|
||||
val bodyA = fixtureA.body as Body
|
||||
@ -72,7 +89,7 @@ class ContactManager : IContactManager {
|
||||
* all the narrow phase collision is processed for the world
|
||||
* contact list.
|
||||
*/
|
||||
override fun collide() {
|
||||
fun collide() {
|
||||
// Update awake contacts.
|
||||
|
||||
for (c in contactListIterator) {
|
||||
@ -123,11 +140,11 @@ class ContactManager : IContactManager {
|
||||
}
|
||||
}
|
||||
|
||||
override fun findNewContacts() {
|
||||
fun findNewContacts() {
|
||||
broadPhase.updatePairs(this::addPair)
|
||||
}
|
||||
|
||||
override fun addPair(proxyUserDataA: Any?, proxyUserDataB: Any?) {
|
||||
fun addPair(proxyUserDataA: Any?, proxyUserDataB: Any?) {
|
||||
val proxyA = proxyUserDataA as FixtureProxy
|
||||
val proxyB = proxyUserDataB as FixtureProxy
|
||||
|
||||
|
@ -1,28 +1,77 @@
|
||||
package ru.dbotthepony.kbox2d.dynamics
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.*
|
||||
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
open class Fixture(
|
||||
body: ru.dbotthepony.kbox2d.api.IBody,
|
||||
/**
|
||||
* A fixture is used to attach a shape to a body for collision detection. A fixture
|
||||
* inherits its transform from its parent. Fixtures hold additional non-geometric data
|
||||
* such as friction, collision filters, etc.
|
||||
* Fixtures are created via b2Body::CreateFixture.
|
||||
* @warning you cannot reuse fixtures.
|
||||
*/
|
||||
class Fixture(
|
||||
body: Body,
|
||||
def: FixtureDef
|
||||
) : IFixture {
|
||||
final override var body: ru.dbotthepony.kbox2d.api.IBody? = body
|
||||
protected set
|
||||
) {
|
||||
/**
|
||||
* Get the parent body of this fixture. This is nullptr if the fixture is not attached.
|
||||
* @return the parent body.
|
||||
*/
|
||||
var body: Body? = body
|
||||
private set
|
||||
|
||||
final override var next: IFixture? = null
|
||||
/**
|
||||
* Get the next fixture in the parent body's fixture list.
|
||||
* @return the next shape.
|
||||
*/
|
||||
var next: Fixture? = null
|
||||
|
||||
override val userData: Any? = def.userData
|
||||
override var friction: Double = def.friction
|
||||
override var restitution: Double = def.restitution
|
||||
override var restitutionThreshold: Double = def.restitutionThreshold
|
||||
/**
|
||||
* Get the user data that was assigned in the fixture definition. Use this to
|
||||
* store your application specific data.
|
||||
*/
|
||||
var userData: Any? = def.userData
|
||||
|
||||
override val type: IShape.Type get() = shape.type
|
||||
/**
|
||||
* Get the coefficient of friction.
|
||||
* Set the coefficient of friction. This will _not_ change the friction of
|
||||
* existing contacts.
|
||||
*/
|
||||
var friction: Double = def.friction
|
||||
|
||||
override var isSensor: Boolean = def.isSensor
|
||||
/**
|
||||
* Get the coefficient of restitution.
|
||||
* Set the coefficient of restitution. This will _not_ change the restitution of
|
||||
* existing contacts.
|
||||
*/
|
||||
var restitution: Double = def.restitution
|
||||
|
||||
/**
|
||||
* Get the restitution velocity threshold.
|
||||
* Set the restitution threshold. This will _not_ change the restitution threshold of
|
||||
* existing contacts.
|
||||
*/
|
||||
var restitutionThreshold: Double = def.restitutionThreshold
|
||||
|
||||
/**
|
||||
* Get the type of the child shape. You can use this to down cast to the concrete shape.
|
||||
* @return the shape type.
|
||||
*/
|
||||
val type: IShape.Type get() = shape.type
|
||||
|
||||
/**
|
||||
* Set if this fixture is a sensor.
|
||||
* Is this fixture a sensor (non-solid)?
|
||||
* @return the true if the shape is a sensor.
|
||||
*/
|
||||
var isSensor: Boolean = def.isSensor
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
checkNotNull(body) { "Already destroyed" }.isAwake = true
|
||||
@ -30,7 +79,12 @@ open class Fixture(
|
||||
}
|
||||
}
|
||||
|
||||
final override val shape: IShape<*> = requireNotNull(def.shape) { "null shape provided" }.copy()
|
||||
/**
|
||||
* Get the child shape. You can modify the child shape, however you should not change the
|
||||
* number of vertices because this will crash some collision caching mechanisms.
|
||||
* Manipulating the shape may lead to non-physical behavior.
|
||||
*/
|
||||
val shape: IShape<*> = requireNotNull(def.shape) { "null shape provided" }.copy()
|
||||
|
||||
init {
|
||||
if (shape is PolygonShape) {
|
||||
@ -42,10 +96,15 @@ open class Fixture(
|
||||
}
|
||||
}
|
||||
|
||||
protected val internalProxies = ArrayList<FixtureProxy>(shape.childCount)
|
||||
final override val proxies: List<FixtureProxy> = Collections.unmodifiableList(internalProxies)
|
||||
private val internalProxies = ArrayList<FixtureProxy>(shape.childCount)
|
||||
val proxies: List<FixtureProxy> = Collections.unmodifiableList(internalProxies)
|
||||
|
||||
override var density: Double = def.density
|
||||
/**
|
||||
* Set the density of this fixture. This will _not_ automatically adjust the mass
|
||||
* of the body. You must call b2Body::ResetMassData to update the body's mass.
|
||||
* Get the density of this fixture.
|
||||
*/
|
||||
var density: Double = def.density
|
||||
set(value) {
|
||||
require(value.isFinite()) { "Infinite density" }
|
||||
require(!value.isNaN()) { "NaN density" }
|
||||
@ -53,7 +112,12 @@ open class Fixture(
|
||||
field = value
|
||||
}
|
||||
|
||||
override var filter: IFilter = def.filter.immutable()
|
||||
/**
|
||||
* Set the contact filtering data. This will not update contacts until the next time
|
||||
* step when either parent body is active and awake.
|
||||
* This automatically calls Refilter.
|
||||
*/
|
||||
var filter: IFilter = def.filter.immutable()
|
||||
set(value) {
|
||||
if (value is ImmutableFilter)
|
||||
field = value
|
||||
@ -76,7 +140,7 @@ open class Fixture(
|
||||
/**
|
||||
* These support body activation/deactivation.
|
||||
*/
|
||||
internal fun createProxies(broadPhase: IBroadPhase, xf: Transform) {
|
||||
internal fun createProxies(broadPhase: BroadPhase, xf: Transform) {
|
||||
check(body != null) { "Already destroyed" }
|
||||
check(internalProxies.isEmpty()) { "Already having proxies" }
|
||||
|
||||
@ -97,7 +161,7 @@ open class Fixture(
|
||||
/**
|
||||
* These support body activation/deactivation.
|
||||
*/
|
||||
internal fun destroyProxies(broadPhase: IBroadPhase) {
|
||||
internal fun destroyProxies(broadPhase: BroadPhase) {
|
||||
check(body != null) { "Already destroyed" }
|
||||
|
||||
// Destroy proxies in the broad-phase.
|
||||
@ -108,7 +172,7 @@ open class Fixture(
|
||||
internalProxies.clear()
|
||||
}
|
||||
|
||||
internal fun synchronize(broadPhase: IBroadPhase, transform1: Transform, transform2: Transform) {
|
||||
internal fun synchronize(broadPhase: BroadPhase, transform1: Transform, transform2: Transform) {
|
||||
check(body != null) { "Already destroyed" }
|
||||
|
||||
for (proxy in internalProxies) {
|
||||
@ -121,7 +185,10 @@ open class Fixture(
|
||||
}
|
||||
}
|
||||
|
||||
override fun refilter() {
|
||||
/**
|
||||
* Call this if you want to establish collision that was previously disabled by b2ContactFilter::ShouldCollide.
|
||||
*/
|
||||
fun refilter() {
|
||||
val body = body
|
||||
check(body != null) { "Already destroyed" }
|
||||
var edge = body.contactEdge
|
||||
@ -144,4 +211,45 @@ open class Fixture(
|
||||
broadPhase.touchProxy(proxy.proxyId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a point for containment in this fixture.
|
||||
* @param p a point in world coordinates.
|
||||
*/
|
||||
fun testPoint(p: Vector2d): Boolean {
|
||||
return shape.testPoint(checkNotNull(body) { "Tried to use detached fixture" }.transform, p)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a ray against this shape.
|
||||
* @param output the ray-cast results.
|
||||
* @param input the ray-cast input parameters.
|
||||
* @param childIndex the child shape index (e.g. edge index)
|
||||
*/
|
||||
fun rayCast(input: RayCastInput, childIndex: Int): RayCastOutput {
|
||||
return shape.rayCast(input, checkNotNull(body) { "Tried to use detached fixture" }.transform, childIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mass data for this fixture. The mass data is based on the density and
|
||||
* the shape. The rotational inertia is about the shape's origin. This operation
|
||||
* may be expensive.
|
||||
*/
|
||||
fun getMassData(): MassData {
|
||||
return shape.computeMass(density)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fixture's AABB. This AABB may be enlarge and/or stale.
|
||||
* If you need a more accurate AABB, compute it using the shape and
|
||||
* the body transform.
|
||||
*/
|
||||
fun getAABB(childIndex: Int): AABB {
|
||||
return proxies[childIndex].aabb
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump this fixture to the log file.
|
||||
*/
|
||||
fun dump(childIndex: Int) { }
|
||||
}
|
||||
|
@ -4,30 +4,61 @@ import ru.dbotthepony.kbox2d.api.*
|
||||
import ru.dbotthepony.kbox2d.collision.WorldManifold
|
||||
import ru.dbotthepony.kbox2d.collision.b2TestOverlap
|
||||
import ru.dbotthepony.kbox2d.dynamics.Body
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
fun interface ContactFactory {
|
||||
fun factorize(fixtureA: IFixture, childIndexA: Int, fixtureB: IFixture, childIndexB: Int): AbstractContact
|
||||
fun factorize(fixtureA: Fixture, childIndexA: Int, fixtureB: Fixture, childIndexB: Int): AbstractContact
|
||||
}
|
||||
|
||||
/**
|
||||
* The class manages contact between two shapes. A contact exists for each overlapping
|
||||
* AABB in the broad-phase (except if filtered). Therefore a contact object may exist
|
||||
* that has no contact points.
|
||||
*/
|
||||
sealed class AbstractContact(
|
||||
final override val fixtureA: IFixture,
|
||||
final override val childIndexA: Int,
|
||||
final override val fixtureB: IFixture,
|
||||
final override val childIndexB: Int,
|
||||
) : IContact {
|
||||
/**
|
||||
* Get fixture A in this contact.
|
||||
*/
|
||||
val fixtureA: Fixture,
|
||||
|
||||
/**
|
||||
* Get the child primitive index for fixture A.
|
||||
*/
|
||||
val childIndexA: Int,
|
||||
|
||||
/**
|
||||
* Get fixture B in this contact.
|
||||
*/
|
||||
val fixtureB: Fixture,
|
||||
|
||||
/**
|
||||
* Get the child primitive index for fixture B.
|
||||
*/
|
||||
val childIndexB: Int,
|
||||
) {
|
||||
internal var flags: Int = ContactFlags.ENABLED.bitmask
|
||||
internal var isOnIsland = false
|
||||
internal var toiFlag = false
|
||||
|
||||
final override var manifold: Manifold = Manifold()
|
||||
/**
|
||||
* Get the contact manifold. Do not modify the manifold unless you understand the
|
||||
* internals of Box2D.
|
||||
*/
|
||||
var manifold: Manifold = Manifold()
|
||||
private set
|
||||
|
||||
override var next: IContact? = null
|
||||
/**
|
||||
* Get the next contact in the world's contact list.
|
||||
*/
|
||||
var next: AbstractContact? = null
|
||||
internal set
|
||||
|
||||
override var prev: IContact? = null
|
||||
/**
|
||||
* Get the previous contact in the world's contact list.
|
||||
*/
|
||||
var prev: AbstractContact? = null
|
||||
internal set
|
||||
|
||||
internal var isFlaggedForFiltering: Boolean
|
||||
@ -51,15 +82,44 @@ sealed class AbstractContact(
|
||||
bodyB.contactEdge = nodeB
|
||||
}
|
||||
|
||||
override var friction: Double = b2MixFriction(fixtureA.friction, fixtureB.friction)
|
||||
override var restitution: Double = b2MixRestitution(fixtureA.restitution, fixtureB.restitution)
|
||||
override var restitutionThreshold: Double = b2MixRestitutionThreshold(fixtureA.restitutionThreshold, fixtureB.restitutionThreshold)
|
||||
override var tangentSpeed: Double = 0.0
|
||||
/**
|
||||
* Override the default friction mixture. You can call this in b2ContactListener::PreSolve.
|
||||
* This value persists until set or reset.
|
||||
* Get the friction.
|
||||
*/
|
||||
var friction: Double = b2MixFriction(fixtureA.friction, fixtureB.friction)
|
||||
|
||||
/**
|
||||
* Override the default restitution mixture. You can call this in b2ContactListener::PreSolve.
|
||||
* The value persists until you set or reset.
|
||||
*/
|
||||
var restitution: Double = b2MixRestitution(fixtureA.restitution, fixtureB.restitution)
|
||||
|
||||
/**
|
||||
* Override the default restitution velocity threshold mixture. You can call this in b2ContactListener::PreSolve.
|
||||
* The value persists until you set or reset.
|
||||
* Get the restitution threshold.
|
||||
*/
|
||||
var restitutionThreshold: Double = b2MixRestitutionThreshold(fixtureA.restitutionThreshold, fixtureB.restitutionThreshold)
|
||||
|
||||
/**
|
||||
* Set the desired tangent speed for a conveyor belt behavior. In meters per second.
|
||||
* Get the desired tangent speed. In meters per second.
|
||||
*/
|
||||
var tangentSpeed: Double = 0.0
|
||||
|
||||
internal var toiCount: Int = 0
|
||||
internal var toi: Double = 0.0
|
||||
|
||||
override val worldManifold: IWorldManifold get() {
|
||||
/**
|
||||
* Evaluate this contact with your own manifold and transforms.
|
||||
*/
|
||||
abstract fun evaluate(xfA: Transform, xfB: Transform): Manifold
|
||||
|
||||
/**
|
||||
* Get the world manifold.
|
||||
*/
|
||||
val worldManifold: IWorldManifold get() {
|
||||
val bodyA = checkNotNull(fixtureA.body) { "FixtureA has no body attached" }
|
||||
val bodyB = checkNotNull(fixtureB.body) { "FixtureB has no body attached" }
|
||||
|
||||
@ -69,15 +129,23 @@ sealed class AbstractContact(
|
||||
return WorldManifold(manifold, bodyA.transform, shapeA.radius, bodyB.transform, shapeB.radius)
|
||||
}
|
||||
|
||||
override var isTouching: Boolean
|
||||
/**
|
||||
* Is this contact touching?
|
||||
*/
|
||||
var isTouching: Boolean
|
||||
get() = (flags and ContactFlags.TOUCHING.bitmask) == ContactFlags.TOUCHING.bitmask
|
||||
internal set(value) { flags = ContactFlags.TOUCHING.update(flags, value) }
|
||||
|
||||
override var isEnabled: Boolean
|
||||
/**
|
||||
* Enable/disable this contact. This can be used inside the pre-solve
|
||||
* contact listener. The contact is only disabled for the current
|
||||
* time step (or sub-step in continuous collisions).
|
||||
*/
|
||||
var isEnabled: Boolean
|
||||
get() = (flags and ContactFlags.ENABLED.bitmask) == ContactFlags.ENABLED.bitmask
|
||||
set(value) { flags = ContactFlags.ENABLED.update(flags, value) }
|
||||
|
||||
override fun flagForFiltering() {
|
||||
fun flagForFiltering() {
|
||||
flags = flags or ContactFlags.FILTER.bitmask
|
||||
}
|
||||
|
||||
@ -159,6 +227,27 @@ sealed class AbstractContact(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the friction mixture to the default value.
|
||||
*/
|
||||
fun resetFriction() {
|
||||
friction = b2MixFriction(fixtureA.friction, fixtureB.friction)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the restitution to the default value.
|
||||
*/
|
||||
fun resetRestitution() {
|
||||
restitution = b2MixRestitution(fixtureA.restitution, fixtureB.restitution)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the restitution threshold to the default value.
|
||||
*/
|
||||
fun resetRestitutionThreshold() {
|
||||
restitutionThreshold = b2MixRestitutionThreshold(fixtureA.restitutionThreshold, fixtureB.restitutionThreshold)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val registry =
|
||||
EnumMap<IShape.Type, EnumMap<IShape.Type, ContactFactory>>(IShape.Type::class.java)
|
||||
@ -168,9 +257,9 @@ sealed class AbstractContact(
|
||||
}
|
||||
|
||||
internal fun create(
|
||||
fixtureA: IFixture,
|
||||
fixtureA: Fixture,
|
||||
indexA: Int,
|
||||
fixtureB: IFixture,
|
||||
fixtureB: Fixture,
|
||||
indexB: Int,
|
||||
): AbstractContact {
|
||||
val type1 = fixtureA.type
|
||||
@ -182,31 +271,31 @@ sealed class AbstractContact(
|
||||
}
|
||||
|
||||
init {
|
||||
register(IShape.Type.POLYGON, IShape.Type.POLYGON) { fixtureA: IFixture, _: Int, fixtureB: IFixture, _: Int ->
|
||||
register(IShape.Type.POLYGON, IShape.Type.POLYGON) { fixtureA: Fixture, _: Int, fixtureB: Fixture, _: Int ->
|
||||
return@register PolygonContact(fixtureA, fixtureB)
|
||||
}
|
||||
|
||||
register(IShape.Type.POLYGON, IShape.Type.CIRCLE) { fixtureA: IFixture, _: Int, fixtureB: IFixture, _: Int ->
|
||||
register(IShape.Type.POLYGON, IShape.Type.CIRCLE) { fixtureA: Fixture, _: Int, fixtureB: Fixture, _: Int ->
|
||||
return@register PolygonCircleContact(fixtureA, fixtureB)
|
||||
}
|
||||
|
||||
register(IShape.Type.CIRCLE, IShape.Type.CIRCLE) { fixtureA: IFixture, _: Int, fixtureB: IFixture, _: Int ->
|
||||
register(IShape.Type.CIRCLE, IShape.Type.CIRCLE) { fixtureA: Fixture, _: Int, fixtureB: Fixture, _: Int ->
|
||||
return@register CircleContact(fixtureA, fixtureB)
|
||||
}
|
||||
|
||||
register(IShape.Type.EDGE, IShape.Type.CIRCLE) { fixtureA: IFixture, _: Int, fixtureB: IFixture, _: Int ->
|
||||
register(IShape.Type.EDGE, IShape.Type.CIRCLE) { fixtureA: Fixture, _: Int, fixtureB: Fixture, _: Int ->
|
||||
return@register EdgeCircleContact(fixtureA, fixtureB)
|
||||
}
|
||||
|
||||
register(IShape.Type.EDGE, IShape.Type.POLYGON) { fixtureA: IFixture, _: Int, fixtureB: IFixture, _: Int ->
|
||||
register(IShape.Type.EDGE, IShape.Type.POLYGON) { fixtureA: Fixture, _: Int, fixtureB: Fixture, _: Int ->
|
||||
return@register EdgePolygonContact(fixtureA, fixtureB)
|
||||
}
|
||||
|
||||
register(IShape.Type.CHAIN, IShape.Type.POLYGON) { fixtureA: IFixture, indexA: Int, fixtureB: IFixture, indexB: Int ->
|
||||
register(IShape.Type.CHAIN, IShape.Type.POLYGON) { fixtureA: Fixture, indexA: Int, fixtureB: Fixture, indexB: Int ->
|
||||
return@register ChainPolygonContact(fixtureA, indexA, fixtureB, indexB)
|
||||
}
|
||||
|
||||
register(IShape.Type.CHAIN, IShape.Type.CIRCLE) { fixtureA: IFixture, indexA: Int, fixtureB: IFixture, indexB: Int ->
|
||||
register(IShape.Type.CHAIN, IShape.Type.CIRCLE) { fixtureA: Fixture, indexA: Int, fixtureB: Fixture, indexB: Int ->
|
||||
return@register ChainCircleContact(fixtureA, indexA, fixtureB, indexB)
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.IFixture
|
||||
import ru.dbotthepony.kbox2d.api.IShape
|
||||
import ru.dbotthepony.kbox2d.api.Manifold
|
||||
import ru.dbotthepony.kbox2d.api.Transform
|
||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndCircle
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
|
||||
class ChainCircleContact(
|
||||
fixtureA: IFixture,
|
||||
fixtureA: Fixture,
|
||||
childIndexA: Int,
|
||||
fixtureB: IFixture,
|
||||
fixtureB: Fixture,
|
||||
childIndexB: Int,
|
||||
) : AbstractContact(fixtureA, childIndexA, fixtureB, childIndexB) {
|
||||
init {
|
||||
|
@ -1,17 +1,17 @@
|
||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.IFixture
|
||||
import ru.dbotthepony.kbox2d.api.IShape
|
||||
import ru.dbotthepony.kbox2d.api.Manifold
|
||||
import ru.dbotthepony.kbox2d.api.Transform
|
||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndPolygon
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
|
||||
class ChainPolygonContact(
|
||||
fixtureA: IFixture,
|
||||
fixtureA: Fixture,
|
||||
childIndexA: Int,
|
||||
fixtureB: IFixture,
|
||||
fixtureB: Fixture,
|
||||
childIndexB: Int,
|
||||
) : AbstractContact(fixtureA, childIndexA, fixtureB, childIndexB) {
|
||||
init {
|
||||
|
@ -1,15 +1,15 @@
|
||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.IFixture
|
||||
import ru.dbotthepony.kbox2d.api.IShape
|
||||
import ru.dbotthepony.kbox2d.api.Manifold
|
||||
import ru.dbotthepony.kbox2d.api.Transform
|
||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideCircles
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
|
||||
class CircleContact(
|
||||
fixtureA: IFixture,
|
||||
fixtureB: IFixture,
|
||||
fixtureA: Fixture,
|
||||
fixtureB: Fixture,
|
||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||
init {
|
||||
require(fixtureA.type == IShape.Type.CIRCLE) { "Fixture A is of type ${fixtureA.type}" }
|
||||
|
@ -1,16 +1,16 @@
|
||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.IFixture
|
||||
import ru.dbotthepony.kbox2d.api.IShape
|
||||
import ru.dbotthepony.kbox2d.api.Manifold
|
||||
import ru.dbotthepony.kbox2d.api.Transform
|
||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndCircle
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.EdgeShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
|
||||
class EdgeCircleContact(
|
||||
fixtureA: IFixture,
|
||||
fixtureB: IFixture,
|
||||
fixtureA: Fixture,
|
||||
fixtureB: Fixture,
|
||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||
init {
|
||||
require(fixtureA.type == IShape.Type.EDGE) { "Fixture A is of type ${fixtureA.type}, expected EDGE" }
|
||||
|
@ -1,16 +1,16 @@
|
||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.IFixture
|
||||
import ru.dbotthepony.kbox2d.api.IShape
|
||||
import ru.dbotthepony.kbox2d.api.Manifold
|
||||
import ru.dbotthepony.kbox2d.api.Transform
|
||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndPolygon
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.EdgeShape
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
|
||||
class EdgePolygonContact(
|
||||
fixtureA: IFixture,
|
||||
fixtureB: IFixture,
|
||||
fixtureA: Fixture,
|
||||
fixtureB: Fixture,
|
||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||
init {
|
||||
require(fixtureA.type == IShape.Type.EDGE) { "Fixture A is of type ${fixtureA.type}, expected EDGE" }
|
||||
|
@ -1,16 +1,16 @@
|
||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.IFixture
|
||||
import ru.dbotthepony.kbox2d.api.IShape
|
||||
import ru.dbotthepony.kbox2d.api.Manifold
|
||||
import ru.dbotthepony.kbox2d.api.Transform
|
||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollidePolygonAndCircle
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
|
||||
class PolygonCircleContact(
|
||||
fixtureA: IFixture,
|
||||
fixtureB: IFixture,
|
||||
fixtureA: Fixture,
|
||||
fixtureB: Fixture,
|
||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||
init {
|
||||
require(fixtureA.type == IShape.Type.POLYGON) { "Fixture A is of type ${fixtureA.type}, expected POLYGON" }
|
||||
|
@ -1,15 +1,15 @@
|
||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||
|
||||
import ru.dbotthepony.kbox2d.api.IFixture
|
||||
import ru.dbotthepony.kbox2d.api.IShape
|
||||
import ru.dbotthepony.kbox2d.api.Manifold
|
||||
import ru.dbotthepony.kbox2d.api.Transform
|
||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollidePolygons
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||
|
||||
class PolygonContact(
|
||||
fixtureA: IFixture,
|
||||
fixtureB: IFixture,
|
||||
fixtureA: Fixture,
|
||||
fixtureB: Fixture,
|
||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||
init {
|
||||
require(fixtureA.type == IShape.Type.POLYGON) { "Fixture A has type of ${fixtureA.type}" }
|
||||
|
@ -9,7 +9,7 @@ fun interface JointFactory {
|
||||
fun factorize(jointDef: IJointDef): AbstractJoint
|
||||
}
|
||||
|
||||
sealed class AbstractJoint(def: IJointDef) : IJoint {
|
||||
sealed class AbstractJoint(def: IJointDef) : IMovable {
|
||||
init {
|
||||
require(def.bodyA != def.bodyB) { "Tried to create join on same body" }
|
||||
}
|
||||
@ -18,9 +18,28 @@ sealed class AbstractJoint(def: IJointDef) : IJoint {
|
||||
|
||||
protected var index: Int = 0
|
||||
|
||||
override val collideConnected: Boolean = def.collideConnected
|
||||
override val type: JointType = def.type
|
||||
override var userData: Any? = def.userData
|
||||
/**
|
||||
* Get collide connected.
|
||||
* Note: modifying the collide connect flag won't work correctly because
|
||||
* the flag is only checked when fixture AABBs begin to overlap.
|
||||
*/
|
||||
val collideConnected: Boolean = def.collideConnected
|
||||
|
||||
/**
|
||||
* Dump this joint to the log file.
|
||||
*/
|
||||
open fun dump() { }
|
||||
|
||||
/**
|
||||
* Debug draw this joint
|
||||
*/
|
||||
open fun draw(draw: IDebugDraw) { }
|
||||
|
||||
/**
|
||||
* Get the type of the concrete joint.
|
||||
*/
|
||||
val type: JointType = def.type
|
||||
var userData: Any? = def.userData
|
||||
|
||||
// KBox2D: In original code, nothing expects bodies to be null
|
||||
// but certain joints (notably, mouse joint) have no meaningful
|
||||
@ -31,8 +50,35 @@ sealed class AbstractJoint(def: IJointDef) : IJoint {
|
||||
protected var _bodyA: Body? = def.bodyA as Body?
|
||||
protected var _bodyB: Body? = def.bodyB as Body?
|
||||
|
||||
final override val bodyA: Body get() = checkNotNull(_bodyA) { "Body A is not present" }
|
||||
final override val bodyB: Body get() = checkNotNull(_bodyB) { "Body B is not present" }
|
||||
/**
|
||||
* Get the first body attached to this joint.
|
||||
*/
|
||||
val bodyA: Body get() = checkNotNull(_bodyA) { "Body A is not present" }
|
||||
|
||||
/**
|
||||
* Get the second body attached to this joint.
|
||||
*/
|
||||
val bodyB: Body get() = checkNotNull(_bodyB) { "Body B is not present" }
|
||||
|
||||
/**
|
||||
* Get the anchor point on bodyA in world coordinates.
|
||||
*/
|
||||
abstract val anchorA: Vector2d
|
||||
|
||||
/**
|
||||
* Get the anchor point on bodyB in world coordinates.
|
||||
*/
|
||||
abstract val anchorB: Vector2d
|
||||
|
||||
/**
|
||||
* Get the reaction force on bodyB at the joint anchor in Newtons.
|
||||
*/
|
||||
abstract fun getReactionForce(inv_dt: Double): Vector2d
|
||||
|
||||
/**
|
||||
* Get the reaction torque on bodyB in N*m.
|
||||
*/
|
||||
abstract fun getReactionTorque(inv_dt: Double): Double
|
||||
|
||||
val hasBodyA: Boolean get() = _bodyA != null
|
||||
val hasBodyB: Boolean get() = _bodyB != null
|
||||
@ -58,11 +104,26 @@ sealed class AbstractJoint(def: IJointDef) : IJoint {
|
||||
_bodyB?.jointList = edgeB
|
||||
}
|
||||
|
||||
final override var next: IJoint? = null
|
||||
/**
|
||||
* Get the next joint the world joint list.
|
||||
*
|
||||
* Kotlin: This is not a list, but, as in C++ impl, a custom
|
||||
* linked list.
|
||||
*/
|
||||
var next: AbstractJoint? = null
|
||||
internal set
|
||||
final override var prev: IJoint? = null
|
||||
var prev: AbstractJoint? = null
|
||||
internal set
|
||||
|
||||
fun destroy() {
|
||||
bodyA.world.destroyJoint(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Short-cut function to determine if either body is enabled.
|
||||
*/
|
||||
val isEnabled: Boolean get() = bodyA.isEnabled || bodyB.isEnabled
|
||||
|
||||
/**
|
||||
* Signals that this joint was destroyed, invalidate stuff
|
||||
* to fail-fast this object
|
||||
|
@ -8,6 +8,7 @@ import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||
import ru.dbotthepony.kbox2d.dynamics.B2World
|
||||
import ru.dbotthepony.kbox2d.dynamics.Body
|
||||
import ru.dbotthepony.kbox2d.dynamics.joint.MouseJoint
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.world.Chunk
|
||||
@ -53,7 +54,7 @@ fun main() {
|
||||
|
||||
ground.createFixture(groundPoly, 0.0)
|
||||
|
||||
val boxes = ArrayList<ru.dbotthepony.kbox2d.api.IBody>()
|
||||
val boxes = ArrayList<Body>()
|
||||
|
||||
/*run {
|
||||
val movingDef = BodyDef(
|
||||
|
Loading…
Reference in New Issue
Block a user