///|
/// FT_MulFix: multiply two 16.16 fixed-point values, return 16.16 result.
/// Equivalent to (a * b) / 0x10000 with rounding.
/// Port of ftcalc.c FT_MulFix (64-bit path).
pub fn mul_fix(a : Fixed, b : Fixed) -> Fixed {
let ab = a.val * b.val
// Arithmetic right shift for rounding bias: add 0x8000, but if negative
// add 0x7FFF (the >> 63 gives -1 for negative, 0 for non-negative).
let result = (ab + 0x8000L + (ab >> 63)) >> 16
{ val: result }
}
///|
/// FT_MulDiv: compute (a * b + c/2) / c with rounding to nearest.
/// Port of ftcalc.c FT_MulDiv (64-bit path).
pub fn mul_div(a : Int64, b : Int64, c : Int64) -> Int64 {
let mut s = 1
let mut ua = a
let mut ub = b
let mut uc = c
if a < 0L {
ua = -a
s = -s
}
if b < 0L {
ub = -b
s = -s
}
if c < 0L {
uc = -c
s = -s
}
let d = if uc > 0L { (ua * ub + (uc >> 1)) / uc } else { 0x7FFF_FFFFL }
if s < 0 {
-d
} else {
d
}
}
///|
/// FT_DivFix: compute (a << 16) / b with rounding. Returns 16.16 fixed.
/// Port of ftcalc.c FT_DivFix (64-bit path).
pub fn div_fix(a : Int64, b : Int64) -> Fixed {
let mut s = 1
let mut ua = a
let mut ub = b
if a < 0L {
ua = -a
s = -s
}
if b < 0L {
ub = -b
s = -s
}
let q = if ub > 0L { ((ua << 16) + (ub >> 1)) / ub } else { 0x7FFF_FFFFL }
let result = if s < 0 { -q } else { q }
{ val: result }
}
///|
/// FT_MSB: return index of the most significant bit (0-based).
/// Returns -1 if z == 0.
pub fn msb(z : UInt) -> Int {
if z == 0U {
return -1
}
let mut v = z
let mut shift = 0
if (v & 0xFFFF_0000U) != 0U {
v = v >> 16
shift = shift + 16
}
if (v & 0x0000_FF00U) != 0U {
v = v >> 8
shift = shift + 8
}
if (v & 0x0000_00F0U) != 0U {
v = v >> 4
shift = shift + 4
}
if (v & 0x0000_000CU) != 0U {
v = v >> 2
shift = shift + 2
}
if (v & 0x0000_0002U) != 0U {
shift = shift + 1
}
shift
}
///|
/// FT_SqrtFixed: integer square root of a 16.16 fixed-point value.
/// Uses Babylonian method. Port of ftcalc.c FT_SqrtFixed.
pub fn sqrt_fixed(v : UInt64) -> UInt {
if v == 0UL {
return 0U
}
let r = (v << 16) - 1UL
// Initial guess: 1 << ((17 + MSB(v)) >> 1)
let m = msb(v.to_uint())
let mut q : UInt64 = 1UL << ((17 + m) >> 1)
for ;; {
let t = q
q = (t + r / t + 1UL) >> 1
if q == t {
break
}
}
q.to_uint()
}
///|
/// Determine orientation of corner formed by vectors (in_x,in_y) → (out_x,out_y).
/// Returns +1 (counter-clockwise), -1 (clockwise), or 0 (colinear).
pub fn corner_orientation(
in_x : Int64,
in_y : Int64,
out_x : Int64,
out_y : Int64,
) -> Int {
let delta = in_x * out_y - in_y * out_x
if delta > 0L {
1
} else if delta < 0L {
-1
} else {
0
}
}
///|
/// Test whether a corner is flat (nearly colinear).
/// Port of ftcalc.c ft_corner_is_flat.
pub fn corner_is_flat(
in_x : Int64,
in_y : Int64,
out_x : Int64,
out_y : Int64,
) -> Bool {
fn hypot(x : Int64, y : Int64) -> Int64 {
let ax = if x < 0L { -x } else { x }
let ay = if y < 0L { -y } else { y }
if ax > ay {
ax + (ay >> 1)
} else {
ay + (ax >> 1)
}
}
let d_in = hypot(in_x, in_y)
let d_out = hypot(out_x, out_y)
let d_hypot = hypot(in_x + out_x, in_y + out_y)
d_in + d_out - d_hypot < d_hypot >> 4
}