#define ceil(x, N) (-round((-x)/(N))*(N))


ceil function to the nearest multiple you want by lummox jr
In response to Kaiochao
Most interpolation techniques are pretty much just mathematics ;)
Speaking of interpolation, here's a bunch of interpolation algorithms I use in my projects:

proc
// Linear interpolation
lerp(v0, v1, d)
return v0+(v1-v0)*d

// Cosine interpolation (for smooth in/out easing)
cerp(v0, v1, d)
var/f = (1-cos(toDeg(d*pi))) / 2 // convert the input to degrees because byond only works with degrees
return (v0)*(1-f)+(v1*d)

// Cubic interpolation (for continous and much smoother easing)
// This form of interpolation requires 4 points instead of 2
cuerp(v0, v1, v2, v3, d)
var
p = (v3 - v2) - (v0 - v1)
q = (v0 - v1) - p
r = v2 - v0
s = v1
d2 = d * d
d3 = d2 * d

return (p * (d3)) + (q * d2) + (r * d) + s

// Interpolate two degrees angles
slerp(a, b, t, method = /proc/lerp)
var/difference = abs(b - a)

if(difference > 180)
if(b > a)
a += 360
else
b += 360

// Perform the interpolation
var/value = call(method)(a, b, t)

if(value >= 0 && value <= 360)
return value
else
return (value % 360)

// Helpful angle functions.
toRadian(x) return x*(pi/180)
toDeg(x) return x*(180/pi)
In response to Doohl
Hurray for math! :D
I opted to add an appearance datum interface to the stdlib, which will make it so that you can cast appearances to get specific values from them using the path operator instead of the look-up operator:

var/appearance/a
for(var/v in overlays)
a = v
world << a.layer


You should note that appearances are immutable, so you should never attempt to set any values on an appearance. It will fail.

You should also note that you can't do:

for(var/appearance/a in overlays)

It won't work, as there are no appearance datums in the overlays/underlays list.

You should also note that atom.appearance is not cast as this type, so you need to cast it if you want to look at parts of it.

This is for investigation purposes only. Don't use this unless you understand appearances.
In response to Ter13
Ter13 wrote:
You should note that appearances are immutable

That's always a cherry token of information. I'm glad you said something before inevitably I started messing with them to no avail. Lol.
@Kozuma3: I'm not sure why you deleted your question regarding the use of preprocessor defines, but...

The reason I opted to use FPS, TICK_LAG, TILE_WIDTH, and TILE_HEIGHT defines rather than use the world's properties actually has a lot of justification for it.

1) Speed. Constants are faster than variable accesses.

2) Instructions. Constants incur less instructions than variable accesses because constants can take advantage of compiler optimization. For instance:

world.tick_lag * 3 compiles down to:
access <world> tick_lag
(pop)
mul 3
(push)


Whereas TICK_LAG * 3 compiles down to:

push TICK_LAG*3

macro replacement phase:

push [0.25]*3


constant optimization phase:

push 0.75


3) Type safety. Not all world variables make sense to be used in mathematical operations. For instance, icon_size can be a number or a string. This is problematic for figuring out how big your icons are on each size programmatically.

4) Standardization. Preprocessor definitions allow you to produce modular code that we can be sure works properly. By not tying our code to any one variable, we can get information from the developer implementing our library and warn them about conflicts. For instance, if our code was designed to run in pixel movement mode only, we can actually set up a preprocessor definition that will serve as a contract with the user stating: "I'm using pixel movement and I understand that this library uses pixel movement.". We can then use #ifdef and #warn to notify the user that maybe they shouldn't be using our library unless they are using a pixel-movement enabled world.

5) Handling conflicts. Preprocessor macros allow us to detect and handle conflicts with other libraries ahead of time. Simply detecting top-level definitions at the library level can aid in preventing problems ahead of time without having to constantly ask the same questions: "Are you using Forum_Account's pixel movement?" etc.
Ah, gotcha. :)
* datum.Cleanup() (replaces Del() to speed up garbage collection)
* datum.Clone() (override function for creating clones of objects)

This is too program-specific to be part of an stdlib; even as procs meant to be overriden.
This is too program-specific to be part of an stdlib; even as procs meant to be overriden.

Anyone else share this view? I'm of the opinion that C-style destructors should be a feature of the language owing to the serious problems manual deletion creates. Tom agreed del should probably never be used.
The tricky bit with those two is what the defaults should do, in order to fulfil the contracts their names imply.

Having a Cleanup() is fine, provided that the Cleanup() contract is as such that by the end of the call, the object is down to 0 incoming references, so I can re-use it in whatever pooling/re-use mechanism I can dream up. Given the high level you've placed the proc, and the proliferation of code that won't be meeting that contract, the proc will essentially be too unreliable to be of general use.

Clone() kind of requires that you specify the default behaviour as being a shallow clone or deep clone. I think once done (and shallow clone would be Java's default, as it's the more understandable and easier to implement sanely) you could at least use that. How much you would ... I'm not so sure.
That's definitely a good argument. Creating a contractual guideline and trying to force people to use that guideline and ideology is probably outside of the scope of stdlib. Clone and Cleanup will be removed as reliable implementations require too much of the user to be useful for everyone.

Also, I've identified a very minor but concerning arithmetic issue with some of the rounding procs. I'll be uploading a 1.0 release soonish.
In response to Ter13
Ter13 wrote:
This is too program-specific to be part of an stdlib; even as procs meant to be overriden.

Anyone else share this view? I'm of the opinion that C-style destructors should be a feature of the language owing to the serious problems manual deletion creates. Tom agreed del should probably never be used.

This is a little off-topic but how does del work internally? Loop over all references and remove them? I'm confused about how chores are divided between Del() and del respectively.
This is a little off-topic but how does del work internally? Loop over all references and remove them? I'm confused about how chores are divided between Del() and del respectively.

Del() is called when an object is deleted. del and Del() aren't even really related. del causes an object to be forcibly deleted, which causes the VM to set its location to null first, then search for any remaining references to the object.

The best case scenario is that there are no references left after moving it to null.

The worst case scenario is that one or more references exist in a list somewhere.

Searching for references to the object is expensive and time consuming.

Tom agreed that del should never be used:



Del() is simply a function that is called just before an object is deleted, which can happen either when the object is manually deleted or when its refcount reaches zero
What's a refcount?
A count of how many variables are pointing to a given /datum.

var
mob/theMob = new()
myMob = theMob
yourMob = theMob


Theoretically, the refcount of theMob would be 2. Set both myMob and yourMob to null and the garbage collector will erase theMob from memory.
DM's garbage collection works based on something called a refcount.

Any time an object is referred to by placing it in a variable or a list, the refcount is increased by 1. Any time an object is dereferenced by either removing the list index or clearing the variable that referred to it, the refcount is decreased by 1.

When the refcount reaches zero, the object's memory is destroyed and the reference id for that object is marked as free. Free reference ids are stored in a list which tells BYOND what ids can be used for any new objects of that type. All objects must have a unique reference id.

Edit: @Doohl: The refcount of the object stored in theMob would be 3 in that case, because theMob isn't the mob, but rather a container that stores a reference to the actual mob object. So theMob, myMob, and yourMob all refer to the same object.
._.
In response to Ter13
Whoops! Thanks, wasn't thinking about that.
Mmmm.... I guess this means me calling del dozens of times per second for all my particle effects is a bad thing?
Page: 1 2 3 4