feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,26 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
-fexceptions
-frtti
-std=c++20
-Wall
-Wpedantic
-DLOG_TAG=\"Fabric\")
file(GLOB react_render_graphics_SRC CONFIGURE_DEPENDS *.cpp)
add_library(react_render_graphics SHARED ${react_render_graphics_SRC})
target_include_directories(react_render_graphics
PUBLIC
${REACT_COMMON_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/platform/android/
)
target_link_libraries(react_render_graphics glog fb fbjni folly_runtime react_debug react_utils)

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "Color.h"
namespace facebook::react {
bool isColorMeaningful(const SharedColor& color) noexcept {
if (!color) {
return false;
}
return colorComponentsFromColor(color).alpha > 0;
}
SharedColor colorFromComponents(ColorComponents components) {
return {hostPlatformColorFromComponents(components)};
}
ColorComponents colorComponentsFromColor(SharedColor sharedColor) {
return colorComponentsFromHostPlatformColor(*sharedColor);
}
SharedColor clearColor() {
static SharedColor color = colorFromComponents(ColorComponents{0, 0, 0, 0});
return color;
}
SharedColor blackColor() {
static SharedColor color = colorFromComponents(ColorComponents{0, 0, 0, 1});
return color;
}
SharedColor whiteColor() {
static SharedColor color = colorFromComponents(ColorComponents{1, 1, 1, 1});
return color;
}
} // namespace facebook::react

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <functional>
#include <limits>
#include <react/renderer/graphics/ColorComponents.h>
#include <react/renderer/graphics/HostPlatformColor.h>
namespace facebook::react {
/*
* On Android, a color can be represented as 32 bits integer, so there is no
* need to instantiate complex color objects and then pass them as shared
* pointers. Hense instead of using shared_ptr, we use a simple wrapper class
* which provides a pointer-like interface. On other platforms, colors may be
* represented by more complex objects that cannot be represented as 32-bits
* integers, so we hide the implementation detail in HostPlatformColor.h.
*/
class SharedColor {
public:
SharedColor() : color_(HostPlatformColor::UndefinedColor) {}
SharedColor(Color color) : color_(color) {}
Color& operator*() {
return color_;
}
const Color& operator*() const {
return color_;
}
bool operator==(const SharedColor& otherColor) const {
return color_ == otherColor.color_;
}
bool operator!=(const SharedColor& otherColor) const {
return color_ != otherColor.color_;
}
operator bool() const {
return color_ != HostPlatformColor::UndefinedColor;
}
private:
Color color_;
};
bool isColorMeaningful(const SharedColor& color) noexcept;
SharedColor colorFromComponents(ColorComponents components);
ColorComponents colorComponentsFromColor(SharedColor color);
SharedColor clearColor();
SharedColor blackColor();
SharedColor whiteColor();
} // namespace facebook::react
template <>
struct std::hash<facebook::react::SharedColor> {
size_t operator()(const facebook::react::SharedColor& color) const {
return std::hash<facebook::react::Color>{}(*color);
}
};

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook::react {
struct ColorComponents {
float red{0};
float green{0};
float blue{0};
float alpha{0};
};
} // namespace facebook::react

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/graphics/Float.h>
#include <react/renderer/graphics/Point.h>
#include <react/renderer/graphics/Rect.h>
#include <react/renderer/graphics/RectangleCorners.h>
#include <react/renderer/graphics/RectangleEdges.h>
#include <react/renderer/graphics/Size.h>
#include <react/renderer/graphics/Vector.h>
#warning \
"The Geometry.h file is deprecated and will be removed in the next version of React Native. Please update your #include and #import statements to use the specific files. For example, if you imported Geometry.h to use Float.h, replase the #include <react/renderer/graphics/Geometry.h> with #include <react/renderer/graphics/Float.h>"

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <tuple>
#include <react/renderer/graphics/Float.h>
#include <react/utils/hash_combine.h>
namespace facebook::react {
/*
* Contains a point in a two-dimensional coordinate system.
*/
struct Point {
Float x{0};
Float y{0};
Point& operator+=(const Point& point) noexcept {
x += point.x;
y += point.y;
return *this;
}
Point& operator-=(const Point& point) noexcept {
x -= point.x;
y -= point.y;
return *this;
}
Point& operator*=(const Point& point) noexcept {
x *= point.x;
y *= point.y;
return *this;
}
friend Point operator+(Point lhs, const Point& rhs) noexcept {
return lhs += rhs;
}
friend Point operator-(Point lhs, const Point& rhs) noexcept {
return lhs -= rhs;
}
};
inline bool operator==(const Point& rhs, const Point& lhs) noexcept {
return std::tie(lhs.x, lhs.y) == std::tie(rhs.x, rhs.y);
}
inline bool operator!=(const Point& rhs, const Point& lhs) noexcept {
return !(lhs == rhs);
}
} // namespace facebook::react
namespace std {
template <>
struct hash<facebook::react::Point> {
size_t operator()(const facebook::react::Point& point) const noexcept {
return facebook::react::hash_combine(point.x, point.y);
}
};
} // namespace std

View File

@@ -0,0 +1,68 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
require "json"
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
folly_config = get_folly_config()
folly_compiler_flags = folly_config[:compiler_flags]
folly_version = folly_config[:version]
boost_compiler_flags = '-Wno-documentation'
Pod::Spec.new do |s|
source_files = "**/*.{m,mm,cpp,h}"
header_search_paths = [
"\"$(PODS_ROOT)/boost\"",
"\"$(PODS_TARGET_SRCROOT)/../../../\"",
"\"$(PODS_ROOT)/RCT-Folly\"",
"\"$(PODS_ROOT)/DoubleConversion\"",
"\"$(PODS_ROOT)/fmt/include\""
]
s.name = "React-graphics"
s.version = version
s.summary = "Fabric for React Native."
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
s.source_files = source_files
s.exclude_files = "tests",
"platform/android",
"platform/cxx",
"platform/windows",
s.header_dir = "react/renderer/graphics"
if ENV['USE_FRAMEWORKS']
s.module_name = "React_graphics"
s.header_mappings_dir = "../../.."
header_search_paths = header_search_paths + ["\"$(PODS_TARGET_SRCROOT)/platform/ios\""]
end
s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO",
"HEADER_SEARCH_PATHS" => header_search_paths.join(" "),
"DEFINES_MODULE" => "YES",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20" }
s.dependency "glog"
s.dependency "RCT-Folly/Fabric", folly_version
s.dependency "React-Core/Default", version
s.dependency "React-utils"
s.dependency "DoubleConversion"
s.dependency "fmt", "9.1.0"
end

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <algorithm>
#include <tuple>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/graphics/Point.h>
#include <react/renderer/graphics/Size.h>
#include <react/utils/hash_combine.h>
namespace facebook::react {
/*
* Contains the location and dimensions of a rectangle.
*/
struct Rect {
Point origin{0, 0};
Size size{0, 0};
bool operator==(const Rect& rhs) const noexcept {
return std::tie(this->origin, this->size) == std::tie(rhs.origin, rhs.size);
}
bool operator!=(const Rect& rhs) const noexcept {
return !(*this == rhs);
}
Float getMaxX() const noexcept {
return size.width > 0 ? origin.x + size.width : origin.x;
}
Float getMaxY() const noexcept {
return size.height > 0 ? origin.y + size.height : origin.y;
}
Float getMinX() const noexcept {
return size.width >= 0 ? origin.x : origin.x + size.width;
}
Float getMinY() const noexcept {
return size.height >= 0 ? origin.y : origin.y + size.height;
}
Float getMidX() const noexcept {
return origin.x + size.width / 2;
}
Float getMidY() const noexcept {
return origin.y + size.height / 2;
}
Point getCenter() const noexcept {
return {getMidX(), getMidY()};
}
void unionInPlace(const Rect& rect) noexcept {
auto x1 = std::min(getMinX(), rect.getMinX());
auto y1 = std::min(getMinY(), rect.getMinY());
auto x2 = std::max(getMaxX(), rect.getMaxX());
auto y2 = std::max(getMaxY(), rect.getMaxY());
origin = {x1, y1};
size = {x2 - x1, y2 - y1};
}
bool containsPoint(Point point) noexcept {
return point.x >= origin.x && point.y >= origin.y &&
point.x <= (origin.x + size.width) &&
point.y <= (origin.y + size.height);
}
static Rect intersect(const Rect& rect1, const Rect& rect2) {
Float x1 = std::max(rect1.origin.x, rect2.origin.x);
Float y1 = std::max(rect1.origin.y, rect2.origin.y);
Float x2 = std::min(
rect1.origin.x + rect1.size.width, rect2.origin.x + rect2.size.width);
Float y2 = std::min(
rect1.origin.y + rect1.size.height, rect2.origin.y + rect2.size.height);
Float intersectionWidth = x2 - x1;
Float intersectionHeight = y2 - y1;
if (intersectionWidth < 0 || intersectionHeight < 0) {
return {};
}
return {{x1, y1}, {intersectionWidth, intersectionHeight}};
}
static Rect boundingRect(
const Point& a,
const Point& b,
const Point& c,
const Point& d) noexcept {
auto leftTopPoint = a;
auto rightBottomPoint = a;
leftTopPoint.x = std::min(leftTopPoint.x, b.x);
leftTopPoint.x = std::min(leftTopPoint.x, c.x);
leftTopPoint.x = std::min(leftTopPoint.x, d.x);
leftTopPoint.y = std::min(leftTopPoint.y, b.y);
leftTopPoint.y = std::min(leftTopPoint.y, c.y);
leftTopPoint.y = std::min(leftTopPoint.y, d.y);
rightBottomPoint.x = std::max(rightBottomPoint.x, b.x);
rightBottomPoint.x = std::max(rightBottomPoint.x, c.x);
rightBottomPoint.x = std::max(rightBottomPoint.x, d.x);
rightBottomPoint.y = std::max(rightBottomPoint.y, b.y);
rightBottomPoint.y = std::max(rightBottomPoint.y, c.y);
rightBottomPoint.y = std::max(rightBottomPoint.y, d.y);
return {
leftTopPoint,
{rightBottomPoint.x - leftTopPoint.x,
rightBottomPoint.y - leftTopPoint.y}};
}
};
} // namespace facebook::react
namespace std {
template <>
struct hash<facebook::react::Rect> {
size_t operator()(const facebook::react::Rect& rect) const noexcept {
return facebook::react::hash_combine(rect.origin, rect.size);
}
};
} // namespace std

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <tuple>
#include <react/renderer/graphics/Float.h>
#include <react/utils/hash_combine.h>
namespace facebook::react {
/*
* Generic data structure describes some values associated with *corners*
* of a rectangle.
*/
template <typename T>
struct RectangleCorners {
T topLeft{};
T topRight{};
T bottomLeft{};
T bottomRight{};
bool operator==(const RectangleCorners<T>& rhs) const noexcept {
return std::tie(
this->topLeft,
this->topRight,
this->bottomLeft,
this->bottomRight) ==
std::tie(rhs.topLeft, rhs.topRight, rhs.bottomLeft, rhs.bottomRight);
}
bool operator!=(const RectangleCorners<T>& rhs) const noexcept {
return !(*this == rhs);
}
bool isUniform() const noexcept {
return topLeft == topRight && topLeft == bottomLeft &&
topLeft == bottomRight;
}
};
/*
* CornerInsets
*/
using CornerInsets = RectangleCorners<Float>;
} // namespace facebook::react
namespace std {
template <typename T>
struct hash<facebook::react::RectangleCorners<T>> {
size_t operator()(
const facebook::react::RectangleCorners<T>& corners) const noexcept {
return facebook::react::hash_combine(
corners.topLeft,
corners.bottomLeft,
corners.topRight,
corners.bottomRight);
}
};
} // namespace std

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <tuple>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/graphics/Rect.h>
#include <react/utils/hash_combine.h>
namespace facebook::react {
/*
* Generic data structure describes some values associated with *edges*
* of a rectangle.
*/
template <typename T>
struct RectangleEdges {
T left{};
T top{};
T right{};
T bottom{};
bool operator==(const RectangleEdges<T>& rhs) const noexcept {
return std::tie(this->left, this->top, this->right, this->bottom) ==
std::tie(rhs.left, rhs.top, rhs.right, rhs.bottom);
}
bool operator!=(const RectangleEdges<T>& rhs) const noexcept {
return !(*this == rhs);
}
bool isUniform() const noexcept {
return left == top && left == right && left == bottom;
}
static const RectangleEdges<T> ZERO;
};
template <typename T>
const RectangleEdges<T> RectangleEdges<T>::ZERO = {};
template <typename T>
RectangleEdges<T> operator+(
const RectangleEdges<T>& lhs,
const RectangleEdges<T>& rhs) noexcept {
return RectangleEdges<T>{
lhs.left + rhs.left,
lhs.top + rhs.top,
lhs.right + rhs.right,
lhs.bottom + rhs.bottom};
}
template <typename T>
RectangleEdges<T> operator-(
const RectangleEdges<T>& lhs,
const RectangleEdges<T>& rhs) noexcept {
return RectangleEdges<T>{
lhs.left - rhs.left,
lhs.top - rhs.top,
lhs.right - rhs.right,
lhs.bottom - rhs.bottom};
}
/*
* EdgeInsets
*/
using EdgeInsets = RectangleEdges<Float>;
/*
* Adjusts a rectangle by the given edge insets.
*/
inline Rect insetBy(const Rect& rect, const EdgeInsets& insets) noexcept {
return Rect{
{rect.origin.x + insets.left, rect.origin.y + insets.top},
{rect.size.width - insets.left - insets.right,
rect.size.height - insets.top - insets.bottom}};
}
/*
* Adjusts a rectangle by the given edge outsets.
*/
inline Rect outsetBy(const Rect& rect, const EdgeInsets& outsets) noexcept {
return Rect{
{rect.origin.x - outsets.left, rect.origin.y - outsets.top},
{rect.size.width + outsets.left + outsets.right,
rect.size.height + outsets.top + outsets.bottom}};
}
} // namespace facebook::react
namespace std {
template <typename T>
struct hash<facebook::react::RectangleEdges<T>> {
size_t operator()(
const facebook::react::RectangleEdges<T>& edges) const noexcept {
return facebook::react::hash_combine(
edges.left, edges.right, edges.top, edges.bottom);
}
};
} // namespace std

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <tuple>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/graphics/Point.h>
#include <react/utils/hash_combine.h>
namespace facebook::react {
/*
* Contains width and height values.
*/
struct Size {
Float width{0};
Float height{0};
Size& operator+=(const Point& point) noexcept {
width += point.x;
height += point.y;
return *this;
}
Size& operator*=(const Point& point) noexcept {
width *= point.x;
height *= point.y;
return *this;
}
};
inline bool operator==(const Size& rhs, const Size& lhs) noexcept {
return std::tie(lhs.width, lhs.height) == std::tie(rhs.width, rhs.height);
}
inline bool operator!=(const Size& rhs, const Size& lhs) noexcept {
return !(lhs == rhs);
}
} // namespace facebook::react
namespace std {
template <>
struct hash<facebook::react::Size> {
size_t operator()(const facebook::react::Size& size) const {
return facebook::react::hash_combine(size.width, size.height);
}
};
} // namespace std

View File

@@ -0,0 +1,426 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "Transform.h"
#include <cmath>
#include <glog/logging.h>
#include <react/debug/react_native_assert.h>
namespace facebook::react {
#if RN_DEBUG_STRING_CONVERTIBLE
void Transform::print(const Transform& t, std::string prefix) {
LOG(ERROR) << prefix << "[ " << t.matrix[0] << " " << t.matrix[1] << " "
<< t.matrix[2] << " " << t.matrix[3] << " ]";
LOG(ERROR) << prefix << "[ " << t.matrix[4] << " " << t.matrix[5] << " "
<< t.matrix[6] << " " << t.matrix[7] << " ]";
LOG(ERROR) << prefix << "[ " << t.matrix[8] << " " << t.matrix[9] << " "
<< t.matrix[10] << " " << t.matrix[11] << " ]";
LOG(ERROR) << prefix << "[ " << t.matrix[12] << " " << t.matrix[13] << " "
<< t.matrix[14] << " " << t.matrix[15] << " ]";
}
#endif
Transform Transform::Identity() {
return {};
}
Transform Transform::VerticalInversion() {
return Transform::Scale(1, -1, 1);
}
Transform Transform::HorizontalInversion() {
return Transform::Scale(-1, 1, 1);
}
Transform Transform::Perspective(Float perspective) {
auto transform = Transform{};
transform.operations.push_back(TransformOperation{
TransformOperationType::Perspective, perspective, 0, 0});
transform.matrix[11] = -1 / perspective;
return transform;
}
Transform Transform::Scale(Float x, Float y, Float z) {
auto transform = Transform{};
Float xprime = isZero(x) ? 0 : x;
Float yprime = isZero(y) ? 0 : y;
Float zprime = isZero(z) ? 0 : z;
if (xprime != 1 || yprime != 1 || zprime != 1) {
transform.operations.push_back(TransformOperation{
TransformOperationType::Scale, xprime, yprime, zprime});
transform.matrix[0] = xprime;
transform.matrix[5] = yprime;
transform.matrix[10] = zprime;
}
return transform;
}
Transform Transform::Translate(Float x, Float y, Float z) {
auto transform = Transform{};
Float xprime = isZero(x) ? 0 : x;
Float yprime = isZero(y) ? 0 : y;
Float zprime = isZero(z) ? 0 : z;
if (xprime != 0 || yprime != 0 || zprime != 0) {
transform.operations.push_back(TransformOperation{
TransformOperationType::Translate, xprime, yprime, zprime});
transform.matrix[12] = xprime;
transform.matrix[13] = yprime;
transform.matrix[14] = zprime;
}
return transform;
}
Transform Transform::Skew(Float x, Float y) {
auto transform = Transform{};
Float xprime = isZero(x) ? 0 : x;
Float yprime = isZero(y) ? 0 : y;
transform.operations.push_back(
TransformOperation{TransformOperationType::Skew, xprime, yprime, 0});
transform.matrix[4] = std::tan(xprime);
transform.matrix[1] = std::tan(yprime);
return transform;
}
Transform Transform::RotateX(Float radians) {
auto transform = Transform{};
if (!isZero(radians)) {
transform.operations.push_back(
TransformOperation{TransformOperationType::Rotate, radians, 0, 0});
transform.matrix[5] = std::cos(radians);
transform.matrix[6] = std::sin(radians);
transform.matrix[9] = -std::sin(radians);
transform.matrix[10] = std::cos(radians);
}
return transform;
}
Transform Transform::RotateY(Float radians) {
auto transform = Transform{};
if (!isZero(radians)) {
transform.operations.push_back(
TransformOperation{TransformOperationType::Rotate, 0, radians, 0});
transform.matrix[0] = std::cos(radians);
transform.matrix[2] = -std::sin(radians);
transform.matrix[8] = std::sin(radians);
transform.matrix[10] = std::cos(radians);
}
return transform;
}
Transform Transform::RotateZ(Float radians) {
auto transform = Transform{};
if (!isZero(radians)) {
transform.operations.push_back(
TransformOperation{TransformOperationType::Rotate, 0, 0, radians});
transform.matrix[0] = std::cos(radians);
transform.matrix[1] = std::sin(radians);
transform.matrix[4] = -std::sin(radians);
transform.matrix[5] = std::cos(radians);
}
return transform;
}
Transform Transform::Rotate(Float x, Float y, Float z) {
auto transform = Transform{};
transform.operations.push_back(
TransformOperation{TransformOperationType::Rotate, x, y, z});
if (!isZero(x)) {
transform = transform * Transform::RotateX(x);
}
if (!isZero(y)) {
transform = transform * Transform::RotateY(y);
}
if (!isZero(z)) {
transform = transform * Transform::RotateZ(z);
}
return transform;
}
Transform Transform::FromTransformOperation(
TransformOperation transformOperation) {
if (transformOperation.type == TransformOperationType::Perspective) {
return Transform::Perspective(transformOperation.x);
}
if (transformOperation.type == TransformOperationType::Scale) {
return Transform::Scale(
transformOperation.x, transformOperation.y, transformOperation.z);
}
if (transformOperation.type == TransformOperationType::Translate) {
return Transform::Translate(
transformOperation.x, transformOperation.y, transformOperation.z);
}
if (transformOperation.type == TransformOperationType::Skew) {
return Transform::Skew(transformOperation.x, transformOperation.y);
}
if (transformOperation.type == TransformOperationType::Rotate) {
return Transform::Rotate(
transformOperation.x, transformOperation.y, transformOperation.z);
}
// Identity or Arbitrary
return Transform::Identity();
}
TransformOperation Transform::DefaultTransformOperation(
TransformOperationType type) {
switch (type) {
case TransformOperationType::Arbitrary:
return TransformOperation{TransformOperationType::Arbitrary, 0, 0, 0};
case TransformOperationType::Perspective:
return TransformOperation{TransformOperationType::Perspective, 0, 0, 0};
case TransformOperationType::Scale:
return TransformOperation{TransformOperationType::Scale, 1, 1, 1};
case TransformOperationType::Translate:
return TransformOperation{TransformOperationType::Translate, 0, 0, 0};
case TransformOperationType::Rotate:
return TransformOperation{TransformOperationType::Rotate, 0, 0, 0};
case TransformOperationType::Skew:
return TransformOperation{TransformOperationType::Skew, 0, 0, 0};
default:
case TransformOperationType::Identity:
return TransformOperation{TransformOperationType::Identity, 0, 0, 0};
}
}
Transform Transform::Interpolate(
Float animationProgress,
const Transform& lhs,
const Transform& rhs) {
// Iterate through operations and reconstruct an interpolated resulting
// transform If at any point we hit an "Arbitrary" Transform, return at that
// point
Transform result = Transform::Identity();
for (size_t i = 0, j = 0;
i < lhs.operations.size() || j < rhs.operations.size();) {
bool haveLHS = i < lhs.operations.size();
bool haveRHS = j < rhs.operations.size();
if ((haveLHS &&
lhs.operations[i].type == TransformOperationType::Arbitrary) ||
(haveRHS &&
rhs.operations[i].type == TransformOperationType::Arbitrary)) {
return result;
}
if (haveLHS && lhs.operations[i].type == TransformOperationType::Identity) {
i++;
continue;
}
if (haveRHS && rhs.operations[j].type == TransformOperationType::Identity) {
j++;
continue;
}
// Here we either set:
// 1. lhs = next left op, rhs = next right op (when types are identical and
// both exist)
// 2. lhs = next left op, rhs = default of type (if types unequal, or rhs
// doesn't exist)
// 3. lhs = default of type, rhs = next right op (if types unequal, or rhs
// doesn't exist) This guarantees that the types of both sides are equal,
// and that one or both indices moves forward.
TransformOperationType type =
(haveLHS ? lhs.operations[i] : rhs.operations[j]).type;
TransformOperation lhsOp =
(haveLHS ? lhs.operations[i++]
: Transform::DefaultTransformOperation(type));
TransformOperation rhsOp =
(haveRHS && rhs.operations[j].type == type
? rhs.operations[j++]
: Transform::DefaultTransformOperation(type));
react_native_assert(type == lhsOp.type);
react_native_assert(type == rhsOp.type);
result = result *
Transform::FromTransformOperation(TransformOperation{
type,
lhsOp.x + (rhsOp.x - lhsOp.x) * animationProgress,
lhsOp.y + (rhsOp.y - lhsOp.y) * animationProgress,
lhsOp.z + (rhsOp.z - lhsOp.z) * animationProgress});
}
return result;
}
bool Transform::isVerticalInversion(const Transform& transform) {
return transform.at(1, 1) == -1;
}
bool Transform::isHorizontalInversion(const Transform& transform) {
return transform.at(0, 0) == -1;
}
bool Transform::operator==(const Transform& rhs) const {
for (auto i = 0; i < 16; i++) {
if (matrix[i] != rhs.matrix[i]) {
return false;
}
}
return true;
}
bool Transform::operator!=(const Transform& rhs) const {
return !(*this == rhs);
}
Transform Transform::operator*(const Transform& rhs) const {
if (*this == Transform::Identity()) {
return rhs;
}
const auto& lhs = *this;
auto result = Transform{};
for (const auto& op : this->operations) {
if (op.type == TransformOperationType::Identity &&
!result.operations.empty()) {
continue;
}
result.operations.push_back(op);
}
for (const auto& op : rhs.operations) {
if (op.type == TransformOperationType::Identity &&
!result.operations.empty()) {
continue;
}
result.operations.push_back(op);
}
auto lhs00 = lhs.matrix[0];
auto lhs01 = lhs.matrix[1];
auto lhs02 = lhs.matrix[2];
auto lhs03 = lhs.matrix[3];
auto lhs10 = lhs.matrix[4];
auto lhs11 = lhs.matrix[5];
auto lhs12 = lhs.matrix[6];
auto lhs13 = lhs.matrix[7];
auto lhs20 = lhs.matrix[8];
auto lhs21 = lhs.matrix[9];
auto lhs22 = lhs.matrix[10];
auto lhs23 = lhs.matrix[11];
auto lhs30 = lhs.matrix[12];
auto lhs31 = lhs.matrix[13];
auto lhs32 = lhs.matrix[14];
auto lhs33 = lhs.matrix[15];
auto rhs0 = rhs.matrix[0];
auto rhs1 = rhs.matrix[1];
auto rhs2 = rhs.matrix[2];
auto rhs3 = rhs.matrix[3];
result.matrix[0] = rhs0 * lhs00 + rhs1 * lhs10 + rhs2 * lhs20 + rhs3 * lhs30;
result.matrix[1] = rhs0 * lhs01 + rhs1 * lhs11 + rhs2 * lhs21 + rhs3 * lhs31;
result.matrix[2] = rhs0 * lhs02 + rhs1 * lhs12 + rhs2 * lhs22 + rhs3 * lhs32;
result.matrix[3] = rhs0 * lhs03 + rhs1 * lhs13 + rhs2 * lhs23 + rhs3 * lhs33;
rhs0 = rhs.matrix[4];
rhs1 = rhs.matrix[5];
rhs2 = rhs.matrix[6];
rhs3 = rhs.matrix[7];
result.matrix[4] = rhs0 * lhs00 + rhs1 * lhs10 + rhs2 * lhs20 + rhs3 * lhs30;
result.matrix[5] = rhs0 * lhs01 + rhs1 * lhs11 + rhs2 * lhs21 + rhs3 * lhs31;
result.matrix[6] = rhs0 * lhs02 + rhs1 * lhs12 + rhs2 * lhs22 + rhs3 * lhs32;
result.matrix[7] = rhs0 * lhs03 + rhs1 * lhs13 + rhs2 * lhs23 + rhs3 * lhs33;
rhs0 = rhs.matrix[8];
rhs1 = rhs.matrix[9];
rhs2 = rhs.matrix[10];
rhs3 = rhs.matrix[11];
result.matrix[8] = rhs0 * lhs00 + rhs1 * lhs10 + rhs2 * lhs20 + rhs3 * lhs30;
result.matrix[9] = rhs0 * lhs01 + rhs1 * lhs11 + rhs2 * lhs21 + rhs3 * lhs31;
result.matrix[10] = rhs0 * lhs02 + rhs1 * lhs12 + rhs2 * lhs22 + rhs3 * lhs32;
result.matrix[11] = rhs0 * lhs03 + rhs1 * lhs13 + rhs2 * lhs23 + rhs3 * lhs33;
rhs0 = rhs.matrix[12];
rhs1 = rhs.matrix[13];
rhs2 = rhs.matrix[14];
rhs3 = rhs.matrix[15];
result.matrix[12] = rhs0 * lhs00 + rhs1 * lhs10 + rhs2 * lhs20 + rhs3 * lhs30;
result.matrix[13] = rhs0 * lhs01 + rhs1 * lhs11 + rhs2 * lhs21 + rhs3 * lhs31;
result.matrix[14] = rhs0 * lhs02 + rhs1 * lhs12 + rhs2 * lhs22 + rhs3 * lhs32;
result.matrix[15] = rhs0 * lhs03 + rhs1 * lhs13 + rhs2 * lhs23 + rhs3 * lhs33;
return result;
}
Float& Transform::at(int i, int j) {
return matrix[(i * 4) + j];
}
const Float& Transform::at(int i, int j) const {
return matrix[(i * 4) + j];
}
Point operator*(const Point& point, const Transform& transform) {
if (transform == Transform::Identity()) {
return point;
}
auto result = transform * Vector{point.x, point.y, 0, 1};
return {result.x, result.y};
}
Rect operator*(const Rect& rect, const Transform& transform) {
auto center = rect.getCenter();
return transform.applyWithCenter(rect, center);
}
Rect Transform::applyWithCenter(const Rect& rect, const Point& center) const {
auto a = Point{rect.origin.x, rect.origin.y} - center;
auto b = Point{rect.getMaxX(), rect.origin.y} - center;
auto c = Point{rect.getMaxX(), rect.getMaxY()} - center;
auto d = Point{rect.origin.x, rect.getMaxY()} - center;
auto vectorA = *this * Vector{a.x, a.y, 0, 1};
auto vectorB = *this * Vector{b.x, b.y, 0, 1};
auto vectorC = *this * Vector{c.x, c.y, 0, 1};
auto vectorD = *this * Vector{d.x, d.y, 0, 1};
Point transformedA{vectorA.x + center.x, vectorA.y + center.y};
Point transformedB{vectorB.x + center.x, vectorB.y + center.y};
Point transformedC{vectorC.x + center.x, vectorC.y + center.y};
Point transformedD{vectorD.x + center.x, vectorD.y + center.y};
return Rect::boundingRect(
transformedA, transformedB, transformedC, transformedD);
}
EdgeInsets operator*(const EdgeInsets& edgeInsets, const Transform& transform) {
return EdgeInsets{
edgeInsets.left * transform.matrix[0],
edgeInsets.top * transform.matrix[5],
edgeInsets.right * transform.matrix[0],
edgeInsets.bottom * transform.matrix[5]};
}
Vector operator*(const Transform& transform, const Vector& vector) {
return {
vector.x * transform.at(0, 0) + vector.y * transform.at(1, 0) +
vector.z * transform.at(2, 0) + vector.w * transform.at(3, 0),
vector.x * transform.at(0, 1) + vector.y * transform.at(1, 1) +
vector.z * transform.at(2, 1) + vector.w * transform.at(3, 1),
vector.x * transform.at(0, 2) + vector.y * transform.at(1, 2) +
vector.z * transform.at(2, 2) + vector.w * transform.at(3, 2),
vector.x * transform.at(0, 3) + vector.y * transform.at(1, 3) +
vector.z * transform.at(2, 3) + vector.w * transform.at(3, 3),
};
}
Size operator*(const Size& size, const Transform& transform) {
if (transform == Transform::Identity()) {
return size;
}
auto result = Size{};
result.width = std::abs(transform.at(0, 0) * size.width);
result.height = std::abs(transform.at(1, 1) * size.height);
return result;
}
} // namespace facebook::react

View File

@@ -0,0 +1,255 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <array>
#include <vector>
#include <react/renderer/graphics/Float.h>
#include <react/renderer/graphics/Point.h>
#include <react/renderer/graphics/RectangleEdges.h>
#include <react/renderer/graphics/Size.h>
#include <react/renderer/graphics/ValueUnit.h>
#include <react/renderer/graphics/Vector.h>
#include <react/utils/hash_combine.h>
#ifdef ANDROID
#include <folly/dynamic.h>
#endif
namespace facebook::react {
inline bool isZero(Float n) {
// We use this ternary expression instead of abs, fabsf, etc, because
// Float can be double or float depending on compilation target.
return (n < 0 ? n * (-1) : n) < 0.00001;
}
/**
* Defines operations used to construct a transform matrix.
* An "Arbitrary" operation means that the transform was seeded with some
* arbitrary initial result.
*/
enum class TransformOperationType {
Arbitrary,
Identity,
Perspective,
Scale,
Translate,
Rotate,
Skew
};
struct TransformOperation {
TransformOperationType type;
Float x;
Float y;
Float z;
};
struct TransformOrigin {
std::array<ValueUnit, 2> xy;
float z = 0.0f;
bool operator==(const TransformOrigin& other) const {
return xy[0] == other.xy[0] && xy[1] == other.xy[1] && z == other.z;
}
bool operator!=(const TransformOrigin& other) const {
return !(*this == other);
}
bool isSet() const {
return xy[0].value != 0.0f || xy[0].unit != UnitType::Undefined ||
xy[1].value != 0.0f || xy[1].unit != UnitType::Undefined || z != 0.0f;
}
};
/*
* Defines transform matrix to apply affine transformations.
*/
struct Transform {
std::vector<TransformOperation> operations{};
std::array<Float, 16> matrix{
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}};
/**
* For debugging only. Prints out the matrix.
*/
#if RN_DEBUG_STRING_CONVERTIBLE
static void print(const Transform& t, std::string prefix);
#endif
/*
* Given a TransformOperation, return the proper transform.
*/
static Transform FromTransformOperation(
TransformOperation transformOperation);
static TransformOperation DefaultTransformOperation(
TransformOperationType type);
/*
* Returns the identity transform (`[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]`).
*/
static Transform Identity();
/*
* Returns the vertival inversion transform (`[1 0 0 0; 0 -1 0 0; 0 0 1 0; 0 0
* 0 1]`).
*/
static Transform VerticalInversion();
/*
* Returns the horizontal inversion transform (`[-1 0 0 0; 0 1 0 0; 0 0 1 0; 0
* 0 0 1]`).
*/
static Transform HorizontalInversion();
/*
* Returns a Perspective transform.
*/
static Transform Perspective(Float perspective);
/*
* Returns a Scale transform.
*/
static Transform Scale(Float factorX, Float factorY, Float factorZ);
/*
* Returns a Translate transform.
*/
static Transform Translate(Float x, Float y, Float z);
/*
* Returns a Skew transform.
*/
static Transform Skew(Float x, Float y);
/*
* Returns a transform that rotates by `angle` radians along the given axis.
*/
static Transform RotateX(Float radians);
static Transform RotateY(Float radians);
static Transform RotateZ(Float radians);
static Transform Rotate(Float angleX, Float angleY, Float angleZ);
/**
* Perform an interpolation between lhs and rhs, given progress.
* This first decomposes the matrices into translation, scale, and rotation,
* performs slerp between the two rotations, and a linear interpolation
* of scale and translation.
*
* @param progress
* @param lhs
* @param rhs
* @return
*/
static Transform Interpolate(
Float animationProgress,
const Transform& lhs,
const Transform& rhs);
static bool isVerticalInversion(const Transform& transform);
static bool isHorizontalInversion(const Transform& transform);
/*
* Equality operators.
*/
bool operator==(const Transform& rhs) const;
bool operator!=(const Transform& rhs) const;
/*
* Matrix subscript.
*/
Float& at(int i, int j);
const Float& at(int i, int j) const;
/*
* Concatenates (multiplies) transform matrices.
*/
Transform operator*(const Transform& rhs) const;
Rect applyWithCenter(const Rect& rect, const Point& center) const;
/**
* Convert to folly::dynamic.
*/
#ifdef ANDROID
operator folly::dynamic() const {
return folly::dynamic::array(
matrix[0],
matrix[1],
matrix[2],
matrix[3],
matrix[4],
matrix[5],
matrix[6],
matrix[7],
matrix[8],
matrix[9],
matrix[10],
matrix[11],
matrix[12],
matrix[13],
matrix[14],
matrix[15]);
}
#endif
};
/*
* Applies transformation to the given point.
*/
Point operator*(const Point& point, const Transform& transform);
/*
* Applies transformation to the given size.
*/
Size operator*(const Size& size, const Transform& transform);
/*
* Applies transformation to the given rect.
* ONLY SUPPORTS scale and translation transformation.
*/
Rect operator*(const Rect& rect, const Transform& transform);
/*
* Applies transformation to the given EdgeInsets.
* ONLY SUPPORTS scale transformation.
*/
EdgeInsets operator*(const EdgeInsets& edgeInsets, const Transform& transform);
Vector operator*(const Transform& transform, const Vector& vector);
} // namespace facebook::react
namespace std {
template <>
struct hash<facebook::react::Transform> {
size_t operator()(const facebook::react::Transform& transform) const {
return facebook::react::hash_combine(
transform.matrix[0],
transform.matrix[1],
transform.matrix[2],
transform.matrix[3],
transform.matrix[4],
transform.matrix[5],
transform.matrix[6],
transform.matrix[7],
transform.matrix[8],
transform.matrix[9],
transform.matrix[10],
transform.matrix[11],
transform.matrix[12],
transform.matrix[13],
transform.matrix[14],
transform.matrix[15]);
}
};
} // namespace std

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook::react {
enum class UnitType {
Undefined,
Point,
Percent,
};
struct ValueUnit {
float value{0.0f};
UnitType unit{UnitType::Undefined};
ValueUnit() = default;
ValueUnit(float v, UnitType u) : value(v), unit(u) {}
bool operator==(const ValueUnit& other) const {
return value == other.value && unit == other.unit;
}
bool operator!=(const ValueUnit& other) const {
return !(*this == other);
}
};
} // namespace facebook::react

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/graphics/Float.h>
namespace facebook::react {
struct Vector {
Float x{0};
Float y{0};
Float z{0};
Float w{0};
};
} // namespace facebook::react

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/core/graphicsConversions.h>
// This file belongs to the React-graphics module.
// This file also used to have a reference to two files that are located in the
// react/renderer/core folder. That folder belongs to a module that is called
// React-Fabric.
// The React-Fabric module declares an explicit dependency on
// React-graphics. Including those files in a React-graphics' file created a
// circular dependency because React-Fabric was explicitly depending on
// React-graphics, which was implicitly depending on React-Fabric. We break that
// dependency by moving the old `graphics/conversions.h` file to the
// React-Fabric module and renaming it `core/graphicsConversions.h`.
#warning \
"[DEPRECATION] `graphics/conversions.h` is deprecated and will be removed in the future. \
If this warning appears due to a library, please open an issue in that library, and ask for an update. \
Please, replace the `#include <react/renderer/graphics/conversions.h>` statements \
with `#include <react/renderer/core/graphicsConversions.h>`."

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <react/debug/react_native_expect.h>
#include <react/renderer/core/RawValue.h>
#include <react/renderer/graphics/Color.h>
#include <react/utils/ContextContainer.h>
#pragma once
namespace facebook::react {
using parsePlatformColorFn =
SharedColor (*)(const ContextContainer&, int32_t, const RawValue&);
inline void fromRawValueShared(
const ContextContainer& contextContainer,
int32_t surfaceId,
const RawValue& value,
SharedColor& result,
parsePlatformColorFn parsePlatformColor) {
ColorComponents colorComponents = {0, 0, 0, 0};
if (value.hasType<int>()) {
auto argb = (int64_t)value;
auto ratio = 255.f;
colorComponents.alpha = ((argb >> 24) & 0xFF) / ratio;
colorComponents.red = ((argb >> 16) & 0xFF) / ratio;
colorComponents.green = ((argb >> 8) & 0xFF) / ratio;
colorComponents.blue = (argb & 0xFF) / ratio;
result = colorFromComponents(colorComponents);
} else if (value.hasType<std::vector<float>>()) {
auto items = (std::vector<float>)value;
auto length = items.size();
react_native_expect(length == 3 || length == 4);
colorComponents.red = items.at(0);
colorComponents.green = items.at(1);
colorComponents.blue = items.at(2);
colorComponents.alpha = length == 4 ? items.at(3) : 1.0f;
result = colorFromComponents(colorComponents);
} else {
result = parsePlatformColor(contextContainer, surfaceId, value);
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <limits>
namespace facebook::react {
/*
* Exact type of float numbers which ideally should match a type behing
* platform- and chip-architecture-specific float type.
*/
using Float = float;
} // namespace facebook::react

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/graphics/ColorComponents.h>
#include <cmath>
namespace facebook::react {
using Color = int32_t;
namespace HostPlatformColor {
static const facebook::react::Color UndefinedColor =
std::numeric_limits<facebook::react::Color>::max();
}
inline Color hostPlatformColorFromComponents(ColorComponents components) {
float ratio = 255;
return ((int)round(components.alpha * ratio) & 0xff) << 24 |
((int)round(components.red * ratio) & 0xff) << 16 |
((int)round(components.green * ratio) & 0xff) << 8 |
((int)round(components.blue * ratio) & 0xff);
}
inline ColorComponents colorComponentsFromHostPlatformColor(Color color) {
float ratio = 255;
return ColorComponents{
(float)((color >> 16) & 0xff) / ratio,
(float)((color >> 8) & 0xff) / ratio,
(float)((color >> 0) & 0xff) / ratio,
(float)((color >> 24) & 0xff) / ratio};
}
} // namespace facebook::react

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fbjni/fbjni.h>
#include <react/debug/react_native_expect.h>
#include <react/renderer/core/RawValue.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/fromRawValueShared.h>
#include <react/utils/ContextContainer.h>
#include <unordered_map>
namespace facebook::react {
inline SharedColor parsePlatformColor(
const ContextContainer& contextContainer,
int32_t surfaceId,
const RawValue& value) {
ColorComponents colorComponents = {0, 0, 0, 0};
if (value.hasType<
std::unordered_map<std::string, std::vector<std::string>>>()) {
const auto& fabricUIManager =
contextContainer.at<jni::global_ref<jobject>>("FabricUIManager");
static auto getColorFromJava =
fabricUIManager->getClass()
->getMethod<jint(jint, jni::JArrayClass<jni::JString>)>("getColor");
auto map = (std::unordered_map<std::string, std::vector<std::string>>)value;
auto& resourcePaths = map["resource_paths"];
auto javaResourcePaths =
jni::JArrayClass<jni::JString>::newArray(resourcePaths.size());
for (int i = 0; i < resourcePaths.size(); i++) {
javaResourcePaths->setElement(i, *jni::make_jstring(resourcePaths[i]));
}
auto color =
getColorFromJava(fabricUIManager, surfaceId, *javaResourcePaths);
auto argb = (int64_t)color;
auto ratio = 255.f;
colorComponents.alpha = ((argb >> 24) & 0xFF) / ratio;
colorComponents.red = ((argb >> 16) & 0xFF) / ratio;
colorComponents.green = ((argb >> 8) & 0xFF) / ratio;
colorComponents.blue = (argb & 0xFF) / ratio;
}
return {colorFromComponents(colorComponents)};
}
inline void fromRawValue(
const ContextContainer& contextContainer,
int32_t surfaceId,
const RawValue& value,
SharedColor& result) {
fromRawValueShared(
contextContainer, surfaceId, value, result, parsePlatformColor);
}
} // namespace facebook::react

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <limits>
namespace facebook::react {
/*
* Exact type of float numbers which ideally should match a type behing
* platform- and chip-architecture-specific float type.
*/
using Float = float;
} // namespace facebook::react

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/graphics/ColorComponents.h>
#include <cmath>
namespace facebook::react {
using Color = int32_t;
namespace HostPlatformColor {
static const facebook::react::Color UndefinedColor =
std::numeric_limits<facebook::react::Color>::max();
}
inline Color hostPlatformColorFromComponents(ColorComponents components) {
float ratio = 255;
return ((int)std::round(components.alpha * ratio) & 0xff) << 24 |
((int)std::round(components.red * ratio) & 0xff) << 16 |
((int)std::round(components.green * ratio) & 0xff) << 8 |
((int)std::round(components.blue * ratio) & 0xff);
}
inline ColorComponents colorComponentsFromHostPlatformColor(Color color) {
float ratio = 255;
return ColorComponents{
(float)((color >> 16) & 0xff) / ratio,
(float)((color >> 8) & 0xff) / ratio,
(float)((color >> 0) & 0xff) / ratio,
(float)((color >> 24) & 0xff) / ratio};
}
} // namespace facebook::react

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/debug/react_native_expect.h>
#include <react/renderer/core/RawValue.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/fromRawValueShared.h>
#include <react/utils/ContextContainer.h>
namespace facebook::react {
inline SharedColor parsePlatformColor(
const ContextContainer& /*contextContainer*/,
int32_t /*surfaceId*/,
const RawValue& /*value*/) {
float alpha = 0;
float red = 0;
float green = 0;
float blue = 0;
return {colorFromComponents({red, green, blue, alpha})};
}
inline void fromRawValue(
const ContextContainer& contextContainer,
int32_t surfaceId,
const RawValue& value,
SharedColor& result) {
fromRawValueShared(
contextContainer, surfaceId, value, result, parsePlatformColor);
}
} // namespace facebook::react

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <CoreGraphics/CoreGraphics.h>
#include <limits>
namespace facebook::react {
/*
* Exact type of float numbers which ideally should match a type behing
* platform- and chip-architecture-specific float type.
*/
using Float = CGFloat;
} // namespace facebook::react

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/graphics/ColorComponents.h>
#include <react/utils/hash_combine.h>
#include <cmath>
namespace facebook::react {
struct DynamicColor {
int32_t lightColor = 0;
int32_t darkColor = 0;
int32_t highContrastLightColor = 0;
int32_t highContrastDarkColor = 0;
};
struct Color {
Color(int32_t color);
Color(const DynamicColor& dynamicColor);
Color(const ColorComponents& components);
Color(std::shared_ptr<void> uiColor);
int32_t getColor() const;
std::shared_ptr<void> getUIColor() const {
return uiColor_;
}
ColorComponents getColorComponents() const {
float ratio = 255;
int32_t primitiveColor = getColor();
return ColorComponents{
(float)((primitiveColor >> 16) & 0xff) / ratio,
(float)((primitiveColor >> 8) & 0xff) / ratio,
(float)((primitiveColor >> 0) & 0xff) / ratio,
(float)((primitiveColor >> 24) & 0xff) / ratio};
}
bool operator==(const Color& other) const;
bool operator!=(const Color& other) const;
operator int32_t() const {
return getColor();
}
private:
std::shared_ptr<void> uiColor_;
};
namespace HostPlatformColor {
#if defined(__clang__)
#define NO_DESTROY [[clang::no_destroy]]
#else
#define NO_DESTROY
#endif
NO_DESTROY static const facebook::react::Color UndefinedColor = Color(nullptr);
} // namespace HostPlatformColor
inline Color hostPlatformColorFromComponents(ColorComponents components) {
return Color(components);
}
inline ColorComponents colorComponentsFromHostPlatformColor(Color color) {
return color.getColorComponents();
}
} // namespace facebook::react
template <>
struct std::hash<facebook::react::Color> {
size_t operator()(const facebook::react::Color& color) const {
auto seed = size_t{0};
facebook::react::hash_combine(seed, color.getColor());
return seed;
}
};

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "HostPlatformColor.h"
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <react/utils/ManagedObjectWrapper.h>
#import <string>
using namespace facebook::react;
NS_ASSUME_NONNULL_BEGIN
namespace facebook::react {
namespace {
UIColor *_Nullable UIColorFromInt32(int32_t intColor)
{
CGFloat a = CGFloat((intColor >> 24) & 0xFF) / 255.0;
CGFloat r = CGFloat((intColor >> 16) & 0xFF) / 255.0;
CGFloat g = CGFloat((intColor >> 8) & 0xFF) / 255.0;
CGFloat b = CGFloat(intColor & 0xFF) / 255.0;
return [UIColor colorWithRed:r green:g blue:b alpha:a];
}
UIColor *_Nullable UIColorFromDynamicColor(const facebook::react::DynamicColor &dynamicColor)
{
int32_t light = dynamicColor.lightColor;
int32_t dark = dynamicColor.darkColor;
int32_t highContrastLight = dynamicColor.highContrastLightColor;
int32_t highContrastDark = dynamicColor.highContrastDarkColor;
UIColor *lightColor = UIColorFromInt32(light);
UIColor *darkColor = UIColorFromInt32(dark);
UIColor *highContrastLightColor = UIColorFromInt32(highContrastLight);
UIColor *highContrastDarkColor = UIColorFromInt32(highContrastDark);
if (lightColor != nil && darkColor != nil) {
UIColor *color = [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull collection) {
if (collection.userInterfaceStyle == UIUserInterfaceStyleDark) {
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastDarkColor != nil) {
return highContrastDarkColor;
} else {
return darkColor;
}
} else {
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastLightColor != nil) {
return highContrastLightColor;
} else {
return lightColor;
}
}
}];
return color;
} else {
return nil;
}
return nil;
}
int32_t ColorFromUIColor(UIColor *color)
{
float ratio = 255;
CGFloat rgba[4];
[color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]];
return ((int32_t)round((float)rgba[3] * ratio) & 0xff) << 24 | ((int)round((float)rgba[0] * ratio) & 0xff) << 16 |
((int)round((float)rgba[1] * ratio) & 0xff) << 8 | ((int)round((float)rgba[2] * ratio) & 0xff);
}
int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor)
{
UIColor *color = (UIColor *)unwrapManagedObject(uiColor);
if (color) {
UITraitCollection *currentTraitCollection = [UITraitCollection currentTraitCollection];
color = [color resolvedColorWithTraitCollection:currentTraitCollection];
return ColorFromUIColor(color);
}
return 0;
}
UIColor *_Nullable UIColorFromComponentsColor(const facebook::react::ColorComponents &components)
{
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
}
} // anonymous namespace
Color::Color(int32_t color)
{
uiColor_ = wrapManagedObject(UIColorFromInt32(color));
}
Color::Color(const DynamicColor &dynamicColor)
{
uiColor_ = wrapManagedObject(UIColorFromDynamicColor(dynamicColor));
}
Color::Color(const ColorComponents &components)
{
uiColor_ = wrapManagedObject(UIColorFromComponentsColor(components));
}
Color::Color(std::shared_ptr<void> uiColor)
{
uiColor_ = std::move(uiColor);
}
bool Color::operator==(const Color &other) const
{
return (!uiColor_ && !other.uiColor_) ||
(uiColor_ && other.uiColor_ &&
[unwrapManagedObject(getUIColor()) isEqual:unwrapManagedObject(other.getUIColor())]);
}
bool Color::operator!=(const Color &other) const
{
return !(*this == other);
}
int32_t Color::getColor() const
{
return ColorFromUIColor(uiColor_);
}
} // namespace facebook::react
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/debug/react_native_expect.h>
#include <react/renderer/core/RawValue.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/fromRawValueShared.h>
#include <react/utils/ContextContainer.h>
namespace facebook::react {
SharedColor parsePlatformColor(
const ContextContainer& contextContainer,
int32_t surfaceId,
const RawValue& value);
inline void fromRawValue(
const ContextContainer& contextContainer,
int32_t surfaceId,
const RawValue& value,
SharedColor& result) {
fromRawValueShared(
contextContainer, surfaceId, value, result, parsePlatformColor);
}
} // namespace facebook::react

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "PlatformColorParser.h"
#import <react/renderer/core/RawValue.h>
#import <react/renderer/graphics/HostPlatformColor.h>
#import <react/renderer/graphics/RCTPlatformColorUtils.h>
#import <react/utils/ManagedObjectWrapper.h>
#import <string>
#import <unordered_map>
using namespace facebook::react;
NS_ASSUME_NONNULL_BEGIN
namespace facebook::react {
inline facebook::react::SharedColor RCTPlatformColorComponentsFromDynamicItems(
const facebook::react::ContextContainer &contextContainer,
int32_t surfaceId,
std::unordered_map<std::string, facebook::react::RawValue> &dynamicItems)
{
SharedColor lightSharedColor{};
SharedColor darkSharedColor{};
SharedColor highContrastLightSharedColor{};
SharedColor highContrastDarkSharedColor{};
if (dynamicItems.count("light")) {
fromRawValue(contextContainer, surfaceId, dynamicItems.at("light"), lightSharedColor);
}
if (dynamicItems.count("dark")) {
fromRawValue(contextContainer, surfaceId, dynamicItems.at("dark"), darkSharedColor);
}
if (dynamicItems.count("highContrastLight")) {
fromRawValue(contextContainer, surfaceId, dynamicItems.at("highContrastLight"), highContrastLightSharedColor);
}
if (dynamicItems.count("highContrastDark")) {
fromRawValue(contextContainer, surfaceId, dynamicItems.at("highContrastDark"), highContrastDarkSharedColor);
}
Color color = Color(DynamicColor{
(*lightSharedColor).getColor(),
(*darkSharedColor).getColor(),
(*highContrastLightSharedColor).getColor(),
(*highContrastDarkSharedColor).getColor()});
return SharedColor(color);
}
SharedColor parsePlatformColor(const ContextContainer &contextContainer, int32_t surfaceId, const RawValue &value)
{
if (value.hasType<std::unordered_map<std::string, RawValue>>()) {
auto items = (std::unordered_map<std::string, RawValue>)value;
if (items.find("semantic") != items.end() && items.at("semantic").hasType<std::vector<std::string>>()) {
auto semanticItems = (std::vector<std::string>)items.at("semantic");
return {wrapManagedObject(RCTPlatformColorFromSemanticItems(semanticItems))};
} else if (
items.find("dynamic") != items.end() &&
items.at("dynamic").hasType<std::unordered_map<std::string, RawValue>>()) {
auto dynamicItems = (std::unordered_map<std::string, RawValue>)items.at("dynamic");
return RCTPlatformColorComponentsFromDynamicItems(contextContainer, surfaceId, dynamicItems);
}
}
return clearColor();
}
} // namespace facebook::react
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#import <UIKit/UIKit.h>
#import <react/renderer/graphics/HostPlatformColor.h>
#import <vector>
facebook::react::ColorComponents RCTPlatformColorComponentsFromSemanticItems(
std::vector<std::string>& semanticItems);
UIColor* RCTPlatformColorFromSemanticItems(
std::vector<std::string>& semanticItems);
UIColor* RCTPlatformColorFromColor(const facebook::react::Color& color);

View File

@@ -0,0 +1,214 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTPlatformColorUtils.h"
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <react/utils/ManagedObjectWrapper.h>
#include <string>
NS_ASSUME_NONNULL_BEGIN
static NSString *const kColorSuffix = @"Color";
static NSString *const kFallbackARGBKey = @"fallback-argb";
static NSDictionary<NSString *, NSDictionary *> *_PlatformColorSelectorsDict()
{
static NSDictionary<NSString *, NSDictionary *> *dict;
static dispatch_once_t once_token;
dispatch_once(&once_token, ^(void) {
dict = @{
// https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors
// Label Colors
@"label" : @{
kFallbackARGBKey : @(0xFF000000) // iOS 13.0
},
@"secondaryLabel" : @{
kFallbackARGBKey : @(0x993c3c43) // iOS 13.0
},
@"tertiaryLabel" : @{
kFallbackARGBKey : @(0x4c3c3c43) // iOS 13.0
},
@"quaternaryLabel" : @{
kFallbackARGBKey : @(0x2d3c3c43) // iOS 13.0
},
// Fill Colors
@"systemFill" : @{
kFallbackARGBKey : @(0x33787880) // iOS 13.0
},
@"secondarySystemFill" : @{
kFallbackARGBKey : @(0x28787880) // iOS 13.0
},
@"tertiarySystemFill" : @{
kFallbackARGBKey : @(0x1e767680) // iOS 13.0
},
@"quaternarySystemFill" : @{
kFallbackARGBKey : @(0x14747480) // iOS 13.0
},
// Text Colors
@"placeholderText" : @{
kFallbackARGBKey : @(0x4c3c3c43) // iOS 13.0
},
// Standard Content Background Colors
@"systemBackground" : @{
kFallbackARGBKey : @(0xFFffffff) // iOS 13.0
},
@"secondarySystemBackground" : @{
kFallbackARGBKey : @(0xFFf2f2f7) // iOS 13.0
},
@"tertiarySystemBackground" : @{
kFallbackARGBKey : @(0xFFffffff) // iOS 13.0
},
// Grouped Content Background Colors
@"systemGroupedBackground" : @{
kFallbackARGBKey : @(0xFFf2f2f7) // iOS 13.0
},
@"secondarySystemGroupedBackground" : @{
kFallbackARGBKey : @(0xFFffffff) // iOS 13.0
},
@"tertiarySystemGroupedBackground" : @{
kFallbackARGBKey : @(0xFFf2f2f7) // iOS 13.0
},
// Separator Colors
@"separator" : @{
kFallbackARGBKey : @(0x493c3c43) // iOS 13.0
},
@"opaqueSeparator" : @{
kFallbackARGBKey : @(0xFFc6c6c8) // iOS 13.0
},
// Link Color
@"link" : @{
kFallbackARGBKey : @(0xFF007aff) // iOS 13.0
},
// Nonadaptable Colors
@"darkText" : @{},
@"lightText" : @{},
// https://developer.apple.com/documentation/uikit/uicolor/standard_colors
// Adaptable Colors
@"systemBlue" : @{},
@"systemBrown" : @{
kFallbackARGBKey : @(0xFFa2845e) // iOS 13.0
},
@"systemGreen" : @{},
@"systemIndigo" : @{
kFallbackARGBKey : @(0xFF5856d6) // iOS 13.0
},
@"systemOrange" : @{},
@"systemPink" : @{},
@"systemPurple" : @{},
@"systemRed" : @{},
@"systemTeal" : @{},
@"systemYellow" : @{},
// Adaptable Gray Colors
@"systemGray" : @{},
@"systemGray2" : @{
kFallbackARGBKey : @(0xFFaeaeb2) // iOS 13.0
},
@"systemGray3" : @{
kFallbackARGBKey : @(0xFFc7c7cc) // iOS 13.0
},
@"systemGray4" : @{
kFallbackARGBKey : @(0xFFd1d1d6) // iOS 13.0
},
@"systemGray5" : @{
kFallbackARGBKey : @(0xFFe5e5ea) // iOS 13.0
},
@"systemGray6" : @{
kFallbackARGBKey : @(0xFFf2f2f7) // iOS 13.0
},
// Transparent Color
@"clear" : @{
kFallbackARGBKey : @(0x00000000) // iOS 13.0
},
};
});
return dict;
}
static UIColor *_UIColorFromHexValue(NSNumber *hexValue)
{
NSUInteger hexIntValue = [hexValue unsignedIntegerValue];
CGFloat red = ((CGFloat)((hexIntValue & 0xFF000000) >> 24)) / 255.0;
CGFloat green = ((CGFloat)((hexIntValue & 0xFF0000) >> 16)) / 255.0;
CGFloat blue = ((CGFloat)((hexIntValue & 0xFF00) >> 8)) / 255.0;
CGFloat alpha = ((CGFloat)(hexIntValue & 0xFF)) / 255.0;
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}
static UIColor *_Nullable _UIColorFromSemanticString(NSString *semanticString)
{
NSString *platformColorString = [semanticString hasSuffix:kColorSuffix]
? [semanticString substringToIndex:[semanticString length] - [kColorSuffix length]]
: semanticString;
NSDictionary<NSString *, NSDictionary *> *platformColorSelectorsDict = _PlatformColorSelectorsDict();
NSDictionary<NSString *, id> *colorInfo = platformColorSelectorsDict[platformColorString];
if (colorInfo) {
SEL objcColorSelector = NSSelectorFromString([platformColorString stringByAppendingString:kColorSuffix]);
if (![UIColor respondsToSelector:objcColorSelector]) {
NSNumber *fallbackRGB = colorInfo[kFallbackARGBKey];
if (fallbackRGB) {
return _UIColorFromHexValue(fallbackRGB);
}
} else {
Class uiColorClass = [UIColor class];
IMP imp = [uiColorClass methodForSelector:objcColorSelector];
id (*getUIColor)(id, SEL) = ((id(*)(id, SEL))imp);
id colorObject = getUIColor(uiColorClass, objcColorSelector);
if ([colorObject isKindOfClass:[UIColor class]]) {
return colorObject;
}
}
}
return nil;
}
static inline NSString *_NSStringFromCString(
const std::string &string,
const NSStringEncoding &encoding = NSUTF8StringEncoding)
{
return [NSString stringWithCString:string.c_str() encoding:encoding];
}
static inline facebook::react::ColorComponents _ColorComponentsFromUIColor(UIColor *color)
{
CGFloat rgba[4];
[color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]];
return {(float)rgba[0], (float)rgba[1], (float)rgba[2], (float)rgba[3]};
}
facebook::react::ColorComponents RCTPlatformColorComponentsFromSemanticItems(std::vector<std::string> &semanticItems)
{
return _ColorComponentsFromUIColor(RCTPlatformColorFromSemanticItems(semanticItems));
}
UIColor *RCTPlatformColorFromSemanticItems(std::vector<std::string> &semanticItems)
{
for (const auto &semanticCString : semanticItems) {
NSString *semanticNSString = _NSStringFromCString(semanticCString);
UIColor *uiColor = [UIColor colorNamed:semanticNSString];
if (uiColor != nil) {
return uiColor;
}
uiColor = _UIColorFromSemanticString(semanticNSString);
if (uiColor != nil) {
return uiColor;
}
}
return UIColor.clearColor;
}
UIColor *RCTPlatformColorFromColor(const facebook::react::Color &color)
{
return (UIColor *)facebook::react::unwrapManagedObject(color.getUIColor());
}
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/graphics/Float.h>
namespace facebook::react {
/*
* Convenience functions for rounding float values to be aligned with a device
* pixel grid.
*
* Usage example:
* auto scaleFactor = Float{3};
* auto value = Float{42.9001};
* auto crispValue = roundToPixel<&std::ceil>(value, scaleFactor);
* auto size = Size{value, value + 42.0};
* auto crispSize = roundToPixel<&std::ceil>(size, scaleFactor);
*/
template <Float (*RoundingFunction)(Float)>
Float roundToPixel(Float value, Float scaleFactor) {
return RoundingFunction(value * scaleFactor) / scaleFactor;
}
template <Float (*RoundingFunction)(Float)>
Point roundToPixel(Point value, Float scaleFactor) {
return Point{
roundToPixel<RoundingFunction>(value.x, scaleFactor),
roundToPixel<RoundingFunction>(value.y, scaleFactor)};
}
template <Float (*RoundingFunction)(Float)>
Size roundToPixel(Size value, Float scaleFactor) {
return Size{
roundToPixel<RoundingFunction>(value.width, scaleFactor),
roundToPixel<RoundingFunction>(value.height, scaleFactor)};
}
template <Float (*RoundingFunction)(Float)>
Rect roundToPixel(Rect value, Float scaleFactor) {
return Rect{
roundToPixel<RoundingFunction>(value.origin),
roundToPixel<RoundingFunction>(value.size)};
}
/*
* GCC-based Android NDK does not have rounding functions as part of STL.
*/
inline float round(float value) noexcept {
return ::roundf(value);
}
inline double round(double value) noexcept {
return ::round(value);
}
inline long double round(long double value) noexcept {
return ::roundl(value);
}
inline float ceil(float value) noexcept {
return ::ceilf(value);
}
inline double ceil(double value) noexcept {
return ::ceil(value);
}
inline long double ceil(long double value) noexcept {
return ::ceill(value);
}
inline float floor(float value) noexcept {
return ::floorf(value);
}
inline double floor(double value) noexcept {
return ::floor(value);
}
inline long double floor(long double value) noexcept {
return ::floorl(value);
}
} // namespace facebook::react

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <gtest/gtest.h>
TEST(GraphicsTest, testSomething) {
// TODO
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <react/renderer/graphics/Transform.h>
#include <gtest/gtest.h>
#include <cmath>
using namespace facebook::react;
TEST(TransformTest, transformingSize) {
auto size = facebook::react::Size{100, 200};
auto scaledSize = size * Transform::Scale(0.5, 0.5, 1);
EXPECT_EQ(scaledSize.width, 50);
EXPECT_EQ(scaledSize.height, 100);
}
TEST(TransformTest, transformingPoint) {
auto point = facebook::react::Point{100, 200};
auto translatedPoint = point * Transform::Translate(-50, -100, 0);
EXPECT_EQ(translatedPoint.x, 50);
EXPECT_EQ(translatedPoint.y, 100);
}
TEST(TransformTest, scalingRect) {
auto point = facebook::react::Point{100, 200};
auto size = facebook::react::Size{300, 400};
auto rect = facebook::react::Rect{point, size};
auto transformedRect = rect * Transform::Scale(0.5, 0.5, 1);
EXPECT_EQ(transformedRect.origin.x, 175);
EXPECT_EQ(transformedRect.origin.y, 300);
EXPECT_EQ(transformedRect.size.width, 150);
EXPECT_EQ(transformedRect.size.height, 200);
}
TEST(TransformTest, scalingRectWithDifferentCenter) {
auto point = facebook::react::Point{100, 200};
auto size = facebook::react::Size{300, 400};
auto rect = facebook::react::Rect{point, size};
auto center = facebook::react::Point{0, 0};
auto transformedRect =
Transform::Scale(0.5, 0.5, 1).applyWithCenter(rect, center);
EXPECT_EQ(transformedRect.origin.x, 50);
EXPECT_EQ(transformedRect.origin.y, 100);
EXPECT_EQ(transformedRect.size.width, 150);
EXPECT_EQ(transformedRect.size.height, 200);
}
TEST(TransformTest, invertingSize) {
auto size = facebook::react::Size{300, 400};
auto transformedSize = size * Transform::VerticalInversion();
EXPECT_EQ(transformedSize.width, 300);
EXPECT_EQ(transformedSize.height, 400);
}
TEST(TransformTest, rotatingRect) {
auto point = facebook::react::Point{10, 10};
auto size = facebook::react::Size{10, 10};
auto rect = facebook::react::Rect{point, size};
auto transformedRect = rect * Transform::RotateZ(M_PI_4);
ASSERT_NEAR(transformedRect.origin.x, 7.9289, 0.0001);
ASSERT_NEAR(transformedRect.origin.y, 7.9289, 0.0001);
ASSERT_NEAR(transformedRect.size.width, 14.1421, 0.0001);
ASSERT_NEAR(transformedRect.size.height, 14.1421, 0.0001);
}
TEST(TransformTest, scalingAndTranslatingRect) {
auto point = facebook::react::Point{100, 200};
auto size = facebook::react::Size{300, 400};
auto rect = facebook::react::Rect{point, size};
auto transformedRect =
rect * Transform::Scale(0.5, 0.5, 1) * Transform::Translate(1, 1, 0);
EXPECT_EQ(transformedRect.origin.x, 176);
EXPECT_EQ(transformedRect.origin.y, 301);
EXPECT_EQ(transformedRect.size.width, 150);
EXPECT_EQ(transformedRect.size.height, 200);
}