infinipaint/src/CanvasComponents/CanvasComponentContainer.cpp

207 lines
9.2 KiB
C++
Raw Normal View History

#include "CanvasComponentContainer.hpp"
2025-12-13 13:03:27 +03:00
#include "CanvasComponentAllocator.hpp"
#include "../DrawData.hpp"
#include "../DrawingProgram/DrawingProgram.hpp"
#include "../World.hpp"
#include "CanvasComponent.hpp"
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
2025-12-31 16:30:56 +03:00
#include "../ScaleUpCanvas.hpp"
2026-01-01 22:46:02 +03:00
#include "Helpers/NetworkingObjects/NetObjManager.hpp"
2025-12-13 13:03:27 +03:00
using namespace NetworkingObjects;
2026-01-04 11:37:38 +03:00
void CanvasComponentContainer::CopyData::scale_up(const WorldScalar& scaleUpAmount) {
coords.scale_about(WorldVec{0, 0}, scaleUpAmount, true);
}
CanvasComponentContainer::CanvasComponentContainer() { }
CanvasComponentContainer::CanvasComponentContainer(NetworkingObjects::NetObjManager& objMan, CanvasComponentType type) {
2025-12-13 13:03:27 +03:00
compAllocator = objMan.make_obj_direct<CanvasComponentAllocator>(type);
2025-12-15 16:49:51 +03:00
compAllocator->comp->compContainer = this;
}
2025-12-29 19:57:36 +03:00
CanvasComponentContainer::CanvasComponentContainer(NetworkingObjects::NetObjManager& objMan, const CopyData& copyData) {
compAllocator = objMan.make_obj_direct<CanvasComponentAllocator>(copyData.obj->get_type());
compAllocator->comp->compContainer = this;
compAllocator->comp->set_data_from(*copyData.obj);
coords = copyData.coords;
}
2025-12-31 16:30:56 +03:00
void CanvasComponentContainer::register_class(World& w) {
auto readConstructorFunc = [&w](const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) {
a(o->coords);
o->compAllocator = o.get_obj_man()->read_create_message<CanvasComponentAllocator>(a, c);
o->compAllocator->comp->compContainer = o.get();
canvas_scale_up_check(*o, w, c);
};
w.netObjMan.register_class<CanvasComponentContainer, CanvasComponentContainer, CanvasComponentContainer, CanvasComponentContainer>({
2025-12-13 13:03:27 +03:00
.writeConstructorFuncClient = write_constructor_func,
2025-12-31 16:30:56 +03:00
.readConstructorFuncClient = readConstructorFunc,
2025-12-13 13:03:27 +03:00
.readUpdateFuncClient = nullptr,
.writeConstructorFuncServer = write_constructor_func,
2025-12-31 16:30:56 +03:00
.readConstructorFuncServer = readConstructorFunc,
2025-12-13 13:03:27 +03:00
.readUpdateFuncServer = nullptr,
});
}
void CanvasComponentContainer::reassign_netobj_ids_call() {
compAllocator.reassign_ids();
}
std::unique_ptr<CanvasComponentContainer::CopyData> CanvasComponentContainer::get_data_copy() const {
auto toRet = std::make_unique<CanvasComponentContainer::CopyData>();
2025-12-29 19:57:36 +03:00
toRet->coords = coords;
toRet->obj = compAllocator->comp->get_data_copy();
return toRet;
}
void CanvasComponentContainer::send_comp_update(DrawingProgram& drawP, bool finalUpdate) {
drawP.world.delayedUpdateObjectManager.send_update_to_all<CanvasComponentAllocator>(compAllocator, finalUpdate);
}
2025-12-13 13:03:27 +03:00
void CanvasComponentContainer::write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer>& o, cereal::PortableBinaryOutputArchive& a) {
a(o->coords);
o->compAllocator.write_create_message(a);
}
void CanvasComponentContainer::save_file(cereal::PortableBinaryOutputArchive& a) const {
2025-12-13 13:03:27 +03:00
a(coords);
2026-01-01 22:46:02 +03:00
compAllocator->save_file(a);
}
2026-01-01 22:46:02 +03:00
void CanvasComponentContainer::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version, NetObjManager& objMan) {
2026-01-02 09:22:52 +03:00
if(version >= VersionNumber(0, 4, 0)) {
a(coords);
compAllocator = objMan.make_obj_direct<CanvasComponentAllocator>();
compAllocator->load_file(a, version);
compAllocator->comp->compContainer = this;
}
else {
CanvasComponentType t;
NetworkingObjects::NetObjID uselessID;
a(t, uselessID, coords);
compAllocator = objMan.make_obj_direct<CanvasComponentAllocator>(t);
compAllocator->comp->load_file(a, version);
compAllocator->comp->compContainer = this;
}
2025-12-13 13:03:27 +03:00
}
void CanvasComponentContainer::draw(SkCanvas* canvas, const DrawData& drawData) const {
if(should_draw(drawData))
draw_with_predraw_data(canvas, drawData, calculate_predraw_data(drawData));
}
void CanvasComponentContainer::draw_with_predraw_data(SkCanvas* canvas, const DrawData& drawData, const PreDrawData& preDrawData) const {
if(preDrawData.transformData.scale < COMP_MAX_BEFORE_STOP_SCALING || !get_comp().accurate_draw(canvas, drawData, coords, preDrawData.extraData)) {
2026-02-07 16:03:02 +03:00
canvas->save();
canvas_do_transform(canvas, preDrawData.transformData);
get_comp().draw(canvas, drawData, preDrawData.extraData);
2026-02-07 16:03:02 +03:00
canvas->restore();
}
2025-12-13 13:03:27 +03:00
}
void CanvasComponentContainer::commit_update(DrawingProgram& drawP) {
if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
drawP.preupdate_component(&(*objInfo));
2025-12-13 13:03:27 +03:00
get_comp().initialize_draw_data(drawP);
calculate_world_bounds();
drawP.preupdate_component(&(*objInfo));
2025-12-13 13:03:27 +03:00
}
void CanvasComponentContainer::commit_transform(DrawingProgram& drawP) {
if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
drawP.preupdate_component(&(*objInfo));
2025-12-13 13:03:27 +03:00
calculate_world_bounds();
drawP.preupdate_component(&(*objInfo));
2025-12-13 13:03:27 +03:00
}
void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgram& drawP) {
get_comp().initialize_draw_data(drawP);
calculate_world_bounds();
}
2025-12-31 16:30:56 +03:00
void CanvasComponentContainer::commit_transform_dont_invalidate_cache() {
calculate_world_bounds();
}
2025-12-13 13:03:27 +03:00
bool CanvasComponentContainer::collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld) const {
return collides_with(camCoords, checkAgainstWorld, camCoords.world_collider_to_coords<SCollision::ColliderCollection<float>>(checkAgainstWorld));
}
bool CanvasComponentContainer::collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<float>& checkAgainstCam) const {
return collides_with(camCoords, camCoords.collider_to_world<SCollision::ColliderCollection<WorldScalar>>(checkAgainstCam), checkAgainstCam);
}
// We could just send one of the checkAgainst colliders, since they represent the same thing, but sending both saves on redundant transformations, since we can save both of the versions on the executor side
bool CanvasComponentContainer::collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& checkAgainstCam) const {
if((camCoords.inverseScale << COMP_MAX_SHIFT_BEFORE_STOP_COLLISIONS) < coords.inverseScale) // Object is too large, just dismiss the collision
return false;
else if(coords.inverseScale < (camCoords.inverseScale >> COMP_COLLIDE_MIN_SHIFT_TINY))
2025-12-13 13:03:27 +03:00
return SCollision::collide(checkAgainstCam, camCoords.to_space(worldAABB.value().min));
else {
auto c = checkAgainstWorld.transform<float>(
[&coords = coords](const WorldVec& a) {
return coords.to_space(a);
},
[&coords = coords](const WorldScalar& a) {
return coords.scalar_to_space(a);
});
return get_comp().collides_within_coords(c);
}
}
void CanvasComponentContainer::canvas_do_transform(SkCanvas* canvas, const TransformData& transformData) const {
2025-12-13 13:03:27 +03:00
canvas->scale(transformData.scale, transformData.scale);
canvas->rotate(transformData.rotation);
canvas->translate(transformData.translation.x(), transformData.translation.y());
}
bool CanvasComponentContainer::should_draw(const DrawData& drawData) const {
return (!drawData.clampDrawBetween || (drawData.clampDrawMinimum < coords.inverseScale)) && SCollision::collide(worldAABB.value(), drawData.cam.viewingAreaGenerousCollider);
}
CanvasComponentContainer::PreDrawData CanvasComponentContainer::calculate_predraw_data(const DrawData& drawData) const {
PreDrawData toRet;
toRet.transformData = calculate_draw_transform(drawData);
if(toRet.transformData.scale < COMP_MAX_BEFORE_STOP_SCALING)
toRet.extraData = get_comp().get_predraw_data(drawData);
else
toRet.extraData = get_comp().get_predraw_data_accurate(drawData, coords);
return toRet;
}
CanvasComponentContainer::TransformData CanvasComponentContainer::calculate_draw_transform(const DrawData& drawData) const {
TransformData toRet;
2025-12-13 13:03:27 +03:00
toRet.translation = -coords.to_space(drawData.cam.c.pos);
toRet.rotation = (coords.rotation - drawData.cam.c.rotation) * 180.0 / std::numbers::pi;
2026-02-07 16:03:02 +03:00
toRet.scale = std::min(static_cast<float>(coords.inverseScale / drawData.cam.c.inverseScale), COMP_MAX_BEFORE_STOP_SCALING);
2025-12-13 13:03:27 +03:00
return toRet;
}
2025-12-31 16:30:56 +03:00
void CanvasComponentContainer::scale_up(const WorldScalar& scaleUpAmount) {
2025-12-31 16:43:08 +03:00
coords.scale_about(WorldVec{0, 0}, scaleUpAmount, true);
2025-12-31 16:30:56 +03:00
calculate_world_bounds();
}
2025-12-13 13:03:27 +03:00
unsigned CanvasComponentContainer::get_mipmap_level(const DrawData& drawData) const {
if(drawData.mipMapLevelTwo > coords.inverseScale)
return 2;
else if(drawData.mipMapLevelOne > coords.inverseScale)
return 1;
else
return 0;
}
void CanvasComponentContainer::calculate_world_bounds() {
worldAABB = coords.collider_to_world<SCollision::AABB<WorldScalar>, SCollision::AABB<float>>(get_comp().get_obj_coord_bounds());
}
SCollision::AABB<WorldScalar> CanvasComponentContainer::get_world_bounds() const {
return worldAABB.value();
}
2025-12-13 13:03:27 +03:00
CanvasComponent& CanvasComponentContainer::get_comp() const {
return *compAllocator->comp;
}