diff --git a/CMakeLists.txt b/CMakeLists.txt index 769af22e..a0c75107 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,3 +52,7 @@ target_link_libraries(typed-geometry PUBLIC if (TG_EXPORT_LITERALS) target_compile_definitions(typed-geometry PUBLIC TG_EXPORT_LITERALS) endif() + +if(TG_IMPLEMENTATION_REPORT) + target_compile_definitions(typed-geometry PUBLIC TG_IMPLEMENTATION_REPORT) +endif() diff --git a/src/typed-geometry/detail/color-literals.hh b/src/typed-geometry/detail/color-literals.hh new file mode 100644 index 00000000..138f2b6b --- /dev/null +++ b/src/typed-geometry/detail/color-literals.hh @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace tg +{ +namespace literals +{ +constexpr srgb8 operator""_srgb8(char const* str, size_t s) { return srgb8::from_hex_string({str, s}); } +constexpr srgba8 operator""_srgba8(char const* str, size_t s) { return srgba8::from_hex_string({str, s}); } +constexpr color3 operator""_color3(char const* str, size_t s) { return color3::from_hex_string({str, s}); } +constexpr color4 operator""_color4(char const* str, size_t s) { return color4::from_hex_string({str, s}); } +} // namespace literals +} // namespace tg + +#ifdef TG_EXPORT_LITERALS +using namespace tg::literals; +#endif diff --git a/src/typed-geometry/detail/color_traits.hh b/src/typed-geometry/detail/color_traits.hh new file mode 100644 index 00000000..e30be0dc --- /dev/null +++ b/src/typed-geometry/detail/color_traits.hh @@ -0,0 +1,111 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace tg +{ +enum class alpha_type +{ + /// no alpha in the type, e.g. (R, G, B) + none, + + /// extra alpha component, e.g. (R, G, B, A) + straight, + + /// premultiplied alpha, e.g. (aR, aG, aB, a) + premultiplied +}; + +template +struct color_traits +{ + static constexpr bool is_color = false; +}; + +/// checks if a type is a color +/// e.g. is_color == true +/// but is_color == false +template +static constexpr bool is_color = color_traits::is_color; + +template +static constexpr bool has_alpha = color_traits::alpha != alpha_type::none; +template +static constexpr bool has_straight_alpha = color_traits::alpha == alpha_type::straight; +template +static constexpr bool has_premultiplied_alpha = color_traits::alpha == alpha_type::premultiplied; + +template +using linear_color_t_of = typename color_traits::linear_color_t; + +namespace detail +{ +template +struct color_scalar +{ + static_assert(always_false, "this scalar type is not cleared for use with colors"); +}; + +template <> +struct color_scalar +{ + static constexpr f32 to_float(f32 v) { return v; } + static constexpr f32 from_float(f32 v) { return v; } + + static constexpr f32 one() { return 1.f; } +}; +template <> +struct color_scalar +{ + static constexpr f32 to_float(u8 v) { return v / 255.f; } + static constexpr u8 from_float(f32 v) + { + v *= 256; + return u8(v < 0 ? 0 : v > 255 ? 255 : int(v)); + } + + static constexpr u8 one() { return 255; } +}; +} + +/// converts a float value to the appropriate value for storing it as a certain color scalar type +/// e.g. if ScalarT is u8, we store 255 * v +template +constexpr ScalarT color_scalar_from_float(float v) +{ + return detail::color_scalar::from_float(v); +} +/// converts a color storage type back to float [0..1] +template +constexpr float color_scalar_to_float(ScalarT v) +{ + return detail::color_scalar::to_float(v); +} + +namespace detail +{ +/// helper for implementing color traits +/// NOTE: D is WITH alpha! +template +struct base_color_traits +{ + using scalar_t = ScalarT; + + /// the linear RGB type appropriate for this color + /// is usually color3 or color4 (if alpha) + /// (or double versions for larger types) + using linear_color_t = std::conditional_t>, + color<4, fractional_result>>; + + static constexpr alpha_type alpha = Alpha; + static constexpr bool is_color = true; + static constexpr int comps = D; +}; +} +} diff --git a/src/typed-geometry/feature/colors.hh b/src/typed-geometry/feature/colors.hh index ff5a8c21..6494874b 100644 --- a/src/typed-geometry/feature/colors.hh +++ b/src/typed-geometry/feature/colors.hh @@ -1,5 +1,529 @@ #pragma once +#include +#include + #include -// TODO +#include + +#include +#include +#include + +// Feature/Color: +// - color spaces (rgb, srgb, hsv, lab, yiq, yuv, ...) +// - static handling of srgb (via types) +// - support for alpha and premultiplied alpha +// - color space conversions +// - different representation (8bit, float) +// - color manipulation (darken, lighten, invert, rotate, ...) +// - parsing ("#rgb" etc) +// - literals ("#FF00FF"_srgb) +// - colormaps and distributions +// +// inspired by https://github.com/bgrins/TinyColor + +/// common color types +/// +/// tg::color3 - linear RGB (in BT.709) +/// tg::color4 - linear RGB (in BT.709) with straight alpha +/// tg::srgb8 - sRGB stored in gamma-corrected 8bit +/// tg::srgba8 - sRGB stored in gamma-corrected 8bit with straight alpha +/// +/// Notes: +/// - there is no rgb8 type as that is usually NOT what is desired +/// - only color are comp-like types (subscript, explicit-init-by-other-comp-types, ...) +/// (because all other colors types are a lot more "storage only") +/// (TODO: this might get relaxed in the future) +/// - most types are in-memory first, aka usually used with float +/// compact storage of _colors_ is usually done in srgb8 +/// TODO: provide hdr types with 16 bit per channel +/// + +namespace tg +{ +// +// =========================== conversions =========================== +// + +/// converts a single component from linear to sRGB gamma-corrected +template +[[nodiscard]] constexpr T convert_comp_linear_to_srgb(T v) +{ + static_assert(!std::is_integral_v, "only works with floating types currently"); + if (v <= T(0.0031308)) + return T(12.92) * v; + else + return T(1.055) * pow(v, T(1.0 / 2.4)) - T(0.055); +} +/// converts a single component from sRGB gamma-corrected to linear +template +[[nodiscard]] constexpr T convert_comp_srgb_to_linear(T v) +{ + static_assert(!std::is_integral_v, "only works with floating types currently"); + if (v <= T(0.04045)) + return v * T(1 / 12.92); + else + return pow(v + T(0.055), T(2.4)) * T(1 / 1.055); +} + +namespace detail +{ +template +struct color_converter; +} + +/// Converts the given color into linear space +/// (If it has alpha, it becomes straight alpha) +template +[[nodiscard]] constexpr linear_color_t_of to_linear_color(Color const& c) +{ + return detail::color_converter::to_linear(c); +} + +/// Converts the given linear color into a target color +template +[[nodiscard]] constexpr Color from_linear_color(linear_color_t_of const& c) +{ + return detail::color_converter::from_linear(c); +} + +/// converts between two color types +/// NOTE: currently requires "shape compatibility", i.e. both alpha or none alpha +template +[[nodiscard]] constexpr ColorTo convert_color_to(ColorFrom const& c) +{ + if constexpr (std::is_same_v) + return c; + else + return tg::from_linear_color(tg::to_linear_color(c)); +} + +namespace detail +{ + +template +constexpr T hue2rgb(T p, T q, T t) +{ + if (t < 0) + t += 1; + if (t > 1) + t -= 1; + if (t < T(1.0 / 6)) + return p + (q - p) * T(6) * t; + if (t < T(1.0 / 2)) + return q; + if (t < T(2.0 / 3)) + return p + (q - p) * (T(2. / 3) - t) * T(6); + + return p; +} + +template +constexpr LinearColor srgb_to_linear(Color const& c) +{ + LinearColor cc; + cc.r = tg::convert_comp_srgb_to_linear(tg::color_scalar_to_float(c.r)); + cc.g = tg::convert_comp_srgb_to_linear(tg::color_scalar_to_float(c.g)); + cc.b = tg::convert_comp_srgb_to_linear(tg::color_scalar_to_float(c.b)); + if constexpr (tg::has_alpha) + cc.a = tg::color_scalar_to_float(c.a); + return cc; +} + +template +constexpr Color srgb_from_linear(LinearColor const& c) +{ + Color cc; + using T = decltype(cc.r); + cc.r = tg::color_scalar_from_float(tg::convert_comp_linear_to_srgb(c.r)); + cc.g = tg::color_scalar_from_float(tg::convert_comp_linear_to_srgb(c.g)); + cc.b = tg::color_scalar_from_float(tg::convert_comp_linear_to_srgb(c.b)); + if constexpr (tg::has_alpha) + cc.a = tg::color_scalar_from_float(c.a); + return cc; +} + +template +constexpr LinearColor hsl_to_linear(Color const& c) +{ + LinearColor cc; + using T = decltype(c.l); + if (c.l == T(0)) + { + cc.r = T(0); + cc.g = T(0); + cc.b = T(0); + } + else + { + auto const q = c.l < T(0.5) ? c.l * (T(1) + c.s) : c.l + c.s - c.l * c.s; + auto const p = T(2) * c.l - q; + + cc.r = detail::hue2rgb(p, q, c.h + T(1.0 / 3)); + cc.g = detail::hue2rgb(p, q, c.h); + cc.b = detail::hue2rgb(p, q, c.h - T(1.0 / 3)); + } + if constexpr (tg::has_alpha) + cc.a = tg::color_scalar_to_float(c.a); + return cc; +} + +template +constexpr Color hsl_from_linear(LinearColor const& c) +{ + Color cc; + using T = decltype(cc.l); + auto const M = tg::max(c.r, c.g, c.b); + auto const m = tg::min(c.r, c.g, c.b); + cc.l = (M + m) * T(0.5); + if (M == m) // achromatic? + { + cc.h = T(0); + cc.s = T(0); + } + else + { + auto const C = M - m; + cc.s = cc.l > T(0.5) ? C / (T(2) - M - m) : C / (M + m); + + if (M == c.r) + cc.h = (c.g - c.b) / C * T(60 / 360.0) + (c.g < c.b ? T(1) : T(0)); + + else if (M == c.g) + cc.h = (c.b - c.r) / C * T(60 / 360.0) + T(120 / 360.0); + + else + cc.h = (c.r - c.g) / C * T(60 / 360.0) + T(240 / 360.0); + } + if constexpr (tg::has_alpha) + cc.a = tg::color_scalar_from_float(c.a); + return cc; +} + +template +struct color_converter> +{ + CC_FORCE_INLINE static constexpr color to_linear(color const& c) { return c; } + CC_FORCE_INLINE static constexpr color from_linear(color const& c) { return c; } +}; + +template +struct color_converter> +{ + using color_t = srgb; + using linear_color_t = tg::linear_color_t_of; + + static constexpr linear_color_t to_linear(color_t const& c) { return detail::srgb_to_linear(c); } + static constexpr color_t from_linear(linear_color_t const& c) { return detail::srgb_from_linear(c); } +}; + +template +struct color_converter> +{ + using color_t = srgba; + using linear_color_t = tg::linear_color_t_of; + + static constexpr linear_color_t to_linear(color_t const& c) { return detail::srgb_to_linear(c); } + static constexpr color_t from_linear(linear_color_t const& c) { return detail::srgb_from_linear(c); } +}; + +template +struct color_converter> +{ + using color_t = hsl_t; + using linear_color_t = tg::linear_color_t_of; + + static constexpr linear_color_t to_linear(color_t const& c) { return detail::hsl_to_linear(c); } + static constexpr color_t from_linear(linear_color_t const& c) { return detail::hsl_from_linear(c); } +}; + +template +struct color_converter> +{ + using color_t = hsla_t; + using linear_color_t = tg::linear_color_t_of; + + static constexpr linear_color_t to_linear(color_t const& c) { return detail::hsl_to_linear(c); } + static constexpr color_t from_linear(linear_color_t const& c) { return detail::hsl_from_linear(c); } +}; +} + +template +[[nodiscard]] constexpr auto to_linear_color(hsla_t const& c) -> linear_color_t_of> +{ + linear_color_t_of> cc; + if (c.l == T(0)) + { + cc.r = T(0); + cc.g = T(0); + cc.b = T(0); + } + else + { + auto const q = c.l < T(0.5) ? c.l * (T(1) + c.s) : c.l + c.s - c.l * c.s; + auto const p = T(2) * c.l - q; + + cc.r = detail::hue2rgb(p, q, c.h + T(1.0 / 3)); + cc.g = detail::hue2rgb(p, q, c.h); + cc.b = detail::hue2rgb(p, q, c.h - T(1.0 / 3)); + } + cc.a = c.a; + return cc; +} + +// +// =========================== properties =========================== +// TODO: many of these properties can be performance optimized by special overloads +// + +/// returns the "attribute of a visual sensation according to which an area appears to emit more or less light" +/// NOTE: this is the 'V' of HSV +/// NOTE: do not confuse with luminance or luma +/// result is in [0,1] and floating-point +template +[[nodiscard]] constexpr auto brightness_of(Color const& color) +{ + auto const c = tg::to_linear_color(color); + return max(c.r, c.g, c.b); +} + +/// returns the intensity of a color (the I in HSI) +/// result is in [0,1] and floating-point +template +[[nodiscard]] constexpr auto intensity_of(Color const& color) +{ + auto const c = tg::to_linear_color(color); + return (c.r + c.g + c.b) / 3; +} + +/// returns the "brightness relative to the brightness of a similarly illuminated white" (the L in HSL) +/// result is in [0,1] and floating-point +template +[[nodiscard]] constexpr auto lightness_of(Color const& color) +{ + auto const c = tg::to_linear_color(color); + auto const M = max(c.r, c.g, c.b); + auto const m = min(c.r, c.g, c.b); + return (M + m) / 2; +} + +/// returns the "colorfulness relative to the brightness of a similarly illuminated white" +/// result is in [0,1] and floating-point +template +[[nodiscard]] constexpr auto chroma_of(Color const& color) +{ + auto const c = tg::to_linear_color(color); + auto const M = max(c.r, c.g, c.b); + auto const m = min(c.r, c.g, c.b); + return M - m; +} + +/// returns the "colorfulness of a stimulus relative to its own brightness" (the S in HSV/HSL) +/// result is in [0,1] and floating-point +template +[[nodiscard]] constexpr auto saturation_of(Color const& color) +{ + auto const c = tg::to_linear_color(color); + using T = decltype(c.r); + auto const M = max(c.r, c.g, c.b); + auto const m = min(c.r, c.g, c.b); + auto const C = M - m; + if (M <= T(0)) + return T(0); + else + return C / M; +} + +/// returns the "attribute of a visual sensation according to which an area appears to be similar to one of the perceived colors: red, yellow, green, +/// and blue, or to a combination of two of them" result is in [0, 1] and floating-point. hue of black is 0 +/// NOTE: the result is NOT [0, 6] or [0, 360] +template +[[nodiscard]] constexpr auto hue_of(Color const& color) +{ + auto const c = tg::to_linear_color(color); + using T = decltype(c.r); + auto const M = max(c.r, c.g, c.b); + auto const m = min(c.r, c.g, c.b); + auto const C = M - m; + + if (C <= T(0)) + return T(0); + + else if (M == c.r) + return (c.g - c.b) / C * T(60 / 360.0) + (c.g < c.b ? T(1) : T(0)); + + else if (M == c.g) + return (c.b - c.r) / C * T(60 / 360.0) + T(120 / 360.0); + + else + return (c.r - c.g) / C * T(60 / 360.0) + T(240 / 360.0); +} + +/// returns the relative luminance (Rec. 709 "brightness") +/// result is in [0,1] and floating-point +template +[[nodiscard]] constexpr auto luminance_of(Color const& color) +{ + auto const c = tg::to_linear_color(color); + using T = decltype(c.r); + return T(0.2126) * c.r + T(0.7152) * c.g + T(0.0722) * c.b; +} + +/// returns the luma (Rec. 709 "brightness" but gamma-corrected) +/// result is in [0,1] and floating-point +/// TODO: is this really sRGB or some other correction? +template +[[nodiscard]] constexpr auto luma_of(Color const& color) +{ + auto const c = tg::to_linear_color(color); + using T = decltype(c.r); + auto const r = tg::convert_comp_linear_to_srgb(c.r); + auto const g = tg::convert_comp_linear_to_srgb(c.g); + auto const b = tg::convert_comp_linear_to_srgb(c.b); + return T(0.2126) * r + T(0.7152) * g + T(0.0722) * b; +} + +/// returns true iff the colors perceived brightness is light +template +[[nodiscard]] constexpr bool is_light(Color const& color) +{ + return brightness_of(color) >= 0.5f; +} + +/// returns true iff the colors perceived brightness is dark +template +[[nodiscard]] constexpr bool is_dark(Color const& color) +{ + return brightness_of(color) < 0.5f; +} + +/// lightens a color by a relative value (changes L in HSL) +/// 0.0 means unchanged +/// 1.0 means white +template +[[nodiscard]] constexpr Color lightened(Color const& color, float amount = 0.1f) +{ + auto cc = tg::convert_color_to(color); + cc.l = tg::saturate(cc.l + amount); + return tg::convert_color_to(cc); +} + +/// darkens a color by a relative value (changes L in HSL) +/// 0.0 means unchanged +/// 1.0 means black +template +[[nodiscard]] constexpr Color darkened(Color const& color, float amount = 0.1f) +{ + auto cc = tg::convert_color_to(color); + cc.l = tg::saturate(cc.l - amount); + return tg::convert_color_to(cc); +} + +/// brightens a color by a relative value (changes V in HSV by increasing all components at the same time) +/// 0.0 means unchanged +/// 1.0 means white +template +[[nodiscard]] constexpr Color brightened(Color const& color, float amount = 0.1f) +{ + auto cc = tg::to_linear_color(color); + cc.r = tg::saturate(cc.r + amount); + cc.g = tg::saturate(cc.g + amount); + cc.b = tg::saturate(cc.b + amount); + return tg::from_linear_color(color); +} + +/// saturates a color by a relative value (changes S in HSL) +/// 0.0 means unchanged +/// 1.0 means as-vibrant-as-possible +/// CAUTION: do not confuse with tg::saturate +template +[[nodiscard]] constexpr Color saturated(Color const& color, float amount = 0.1f) +{ + static_assert(tg::is_color, "did you mean tg::saturate?"); + auto cc = tg::convert_color_to(color); + cc.s = tg::saturate(cc.s + amount); + return tg::convert_color_to(cc); +} + +/// desaturates a color by a relative value (changes S in HSL) +/// 0.0 means unchanged +/// 1.0 means grayscale +template +[[nodiscard]] constexpr Color desaturated(Color const& color, float amount = 0.1f) +{ + auto cc = tg::convert_color_to(color); + cc.s = tg::saturate(cc.s - amount); + return tg::convert_color_to(cc); +} + +/// converts a color to grayscale (but staying in the same color space/type) +template +[[nodiscard]] constexpr Color grayscale_of(Color const& color) +{ + auto cc = tg::convert_color_to(color); + cc.s = 0; + return tg::convert_color_to(cc); +} + +/// returns the complementary color (i.e. hue + 120°) +template +[[nodiscard]] constexpr Color complement_of(Color const& color) +{ + auto cc = tg::convert_color_to(color); + cc.h = tg::fract(cc.h + 0.5f); + return tg::convert_color_to(cc); +} + +/// returns the color triad (i.e. color, color + 120°, color + 240°) +template +[[nodiscard]] constexpr cc::array triad_of(Color const& color) +{ + auto c0 = tg::convert_color_to(color); + auto c1 = c0; + auto c2 = c0; + c1.h = tg::fract(c0.h + 1.f / 3); + c2.h = tg::fract(c0.h + 2.f / 3); + return {color, tg::convert_color_to(c1), tg::convert_color_to(c2)}; +} + +/// returns the color tetrad +template +[[nodiscard]] constexpr cc::array tetrad_of(Color const& color) +{ + auto c0 = tg::convert_color_to(color); + auto c1 = c0; + auto c2 = c0; + auto c3 = c0; + c1.h = tg::fract(c0.h + 1.f / 4); + c2.h = tg::fract(c0.h + 2.f / 4); + c3.h = tg::fract(c0.h + 3.f / 4); + return {color, tg::convert_color_to(c1), tg::convert_color_to(c2), tg::convert_color_to(c3)}; +} + +/// returns the color triad (i.e. color, color + 120°, color + 240°) +template +[[nodiscard]] constexpr cc::array split_complement_of(Color const& color) +{ + auto c0 = tg::convert_color_to(color); + auto c1 = c0; + auto c2 = c0; + c1.h = tg::fract(c0.h + 72.f / 360); + c2.h = tg::fract(c0.h + 216.f / 360); + return {color, tg::convert_color_to(c1), tg::convert_color_to(c2)}; +} + +/// rotates the hue of a color +/// 0.00 means unchanged +/// 0.33 means "one major shift" (red -> green -> blue) +/// 1.00 means unchanged as well (360°) +template +[[nodiscard]] constexpr Color rotate_color(Color const& color, float amount) +{ + auto cc = tg::convert_color_to(color); + cc.h = tg::fract(cc.h + amount); + return tg::convert_color_to(cc); +} + +} diff --git a/src/typed-geometry/feature/fixed_int.hh b/src/typed-geometry/feature/fixed_int.hh index eb0e649a..15f8af3c 100644 --- a/src/typed-geometry/feature/fixed_int.hh +++ b/src/typed-geometry/feature/fixed_int.hh @@ -1,5 +1,13 @@ #pragma once +#include + +#ifdef CC_COMPILER_MSVC +#define TG_MUL_U128 _umul128 +#else +#define TG_MUL_U128 _mulx_u64 +#endif + #include #include #include diff --git a/src/typed-geometry/functions/color/string-conversions.hh b/src/typed-geometry/functions/color/string-conversions.hh new file mode 100644 index 00000000..1beee231 --- /dev/null +++ b/src/typed-geometry/functions/color/string-conversions.hh @@ -0,0 +1,168 @@ +#pragma once + +#include + +#include +#include + +namespace tg +{ +namespace detail +{ +// -1 if invalid +constexpr int hex_from_char(char c) +{ + if ('0' <= c && c <= '9') + return c - '0'; + + if ('a' <= c && c <= 'f') + return 10 + (c - 'a'); + + if ('A' <= c && c <= 'F') + return 10 + (c - 'A'); + + return -1; +} + +// -1 if invalid +constexpr int hex_from_str12(cc::string_view s) +{ + if (s.size() == 1) + { + auto i = hex_from_char(s[0]); + if (i == -1) + return -1; + + return i * 16 + i; + } + + if (s.size() == 2) + { + auto i0 = hex_from_char(s[0]); + auto i1 = hex_from_char(s[1]); + if (i0 == -1 || i1 == -1) + return -1; + + return i0 * 16 + i1; + } + + return -1; +} + +template +constexpr bool parse_color_comp(T& c, cc::string_view s) +{ + auto i = hex_from_str12(s); + if (i < 0) + return false; + + if constexpr (std::is_same_v) + c = u8(i); + else + c = tg::color_scalar_from_float(i / 255.f); + + return true; +} +} + +template +constexpr bool parse_color_from_hex_string(ColorT& c, cc::string_view s) +{ + using T = std::decay_t; + + s = s.trim().trim('#'); + if (s.starts_with("0x")) + s = s.subview(2); + + switch (s.size()) + { + case 3: // rgb + if (!detail::parse_color_comp(c.r, s.subview(0, 1))) + return false; + if (!detail::parse_color_comp(c.g, s.subview(1, 1))) + return false; + if (!detail::parse_color_comp(c.b, s.subview(2, 1))) + return false; + if constexpr (tg::has_alpha) + c.a = detail::color_scalar::one(); + return true; + + case 6: // rrggbb + if (!detail::parse_color_comp(c.r, s.subview(0, 2))) + return false; + if (!detail::parse_color_comp(c.g, s.subview(2, 2))) + return false; + if (!detail::parse_color_comp(c.b, s.subview(4, 2))) + return false; + if constexpr (tg::has_alpha) + c.a = detail::color_scalar::one(); + return true; + } + + if constexpr (tg::has_alpha) + { + switch (s.size()) + { + case 4: // rgba + if (!detail::parse_color_comp(c.r, s.subview(0, 1))) + return false; + if (!detail::parse_color_comp(c.g, s.subview(1, 1))) + return false; + if (!detail::parse_color_comp(c.b, s.subview(2, 1))) + return false; + if (!detail::parse_color_comp(c.a, s.subview(3, 1))) + return false; + return true; + + case 8: // rrggbbaa + if (!detail::parse_color_comp(c.r, s.subview(0, 2))) + return false; + if (!detail::parse_color_comp(c.g, s.subview(2, 2))) + return false; + if (!detail::parse_color_comp(c.b, s.subview(4, 2))) + return false; + if (!detail::parse_color_comp(c.a, s.subview(6, 2))) + return false; + return true; + } + } + + return false; +} + +template +constexpr color<3, ScalarT> color<3, ScalarT>::from_hex_string(cc::string_view s) +{ + color<3, ScalarT> c; + auto ok = tg::parse_color_from_hex_string(c, s); + CC_ASSERT(ok && "could not parse color"); + return c; +} + +template +constexpr color<4, ScalarT> color<4, ScalarT>::from_hex_string(cc::string_view s) +{ + color<4, ScalarT> c; + auto ok = tg::parse_color_from_hex_string(c, s); + CC_ASSERT(ok && "could not parse color"); + return c; +} + +template +constexpr srgb srgb::from_hex_string(cc::string_view s) +{ + srgb c; + auto ok = tg::parse_color_from_hex_string(c, s); + CC_ASSERT(ok && "could not parse color"); + return c; +} + +template +constexpr srgba srgba::from_hex_string(cc::string_view s) +{ + srgba c; + auto ok = tg::parse_color_from_hex_string(c, s); + CC_ASSERT(ok && "could not parse color"); + return c; +} +} diff --git a/src/typed-geometry/functions/fixed_int/fixed_int_gen.hh b/src/typed-geometry/functions/fixed_int/fixed_int_gen.hh index 6281fc71..46a529c5 100644 --- a/src/typed-geometry/functions/fixed_int/fixed_int_gen.hh +++ b/src/typed-geometry/functions/fixed_int/fixed_int_gen.hh @@ -31,7 +31,7 @@ inline i128 imul(i128 lhs, i128 rhs) u64_word l01 = 0; u64_word l10 = 0; u64_word h00 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); l01 = u64_word(lhs.d[0]) * u64_word(rhs.d[1]); l10 = u64_word(lhs.d[1]) * u64_word(rhs.d[0]); unsigned char c = 0; @@ -106,8 +106,8 @@ inline i192 imul(i128 lhs, i64 rhs) u64_word l10 = 0; u64_word h00 = 0; u64_word h10 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs), &h00); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs), &h10); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs), &h00); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs), &h10); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -147,8 +147,8 @@ inline i192 imul(i192 lhs, i64 rhs) u64_word l20 = 0; u64_word h00 = 0; u64_word h10 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs), &h00); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs), &h10); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs), &h00); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs), &h10); l20 = u64_word(lhs.d[2]) * u64_word(rhs); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -186,8 +186,8 @@ inline i192 imul(i64 lhs, i128 rhs) u64_word l01 = 0; u64_word h00 = 0; u64_word h01 = 0; - l00 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[1]), &h01); + l00 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[1]), &h01); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -229,9 +229,9 @@ inline i192 imul(i128 lhs, i128 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h10 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); l11 = u64_word(lhs.d[1]) * u64_word(rhs.d[1]); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -278,9 +278,9 @@ inline i192 imul(i192 lhs, i128 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h10 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); l11 = u64_word(lhs.d[1]) * u64_word(rhs.d[1]); l20 = u64_word(lhs.d[2]) * u64_word(rhs.d[0]); unsigned char c = 0; @@ -323,8 +323,8 @@ inline i192 imul(i64 lhs, i192 rhs) u64_word l02 = 0; u64_word h00 = 0; u64_word h01 = 0; - l00 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[1]), &h01); + l00 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[1]), &h01); l02 = u64_word(lhs) * u64_word(rhs.d[2]); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -370,9 +370,9 @@ inline i192 imul(i128 lhs, i192 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h10 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); l02 = u64_word(lhs.d[0]) * u64_word(rhs.d[2]); l11 = u64_word(lhs.d[1]) * u64_word(rhs.d[1]); unsigned char c = 0; @@ -406,9 +406,9 @@ inline i192 imul(i192 lhs, i192 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h10 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); l02 = u64_word(lhs.d[0]) * u64_word(rhs.d[2]); l11 = u64_word(lhs.d[1]) * u64_word(rhs.d[1]); l20 = u64_word(lhs.d[2]) * u64_word(rhs.d[0]); @@ -446,9 +446,9 @@ inline i256 imul(i192 lhs, i64 rhs) u64_word h00 = 0; u64_word h10 = 0; u64_word h20 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs), &h00); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs), &h10); - l20 = _mulx_u64(u64_word(lhs.d[2]), u64_word(rhs), &h20); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs), &h00); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs), &h10); + l20 = TG_MUL_U128(u64_word(lhs.d[2]), u64_word(rhs), &h20); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -498,9 +498,9 @@ inline i256 imul(i256 lhs, i64 rhs) u64_word h00 = 0; u64_word h10 = 0; u64_word h20 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs), &h00); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs), &h10); - l20 = _mulx_u64(u64_word(lhs.d[2]), u64_word(rhs), &h20); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs), &h00); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs), &h10); + l20 = TG_MUL_U128(u64_word(lhs.d[2]), u64_word(rhs), &h20); l30 = u64_word(lhs.d[3]) * u64_word(rhs); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -550,10 +550,10 @@ inline i256 imul(i128 lhs, i128 rhs) u64_word h01 = 0; u64_word h10 = 0; u64_word h11 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -609,11 +609,11 @@ inline i256 imul(i192 lhs, i128 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); - l20 = _mulx_u64(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l20 = TG_MUL_U128(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); l21 = u64_word(lhs.d[2]) * u64_word(rhs.d[1]); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -674,11 +674,11 @@ inline i256 imul(i256 lhs, i128 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); - l20 = _mulx_u64(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l20 = TG_MUL_U128(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); l21 = u64_word(lhs.d[2]) * u64_word(rhs.d[1]); l30 = u64_word(lhs.d[3]) * u64_word(rhs.d[0]); unsigned char c = 0; @@ -730,9 +730,9 @@ inline i256 imul(i64 lhs, i192 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h02 = 0; - l00 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[1]), &h01); - l02 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[2]), &h02); + l00 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[1]), &h01); + l02 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[2]), &h02); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -786,11 +786,11 @@ inline i256 imul(i128 lhs, i192 rhs) u64_word h02 = 0; u64_word h10 = 0; u64_word h11 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l02 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l02 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); l12 = u64_word(lhs.d[1]) * u64_word(rhs.d[2]); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -853,12 +853,12 @@ inline i256 imul(i192 lhs, i192 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l02 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); - l20 = _mulx_u64(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l02 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l20 = TG_MUL_U128(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); l12 = u64_word(lhs.d[1]) * u64_word(rhs.d[2]); l21 = u64_word(lhs.d[2]) * u64_word(rhs.d[1]); unsigned char c = 0; @@ -926,12 +926,12 @@ inline i256 imul(i256 lhs, i192 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l02 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); - l20 = _mulx_u64(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l02 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l20 = TG_MUL_U128(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); l12 = u64_word(lhs.d[1]) * u64_word(rhs.d[2]); l21 = u64_word(lhs.d[2]) * u64_word(rhs.d[1]); l30 = u64_word(lhs.d[3]) * u64_word(rhs.d[0]); @@ -988,9 +988,9 @@ inline i256 imul(i64 lhs, i256 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h02 = 0; - l00 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[1]), &h01); - l02 = _mulx_u64(u64_word(lhs), u64_word(rhs.d[2]), &h02); + l00 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[1]), &h01); + l02 = TG_MUL_U128(u64_word(lhs), u64_word(rhs.d[2]), &h02); l03 = u64_word(lhs) * u64_word(rhs.d[3]); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -1048,11 +1048,11 @@ inline i256 imul(i128 lhs, i256 rhs) u64_word h02 = 0; u64_word h10 = 0; u64_word h11 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l02 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l02 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); l03 = u64_word(lhs.d[0]) * u64_word(rhs.d[3]); l12 = u64_word(lhs.d[1]) * u64_word(rhs.d[2]); unsigned char c = 0; @@ -1119,12 +1119,12 @@ inline i256 imul(i192 lhs, i256 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l02 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); - l20 = _mulx_u64(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l02 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l20 = TG_MUL_U128(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); l03 = u64_word(lhs.d[0]) * u64_word(rhs.d[3]); l12 = u64_word(lhs.d[1]) * u64_word(rhs.d[2]); l21 = u64_word(lhs.d[2]) * u64_word(rhs.d[1]); @@ -1175,12 +1175,12 @@ inline i256 imul(i256 lhs, i256 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); - l01 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); - l02 = _mulx_u64(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); - l10 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); - l11 = _mulx_u64(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); - l20 = _mulx_u64(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); + l00 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[0]), &h00); + l01 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[1]), &h01); + l02 = TG_MUL_U128(u64_word(lhs.d[0]), u64_word(rhs.d[2]), &h02); + l10 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[0]), &h10); + l11 = TG_MUL_U128(u64_word(lhs.d[1]), u64_word(rhs.d[1]), &h11); + l20 = TG_MUL_U128(u64_word(lhs.d[2]), u64_word(rhs.d[0]), &h20); l03 = u64_word(lhs.d[0]) * u64_word(rhs.d[3]); l12 = u64_word(lhs.d[1]) * u64_word(rhs.d[2]); l21 = u64_word(lhs.d[2]) * u64_word(rhs.d[1]); diff --git a/src/typed-geometry/functions/fixed_int/fixed_uint_gen.hh b/src/typed-geometry/functions/fixed_int/fixed_uint_gen.hh index 582079a7..fad16c97 100644 --- a/src/typed-geometry/functions/fixed_int/fixed_uint_gen.hh +++ b/src/typed-geometry/functions/fixed_int/fixed_uint_gen.hh @@ -20,7 +20,7 @@ inline u128 mul(u64 lhs, u64 rhs) u128 res; u64_word l00 = 0; u64_word h00 = 0; - l00 = _mulx_u64(lhs, rhs, &h00); + l00 = TG_MUL_U128(lhs, rhs, &h00); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); res.d[1] = c + h00; @@ -34,7 +34,7 @@ inline u128 mul(u128 lhs, u64 rhs) u64_word l00 = 0; u64_word l10 = 0; u64_word h00 = 0; - l00 = _mulx_u64(lhs.d[0], rhs, &h00); + l00 = TG_MUL_U128(lhs.d[0], rhs, &h00); l10 = lhs.d[1] * rhs; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -49,7 +49,7 @@ inline u128 mul(u64 lhs, u128 rhs) u64_word l00 = 0; u64_word l01 = 0; u64_word h00 = 0; - l00 = _mulx_u64(lhs, rhs.d[0], &h00); + l00 = TG_MUL_U128(lhs, rhs.d[0], &h00); l01 = lhs * rhs.d[1]; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -65,7 +65,7 @@ inline u128 mul(u128 lhs, u128 rhs) u64_word l01 = 0; u64_word l10 = 0; u64_word h00 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); l01 = lhs.d[0] * rhs.d[1]; l10 = lhs.d[1] * rhs.d[0]; unsigned char c = 0; @@ -82,8 +82,8 @@ inline u192 mul(u128 lhs, u64 rhs) u64_word l10 = 0; u64_word h00 = 0; u64_word h10 = 0; - l00 = _mulx_u64(lhs.d[0], rhs, &h00); - l10 = _mulx_u64(lhs.d[1], rhs, &h10); + l00 = TG_MUL_U128(lhs.d[0], rhs, &h00); + l10 = TG_MUL_U128(lhs.d[1], rhs, &h10); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -103,8 +103,8 @@ inline u192 mul(u192 lhs, u64 rhs) u64_word l20 = 0; u64_word h00 = 0; u64_word h10 = 0; - l00 = _mulx_u64(lhs.d[0], rhs, &h00); - l10 = _mulx_u64(lhs.d[1], rhs, &h10); + l00 = TG_MUL_U128(lhs.d[0], rhs, &h00); + l10 = TG_MUL_U128(lhs.d[1], rhs, &h10); l20 = lhs.d[2] * rhs; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -124,8 +124,8 @@ inline u192 mul(u64 lhs, u128 rhs) u64_word l01 = 0; u64_word h00 = 0; u64_word h01 = 0; - l00 = _mulx_u64(lhs, rhs.d[0], &h00); - l01 = _mulx_u64(lhs, rhs.d[1], &h01); + l00 = TG_MUL_U128(lhs, rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs, rhs.d[1], &h01); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -147,9 +147,9 @@ inline u192 mul(u128 lhs, u128 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h10 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); l11 = lhs.d[1] * rhs.d[1]; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -174,9 +174,9 @@ inline u192 mul(u192 lhs, u128 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h10 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); l11 = lhs.d[1] * rhs.d[1]; l20 = lhs.d[2] * rhs.d[0]; unsigned char c = 0; @@ -199,8 +199,8 @@ inline u192 mul(u64 lhs, u192 rhs) u64_word l02 = 0; u64_word h00 = 0; u64_word h01 = 0; - l00 = _mulx_u64(lhs, rhs.d[0], &h00); - l01 = _mulx_u64(lhs, rhs.d[1], &h01); + l00 = TG_MUL_U128(lhs, rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs, rhs.d[1], &h01); l02 = lhs * rhs.d[2]; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -224,9 +224,9 @@ inline u192 mul(u128 lhs, u192 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h10 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); l02 = lhs.d[0] * rhs.d[2]; l11 = lhs.d[1] * rhs.d[1]; unsigned char c = 0; @@ -253,9 +253,9 @@ inline u192 mul(u192 lhs, u192 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h10 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); l02 = lhs.d[0] * rhs.d[2]; l11 = lhs.d[1] * rhs.d[1]; l20 = lhs.d[2] * rhs.d[0]; @@ -280,9 +280,9 @@ inline u256 mul(u192 lhs, u64 rhs) u64_word h00 = 0; u64_word h10 = 0; u64_word h20 = 0; - l00 = _mulx_u64(lhs.d[0], rhs, &h00); - l10 = _mulx_u64(lhs.d[1], rhs, &h10); - l20 = _mulx_u64(lhs.d[2], rhs, &h20); + l00 = TG_MUL_U128(lhs.d[0], rhs, &h00); + l10 = TG_MUL_U128(lhs.d[1], rhs, &h10); + l20 = TG_MUL_U128(lhs.d[2], rhs, &h20); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -308,9 +308,9 @@ inline u256 mul(u256 lhs, u64 rhs) u64_word h00 = 0; u64_word h10 = 0; u64_word h20 = 0; - l00 = _mulx_u64(lhs.d[0], rhs, &h00); - l10 = _mulx_u64(lhs.d[1], rhs, &h10); - l20 = _mulx_u64(lhs.d[2], rhs, &h20); + l00 = TG_MUL_U128(lhs.d[0], rhs, &h00); + l10 = TG_MUL_U128(lhs.d[1], rhs, &h10); + l20 = TG_MUL_U128(lhs.d[2], rhs, &h20); l30 = lhs.d[3] * rhs; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -338,10 +338,10 @@ inline u256 mul(u128 lhs, u128 rhs) u64_word h01 = 0; u64_word h10 = 0; u64_word h11 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -373,11 +373,11 @@ inline u256 mul(u192 lhs, u128 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); - l20 = _mulx_u64(lhs.d[2], rhs.d[0], &h20); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); + l20 = TG_MUL_U128(lhs.d[2], rhs.d[0], &h20); l21 = lhs.d[2] * rhs.d[1]; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -412,11 +412,11 @@ inline u256 mul(u256 lhs, u128 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); - l20 = _mulx_u64(lhs.d[2], rhs.d[0], &h20); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); + l20 = TG_MUL_U128(lhs.d[2], rhs.d[0], &h20); l21 = lhs.d[2] * rhs.d[1]; l30 = lhs.d[3] * rhs.d[0]; unsigned char c = 0; @@ -446,9 +446,9 @@ inline u256 mul(u64 lhs, u192 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h02 = 0; - l00 = _mulx_u64(lhs, rhs.d[0], &h00); - l01 = _mulx_u64(lhs, rhs.d[1], &h01); - l02 = _mulx_u64(lhs, rhs.d[2], &h02); + l00 = TG_MUL_U128(lhs, rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs, rhs.d[1], &h01); + l02 = TG_MUL_U128(lhs, rhs.d[2], &h02); unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); c += _addcarry_u64(0, res.d[1], c, &res.d[1]); @@ -478,11 +478,11 @@ inline u256 mul(u128 lhs, u192 rhs) u64_word h02 = 0; u64_word h10 = 0; u64_word h11 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l02 = _mulx_u64(lhs.d[0], rhs.d[2], &h02); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l02 = TG_MUL_U128(lhs.d[0], rhs.d[2], &h02); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); l12 = lhs.d[1] * rhs.d[2]; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -519,12 +519,12 @@ inline u256 mul(u192 lhs, u192 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l02 = _mulx_u64(lhs.d[0], rhs.d[2], &h02); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); - l20 = _mulx_u64(lhs.d[2], rhs.d[0], &h20); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l02 = TG_MUL_U128(lhs.d[0], rhs.d[2], &h02); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); + l20 = TG_MUL_U128(lhs.d[2], rhs.d[0], &h20); l12 = lhs.d[1] * rhs.d[2]; l21 = lhs.d[2] * rhs.d[1]; unsigned char c = 0; @@ -564,12 +564,12 @@ inline u256 mul(u256 lhs, u192 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l02 = _mulx_u64(lhs.d[0], rhs.d[2], &h02); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); - l20 = _mulx_u64(lhs.d[2], rhs.d[0], &h20); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l02 = TG_MUL_U128(lhs.d[0], rhs.d[2], &h02); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); + l20 = TG_MUL_U128(lhs.d[2], rhs.d[0], &h20); l12 = lhs.d[1] * rhs.d[2]; l21 = lhs.d[2] * rhs.d[1]; l30 = lhs.d[3] * rhs.d[0]; @@ -602,9 +602,9 @@ inline u256 mul(u64 lhs, u256 rhs) u64_word h00 = 0; u64_word h01 = 0; u64_word h02 = 0; - l00 = _mulx_u64(lhs, rhs.d[0], &h00); - l01 = _mulx_u64(lhs, rhs.d[1], &h01); - l02 = _mulx_u64(lhs, rhs.d[2], &h02); + l00 = TG_MUL_U128(lhs, rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs, rhs.d[1], &h01); + l02 = TG_MUL_U128(lhs, rhs.d[2], &h02); l03 = lhs * rhs.d[3]; unsigned char c = 0; c += _addcarry_u64(0, res.d[0], l00, &res.d[0]); @@ -636,11 +636,11 @@ inline u256 mul(u128 lhs, u256 rhs) u64_word h02 = 0; u64_word h10 = 0; u64_word h11 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l02 = _mulx_u64(lhs.d[0], rhs.d[2], &h02); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l02 = TG_MUL_U128(lhs.d[0], rhs.d[2], &h02); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); l03 = lhs.d[0] * rhs.d[3]; l12 = lhs.d[1] * rhs.d[2]; unsigned char c = 0; @@ -679,12 +679,12 @@ inline u256 mul(u192 lhs, u256 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l02 = _mulx_u64(lhs.d[0], rhs.d[2], &h02); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); - l20 = _mulx_u64(lhs.d[2], rhs.d[0], &h20); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l02 = TG_MUL_U128(lhs.d[0], rhs.d[2], &h02); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); + l20 = TG_MUL_U128(lhs.d[2], rhs.d[0], &h20); l03 = lhs.d[0] * rhs.d[3]; l12 = lhs.d[1] * rhs.d[2]; l21 = lhs.d[2] * rhs.d[1]; @@ -726,12 +726,12 @@ inline u256 mul(u256 lhs, u256 rhs) u64_word h10 = 0; u64_word h11 = 0; u64_word h20 = 0; - l00 = _mulx_u64(lhs.d[0], rhs.d[0], &h00); - l01 = _mulx_u64(lhs.d[0], rhs.d[1], &h01); - l02 = _mulx_u64(lhs.d[0], rhs.d[2], &h02); - l10 = _mulx_u64(lhs.d[1], rhs.d[0], &h10); - l11 = _mulx_u64(lhs.d[1], rhs.d[1], &h11); - l20 = _mulx_u64(lhs.d[2], rhs.d[0], &h20); + l00 = TG_MUL_U128(lhs.d[0], rhs.d[0], &h00); + l01 = TG_MUL_U128(lhs.d[0], rhs.d[1], &h01); + l02 = TG_MUL_U128(lhs.d[0], rhs.d[2], &h02); + l10 = TG_MUL_U128(lhs.d[1], rhs.d[0], &h10); + l11 = TG_MUL_U128(lhs.d[1], rhs.d[1], &h11); + l20 = TG_MUL_U128(lhs.d[2], rhs.d[0], &h20); l03 = lhs.d[0] * rhs.d[3]; l12 = lhs.d[1] * rhs.d[2]; l21 = lhs.d[2] * rhs.d[1]; diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index ee4ffbdf..12b302f2 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -235,16 +235,19 @@ template line<3, ScalarT> segment_line = line<3, ScalarT>(s.pos0, normalize(s.pos1 - s.pos0)); auto insec = intersection_parameter(segment_line, o); - // no intersection exists if (!insec.has_value()) return {}; - // case 2: One seg. point inside the cylinder and one outside -> intersection with boundary must exist - if (con_pos0 || con_pos1) + // case 2: One seg. point inside the convex object and one outside -> intersection with boundary must exist + if (con_pos0) { - // one point of the segment is inside the cylinder and one is outside -> intersection must exist - auto param = con_pos0 ? max(insec.value().start, insec.value().end) : min(insec.value().start, insec.value().end); - return segment<3, ScalarT>{segment_line.pos + segment_line.dir * param, con_pos0 ? s.pos0 : s.pos1}; + float param = (dot(s.pos1 - s.pos0, segment_line[insec.value().start]) > 0) ? insec.value().start : insec.value().end; + return segment<3, ScalarT>{s.pos0, segment_line.pos + segment_line.dir * param}; + } + else if (con_pos1) + { + float param = (dot(s.pos0 - s.pos1, segment_line[insec.value().start]) > 0) ? insec.value().start : insec.value().end; + return segment<3, ScalarT>{segment_line.pos + segment_line.dir * param, s.pos1}; } // case 3: both points of segment outside of the convex object @@ -254,8 +257,87 @@ template return {}; } +// segment - boundary object +template +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection_segment_boundary_impl(segment<3, ScalarT> const& s, B const& b) +{ + // line extension of segment + auto const line = line3::from_points(s.pos0, s.pos1); + // intersection of line with boundary object + auto const params = intersection_parameter(line, b); + + if (!params.any()) + return {}; + + auto const dist = distance(s.pos0, s.pos1); + auto n_hits = 0; + tg::pos<3, ScalarT> ps[2]; + // check if line intersections are within the segment range + for (auto i = 0; i < params.size(); ++i) + { + auto const p = params[i]; + if (ScalarT(0) <= p && p <= dist) + { + ps[n_hits++] = line[p]; + } + } + return hits<2, tg::pos<3, ScalarT>>(ps, n_hits); +} + template using try_closest_intersection_parameter = decltype(closest_intersection_parameter(std::declval(), std::declval())); + +// circular permutation to the vertices of triangle ta such that ta.pos0 is the only vertex that lies on positive halfspace induced by tb +template +void rotate_devillers_triangle(tg::triangle<3, ScalarT>& ta, tg::triangle<3, ScalarT>& tb, tg::comp3& determinants, tg::comp3& determinants_t2) +{ + // implementation of triangle permutation according to: https://hal.inria.fr/inria-00072100/document + + ScalarT d01 = determinants[0] * determinants[1]; + ScalarT d02 = determinants[0] * determinants[2]; + + if (d01 > 0.0f) // vertices 0 and 1 on one side + { + ta = {ta.pos2, ta.pos0, ta.pos1}; + determinants = {determinants[2], determinants[0], determinants[1]}; + } + else if (d02 > 0.0f) // vertices 0 and 2 on one side + { + ta = {ta.pos1, ta.pos2, ta.pos0}; + determinants = {determinants[1], determinants[2], determinants[0]}; + } + else if (determinants[0] == 0.f) + { + if (determinants[1] * determinants[2] < 0.f || determinants[1] == 0.f) // vertices 1 and 2 on different sides of plane and vertex 0 on plane + { + ta = {ta.pos2, ta.pos0, ta.pos1}; + determinants = {determinants[2], determinants[0], determinants[1]}; + } + else if (determinants[2] == 0.f) // vertices 0 and 2 on the plane + { + ta = {ta.pos1, ta.pos2, ta.pos0}; + determinants = {determinants[1], determinants[2], determinants[0]}; + } + } + + // swap operation to map triangle1.pos0 to positive halfspace induced by plane of triangle2 + if (determinants[0] < 0.f) + { + tb = {tb.pos0, tb.pos2, tb.pos1}; + determinants = {-determinants[0], -determinants[1], -determinants[2]}; + determinants_t2 = {determinants_t2[0], determinants_t2[2], determinants_t2[1]}; + } + + else if (determinants[0] == 0.f && (determinants[1] * determinants[2] > 0)) + { + if (determinants[1] > 0) // swap + { + tb = {tb.pos0, tb.pos2, tb.pos1}; + determinants = {-determinants[0], -determinants[1], -determinants[2]}; + determinants_t2 = {determinants_t2[0], determinants_t2[2], determinants_t2[1]}; + } + } +} } @@ -2091,15 +2173,15 @@ template // ====================================== Checks if Object Intersects Object ====================================== template -[[nodiscard]] constexpr bool intersects(segment<3, ScalarT> const& seg, sphere<2, ScalarT, 3> const& disk) +[[nodiscard]] constexpr bool intersects(segment<3, ScalarT> const& segment, sphere<2, ScalarT, 3> const& disk) { - auto t = intersection(seg, tg::plane<3, ScalarT>(disk.normal, disk.center)); + auto t = intersection(segment, tg::plane<3, ScalarT>(disk.normal, disk.center)); return t.has_value() && distance_sqr(t.value(), disk.center) <= pow2(disk.radius); } template -[[nodiscard]] constexpr bool intersects(sphere<2, ScalarT, 3> const& disk, segment<3, ScalarT> const& seg) +[[nodiscard]] constexpr bool intersects(sphere<2, ScalarT, 3> const& disk, segment<3, ScalarT> const& segment) { - return intersects(seg, disk); + return intersects(segment, disk); } template @@ -2195,32 +2277,32 @@ template } template -[[nodiscard]] constexpr bool intersects_conservative(frustum<3, ScalarT> const& f, sphere<3, ScalarT> const& s, dont_deduce eps = ScalarT(0)) +[[nodiscard]] constexpr bool intersects_conservative(frustum<3, ScalarT> const& frustum, sphere<3, ScalarT> const& sphere, dont_deduce eps = ScalarT(0)) { // if center is further away than radius, there cannot be any intersection - auto const d_nx = signed_distance(s.center, f.planes[f.plane_idx_neg_x]); - if (d_nx > s.radius + eps) + auto const d_nx = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_neg_x]); + if (d_nx > sphere.radius + eps) return false; - auto const d_ny = signed_distance(s.center, f.planes[f.plane_idx_neg_y]); - if (d_ny > s.radius + eps) + auto const d_ny = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_neg_y]); + if (d_ny > sphere.radius + eps) return false; - auto const d_nz = signed_distance(s.center, f.planes[f.plane_idx_neg_z]); - if (d_nz > s.radius + eps) + auto const d_nz = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_neg_z]); + if (d_nz > sphere.radius + eps) return false; - auto const d_px = signed_distance(s.center, f.planes[f.plane_idx_pos_x]); - if (d_px > s.radius + eps) + auto const d_px = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_pos_x]); + if (d_px > sphere.radius + eps) return false; - auto const d_py = signed_distance(s.center, f.planes[f.plane_idx_pos_y]); - if (d_py > s.radius + eps) + auto const d_py = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_pos_y]); + if (d_py > sphere.radius + eps) return false; - auto const d_pz = signed_distance(s.center, f.planes[f.plane_idx_pos_z]); - if (d_pz > s.radius + eps) + auto const d_pz = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_pos_z]); + if (d_pz > sphere.radius + eps) return false; // conservative approximation! @@ -2228,21 +2310,21 @@ template } template -[[nodiscard]] constexpr bool intersects_conservative(frustum<3, ScalarT> const& f, aabb<3, ScalarT> const& bb) +[[nodiscard]] constexpr bool intersects_conservative(frustum<3, ScalarT> const& frustum, aabb<3, ScalarT> const& bb) { using halfspace_t = halfspace<3, ScalarT>; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_x]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_x]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_y]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_y]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_z]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_z]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_x]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_x]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_y]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_y]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_z]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_z]), bb)) return false; // conservative approximation! @@ -2250,21 +2332,21 @@ template } template -[[nodiscard]] constexpr bool intersects_conservative(frustum<3, ScalarT> const& f, box<3, ScalarT> const& bb) +[[nodiscard]] constexpr bool intersects_conservative(frustum<3, ScalarT> const& frustum, box<3, ScalarT> const& box) { using halfspace_t = halfspace<3, ScalarT>; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_x]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_x]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_y]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_y]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_z]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_z]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_x]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_x]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_y]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_y]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_z]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_z]), box)) return false; // conservative approximation! @@ -2272,28 +2354,28 @@ template } template -[[nodiscard]] constexpr bool intersects_conservative(inf_frustum<3, ScalarT> const& f, sphere<3, ScalarT> const& s, dont_deduce eps = ScalarT(0)) +[[nodiscard]] constexpr bool intersects_conservative(inf_frustum<3, ScalarT> const& frustum, sphere<3, ScalarT> const& sphere, dont_deduce eps = ScalarT(0)) { // if center is further away than radius, there cannot be any intersection - auto const d_nx = signed_distance(s.center, f.planes[f.plane_idx_neg_x]); - if (d_nx > s.radius + eps) + auto const d_nx = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_neg_x]); + if (d_nx > sphere.radius + eps) return false; - auto const d_ny = signed_distance(s.center, f.planes[f.plane_idx_neg_y]); - if (d_ny > s.radius + eps) + auto const d_ny = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_neg_y]); + if (d_ny > sphere.radius + eps) return false; - auto const d_px = signed_distance(s.center, f.planes[f.plane_idx_pos_x]); - if (d_px > s.radius + eps) + auto const d_px = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_pos_x]); + if (d_px > sphere.radius + eps) return false; - auto const d_py = signed_distance(s.center, f.planes[f.plane_idx_pos_y]); - if (d_py > s.radius + eps) + auto const d_py = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_pos_y]); + if (d_py > sphere.radius + eps) return false; - auto const d_pz = signed_distance(s.center, f.planes[f.plane_idx_pos_z]); - if (d_pz > s.radius + eps) + auto const d_pz = signed_distance(sphere.center, frustum.planes[frustum.plane_idx_pos_z]); + if (d_pz > sphere.radius + eps) return false; // conservative approximation! @@ -2301,19 +2383,19 @@ template } template -[[nodiscard]] constexpr bool intersects_conservative(inf_frustum<3, ScalarT> const& f, aabb<3, ScalarT> const& bb) +[[nodiscard]] constexpr bool intersects_conservative(inf_frustum<3, ScalarT> const& frustum, aabb<3, ScalarT> const& bb) { using halfspace_t = halfspace<3, ScalarT>; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_x]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_x]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_y]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_y]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_x]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_x]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_y]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_y]), bb)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_z]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_z]), bb)) return false; // conservative approximation! @@ -2321,19 +2403,19 @@ template } template -[[nodiscard]] constexpr bool intersects_conservative(inf_frustum<3, ScalarT> const& f, box<3, ScalarT> const& bb) +[[nodiscard]] constexpr bool intersects_conservative(inf_frustum<3, ScalarT> const& frustum, box<3, ScalarT> const& box) { using halfspace_t = halfspace<3, ScalarT>; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_x]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_x]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_neg_y]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_neg_y]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_x]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_x]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_y]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_y]), box)) return false; - if (!intersects(halfspace_t(f.planes[f.plane_idx_pos_z]), bb)) + if (!intersects(halfspace_t(frustum.planes[frustum.plane_idx_pos_z]), box)) return false; // conservative approximation! @@ -2341,12 +2423,12 @@ template } template -[[nodiscard]] constexpr optional> intersection(plane<3, ScalarT> const& plane, triangle<3, ScalarT> const& t) +[[nodiscard]] constexpr optional> intersection(plane<3, ScalarT> const& plane, triangle<3, ScalarT> const& triangle) { // classify vertices - auto sign_v1 = signed_distance(t.pos0, plane) < 0 ? false : true; - auto sign_v2 = signed_distance(t.pos1, plane) < 0 ? false : true; - auto sign_v3 = signed_distance(t.pos2, plane) < 0 ? false : true; + auto sign_v1 = signed_distance(triangle.pos0, plane) < 0 ? false : true; + auto sign_v2 = signed_distance(triangle.pos1, plane) < 0 ? false : true; + auto sign_v3 = signed_distance(triangle.pos2, plane) < 0 ? false : true; // exclude some degenerate cases? e.g. vertices of triangle on same positions, angle constraints.. @@ -2361,18 +2443,18 @@ template // intersection exists (exactly 2 vertices on one side of the plane and exactly 1 vertex on the other side) if (iv == sign_v1) { - i1 = intersection(segment<3, ScalarT>(t.pos0, t.pos1), plane).value(); - i2 = intersection(segment<3, ScalarT>(t.pos0, t.pos2), plane).value(); + i1 = intersection(segment<3, ScalarT>(triangle.pos0, triangle.pos1), plane).value(); + i2 = intersection(segment<3, ScalarT>(triangle.pos0, triangle.pos2), plane).value(); } else if (iv == sign_v2) { - i1 = intersection(segment<3, ScalarT>(t.pos0, t.pos1), plane).value(); - i2 = intersection(segment<3, ScalarT>(t.pos1, t.pos2), plane).value(); + i1 = intersection(segment<3, ScalarT>(triangle.pos0, triangle.pos1), plane).value(); + i2 = intersection(segment<3, ScalarT>(triangle.pos1, triangle.pos2), plane).value(); } else if (iv == sign_v3) { - i1 = intersection(segment<3, ScalarT>(t.pos0, t.pos2), plane).value(); - i2 = intersection(segment<3, ScalarT>(t.pos1, t.pos2), plane).value(); + i1 = intersection(segment<3, ScalarT>(triangle.pos0, triangle.pos2), plane).value(); + i2 = intersection(segment<3, ScalarT>(triangle.pos1, triangle.pos2), plane).value(); } else return {}; @@ -2381,15 +2463,15 @@ template } template -[[nodiscard]] constexpr optional> intersection(triangle<3, ScalarT> const& t, plane<3, ScalarT> const& plane) +[[nodiscard]] constexpr optional> intersection(triangle<3, ScalarT> const& triangle, plane<3, ScalarT> const& plane) { - return intersection(plane, t); + return intersection(plane, triangle); } template -[[nodiscard]] constexpr bool intersects(plane<3, ScalarT> const& plane, triangle<3, ScalarT> const& t) +[[nodiscard]] constexpr bool intersects(plane<3, ScalarT> const& plane, triangle<3, ScalarT> const& triangle) { - tg::array, 3> triangle_pos = {t.pos0, t.pos1, t.pos2}; + tg::array, 3> triangle_pos = {triangle.pos0, triangle.pos1, triangle.pos2}; ScalarT sign = 0; for (auto tr : triangle_pos) @@ -2412,77 +2494,329 @@ template } template -[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& t, plane<3, ScalarT> const& plane) +[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& triangle, plane<3, ScalarT> const& plane) { - return intersects(plane, t); + return intersects(plane, triangle); } template -[[nodiscard]] constexpr optional> intersection(triangle<3, ScalarT> const& t1, triangle<3, ScalarT> const& t2) +[[nodiscard]] constexpr bool intersects(tg::triangle<2, ScalarT> const& t1, tg::triangle<2, ScalarT> const& t2) { - // early out: check with plane clamped by triangle t1 - auto const plane_t1 = plane_of(t1); + // Implementation of: https://hal.inria.fr/inria-00072100/document - if (!intersects(t2, plane_t1)) - return {}; + auto const determin = [](tg::pos<2, ScalarT> pa, tg::pos<2, ScalarT> pb, tg::pos<2, ScalarT> pc) -> float + { + auto m = tg::mat<2, 2, ScalarT>::from_data_colwise({pa.x - pc.x, pb.x - pc.x, pa.y - pc.y, pb.y - pc.y}); + return tg::determinant(m); + }; - array, 2> insecs; - array, 3> segments_t1 = edges_of(t1); - array, 3> segments_t2 = edges_of(t2); - int insec_count = 0; + auto const counter_clock = [determin](tg::triangle<2, ScalarT>& tri_a) -> void + { + // if (tg::cross(vec<2, ScalarT>(tri_a.pos1 - tri_a.pos0), vec<2, ScalarT>(tri_a.pos2 - tri_a.pos0)) < 0) + if (determin(tri_a.pos0, tri_a.pos1, tri_a.pos2) < 0) + tri_a = tg::triangle<2, ScalarT>(tri_a.pos0, tri_a.pos2, tri_a.pos1); // swap + }; - // check intersection of t1 segments with t2 - for (auto const& s : segments_t1) + auto const rotate = [determin](tg::triangle<2, ScalarT>& tri_b, tg::triangle<2, ScalarT>& tri_a, tg::comp3& determinants_a) -> void { - auto insec = intersection(s, t2); + // rotate triangle vertices by one + tri_b = tg::triangle<2, ScalarT>(tri_b.pos2, tri_b.pos0, tri_b.pos1); + + // recompute determinants + determinants_a = tg::comp3(determin(tri_b.pos0, tri_b.pos1, tri_a.pos0), determin(tri_b.pos1, tri_b.pos2, tri_a.pos0), + determin(tri_b.pos2, tri_b.pos0, tri_a.pos0)); + }; + + tg::triangle<2, ScalarT> ta = t1; + tg::triangle<2, ScalarT> tb = t2; + + // ensure counter-clockwise orientation + counter_clock(ta); + counter_clock(tb); + + auto det_ta0 = tg::comp3(determin(tb.pos0, tb.pos1, ta.pos0), determin(tb.pos1, tb.pos2, ta.pos0), determin(tb.pos2, tb.pos0, ta.pos0)); + auto det_01 = det_ta0[0] * det_ta0[1]; + auto det_12 = det_ta0[1] * det_ta0[2]; + auto det_02 = det_ta0[0] * det_ta0[2]; + - if (insec.has_value()) + if (det_01 > 0.f && det_12 > 0.f) // ta.pos0 interior of tb + return true; + + if (det_01 == 0 && det_12 == 0 && det_02 == 0) // ta.pos0 lies on vertex of tb + return true; + + if (det_01 == 0 && ((det_ta0[1] > 0 && det_ta0[2] > 0) || (det_ta0[0] > 0 && det_ta0[2] > 0))) // ta.pos0 interior of edge of tb + return true; + + if (det_12 == 0 && ((det_ta0[0] > 0 && det_ta0[1] > 0) || (det_ta0[0] > 0 && det_ta0[2] > 0))) // ta.pos0 interior of edge of tb + return true; + + // Rotate to Region1 or Region2 + while (!(det_ta0[0] > 0 && det_ta0[2] < 0)) + rotate(tb, ta, det_ta0); + + // decision tree + // R1 + if (det_ta0[1] > 0) + { + // I + if (determin(tb.pos2, tb.pos0, ta.pos1) >= 0) { - insecs[insec_count++] = insec.value(); + // II.a + if (determin(tb.pos2, ta.pos0, ta.pos1) < 0) + return false; + // III.a + else if (determin(ta.pos0, tb.pos0, ta.pos1) < 0) + { + // IV.a + if (determin(ta.pos0, tb.pos0, ta.pos2) < 0) + return false; + // V + else if (determin(ta.pos1, ta.pos2, tb.pos0) < 0) + return false; + } + return true; } + // II.b + else if (determin(tb.pos2, tb.pos0, ta.pos2) < 0) + return false; + // III.b + else if (determin(ta.pos1, ta.pos2, tb.pos2) < 0) + return false; + // IV.b + else if (determin(ta.pos0, tb.pos0, ta.pos2) < 0) + return false; - if (insec_count >= 2) - return segment<3, ScalarT>{insecs[0], insecs[1]}; + return true; } - // check intersection of t2 segments with t1 - for (auto const& s : segments_t2) + // R2 + else if (determin(tb.pos2, tb.pos0, ta.pos1) >= 0) { - auto insec = intersection(s, t1); - if (insec.has_value()) + // II.a + if (determin(tb.pos1, tb.pos2, ta.pos1) >= 0) { - insecs[insec_count++] = insec.value(); + // III.a + if (determin(ta.pos0, tb.pos0, ta.pos1) >= 0) + { + // IV.a + if (determin(ta.pos0, tb.pos1, ta.pos1) > 0) + return false; + + return true; + } + // IV.b + else if (determin(ta.pos0, tb.pos0, ta.pos2) < 0) + return false; + // V.a + else if (determin(tb.pos2, tb.pos0, ta.pos2) < 0) + { + if (determin(ta.pos1, tb.pos0, ta.pos2) > 0) + return false; + } + + return true; } + // III.b + else if (determin(ta.pos0, tb.pos1, ta.pos1) > 0) + return false; + // IV.c + else if (determin(tb.pos1, tb.pos2, ta.pos2) < 0) + return false; + // V.b + else if (determin(ta.pos1, ta.pos2, tb.pos1) < 0) + return false; - if (insec_count >= 2) - return segment<3, ScalarT>{insecs[0], insecs[1]}; + return true; + } + // II.b + else if (determin(tb.pos2, tb.pos0, ta.pos2) < 0) + return false; + // III.c + else if (determin(ta.pos1, ta.pos2, tb.pos2) >= 0) + { + // IV.d + if (determin(ta.pos2, ta.pos0, tb.pos0) < 0) + return false; } + // IV.e + else if (determin(ta.pos1, ta.pos2, tb.pos1) < 0) + return false; + // V.c + else if (determin(tb.pos1, tb.pos2, ta.pos2) < 0) + return false; - if (insec_count == 1) - return segment<3, ScalarT>{insecs[0], insecs[0]}; + return true; +} - return {}; +template +[[nodiscard]] constexpr bool intersects(tg::triangle<3, ScalarT> const& t1, tg::triangle<3, ScalarT> const& t2) +{ + // https://hal.inria.fr/inria-00072100/document + + auto const determin = [](tg::pos<3, ScalarT> pa, tg::pos<3, ScalarT> pb, tg::pos<3, ScalarT> pc, tg::pos<3, ScalarT> pd) -> float + { + auto m = tg::mat<3, 3, ScalarT>::from_data_colwise( + {pa.x - pd.x, pb.x - pd.x, pc.x - pd.x, pa.y - pd.y, pb.y - pd.y, pc.y - pd.y, pa.z - pd.z, pb.z - pd.z, pc.z - pd.z}); + return tg::determinant(m); + }; + + auto det_t2_t1 = tg::comp3(determin(t2.pos0, t2.pos1, t2.pos2, t1.pos0), determin(t2.pos0, t2.pos1, t2.pos2, t1.pos1), + determin(t2.pos0, t2.pos1, t2.pos2, t1.pos2)); + + auto const dt2_01 = det_t2_t1[0] * det_t2_t1[1]; + auto const dt2_02 = det_t2_t1[0] * det_t2_t1[2]; + + // a) no insec of t1 with plane of t2 + if (dt2_01 > 0.f && dt2_02 > 0.f) + return false; + + // b) coplanar -> all dets are 0 + if (det_t2_t1[0] == det_t2_t1[1] && det_t2_t1[1] == det_t2_t1[2] && det_t2_t1[2] == 0.f) + { + auto n = normal_of(t1); + // find appropriate projection plane + auto proj_plane = dot(n, tg::dir<3, ScalarT>(0.f, 1.f, 0.f)) == 0.f ? tg::plane<3, ScalarT>({0.f, 0.f, 1.f}, tg::pos<3, ScalarT>::zero) + : tg::plane<3, ScalarT>({0.f, 1.f, 0.f}, tg::pos<3, ScalarT>::zero); + // project triangles onto plane + auto t1_2D = proj_plane.normal.z == 0 + ? tg::triangle<2, ScalarT>(xz(project(t1.pos0, proj_plane)), xz(project(t1.pos1, proj_plane)), xz(project(t1.pos2, proj_plane))) + : tg::triangle<2, ScalarT>(xy(project(t1.pos0, proj_plane)), xy(project(t1.pos1, proj_plane)), xy(project(t1.pos2, proj_plane))); + auto t2_2D = proj_plane.normal.z == 0 + ? tg::triangle<2, ScalarT>(xz(project(t2.pos0, proj_plane)), xz(project(t2.pos1, proj_plane)), xz(project(t2.pos2, proj_plane))) + : tg::triangle<2, ScalarT>(xy(project(t2.pos0, proj_plane)), xy(project(t2.pos1, proj_plane)), xy(project(t2.pos2, proj_plane))); + + return intersects(t1_2D, t2_2D); + } + + auto det_t1_t2 = tg::comp3(determin(t1.pos0, t1.pos1, t1.pos2, t2.pos0), determin(t1.pos0, t1.pos1, t1.pos2, t2.pos1), + determin(t1.pos0, t1.pos1, t1.pos2, t2.pos2)); + + auto const dt1_01 = det_t1_t2[0] * det_t1_t2[1]; + auto const dt1_02 = det_t1_t2[0] * det_t1_t2[2]; + + // a) no insec of t2 with plane of t1 + if (dt1_01 > 0.f && dt1_02 > 0.f) + return false; + + tg::triangle<3, ScalarT> ta = t1; + tg::triangle<3, ScalarT> tb = t2; + + // circular permutation + detail::rotate_devillers_triangle(ta, tb, det_t2_t1, det_t1_t2); + detail::rotate_devillers_triangle(tb, ta, det_t1_t2, det_t2_t1); + + // decision tree + if (determin(ta.pos0, ta.pos1, tb.pos0, tb.pos1) > 0) + return false; + + if (determin(ta.pos0, ta.pos2, tb.pos2, tb.pos0) > 0) + return false; + + return true; +} + +template +[[nodiscard]] constexpr optional> intersection(triangle<3, ScalarT> const& t1, triangle<3, ScalarT> const& t2) +{ + auto const determin = [](tg::pos<3, ScalarT> pa, tg::pos<3, ScalarT> pb, tg::pos<3, ScalarT> pc, tg::pos<3, ScalarT> pd) -> float + { + auto m = tg::mat<3, 3, ScalarT>::from_data_colwise( + {pa.x - pd.x, pb.x - pd.x, pc.x - pd.x, pa.y - pd.y, pb.y - pd.y, pc.y - pd.y, pa.z - pd.z, pb.z - pd.z, pc.z - pd.z}); + return tg::determinant(m); + }; + + auto det_t2_t1 = tg::comp3(determin(t2.pos0, t2.pos1, t2.pos2, t1.pos0), determin(t2.pos0, t2.pos1, t2.pos2, t1.pos1), + determin(t2.pos0, t2.pos1, t2.pos2, t1.pos2)); + + auto const dt2_01 = det_t2_t1[0] * det_t2_t1[1]; + auto const dt2_02 = det_t2_t1[0] * det_t2_t1[2]; + + // a) no insec of t1 with plane of t2 + if (dt2_01 > 0.f && dt2_02 > 0.f) + return {}; + + // b) coplanar -> case not yet handled (arbitrary polygonal return type required) + if (det_t2_t1[0] == det_t2_t1[1] && det_t2_t1[1] == det_t2_t1[2] && det_t2_t1[2] == 0.f) + return {}; + + auto det_t1_t2 = tg::comp3(determin(t1.pos0, t1.pos1, t1.pos2, t2.pos0), determin(t1.pos0, t1.pos1, t1.pos2, t2.pos1), + determin(t1.pos0, t1.pos1, t1.pos2, t2.pos2)); + + auto const dt1_01 = det_t1_t2[0] * det_t1_t2[1]; + auto const dt1_02 = det_t1_t2[0] * det_t1_t2[2]; + + // a) no insec of t2 with plane of t1 + if (dt1_01 > 0.f && dt1_02 > 0.f) + return {}; + + tg::triangle<3, ScalarT> ta = t1; + tg::triangle<3, ScalarT> tb = t2; + + // circular permutation for triangle orientation + detail::rotate_devillers_triangle(ta, tb, det_t2_t1, det_t1_t2); + detail::rotate_devillers_triangle(tb, ta, det_t1_t2, det_t2_t1); + + // // setup for algorithm of devillers and guigue -> triangle in general position + // if (!tg::devillers_tri(ta, tb)) + // return {}; + + // // check if coplanar -> case not handled (complex return types) + // if (length(cross(normal_of(ta), normal_of(tb))) == 0.f) + // return {}; + + // decision tree + tg::plane<3, ScalarT> p1 = plane_of(ta); + tg::plane<3, ScalarT> p2 = plane_of(tb); + + // intersects tests + if (determin(ta.pos0, ta.pos1, tb.pos0, tb.pos1) > 0) + return {}; + + else if (determin(ta.pos0, ta.pos2, tb.pos2, tb.pos0) > 0) + return {}; + + // decision tree to find intersection segment + else if (determin(ta.pos0, ta.pos2, tb.pos1, tb.pos0) > 0) + { + if (determin(ta.pos0, ta.pos1, tb.pos2, tb.pos0) > 0) + { + return tg::segment<3, ScalarT>{intersection(tg::inf_of(tg::segment<3, ScalarT>{ta.pos0, ta.pos2}), p2).first(), + intersection(tg::inf_of(tg::segment<3, ScalarT>{tb.pos0, tb.pos2}), p1).first()}; + } + + return tg::segment<3, ScalarT>{intersection(tg::inf_of(tg::segment<3, ScalarT>{ta.pos0, ta.pos2}), p2).first(), + intersection(tg::inf_of(tg::segment<3, ScalarT>{tb.pos0, tb.pos1}), p1).first()}; + } + + else if (determin(ta.pos0, ta.pos1, tb.pos2, tb.pos0) > 0) + return tg::segment<3, ScalarT>{intersection(tg::inf_of(tg::segment<3, ScalarT>{tb.pos0, tb.pos1}), p1).first(), + intersection(tg::inf_of(tg::segment<3, ScalarT>{tb.pos0, tb.pos2}), p1).first()}; + + return tg::segment<3, ScalarT>{intersection(tg::inf_of(tg::segment<3, ScalarT>{tb.pos0, tb.pos1}), p1).first(), + intersection(tg::inf_of(tg::segment<3, ScalarT>{ta.pos0, ta.pos1}), p2).first()}; } template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& seg, triangle<3, ScalarT> const& t) +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, triangle<3, ScalarT> const& triangle) { - dir<3, ScalarT> normal_t = normalize(cross((t.pos1 - t.pos0), (t.pos2 - t.pos0))); + dir<3, ScalarT> normal_t = normalize(cross((triangle.pos1 - triangle.pos0), (triangle.pos2 - triangle.pos0))); - plane<3, ScalarT> plane_t = plane<3, ScalarT>(normal_t, t.pos0); + plane<3, ScalarT> plane_t = plane<3, ScalarT>(normal_t, triangle.pos0); // intersection point segment-plane - auto insec = intersection(seg, plane_t); + auto insec = intersection(segment, plane_t); // early out if (!insec.has_value()) return {}; // insec in triangle? - dir<3, ScalarT> a = normalize(cross(t.pos1 - t.pos0, normal_t)); - dir<3, ScalarT> b = normalize(cross(t.pos2 - t.pos1, normal_t)); - dir<3, ScalarT> c = normalize(cross(t.pos0 - t.pos2, normal_t)); - bool b_a = signed_distance(insec.value(), plane<3, ScalarT>(a, t.pos1)) <= 0 ? false : true; - bool b_b = signed_distance(insec.value(), plane<3, ScalarT>(b, t.pos2)) <= 0 ? false : true; - bool b_c = signed_distance(insec.value(), plane<3, ScalarT>(c, t.pos0)) <= 0 ? false : true; + dir<3, ScalarT> a = normalize(cross(triangle.pos1 - triangle.pos0, normal_t)); + dir<3, ScalarT> b = normalize(cross(triangle.pos2 - triangle.pos1, normal_t)); + dir<3, ScalarT> c = normalize(cross(triangle.pos0 - triangle.pos2, normal_t)); + bool b_a = signed_distance(insec.value(), plane<3, ScalarT>(a, triangle.pos1)) <= 0 ? false : true; + bool b_b = signed_distance(insec.value(), plane<3, ScalarT>(b, triangle.pos2)) <= 0 ? false : true; + bool b_c = signed_distance(insec.value(), plane<3, ScalarT>(c, triangle.pos0)) <= 0 ? false : true; if (b_a == b_b && b_b == b_c) return insec; @@ -2498,9 +2832,9 @@ template // TODO: there might be a more effective way template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& s, aabb<3, ScalarT> const& bb) // NOT CONFIRMED +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, aabb<3, ScalarT> const& bb) // NOT CONFIRMED { - line<3, ScalarT> segment_line = {s.pos0, normalize(s.pos1 - s.pos0)}; + line<3, ScalarT> segment_line = {segment.pos0, normalize(segment.pos1 - segment.pos0)}; auto param_insec = intersection_parameter(segment_line, bb); if (!param_insec.has_value()) @@ -2511,29 +2845,29 @@ template auto b = param_insec.value().end; // intersection may exist - if (param_insec.value().start < length(s) && param_insec.value().end < length(s)) + if (param_insec.value().start < length(segment) && param_insec.value().end < length(segment)) { - return segment<3, ScalarT>{segment_line.pos + segment_line.dir * a, segment_line.pos + segment_line.dir * b}; + return tg::segment<3, ScalarT>{segment_line.pos + segment_line.dir * a, segment_line.pos + segment_line.dir * b}; } return {}; } template -[[nodiscard]] constexpr optional> intersection(aabb<3, ScalarT> const& bb, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr optional> intersection(aabb<3, ScalarT> const& bb, segment<3, ScalarT> const& segment) { - return intersection(s, bb); + return intersection(segment, bb); } template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& s, box<3, ScalarT> const& bx) +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, box<3, ScalarT> const& box) { // early-out: Both segment points inside of box - if (contains(bx, s.pos0) && contains(bx, s.pos1)) - return s; + if (contains(box, segment.pos0) && contains(box, segment.pos1)) + return segment; - line<3, ScalarT> segment_line = {s.pos0, normalize(s.pos1 - s.pos0)}; - auto param_insec = intersection_parameter(segment_line, bx); + line<3, ScalarT> segment_line = {segment.pos0, normalize(segment.pos1 - segment.pos0)}; + auto param_insec = intersection_parameter(segment_line, box); if (!param_insec.has_value()) return {}; @@ -2543,16 +2877,16 @@ template auto b = param_insec.value().end; // one point of the segment inside the box - if (contains(bx, s.pos0)) - return segment<3, ScalarT>{s.pos0, segment_line.pos + segment_line.dir * b}; + if (contains(box, segment.pos0)) + return tg::segment<3, ScalarT>{segment.pos0, segment_line.pos + segment_line.dir * b}; - if (contains(bx, s.pos1)) - return segment<3, ScalarT>{segment_line.pos + segment_line.dir * a, s.pos1}; + if (contains(box, segment.pos1)) + return tg::segment<3, ScalarT>{segment_line.pos + segment_line.dir * a, segment.pos1}; // intersection may exist - if (a < length(s) && b < length(s) && a >= 0 && b > 0) + if (a < length(segment) && b < length(segment) && a >= 0 && b > 0) { - return segment<3, ScalarT>{segment_line.pos + segment_line.dir * a, segment_line.pos + segment_line.dir * b}; + return tg::segment<3, ScalarT>{segment_line.pos + segment_line.dir * a, segment_line.pos + segment_line.dir * b}; } return {}; @@ -2560,113 +2894,135 @@ template // segment3 - capsule3 template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& s, capsule<3, ScalarT> const& c) +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, capsule<3, ScalarT> const& capsule) { - return detail::intersection_segment_object_impl(s, c); + return detail::intersection_segment_object_impl(segment, capsule); } template -[[nodiscard]] constexpr optional> intersection(capsule<3, ScalarT> const& c, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr optional> intersection(capsule<3, ScalarT> const& capsule, segment<3, ScalarT> const& segment) { - return detail::intersection_segment_object_impl(s, c); + return detail::intersection_segment_object_impl(segment, capsule); } // segment3 - cylinder3 template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& s, cylinder<3, ScalarT> const& c) +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, cylinder<3, ScalarT> const& cylinder) { - return detail::intersection_segment_object_impl(s, c); + return detail::intersection_segment_object_impl(segment, cylinder); } template -[[nodiscard]] constexpr optional> intersection(cylinder<3, ScalarT> const& c, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr optional> intersection(cylinder<3, ScalarT> const& cylinder, segment<3, ScalarT> const& segment) { - return detail::intersection_segment_object_impl(s, c); + return detail::intersection_segment_object_impl(segment, cylinder); } // segment3 - ellipse3 template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& s, ellipse<3, ScalarT> const& e) +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, ellipse<3, ScalarT> const& ellipse) { - return detail::intersection_segment_object_impl(s, e); + return detail::intersection_segment_object_impl(segment, ellipse); } template -[[nodiscard]] constexpr optional> intersection(ellipse<3, ScalarT> const& e, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr optional> intersection(ellipse<3, ScalarT> const& ellipse, segment<3, ScalarT> const& segment) { - return detail::intersection_segment_object_impl(s, e); + return detail::intersection_segment_object_impl(segment, ellipse); } // segment3 - sphere3 template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& s, sphere<3, ScalarT> const& e) +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, sphere<3, ScalarT> const& sphere) { - return detail::intersection_segment_object_impl(s, e); + return detail::intersection_segment_object_impl(segment, sphere); } template -[[nodiscard]] constexpr optional> intersection(sphere<3, ScalarT> const& e, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr optional> intersection(sphere<3, ScalarT> const& sphere, segment<3, ScalarT> const& segment) { - return detail::intersection_segment_object_impl(s, e); + return detail::intersection_segment_object_impl(segment, sphere); } -// segment3 - tube3 +// segment3 - cone3 template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& s, tube<3, ScalarT> const& t) +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, cone<3, ScalarT> const& cone) { - return detail::intersection_segment_object_impl(s, t); + return detail::intersection_segment_object_impl(segment, cone); } template -[[nodiscard]] constexpr optional> intersection(tube<3, ScalarT> const& t, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr optional> intersection(cone<3, ScalarT> const& cone, segment<3, ScalarT> const& segment) { - return detail::intersection_segment_object_impl(s, t); + return detail::intersection_segment_object_impl(segment, cone); } -// segment3 - cone3 +// segment3 - tube3 template -[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& s, cone<3, ScalarT> const& c) +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection(segment<3, ScalarT> const& segment, tube<3, ScalarT> const& tube) { - return detail::intersection_segment_object_impl(s, c); + return detail::intersection_segment_boundary_impl(segment, tube); } template -[[nodiscard]] constexpr optional> intersection(cone<3, ScalarT> const& c, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection(tube<3, ScalarT> const& tube, segment<3, ScalarT> const& segment) { - return detail::intersection_segment_object_impl(s, c); + return detail::intersection_segment_boundary_impl(segment, tube); } // segment3 - cylinder_boundary template -[[nodiscard]] constexpr hits<2, tg::pos<3, ScalarT>> intersection(segment<3, ScalarT> const& s, cylinder_boundary<3, ScalarT> const& c) +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection(segment<3, ScalarT> const& segment, cylinder_boundary<3, ScalarT> const& cylinder) { - // TODO: This is a standard solution that can be applied to any boundary case - auto const line = line3::from_points(s.pos0, s.pos1); - auto const params = intersection_parameter(line, c); + return detail::intersection_segment_boundary_impl(segment, cylinder); +} - if (!params.any()) - return {}; +template +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection(cylinder_boundary<3, ScalarT> const& cylinder, segment<3, ScalarT> const& segment) +{ + return detail::intersection_segment_boundary_impl(segment, cylinder); +} - auto const dist = distance(s.pos0, s.pos1); - auto n_hits = 0; - tg::pos<3, ScalarT> ps[2]; - for (auto i = 0; i < params.size(); ++i) - { - auto const t = params[i]; - if (ScalarT(0) <= t && t <= dist) - { - ps[n_hits++] = line[t]; - } - } - return hits<2, tg::pos<3, ScalarT>>(ps, n_hits); +// segment3 - box_boundary3 +template +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection(segment<3, ScalarT> const& segment, box_boundary<3, ScalarT> const& box) +{ + return detail::intersection_segment_boundary_impl(segment, box); +} + +template +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection(box_boundary<3, ScalarT> const& box, segment<3, ScalarT> const& segment) +{ + return detail::intersection_segment_boundary_impl(segment, box); +} + +// segment3 - capsule_boundary3 +template +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection(segment<3, ScalarT> const& segment, capsule_boundary<3, ScalarT> const& capsule) +{ + return detail::intersection_segment_boundary_impl(segment, capsule); +} + +template +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersection(capsule_boundary<3, ScalarT> const& capsule, segment<3, ScalarT> const& segment) +{ + return detail::intersection_segment_boundary_impl(segment, capsule); } +// segment3 - cone_boundary3 template -[[nodiscard]] constexpr hits<2, tg::pos<3, ScalarT>> intersection(cylinder_boundary<3, ScalarT> const& c, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersecion(segment<3, ScalarT> const& segment, cone_boundary<3, ScalarT> const& cone) { - return intersection(s, c); + return detail::intersection_segment_boundary_impl(segment, cone); } +template +[[nodiscard]] constexpr hits<2, pos<3, ScalarT>> intersecion(cone_boundary<3, ScalarT> const& cone, segment<3, ScalarT> const& segment) +{ + return detail::intersection_segment_boundary_impl(segment, cone); +} + + template [[nodiscard]] constexpr bool intersects(box<3, ScalarT> const& a, box<3, ScalarT> const& b) { @@ -2790,31 +3146,31 @@ template } template -[[nodiscard]] constexpr bool intersects(sphere<3, ScalarT> const& a, sphere<3, ScalarT> const& b) +[[nodiscard]] constexpr bool intersects(sphere<3, ScalarT> const& s0, sphere<3, ScalarT> const& s1) { - return distance(a.center, b.center) <= (a.radius + b.radius); + return distance(s0.center, s1.center) <= (s0.radius + s1.radius); } template -[[nodiscard]] constexpr bool intersects(box<3, ScalarT> const& b, sphere<3, ScalarT> const& s) +[[nodiscard]] constexpr bool intersects(box<3, ScalarT> const& box, sphere<3, ScalarT> const& sphere) { // early-out: sphere-center inside box - if (contains(b, s.center)) + if (contains(box, sphere.center)) return true; - array, 8> box_vertices = vertices_of(b); + array, 8> box_vertices = vertices_of(box); // box vertex inside the sphere for (auto const& v : box_vertices) { - if (length_sqr(v - s.center) < pow2(s.radius)) + if (length_sqr(v - sphere.center) < pow2(sphere.radius)) return true; } - array, 12> box_edges = edges_of(b); + array, 12> box_edges = edges_of(box); // box edge intersects sphere for (auto const& e : box_edges) { - if (intersects(e, s)) + if (intersects(e, sphere)) return true; } @@ -2822,24 +3178,24 @@ template } template -[[nodiscard]] constexpr bool intersects(sphere<3, ScalarT> const& s, box<3, ScalarT> const& b) +[[nodiscard]] constexpr bool intersects(sphere<3, ScalarT> const& sphere, box<3, ScalarT> const& box) { - return intersects(b, s); + return intersects(box, sphere); } template -[[nodiscard]] constexpr bool intersects(plane<3, ScalarT> const& a, sphere<3, ScalarT> const& b) +[[nodiscard]] constexpr bool intersects(plane<3, ScalarT> const& plane, sphere<3, ScalarT> const& sphere) { - if (distance(a, b.center) <= b.radius) + if (distance(plane, sphere.center) <= sphere.radius) return true; return false; } template -[[nodiscard]] constexpr bool intersects(sphere<3, ScalarT> const& a, plane<3, ScalarT> const& b) +[[nodiscard]] constexpr bool intersects(sphere<3, ScalarT> const& sphere, plane<3, ScalarT> const& plane) { - return intersects(b, a); + return intersects(plane, sphere); } // box3 -plane3 @@ -2885,7 +3241,7 @@ template return true; // intersection of box with triangle-plane - tg::plane<3, ScalarT> plane_of_triangle = tg::plane_of(triangle); + plane<3, ScalarT> plane_of_triangle = plane_of(triangle); if (!intersects(plane_of_triangle, box)) return false; @@ -2919,7 +3275,7 @@ template template [[nodiscard]] constexpr bool intersects(box<2, ScalarT> const& box, sphere<2, ScalarT> const& sphere) { - array, 4> vertices_box = vertices_of(box); + // array, 4> vertices_box = vertices_of(box); array, 4> edges_box = edges_of(box); if (contains(box, sphere.center)) @@ -2941,28 +3297,28 @@ template } template -[[nodiscard]] constexpr bool intersects(sphere const& sphere, halfspace const& hs) +[[nodiscard]] constexpr bool intersects(sphere const& sphere, halfspace const& halfspace) { - if (dot(hs.normal, sphere.center) - hs.dis <= sphere.radius) + if (dot(halfspace.normal, sphere.center) - halfspace.dis <= sphere.radius) return true; return false; } template -[[nodiscard]] constexpr bool intersects(halfspace<3, ScalarT> const& hs, sphere<3, ScalarT> const& sphere) +[[nodiscard]] constexpr bool intersects(halfspace<3, ScalarT> const& halfspace, sphere<3, ScalarT> const& sphere) { - return intersects(sphere, hs); + return intersects(sphere, halfspace); } // TODO: optimized version template -[[nodiscard]] constexpr bool intersects(box<3, ScalarT> const& box, halfspace<3, ScalarT> const& hs) +[[nodiscard]] constexpr bool intersects(box<3, ScalarT> const& box, halfspace<3, ScalarT> const& halfspace) { array, 8> vertices_box = vertices_of(box); for (auto const& v : vertices_box) { - if (dot(hs.normal, v) - hs.dis <= 0) + if (dot(halfspace.normal, v) - halfspace.dis <= 0) return true; } @@ -2970,47 +3326,47 @@ template } template -[[nodiscard]] constexpr bool intersects(halfspace<3, ScalarT> const& hs, box<3, ScalarT> const& box) +[[nodiscard]] constexpr bool intersects(halfspace<3, ScalarT> const& halfspace, box<3, ScalarT> const& box) { - return intersects(box, hs); + return intersects(box, halfspace); } // segment3 - halfspace3 template -[[nodiscard]] constexpr bool intersects(segment<3, ScalarT> const& s, halfspace<3, ScalarT> const& hs) +[[nodiscard]] constexpr bool intersects(segment<3, ScalarT> const& segment, halfspace<3, ScalarT> const& halfspace) { - if ((dot(hs.normal, s.pos0) - hs.dis) <= 0 || (dot(hs.normal, s.pos1) - hs.dis) <= 0) + if ((dot(halfspace.normal, segment.pos0) - halfspace.dis) <= 0 || (dot(halfspace.normal, segment.pos1) - halfspace.dis) <= 0) return true; return false; } template -[[nodiscard]] constexpr bool intersects(halfspace<3, ScalarT> const& hs, segment<3, ScalarT> const& s) +[[nodiscard]] constexpr bool intersects(halfspace<3, ScalarT> const& halfspace, segment<3, ScalarT> const& segment) { - return intersects(s, hs); + return intersects(segment, halfspace); } // triangle3 - sphere3 template -[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& t, sphere<3, ScalarT> const& s) +[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& triangle, sphere<3, ScalarT> const& sphere) { // triangle vertex inside sphere - if (contains(s, t.pos0) || contains(s, t.pos1) || contains(s, t.pos2)) + if (contains(sphere, triangle.pos0) || contains(sphere, triangle.pos1) || contains(sphere, triangle.pos2)) return true; - plane<3, ScalarT> plane_t = plane_of(t); + plane<3, ScalarT> plane_t = plane_of(triangle); // check if the closest point on triangle to sphere center is inside the sphere - auto cp = closest_points(s.center, t); + auto cp = closest_points(sphere.center, triangle); - if (contains(s, cp.first) && contains(s, cp.second)) + if (contains(sphere, cp.first) && contains(sphere, cp.second)) return true; // triangle edge intersects sphere - for (auto const& e : edges_of(t)) + for (auto const& e : edges_of(triangle)) { - if (intersects(e, s)) + if (intersects(e, sphere)) return true; } @@ -3018,30 +3374,30 @@ template } template -[[nodiscard]] constexpr bool intersects(sphere<3, ScalarT> const& s, triangle<3, ScalarT> const& t) +[[nodiscard]] constexpr bool intersects(sphere<3, ScalarT> const& sphere, triangle<3, ScalarT> const& triangle) { - return intersects(t, s); + return intersects(triangle, sphere); } -// sphere2in3 - plane3 +// disk3 - plane3 template -[[nodiscard]] constexpr bool intersects(sphere<2, ScalarT, 3> const& s, plane<3, ScalarT> const& p) +[[nodiscard]] constexpr bool intersects(disk<3, ScalarT> const& disk, plane<3, ScalarT> const& plane) { - auto plane_s = tg::plane<3, ScalarT>(s.normal, s.center); + auto plane_s = tg::plane<3, ScalarT>(disk.normal, disk.center); // sphere center on plane - if (contains(p, s.center)) + if (contains(plane, disk.center)) return true; // no intersection if planes are parallel - if ((plane_s.normal == p.normal || plane_s.normal == -p.normal) && !contains(p, s.center)) + if ((plane_s.normal == plane.normal || plane_s.normal == -plane.normal) && !contains(plane, disk.center)) return false; // line intersection of two planes - auto insec = intersection(plane_s, p); + auto insec = intersection(plane_s, plane); // if distance of plane intersection is inside the sphere, intersection exists - if (distance_sqr(insec, s.center) <= pow2(s.radius)) + if (distance_sqr(insec, disk.center) <= pow2(disk.radius)) return true; return false; @@ -3049,21 +3405,21 @@ template template -[[nodiscard]] constexpr bool intersects(plane<3, ScalarT> const& p, sphere<2, ScalarT, 3> const& s) +[[nodiscard]] constexpr bool intersects(plane<3, ScalarT> const& plane, disk<3, ScalarT> const& disk) { - return intersects(s, p); + return intersects(disk, plane); } // plane3 - cone3 template -[[nodiscard]] constexpr bool intersects(plane<3, ScalarT> const& p, cone<3, ScalarT> const& c) +[[nodiscard]] constexpr bool intersects(plane<3, ScalarT> const& plane, cone<3, ScalarT> const& cone) { // cone base intersects the plane - if (intersects(c.base, p)) + if (intersects(cone.base, plane)) return true; - auto d_cone_tip = (dot(p.normal, apex_of(c)) - p.dis) >= 0; - auto d_cone_base = (dot(p.normal, c.base.center) - p.dis) >= 0; + auto d_cone_tip = (dot(plane.normal, apex_of(cone)) - plane.dis) >= 0; + auto d_cone_base = (dot(plane.normal, cone.base.center) - plane.dis) >= 0; // base and tip of the cone are on different sides of the plane if (d_cone_tip != d_cone_base) @@ -3073,45 +3429,46 @@ template } template -[[nodiscard]] constexpr bool intersects(cone<3, ScalarT> const& c, plane<3, ScalarT> const& p) +[[nodiscard]] constexpr bool intersects(cone<3, ScalarT> const& cone, plane<3, ScalarT> const& plane) { - return intersects(p, c); + return intersects(plane, cone); } // triangle3 - halfspace3 template -[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& t, halfspace<3, ScalarT> const hs) +[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& triangle, halfspace<3, ScalarT> const halfspace) { - if ((dot(hs.normal, t.pos0) - hs.dis <= 0) || (dot(hs.normal, t.pos1) - hs.dis <= 0) || (dot(hs.normal, t.pos2) - hs.dis <= 0)) + if ((dot(halfspace.normal, triangle.pos0) - halfspace.dis <= 0) || (dot(halfspace.normal, triangle.pos1) - halfspace.dis <= 0) + || (dot(halfspace.normal, triangle.pos2) - halfspace.dis <= 0)) return true; return false; } template -[[nodiscard]] constexpr bool intersects(halfspace<3, ScalarT> const hs, triangle<3, ScalarT> const& t) +[[nodiscard]] constexpr bool intersects(halfspace<3, ScalarT> const halfspace, triangle<3, ScalarT> const& triangle) { - return intersects(t, hs); + return intersects(triangle, halfspace); } -// sphere2in3 - triangle3 +// disk3 - triangle3 template -[[nodiscard]] constexpr bool intersects(sphere<2, ScalarT, 3> const& s, triangle<3, ScalarT> const& t) +[[nodiscard]] constexpr bool intersects(disk<3, ScalarT> const& disk, triangle<3, ScalarT> const& triangle) { // circle inside triangle or triangle vertex inside the circle - if (contains(s, centroid_of(t)) || contains(s, t.pos0) || contains(s, t.pos1) || contains(s, t.pos2)) + if (contains(disk, centroid_of(triangle)) || contains(disk, triangle.pos0) || contains(disk, triangle.pos1) || contains(disk, triangle.pos2)) return true; // area of triangle intersects with circle - auto cp = closest_points(s.center, t); + auto cp = closest_points(disk.center, triangle); - if (contains(s, cp.first) && contains(s, cp.second)) + if (contains(disk, cp.first) && contains(disk, cp.second)) return true; // triangle edge intersects with circle - for (auto const& e : edges_of(t)) + for (auto const& e : edges_of(triangle)) { - if (intersects(e, s)) + if (intersects(e, disk)) return true; } @@ -3119,29 +3476,29 @@ template } template -[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& t, sphere<2, ScalarT, 3> const& s) +[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& triangle, disk<3, ScalarT> const& disk) { - return intersects(s, t); + return intersects(disk, triangle); } // cone3 - triangle3 template -[[nodiscard]] constexpr bool intersects(cone<3, ScalarT> const& c, triangle<3, ScalarT> const& t) +[[nodiscard]] constexpr bool intersects(cone<3, ScalarT> const& cone, triangle<3, ScalarT> const& triangle) { - auto mid_axis = tg::segment<3, ScalarT>(c.base.center, apex_of(c)); + auto mid_axis = tg::segment<3, ScalarT>(cone.base.center, apex_of(cone)); // area of the triangle intersects with cone - if (intersects(mid_axis, t)) + if (intersects(mid_axis, triangle)) return true; // triangle intersects with the cone base - if (intersects(c.base, t)) + if (intersects(cone.base, triangle)) return true; // at least one segment of triangle intersects with cone - for (auto const& e : edges_of(t)) + for (auto const& e : edges_of(triangle)) { - if (intersects(e, c)) + if (intersects(e, cone)) return true; } @@ -3149,10 +3506,197 @@ template } template -[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& t, cone<3, ScalarT> const& c) +[[nodiscard]] constexpr bool intersects(triangle<3, ScalarT> const& triangle, cone<3, ScalarT> const& cone) +{ + return intersects(cone, triangle); +} + +// segment3 - halfspace3 +template +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, halfspace<3, ScalarT> const& halfspace) +{ + bool cont_pos0 = contains(halfspace, segment.pos0); + bool cont_pos1 = contains(halfspace, segment.pos1); + + // both segment points inside the halfspace + if (cont_pos0 && cont_pos1) + return segment; + + // check if there is an intersection with the plane of the halfspace + auto insec = intersection(segment, plane_of(halfspace)); + + if (!insec.has_value()) + return {}; + + if (cont_pos0) + return tg::segment<3, ScalarT>{insec.value(), segment.pos0}; + + if (cont_pos1) + return tg::segment<3, ScalarT>{insec.value(), segment.pos1}; + + return {}; +} + +template +[[nodiscard]] constexpr optional> intersection(halfspace<3, ScalarT> const& halfspace, segment<3, ScalarT> const& segment) +{ + return intersection(segment, halfspace); +} + +// segment3 - disk3 +template +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, disk<3, ScalarT> const& disk) { - return intersects(c, t); + auto plane = tg::plane3(disk.normal, disk.center); + // check if both seg points lie on same side of the circle + if (!intersects(segment, plane)) + return {}; + + // seg points on different sides of the circle + auto insec = intersection(segment, plane); + if (!insec.has_value()) + return {}; + + if (distance_sqr(insec.value(), disk.center) <= pow2(disk.radius)) + return insec.value(); + + return {}; +} + +template +[[nodiscard]] constexpr optional> intersection(disk<3, ScalarT> const& disk, segment<3, ScalarT> const& segment) +{ + return intersection(segment, disk); +} + +// segment3 - hemisphere3 +template +[[nodiscard]] constexpr optional> intersection(segment<3, ScalarT> const& segment, hemisphere<3, ScalarT> const& hemisphere) +{ + // early-out: both seg points inside hemisphere + if (contains(hemisphere, segment.pos0) && contains(hemisphere, segment.pos1)) + return segment; + + // sphere extension of hemisphere + auto sp = sphere<3, ScalarT>(hemisphere.center, hemisphere.radius); + + // intersection with sphere extension + auto insec_sp = intersection(segment, sp); + + if (!insec_sp.has_value()) + return {}; + + // halfspace spanned by hemisphere base containing the hemisphere + auto halfspace_hs = tg::halfspace<3, ScalarT>(-hemisphere.normal, hemisphere.center); + + // intersection with halfspace + auto insec_hs = intersection(insec_sp.value(), halfspace_hs); + + if (insec_hs.has_value()) + return insec_hs.value(); + + else + return {}; +} + +template +[[nodiscard]] constexpr optional> intersection(hemisphere<3, ScalarT> const& halfspace, segment<3, ScalarT> const& segment) +{ + return intersection(segment, halfspace); +} + +// sphere3 - plane3 +template +[[nodiscard]] constexpr optional> intersection(sphere<3, ScalarT> const& sphere, plane<3, ScalarT> const& plane) +{ + if (!intersects(sphere, plane)) + return {}; + + // project sphere center onto plane + tg::pos<3, ScalarT> disk_center = project(sphere.center, plane); + + // pythagoras + auto rad = sqrt(sphere.radius * sphere.radius - distance_sqr(sphere.center, disk_center)); + + return tg::disk<3, ScalarT>(disk_center, rad, plane.normal); } +template +[[nodiscard]] constexpr optional> intersection(plane<3, ScalarT> const& plane, sphere<3, ScalarT> const& sphere) +{ + return intersection(sphere, plane); +} + +// plane3 - inf_cylinder3 +template +[[nodiscard]] constexpr optional> intersection(plane<3, ScalarT> const& plane, inf_cylinder<3, ScalarT> const& cylinder) +{ + // early-out: plane normal and cylinder axis are orthogonal + if (dot(plane.normal, cylinder.axis.dir) == 0) + return {}; + + // ellipse mid_point = intersection cylinder axis and plane + auto insec_mid_axis = tg::intersection(cylinder.axis, plane); + auto mid_point = insec_mid_axis.first(); + + // find semi axes + // vector orthogonal to cylinder axis and plane normal + vec<3, ScalarT> orth_vec1 = cross(cylinder.axis.dir, plane.normal); + + if (length(orth_vec1) == 0) + { + // return will be a disk (i.e. ellipse with identical sized semi axes) + auto semi_vec1 = any_normal(cylinder.axis.dir) * cylinder.radius; + auto semi_vec2 = cylinder.radius * normalize(cross(semi_vec1, cylinder.axis.dir)); + auto semi_axes = mat<2, 3, ScalarT>::from_cols(semi_vec1, semi_vec2); + + return ellipse<2, ScalarT, 3>(mid_point, semi_axes); + } + + // first semi-axis + vec<3, ScalarT> semi_vec1 = cylinder.radius * normalize(orth_vec1); + + // vector orthogonal to plane_normal and orthogonal to orth_vec1 + vec<3, ScalarT> orth_vec2 = cross(orth_vec1, plane.normal); + + // second semi-axis + vec<3, ScalarT> semi_vec2 = cylinder.radius / (tg::sin(tg::angle_between(cylinder.axis.dir, plane.normal))) * normalize(orth_vec2); + + auto semi_axes = mat<2, 3, ScalarT>::from_cols(semi_vec1, semi_vec2); + + return ellipse<2, ScalarT, 3>(mid_point, semi_axes); +} +template +[[nodiscard]] constexpr optional> intersection(inf_cylinder<3, ScalarT> const& t, plane<3, ScalarT> const& p) +{ + return intersection(p, t); +} + +// disk3 - plane3 +template +[[nodiscard]] constexpr optional> intersection(disk<3, ScalarT> const& disk, plane<3, ScalarT> const& plane) +{ + if (length(cross(disk.normal, plane.normal)) == 0) + return {}; + + auto disk_plane = plane_of(disk); + + // intersection of circle_plane and plane results in line parallel to intersection of disk and plane + line<3, ScalarT> insec_line = intersection(plane, disk_plane); + auto sphere_disk = tg::sphere<3, ScalarT>(disk.center, disk.radius); + + auto insec_sphere = intersection(insec_line, sphere_disk); + + if (!insec_sphere.has_value()) + return {}; + + return insec_sphere.value(); +} + +template +[[nodiscard]] constexpr optional> intersection(plane<3, ScalarT> const& plane, disk<3, ScalarT> const& disk) +{ + return intersection(disk, plane); +} } // namespace tg diff --git a/src/typed-geometry/functions/std/io.hh b/src/typed-geometry/functions/std/io.hh index 666128d3..ddfecbe6 100644 --- a/src/typed-geometry/functions/std/io.hh +++ b/src/typed-geometry/functions/std/io.hh @@ -9,7 +9,7 @@ namespace tg { -template +template ())>> std::string to_string(T const& v) { std::ostringstream ss; diff --git a/src/typed-geometry/types/color.hh b/src/typed-geometry/types/color.hh index edd9541e..cc1a97d7 100644 --- a/src/typed-geometry/types/color.hh +++ b/src/typed-geometry/types/color.hh @@ -1,6 +1,9 @@ #pragma once +#include + #include +#include "../detail/color_traits.hh" #include "../detail/comp_traits.hh" #include "../detail/macros.hh" #include "../detail/scalar_traits.hh" @@ -12,12 +15,11 @@ namespace tg template struct color; -// Common color types -// TODO: premultiplied alpha vs postmultiplied alpha? -// TODO: float vs u8 colors? -// TODO: sRGB handling +// see feature/colors.hh for rationale / more doc +/// linear color in RGB using float components using color3 = color<3, f32>; +/// linear color in RGB with straight alpha a using float components using color4 = color<4, f32>; // ======== IMPLEMENTATION ======== @@ -29,16 +31,19 @@ struct color<3, ScalarT> ScalarT g = static_cast(0); ScalarT b = static_cast(0); - static const color black; - static const color white; - static const color red; - static const color green; - static const color blue; - static const color cyan; - static const color magenta; - static const color yellow; + static const color black; ///< a solid black (0,0,0) color (NOTE: this is a static member, NOT a property) + static const color white; ///< a solid white (1,1,1) color (NOTE: this is a static member, NOT a property) + static const color red; ///< a solid red (1,0,0) color (NOTE: this is a static member, NOT a property) + static const color green; ///< a solid green (0,1,0) color (NOTE: this is a static member, NOT a property) + static const color blue; ///< a solid blue (0,0,1) color (NOTE: this is a static member, NOT a property) + static const color cyan; ///< a solid cyan (0,1,1) color (NOTE: this is a static member, NOT a property) + static const color magenta; ///< a solid magenta (1,0,1) color (NOTE: this is a static member, NOT a property) + static const color yellow; ///< a solid yellow (1,1,0) color (NOTE: this is a static member, NOT a property) TG_DECLARE_COMP_TYPE_3(color); + + // NOTE: requires feature/color included + constexpr static color from_hex_string(cc::string_view s); }; template @@ -66,20 +71,23 @@ struct color<4, ScalarT> ScalarT b = static_cast(0); ScalarT a = static_cast(1); - static const color black; - static const color white; - static const color red; - static const color green; - static const color blue; - static const color cyan; - static const color magenta; - static const color yellow; - static const color transparent; + static const color black; ///< a solid opaque black (0,0,0,1) color (NOTE: this is a static member, NOT a property) + static const color white; ///< a solid opaque white (1,1,1,1) color (NOTE: this is a static member, NOT a property) + static const color red; ///< a solid opaque red (1,0,0,1) color (NOTE: this is a static member, NOT a property) + static const color green; ///< a solid opaque green (0,1,0,1) color (NOTE: this is a static member, NOT a property) + static const color blue; ///< a solid opaque blue (0,0,1,1) color (NOTE: this is a static member, NOT a property) + static const color cyan; ///< a solid opaque cyan (0,1,1,1) color (NOTE: this is a static member, NOT a property) + static const color magenta; ///< a solid opaque magenta (1,0,1,1) color (NOTE: this is a static member, NOT a property) + static const color yellow; ///< a solid opaque yellow (1,1,0,1) color (NOTE: this is a static member, NOT a property) + static const color transparent; ///< a fully transparent black (0,0,0,0) (NOTE: this is a static member, NOT a property) TG_DECLARE_COMP_TYPE_4(color); constexpr color(ScalarT r, ScalarT g, ScalarT b) : r(r), g(g), b(b) {} constexpr color(color<3, ScalarT> const& rgb, ScalarT a = ScalarT(1)) : r(rgb.r), g(rgb.g), b(rgb.b), a(a) {} + + // NOTE: requires feature/color included + constexpr static color from_hex_string(cc::string_view s); }; template @@ -111,4 +119,14 @@ TG_IMPL_COMP_DEDUCTION_GUIDES(color); // reflection TG_IMPL_COMP_INTROSPECT(color); +// traits +template +struct color_traits> : detail::base_color_traits<3, T, alpha_type::none> +{ +}; +template +struct color_traits> : detail::base_color_traits<4, T, alpha_type::straight> +{ +}; + } // namespace tg diff --git a/src/typed-geometry/types/hsl.hh b/src/typed-geometry/types/hsl.hh new file mode 100644 index 00000000..9d5202f7 --- /dev/null +++ b/src/typed-geometry/types/hsl.hh @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include + +namespace tg +{ +template +struct hsl_t; +template +struct hsla_t; + +// see feature/colors.hh for rationale / more doc + +/// color in hsl, float per comp +using hsl = hsl_t; +/// color in hsl with straight alpha, float per comp +using hsla = hsla_t; + +// ======== IMPLEMENTATION ======== + +template +struct hsl_t +{ + ScalarT h = static_cast(0); // [0, 1] NOT [0, 6] or [0, 360] + ScalarT s = static_cast(0); // [0, 1] + ScalarT l = static_cast(0); // [0, 1] + + hsl_t() = default; + hsl_t(ScalarT h, ScalarT s, ScalarT l) : h(h), s(s), l(l) {} + + constexpr bool operator==(hsl_t const& v) const { return h == v.h && l == v.l && s == v.s; } + constexpr bool operator!=(hsl_t const& v) const { return h != v.h || l != v.l || s != v.s; } +}; + +template +struct hsla_t +{ + ScalarT h = static_cast(0); // [0, 1] NOT [0, 6] or [0, 360] + ScalarT s = static_cast(0); // [0, 1] + ScalarT l = static_cast(0); // [0, 1] + ScalarT a = color_scalar_from_float(1); + + hsla_t() = default; + hsla_t(ScalarT h, ScalarT s, ScalarT l, ScalarT a = color_scalar_from_float(1)) : h(h), s(s), l(l), a(a) {} + + constexpr bool operator==(hsla_t const& v) const { return h == v.h && l == v.l && s == v.s && a == v.a; } + constexpr bool operator!=(hsla_t const& v) const { return h != v.h || l != v.l || s != v.s || a != v.a; } +}; + +// reflection +template +constexpr void introspect(I&& i, hsl_t& v) +{ + i(v.h, "h"); + i(v.s, "s"); + i(v.l, "l"); +} +template +constexpr void introspect(I&& i, hsla_t& v) +{ + i(v.h, "h"); + i(v.s, "s"); + i(v.l, "l"); + i(v.a, "a"); +} + +// traits +template +struct color_traits> : detail::base_color_traits<3, T, alpha_type::none> +{ +}; +template +struct color_traits> : detail::base_color_traits<4, T, alpha_type::straight> +{ +}; +} diff --git a/src/typed-geometry/types/srgb.hh b/src/typed-geometry/types/srgb.hh new file mode 100644 index 00000000..350780d1 --- /dev/null +++ b/src/typed-geometry/types/srgb.hh @@ -0,0 +1,143 @@ +#pragma once + +#include + +#include +#include + +namespace tg +{ +template +struct srgb; +template +struct srgba; + +// see feature/colors.hh for rationale / more doc + +/// color in sRGB (gamme-compressed, 8 bit per channel) +using srgb8 = srgb; +/// color in sRGB (gamme-compressed, 8 bit per channel) with straight alpha +using srgba8 = srgba; + +// ======== IMPLEMENTATION ======== + +template +struct srgb +{ + ScalarT r = static_cast(0); + ScalarT g = static_cast(0); + ScalarT b = static_cast(0); + + static const srgb black; ///< a solid black (0,0,0) color (NOTE: this is a static member, NOT a property) + static const srgb white; ///< a solid white (1,1,1) color (NOTE: this is a static member, NOT a property) + static const srgb red; ///< a solid red (1,0,0) color (NOTE: this is a static member, NOT a property) + static const srgb green; ///< a solid green (0,1,0) color (NOTE: this is a static member, NOT a property) + static const srgb blue; ///< a solid blue (0,0,1) color (NOTE: this is a static member, NOT a property) + static const srgb cyan; ///< a solid cyan (0,1,1) color (NOTE: this is a static member, NOT a property) + static const srgb magenta; ///< a solid magenta (1,0,1) color (NOTE: this is a static member, NOT a property) + static const srgb yellow; ///< a solid yellow (1,1,0) color (NOTE: this is a static member, NOT a property) + + srgb() = default; + srgb(ScalarT r, ScalarT g, ScalarT b) : r(r), g(g), b(b) {} + + constexpr bool operator==(srgb const& v) const { return r == v.r && b == v.b && g == v.g; } + constexpr bool operator!=(srgb const& v) const { return r != v.r || b != v.b || g != v.g; } + + // NOTE: requires feature/color included + constexpr static srgb from_hex_string(cc::string_view s); +}; + +// TODO: 1 vs 255 + +template +const srgb srgb::black = {T(0), T(0), T(0)}; +template +const srgb srgb::white = {T(255), T(255), T(255)}; +template +const srgb srgb::red = {T(255), T(0), T(0)}; +template +const srgb srgb::green = {T(0), T(255), T(0)}; +template +const srgb srgb::blue = {T(0), T(0), T(255)}; +template +const srgb srgb::cyan = {T(0), T(255), T(255)}; +template +const srgb srgb::magenta = {T(255), T(0), T(255)}; +template +const srgb srgb::yellow = {T(255), T(255), T(0)}; + + +template +struct srgba +{ + ScalarT r = static_cast(0); + ScalarT g = static_cast(0); + ScalarT b = static_cast(0); + ScalarT a = color_scalar_from_float(1); + + static const srgba black; ///< a solid opaque black (0,0,0,1) color (NOTE: this is a static member, NOT a property) + static const srgba white; ///< a solid opaque white (1,1,1,1) color (NOTE: this is a static member, NOT a property) + static const srgba red; ///< a solid opaque red (1,0,0,1) color (NOTE: this is a static member, NOT a property) + static const srgba green; ///< a solid opaque green (0,1,0,1) color (NOTE: this is a static member, NOT a property) + static const srgba blue; ///< a solid opaque blue (0,0,1,1) color (NOTE: this is a static member, NOT a property) + static const srgba cyan; ///< a solid opaque cyan (0,1,1,1) color (NOTE: this is a static member, NOT a property) + static const srgba magenta; ///< a solid opaque magenta (1,0,1,1) color (NOTE: this is a static member, NOT a property) + static const srgba yellow; ///< a solid opaque yellow (1,1,0,1) color (NOTE: this is a static member, NOT a property) + static const srgba transparent; ///< a fully transparent black (0,0,0,0) (NOTE: this is a static member, NOT a property) + + srgba() = default; + srgba(ScalarT r, ScalarT g, ScalarT b, ScalarT a = color_scalar_from_float(1)) : r(r), g(g), b(b), a(a) {} + + constexpr bool operator==(srgba const& v) const { return r == v.r && b == v.b && g == v.g && a == v.a; } + constexpr bool operator!=(srgba const& v) const { return r != v.r || b != v.b || g != v.g || a != v.a; } + + // NOTE: requires feature/color included + constexpr static srgba from_hex_string(cc::string_view s); +}; + +template +const srgba srgba::black = {T(0), T(0), T(0), T(255)}; +template +const srgba srgba::white = {T(255), T(255), T(255), T(255)}; +template +const srgba srgba::red = {T(255), T(0), T(0), T(255)}; +template +const srgba srgba::green = {T(0), T(255), T(0), T(255)}; +template +const srgba srgba::blue = {T(0), T(0), T(255), T(255)}; +template +const srgba srgba::cyan = {T(0), T(255), T(255), T(255)}; +template +const srgba srgba::magenta = {T(255), T(0), T(255), T(255)}; +template +const srgba srgba::yellow = {T(255), T(255), T(0), T(255)}; +template +const srgba srgba::transparent = {T(0), T(0), T(0), T(0)}; + +// reflection +template +constexpr void introspect(I&& i, srgb& v) +{ + i(v.r, "r"); + i(v.g, "g"); + i(v.b, "b"); +} +template +constexpr void introspect(I&& i, srgba& v) +{ + i(v.r, "r"); + i(v.g, "g"); + i(v.b, "b"); + i(v.a, "a"); +} + +// traits +template +struct color_traits> : detail::base_color_traits<3, T, alpha_type::none> +{ +}; +template +struct color_traits> : detail::base_color_traits<4, T, alpha_type::straight> +{ +}; +} diff --git a/src/typed-geometry/types/types.hh b/src/typed-geometry/types/types.hh index 1ac6579f..2961c130 100644 --- a/src/typed-geometry/types/types.hh +++ b/src/typed-geometry/types/types.hh @@ -29,6 +29,9 @@ #include "bezier.hh" +#include "hsl.hh" +#include "srgb.hh" + #include "capped_array.hh" #include "capped_vector.hh" diff --git a/tests/feature/color/basics.cc b/tests/feature/color/basics.cc new file mode 100644 index 00000000..b92e9643 --- /dev/null +++ b/tests/feature/color/basics.cc @@ -0,0 +1,23 @@ +#include + +#include + +TEST("color basics", disabled) +{ + // standard linear colors + [[maybe_unused]] tg::color3 c0; + [[maybe_unused]] tg::color4 c1; + + // compressed srgb colors + [[maybe_unused]] tg::srgb8 c2; + [[maybe_unused]] tg::srgba8 c3; +} + +TEST("color literals") +{ + CHECK("fff"_color3 == tg::color3::white); + CHECK("ff0000"_color4 == tg::color4::red); + CHECK("0f0f"_color4 == tg::color4::green); + CHECK("000"_srgb8 == tg::srgb8::black); + CHECK("#00f"_srgba8 == tg::srgba8::blue); +} diff --git a/tests/feature/fixed_int/generate_fixed_uint_multiplications.cc b/tests/feature/fixed_int/generate_fixed_uint_multiplications.cc index 5006957b..37ef1db7 100644 --- a/tests/feature/fixed_int/generate_fixed_uint_multiplications.cc +++ b/tests/feature/fixed_int/generate_fixed_uint_multiplications.cc @@ -26,7 +26,7 @@ namespace { return "inline u64 mul(u64 lhs, u64 rhs, u64* high)\n" "{\n" - " return _mulx_u64(lhs, rhs, high);\n" + " return TG_MUL_U128(lhs, rhs, high);\n" "}\n"; } @@ -94,7 +94,7 @@ std::string generate_mul(int w_r, int w_a, int w_b) for (auto const& m : mul_intrin) { - result += " " + l_of(m) + " = _mulx_u64(" + lhs_of(m.first) + ", " + rhs_of(m.second) + ", &" + h_of(m) + ");\n"; + result += " " + l_of(m) + " = TG_MUL_U128(" + lhs_of(m.first) + ", " + rhs_of(m.second) + ", &" + h_of(m) + ");\n"; } for (auto const& m : mul) { @@ -275,7 +275,7 @@ std::string generate_imul(int w_r, int w_a, int w_b) for (auto const& m : mul_intrin) { - result += " " + l_of(m) + " = _mulx_u64(u64(" + lhs_of(m.first) + "), u64(" + rhs_of(m.second) + "), &" + h_of(m) + ");\n"; + result += " " + l_of(m) + " = TG_MUL_U128(u64(" + lhs_of(m.first) + "), u64(" + rhs_of(m.second) + "), &" + h_of(m) + ");\n"; } for (auto const& m : mul) { diff --git a/tests/feature/intersections/intersection.cc b/tests/feature/intersections/intersection.cc index f98d9573..eb681b6a 100644 --- a/tests/feature/intersections/intersection.cc +++ b/tests/feature/intersections/intersection.cc @@ -3,6 +3,327 @@ #include +FUZZ_TEST("IntersectionDisk3Plane3")(tg::rng& rng) +{ + auto plane_xz = tg::plane3({0.f, 1.f, 0.f}, tg::pos3::zero); + + { // a) xz-plane and orthogonal disk + auto disk = tg::disk3(tg::pos3::zero, 1.f, {1.f, 0.f, 0.f}); + + auto insec = tg::intersection(disk, plane_xz); + + CHECK(insec.has_value()); + CHECK(distance(insec.value().pos0, tg::pos3(0.f, 0.f, 1.f)) == nx::approx(0.f)); + CHECK(distance(insec.value().pos1, tg::pos3(0.f, 0.f, -1.f)) == nx::approx(0.f)); + } + + { // b) xz-plane and disk with random orientation - intersection through origin + auto disk = tg::disk3(tg::pos3::zero, 1.f, tg::uniform(rng)); + + bool insec_exists = (disk.normal == plane_xz.normal) ? false : true; + auto insec = tg::intersection(disk, plane_xz); + + CHECK(insec.has_value() == insec_exists); + CHECK(tg::contains(insec.value(), tg::pos3::zero)); + } + + { // c) disk in plane - no intersection + auto rng_dir = tg::uniform(rng); + auto disk = tg::disk3(tg::pos3::zero, 1.f, rng_dir); + auto plane = tg::plane3(rng_dir, tg::pos3::zero); + + auto insec = tg::intersection(disk, plane); + + CHECK(!insec.has_value()); + } +} + +FUZZ_TEST("IntersectionPlane3Tube3")(tg::rng& rng) +{ + auto inf_cyl = tg::inf_cylinder3(tg::line3({0, 0, 0}, {0, 1.f, 0}), 1.f); + auto cylinder_env = tg::aabb3({0.f, -5.f, 0.f}, {0.f, 5.f, 0.f}); + + { // a) plane with random orientation through tube (intersection might not exist if exceeding tube on one side) + auto p_pos = tg::uniform(rng, cylinder_env); + auto normal = tg::uniform(rng); + + auto plane = tg::plane3(normal, p_pos); + + bool insec_exists = dot(normal, inf_cyl.axis.dir) == 0 ? false : true; + + auto insec = tg::intersection(plane, inf_cyl); + + if (insec.has_value()) + CHECK(distance(insec.value().center, p_pos) == nx::approx(0.f)); + } + + { // b) plane normal parallel to tub axis -> results in a circle/disk + auto p_pos = tg::uniform(rng, cylinder_env); + auto normal = inf_cyl.axis.dir; + + auto plane = tg::plane3(normal, p_pos); + + auto insec = tg::intersection(plane, inf_cyl); + + CHECK(insec.has_value()); + CHECK(tg::length_sqr(insec.value().semi_axes[0]) == tg::length_sqr(insec.value().semi_axes[1])); + } +} + +FUZZ_TEST("IntersectionSegment3BoundaryObject")(tg::rng& rng) +{ + { // a) segment3 - cylinder_boundary3 + auto cylinder_b = tg::cylinder_boundary<3, float>({0.f, -2.f, 0.f}, {0.f, 2.f, 0.f}, 1.f); + + auto rng_pos = tg::uniform(rng, cylinder_b); + auto rng_dir = tg::uniform(rng); + + auto pos0 = rng_pos + 4.1f * rng_dir; + auto pos1 = rng_pos - 4.1f * rng_dir; + auto seg = tg::segment3(pos0, pos1); + + auto insec = tg::intersection(seg, cylinder_b); + + CHECK(insec.any()); + } + + { // b) segment3 - box_boundary3 + // boundary of unit cube + auto box_b = tg::box_boundary<3, float>(tg::pos3::zero, tg::mat3::identity); + + auto rng_pos = tg::uniform(rng, box_b); + auto rng_dir = tg::uniform(rng); + + auto pos0 = rng_pos + 4.1f * rng_dir; + auto pos1 = rng_pos - 4.1f * rng_dir; + auto seg = tg::segment3(pos0, pos1); + + auto insec = tg::intersection(seg, box_b); + + CHECK(insec.any()); + } +} + +FUZZ_TEST("IntersectionSegment3Tube3")(tg::rng& rng) +{ + auto tube = tg::tube3({{0.f, -1.f, 0.f}, {0.f, 1.f, 0.f}}, 1.f); + auto scalar_range = tg::aabb1(1.1f, 3.f); + + { // a) intersection through origin + auto orth_vec = tg::vec3(1.f, 0.f, 0.f); + auto pos1 = tg::pos3::zero + tg::uniform(rng, scalar_range).x * orth_vec; + auto pos2 = tg::pos3::zero - tg::uniform(rng, scalar_range).x * orth_vec; + + tg::segment3 seg = {pos1, pos2}; + + auto insec = tg::intersection(seg, tube); + + CHECK(insec.any()); + CHECK(insec.size() == 2); + CHECK(tg::distance_sqr(tg::pos3::zero, insec.first()) > 0.f); + CHECK(tg::distance_sqr(tg::pos3::zero, insec.last()) > 0.f); + } + + { // b) segment inside the tube + auto cylinder = tg::cylinder3({{0.f, -1.f, 0.f}, {0.f, 1.f, 0.f}}, 1.f); + auto pos1 = tg::uniform(rng, cylinder); + auto pos2 = tg::uniform(rng, cylinder); + + tg::segment3 seg = {pos1, pos2}; + + auto insec = tg::intersection(seg, tube); + + CHECK(!insec.any()); + } +} + +FUZZ_TEST("IntersectionSphere3Plane3")(tg::rng& rng) +{ + auto sphere = tg::sphere3::unit; + auto scalar_range = tg::aabb1(1.1f, 5.f); + + { // a) intersection (through center of sphere) + tg::dir3 normal = tg::uniform(rng); + auto plane = tg::plane3(normal, {0.f, 0.f, 0.f}); + + auto insec = tg::intersection(sphere, plane); + + CHECK(insec.has_value()); + CHECK(tg::distance_sqr(insec.value().center, {0.f, 0.f, 0.f}) == nx::approx(0.f)); + CHECK(insec.value().radius == nx::approx(1.f)); + } + + { // b) no intersection + auto normal = tg::uniform(rng); + auto pos = tg::pos3::zero + tg::uniform(rng, scalar_range).x * normal; + + auto plane = tg::plane3(normal, pos); + + auto insec = tg::intersection(sphere, plane); + + CHECK(!insec.has_value()); + } +} + +FUZZ_TEST("IntersectionSegment3Hemisphere3")(tg::rng& rng) +{ + // upper part of the unit sphere + auto hemis = tg::hemisphere3({0.f, 0.f, 0.f}, 1.f, {0.f, 1.f, 0.f}); + auto below_hemis = tg::aabb3({-0.5f, -5.f, -0.5f}, {0.5f, -0.1f, 0.5f}); + auto above_hemis_base = tg::aabb3({-0.5f, 0.1f, -0.5f}, {0.5f, 5.f, 0.5f}); + auto scalar_range = tg::aabb1(1.f, 5.f); + auto short_scalar_range = tg::aabb1(0.1f, 0.99f); + auto sphere_hemis = tg::sphere3(hemis.center, hemis.radius); + + { // a) intersection with the circle base + auto pos0 = tg::uniform(rng, below_hemis); + auto pos1 = tg::uniform(rng, above_hemis_base); + + auto seg = tg::segment3{pos0, pos1}; + bool insec_exists = tg::length(pos1 - pos0) > 0.f ? true : false; + auto insec = tg::intersection(seg, hemis); + + CHECK(insec.has_value() == insec_exists); + } + + { // b) intersection with sphere extension (and with hemisphere part) + auto rng_dir = tg::uniform(rng); + auto pos0 = hemis.center + tg::uniform(rng, scalar_range).x * rng_dir; + auto pos1 = hemis.center - tg::uniform(rng, scalar_range).x * rng_dir; + + auto seg = tg::segment3{pos0, pos1}; + + auto insec = tg::intersection(seg, hemis); + + CHECK(insec.has_value()); + CHECK(distance(insec.value(), tg::pos3::zero) == nx::approx(0.f)); + } + + { // c) intersection with sphere extension (but not with the hemisphere) + auto rot_range = tg::aabb1(0.f, 89.f); + auto rot_mat = tg::rotation_mat3_of(tg::rotation_z(tg::degree(tg::uniform(rng, rot_range).x))); + + tg::dir3 inv_normal_rot = tg::normalize(rot_mat * hemis.normal); + + auto pos0 = hemis.center - tg::uniform(rng, short_scalar_range).x * inv_normal_rot; + auto pos1 = tg::uniform(rng, below_hemis); + + auto seg = tg::segment3{pos0, pos1}; + + auto insec = tg::intersection(seg, hemis); + + CHECK(!insec.has_value()); + } + + { // d) both segment points inside the hemisphere + auto pos0 = tg::uniform(rng, hemis); + auto pos1 = tg::uniform(rng, hemis); + + auto seg = tg::segment3{pos0, pos1}; + + auto insec = tg::intersection(seg, hemis); + + CHECK(insec.has_value()); + CHECK(distance(insec.value().pos0, pos0) == nx::approx(0.f)); + CHECK(distance(insec.value().pos1, pos1) == nx::approx(0.f)); + } +} + +FUZZ_TEST("IntersectionSegment3Disk3")(tg::rng& rng) +{ + auto disk = tg::disk3({0.f, 0.f, 0.f}, 1.f, {0.f, 1.f, 0.f}); + auto below_circle = tg::aabb3({-5.f, -5.f, -5.f}, {5.f, -0.1f, 5.f}); + auto scalar_range = tg::aabb1(1.f, 5.f); + + { // a) both seg points below the circle -> no intersection + tg::pos3 pos0 = tg::uniform(rng, below_circle); + tg::pos3 pos1 = tg::uniform(rng, below_circle); + + auto seg = tg::segment3{pos0, pos1}; + + auto insec = tg::intersection(seg, disk); + + CHECK(!insec.has_value()); + } + + { // b) one seg point below the circle and one above with intersection + + auto rng_dir = tg::uniform(rng); + + tg::pos3 pos0 = tg::pos3::zero + tg::uniform(rng, scalar_range).x * rng_dir; + tg::pos3 pos1 = tg::pos3::zero - tg::uniform(rng, scalar_range).x * rng_dir; + + auto seg = tg::segment3{pos0, pos1}; + + auto insec = tg::intersection(seg, disk); + + CHECK(insec.has_value()); + CHECK(distance(insec.value(), tg::pos3::zero) == nx::approx(0.f)); + } + + { // c) segment in plane of circle (degenerated) + tg::pos3 pos0 = tg::pos3::zero + tg::uniform(rng, scalar_range).x * tg::vec3{1.f, 0, 1.f}; + tg::pos3 pos1 = tg::pos3::zero + tg::uniform(rng, scalar_range).x * tg::vec3{-1.f, 0, -1.f}; + + auto seg = tg::segment3{pos0, pos1}; + + auto insec = tg::intersection(seg, disk); + + CHECK(!insec.has_value()); + } +} + +FUZZ_TEST("IntersectionSegment3Halfspace3")(tg::rng& rng) +{ + auto env = tg::aabb3({-10.f, -10.f, -10.f}, {10.f, 10.f, 10.f}); + auto scalar_range = tg::aabb1(1.f, 5.f); + + auto hs_pos = tg::uniform(rng, env); + auto hs = tg::halfspace3(tg::uniform(rng), hs_pos); + + { // a) one point inside and one point outside of the halfspace + tg::pos3 pos1 = hs_pos + (hs.normal * tg::uniform(rng, scalar_range).x); + tg::pos3 pos2 = hs_pos - (hs.normal * tg::uniform(rng, scalar_range).x); + + auto seg = tg::segment3{pos1, pos2}; + + auto insec = tg::intersection(seg, hs); + + CHECK(insec.has_value()); + CHECK(distance(insec.value().pos0, hs_pos) == nx::approx(0.f)); + CHECK(distance(insec.value().pos1, pos2) == nx::approx(0.f)); + } + + { // b) both points inside of the halfspace + auto rng_dir = tg::uniform(rng); + auto in_plane = tg::cross(rng_dir, hs.normal); + + tg::pos3 pos1 = hs_pos + tg::uniform(rng, scalar_range).x * in_plane - tg::uniform(rng, scalar_range).x * hs.normal; + tg::pos3 pos2 = hs_pos - tg::uniform(rng, scalar_range).x * in_plane - tg::uniform(rng, scalar_range).x * hs.normal; + + auto seg = tg::segment3{pos1, pos2}; + + auto insec = tg::intersection(seg, hs); + + CHECK(insec.has_value()); + CHECK(distance(insec.value().pos0, pos1) == nx::approx(0.f)); + CHECK(distance(insec.value().pos1, pos2) == nx::approx(0.f)); + } + + { // c) both points outside of the halfspace -> no intersection + auto rng_dir = tg::uniform(rng); + auto in_plane = tg::cross(rng_dir, hs.normal); + + tg::pos3 pos1 = hs_pos + tg::uniform(rng, scalar_range).x * in_plane + tg::uniform(rng, scalar_range).x * hs.normal; + tg::pos3 pos2 = hs_pos - tg::uniform(rng, scalar_range).x * in_plane + tg::uniform(rng, scalar_range).x * hs.normal; + + auto seg = tg::segment3{pos1, pos2}; + + auto insec = tg::intersection(seg, hs); + + CHECK(!insec.has_value()); + } +} FUZZ_TEST("IntersectionSegment3Box3")(tg::rng& rng) { @@ -73,13 +394,12 @@ FUZZ_TEST("IntersectionTriangle3Triangle3")(tg::rng& rng) } { // b) no intersection - tg::aabb3 above_plane = tg::aabb3({-15.0f, 0.0f, -15.0f}, {15.0f, 15.0f, 15.0f}); + tg::aabb3 above_plane = tg::aabb3({-15.0f, 0.5f, -15.0f}, {15.0f, 15.0f, 15.0f}); auto pos1 = tg::uniform(rng, above_plane); auto pos2 = tg::uniform(rng, above_plane); auto pos3 = tg::uniform(rng, above_plane); tg::triangle3 t3 = {pos1, pos2, pos3}; - CHECK(!tg::intersection(t1, t3).has_value()); } @@ -99,7 +419,6 @@ FUZZ_TEST("IntersectionTriangle3Triangle3")(tg::rng& rng) } } - FUZZ_TEST("IntersectionSegment3ConvexShapes3")(tg::rng& rng) { // segment3 - capsule3 diff --git a/tests/feature/intersections/intersects.cc b/tests/feature/intersections/intersects.cc index 3519d329..a03929e0 100644 --- a/tests/feature/intersections/intersects.cc +++ b/tests/feature/intersections/intersects.cc @@ -3,6 +3,45 @@ #include +FUZZ_TEST("IntersectionTriangle3Triangle3")(tg::rng& rng) +{ + // a) Triangles in xz-plane and not intersecting + auto bb_l_planar = tg::aabb3({-10, 0, -10}, {-0.1f, 0, 10}); + auto bb_r_planar = tg::aabb3({0.1f, 0, -10}, {10, 0, 10}); + + auto ta = tg::triangle3(tg::uniform(rng, bb_l_planar), tg::uniform(rng, bb_l_planar), tg::uniform(rng, bb_l_planar)); + auto tb = tg::triangle3(tg::uniform(rng, bb_r_planar), tg::uniform(rng, bb_r_planar), tg::uniform(rng, bb_r_planar)); + + CHECK(!intersects(ta, tb)); + + // b) Tris in x-plane and intersecting + auto centroid_ta = tg::centroid_of(ta); + auto tc = tg::triangle3(tg::uniform(rng, bb_r_planar), tg::uniform(rng, bb_r_planar), centroid_ta); + auto insec = tg::intersects(ta, tc); + + CHECK(intersects(ta, tc)); + + // c) non-coplanar and not intersecting + auto bb_l = tg::aabb3({-10, -10, -10}, {-0.1f, 10, 10}); + auto bb_r = tg::aabb3({0.1f, -10, -10}, {10, 10, 10}); + + auto td = tg::triangle3(tg::uniform(rng, bb_l), tg::uniform(rng, bb_l), tg::uniform(rng, bb_l)); + auto te = tg::triangle3(tg::uniform(rng, bb_r), tg::uniform(rng, bb_r), tg::uniform(rng, bb_r)); + + CHECK(!intersects(td, te)); + + // d) non-coplanar and intersecting + auto bb = tg::aabb3(-10, 10); + + auto tf = tg::triangle3(tg::uniform(rng, bb), tg::uniform(rng, bb), tg::uniform(rng, bb)); + auto tg_pos0 = tg::uniform(rng, bb); + auto centroid_tf = tg::centroid_of(tf); + auto p_elong_centroid = centroid_tf + (centroid_tf - tg_pos0); + auto tg = tg::triangle3(tg_pos0, p_elong_centroid, tg::uniform(rng, bb)); + + CHECK(intersects(tf, tg)); +} + FUZZ_TEST("IntersectsSphere2in3Triangle3")(tg::rng& rng) { auto circ = tg::sphere2in3({0, 0, 0}, 1.f, {0.f, 1.f, 0.f}); @@ -705,6 +744,31 @@ FUZZ_TEST("IntersectsBox2Box2")(tg::rng& rng) CHECK(intersects(box1, box2) == insec); } +TEST("IntersectsTriangle2Triangle2") +{ + auto t1 = tg::triangle2({0.f, 0.f}, {2.f, 0.f}, {2.f, 2.f}); + auto t2 = tg::triangle2({-1.f, -1.f}, {1.f, -1.f}, {1.f, 1.f}); + + CHECK(tg::intersects(t1, t2)); + + auto t3 = tg::triangle2({-2.f, -2.f}, {0.f, -0.5f}, {-2.f, -0.5f}); + + CHECK(!tg::intersects(t1, t3)); +} + +TEST("IntersectsTriangle3Triangle3") +{ + auto t1 = tg::triangle3({0.f, 0.f, 0.f}, {2.f, 0.f, 0.f}, {2.f, 2.f, 0.f}); + auto t2 = tg::triangle3({-1.f, -1.f, 0.f}, {1.f, -1.f, 0.f}, {1.f, 1.f, 0.f}); + + CHECK(tg::intersects(t1, t2)); + + auto t3 = tg::triangle3({-2.f, -2.f, 0.f}, {0.f, -0.5f, 0.f}, {-2.f, -0.5f, 0.f}); + + auto insec = intersects(t1, t3); + CHECK(!tg::intersects(t1, t3)); +} + TEST("IntersectsBox3Sphere3") { tg::sphere3 s1 = tg::sphere3(tg::pos3(0.0f, 0.0f, 0.0f), 5.0f); diff --git a/tests/impl-report.cc b/tests/impl-report.cc index 876dd4d3..de5f40ab 100644 --- a/tests/impl-report.cc +++ b/tests/impl-report.cc @@ -1,11 +1,12 @@ #include +#include #include #include #include #include -#ifdef TG_IMPLEMENTATION_REPORT +#if defined(TG_IMPLEMENTATION_REPORT) #include @@ -72,6 +73,45 @@ using try_intersects_aabb2_of = decltype(intersects(std::declval(), tg template using try_intersects_aabb3_of = decltype(intersects(std::declval(), tg::aabb3())); +template +using try_intersects_segment3_of = decltype(intersects(std::declval(), tg::segment3())); + +template +using try_intersects_sphere3_of = decltype(intersects(std::declval(), tg::sphere3())); + +template +using try_intersects_box3_of = decltype(intersects(std::declval(), tg::box3())); + +template +using try_intersects_capsule3_of = decltype(intersects(std::declval(), tg::capsule3())); + +template +using try_intersects_cone3_of = decltype(intersects(std::declval, tg::cone3())); + +template +using try_intersects_cylinder3_of = decltype(intersects(std::declval(), tg::cylinder3())); + +template +using try_intersects_ellipse3_of = decltype(intersects(std::declval(), tg::ellipse3())); + +template +using try_intersects_halfspace3_of = decltype(intersects(std::declval(), tg::halfspace3())); + +template +using try_intersects_hemisphere3_of = decltype(intersects(std::declval(), tg::hemisphere3())); + +template +using try_intersects_triangle3_of = decltype(intersects(std::declval(), tg::triangle3())); + +template +using try_intersects_plane3_of = decltype(intersects(std::declval(), tg::plane3())); + +template +using try_intersects_tube3_of = decltype(intersects(std::declval(), tg::tube3())); + +template +using try_intersects_sphere2in3_of = decltype(intersects(std::declval(), tg::sphere2in3())); + template using try_solid_of = decltype(solid_of(std::declval())); @@ -80,7 +120,8 @@ void test_single_object_type(std::string name) { static auto constexpr domainD = tg::object_traits::domain_dimension; static auto constexpr objectD = tg::object_traits::object_dimension; - static auto constexpr solidD = [] { + static auto constexpr solidD = [] + { if constexpr (tg::can_apply) return tg::object_traits>::object_dimension; return objectD; @@ -139,7 +180,7 @@ void test_single_object_type(std::string name) std::cerr << "no contains(tg::" << name << ", tg::pos3)" << std::endl; } else - static_assert(tg::always_false, "not implemented"); + static_assert(tg::always_false_v, "not implemented"); // operations for finite objects if constexpr (tg::object_traits::is_finite) @@ -178,7 +219,7 @@ void test_single_object_type(std::string name) std::cerr << "no area_of(tg::" << name << ")" << std::endl; } else - static_assert(tg::always_false, "not implemented"); + static_assert(tg::always_false_v, "not implemented"); } // ray intersections @@ -194,6 +235,45 @@ void test_single_object_type(std::string name) if constexpr (domainD == 3 && !tg::can_apply) std::cerr << "no intersects(tg::" << name << ", tg::aabb3)" << std::endl; + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::box3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::segment3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::sphere3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::capsule3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::cone3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::cylinder3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::ellipse3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::halfspace3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::hemisphere3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::triangle3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::plane3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::tube3)" << std::endl; + + if constexpr (domainD == 3 && !tg::can_apply) + std::cerr << "no intersects(tg::" << name << ", tg::sphere2in3)" << std::endl; + // TODO: more } } @@ -281,7 +361,7 @@ TEST("implementation report") test_object_type_nested_boundary_caps("pyramid", "sphere2in3"); test_object_type_nested_boundary_caps("pyramid", "box2in3"); test_object_type_nested_boundary_caps("pyramid", "triangle3"); - test_object_type_nested_boundary_caps("pyramid", "quad3"); + // test_object_type_nested_boundary_caps("pyramid", "quad3"); } #endif