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()
|
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
|
package ru.dbotthepony.kbox2d.api
|
||||||
|
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Body
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
/// Friction mixing law. The idea is to allow either fixture to drive the friction to zero.
|
/// 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.
|
* nodes, one for each attached body.
|
||||||
*/
|
*/
|
||||||
data class ContactEdge(
|
data class ContactEdge(
|
||||||
val other: ru.dbotthepony.kbox2d.api.IBody, ///< provides quick access to the other body attached.
|
val other: Body, ///< provides quick access to the other body attached.
|
||||||
val contact: IContact, ///< the contact
|
val contact: AbstractContact, ///< the contact
|
||||||
var prev: ContactEdge? = null, ///< the previous contact edge in the body's contact list
|
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
|
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()
|
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
|
package ru.dbotthepony.kbox2d.api
|
||||||
|
|
||||||
|
import ru.dbotthepony.kbox2d.collision.DistanceProxy
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
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.
|
* Used to warm start b2Distance.
|
||||||
* Set count to zero on first call.
|
* Set count to zero on first call.
|
||||||
@ -38,8 +20,8 @@ data class SimplexCache(
|
|||||||
* in the computation. Even
|
* in the computation. Even
|
||||||
*/
|
*/
|
||||||
data class DistanceInput(
|
data class DistanceInput(
|
||||||
var proxyA: IDistanceProxy,
|
var proxyA: DistanceProxy,
|
||||||
var proxyB: IDistanceProxy,
|
var proxyB: DistanceProxy,
|
||||||
var transformA: Transform = Transform(),
|
var transformA: Transform = Transform(),
|
||||||
var transformB: Transform = Transform(),
|
var transformB: Transform = Transform(),
|
||||||
var useRadii: Boolean = false
|
var useRadii: Boolean = false
|
||||||
@ -57,8 +39,8 @@ data class DistanceOutput(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class ShapeCastInput(
|
data class ShapeCastInput(
|
||||||
var proxyA: IDistanceProxy,
|
var proxyA: DistanceProxy,
|
||||||
var proxyB: IDistanceProxy,
|
var proxyB: DistanceProxy,
|
||||||
var transformA: Transform,
|
var transformA: Transform,
|
||||||
var transformB: Transform,
|
var transformB: Transform,
|
||||||
var translationB: Vector2d,
|
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
|
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.util2d.AABB
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
|
|
||||||
@ -40,26 +42,38 @@ data class ImmutableFilter(
|
|||||||
) : IFilter
|
) : IFilter
|
||||||
|
|
||||||
data class FixtureDef(
|
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 shape: IShape<*>? = null,
|
||||||
|
|
||||||
var friction: Double = 0.2,
|
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,
|
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,
|
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,
|
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,
|
var isSensor: Boolean = false,
|
||||||
|
|
||||||
/// Use this to store application specific fixture data.
|
/**
|
||||||
|
* Use this to store application specific fixture data.
|
||||||
|
*/
|
||||||
var userData: Any? = null,
|
var userData: Any? = null,
|
||||||
|
|
||||||
val filter: Filter = Filter()
|
val filter: Filter = Filter()
|
||||||
@ -67,7 +81,7 @@ data class FixtureDef(
|
|||||||
|
|
||||||
data class FixtureProxy(
|
data class FixtureProxy(
|
||||||
var aabb: AABB,
|
var aabb: AABB,
|
||||||
val fixture: IFixture,
|
val fixture: Fixture,
|
||||||
val childIndex: Int,
|
val childIndex: Int,
|
||||||
) {
|
) {
|
||||||
private var setProxyID = false
|
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)")
|
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.util2d.AABB
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
|
import ru.dbotthepony.kbox2d.collision.DynamicTree
|
||||||
|
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
||||||
|
|
||||||
fun interface ProxyQueryCallback {
|
fun interface ProxyQueryCallback {
|
||||||
fun invoke(nodeId: Int, userData: Any?): Boolean
|
fun invoke(nodeId: Int, userData: Any?): Boolean
|
||||||
@ -11,33 +13,33 @@ fun interface ProxyRayCastCallback {
|
|||||||
fun invoke(subInput: RayCastInput, nodeId: Int, userData: Any?): Double
|
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
|
* Create a proxy with an initial AABB. Pairs are not reported until
|
||||||
* UpdatePairs is called.
|
* UpdatePairs is called.
|
||||||
*
|
*
|
||||||
* For [IDynamicTree]:
|
* For [DynamicTree]:
|
||||||
* Create a proxy. Provide a tight fitting AABB and a userData pointer.
|
* Create a proxy. Provide a tight fitting AABB and a userData pointer.
|
||||||
*/
|
*/
|
||||||
fun createProxy(aabb: AABB, userData: Any?): Int
|
fun createProxy(aabb: AABB, userData: Any?): Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For [IBroadPhase]:
|
* For [BroadPhase]:
|
||||||
* Destroy a proxy. It is up to the client to remove any pairs.
|
* 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.
|
* Destroy a proxy. This asserts if the id is invalid.
|
||||||
*/
|
*/
|
||||||
fun destroyProxy(proxyID: Int)
|
fun destroyProxy(proxyID: Int)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For [IBroadPhase]:
|
* For [BroadPhase]:
|
||||||
* Call MoveProxy as many times as you like, then when you are done
|
* Call MoveProxy as many times as you like, then when you are done
|
||||||
* call UpdatePairs to finalized the proxy pairs (for your time step).
|
* call UpdatePairs to finalized the proxy pairs (for your time step).
|
||||||
* @return true
|
* @return true
|
||||||
*
|
*
|
||||||
* For [IDynamicTree]:
|
* For [DynamicTree]:
|
||||||
* Move a proxy with a swepted AABB. If the proxy has moved outside of its fattened AABB,
|
* 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
|
* then the proxy is removed from the tree and re-inserted. Otherwise
|
||||||
* the function returns immediately.
|
* the function returns immediately.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.kbox2d.api
|
package ru.dbotthepony.kbox2d.api
|
||||||
|
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Body
|
||||||
import ru.dbotthepony.kbox2d.dynamics.joint.AbstractJoint
|
import ru.dbotthepony.kbox2d.dynamics.joint.AbstractJoint
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
@ -12,8 +13,8 @@ data class StiffnessResult(val stiffness: Double, val damping: Double)
|
|||||||
fun b2LinearStiffness(
|
fun b2LinearStiffness(
|
||||||
frequencyHertz: Double,
|
frequencyHertz: Double,
|
||||||
dampingRatio: Double,
|
dampingRatio: Double,
|
||||||
bodyA: IBody?,
|
bodyA: Body?,
|
||||||
bodyB: IBody?,
|
bodyB: Body?,
|
||||||
): StiffnessResult {
|
): StiffnessResult {
|
||||||
val massA = bodyA?.mass ?: 0.0
|
val massA = bodyA?.mass ?: 0.0
|
||||||
val massB = bodyB?.mass ?: 0.0
|
val massB = bodyB?.mass ?: 0.0
|
||||||
@ -41,8 +42,8 @@ fun b2LinearStiffness(
|
|||||||
fun b2AngularStiffness(
|
fun b2AngularStiffness(
|
||||||
frequencyHertz: Double,
|
frequencyHertz: Double,
|
||||||
dampingRatio: Double,
|
dampingRatio: Double,
|
||||||
bodyA: IBody?,
|
bodyA: Body?,
|
||||||
bodyB: IBody?,
|
bodyB: Body?,
|
||||||
): StiffnessResult {
|
): StiffnessResult {
|
||||||
val inertiaA = bodyA?.inertia ?: 0.0
|
val inertiaA = bodyA?.inertia ?: 0.0
|
||||||
val inertiaB = bodyB?.inertia ?: 0.0
|
val inertiaB = bodyB?.inertia ?: 0.0
|
||||||
@ -91,13 +92,13 @@ data class Jacobian(
|
|||||||
* nodes, one for each attached body.
|
* nodes, one for each attached body.
|
||||||
*/
|
*/
|
||||||
class JointEdge(
|
class JointEdge(
|
||||||
other: IBody?, ///< provides quick access to the other body attached.
|
other: Body?, ///< provides quick access to the other body attached.
|
||||||
val joint: IJoint, ///< the joint
|
val joint: AbstractJoint, ///< the joint
|
||||||
var prev: JointEdge? = null, ///< the previous joint edge in the body's joint list
|
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
|
var next: JointEdge? = null ///< the next joint edge in the body's joint list
|
||||||
) {
|
) {
|
||||||
val otherNullable: IBody? = other
|
val otherNullable: Body? = other
|
||||||
val other: IBody get() = checkNotNull(otherNullable) { "Other body is not present" }
|
val other: Body get() = checkNotNull(otherNullable) { "Other body is not present" }
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface IJointDef {
|
sealed interface IJointDef {
|
||||||
@ -109,12 +110,12 @@ sealed interface IJointDef {
|
|||||||
/**
|
/**
|
||||||
* The first attached body.
|
* The first attached body.
|
||||||
*/
|
*/
|
||||||
val bodyA: IBody?
|
val bodyA: Body?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The second attached body.
|
* The second attached body.
|
||||||
*/
|
*/
|
||||||
val bodyB: IBody?
|
val bodyB: Body?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this flag to true if the attached bodies should collide.
|
* Set this flag to true if the attached bodies should collide.
|
||||||
@ -128,8 +129,8 @@ sealed interface IJointDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DistanceJointDef(
|
class DistanceJointDef(
|
||||||
b1: IBody,
|
b1: Body,
|
||||||
b2: IBody,
|
b2: Body,
|
||||||
anchor1: Vector2d,
|
anchor1: Vector2d,
|
||||||
anchor2: Vector2d
|
anchor2: Vector2d
|
||||||
) : IJointDef {
|
) : IJointDef {
|
||||||
@ -160,8 +161,8 @@ class DistanceJointDef(
|
|||||||
*/
|
*/
|
||||||
val damping: Double = 0.0
|
val damping: Double = 0.0
|
||||||
|
|
||||||
override var bodyA: IBody = b1
|
override var bodyA: Body = b1
|
||||||
override var bodyB: IBody = b2
|
override var bodyB: Body = b2
|
||||||
|
|
||||||
override var collideConnected: Boolean = false
|
override var collideConnected: Boolean = false
|
||||||
override var userData: Any? = null
|
override var userData: Any? = null
|
||||||
@ -185,13 +186,13 @@ class DistanceJointDef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RevoluteJointDef(
|
class RevoluteJointDef(
|
||||||
b1: IBody,
|
b1: Body,
|
||||||
b2: IBody,
|
b2: Body,
|
||||||
anchor: Vector2d,
|
anchor: Vector2d,
|
||||||
) : IJointDef {
|
) : IJointDef {
|
||||||
override val type: JointType = JointType.REVOLUTE
|
override val type: JointType = JointType.REVOLUTE
|
||||||
override var bodyA: IBody = b1
|
override var bodyA: Body = b1
|
||||||
override var bodyB: IBody = b2
|
override var bodyB: Body = b2
|
||||||
override var collideConnected: Boolean = false
|
override var collideConnected: Boolean = false
|
||||||
override var userData: Any? = null
|
override var userData: Any? = null
|
||||||
|
|
||||||
@ -244,14 +245,14 @@ class RevoluteJointDef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PrismaticJointDef(
|
class PrismaticJointDef(
|
||||||
b1: IBody,
|
b1: Body,
|
||||||
b2: IBody,
|
b2: Body,
|
||||||
anchor: Vector2d,
|
anchor: Vector2d,
|
||||||
axis: Vector2d,
|
axis: Vector2d,
|
||||||
) : IJointDef {
|
) : IJointDef {
|
||||||
override val type: JointType = JointType.PRISMATIC
|
override val type: JointType = JointType.PRISMATIC
|
||||||
override var bodyA: IBody = b1
|
override var bodyA: Body = b1
|
||||||
override var bodyB: IBody = b2
|
override var bodyB: Body = b2
|
||||||
override var collideConnected: Boolean = false
|
override var collideConnected: Boolean = false
|
||||||
override var userData: Any? = null
|
override var userData: Any? = null
|
||||||
|
|
||||||
@ -311,8 +312,8 @@ class PrismaticJointDef(
|
|||||||
* two dynamic body anchor points, and a pulley ratio.
|
* two dynamic body anchor points, and a pulley ratio.
|
||||||
*/
|
*/
|
||||||
class PulleyJointDef(
|
class PulleyJointDef(
|
||||||
b1: IBody,
|
b1: Body,
|
||||||
b2: IBody,
|
b2: Body,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first ground anchor in world coordinates. This point never moves.
|
* The first ground anchor in world coordinates. This point never moves.
|
||||||
@ -329,8 +330,8 @@ class PulleyJointDef(
|
|||||||
ratio: Double,
|
ratio: Double,
|
||||||
) : IJointDef {
|
) : IJointDef {
|
||||||
override val type: JointType = JointType.PULLEY
|
override val type: JointType = JointType.PULLEY
|
||||||
override var bodyA: IBody = b1
|
override var bodyA: Body = b1
|
||||||
override var bodyB: IBody = b2
|
override var bodyB: Body = b2
|
||||||
override var collideConnected: Boolean = false
|
override var collideConnected: Boolean = false
|
||||||
override var userData: Any? = null
|
override var userData: Any? = null
|
||||||
|
|
||||||
@ -375,8 +376,8 @@ class PulleyJointDef(
|
|||||||
* @warning bodyB on the input joints must both be dynamic
|
* @warning bodyB on the input joints must both be dynamic
|
||||||
*/
|
*/
|
||||||
class GearJointDef(
|
class GearJointDef(
|
||||||
override var bodyA: IBody,
|
override var bodyA: Body,
|
||||||
override var bodyB: IBody,
|
override var bodyB: Body,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first revolute/prismatic joint attached to the gear joint.
|
* The first revolute/prismatic joint attached to the gear joint.
|
||||||
@ -416,8 +417,8 @@ class MouseJointDef(
|
|||||||
*/
|
*/
|
||||||
var target: Vector2d,
|
var target: Vector2d,
|
||||||
|
|
||||||
override var bodyB: IBody,
|
override var bodyB: Body,
|
||||||
override val bodyA: IBody? = null,
|
override val bodyA: Body? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum constraint force that can be exerted
|
* The maximum constraint force that can be exerted
|
||||||
@ -436,8 +437,8 @@ class MouseJointDef(
|
|||||||
fun linearStiffness(
|
fun linearStiffness(
|
||||||
frequencyHertz: Double,
|
frequencyHertz: Double,
|
||||||
dampingRatio: Double,
|
dampingRatio: Double,
|
||||||
bodyA: IBody?,
|
bodyA: Body?,
|
||||||
bodyB: IBody?
|
bodyB: Body?
|
||||||
): MouseJointDef {
|
): MouseJointDef {
|
||||||
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||||
this.stiffness = stiffness
|
this.stiffness = stiffness
|
||||||
@ -448,8 +449,8 @@ class MouseJointDef(
|
|||||||
fun angularStiffness(
|
fun angularStiffness(
|
||||||
frequencyHertz: Double,
|
frequencyHertz: Double,
|
||||||
dampingRatio: Double,
|
dampingRatio: Double,
|
||||||
bodyA: IBody?,
|
bodyA: Body?,
|
||||||
bodyB: IBody?
|
bodyB: Body?
|
||||||
): MouseJointDef {
|
): MouseJointDef {
|
||||||
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||||
this.stiffness = stiffness
|
this.stiffness = stiffness
|
||||||
@ -467,8 +468,8 @@ class MouseJointDef(
|
|||||||
* anchors and a local axis helps when saving and loading a game.
|
* anchors and a local axis helps when saving and loading a game.
|
||||||
*/
|
*/
|
||||||
class WheelJointDef(
|
class WheelJointDef(
|
||||||
override var bodyA: IBody,
|
override var bodyA: Body,
|
||||||
override var bodyB: IBody,
|
override var bodyB: Body,
|
||||||
anchor: Vector2d,
|
anchor: Vector2d,
|
||||||
axis: Vector2d,
|
axis: Vector2d,
|
||||||
|
|
||||||
@ -534,8 +535,8 @@ class WheelJointDef(
|
|||||||
fun linearStiffness(
|
fun linearStiffness(
|
||||||
frequencyHertz: Double,
|
frequencyHertz: Double,
|
||||||
dampingRatio: Double,
|
dampingRatio: Double,
|
||||||
bodyA: IBody = this.bodyA,
|
bodyA: Body = this.bodyA,
|
||||||
bodyB: IBody = this.bodyB
|
bodyB: Body = this.bodyB
|
||||||
): WheelJointDef {
|
): WheelJointDef {
|
||||||
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||||
this.stiffness = stiffness
|
this.stiffness = stiffness
|
||||||
@ -546,8 +547,8 @@ class WheelJointDef(
|
|||||||
fun angularStiffness(
|
fun angularStiffness(
|
||||||
frequencyHertz: Double,
|
frequencyHertz: Double,
|
||||||
dampingRatio: Double,
|
dampingRatio: Double,
|
||||||
bodyA: IBody = this.bodyA,
|
bodyA: Body = this.bodyA,
|
||||||
bodyB: IBody = this.bodyB
|
bodyB: Body = this.bodyB
|
||||||
): WheelJointDef {
|
): WheelJointDef {
|
||||||
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||||
this.stiffness = stiffness
|
this.stiffness = stiffness
|
||||||
@ -562,8 +563,8 @@ class WheelJointDef(
|
|||||||
* of the anchor points is important for computing the reaction torque.
|
* of the anchor points is important for computing the reaction torque.
|
||||||
*/
|
*/
|
||||||
class WeldJointDef(
|
class WeldJointDef(
|
||||||
override var bodyA: IBody,
|
override var bodyA: Body,
|
||||||
override var bodyB: IBody,
|
override var bodyB: Body,
|
||||||
anchor: Vector2d,
|
anchor: Vector2d,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -598,8 +599,8 @@ class WeldJointDef(
|
|||||||
fun linearStiffness(
|
fun linearStiffness(
|
||||||
frequencyHertz: Double,
|
frequencyHertz: Double,
|
||||||
dampingRatio: Double,
|
dampingRatio: Double,
|
||||||
bodyA: IBody = this.bodyA,
|
bodyA: Body = this.bodyA,
|
||||||
bodyB: IBody = this.bodyB
|
bodyB: Body = this.bodyB
|
||||||
): WeldJointDef {
|
): WeldJointDef {
|
||||||
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
val (stiffness, damping) = b2LinearStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||||
this.stiffness = stiffness
|
this.stiffness = stiffness
|
||||||
@ -610,8 +611,8 @@ class WeldJointDef(
|
|||||||
fun angularStiffness(
|
fun angularStiffness(
|
||||||
frequencyHertz: Double,
|
frequencyHertz: Double,
|
||||||
dampingRatio: Double,
|
dampingRatio: Double,
|
||||||
bodyA: IBody = this.bodyA,
|
bodyA: Body = this.bodyA,
|
||||||
bodyB: IBody = this.bodyB
|
bodyB: Body = this.bodyB
|
||||||
): WeldJointDef {
|
): WeldJointDef {
|
||||||
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
val (stiffness, damping) = b2AngularStiffness(frequencyHertz, dampingRatio, bodyA, bodyB)
|
||||||
this.stiffness = stiffness
|
this.stiffness = stiffness
|
||||||
@ -624,8 +625,8 @@ class WeldJointDef(
|
|||||||
* Friction joint definition.
|
* Friction joint definition.
|
||||||
*/
|
*/
|
||||||
class FrictionJointDef(
|
class FrictionJointDef(
|
||||||
override var bodyA: IBody,
|
override var bodyA: Body,
|
||||||
override var bodyB: IBody,
|
override var bodyB: Body,
|
||||||
anchor: Vector2d,
|
anchor: Vector2d,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -655,8 +656,8 @@ class FrictionJointDef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MotorJointDef(
|
class MotorJointDef(
|
||||||
override var bodyA: IBody,
|
override var bodyA: Body,
|
||||||
override var bodyB: IBody,
|
override var bodyB: Body,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum motor force in N.
|
* The maximum motor force in N.
|
||||||
@ -692,77 +693,3 @@ class MotorJointDef(
|
|||||||
field = value
|
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,
|
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>> {
|
interface IShape<S : IShape<S>> {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
CIRCLE,
|
CIRCLE,
|
||||||
@ -20,41 +23,58 @@ interface IShape<S : IShape<S>> {
|
|||||||
CHAIN,
|
CHAIN,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clone the concrete shape.
|
/**
|
||||||
|
* Clone the concrete shape.
|
||||||
|
*/
|
||||||
fun copy(): S
|
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
|
val type: Type
|
||||||
|
|
||||||
/// Get the number of child primitives.
|
/**
|
||||||
|
* Get the number of child primitives.
|
||||||
|
*/
|
||||||
val childCount: Int
|
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 }
|
fun testPoint(transform: Transform, p: Vector2d): Boolean { return false }
|
||||||
|
|
||||||
/// Cast a ray against a child shape.
|
/**
|
||||||
/// @param output the ray-cast results.
|
* Cast a ray against a child shape.
|
||||||
/// @param input the ray-cast input parameters.
|
* @param output the ray-cast results.
|
||||||
/// @param transform the transform to be applied to the shape.
|
* @param input the ray-cast input parameters.
|
||||||
/// @param childIndex the child shape index
|
* @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
|
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.
|
* Given a transform, compute the associated axis aligned bounding box for a child shape.
|
||||||
/// @param xf the world transform of the shape.
|
* @param aabb returns the axis aligned box.
|
||||||
/// @param childIndex the child shape
|
* @param xf the world transform of the shape.
|
||||||
|
* @param childIndex the child shape
|
||||||
|
*/
|
||||||
fun computeAABB(transform: Transform, childIndex: Int): AABB
|
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.
|
* Compute the mass properties of this shape using its dimensions and density.
|
||||||
/// @param massData returns the mass data for this shape.
|
* The inertia tensor is computed about the local origin.
|
||||||
/// @param density the density in kilograms per meter squared.
|
* @param massData returns the mass data for this shape.
|
||||||
|
* @param density the density in kilograms per meter squared.
|
||||||
|
*/
|
||||||
fun computeMass(density: Double): MassData
|
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
|
var radius: Double
|
||||||
}
|
}
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
package ru.dbotthepony.kbox2d.api
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
|||||||
package ru.dbotthepony.kbox2d.api
|
package ru.dbotthepony.kbox2d.api
|
||||||
|
|
||||||
|
import ru.dbotthepony.kbox2d.collision.DistanceProxy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input parameters for b2TimeOfImpact
|
* Input parameters for b2TimeOfImpact
|
||||||
*/
|
*/
|
||||||
data class TOIInput(
|
data class TOIInput(
|
||||||
var proxyA: IDistanceProxy,
|
var proxyA: DistanceProxy,
|
||||||
var proxyB: IDistanceProxy,
|
var proxyB: DistanceProxy,
|
||||||
var sweepA: Sweep,
|
var sweepA: Sweep,
|
||||||
var sweepB: Sweep,
|
var sweepB: Sweep,
|
||||||
var tMax: Double, // defines sweep interval [0, tMax]
|
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
|
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
|
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.
|
* 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.
|
* @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
|
* Called when any joint is about to be destroyed due
|
||||||
* to the destruction of one of its attached bodies.
|
* 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
|
* Called when any fixture is about to be destroyed due
|
||||||
* to the destruction of its parent body.
|
* 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.
|
* Called when two fixtures begin to touch.
|
||||||
*/
|
*/
|
||||||
fun beginContact(contact: IContact)
|
fun beginContact(contact: AbstractContact)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when two fixtures cease to touch.
|
* 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
|
* 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
|
* get an EndContact callback. However, you may get a BeginContact callback
|
||||||
* the next step.
|
* 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
|
* 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.
|
* 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.
|
* Called for each fixture found in the query AABB.
|
||||||
* @return false to terminate the query.
|
* @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
|
* @return -1 to filter, 0 to terminate, fraction to clip the ray for
|
||||||
* closest hit, 1 to continue
|
* 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.util2d.AABB
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
|
|
||||||
|
typealias b2Pair = Pair<Int, Int>
|
||||||
|
|
||||||
|
const val e_nullProxy = -1
|
||||||
|
|
||||||
private class IntArrayList {
|
private class IntArrayList {
|
||||||
private var buffer = IntArray(16)
|
private var buffer = IntArray(16)
|
||||||
var size: Int = 0
|
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 moveBuffer = IntArrayList()
|
||||||
private val pairBuffer = ArrayList<b2Pair>()
|
private val pairBuffer = ArrayList<b2Pair>()
|
||||||
private var moveCount = 0
|
private var moveCount = 0
|
||||||
|
|
||||||
private val tree = DynamicTree()
|
private val tree = DynamicTree()
|
||||||
|
|
||||||
override var proxyCount: Int = 0
|
/**
|
||||||
|
* Get the number of proxies.
|
||||||
|
*/
|
||||||
|
var proxyCount: Int = 0
|
||||||
private set
|
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)
|
bufferMove(proxyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,16 +122,28 @@ class BroadPhase : IBroadPhase {
|
|||||||
return tree.getFatAABB(proxyID)
|
return tree.getFatAABB(proxyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val treeHeight: Int
|
/**
|
||||||
|
* Get the height of the embedded tree.
|
||||||
|
*/
|
||||||
|
val treeHeight: Int
|
||||||
get() = tree.height
|
get() = tree.height
|
||||||
|
|
||||||
override val treeBalance: Int
|
/**
|
||||||
|
* Get the balance of the embedded tree.
|
||||||
|
*/
|
||||||
|
val treeBalance: Int
|
||||||
get() = tree.maxBalance
|
get() = tree.maxBalance
|
||||||
|
|
||||||
override val treeQuality: Double
|
/**
|
||||||
|
* Get the quality metric of the embedded tree.
|
||||||
|
*/
|
||||||
|
val treeQuality: Double
|
||||||
get() = tree.getAreaRatio
|
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))
|
return tree.getFatAABB(proxyIDA).intersect(tree.getFatAABB(proxyIDB))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +170,10 @@ class BroadPhase : IBroadPhase {
|
|||||||
return true
|
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()
|
pairBuffer.clear()
|
||||||
|
|
||||||
for (i in 0 until moveCount) {
|
for (i in 0 until moveCount) {
|
||||||
|
@ -17,9 +17,13 @@ var b2_gjkIters = 0
|
|||||||
var b2_gjkMaxIters = 0
|
var b2_gjkMaxIters = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
class DistanceProxy : IDistanceProxy {
|
/**
|
||||||
override val vertices: List<Vector2d>
|
* A distance proxy is used by the GJK algorithm.
|
||||||
override val radius: Double
|
* It encapsulates any shape.
|
||||||
|
*/
|
||||||
|
class DistanceProxy {
|
||||||
|
val vertices: List<Vector2d>
|
||||||
|
val radius: Double
|
||||||
|
|
||||||
constructor(shape: IShape<*>, index: Int) {
|
constructor(shape: IShape<*>, index: Int) {
|
||||||
when (shape.type) {
|
when (shape.type) {
|
||||||
@ -63,7 +67,10 @@ class DistanceProxy : IDistanceProxy {
|
|||||||
this.radius = radius
|
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 bestIndex = 0
|
||||||
var bestValue = b2Dot(vertices[0], d)
|
var bestValue = b2Dot(vertices[0], d)
|
||||||
|
|
||||||
@ -79,7 +86,10 @@ class DistanceProxy : IDistanceProxy {
|
|||||||
return bestIndex
|
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)]
|
return vertices[getSupport(d)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,9 +126,9 @@ class Simplex() {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
cache: SimplexCache,
|
cache: SimplexCache,
|
||||||
proxyA: IDistanceProxy,
|
proxyA: DistanceProxy,
|
||||||
transformA: Transform,
|
transformA: Transform,
|
||||||
proxyB: IDistanceProxy,
|
proxyB: DistanceProxy,
|
||||||
transformB: Transform,
|
transformB: Transform,
|
||||||
) : this() {
|
) : this() {
|
||||||
check(cache.count <= 3)
|
check(cache.count <= 3)
|
||||||
@ -413,8 +423,8 @@ private const val k_maxIters = 20
|
|||||||
*/
|
*/
|
||||||
fun b2Distance(
|
fun b2Distance(
|
||||||
cache: SimplexCache,
|
cache: SimplexCache,
|
||||||
proxyA: IDistanceProxy,
|
proxyA: DistanceProxy,
|
||||||
proxyB: IDistanceProxy,
|
proxyB: DistanceProxy,
|
||||||
transformA: Transform,
|
transformA: Transform,
|
||||||
transformB: Transform,
|
transformB: Transform,
|
||||||
useRadii: Boolean = false
|
useRadii: Boolean = false
|
||||||
@ -553,8 +563,8 @@ private const val gjk_tolerance = 0.5 * b2_linearSlop
|
|||||||
*/
|
*/
|
||||||
fun b2ShapeCast(
|
fun b2ShapeCast(
|
||||||
output: ShapeCastOutput,
|
output: ShapeCastOutput,
|
||||||
proxyA: IDistanceProxy,
|
proxyA: DistanceProxy,
|
||||||
proxyB: IDistanceProxy,
|
proxyB: DistanceProxy,
|
||||||
xfA: Transform,
|
xfA: Transform,
|
||||||
xfB: Transform,
|
xfB: Transform,
|
||||||
r: Vector2d,
|
r: Vector2d,
|
||||||
|
@ -5,9 +5,36 @@ import ru.dbotthepony.kvector.util2d.AABB
|
|||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
import kotlin.math.absoluteValue
|
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
|
const val b2_nullNode = -1
|
||||||
|
|
||||||
class DynamicTree : IDynamicTree {
|
class DynamicTree : IProxieable, IMovable {
|
||||||
private var root: Int = b2_nullNode
|
private var root: Int = b2_nullNode
|
||||||
|
|
||||||
private val nodeCapacity get() = nodes.size
|
private val nodeCapacity get() = nodes.size
|
||||||
@ -15,7 +42,7 @@ class DynamicTree : IDynamicTree {
|
|||||||
private var freeList = 0
|
private var freeList = 0
|
||||||
private var insertionCount = 0
|
private var insertionCount = 0
|
||||||
|
|
||||||
internal var nodes = Array(16) { TreeNode(this) }
|
private var nodes = Array(16) { TreeNode(this) }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Build a linked list for the free list.
|
// Build a linked list for the free list.
|
||||||
@ -162,11 +189,11 @@ class DynamicTree : IDynamicTree {
|
|||||||
return nodes[proxyID].userData
|
return nodes[proxyID].userData
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun wasMoved(proxyID: Int): Boolean {
|
fun wasMoved(proxyID: Int): Boolean {
|
||||||
return nodes[proxyID].moved
|
return nodes[proxyID].moved
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearMoved(proxyID: Int) {
|
fun clearMoved(proxyID: Int) {
|
||||||
nodes[proxyID].moved = false
|
nodes[proxyID].moved = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,9 +503,16 @@ class DynamicTree : IDynamicTree {
|
|||||||
return iA
|
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)
|
if (root == b2_nullNode)
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
@ -562,7 +596,10 @@ class DynamicTree : IDynamicTree {
|
|||||||
validateMetrics(child2)
|
validateMetrics(child2)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun validate() {
|
/**
|
||||||
|
* Validate this tree. For testing.
|
||||||
|
*/
|
||||||
|
fun validate() {
|
||||||
validateStructure(root)
|
validateStructure(root)
|
||||||
validateMetrics(root)
|
validateMetrics(root)
|
||||||
|
|
||||||
@ -578,7 +615,11 @@ class DynamicTree : IDynamicTree {
|
|||||||
check(nodeCount + freeCount == nodeCapacity)
|
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
|
var maxBalance = 0
|
||||||
|
|
||||||
for (node in nodes) {
|
for (node in nodes) {
|
||||||
@ -596,7 +637,10 @@ class DynamicTree : IDynamicTree {
|
|||||||
return maxBalance
|
return maxBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun rebuildBottomUp() {
|
/**
|
||||||
|
* Build an optimal tree. Very expensive. For testing.
|
||||||
|
*/
|
||||||
|
fun rebuildBottomUp() {
|
||||||
TODO("Not Yet Implemented")
|
TODO("Not Yet Implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ private data class MinSeparationResult(
|
|||||||
|
|
||||||
private class SeparationFunction(
|
private class SeparationFunction(
|
||||||
cache: SimplexCache,
|
cache: SimplexCache,
|
||||||
val proxyA: IDistanceProxy,
|
val proxyA: DistanceProxy,
|
||||||
val proxyB: IDistanceProxy,
|
val proxyB: DistanceProxy,
|
||||||
val sweepA: Sweep,
|
val sweepA: Sweep,
|
||||||
val sweepB: Sweep,
|
val sweepB: Sweep,
|
||||||
t1: Double
|
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.
|
* Note: use [b2Distance] to compute the contact point and normal at the time of impact.
|
||||||
*/
|
*/
|
||||||
fun b2TimeOfImpact(
|
fun b2TimeOfImpact(
|
||||||
proxyA: IDistanceProxy,
|
proxyA: DistanceProxy,
|
||||||
proxyB: IDistanceProxy,
|
proxyB: DistanceProxy,
|
||||||
_sweepA: Sweep,
|
_sweepA: Sweep,
|
||||||
_sweepB: Sweep,
|
_sweepB: Sweep,
|
||||||
tMax: Double, // defines sweep interval [0, tMax]
|
tMax: Double, // defines sweep interval [0, tMax]
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package ru.dbotthepony.kbox2d.dynamics
|
package ru.dbotthepony.kbox2d.dynamics
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.*
|
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.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.times
|
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
|
private set
|
||||||
override var jointCount: Int = 0
|
var jointCount: Int = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override val contactManager: IContactManager = ContactManager()
|
val contactManager = ContactManager()
|
||||||
|
|
||||||
override var destructionListener: IDestructionListener? = null
|
/**
|
||||||
override var contactFilter: IContactFilter? by contactManager::contactFilter
|
* Get the world contact list. With the returned contact, use b2Contact::GetNext to get
|
||||||
override var contactListener: IContactListener? by contactManager::contactListener
|
* 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
|
* Register a contact filter to provide specific control over collision.
|
||||||
override var jointList: IJoint? = null
|
* 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
|
private set
|
||||||
|
|
||||||
override var warmStarting: Boolean = true
|
/**
|
||||||
override var continuousPhysics: Boolean = true
|
* Get the world joint list. With the returned joint, use b2Joint::GetNext to get
|
||||||
override var enableSubStepping: Boolean = false
|
* 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) {
|
set(value) {
|
||||||
if (value == field)
|
if (value == field)
|
||||||
return
|
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
|
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 stepComplete = true
|
||||||
private var newContacts = false
|
private var newContacts = false
|
||||||
|
|
||||||
private val profile = ProfileData()
|
private val profile = ProfileData()
|
||||||
|
|
||||||
override fun notifyNewContacts() {
|
fun notifyNewContacts() {
|
||||||
newContacts = true
|
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)
|
if (isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
|
|
||||||
val body = Body(bodyDef, this)
|
val body = Body(bodyDef, this)
|
||||||
|
|
||||||
body.next = bodyList
|
body.next = bodyList
|
||||||
(bodyList as Body?)?.prev = body
|
bodyList?.prev = body
|
||||||
bodyList = body
|
bodyList = body
|
||||||
bodyCount++
|
bodyCount++
|
||||||
|
|
||||||
return body
|
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)
|
if (isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
|
|
||||||
check(body.world == this) { "$body does not belong to $this" }
|
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.
|
// Delete the attached joints.
|
||||||
for (jointEdge in body.jointIterator) {
|
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.
|
// Delete the attached fixtures. This destroys broad-phase proxies.
|
||||||
for (fixture in body.fixtureIterator) {
|
for (fixture in body.fixtureIterator) {
|
||||||
destructionListener?.sayGoodbye(fixture)
|
destructionListener?.sayGoodbye(fixture)
|
||||||
(fixture as Fixture).destroyProxies(contactManager.broadPhase)
|
fixture.destroyProxies(contactManager.broadPhase)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove world body list.
|
// Remove world body list.
|
||||||
val prev = body.prev as Body?
|
val prev = body.prev
|
||||||
val next = body.next as Body?
|
val next = body.next
|
||||||
|
|
||||||
prev?.next = next
|
prev?.next = next
|
||||||
next?.prev = prev
|
next?.prev = prev
|
||||||
@ -116,10 +247,15 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bodyCount--
|
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)
|
if (isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
|
|
||||||
@ -127,7 +263,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
// Connect to the world list.
|
// Connect to the world list.
|
||||||
joint.next = jointList
|
joint.next = jointList
|
||||||
(jointList as AbstractJoint?)?.prev = joint
|
jointList?.prev = joint
|
||||||
jointList = joint
|
jointList = joint
|
||||||
jointCount++
|
jointCount++
|
||||||
|
|
||||||
@ -150,12 +286,15 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
return joint
|
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)
|
if (isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
|
|
||||||
check(jointCount > 0) { "No joints tracked to remove" }
|
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" }
|
require((joint.nullableBodyA?.world == this || joint.nullableBodyA == null) && (joint.nullableBodyB?.world == this || joint.nullableBodyB == null)) { "$joint does not belong to $this" }
|
||||||
|
|
||||||
if (!joint.isValid) {
|
if (!joint.isValid) {
|
||||||
@ -164,8 +303,8 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
// Remove from the doubly linked list.
|
// Remove from the doubly linked list.
|
||||||
this.run {
|
this.run {
|
||||||
val prev = joint.prev as AbstractJoint?
|
val prev = joint.prev
|
||||||
val next = joint.next as AbstractJoint?
|
val next = joint.next
|
||||||
|
|
||||||
prev?.next = next
|
prev?.next = next
|
||||||
next?.prev = prev
|
next?.prev = prev
|
||||||
@ -241,25 +380,20 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
// Clear all the island flags.
|
// Clear all the island flags.
|
||||||
for (body in bodyListIterator) {
|
for (body in bodyListIterator) {
|
||||||
body as Body
|
|
||||||
body.isOnIsland = false
|
body.isOnIsland = false
|
||||||
}
|
}
|
||||||
|
|
||||||
for (contact in contactListIterator) {
|
for (contact in contactListIterator) {
|
||||||
contact as AbstractContact
|
|
||||||
contact.isOnIsland = false
|
contact.isOnIsland = false
|
||||||
}
|
}
|
||||||
|
|
||||||
for (joint in jointListIterator) {
|
for (joint in jointListIterator) {
|
||||||
joint as AbstractJoint
|
|
||||||
check(joint.isValid) { "$joint is no longer valid, but present in linked list" }
|
check(joint.isValid) { "$joint is no longer valid, but present in linked list" }
|
||||||
joint.isOnIsland = false
|
joint.isOnIsland = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build and simulate all awake islands.
|
// Build and simulate all awake islands.
|
||||||
for (seed in bodyListIterator) {
|
for (seed in bodyListIterator) {
|
||||||
seed as Body
|
|
||||||
|
|
||||||
if (seed.type == BodyType.STATIC || !seed.isAwake || !seed.isEnabled || seed.isOnIsland) {
|
if (seed.type == BodyType.STATIC || !seed.isAwake || !seed.isEnabled || seed.isOnIsland) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -287,7 +421,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
// Search all contacts connected to this body.
|
// Search all contacts connected to this body.
|
||||||
for (ce in body.contactEdgeIterator) {
|
for (ce in body.contactEdgeIterator) {
|
||||||
val contact = ce.contact as AbstractContact
|
val contact = ce.contact
|
||||||
|
|
||||||
// Has this contact already been added to an island?
|
// Has this contact already been added to an island?
|
||||||
if (contact.isOnIsland)
|
if (contact.isOnIsland)
|
||||||
@ -304,7 +438,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
island.add(contact)
|
island.add(contact)
|
||||||
contact.isOnIsland = true
|
contact.isOnIsland = true
|
||||||
|
|
||||||
val other = ce.other as Body
|
val other = ce.other
|
||||||
|
|
||||||
// Was the other body already added to this island?
|
// Was the other body already added to this island?
|
||||||
if (!other.isOnIsland) {
|
if (!other.isOnIsland) {
|
||||||
@ -315,13 +449,13 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
// Search all joints connect to this body.
|
// Search all joints connect to this body.
|
||||||
for (je in body.jointIterator) {
|
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" }
|
check(joint.isValid) { "$joint is no longer valid, but present in linked list of $body" }
|
||||||
|
|
||||||
if (joint.isOnIsland)
|
if (joint.isOnIsland)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
val other = je.otherNullable as Body?
|
val other = je.otherNullable
|
||||||
|
|
||||||
// Don't simulate joints connected to disabled bodies.
|
// Don't simulate joints connected to disabled bodies.
|
||||||
if (other != null && !other.isEnabled)
|
if (other != null && !other.isEnabled)
|
||||||
@ -357,8 +491,6 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
// Synchronize fixtures, check for out of range bodies.
|
// Synchronize fixtures, check for out of range bodies.
|
||||||
for (body in bodyListIterator) {
|
for (body in bodyListIterator) {
|
||||||
body as Body
|
|
||||||
|
|
||||||
// If a body was not in an island then it did not move.
|
// If a body was not in an island then it did not move.
|
||||||
if (!body.isOnIsland || body.type == BodyType.STATIC) {
|
if (!body.isOnIsland || body.type == BodyType.STATIC) {
|
||||||
continue
|
continue
|
||||||
@ -381,14 +513,12 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
if (stepComplete) {
|
if (stepComplete) {
|
||||||
for (body in bodyListIterator) {
|
for (body in bodyListIterator) {
|
||||||
body as Body
|
|
||||||
body.isOnIsland = false
|
body.isOnIsland = false
|
||||||
body.sweep.alpha0 = 0.0
|
body.sweep.alpha0 = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
for (c in contactManager.contactListIterator) {
|
for (c in contactManager.contactListIterator) {
|
||||||
// Invalidate TOI
|
// Invalidate TOI
|
||||||
c as AbstractContact
|
|
||||||
c.isOnIsland = false
|
c.isOnIsland = false
|
||||||
c.toiFlag = false
|
c.toiFlag = false
|
||||||
c.toiCount = 0
|
c.toiCount = 0
|
||||||
@ -406,13 +536,11 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
c as AbstractContact
|
|
||||||
|
|
||||||
if (c.toiCount > b2_maxSubSteps) {
|
if (c.toiCount > b2_maxSubSteps) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var alpha = 1.0
|
var alpha: Double
|
||||||
|
|
||||||
if (c.toiFlag) {
|
if (c.toiFlag) {
|
||||||
// This contact has a valid cached TOI.
|
// This contact has a valid cached TOI.
|
||||||
@ -550,7 +678,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
if (body.type == BodyType.DYNAMIC) {
|
if (body.type == BodyType.DYNAMIC) {
|
||||||
for (ce in body.contactEdgeIterator) {
|
for (ce in body.contactEdgeIterator) {
|
||||||
val contact = ce.contact as AbstractContact
|
val contact = ce.contact
|
||||||
|
|
||||||
// Has this contact already been added to the island?
|
// Has this contact already been added to the island?
|
||||||
if (contact.isOnIsland) {
|
if (contact.isOnIsland) {
|
||||||
@ -569,8 +697,6 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
other as Body
|
|
||||||
|
|
||||||
// Tentatively advance the body to the TOI.
|
// Tentatively advance the body to the TOI.
|
||||||
val backup = other.sweep
|
val backup = other.sweep
|
||||||
if (!other.isOnIsland) {
|
if (!other.isOnIsland) {
|
||||||
@ -639,7 +765,7 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
// Invalidate all contact TOIs on this displaced body.
|
// Invalidate all contact TOIs on this displaced body.
|
||||||
for (ce in body.contactEdgeIterator) {
|
for (ce in body.contactEdgeIterator) {
|
||||||
val contact = ce.contact as AbstractContact
|
val contact = ce.contact
|
||||||
contact.toiFlag = false
|
contact.toiFlag = false
|
||||||
contact.isOnIsland = false
|
contact.isOnIsland = false
|
||||||
}
|
}
|
||||||
@ -658,8 +784,15 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
private var m_inv_dt0 = 0.0
|
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 new fixtures were added, we need to find the new contacts.
|
||||||
if (newContacts) {
|
if (newContacts) {
|
||||||
@ -724,19 +857,41 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
profile.step = System.nanoTime() - stepTimer
|
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) {
|
for (body in bodyListIterator) {
|
||||||
body as Body
|
|
||||||
body.force = Vector2d.ZERO
|
body.force = Vector2d.ZERO
|
||||||
body.torque = 0.0
|
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) }
|
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)
|
val input = RayCastInput(point1, point2, 1.0)
|
||||||
|
|
||||||
contactManager.broadPhase.rayCast(input, object : ProxyRayCastCallback {
|
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) {
|
when (fixture.type) {
|
||||||
IShape.Type.CIRCLE -> {
|
IShape.Type.CIRCLE -> {
|
||||||
val circle = fixture.shape as CircleShape
|
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
|
val debugDraw = debugDraw ?: return
|
||||||
|
|
||||||
if (debugDraw.drawShapes) {
|
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) {
|
override fun shiftOrigin(newOrigin: Vector2d) {
|
||||||
if (isLocked)
|
if (isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
@ -891,7 +1038,6 @@ class B2World(override var gravity: Vector2d) : IB2World {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
for (body in bodyListIterator) {
|
for (body in bodyListIterator) {
|
||||||
body as Body
|
|
||||||
body.transform.p -= newOrigin
|
body.transform.p -= newOrigin
|
||||||
body.sweep.c0 -= newOrigin
|
body.sweep.c0 -= newOrigin
|
||||||
body.sweep.c -= 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()
|
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")
|
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.kbox2d.api.*
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
|
|
||||||
open class Body(def: BodyDef, world: IB2World) : IBody {
|
class Body(def: BodyDef, world: B2World) {
|
||||||
private var _world: IB2World? = world
|
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")
|
get() = _world ?: throw IllegalStateException("Tried to use removed body")
|
||||||
|
|
||||||
internal var flags: Int = 0
|
internal var flags: Int = 0
|
||||||
@ -43,21 +46,37 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
|
|
||||||
internal val sweep: Sweep = Sweep()
|
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
|
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
|
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
|
get() = sweep.a
|
||||||
|
|
||||||
override val userData: Any? = def.userData
|
val userData: Any? = def.userData
|
||||||
|
|
||||||
internal var sleepTime: Double = 0.0
|
internal var sleepTime: Double = 0.0
|
||||||
internal var torque: Double = 0.0
|
internal var torque: Double = 0.0
|
||||||
internal var force: Vector2d = Vector2d.ZERO
|
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) {
|
set(value) {
|
||||||
if (type == BodyType.STATIC)
|
if (type == BodyType.STATIC)
|
||||||
return
|
return
|
||||||
@ -68,7 +87,10 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
override var angularVelocity: Double = def.angularVelocity
|
/**
|
||||||
|
* The angular velocity of the body.
|
||||||
|
*/
|
||||||
|
var angularVelocity: Double = def.angularVelocity
|
||||||
set(value) {
|
set(value) {
|
||||||
if (type == BodyType.STATIC)
|
if (type == BodyType.STATIC)
|
||||||
return
|
return
|
||||||
@ -79,27 +101,95 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
override var fixtureList: Fixture? = null
|
var fixtureList: Fixture? = null
|
||||||
protected set
|
protected set
|
||||||
|
|
||||||
protected var fixtureCount: Int = 0
|
var fixtureCount: Int = 0
|
||||||
|
private set
|
||||||
|
|
||||||
override var jointList: JointEdge? = null
|
/**
|
||||||
internal set
|
* Get the list of all joints attached to this body.
|
||||||
override var contactEdge: ContactEdge? = null
|
*
|
||||||
|
* Kotlin: This is not a list, but, as in C++ impl, a custom
|
||||||
|
* linked list.
|
||||||
|
*/
|
||||||
|
var jointList: JointEdge? = null
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
override var next: IBody? = null
|
/**
|
||||||
internal set
|
* Get the list of all contacts attached to this body.
|
||||||
override var prev: IBody? = null
|
* @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
|
internal set
|
||||||
|
|
||||||
override var linearDamping: Double = def.linearDamping
|
val contactEdgeIterator: Iterator<ContactEdge> get() {
|
||||||
override var angularDamping: Double = def.angularDamping
|
return object : Iterator<ContactEdge> {
|
||||||
override var gravityScale: Double = def.gravityScale
|
private var node = contactEdge
|
||||||
|
|
||||||
override var mass: Double = 0.0
|
override fun hasNext(): Boolean {
|
||||||
protected set
|
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
|
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 I by this::rotInertia
|
||||||
internal var invI by this::rotInertiaInv
|
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)
|
get() = BodyFlags.BULLET.isit(flags)
|
||||||
set(value) { flags = BodyFlags.BULLET.update(flags, value) }
|
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)
|
get() = BodyFlags.AWAKE.isit(flags)
|
||||||
set(value) {
|
set(value) {
|
||||||
if (type == BodyType.STATIC || BodyFlags.AWAKE.isit(flags) == 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)
|
get() = BodyFlags.ENABLED.isit(flags)
|
||||||
set(value) {
|
set(value) {
|
||||||
if (world.isLocked)
|
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)
|
get() = BodyFlags.FIXED_ROTATION.isit(flags)
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value == isFixedRotation)
|
if (value == isFixedRotation)
|
||||||
@ -190,7 +311,11 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
resetMassData()
|
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)
|
get() = BodyFlags.AUTO_SLEEP.isit(flags)
|
||||||
set(value) {
|
set(value) {
|
||||||
flags = BodyFlags.AUTO_SLEEP.update(flags, 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) {
|
set(value) {
|
||||||
if (world.isLocked)
|
if (world.isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
@ -234,7 +362,7 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
contactEdge = null
|
contactEdge = null
|
||||||
|
|
||||||
val broadPhase = world.contactManager.broadPhase
|
val broadPhase = world.contactManager.broadPhase
|
||||||
var f: IFixture? = fixtureList
|
var f: Fixture? = fixtureList
|
||||||
|
|
||||||
while (f != null) {
|
while (f != null) {
|
||||||
for (proxy in f.proxies) {
|
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)
|
if (world.isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
|
|
||||||
@ -271,21 +407,48 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
return fixture
|
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)
|
if (world.isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
|
|
||||||
require(fixture.body == this) { "$fixture does not belong to $this (belongs to ${fixture.body})" }
|
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" }
|
check(fixtureCount > 0) { "Having no tracked fixtures, but $fixture belongs to us" }
|
||||||
|
|
||||||
var node: IFixture? = fixtureList
|
var node: Fixture? = fixtureList
|
||||||
var found = false
|
var found = false
|
||||||
var previous: IFixture? = null
|
var previous: Fixture? = null
|
||||||
|
|
||||||
while (node != null) {
|
while (node != null) {
|
||||||
if (node == fixture) {
|
if (node == fixture) {
|
||||||
// TODO: Это должно работать
|
// TODO: Это должно работать
|
||||||
(previous as Fixture?)?.next = node.next
|
previous?.next = node.next
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
} else {
|
} 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.
|
// Compute mass data from shapes. Each shape has its own density.
|
||||||
mass = 0.0
|
mass = 0.0
|
||||||
invMass = 0.0
|
invMass = 0.0
|
||||||
@ -387,7 +555,16 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
linearVelocity += b2Cross(angularVelocity, sweep.c - oldCenter)
|
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(
|
get() = MassData(
|
||||||
mass = mass,
|
mass = mass,
|
||||||
inertia = inertia,
|
inertia = inertia,
|
||||||
@ -428,7 +605,7 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
linearVelocity += b2Cross(angularVelocity, sweep.c - oldCenter)
|
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.
|
// At least one body should be dynamic.
|
||||||
if (type != BodyType.DYNAMIC && other.type != BodyType.DYNAMIC)
|
if (type != BodyType.DYNAMIC && other.type != BodyType.DYNAMIC)
|
||||||
return false
|
return false
|
||||||
@ -454,7 +631,14 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
sweep.a = def.angle
|
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)
|
if (world.isLocked)
|
||||||
throw ConcurrentModificationException()
|
throw ConcurrentModificationException()
|
||||||
|
|
||||||
@ -477,40 +661,88 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
world.notifyNewContacts()
|
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)
|
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
|
get() = sweep.localCenter
|
||||||
|
|
||||||
override val worldCenter: Vector2d
|
/**
|
||||||
|
* Get the world position of the center of mass.
|
||||||
|
*/
|
||||||
|
val worldCenter: Vector2d
|
||||||
get() = sweep.c
|
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)
|
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)
|
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)
|
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)
|
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)
|
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))
|
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)
|
if (type != BodyType.DYNAMIC)
|
||||||
return
|
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)
|
if (type != BodyType.DYNAMIC)
|
||||||
return
|
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)
|
if (type != BodyType.DYNAMIC)
|
||||||
return
|
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)
|
if (type != BodyType.DYNAMIC)
|
||||||
return
|
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)
|
if (type != BodyType.DYNAMIC)
|
||||||
return
|
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)
|
if (type != BodyType.DYNAMIC)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -616,7 +877,7 @@ open class Body(def: BodyDef, world: IB2World) : IBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dump() {
|
fun dump() {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,35 @@ import ru.dbotthepony.kbox2d.api.*
|
|||||||
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
||||||
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
|
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
|
||||||
|
|
||||||
class ContactManager : IContactManager {
|
/**
|
||||||
override val broadPhase: IBroadPhase = BroadPhase()
|
* Delegate of b2World.
|
||||||
override var contactList: IContact? = null
|
*/
|
||||||
override var contactCount: Int = 0
|
class ContactManager {
|
||||||
|
val broadPhase = BroadPhase()
|
||||||
|
var contactList: AbstractContact? = null
|
||||||
|
var contactCount: Int = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override var contactFilter: IContactFilter? = null
|
var contactFilter: IContactFilter? = null
|
||||||
override var contactListener: IContactListener? = null
|
var contactListener: IContactListener? = null
|
||||||
|
|
||||||
override fun destroy(contact: IContact) {
|
val contactListIterator: Iterator<AbstractContact> get() {
|
||||||
contact as AbstractContact
|
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 fixtureA = contact.fixtureA
|
||||||
val fixtureB = contact.fixtureB
|
val fixtureB = contact.fixtureB
|
||||||
val bodyA = fixtureA.body as Body
|
val bodyA = fixtureA.body as Body
|
||||||
@ -72,7 +89,7 @@ class ContactManager : IContactManager {
|
|||||||
* all the narrow phase collision is processed for the world
|
* all the narrow phase collision is processed for the world
|
||||||
* contact list.
|
* contact list.
|
||||||
*/
|
*/
|
||||||
override fun collide() {
|
fun collide() {
|
||||||
// Update awake contacts.
|
// Update awake contacts.
|
||||||
|
|
||||||
for (c in contactListIterator) {
|
for (c in contactListIterator) {
|
||||||
@ -123,11 +140,11 @@ class ContactManager : IContactManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findNewContacts() {
|
fun findNewContacts() {
|
||||||
broadPhase.updatePairs(this::addPair)
|
broadPhase.updatePairs(this::addPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addPair(proxyUserDataA: Any?, proxyUserDataB: Any?) {
|
fun addPair(proxyUserDataA: Any?, proxyUserDataB: Any?) {
|
||||||
val proxyA = proxyUserDataA as FixtureProxy
|
val proxyA = proxyUserDataA as FixtureProxy
|
||||||
val proxyB = proxyUserDataB as FixtureProxy
|
val proxyB = proxyUserDataB as FixtureProxy
|
||||||
|
|
||||||
|
@ -1,28 +1,77 @@
|
|||||||
package ru.dbotthepony.kbox2d.dynamics
|
package ru.dbotthepony.kbox2d.dynamics
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.*
|
import ru.dbotthepony.kbox2d.api.*
|
||||||
|
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
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
|
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
|
* Get the user data that was assigned in the fixture definition. Use this to
|
||||||
override var restitution: Double = def.restitution
|
* store your application specific data.
|
||||||
override var restitutionThreshold: Double = def.restitutionThreshold
|
*/
|
||||||
|
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) {
|
set(value) {
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
checkNotNull(body) { "Already destroyed" }.isAwake = true
|
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 {
|
init {
|
||||||
if (shape is PolygonShape) {
|
if (shape is PolygonShape) {
|
||||||
@ -42,10 +96,15 @@ open class Fixture(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val internalProxies = ArrayList<FixtureProxy>(shape.childCount)
|
private val internalProxies = ArrayList<FixtureProxy>(shape.childCount)
|
||||||
final override val proxies: List<FixtureProxy> = Collections.unmodifiableList(internalProxies)
|
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) {
|
set(value) {
|
||||||
require(value.isFinite()) { "Infinite density" }
|
require(value.isFinite()) { "Infinite density" }
|
||||||
require(!value.isNaN()) { "NaN density" }
|
require(!value.isNaN()) { "NaN density" }
|
||||||
@ -53,7 +112,12 @@ open class Fixture(
|
|||||||
field = value
|
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) {
|
set(value) {
|
||||||
if (value is ImmutableFilter)
|
if (value is ImmutableFilter)
|
||||||
field = value
|
field = value
|
||||||
@ -76,7 +140,7 @@ open class Fixture(
|
|||||||
/**
|
/**
|
||||||
* These support body activation/deactivation.
|
* 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(body != null) { "Already destroyed" }
|
||||||
check(internalProxies.isEmpty()) { "Already having proxies" }
|
check(internalProxies.isEmpty()) { "Already having proxies" }
|
||||||
|
|
||||||
@ -97,7 +161,7 @@ open class Fixture(
|
|||||||
/**
|
/**
|
||||||
* These support body activation/deactivation.
|
* These support body activation/deactivation.
|
||||||
*/
|
*/
|
||||||
internal fun destroyProxies(broadPhase: IBroadPhase) {
|
internal fun destroyProxies(broadPhase: BroadPhase) {
|
||||||
check(body != null) { "Already destroyed" }
|
check(body != null) { "Already destroyed" }
|
||||||
|
|
||||||
// Destroy proxies in the broad-phase.
|
// Destroy proxies in the broad-phase.
|
||||||
@ -108,7 +172,7 @@ open class Fixture(
|
|||||||
internalProxies.clear()
|
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" }
|
check(body != null) { "Already destroyed" }
|
||||||
|
|
||||||
for (proxy in internalProxies) {
|
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
|
val body = body
|
||||||
check(body != null) { "Already destroyed" }
|
check(body != null) { "Already destroyed" }
|
||||||
var edge = body.contactEdge
|
var edge = body.contactEdge
|
||||||
@ -144,4 +211,45 @@ open class Fixture(
|
|||||||
broadPhase.touchProxy(proxy.proxyId)
|
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.WorldManifold
|
||||||
import ru.dbotthepony.kbox2d.collision.b2TestOverlap
|
import ru.dbotthepony.kbox2d.collision.b2TestOverlap
|
||||||
import ru.dbotthepony.kbox2d.dynamics.Body
|
import ru.dbotthepony.kbox2d.dynamics.Body
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
fun interface ContactFactory {
|
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(
|
sealed class AbstractContact(
|
||||||
final override val fixtureA: IFixture,
|
/**
|
||||||
final override val childIndexA: Int,
|
* Get fixture A in this contact.
|
||||||
final override val fixtureB: IFixture,
|
*/
|
||||||
final override val childIndexB: Int,
|
val fixtureA: Fixture,
|
||||||
) : IContact {
|
|
||||||
|
/**
|
||||||
|
* 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 flags: Int = ContactFlags.ENABLED.bitmask
|
||||||
internal var isOnIsland = false
|
internal var isOnIsland = false
|
||||||
internal var toiFlag = 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
|
private set
|
||||||
|
|
||||||
override var next: IContact? = null
|
/**
|
||||||
|
* Get the next contact in the world's contact list.
|
||||||
|
*/
|
||||||
|
var next: AbstractContact? = null
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
override var prev: IContact? = null
|
/**
|
||||||
|
* Get the previous contact in the world's contact list.
|
||||||
|
*/
|
||||||
|
var prev: AbstractContact? = null
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
internal var isFlaggedForFiltering: Boolean
|
internal var isFlaggedForFiltering: Boolean
|
||||||
@ -51,15 +82,44 @@ sealed class AbstractContact(
|
|||||||
bodyB.contactEdge = nodeB
|
bodyB.contactEdge = nodeB
|
||||||
}
|
}
|
||||||
|
|
||||||
override var friction: Double = b2MixFriction(fixtureA.friction, fixtureB.friction)
|
/**
|
||||||
override var restitution: Double = b2MixRestitution(fixtureA.restitution, fixtureB.restitution)
|
* Override the default friction mixture. You can call this in b2ContactListener::PreSolve.
|
||||||
override var restitutionThreshold: Double = b2MixRestitutionThreshold(fixtureA.restitutionThreshold, fixtureB.restitutionThreshold)
|
* This value persists until set or reset.
|
||||||
override var tangentSpeed: Double = 0.0
|
* 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 toiCount: Int = 0
|
||||||
internal var toi: Double = 0.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 bodyA = checkNotNull(fixtureA.body) { "FixtureA has no body attached" }
|
||||||
val bodyB = checkNotNull(fixtureB.body) { "FixtureB 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)
|
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
|
get() = (flags and ContactFlags.TOUCHING.bitmask) == ContactFlags.TOUCHING.bitmask
|
||||||
internal set(value) { flags = ContactFlags.TOUCHING.update(flags, value) }
|
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
|
get() = (flags and ContactFlags.ENABLED.bitmask) == ContactFlags.ENABLED.bitmask
|
||||||
set(value) { flags = ContactFlags.ENABLED.update(flags, value) }
|
set(value) { flags = ContactFlags.ENABLED.update(flags, value) }
|
||||||
|
|
||||||
override fun flagForFiltering() {
|
fun flagForFiltering() {
|
||||||
flags = flags or ContactFlags.FILTER.bitmask
|
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 {
|
companion object {
|
||||||
private val registry =
|
private val registry =
|
||||||
EnumMap<IShape.Type, EnumMap<IShape.Type, ContactFactory>>(IShape.Type::class.java)
|
EnumMap<IShape.Type, EnumMap<IShape.Type, ContactFactory>>(IShape.Type::class.java)
|
||||||
@ -168,9 +257,9 @@ sealed class AbstractContact(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun create(
|
internal fun create(
|
||||||
fixtureA: IFixture,
|
fixtureA: Fixture,
|
||||||
indexA: Int,
|
indexA: Int,
|
||||||
fixtureB: IFixture,
|
fixtureB: Fixture,
|
||||||
indexB: Int,
|
indexB: Int,
|
||||||
): AbstractContact {
|
): AbstractContact {
|
||||||
val type1 = fixtureA.type
|
val type1 = fixtureA.type
|
||||||
@ -182,31 +271,31 @@ sealed class AbstractContact(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
return@register ChainCircleContact(fixtureA, indexA, fixtureB, indexB)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.IFixture
|
|
||||||
import ru.dbotthepony.kbox2d.api.IShape
|
import ru.dbotthepony.kbox2d.api.IShape
|
||||||
import ru.dbotthepony.kbox2d.api.Manifold
|
import ru.dbotthepony.kbox2d.api.Manifold
|
||||||
import ru.dbotthepony.kbox2d.api.Transform
|
import ru.dbotthepony.kbox2d.api.Transform
|
||||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndCircle
|
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndCircle
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||||
|
|
||||||
class ChainCircleContact(
|
class ChainCircleContact(
|
||||||
fixtureA: IFixture,
|
fixtureA: Fixture,
|
||||||
childIndexA: Int,
|
childIndexA: Int,
|
||||||
fixtureB: IFixture,
|
fixtureB: Fixture,
|
||||||
childIndexB: Int,
|
childIndexB: Int,
|
||||||
) : AbstractContact(fixtureA, childIndexA, fixtureB, childIndexB) {
|
) : AbstractContact(fixtureA, childIndexA, fixtureB, childIndexB) {
|
||||||
init {
|
init {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.IFixture
|
|
||||||
import ru.dbotthepony.kbox2d.api.IShape
|
import ru.dbotthepony.kbox2d.api.IShape
|
||||||
import ru.dbotthepony.kbox2d.api.Manifold
|
import ru.dbotthepony.kbox2d.api.Manifold
|
||||||
import ru.dbotthepony.kbox2d.api.Transform
|
import ru.dbotthepony.kbox2d.api.Transform
|
||||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndPolygon
|
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndPolygon
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
import ru.dbotthepony.kbox2d.collision.shapes.ChainShape
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||||
|
|
||||||
class ChainPolygonContact(
|
class ChainPolygonContact(
|
||||||
fixtureA: IFixture,
|
fixtureA: Fixture,
|
||||||
childIndexA: Int,
|
childIndexA: Int,
|
||||||
fixtureB: IFixture,
|
fixtureB: Fixture,
|
||||||
childIndexB: Int,
|
childIndexB: Int,
|
||||||
) : AbstractContact(fixtureA, childIndexA, fixtureB, childIndexB) {
|
) : AbstractContact(fixtureA, childIndexA, fixtureB, childIndexB) {
|
||||||
init {
|
init {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.IFixture
|
|
||||||
import ru.dbotthepony.kbox2d.api.IShape
|
import ru.dbotthepony.kbox2d.api.IShape
|
||||||
import ru.dbotthepony.kbox2d.api.Manifold
|
import ru.dbotthepony.kbox2d.api.Manifold
|
||||||
import ru.dbotthepony.kbox2d.api.Transform
|
import ru.dbotthepony.kbox2d.api.Transform
|
||||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideCircles
|
import ru.dbotthepony.kbox2d.collision.handler.b2CollideCircles
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||||
|
|
||||||
class CircleContact(
|
class CircleContact(
|
||||||
fixtureA: IFixture,
|
fixtureA: Fixture,
|
||||||
fixtureB: IFixture,
|
fixtureB: Fixture,
|
||||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||||
init {
|
init {
|
||||||
require(fixtureA.type == IShape.Type.CIRCLE) { "Fixture A is of type ${fixtureA.type}" }
|
require(fixtureA.type == IShape.Type.CIRCLE) { "Fixture A is of type ${fixtureA.type}" }
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package ru.dbotthepony.kbox2d.dynamics.contact
|
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.IFixture
|
|
||||||
import ru.dbotthepony.kbox2d.api.IShape
|
import ru.dbotthepony.kbox2d.api.IShape
|
||||||
import ru.dbotthepony.kbox2d.api.Manifold
|
import ru.dbotthepony.kbox2d.api.Manifold
|
||||||
import ru.dbotthepony.kbox2d.api.Transform
|
import ru.dbotthepony.kbox2d.api.Transform
|
||||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndCircle
|
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndCircle
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.EdgeShape
|
import ru.dbotthepony.kbox2d.collision.shapes.EdgeShape
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||||
|
|
||||||
class EdgeCircleContact(
|
class EdgeCircleContact(
|
||||||
fixtureA: IFixture,
|
fixtureA: Fixture,
|
||||||
fixtureB: IFixture,
|
fixtureB: Fixture,
|
||||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||||
init {
|
init {
|
||||||
require(fixtureA.type == IShape.Type.EDGE) { "Fixture A is of type ${fixtureA.type}, expected EDGE" }
|
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
|
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.IFixture
|
|
||||||
import ru.dbotthepony.kbox2d.api.IShape
|
import ru.dbotthepony.kbox2d.api.IShape
|
||||||
import ru.dbotthepony.kbox2d.api.Manifold
|
import ru.dbotthepony.kbox2d.api.Manifold
|
||||||
import ru.dbotthepony.kbox2d.api.Transform
|
import ru.dbotthepony.kbox2d.api.Transform
|
||||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndPolygon
|
import ru.dbotthepony.kbox2d.collision.handler.b2CollideEdgeAndPolygon
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.EdgeShape
|
import ru.dbotthepony.kbox2d.collision.shapes.EdgeShape
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||||
|
|
||||||
class EdgePolygonContact(
|
class EdgePolygonContact(
|
||||||
fixtureA: IFixture,
|
fixtureA: Fixture,
|
||||||
fixtureB: IFixture,
|
fixtureB: Fixture,
|
||||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||||
init {
|
init {
|
||||||
require(fixtureA.type == IShape.Type.EDGE) { "Fixture A is of type ${fixtureA.type}, expected EDGE" }
|
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
|
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.IFixture
|
|
||||||
import ru.dbotthepony.kbox2d.api.IShape
|
import ru.dbotthepony.kbox2d.api.IShape
|
||||||
import ru.dbotthepony.kbox2d.api.Manifold
|
import ru.dbotthepony.kbox2d.api.Manifold
|
||||||
import ru.dbotthepony.kbox2d.api.Transform
|
import ru.dbotthepony.kbox2d.api.Transform
|
||||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollidePolygonAndCircle
|
import ru.dbotthepony.kbox2d.collision.handler.b2CollidePolygonAndCircle
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||||
|
|
||||||
class PolygonCircleContact(
|
class PolygonCircleContact(
|
||||||
fixtureA: IFixture,
|
fixtureA: Fixture,
|
||||||
fixtureB: IFixture,
|
fixtureB: Fixture,
|
||||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||||
init {
|
init {
|
||||||
require(fixtureA.type == IShape.Type.POLYGON) { "Fixture A is of type ${fixtureA.type}, expected POLYGON" }
|
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
|
package ru.dbotthepony.kbox2d.dynamics.contact
|
||||||
|
|
||||||
import ru.dbotthepony.kbox2d.api.IFixture
|
|
||||||
import ru.dbotthepony.kbox2d.api.IShape
|
import ru.dbotthepony.kbox2d.api.IShape
|
||||||
import ru.dbotthepony.kbox2d.api.Manifold
|
import ru.dbotthepony.kbox2d.api.Manifold
|
||||||
import ru.dbotthepony.kbox2d.api.Transform
|
import ru.dbotthepony.kbox2d.api.Transform
|
||||||
import ru.dbotthepony.kbox2d.collision.handler.b2CollidePolygons
|
import ru.dbotthepony.kbox2d.collision.handler.b2CollidePolygons
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Fixture
|
||||||
|
|
||||||
class PolygonContact(
|
class PolygonContact(
|
||||||
fixtureA: IFixture,
|
fixtureA: Fixture,
|
||||||
fixtureB: IFixture,
|
fixtureB: Fixture,
|
||||||
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
) : AbstractContact(fixtureA, 0, fixtureB, 0) {
|
||||||
init {
|
init {
|
||||||
require(fixtureA.type == IShape.Type.POLYGON) { "Fixture A has type of ${fixtureA.type}" }
|
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
|
fun factorize(jointDef: IJointDef): AbstractJoint
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class AbstractJoint(def: IJointDef) : IJoint {
|
sealed class AbstractJoint(def: IJointDef) : IMovable {
|
||||||
init {
|
init {
|
||||||
require(def.bodyA != def.bodyB) { "Tried to create join on same body" }
|
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
|
protected var index: Int = 0
|
||||||
|
|
||||||
override val collideConnected: Boolean = def.collideConnected
|
/**
|
||||||
override val type: JointType = def.type
|
* Get collide connected.
|
||||||
override var userData: Any? = def.userData
|
* 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
|
// KBox2D: In original code, nothing expects bodies to be null
|
||||||
// but certain joints (notably, mouse joint) have no meaningful
|
// 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 _bodyA: Body? = def.bodyA as Body?
|
||||||
protected var _bodyB: Body? = def.bodyB 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 hasBodyA: Boolean get() = _bodyA != null
|
||||||
val hasBodyB: Boolean get() = _bodyB != null
|
val hasBodyB: Boolean get() = _bodyB != null
|
||||||
@ -58,11 +104,26 @@ sealed class AbstractJoint(def: IJointDef) : IJoint {
|
|||||||
_bodyB?.jointList = edgeB
|
_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
|
internal set
|
||||||
final override var prev: IJoint? = null
|
var prev: AbstractJoint? = null
|
||||||
internal set
|
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
|
* Signals that this joint was destroyed, invalidate stuff
|
||||||
* to fail-fast this object
|
* 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.CircleShape
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
import ru.dbotthepony.kbox2d.dynamics.B2World
|
import ru.dbotthepony.kbox2d.dynamics.B2World
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.Body
|
||||||
import ru.dbotthepony.kbox2d.dynamics.joint.MouseJoint
|
import ru.dbotthepony.kbox2d.dynamics.joint.MouseJoint
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.world.Chunk
|
import ru.dbotthepony.kstarbound.world.Chunk
|
||||||
@ -53,7 +54,7 @@ fun main() {
|
|||||||
|
|
||||||
ground.createFixture(groundPoly, 0.0)
|
ground.createFixture(groundPoly, 0.0)
|
||||||
|
|
||||||
val boxes = ArrayList<ru.dbotthepony.kbox2d.api.IBody>()
|
val boxes = ArrayList<Body>()
|
||||||
|
|
||||||
/*run {
|
/*run {
|
||||||
val movingDef = BodyDef(
|
val movingDef = BodyDef(
|
||||||
|
Loading…
Reference in New Issue
Block a user