From 6171b44170a81d5b713c2f5bd499446f8c34093f Mon Sep 17 00:00:00 2001 From: Song Minjae Date: Mon, 2 May 2016 00:42:04 +0900 Subject: [PATCH] CCD implementation for hitting ground Former-commit-id: 786f0b4551b32be44767eac627dc247df3773968 Former-commit-id: 6ff2be7b31c91a581125c5682055776047660195 --- .../terrarum/gameactors/ActorWithBody.kt | 139 ++- src/net/torvald/terrarum/gameactors/Hitbox.kt | 18 + src/net/torvald/terrarum/gameactors/Player.kt | 23 +- src/net/torvald/terrarum/gamemap/GameMap.kt | 3 +- .../terrarum/tileproperties/TileProp.kt | 1 - .../terrarum/tileproperties/TilePropCodex.kt | 2 - .../terrarum/tileproperties/tileprop.csv | 238 ++--- src/org/dyn4j/geometry/ChainedVector2.kt | 873 ++++++++++++++++++ src/org/dyn4j/geometry/Vector2.kt | 174 +--- 9 files changed, 1152 insertions(+), 319 deletions(-) create mode 100644 src/org/dyn4j/geometry/ChainedVector2.kt diff --git a/src/net/torvald/terrarum/gameactors/ActorWithBody.kt b/src/net/torvald/terrarum/gameactors/ActorWithBody.kt index a3680eba4..5b725cecb 100644 --- a/src/net/torvald/terrarum/gameactors/ActorWithBody.kt +++ b/src/net/torvald/terrarum/gameactors/ActorWithBody.kt @@ -8,6 +8,7 @@ import net.torvald.terrarum.tileproperties.TilePropCodex import net.torvald.spriteanimation.SpriteAnimation import com.jme3.math.FastMath import net.torvald.terrarum.tileproperties.TileNameCode +import org.dyn4j.geometry.ChainedVector2 import org.dyn4j.geometry.Vector2 import org.newdawn.slick.GameContainer import org.newdawn.slick.Graphics @@ -26,19 +27,21 @@ open class ActorWithBody constructor() : Actor(), Visible { var baseHitboxW: Int = 0 var baseHitboxH: Int = 0 + @Transient private val map: GameMap = Terrarum.game.map + /** * Velocity vector (broken down by axes) for newtonian sim. * Acceleration: used in code like: * veloY += 3.0 * +3.0 is acceleration. You __accumulate__ acceleration to the velocity. */ - val velocity = Vector2(0.0, 0.0) + val velocity = ChainedVector2(0.0, 0.0) var veloX: Double get() = velocity.x - set(value) { velocity.set(value, veloY) } + set(value) { velocity.x = value } var veloY: Double get() = velocity.y - set(value) { velocity.set(veloX, value) } + set(value) { velocity.y = value } @Transient private val VELO_HARD_LIMIT = 10000.0 var grounded = false @@ -106,7 +109,7 @@ open class ActorWithBody constructor() : Actor(), Visible { */ @Transient private val SI_TO_GAME_VEL = METER / Terrarum.TARGET_FPS - @Transient private var gravitation: Double = 0.toDouble() + @Transient private var gravitation: Vector2 = map.gravitation @Transient private val DRAG_COEFF = 1.0 @Transient private val CONTACT_AREA_TOP = 0 @@ -135,8 +138,6 @@ open class ActorWithBody constructor() : Actor(), Visible { */ @Transient val INVINCIBILITY_TIME: Int = 500 - @Transient private val map: GameMap - @Transient private val MASS_DEFAULT: Double = 60.0 internal val physSleep: Boolean @@ -154,8 +155,10 @@ open class ActorWithBody constructor() : Actor(), Visible { @Transient val DYNAMIC = 2 @Transient val STATIC = 3 + private val SLEEP_THRE = 0.05 + init { - map = Terrarum.game.map + } /** @@ -243,10 +246,13 @@ open class ActorWithBody constructor() : Actor(), Visible { //} //else { // compensate for colliding - updateHorizontalCollision() - updateVerticalCollision() + //updateHorizontalCollision() + //updateVerticalCollision() + adjustHit() + applyNormalForce() setHorizontalFriction() + if (isPlayerNoClip) setVerticalFriction() //} // apply our compensation to actual hitbox @@ -273,16 +279,19 @@ open class ActorWithBody constructor() : Actor(), Visible { * weight; gravitational force in action * W = mass * G (9.8 [m/s^2]) */ - val W = gravitation * mass + val W: Vector2 = gravitation * mass /** * Drag of atmosphere * D = Cd (drag coefficient) * 0.5 * rho (density) * V^2 (velocity) * A (area) */ - val A = scale * scale - val D = DRAG_COEFF * 0.5 * 1.292 * veloY * veloY * A + // TODO replace 1.292 with fluid tile density + val A: Double = scale * scale + val D: Vector2 = velocity.copy().toVector() * DRAG_COEFF * 0.5 * 1.292 * A - veloY += clampCeil( - (W - D) / mass * SI_TO_GAME_ACC * G_MUL_PLAYABLE_CONST, VELO_HARD_LIMIT) + //veloY += (W - D) / mass * SI_TO_GAME_ACC + val V: Vector2 = (W - D) / mass * SI_TO_GAME_ACC + veloX += V.x + veloY += V.y } } @@ -298,6 +307,18 @@ open class ActorWithBody constructor() : Actor(), Visible { } } + private fun setVerticalFriction() { + val friction = BASE_FRICTION * tileFriction.tileFrictionToMult() + if (veloY < 0) { + veloY += friction + if (veloY > 0) veloY = 0.0 // compensate overshoot + } + else if (veloY > 0) { + veloY -= friction + if (veloY < 0) veloY = 0.0 // compensate overshoot + } + } + private fun updateVerticalCollision() { if (!isNoCollideWorld) { if (veloY >= 0) { // check downward @@ -451,6 +472,67 @@ open class ActorWithBody constructor() : Actor(), Visible { nextHitbox.setPosition(newX, newY) } + /** + * nextHitbox must NOT altered before this method is called! + */ + private fun adjustHit() { + val delta: Vector2 = Vector2(hitbox.toVector() - nextHitbox.toVector()) // we need to traverse back, so may as well negate at the first place + delta *= SLEEP_THRE // CCD delta + + while (isColliding(CONTACT_AREA_LEFT) || isColliding(CONTACT_AREA_RIGHT) + || isColliding(CONTACT_AREA_TOP) || isColliding(CONTACT_AREA_BOTTOM) + ) { + // CCD to the 'delta' + nextHitbox.translate(delta) + } + } + + private fun applyNormalForce() { + if (!isNoCollideWorld) { + if (gravitation.y != 0.0) { + if (veloY > SLEEP_THRE && isColliding(CONTACT_AREA_BOTTOM, 0, 1)) { + veloY = 0.0 + grounded = (gravitation.y > 0) + } + else if (veloY < -SLEEP_THRE && isColliding(CONTACT_AREA_TOP, 0, -1)) { + veloY = 0.0 + grounded = (gravitation.y < 0) + } + else { + grounded = false + } + } + else { + if ((veloY > SLEEP_THRE && isColliding(CONTACT_AREA_BOTTOM, 0, 1)) + || (veloY < -SLEEP_THRE && isColliding(CONTACT_AREA_TOP, 0, -1)) + ) { + veloY = 0.0 + } + } + + if (gravitation.x != 0.0) { + if (veloX > SLEEP_THRE && isColliding(CONTACT_AREA_BOTTOM, 1, 0)) { + veloX = 0.0 + grounded = (gravitation.x > 0) + } + else if (veloX < -SLEEP_THRE && isColliding(CONTACT_AREA_TOP, -1, 0)) { + veloY = 0.0 + grounded = (gravitation.x < 0) + } + else { + grounded = false + } + } + else { + if ((veloX > SLEEP_THRE && isColliding(CONTACT_AREA_RIGHT, 1, 0)) + || (veloX < -SLEEP_THRE && isColliding(CONTACT_AREA_LEFT, -1, 0)) + ) { + veloX = 0.0 + } + } + } + } + private fun elasticReflectX() { if (veloX != 0.0 && (veloX * elasticity).abs() > 0.5) { veloX = -veloX * elasticity @@ -619,35 +701,6 @@ open class ActorWithBody constructor() : Actor(), Visible { return density } - /** - * Get highest fluid resistance value from tiles that the body occupies. - * @return - */ - private val fluidResistance: Int - get() { - var resistance = 0 - - //get highest fluid density - val tilePosXStart = (nextHitbox.posX / TSIZE).roundToInt() - val tilePosYStart = (nextHitbox.posY / TSIZE).roundToInt() - val tilePosXEnd = (nextHitbox.hitboxEnd.x / TSIZE).roundToInt() - val tilePosYEnd = (nextHitbox.hitboxEnd.y / TSIZE).roundToInt() - for (y in tilePosYStart..tilePosYEnd) { - for (x in tilePosXStart..tilePosXEnd) { - val tile = map.getTileFromTerrain(x, y) - - if (TilePropCodex.getProp(tile).isFluid) { - val thisResistance = TilePropCodex.getProp(tile).movementResistance - - if (thisResistance > resistance) resistance = thisResistance - } - } - } - - return resistance - } - fun Int.resistanceToMult(): Double = 1.0 / (1 + this / 16.0) - private fun clampHitbox() { hitbox.setPositionFromPoint( clampW(hitbox.pointedX), clampH(hitbox.pointedY)) diff --git a/src/net/torvald/terrarum/gameactors/Hitbox.kt b/src/net/torvald/terrarum/gameactors/Hitbox.kt index 5b4ba89e1..76f789292 100644 --- a/src/net/torvald/terrarum/gameactors/Hitbox.kt +++ b/src/net/torvald/terrarum/gameactors/Hitbox.kt @@ -1,6 +1,8 @@ package net.torvald.terrarum.gameactors import net.torvald.point.Point2d +import org.dyn4j.geometry.ChainedVector2 +import org.dyn4j.geometry.Vector2 /** * Created by minjaesong on 16-01-15. @@ -54,6 +56,18 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double) { this.height = height } + fun translate(x: Double, y: Double) { + setPosition(posX + x, posY + y) + } + + fun translate(vec: ChainedVector2) { + translate(vec.x, vec.y) + } + + fun translate(vec: Vector2) { + translate(vec.x, vec.y) + } + fun setPosition(x1: Double, y1: Double) { hitboxStart = Point2d(x1, y1) hitboxEnd = Point2d(x1 + width, y1 + height) @@ -112,4 +126,8 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double) { val centeredY: Double get() = (hitboxStart.y + hitboxEnd.y) * 0.5f + + fun toVector(): Vector2 { + return Vector2(posX, posY) + } } diff --git a/src/net/torvald/terrarum/gameactors/Player.kt b/src/net/torvald/terrarum/gameactors/Player.kt index 88b3af96d..73da15585 100644 --- a/src/net/torvald/terrarum/gameactors/Player.kt +++ b/src/net/torvald/terrarum/gameactors/Player.kt @@ -322,10 +322,12 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan if (isFuncDown(input, EnumKeyFunc.MOVE_RIGHT) && !isFuncDown(input, EnumKeyFunc.MOVE_LEFT)) { walkHorizontal(false, AXIS_POSMAX) prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT) - } else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { + } // ↓F, ↑S + else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { walkHorizontal(true, AXIS_POSMAX) prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT) - } else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { + } // ↓F, ↓S + else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) { if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) { walkHorizontal(false, AXIS_POSMAX) prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT) @@ -333,8 +335,7 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan walkHorizontal(true, AXIS_POSMAX) prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT) } - }// ↓F, ↓S - // ↓F, ↑S + } } /** @@ -346,15 +347,16 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan walkVertical(axisY > 0, AXIS_POSMAX) } } else { - // ↑E - // ↓D + // ↑E, ↓D if (isFuncDown(input, EnumKeyFunc.MOVE_DOWN) && !isFuncDown(input, EnumKeyFunc.MOVE_UP)) { walkVertical(false, AXIS_POSMAX) prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN) - } else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { + } // ↓E, ↑D + else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { walkVertical(true, AXIS_POSMAX) prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP) - } else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { + } // ↓E, ↓D + else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) { if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) { walkVertical(false, AXIS_POSMAX) prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN) @@ -362,10 +364,7 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan walkVertical(true, AXIS_POSMAX) prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP) } - }// ↓E - // ↓D - // ↓E - // ↑D + } } } diff --git a/src/net/torvald/terrarum/gamemap/GameMap.kt b/src/net/torvald/terrarum/gamemap/GameMap.kt index 54c3ed63e..bca168cd6 100644 --- a/src/net/torvald/terrarum/gamemap/GameMap.kt +++ b/src/net/torvald/terrarum/gamemap/GameMap.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.gamemap +import org.dyn4j.geometry.Vector2 import org.newdawn.slick.SlickException class GameMap @@ -32,7 +33,7 @@ constructor(//properties //public World physWorld = new World( new Vec2(0, -TerrarumMain.game.gravitationalAccel) ); //physics /** \[m / s^2\] */ - var gravitation: Double = 9.8 + var gravitation: Vector2 = Vector2(0.0, 9.8) /** RGB in Integer */ var globalLight: Int = 0 val worldTime: WorldTime diff --git a/src/net/torvald/terrarum/tileproperties/TileProp.kt b/src/net/torvald/terrarum/tileproperties/TileProp.kt index b255df6c3..e12693e82 100644 --- a/src/net/torvald/terrarum/tileproperties/TileProp.kt +++ b/src/net/torvald/terrarum/tileproperties/TileProp.kt @@ -21,7 +21,6 @@ class TileProp { var density: Int = 0 var isFluid: Boolean = false - var movementResistance: Int = 0 var isSolid: Boolean = false diff --git a/src/net/torvald/terrarum/tileproperties/TilePropCodex.kt b/src/net/torvald/terrarum/tileproperties/TilePropCodex.kt index 72eb7ae63..262fed553 100644 --- a/src/net/torvald/terrarum/tileproperties/TilePropCodex.kt +++ b/src/net/torvald/terrarum/tileproperties/TilePropCodex.kt @@ -85,8 +85,6 @@ class TilePropCodex { prop.isWallable = boolVal(record, "wall") prop.isFallable = boolVal(record, "fall") - if (prop.isFluid) prop.movementResistance = intVal(record, "movr") - print(formatNum3(prop.id) + ":" + formatNum2(prop.damage)) println("\t" + prop.name) } diff --git a/src/net/torvald/terrarum/tileproperties/tileprop.csv b/src/net/torvald/terrarum/tileproperties/tileprop.csv index 687ef4e58..43247f85d 100644 --- a/src/net/torvald/terrarum/tileproperties/tileprop.csv +++ b/src/net/torvald/terrarum/tileproperties/tileprop.csv @@ -1,122 +1,122 @@ - "id";"dmg";"name" ; "opacity";"strength";"dsty";"fluid";"movr";"solid";"wall"; "lumcolor";"drop";"ddmg";"fall";"friction" - "0"; "0";"TILE_AIR" ; "8396808"; "0"; "1"; "0"; "0"; "0"; "0"; "0"; "0"; "0"; "0";"4" - "1"; "0";"TILE_STONE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "1"; "0"; "1"; "0"; "0";"16" - "1"; "1";"TILE_STONE_QUARRIED" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "1"; "0"; "1"; "1"; "0";"16" - "2"; "0";"TILE_DIRT" ; "33587232"; "6";"1400"; "0"; "0"; "1"; "1"; "0"; "2"; "0"; "0";"16" - "2"; "1";"TILE_GRASS" ; "33587232"; "6";"1400"; "0"; "0"; "1"; "1"; "0"; "2"; "1"; "0";"16" - "3"; "0";"TILE_PLANK_NORMAL" ; "33587232"; "12"; "740"; "0"; "0"; "1"; "1"; "0"; "3"; "0"; "0";"16" - "3"; "1";"TILE_PLANK_EBONY" ; "33587232"; "12";"1200"; "0"; "0"; "1"; "1"; "0"; "3"; "1"; "0";"16" - "3"; "2";"TILE_PLANK_BIRCH" ; "33587232"; "12"; "670"; "0"; "0"; "1"; "1"; "0"; "3"; "2"; "0";"16" - "3"; "3";"TILE_PLANK_BLOODROSE" ; "33587232"; "12"; "900"; "0"; "0"; "1"; "1"; "0"; "3"; "3"; "0";"16" - "4"; "0";"TILE_TRUNK_NORMAL" ; "33587232"; "12"; "740"; "0"; "0"; "1"; "0"; "0"; "3"; "0"; "0";"16" - "4"; "1";"TILE_TRUNK_EBONY" ; "33587232"; "12";"1200"; "0"; "0"; "1"; "0"; "0"; "3"; "1"; "0";"16" - "4"; "2";"TILE_TRUNK_BIRCH" ; "33587232"; "12"; "670"; "0"; "0"; "1"; "0"; "0"; "3"; "2"; "0";"16" - "4"; "3";"TILE_TRUNK_BLOODROSE" ; "33587232"; "12"; "900"; "0"; "0"; "1"; "0"; "0"; "3"; "3"; "0";"16" - "5"; "0";"TILE_SAND" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "0"; "1";"16" - "5"; "1";"TILE_SAND_WHITE" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "1"; "1";"16" - "5"; "2";"TILE_SAND_RED" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "2"; "1";"16" - "5"; "3";"TILE_SAND_DESERT" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "3"; "1";"16" - "5"; "4";"TILE_SAND_BLACK" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "4"; "1";"16" - "5"; "5";"TILE_SAND_GREEN" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "5"; "5"; "1";"16" - "6"; "0";"TILE_GRAVEL" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "6"; "0"; "1";"16" - "6"; "1";"TILE_GRAVEL_GREY" ; "33587232"; "6";"2400"; "0"; "0"; "1"; "0"; "0"; "6"; "1"; "1";"16" - "7"; "0";"TILE_ORE_MALACHITE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "0"; "0";"16" - "7"; "1";"TILE_ORE_HEMATITE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "1"; "0";"16" - "7"; "2";"TILE_ORE_NATURAL_GOLD" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "2"; "0";"16" - "7"; "3";"TILE_ORE_NATURAL_SILVER" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "3"; "0";"16" - "7"; "4";"TILE_ORE_RUTILE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "4"; "0";"16" - "7"; "5";"TILE_ORE_AURICHALCUMITE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "7"; "5"; "0";"16" - "8"; "0";"TILE_GEM_RUBY" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "0"; "0";"16" - "8"; "1";"TILE_GEM_EMERALD" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "1"; "0";"16" - "8"; "2";"TILE_GEM_SAPPHIRE" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "2"; "0";"16" - "8"; "3";"TILE_GEM_TOPAZ" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "3"; "0";"16" - "8"; "4";"TILE_GEM_DIAMOND" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "4"; "0";"16" - "8"; "5";"TILE_GEM_AMETHYST" ; "33587232"; "25";"2400"; "0"; "0"; "1"; "0"; "0"; "8"; "5"; "0";"16" - "9"; "0";"TILE_SNOW" ; "33587232"; "6"; "500"; "0"; "0"; "1"; "1"; "0"; "9"; "0"; "0";"16" - "9"; "1";"TILE_ICE_FRAGILE" ; "13644813"; "1"; "930"; "0"; "0"; "1"; "0"; "0"; "9"; "1"; "0";"16" - "9"; "2";"TILE_ICE_NATURAL" ; "27289626"; "25"; "930"; "0"; "0"; "1"; "1"; "0"; "9"; "2"; "0"; "8" - "9"; "3";"TILE_ICE_CLEAR_MAGICAL" ; "33587232"; "25";"3720"; "0"; "0"; "1"; "1"; "19955770"; "9"; "3"; "0"; "8" - "10"; "0";"TILE_PLATFORM_STONE" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "0"; "0";"16" - "10"; "1";"TILE_PLATFORM_WOODEN" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "1"; "0";"16" - "10"; "2";"TILE_PLATFORM_EBONY" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "2"; "0";"16" - "10"; "3";"TILE_PLATFORM_BIRCH" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "3"; "0";"16" - "10"; "4";"TILE_PLATFORM_BLOODROSE" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "0"; "10"; "4"; "0";"16" - "11"; "0";"TILE_TORCH" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "267518016"; "11"; "0"; "0";"16" - "11"; "1";"TILE_TORCH_FROST" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "81916159"; "11"; "1"; "0";"16" - "12"; "0";"TILE_TORCH" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "0"; "11"; "0"; "0";"16" - "12"; "1";"TILE_TORCH_FROST" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "0"; "11"; "1"; "0";"16" - "13"; "0";"TILE_ILLUMINATOR_WHITE" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "246656235"; "13"; "0"; "0";"16" - "13"; "1";"TILE_ILLUMINATOR_YELLOW" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "246656000"; "13"; "1"; "0";"16" - "13"; "2";"TILE_ILLUMINATOR_ORANGE" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "246602752"; "13"; "2"; "0";"16" - "13"; "3";"TILE_ILLUMINATOR_RED" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "246415360"; "13"; "3"; "0";"16" - "13"; "4";"TILE_ILLUMINATOR_FUCHSIA" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "246415543"; "13"; "4"; "0";"16" - "13"; "5";"TILE_ILLUMINATOR_PURPLE" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "191889643"; "13"; "5"; "0";"16" - "13"; "6";"TILE_ILLUMINATOR_BLUE" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "235"; "13"; "6"; "0";"16" - "13"; "7";"TILE_ILLUMINATOR_CYAN" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "207083"; "13"; "7"; "0";"16" - "13"; "8";"TILE_ILLUMINATOR_GREEN" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "68364288"; "13"; "8"; "0";"16" - "13"; "9";"TILE_ILLUMINATOR_GREEN_DARK";"8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "33660928"; "13"; "9"; "0";"16" - "13"; "10";"TILE_ILLUMINATOR_BROWN" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "89161728"; "13"; "10"; "0";"16" - "13"; "11";"TILE_ILLUMINATOR_TAN" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "157392948"; "13"; "11"; "0";"16" - "13"; "12";"TILE_ILLUMINATOR_GREY_LIGHT";"8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "198374589"; "13"; "12"; "0";"16" - "13"; "13";"TILE_ILLUMINATOR_GREY_MED"; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "122803317"; "13"; "13"; "0";"16" - "13"; "14";"TILE_ILLUMINATOR_GREY_DARK"; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "68224065"; "13"; "14"; "0";"16" - "13"; "15";"TILE_ILLUMINATOR_BLACK" ; "8396808"; "0"; "N/A"; "0"; "0"; "1"; "1"; "116392191"; "13"; "15"; "0";"16" - "14"; "0";"TILE_ILLUMINATOR_WHITE" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "0"; "0";"16" - "14"; "1";"TILE_ILLUMINATOR_YELLOW" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "1"; "0";"16" - "14"; "2";"TILE_ILLUMINATOR_ORANGE" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "2"; "0";"16" - "14"; "3";"TILE_ILLUMINATOR_RED" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "3"; "0";"16" - "14"; "4";"TILE_ILLUMINATOR_FUCHSIA" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "4"; "0";"16" - "14"; "5";"TILE_ILLUMINATOR_PURPLE" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "5"; "0";"16" - "14"; "6";"TILE_ILLUMINATOR_BLUE" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "6"; "0";"16" - "14"; "7";"TILE_ILLUMINATOR_CYAN" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "7"; "0";"16" - "14"; "8";"TILE_ILLUMINATOR_GREEN" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "8"; "0";"16" - "14"; "9";"TILE_ILLUMINATOR_GREEN_DARK";"33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "9"; "0";"16" - "14"; "10";"TILE_ILLUMINATOR_BROWN" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "10"; "0";"16" - "14"; "11";"TILE_ILLUMINATOR_TAN" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "11"; "0";"16" - "14"; "12";"TILE_ILLUMINATOR_GREY_LIGHT";"33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "12"; "0";"16" - "14"; "13";"TILE_ILLUMINATOR_GREY_MED"; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "13"; "0";"16" - "14"; "14";"TILE_ILLUMINATOR_GREY_DARK";"33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "14"; "0";"16" - "14"; "15";"TILE_ILLUMINATOR_BLACK" ; "33587232"; "0"; "N/A"; "0"; "0"; "1"; "1"; "0"; "13"; "15"; "0";"16" - "15"; "0";"TILE_SANDSTONE" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "0"; "0";"16" - "15"; "1";"TILE_SANDSTONE_WHITE" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "1"; "0";"16" - "15"; "2";"TILE_SANDSTONE_RED" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "2"; "0";"16" - "15"; "3";"TILE_SANDSTONE_DESERT" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "3"; "0";"16" - "15"; "4";"TILE_SANDSTONE_BLACK" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "4"; "0";"16" - "15"; "5";"TILE_SANDSTONE_BLACK" ; "33587232"; "25";"1900"; "0"; "0"; "1"; "1"; "0"; "15"; "5"; "0";"16" - "16"; "0";"TILE_LANTERN_IRON_REGULAR"; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "267619480"; "16"; "0"; "0";"16" -"254"; "0";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "1";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "2";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "3";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "4";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "5";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "6";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "7";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "8";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "9";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "10";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "11";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "12";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "13";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "14";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"254"; "15";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "48"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" -"255"; "0";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "1";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "2";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "3";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "4";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "5";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "6";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "7";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "8";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "9";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "10";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "11";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "12";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "13";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "14";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"255"; "15";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "12"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" -"256"; "0";"TILE_NULL" ; "0"; "-1";"2600"; "0"; "0"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" + "id";"dmg";"name" ; "opacity";"strength";"dsty";"fluid";"solid";"wall"; "lumcolor";"drop";"ddmg";"fall";"friction" + "0"; "0";"TILE_AIR" ; "8396808"; "0"; "1"; "1"; "0"; "0"; "0"; "0"; "0"; "0";"4" + "1"; "0";"TILE_STONE" ; "33587232"; "25";"2400"; "0"; "1"; "1"; "0"; "1"; "0"; "0";"16" + "1"; "1";"TILE_STONE_QUARRIED" ; "33587232"; "25";"2400"; "0"; "1"; "1"; "0"; "1"; "1"; "0";"16" + "2"; "0";"TILE_DIRT" ; "33587232"; "6";"1400"; "0"; "1"; "1"; "0"; "2"; "0"; "0";"16" + "2"; "1";"TILE_GRASS" ; "33587232"; "6";"1400"; "0"; "1"; "1"; "0"; "2"; "1"; "0";"16" + "3"; "0";"TILE_PLANK_NORMAL" ; "33587232"; "12"; "740"; "0"; "1"; "1"; "0"; "3"; "0"; "0";"16" + "3"; "1";"TILE_PLANK_EBONY" ; "33587232"; "12";"1200"; "0"; "1"; "1"; "0"; "3"; "1"; "0";"16" + "3"; "2";"TILE_PLANK_BIRCH" ; "33587232"; "12"; "670"; "0"; "1"; "1"; "0"; "3"; "2"; "0";"16" + "3"; "3";"TILE_PLANK_BLOODROSE" ; "33587232"; "12"; "900"; "0"; "1"; "1"; "0"; "3"; "3"; "0";"16" + "4"; "0";"TILE_TRUNK_NORMAL" ; "33587232"; "12"; "740"; "0"; "1"; "0"; "0"; "3"; "0"; "0";"16" + "4"; "1";"TILE_TRUNK_EBONY" ; "33587232"; "12";"1200"; "0"; "1"; "0"; "0"; "3"; "1"; "0";"16" + "4"; "2";"TILE_TRUNK_BIRCH" ; "33587232"; "12"; "670"; "0"; "1"; "0"; "0"; "3"; "2"; "0";"16" + "4"; "3";"TILE_TRUNK_BLOODROSE" ; "33587232"; "12"; "900"; "0"; "1"; "0"; "0"; "3"; "3"; "0";"16" + "5"; "0";"TILE_SAND" ; "33587232"; "6";"2400"; "0"; "1"; "0"; "0"; "5"; "0"; "1";"16" + "5"; "1";"TILE_SAND_WHITE" ; "33587232"; "6";"2400"; "0"; "1"; "0"; "0"; "5"; "1"; "1";"16" + "5"; "2";"TILE_SAND_RED" ; "33587232"; "6";"2400"; "0"; "1"; "0"; "0"; "5"; "2"; "1";"16" + "5"; "3";"TILE_SAND_DESERT" ; "33587232"; "6";"2400"; "0"; "1"; "0"; "0"; "5"; "3"; "1";"16" + "5"; "4";"TILE_SAND_BLACK" ; "33587232"; "6";"2400"; "0"; "1"; "0"; "0"; "5"; "4"; "1";"16" + "5"; "5";"TILE_SAND_GREEN" ; "33587232"; "6";"2400"; "0"; "1"; "0"; "0"; "5"; "5"; "1";"16" + "6"; "0";"TILE_GRAVEL" ; "33587232"; "6";"2400"; "0"; "1"; "0"; "0"; "6"; "0"; "1";"16" + "6"; "1";"TILE_GRAVEL_GREY" ; "33587232"; "6";"2400"; "0"; "1"; "0"; "0"; "6"; "1"; "1";"16" + "7"; "0";"TILE_ORE_MALACHITE" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "7"; "0"; "0";"16" + "7"; "1";"TILE_ORE_HEMATITE" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "7"; "1"; "0";"16" + "7"; "2";"TILE_ORE_NATURAL_GOLD" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "7"; "2"; "0";"16" + "7"; "3";"TILE_ORE_NATURAL_SILVER" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "7"; "3"; "0";"16" + "7"; "4";"TILE_ORE_RUTILE" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "7"; "4"; "0";"16" + "7"; "5";"TILE_ORE_AURICHALCUMITE" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "7"; "5"; "0";"16" + "8"; "0";"TILE_GEM_RUBY" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "8"; "0"; "0";"16" + "8"; "1";"TILE_GEM_EMERALD" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "8"; "1"; "0";"16" + "8"; "2";"TILE_GEM_SAPPHIRE" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "8"; "2"; "0";"16" + "8"; "3";"TILE_GEM_TOPAZ" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "8"; "3"; "0";"16" + "8"; "4";"TILE_GEM_DIAMOND" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "8"; "4"; "0";"16" + "8"; "5";"TILE_GEM_AMETHYST" ; "33587232"; "25";"2400"; "0"; "1"; "0"; "0"; "8"; "5"; "0";"16" + "9"; "0";"TILE_SNOW" ; "33587232"; "6"; "500"; "0"; "1"; "1"; "0"; "9"; "0"; "0";"16" + "9"; "1";"TILE_ICE_FRAGILE" ; "13644813"; "1"; "930"; "0"; "1"; "0"; "0"; "9"; "1"; "0";"16" + "9"; "2";"TILE_ICE_NATURAL" ; "27289626"; "25"; "930"; "0"; "1"; "1"; "0"; "9"; "2"; "0"; "8" + "9"; "3";"TILE_ICE_CLEAR_MAGICAL" ; "33587232"; "25";"3720"; "0"; "1"; "1"; "19955770"; "9"; "3"; "0"; "8" + "10"; "0";"TILE_PLATFORM_STONE" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "10"; "0"; "0";"16" + "10"; "1";"TILE_PLATFORM_WOODEN" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "10"; "1"; "0";"16" + "10"; "2";"TILE_PLATFORM_EBONY" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "10"; "2"; "0";"16" + "10"; "3";"TILE_PLATFORM_BIRCH" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "10"; "3"; "0";"16" + "10"; "4";"TILE_PLATFORM_BLOODROSE" ; "8396808"; "1"; "N/A"; "0"; "0"; "0"; "0"; "10"; "4"; "0";"16" + "11"; "0";"TILE_TORCH" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "267518016"; "11"; "0"; "0";"16" + "11"; "1";"TILE_TORCH_FROST" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "81916159"; "11"; "1"; "0";"16" + "12"; "0";"TILE_TORCH" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "11"; "0"; "0";"16" + "12"; "1";"TILE_TORCH_FROST" ; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "0"; "11"; "1"; "0";"16" + "13"; "0";"TILE_ILLUMINATOR_WHITE" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "246656235"; "13"; "0"; "0";"16" + "13"; "1";"TILE_ILLUMINATOR_YELLOW" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "246656000"; "13"; "1"; "0";"16" + "13"; "2";"TILE_ILLUMINATOR_ORANGE" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "246602752"; "13"; "2"; "0";"16" + "13"; "3";"TILE_ILLUMINATOR_RED" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "246415360"; "13"; "3"; "0";"16" + "13"; "4";"TILE_ILLUMINATOR_FUCHSIA" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "246415543"; "13"; "4"; "0";"16" + "13"; "5";"TILE_ILLUMINATOR_PURPLE" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "191889643"; "13"; "5"; "0";"16" + "13"; "6";"TILE_ILLUMINATOR_BLUE" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "235"; "13"; "6"; "0";"16" + "13"; "7";"TILE_ILLUMINATOR_CYAN" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "207083"; "13"; "7"; "0";"16" + "13"; "8";"TILE_ILLUMINATOR_GREEN" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "68364288"; "13"; "8"; "0";"16" + "13"; "9";"TILE_ILLUMINATOR_GREEN_DARK";"8396808"; "0"; "N/A"; "0"; "1"; "1"; "33660928"; "13"; "9"; "0";"16" + "13"; "10";"TILE_ILLUMINATOR_BROWN" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "89161728"; "13"; "10"; "0";"16" + "13"; "11";"TILE_ILLUMINATOR_TAN" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "157392948"; "13"; "11"; "0";"16" + "13"; "12";"TILE_ILLUMINATOR_GREY_LIGHT";"8396808"; "0"; "N/A"; "0"; "1"; "1"; "198374589"; "13"; "12"; "0";"16" + "13"; "13";"TILE_ILLUMINATOR_GREY_MED"; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "122803317"; "13"; "13"; "0";"16" + "13"; "14";"TILE_ILLUMINATOR_GREY_DARK"; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "68224065"; "13"; "14"; "0";"16" + "13"; "15";"TILE_ILLUMINATOR_BLACK" ; "8396808"; "0"; "N/A"; "0"; "1"; "1"; "116392191"; "13"; "15"; "0";"16" + "14"; "0";"TILE_ILLUMINATOR_WHITE" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "0"; "0";"16" + "14"; "1";"TILE_ILLUMINATOR_YELLOW" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "1"; "0";"16" + "14"; "2";"TILE_ILLUMINATOR_ORANGE" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "2"; "0";"16" + "14"; "3";"TILE_ILLUMINATOR_RED" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "3"; "0";"16" + "14"; "4";"TILE_ILLUMINATOR_FUCHSIA" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "4"; "0";"16" + "14"; "5";"TILE_ILLUMINATOR_PURPLE" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "5"; "0";"16" + "14"; "6";"TILE_ILLUMINATOR_BLUE" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "6"; "0";"16" + "14"; "7";"TILE_ILLUMINATOR_CYAN" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "7"; "0";"16" + "14"; "8";"TILE_ILLUMINATOR_GREEN" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "8"; "0";"16" + "14"; "9";"TILE_ILLUMINATOR_GREEN_DARK";"33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "9"; "0";"16" + "14"; "10";"TILE_ILLUMINATOR_BROWN" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "10"; "0";"16" + "14"; "11";"TILE_ILLUMINATOR_TAN" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "11"; "0";"16" + "14"; "12";"TILE_ILLUMINATOR_GREY_LIGHT";"33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "12"; "0";"16" + "14"; "13";"TILE_ILLUMINATOR_GREY_MED"; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "13"; "0";"16" + "14"; "14";"TILE_ILLUMINATOR_GREY_DARK";"33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "14"; "0";"16" + "14"; "15";"TILE_ILLUMINATOR_BLACK" ; "33587232"; "0"; "N/A"; "0"; "1"; "1"; "0"; "13"; "15"; "0";"16" + "15"; "0";"TILE_SANDSTONE" ; "33587232"; "25";"1900"; "0"; "1"; "1"; "0"; "15"; "0"; "0";"16" + "15"; "1";"TILE_SANDSTONE_WHITE" ; "33587232"; "25";"1900"; "0"; "1"; "1"; "0"; "15"; "1"; "0";"16" + "15"; "2";"TILE_SANDSTONE_RED" ; "33587232"; "25";"1900"; "0"; "1"; "1"; "0"; "15"; "2"; "0";"16" + "15"; "3";"TILE_SANDSTONE_DESERT" ; "33587232"; "25";"1900"; "0"; "1"; "1"; "0"; "15"; "3"; "0";"16" + "15"; "4";"TILE_SANDSTONE_BLACK" ; "33587232"; "25";"1900"; "0"; "1"; "1"; "0"; "15"; "4"; "0";"16" + "15"; "5";"TILE_SANDSTONE_BLACK" ; "33587232"; "25";"1900"; "0"; "1"; "1"; "0"; "15"; "5"; "0";"16" + "16"; "0";"TILE_LANTERN_IRON_REGULAR"; "8396808"; "0"; "N/A"; "0"; "0"; "0"; "267619480"; "16"; "0"; "0";"16" +"254"; "0";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "1";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "2";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "3";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "4";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "5";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "6";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "7";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "8";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "9";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "10";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "11";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "12";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "13";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "14";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"254"; "15";"TILE_LAVA" ;"260301048"; "100";"2600"; "1"; "0"; "0"; "205574144"; "N/A"; "N/A"; "0";"16" +"255"; "0";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "1";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "2";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "3";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "4";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "5";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "6";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "7";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "8";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "9";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "10";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "11";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "12";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "13";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "14";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"255"; "15";"TILE_WATER" ; "27282445"; "100";"1000"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" +"256"; "0";"TILE_NULL" ; "0"; "-1";"2600"; "0"; "0"; "0"; "0"; "N/A"; "N/A"; "0";"16" # Friction: 0: frictionless, <16: slippery, 16: regular, >16: sticky # Opacity/Lumcolor: 40-step RGB # Solid: whether the tile has full collision diff --git a/src/org/dyn4j/geometry/ChainedVector2.kt b/src/org/dyn4j/geometry/ChainedVector2.kt new file mode 100644 index 000000000..4957e64ac --- /dev/null +++ b/src/org/dyn4j/geometry/ChainedVector2.kt @@ -0,0 +1,873 @@ +/* + * Copyright (c) 2010-2015 William Bittle http://www.dyn4j.org/ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of dyn4j nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Kotlin translated and modified code Copyright (c) 2016 Torvald aka skyhi14. + */ +package org.dyn4j.geometry + +import org.dyn4j.Epsilon + +/** + * This class represents a vector or point in 2D space. + * + * + * The operations [ChainedVector2.setMagnitude], [ChainedVector2.getNormalized], + * [ChainedVector2.project], and [ChainedVector2.normalize] require the [ChainedVector2] + * to be non-zero in length. + * + * + * Some methods also return the vector to facilitate chaining. For example: + * + * Vector a = new Vector(); + * a.zero().plus(1, 2).times(2); + * + * In this Kotlin code, you can use regular operations like + - * /. + * + * |operator |function | + * |-----------|------------| + * |a + b |a.plus(b) | equivalent of plusAssign + * |a - b |a.minus(b) | equivalent of minusAssign + * |a * b |a.times(b) | equivalent of timesAssign + * |a / b |a.div(b) | equivalent of divAssign + * |a dot b |a.dot(b) | equivalent of dotAssign + * |a cross b |a.cross(b) | equivalent of crossAssign + * |!a |negate(a) | + * |a rotate th|a.rotate(th)| equivalent of rotateAssign + * + * @author William Bittle + * * + * @version 3.1.11 + * * + * @since 1.0.0 + */ +class ChainedVector2 { + + /** The magnitude of the x component of this [ChainedVector2] */ + var x: Double = 0.toDouble() + + /** The magnitude of the y component of this [ChainedVector2] */ + var y: Double = 0.toDouble() + + /** Default constructor. */ + constructor() { + } + + /** + * Copy constructor. + * @param vector the [ChainedVector2] to copy from + */ + constructor(vector: ChainedVector2) { + this.x = vector.x + this.y = vector.y + } + + /** + * Optional constructor. + * @param x the x component + * * + * @param y the y component + */ + constructor(x: Double, y: Double) { + this.x = x + this.y = y + } + + /** + * Optional constructor. + * @param vector non-chainable Vector2 + */ + constructor(vector: Vector2) { + this.x = vector.x + this.y = vector.y + } + + /** + * Creates a [ChainedVector2] from the first point to the second point. + * @param x1 the x coordinate of the first point + * * + * @param y1 the y coordinate of the first point + * * + * @param x2 the x coordinate of the second point + * * + * @param y2 the y coordinate of the second point + */ + constructor(x1: Double, y1: Double, x2: Double, y2: Double) { + this.x = x2 - x1 + this.y = y2 - y1 + } + + /** + * Creates a [ChainedVector2] from the first point to the second point. + * @param p1 the first point + * * + * @param p2 the second point + */ + constructor(p1: ChainedVector2, p2: ChainedVector2) { + this.x = p2.x - p1.x + this.y = p2.y - p1.y + } + + /** + * Creates a unit length vector in the given direction. + * @param direction the direction in radians + * * + * @since 3.0.1 + */ + constructor(direction: Double) { + this.x = Math.cos(direction) + this.y = Math.sin(direction) + } + + /** + * Returns a copy of this [ChainedVector2]. + * @return [ChainedVector2] + */ + fun copy(): ChainedVector2 { + return ChainedVector2(this.x, this.y) + } + + /** + * Returns the distance from this point to the given point. + * @param x the x coordinate of the point + * * + * @param y the y coordinate of the point + * * + * @return double + */ + fun distance(x: Double, y: Double): Double { + //return Math.hypot(this.x - x, this.y - y); + val dx = this.x - x + val dy = this.y - y + return Math.sqrt(dx * dx + dy * dy) + } + + /** + * Returns the distance from this point to the given point. + * @param point the point + * * + * @return double + */ + fun distance(point: ChainedVector2): Double { + //return Math.hypot(this.x - point.x, this.y - point.y); + val dx = this.x - point.x + val dy = this.y - point.y + return Math.sqrt(dx * dx + dy * dy) + } + + /** + * Returns the distance from this point to the given point squared. + * @param x the x coordinate of the point + * * + * @param y the y coordinate of the point + * * + * @return double + */ + fun distanceSquared(x: Double, y: Double): Double { + //return (this.x - x) * (this.x - x) + (this.y - y) * (this.y - y); + val dx = this.x - x + val dy = this.y - y + return dx * dx + dy * dy + } + + /** + * Returns the distance from this point to the given point squared. + * @param point the point + * * + * @return double + */ + fun distanceSquared(point: ChainedVector2): Double { + //return (this.x - point.x) * (this.x - point.x) + (this.y - point.y) * (this.y - point.y); + val dx = this.x - point.x + val dy = this.y - point.y + return dx * dx + dy * dy + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + override fun hashCode(): Int { + val prime = 31 + var result = 1 + var temp: Long + temp = java.lang.Double.doubleToLongBits(x) + result = prime * result + (temp xor temp.ushr(32)).toInt() + temp = java.lang.Double.doubleToLongBits(y) + result = prime * result + (temp xor temp.ushr(32)).toInt() + return result + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + override fun equals(obj: Any?): Boolean { + if (obj == null) return false + if (obj === this) return true + if (obj is ChainedVector2) { + return this.x == obj.x && this.y == obj.y + } + return false + } + + /** + * Returns true if the x and y components of this [ChainedVector2] + * are the same as the given [ChainedVector2]. + * @param vector the [ChainedVector2] to compare to + * * + * @return boolean + */ + fun equals(vector: ChainedVector2?): Boolean { + if (vector == null) return false + if (this === vector) { + return true + } + else { + return this.x == vector.x && this.y == vector.y + } + } + + /** + * Returns true if the x and y components of this [ChainedVector2] + * are the same as the given x and y components. + * @param x the x coordinate of the [ChainedVector2] to compare to + * * + * @param y the y coordinate of the [ChainedVector2] to compare to + * * + * @return boolean + */ + fun equals(x: Double, y: Double): Boolean { + return this.x == x && this.y == y + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + override fun toString(): String { + val sb = StringBuilder() + sb.append("(").append(this.x).append(", ").append(this.y).append(")") + return sb.toString() + } + + /** + * Sets this [ChainedVector2] to the given [ChainedVector2]. + * @param vector the [ChainedVector2] to set this [ChainedVector2] to + * * + * @return [ChainedVector2] this vector + */ + fun set(vector: ChainedVector2): ChainedVector2 { + this.x = vector.x + this.y = vector.y + return this + } + + /** + * Sets this [ChainedVector2] to the given [ChainedVector2]. + * @param x the x component of the [ChainedVector2] to set this [ChainedVector2] to + * * + * @param y the y component of the [ChainedVector2] to set this [ChainedVector2] to + * * + * @return [ChainedVector2] this vector + */ + fun set(x: Double, y: Double): ChainedVector2 { + this.x = x + this.y = y + return this + } + + /** + * Returns the x component of this [ChainedVector2]. + * @return [ChainedVector2] + */ + val xComponent: ChainedVector2 + get() = ChainedVector2(this.x, 0.0) + + /** + * Returns the y component of this [ChainedVector2]. + * @return [ChainedVector2] + */ + val yComponent: ChainedVector2 + get() = ChainedVector2(0.0, this.y) + + /** + * Returns the magnitude of this [ChainedVector2]. + * @return double + */ + // the magnitude is just the pathagorean theorem + val magnitude: Double + get() = Math.sqrt(this.x * this.x + this.y * this.y) + + /** + * Returns the magnitude of this [ChainedVector2] squared. + * @return double + */ + val magnitudeSquared: Double + get() = this.x * this.x + this.y * this.y + + /** + * Sets the magnitude of the [ChainedVector2]. + * @param magnitude the magnitude + * * + * @return [ChainedVector2] this vector + */ + fun setMagnitude(magnitude: Double): ChainedVector2 { + // check the given magnitude + if (Math.abs(magnitude) <= Epsilon.E) { + this.x = 0.0 + this.y = 0.0 + return this + } + // is this vector a zero vector? + if (this.isZero) { + return this + } + // get the magnitude + var mag = Math.sqrt(this.x * this.x + this.y * this.y) + // normalize and multiply by the new magnitude + mag = magnitude / mag + this.x *= mag + this.y *= mag + return this + } + + /** + * Returns the direction of this [ChainedVector2] + * as an angle in radians. + * @return double angle in radians [-, ] + */ + val direction: Double + get() = Math.atan2(this.y, this.x) + + /** + * Sets the direction of this [ChainedVector2]. + * @param angle angle in radians + * * + * @return [ChainedVector2] this vector + */ + fun setDirection(angle: Double): ChainedVector2 { + //double magnitude = Math.hypot(this.x, this.y); + val magnitude = Math.sqrt(this.x * this.x + this.y * this.y) + this.x = magnitude * Math.cos(angle) + this.y = magnitude * Math.sin(angle) + return this + } + + /** + * Adds the given [ChainedVector2] to this [ChainedVector2]. + * @param vector the [ChainedVector2] + * * + * @return [ChainedVector2] this vector + */ + operator fun plus(vector: ChainedVector2): ChainedVector2 { + this.x += vector.x + this.y += vector.y + return this + } + + /** + * Adds the given [ChainedVector2] to this [ChainedVector2]. + * @param x the x component of the [ChainedVector2] + * * + * @param y the y component of the [ChainedVector2] + * * + * @return [ChainedVector2] this vector + */ + fun plus(x: Double, y: Double): ChainedVector2 { + this.x += x + this.y += y + return this + } + + /** + * Adds this [ChainedVector2] and the given [ChainedVector2] returning + * a new [ChainedVector2] containing the result. + * @param vector the [ChainedVector2] + * * + * @return [ChainedVector2] + */ + fun sum(vector: ChainedVector2): ChainedVector2 { + return ChainedVector2(this.x + vector.x, this.y + vector.y) + } + + /** + * Adds this [ChainedVector2] and the given [ChainedVector2] returning + * a new [ChainedVector2] containing the result. + * @param x the x component of the [ChainedVector2] + * * + * @param y the y component of the [ChainedVector2] + * * + * @return [ChainedVector2] + */ + fun sum(x: Double, y: Double): ChainedVector2 { + return ChainedVector2(this.x + x, this.y + y) + } + + /** + * Subtracts the given [ChainedVector2] from this [ChainedVector2]. + * @param vector the [ChainedVector2] + * * + * @return [ChainedVector2] this vector + */ + operator fun minus(vector: ChainedVector2): ChainedVector2 { + this.x -= vector.x + this.y -= vector.y + return this + } + + /** + * Subtracts the given [ChainedVector2] from this [ChainedVector2]. + * @param x the x component of the [ChainedVector2] + * * + * @param y the y component of the [ChainedVector2] + * * + * @return [ChainedVector2] this vector + */ + fun minus(x: Double, y: Double): ChainedVector2 { + this.x -= x + this.y -= y + return this + } + + /** + * Subtracts the given [ChainedVector2] from this [ChainedVector2] returning + * a new [ChainedVector2] containing the result. + * @param vector the [ChainedVector2] + * * + * @return [ChainedVector2] + */ + fun difference(vector: ChainedVector2): ChainedVector2 { + return ChainedVector2(this.x - vector.x, this.y - vector.y) + } + + /** + * Subtracts the given [ChainedVector2] from this [ChainedVector2] returning + * a new [ChainedVector2] containing the result. + * @param x the x component of the [ChainedVector2] + * * + * @param y the y component of the [ChainedVector2] + * * + * @return [ChainedVector2] + */ + fun difference(x: Double, y: Double): ChainedVector2 { + return ChainedVector2(this.x - x, this.y - y) + } + + /** + * Creates a [ChainedVector2] from this [ChainedVector2] to the given [ChainedVector2]. + * @param vector the [ChainedVector2] + * * + * @return [ChainedVector2] + */ + fun to(vector: ChainedVector2): ChainedVector2 { + return ChainedVector2(vector.x - this.x, vector.y - this.y) + } + + /** + * Creates a [ChainedVector2] from this [ChainedVector2] to the given [ChainedVector2]. + * @param x the x component of the [ChainedVector2] + * * + * @param y the y component of the [ChainedVector2] + * * + * @return [ChainedVector2] + */ + fun to(x: Double, y: Double): ChainedVector2 { + return ChainedVector2(x - this.x, y - this.y) + } + + /** + * Multiplies this [ChainedVector2] by the given scalar. + * @param scalar the scalar + * * + * @return [ChainedVector2] this vector + */ + operator fun times(scalar: Double): ChainedVector2 { + this.x *= scalar + this.y *= scalar + return this + } + + /** + * Multiplies this [ChainedVector2] by the given scalar. + * @param scalar the scalar + * * + * @return [ChainedVector2] this vector + */ + operator fun div(scalar: Double): ChainedVector2 { + this.x /= scalar + this.y /= scalar + return this + } + + /** + * Multiplies this [ChainedVector2] by the given scalar returning + * a new [ChainedVector2] containing the result. + * @param scalar the scalar + * * + * @return [ChainedVector2] + */ + infix fun product(scalar: Double): ChainedVector2 { + return ChainedVector2(this.x * scalar, this.y * scalar) + } + + /** + * Returns the dot product of the given [ChainedVector2] + * and this [ChainedVector2]. + * @param vector the [ChainedVector2] + * * + * @return double + */ + infix fun dot(vector: ChainedVector2): Double { + return this.x * vector.x + this.y * vector.y + } + + /** + * Returns the dot product of the given [ChainedVector2] + * and this [ChainedVector2]. + * @param x the x component of the [ChainedVector2] + * * + * @param y the y component of the [ChainedVector2] + * * + * @return double + */ + fun dot(x: Double, y: Double): Double { + return this.x * x + this.y * y + } + + /** + * Returns the cross product of the this [ChainedVector2] and the given [ChainedVector2]. + * @param vector the [ChainedVector2] + * * + * @return double + */ + infix fun cross(vector: ChainedVector2): Double { + return this.x * vector.y - this.y * vector.x + } + + /** + * Returns the cross product of the this [ChainedVector2] and the given [ChainedVector2]. + * @param x the x component of the [ChainedVector2] + * * + * @param y the y component of the [ChainedVector2] + * * + * @return double + */ + fun cross(x: Double, y: Double): Double { + return this.x * y - this.y * x + } + + /** + * Returns the cross product of this [ChainedVector2] and the z value of the right [ChainedVector2]. + * @param z the z component of the [ChainedVector2] + * * + * @return [ChainedVector2] + */ + infix fun cross(z: Double): ChainedVector2 { + return ChainedVector2(-1.0 * this.y * z, this.x * z) + } + + /** + * Returns true if the given [ChainedVector2] is orthogonal (perpendicular) + * to this [ChainedVector2]. + * + * + * If the dot product of this vector and the given vector is + * zero then we know that they are perpendicular + * @param vector the [ChainedVector2] + * * + * @return boolean + */ + fun isOrthogonal(vector: ChainedVector2): Boolean { + return if (Math.abs(this.x * vector.x + this.y * vector.y) <= Epsilon.E) true else false + } + + /** + * Returns true if the given [ChainedVector2] is orthogonal (perpendicular) + * to this [ChainedVector2]. + * + * + * If the dot product of this vector and the given vector is + * zero then we know that they are perpendicular + * @param x the x component of the [ChainedVector2] + * * + * @param y the y component of the [ChainedVector2] + * * + * @return boolean + */ + fun isOrthogonal(x: Double, y: Double): Boolean { + return if (Math.abs(this.x * x + this.y * y) <= Epsilon.E) true else false + } + + /** + * Returns true if this [ChainedVector2] is the zero [ChainedVector2]. + * @return boolean + */ + val isZero: Boolean + get() = Math.abs(this.x) <= Epsilon.E && Math.abs(this.y) <= Epsilon.E + + /** + * Negates this [ChainedVector2]. + * @return [ChainedVector2] this vector + */ + operator fun not() = negate() + + /** + * Negates this [ChainedVector2]. + * @return [ChainedVector2] this vector + */ + fun negate(): ChainedVector2 { + this.x *= -1.0 + this.y *= -1.0 + return this + } + + /** + * Returns a [ChainedVector2] which is the negative of this [ChainedVector2]. + * @return [ChainedVector2] + */ + val negative: ChainedVector2 + get() = ChainedVector2(-this.x, -this.y) + + /** + * Sets the [ChainedVector2] to the zero [ChainedVector2] + * @return [ChainedVector2] this vector + */ + fun zero(): ChainedVector2 { + this.x = 0.0 + this.y = 0.0 + return this + } + + /** + * Rotates about the origin. + * @param theta the rotation angle in radians + * * + * @return [ChainedVector2] this vector + */ + infix fun rotate(theta: Double): ChainedVector2 { + val cos = Math.cos(theta) + val sin = Math.sin(theta) + val x = this.x + val y = this.y + this.x = x * cos - y * sin + this.y = x * sin + y * cos + return this + } + + /** + * Rotates the [ChainedVector2] about the given coordinates. + * @param theta the rotation angle in radians + * * + * @param x the x coordinate to rotate about + * * + * @param y the y coordinate to rotate about + * * + * @return [ChainedVector2] this vector + */ + fun rotate(theta: Double, x: Double, y: Double): ChainedVector2 { + this.x -= x + this.y -= y + this.rotate(theta) + this.x += x + this.y += y + return this + } + + /** + * Rotates the [ChainedVector2] about the given point. + * @param theta the rotation angle in radians + * * + * @param point the point to rotate about + * * + * @return [ChainedVector2] this vector + */ + fun rotate(theta: Double, point: ChainedVector2): ChainedVector2 { + return this.rotate(theta, point.x, point.y) + } + + /** + * Projects this [ChainedVector2] onto the given [ChainedVector2]. + * @param vector the [ChainedVector2] + * * + * @return [ChainedVector2] the projected [ChainedVector2] + */ + fun project(vector: ChainedVector2): ChainedVector2 { + val dotProd = this.dot(vector) + var denominator = vector.dot(vector) + if (denominator <= Epsilon.E) return ChainedVector2() + denominator = dotProd / denominator + return ChainedVector2(denominator * vector.x, denominator * vector.y) + } + + /** + * Returns the right-handed normal of this vector. + * @return [ChainedVector2] the right hand orthogonal [ChainedVector2] + */ + val rightHandOrthogonalVector: ChainedVector2 + get() = ChainedVector2(-this.y, this.x) + + /** + * Sets this vector to the right-handed normal of this vector. + * @return [ChainedVector2] this vector + * * + * @see .getRightHandOrthogonalVector + */ + fun right(): ChainedVector2 { + val temp = this.x + this.x = -this.y + this.y = temp + return this + } + + /** + * Returns the left-handed normal of this vector. + * @return [ChainedVector2] the left hand orthogonal [ChainedVector2] + */ + val leftHandOrthogonalVector: ChainedVector2 + get() = ChainedVector2(this.y, -this.x) + + /** + * Sets this vector to the left-handed normal of this vector. + * @return [ChainedVector2] this vector + * * + * @see .getLeftHandOrthogonalVector + */ + fun left(): ChainedVector2 { + val temp = this.x + this.x = this.y + this.y = -temp + return this + } + + /** + * Returns a unit [ChainedVector2] of this [ChainedVector2]. + * + * + * This method requires the length of this [ChainedVector2] is not zero. + * @return [ChainedVector2] + */ + val normalized: ChainedVector2 + get() { + var magnitude = this.magnitude + if (magnitude <= Epsilon.E) return ChainedVector2() + magnitude = 1.0 / magnitude + return ChainedVector2(this.x * magnitude, this.y * magnitude) + } + + /** + * Converts this [ChainedVector2] into a unit [ChainedVector2] and returns + * the magnitude before normalization. + * + * + * This method requires the length of this [ChainedVector2] is not zero. + * @return double + */ + fun normalize(): Double { + val magnitude = Math.sqrt(this.x * this.x + this.y * this.y) + if (magnitude <= Epsilon.E) return 0.0 + val m = 1.0 / magnitude + this.x *= m + this.y *= m + //return 1.0 / m; + return magnitude + } + + /** + * Returns the smallest angle between the given [ChainedVector2]s. + * + * + * Returns the angle in radians in the range - to . + * @param vector the [ChainedVector2] + * * + * @return angle in radians [-, ] + */ + fun getAngleBetween(vector: ChainedVector2): Double { + val a = Math.atan2(vector.y, vector.x) - Math.atan2(this.y, this.x) + if (a > Math.PI) return a - 2.0 * Math.PI + if (a < -Math.PI) return a + 2.0 * Math.PI + return a + } + + fun toVector(): Vector2 { + return Vector2(x, y) + } + + companion object { + /** A vector representing the x-axis; this vector should not be changed at runtime; used internally */ + internal val X_AXIS = ChainedVector2(1.0, 0.0) + + /** A vector representing the y-axis; this vector should not be changed at runtime; used internally */ + internal val Y_AXIS = ChainedVector2(0.0, 1.0) + + /** + * Returns a new [ChainedVector2] given the magnitude and direction. + * @param magnitude the magnitude of the [ChainedVector2] + * * + * @param direction the direction of the [ChainedVector2] in radians + * * + * @return [ChainedVector2] + */ + fun create(magnitude: Double, direction: Double): ChainedVector2 { + val x = magnitude * Math.cos(direction) + val y = magnitude * Math.sin(direction) + return ChainedVector2(x, y) + } + + /** + * The triple product of [ChainedVector2]s is defined as: + * + * a x (b x c) + * + * However, this method performs the following triple product: + * + * (a x b) x c + * + * this can be simplified to: + * + * -a * (b c) + b * (a c) + * + * or: + * + * b * (a c) - a * (b c) + * + * @param a the a [ChainedVector2] in the above equation + * * + * @param b the b [ChainedVector2] in the above equation + * * + * @param c the c [ChainedVector2] in the above equation + * * + * @return [ChainedVector2] + */ + fun tripleProduct(a: ChainedVector2, b: ChainedVector2, c: ChainedVector2): ChainedVector2 { + // expanded version of above formula + val r = ChainedVector2() + // perform a.dot(c) + val ac = a.x * c.x + a.y * c.y + // perform b.dot(c) + val bc = b.x * c.x + b.y * c.y + // perform b * a.dot(c) - a * b.dot(c) + r.x = b.x * ac - a.x * bc + r.y = b.y * ac - a.y * bc + return r + } + } +} \ No newline at end of file diff --git a/src/org/dyn4j/geometry/Vector2.kt b/src/org/dyn4j/geometry/Vector2.kt index b9048d5fe..3fca54edd 100644 --- a/src/org/dyn4j/geometry/Vector2.kt +++ b/src/org/dyn4j/geometry/Vector2.kt @@ -38,23 +38,24 @@ import org.dyn4j.Epsilon * to be non-zero in length. * * - * Some methods also return the vector to facilitate chaining. For example: - * - * Vector a = new Vector(); - * a.zero().plus(1, 2).times(2); + * Chaining is not available. * * In this Kotlin code, you can use regular operations like + - * /. * - * |operator|function| - * |--------|--------| - * |a + b|a.plus(b)| - * |a - b|a.minus(b)| - * |a * b|a.times(b)| - * |a / b|a.div(b)| - * |a dot b|a.dot(b)| - * |a cross b|a.cross(b)| - * |!a|negate(a)| - * |a rotate th|a.rotate(th)| + * |operator |function | + * |-----------|------------| + * |a + b |Vector(a).plus(b) | + * |a - b |Vector(a).minus(b) | + * |a * b |Vector(a).times(b) | + * |a / b |Vector(a).div(b) | + * |a += b |Vector(a).plusAssign(b)| + * |a -= b |Vector(a).minusAssign(b)| + * |a *= b |Vector(a).timesAssign(b)| + * |a /= b |Vector(a).divAssign(b)| + * |a dot b |Vector(a).dot(b) | + * |a cross b |Vector(a).cross(b) | + * |!a |Vector(negate(a) | + * |a rotate th|Vector(a).rotate(th)| * * @author William Bittle * * @@ -370,47 +371,12 @@ class Vector2 { * @return [Vector2] this vector */ operator fun plus(vector: Vector2): Vector2 { - this.x += vector.x - this.y += vector.y - return this - } - - /** - * Adds the given [Vector2] to this [Vector2]. - * @param x the x component of the [Vector2] - * * - * @param y the y component of the [Vector2] - * * - * @return [Vector2] this vector - */ - fun plus(x: Double, y: Double): Vector2 { - this.x += x - this.y += y - return this - } - - /** - * Adds this [Vector2] and the given [Vector2] returning - * a new [Vector2] containing the result. - * @param vector the [Vector2] - * * - * @return [Vector2] - */ - fun sum(vector: Vector2): Vector2 { return Vector2(this.x + vector.x, this.y + vector.y) } - /** - * Adds this [Vector2] and the given [Vector2] returning - * a new [Vector2] containing the result. - * @param x the x component of the [Vector2] - * * - * @param y the y component of the [Vector2] - * * - * @return [Vector2] - */ - fun sum(x: Double, y: Double): Vector2 { - return Vector2(this.x + x, this.y + y) + operator fun plusAssign(vector: Vector2) { + this.x += vector.x + this.y += vector.y } /** @@ -420,47 +386,12 @@ class Vector2 { * @return [Vector2] this vector */ operator fun minus(vector: Vector2): Vector2 { - this.x -= vector.x - this.y -= vector.y - return this - } - - /** - * Subtracts the given [Vector2] from this [Vector2]. - * @param x the x component of the [Vector2] - * * - * @param y the y component of the [Vector2] - * * - * @return [Vector2] this vector - */ - fun minus(x: Double, y: Double): Vector2 { - this.x -= x - this.y -= y - return this - } - - /** - * Subtracts the given [Vector2] from this [Vector2] returning - * a new [Vector2] containing the result. - * @param vector the [Vector2] - * * - * @return [Vector2] - */ - fun difference(vector: Vector2): Vector2 { return Vector2(this.x - vector.x, this.y - vector.y) } - /** - * Subtracts the given [Vector2] from this [Vector2] returning - * a new [Vector2] containing the result. - * @param x the x component of the [Vector2] - * * - * @param y the y component of the [Vector2] - * * - * @return [Vector2] - */ - fun difference(x: Double, y: Double): Vector2 { - return Vector2(this.x - x, this.y - y) + operator fun minusAssign(vector: Vector2) { + this.x -= vector.x + this.y -= vector.y } /** @@ -473,18 +404,6 @@ class Vector2 { return Vector2(vector.x - this.x, vector.y - this.y) } - /** - * Creates a [Vector2] from this [Vector2] to the given [Vector2]. - * @param x the x component of the [Vector2] - * * - * @param y the y component of the [Vector2] - * * - * @return [Vector2] - */ - fun to(x: Double, y: Double): Vector2 { - return Vector2(x - this.x, y - this.y) - } - /** * Multiplies this [Vector2] by the given scalar. * @param scalar the scalar @@ -492,9 +411,12 @@ class Vector2 { * @return [Vector2] this vector */ operator fun times(scalar: Double): Vector2 { + return Vector2(this.x * scalar, this.y * scalar) + } + + operator fun timesAssign(scalar: Double) { this.x *= scalar this.y *= scalar - return this } /** @@ -504,9 +426,12 @@ class Vector2 { * @return [Vector2] this vector */ operator fun div(scalar: Double): Vector2 { + return Vector2(this.x / scalar, this.y / scalar) + } + + operator fun divAssign(scalar: Double) { this.x /= scalar this.y /= scalar - return this } /** @@ -626,9 +551,7 @@ class Vector2 { * @return [Vector2] this vector */ fun negate(): Vector2 { - this.x *= -1.0 - this.y *= -1.0 - return this + return Vector2(x * -1.0, y * -1.0) } /** @@ -659,40 +582,9 @@ class Vector2 { val sin = Math.sin(theta) val x = this.x val y = this.y - this.x = x * cos - y * sin - this.y = x * sin + y * cos - return this - } - - /** - * Rotates the [Vector2] about the given coordinates. - * @param theta the rotation angle in radians - * * - * @param x the x coordinate to rotate about - * * - * @param y the y coordinate to rotate about - * * - * @return [Vector2] this vector - */ - fun rotate(theta: Double, x: Double, y: Double): Vector2 { - this.x -= x - this.y -= y - this.rotate(theta) - this.x += x - this.y += y - return this - } - - /** - * Rotates the [Vector2] about the given point. - * @param theta the rotation angle in radians - * * - * @param point the point to rotate about - * * - * @return [Vector2] this vector - */ - fun rotate(theta: Double, point: Vector2): Vector2 { - return this.rotate(theta, point.x, point.y) + val newX = x * cos - y * sin + val newY = x * sin + y * cos + return Vector2(newX, newY) } /**