Absolute Positions

by Kaiochao
Helper functions for pixel movement worlds. [More]
To download this library for your Linux/Mac installation, enter this on your command line:

DreamDownload byond://Kaiochao.absolutepositions##version=2

Emulator users, in the BYOND pager go to File | Open Location and enter this URL:

byond://Kaiochao.absolutepositions##version=2

581 downloads
Version 2
Date added: Jan 10 2014
Last updated: Dec 31 2018
10 fans
This library includes:
  • atom.Px(P), atom.Py(P) - The absolute pixel position of the bottom-left of this atom. Include a P percentage (0 to 1) to add a percentage of the atom's size.
  • atom.Cx(), atom.Cy() - The absolute pixel position of the center of this atom. Equivalent to atom.Px(0.5), atom.Py(0.5).
  • movable.SetLoc(Loc, StepX, StepY) - A function to directly set the loc, step_x, and step_y variables. Good for overriding; call ..() to actually do the thing.
  • movable.SetPosition(Px, Py, Z) - Set the absolute pixel position of the bottom-left corner of your movable atom's bounding box.
  • movable.SetCenter(Cx, Cy, Z) - Set the absolute pixel position of the center of the movable atom.
  • movable.Translate(Dx, Dy, Dir) - Move a movable atom by a certain number of pixels in the x and y direction. By including this library, you're able to move at less-than-pixel distances, e.g. Translate(0.5, 0.5) moves half a pixel north and east. You can provide a Dir for the mover to end up with.
  • movable.Project(Distance, Angle, Dir) - Move a movable atom by a certain distance and angle. 0 degrees is north and 90 degrees is east. This uses Translate().


Example
proc/move_towards(atom/movable/a, atom/b, speed)
// speed defaults to the mover's step size
if(isnull(speed)) speed = a.step_size

// if speed turns out to be 0, then there's no movement
if(!speed) return

// a simple vector
// from: the center of 'a'
// to: the center of 'b'
var delta_x = b.Cx() - a.Cx()
var delta_y = b.Cy() - a.Cy()

// a and b are in the same position!
if(!(delta_x || delta_y)) return

// if the delta vector is larger than speed,
// we need to scale it down to speed.
// we do this by dividing by the magnitude of the delta vector,
// which results in a unit vector,
// and then scaling that up by the speed.
var distance = sqrt(delta_x*delta_x + delta_y*delta_y)
if(distance > speed)
var s = speed / distance
delta_x *= s
delta_y *= s
a.Translate(delta_x, delta_y)

Comments

Kaiochao: (Sep 29 2015, 4:20 pm)
1. You shouldn't really use angles to move one object towards another. All you need is rescale a vector in the desired direction. That is, the vector from one object to another is a vector that points from the first object to the target, and you just need to scale that to your step size; no angles necessary:
// vector from src to target
var dx = target.Cx() - Cx() // or Cx(target) - Cx(src) if you use the #define version
var dy = target.Cy() - Cy()

// no distance => no movement
if(!(dx || dy)) return

// vector magnitude
var distance = sqrt(dx * dx + dy * dy)

// scales the (dx, dy) vector to one with length == step_size
// when you divide a vector by its length (called "normalizing a vector")
// the result is a vector with length == 1. We multiply that by the desired length.
var scale = step_size / distance

// your resultant movement vector (in pixels because that's what Cx/y() gives)
var velocity_x = dx * scale
var velocity_y = dy * scale

// or more compactly
var dx = target.Cx() - Cx()
var dy = target.Cy() - Cy()
if(!(dx || dy)) return
var scale = step_size / sqrt(dx * dx + dy * dy)
var velocity_x = dx * scale
var velocity_y = dy * scale


2. Your angle isn't "clockwise from up", which is the more common navigational standard; it's probably "counter-clockwise from right", which is the more common math standard. Here's my atan2() that works in my convention:
proc/atan2(x, y)
return (x || y) && (x >= 0 \
? arccos(y / sqrt(x * x + y * y)) \
: -arccos(y / sqrt(x * x + y * y)))
Reformist: (Sep 29 2015, 3:53 pm)
When using it to move one object towards another, re-calculating the angle to see if the object being moved to has moved as well, it performs strangely. In certain quadrants it will simply move away from the object instead of towards. In quadrants where it moves towards the object it ends up performing a parabola, moving really close to the object then bending away. Switching sin() and cos() corrects this issue.
Kaiochao: (Sep 29 2015, 12:55 am)
They are correct as-is. My BYOND projects (and thus my Project()) have always used the convention that angles increase clockwise from up. I always draw my rotatable artwork pointing up, and positive rotations effectively turn to the right when using matrix.Turn() and icon.Turn().
Reformist: (Sep 29 2015, 12:00 am)
The sin and cos in the Translate() return in Project() need to be flipped.

currently: return Translate(Distance * sin(Angle), Distance * cos(Angle), Dir)

should be: return Translate(Distance * cos(Angle), Distance * sin(Angle), Dir)
FKI: (Aug 20 2015, 7:48 pm)
Yeah, it was something I did somewhere, no idea what though. Github is a lifesaver however.

Again, appreciate the help (and your work as well).