Skip to content

Readability of hit objects and maps (slightly outdated)

Gwineafowl edited this page Oct 19, 2023 · 1 revision

Readability Rating (RR)

RR is derived through a weighting system (much like osu!’s total PP system) incorporating the results of individually evaluating the density $d_f$ of each frame density (excluding spinners) with $f-1$ being the index of the array of frame densities sorted from highest to lowest and $n$ being the size of the array (and number of hit objects).

$$RR = \sum_{f=1}^n d_f(0.95) ^{f-1} = d_1(1) + d_2(0.95) + d_3(0.9025)\dots$$

Frame Density

Each hit object will be assigned a frame density. Each raw density will be determined by only up to the relevant predecessor hit objects, so frame density exists to account for all of the hit objects on the screen in a fair way. Frame density will be composed by a weighting system that favors raw densities of hit object that are closer in time to the current hit object.

Expression that gets added if the raw density D is greater than 1:
D^(sqrt((map_ms - (current_ms - previous_ms)) / map_ms))

Expression that gets added if the raw density D is less than or equal to 1:
D * (sqrt((map_ms - (current_ms - previous_ms)) / map_ms))

Example:

AR: equivalent to 500 ms
Circle 1: at 950 ms with raw density D1
Circle 2: at 1001 ms with raw density D2
Circle 3: at 1400 ms with raw density D3
Circle 4: at 1500 ms with raw density D4

Circle 1's frame density:

Df = D1^(sqrt(500/500))
= D1

Circle 2's frame density:

Df = D1^(sqrt((500-(1001-950)/500)) + D2
≈ D1^0.9476 + D2

Circle 3's frame density:

Df = D1^(sqrt((500-(1400-950)/500)) + D2^(sqrt((500-(1400-1001)/500)) + D3
≈ D1^0.3162 + D2^0.4494 + D3

Circle 4's frame density:

Df = D2^(sqrt((500-(1500-1001)/500)) + D3^(sqrt((500-(1500-1400)/500)) + D4
≈ D2^0.0447 + D3^0.8944 + D4

Raw Density

The raw density of one hit object will be calculated with the following factors.

density of a circle = distance_factor * delta_distance_factor * angle_factor^(1/distance_factor) * obscurity_factor

density of a slider = distance_factor * delta_distance_factor * angle_factor ^ (1/distance_factor) * obscurity_factor * [length_factor * box_factor ^ (length_factor)] ^ [(slides ^ 2) * 0.1]

distance_factor

Applies whenever the current hit object has begun to fade in before the previous hit object is finished. Uses the Euclidean distance between the current hit object and the previous hit object (or end, if the previous hit object is a slider) as shown.

image

distance_factor = (log(EuD / 10) / 3) + 0.595

EXAMPLE

Circle 1: x=100, y=150
Circle 2: x=400, y=380

EuD = sqrt((400 - 100)^2 + (380 - 150)^2) ≈ 378.02

distance_factor ≈ (log(378.02 / 10) / 3) + 0.595
≈ 1.1210

delta_distance_factor

Applies whenever the current hit object has begun to fade in before the 2nd previous hit object is finished. Uses the difference between the current and previous Euclidean distances as shown. image image

(When difference is positive)
delta_distance_factor = (log((difference + 170) / 10) / 3) + 0.595

(When difference is negative)
delta_distance_factor = (log((abs(difference) + 340) / 10) / 3) + 0.595

EXAMPLE

Circle 1: x=100, y=150
Circle 2: x=400, y=380
Circle 3: x=10, y=10

EuD1 = sqrt((400 - 100)^2 + (380 - 150)^2) ≈ 378.02
EuD2 = sqrt((10 - 400)^2 + (10 - 380)^2) ≈ 537.59
difference ≈ 537.59 - 378.02 = 159.57

delta_distance_factor ≈ (log((159.57 + 170) / 10) / 3) + 0.595
≈ 1.1010

angle_factor

image

Before attempting to find the angle, first find the orientation by finding if the points to see if the resulting "slope" bends in a clockwise or counterclockwise angle. If neither, then the points are one of two forms of collinearity: if they are aligned, but you must reverse cursor direction after the 2nd point, it is collinear-reverse. Or, if they are in a straight, always forward line, it is collinear-straight.

If the orientation clockwise or counterclockwise, find the angle that is between the vectors formed by the current hit object and 2 previous hit objects as applicable, then subtract that angle from pi radians to get the angle in which the player changes direction (this is what gets used in calculating the factor). If orientation == clockwise, switch the sign of the angle. This positive or negative value gets stored for the next iteration image image image image

(When the current angle's sign is opposite to the previous)
resulting = abs(current_angle) + abs(previous_angle)

(When the current angle's sign is the same as the previous, or the 3rd previous hit object is out of the current's timeframe)
resulting = abs(current_angle)

angle_factor = (sin(resulting / 485.5))^0.34 + 1

obscurity_factor

Has to do with when a hit object is harder to read because it is obscured by another hit object being placed or rendered directly on top. obscurity_factor accounts for the following scenarios:

  • A circle or slider start is obscured by the previous circle or slider start placed directly on top; it does not render in a more readable way because the mapper turned stack leniency all the way down.
  • Regardless of stack leniency, a circle or slider start is obscured by any circle or slider start that isn't part of the same stack. If there are multiple instances of this for the current hit object, consider only the obscuring hit object with the closest timestamp to the current.
  • A slider is obscured by a completely identical slider being placed on top. If there are multiple instances of this for the current hit slider, consider only the obscuring slider with the closest timestamp to the current.

If there are any hit objects obscuring the view of the current hit object (or slider start), take the milliseconds ms of time for each object that obscures the current hit object. Select only the highest ms and ditch the others. Divide ms by the approach rate converted to milliseconds into one of the following formulas based on whether the current hit object is:

EXAMPLE
(a circle obscured by another circle or slider start)

(a slider start obscured by another slider start)

(a slider start obscured by a hit circle)

(entire slider obscured by an identical slider)



to be written when i feel like it

length_factor

Applies for sliders of all lengths, and is what we get when plugging in the slider length value that appears in the map file into this formula.

box_factor

Serves as a quick approximation of the "size" of the slider, no matter what the length value is. "Box" refers to the smallest possible rectangular box that can be drawn to "contain" the slider. This is done by piecing together the smallest x-coordinate with the smallest y-coordinate across all the coordinate pairs in the slider's curvepoints, start, and end, and doing the same thing with the largest x-coordinate and largest y-coordinate. Pythagorean theorem is used to get the length of the box's diagonal. image