From 60ab8f3c7332f85e74c6c5bae2a6a5fbaefba958 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 24 May 2026 11:58:52 +0300 Subject: [PATCH] Rewrite collision detection --- CMakeLists.txt | 6 +- include/Helpers/Networking/NetLibrary.hpp | 2 +- include/Helpers/SCollision.hpp | 4 +- include/Helpers/Serializers.hpp | 13 ++- macosinstall/Info.plist | 4 +- src/CanvasComponents/BrushComponentCode.cpp | 48 ++------- src/CanvasComponents/BrushComponentCode.hpp | 4 +- src/CanvasComponents/CanvasComponent.hpp | 2 +- .../CanvasComponentContainer.cpp | 54 +++++----- .../CanvasComponentContainer.hpp | 7 +- .../EllipseCanvasComponent.cpp | 13 ++- .../EllipseCanvasComponent.hpp | 2 +- src/CanvasComponents/ImageCanvasComponent.cpp | 16 ++- src/CanvasComponents/ImageCanvasComponent.hpp | 2 +- src/CanvasComponents/MeshCanvasComponent.cpp | 99 ++++++++++++++----- src/CanvasComponents/MeshCanvasComponent.hpp | 5 +- .../RectangleCanvasComponent.cpp | 17 +++- .../RectangleCanvasComponent.hpp | 2 +- .../TextBoxCanvasComponent.cpp | 14 ++- .../TextBoxCanvasComponent.hpp | 2 +- src/DrawingProgram/DrawingProgramCache.cpp | 8 +- src/DrawingProgram/DrawingProgramCache.hpp | 2 +- .../DrawingProgramSelection.cpp | 28 +++--- .../DrawingProgramSelection.hpp | 8 +- src/DrawingProgram/Tools/BrushTool.cpp | 2 +- src/DrawingProgram/Tools/EditTool.cpp | 31 ++---- .../Tools/EditTools/TextBoxEditTool.cpp | 6 +- src/DrawingProgram/Tools/EraserTool.cpp | 94 +++++++++--------- src/DrawingProgram/Tools/EraserTool.hpp | 9 +- src/DrawingProgram/Tools/FillTool.cpp | 8 +- src/DrawingProgram/Tools/LassoSelectTool.cpp | 23 ++--- src/DrawingProgram/Tools/RectSelectTool.cpp | 6 +- src/VersionConstants.cpp | 1 + src/VersionConstants.hpp | 6 +- windowsinstall/infinipaint.manifest | 2 +- 35 files changed, 292 insertions(+), 258 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1afaec5..9b2576b 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,8 +255,8 @@ elseif(APPLE) BUNDLE True MACOSX_BUNDLE_GUI_IDENTIFIER com.infinipaint.infinipaint MACOSX_BUNDLE_BUNDLE_NAME infinipaint - MACOSX_BUNDLE_BUNDLE_VERSION "0.5.0" - MACOSX_BUNDLE_SHORT_VERSION_STRING "0.5.0" + MACOSX_BUNDLE_BUNDLE_VERSION "0.6.0" + MACOSX_BUNDLE_SHORT_VERSION_STRING "0.6.0" MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/macosinstall/Info.plist ) add_custom_command(TARGET main POST_BUILD @@ -451,7 +451,7 @@ if(APPLE OR WIN32) set(CPACK_PACKAGE_NAME "infinipaint") set(CPACK_PACKAGE_VERSION_MAJOR "0") - set(CPACK_PACKAGE_VERSION_MINOR "5") + set(CPACK_PACKAGE_VERSION_MINOR "6") set(CPACK_PACKAGE_VERSION_PATCH "0") set(CPACK_PACKAGE_VENDOR "ErrorAtLine0") set(CPACK_PACKAGE_CONTACT "erroratline0@gmail.com") diff --git a/include/Helpers/Networking/NetLibrary.hpp b/include/Helpers/Networking/NetLibrary.hpp index 6dc6cd0..b25bad6 100644 --- a/include/Helpers/Networking/NetLibrary.hpp +++ b/include/Helpers/Networking/NetLibrary.hpp @@ -32,7 +32,7 @@ template std::weak_ptr make_weak_ptr(std::shared_ptr ptr) { retu #define RELIABLE_COMMAND_CHANNEL "reliableCommand" #define UNRELIABLE_COMMAND_CHANNEL "unreliableCommand" #define RESOURCE_COMMAND_CHANNEL "resourceCommand" -#define MAIN_NETLIBRARY_CHANNEL "main00009" // NOTE: Change this value whenever netcode is incompatible with the previous version +#define MAIN_NETLIBRARY_CHANNEL "main00010" // NOTE: Change this value whenever netcode is incompatible with the previous version typedef uint8_t MessageCommandType; typedef uint8_t NetTypeIDType; diff --git a/include/Helpers/SCollision.hpp b/include/Helpers/SCollision.hpp index ec7eeb4..8d95ae8 100644 --- a/include/Helpers/SCollision.hpp +++ b/include/Helpers/SCollision.hpp @@ -40,8 +40,8 @@ namespace SCollision { max(initMax) {} AABB(const SkRect& r): - min(r.TL), - max(r.BR) + min(r.TL().x(), r.TL().y()), + max(r.BR().x(), r.BR().y()) {} Vector min; Vector max; diff --git a/include/Helpers/Serializers.hpp b/include/Helpers/Serializers.hpp index 99822fe..23b6b93 100644 --- a/include/Helpers/Serializers.hpp +++ b/include/Helpers/Serializers.hpp @@ -5,8 +5,9 @@ #include #include "FixedPoint.hpp" #include - +#include #include +#include "ConvertVec.hpp" template void save(Archive& ar, SkString const & str) { ar(cereal::make_size_tag(static_cast(str.size()))); @@ -20,6 +21,16 @@ template void load(Archive& ar, SkString& str) { ar(cereal::binary_data(const_cast(str.data()), static_cast(size) * sizeof(char))); } +template void save(Archive& ar, const SkPoint& s) { + ar(convert_vec2(s)); +} + +template void load(Archive& ar, SkPoint& s) { + Eigen::Vector2f f; + ar(f); + s = convert_vec2(f); +} + namespace cereal { // https://stackoverflow.com/questions/22884216/serializing-eigenmatrix-using-cereal-library (with some modifications using constexpr) diff --git a/macosinstall/Info.plist b/macosinstall/Info.plist index f41420e..329672d 100755 --- a/macosinstall/Info.plist +++ b/macosinstall/Info.plist @@ -13,9 +13,9 @@ CFBundleSpokenName InfiniPaint CFBundleVersion - 0.5.0 + 0.6.0 CFBundleShortVersionString - 0.5.0 + 0.6.0 CFBundlePackageType APPL NSHumanReadableCopyright diff --git a/src/CanvasComponents/BrushComponentCode.cpp b/src/CanvasComponents/BrushComponentCode.cpp index 39c30ed..8227766 100644 --- a/src/CanvasComponents/BrushComponentCode.cpp +++ b/src/CanvasComponents/BrushComponentCode.cpp @@ -27,39 +27,9 @@ namespace BrushComponentCode { -MeshShapeData brush_stroke_to_mesh_points(const std::vector& brushPoints, bool hasRoundCaps) { +SkPath brush_stroke_to_skpath(const std::vector& brushPoints, bool hasRoundCaps) { std::vector points = smooth_points(brushPoints, 0, brushPoints.size() - 1, DEFAULT_SMOOTHNESS); - std::vector unprocessedPoints; - create_triangles(unprocessedPoints, brushPoints, points, hasRoundCaps); - using namespace Clipper2Lib; - PathsD clippingSubjects; - auto& shape = clippingSubjects.emplace_back(); - for(const Vector2f& p : unprocessedPoints) - shape.emplace_back(p.x(), p.y()); - clippingSubjects = SimplifyPaths(clippingSubjects, 0.1); - - ClipperD clipper; - PolyTreeD solutionTree; - - clipper.AddSubject(clippingSubjects); - clipper.AddClip(clippingSubjects); - clipper.Execute(ClipType::Union, FillRule::NonZero, solutionTree); - - MeshShapeData meshData; - - for(auto& treeChild : solutionTree) { - meshData.contours.emplace_back(); - for(auto& point : treeChild->Polygon()) - meshData.contours.back().emplace_back(point.x, point.y); - for(auto& hole : *treeChild) { - meshData.contours.emplace_back(); - for(auto& point : hole->Polygon()) - meshData.contours.back().emplace_back(point.x, point.y); - } - break; - } - - return meshData; + return create_triangles(brushPoints, points, hasRoundCaps); } std::vector get_wedge_indices(const std::vector& points) { @@ -119,7 +89,8 @@ std::vector smooth_points(const std::vector& points, siz return toRet; } -void create_triangles(std::vector& brushMeshPointList, const std::vector& regularPoints, const std::vector& smoothedPoints, bool hasRoundCaps) { +SkPath create_triangles(const std::vector& regularPoints, const std::vector& smoothedPoints, bool hasRoundCaps) { + SkPathBuilder pathBuilder; const int ARC_SMOOTHNESS = 10; const int CIRCLE_SMOOTHNESS = 20; const std::vector& pointsN = regularPoints; @@ -135,10 +106,10 @@ void create_triangles(std::vector& brushMeshPointList, const std::vect if(pointsN.size() < 2) { if(!topPoints.empty()) { - for(auto& p : topPoints) - brushMeshPointList.emplace_back(convert_vec2(p)); + pathBuilder.addPolygon({topPoints.data(), topPoints.size()}, true); + return pathBuilder.detach(); } - return; + return SkPath(); } auto& points = smoothedPoints; @@ -279,9 +250,10 @@ void create_triangles(std::vector& brushMeshPointList, const std::vect if(!topPoints.empty()) { topPoints.insert(topPoints.begin(), bottomPoints.rbegin(), bottomPoints.rend()); - for(auto& p : topPoints) - brushMeshPointList.emplace_back(convert_vec2(p)); + pathBuilder.addPolygon({topPoints.data(), topPoints.size()}, true); } + + return pathBuilder.detach(); } } diff --git a/src/CanvasComponents/BrushComponentCode.hpp b/src/CanvasComponents/BrushComponentCode.hpp index 10b7c78..65ce9df 100644 --- a/src/CanvasComponents/BrushComponentCode.hpp +++ b/src/CanvasComponents/BrushComponentCode.hpp @@ -36,8 +36,8 @@ namespace BrushComponentCode { } }; - MeshShapeData brush_stroke_to_mesh_points(const std::vector& brushPoints, bool hasRoundCaps); - void create_triangles(std::vector& brushMeshPointList, const std::vector& regularPoints, const std::vector& smoothedPoints, bool hasRoundCaps); + SkPath brush_stroke_to_skpath(const std::vector& brushPoints, bool hasRoundCaps); + SkPath create_triangles(const std::vector& regularPoints, const std::vector& smoothedPoints, bool hasRoundCaps); std::vector get_wedge_indices(const std::vector& points); std::vector smooth_points(const std::vector& points, size_t beginIndex, size_t endIndex, unsigned numOfDivisions); } diff --git a/src/CanvasComponents/CanvasComponent.hpp b/src/CanvasComponents/CanvasComponent.hpp index 10d3491..9e522f5 100644 --- a/src/CanvasComponents/CanvasComponent.hpp +++ b/src/CanvasComponents/CanvasComponent.hpp @@ -56,7 +56,7 @@ class CanvasComponent { virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const = 0; virtual void initialize_draw_data(DrawingProgram& drawP) = 0; - virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const = 0; + virtual bool collides_within_coords_point(const Vector2f& checkAgainst) const = 0; virtual bool collides_within_coords_skpath(const SkPath& checkAgainst) const = 0; virtual SCollision::AABB get_obj_coord_bounds() const = 0; diff --git a/src/CanvasComponents/CanvasComponentContainer.cpp b/src/CanvasComponents/CanvasComponentContainer.cpp index dde845f..7662e91 100644 --- a/src/CanvasComponents/CanvasComponentContainer.cpp +++ b/src/CanvasComponents/CanvasComponentContainer.cpp @@ -23,6 +23,7 @@ #include "../World.hpp" #include "CanvasComponent.hpp" #include +#include #include "../ScaleUpCanvas.hpp" #include "Helpers/NetworkingObjects/NetObjManager.hpp" @@ -141,31 +142,38 @@ void CanvasComponentContainer::commit_transform_dont_invalidate_cache() { calculate_world_bounds(); } -bool CanvasComponentContainer::collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstWorld) const { - return collides_with(camCoords, checkAgainstWorld, camCoords.world_collider_to_coords>(checkAgainstWorld)); -} - -bool CanvasComponentContainer::collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstCam) const { - return collides_with(camCoords, camCoords.collider_to_world>(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& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) const { +bool CanvasComponentContainer::collides_with(const CoordSpaceHelper& camCoords, const SkPath& checkAgainstCam) const { if(!worldAABB.has_value()) return false; 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)) - return SCollision::collide(checkAgainstCam, camCoords.to_space(worldAABB.value().min)); + return checkAgainstCam.contains(convert_vec2(camCoords.to_space(worldAABB.value().min))); else { - auto c = checkAgainstWorld.transform( - [&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); + TransformData drawTransform = calculate_draw_transform(camCoords); + SkMatrix m = SkMatrix::I(); + m.postScale(1.0 / drawTransform.scale, 1.0 / drawTransform.scale).postRotate(-drawTransform.rotation).postTranslate(-drawTransform.translation.x(), -drawTransform.translation.y()); + std::optional transformedPath = checkAgainstCam.tryMakeTransform(m); + if(!transformedPath.has_value()) + return false; + return get_comp().collides_within_coords_skpath(transformedPath.value()); + } +} + +bool CanvasComponentContainer::collides_with_point(const CoordSpaceHelper& camCoords, const Vector2f& checkAgainstCam) const { + if(!worldAABB.has_value()) + return false; + 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)) // Object is too tiny, just dismiss the collision + return false; + else { + TransformData drawTransform = calculate_draw_transform(camCoords); + SkMatrix m = SkMatrix::I(); + m.postScale(1.0 / drawTransform.scale, 1.0 / drawTransform.scale).postRotate(-drawTransform.rotation).postTranslate(-drawTransform.translation.x(), -drawTransform.translation.y()); + SkPoint p = m.mapPoint(convert_vec2(checkAgainstCam)); + return get_comp().collides_within_coords_point(convert_vec2(p)); } } @@ -181,7 +189,7 @@ bool CanvasComponentContainer::should_draw(const DrawData& drawData) const { CanvasComponentContainer::PreDrawData CanvasComponentContainer::calculate_predraw_data(const DrawData& drawData) const { PreDrawData toRet; - toRet.transformData = calculate_draw_transform(drawData); + toRet.transformData = calculate_draw_transform(drawData.cam.c); if(toRet.transformData.scale < COMP_MAX_BEFORE_STOP_SCALING) toRet.extraData = get_comp().get_predraw_data(drawData); else @@ -189,11 +197,11 @@ CanvasComponentContainer::PreDrawData CanvasComponentContainer::calculate_predra return toRet; } -CanvasComponentContainer::TransformData CanvasComponentContainer::calculate_draw_transform(const DrawData& drawData) const { +CanvasComponentContainer::TransformData CanvasComponentContainer::calculate_draw_transform(const CoordSpaceHelper& camCoords) const { TransformData toRet; - toRet.translation = -coords.to_space(drawData.cam.c.pos); - toRet.rotation = (coords.rotation - drawData.cam.c.rotation) * 180.0 / std::numbers::pi; - toRet.scale = std::min(static_cast(coords.inverseScale / drawData.cam.c.inverseScale), COMP_MAX_BEFORE_STOP_SCALING); + toRet.translation = -coords.to_space(camCoords.pos); + toRet.rotation = (coords.rotation - camCoords.rotation) * 180.0 / std::numbers::pi; + toRet.scale = std::min(static_cast(coords.inverseScale / camCoords.inverseScale), COMP_MAX_BEFORE_STOP_SCALING); return toRet; } diff --git a/src/CanvasComponents/CanvasComponentContainer.hpp b/src/CanvasComponents/CanvasComponentContainer.hpp index b3c7cea..4655c5f 100644 --- a/src/CanvasComponents/CanvasComponentContainer.hpp +++ b/src/CanvasComponents/CanvasComponentContainer.hpp @@ -77,15 +77,14 @@ class CanvasComponentContainer { void draw(SkCanvas* canvas, const DrawData& drawData) const; void draw_with_predraw_data(SkCanvas* canvas, const DrawData& drawData, const PreDrawData& preDrawData) const; PreDrawData calculate_predraw_data(const DrawData& drawData) const; - TransformData calculate_draw_transform(const DrawData& drawData) const; + TransformData calculate_draw_transform(const CoordSpaceHelper& camCoords) const; void commit_update(DrawingProgram& drawP); void commit_transform_dont_invalidate_cache(); // Must be thread safe void commit_transform(DrawingProgram& drawP); void commit_update_dont_invalidate_cache(DrawingProgram& drawP); // Must be thread safe bool should_draw(const DrawData& drawData) const; - bool collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstWorld) const; - bool collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstCam) const; - bool collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) const; + bool collides_with(const CoordSpaceHelper& camCoords, const SkPath& checkAgainstCam) const; + bool collides_with_point(const CoordSpaceHelper& camCoords, const Vector2f& checkAgainstCam) const; void send_comp_update(DrawingProgram& drawP, bool finalUpdate); void scale_up(const WorldScalar& scaleUpAmount); diff --git a/src/CanvasComponents/EllipseCanvasComponent.cpp b/src/CanvasComponents/EllipseCanvasComponent.cpp index f01eaeb..1d3697e 100644 --- a/src/CanvasComponents/EllipseCanvasComponent.cpp +++ b/src/CanvasComponents/EllipseCanvasComponent.cpp @@ -90,12 +90,19 @@ void EllipseCanvasComponent::set_data_from(const CanvasComponent& other) { d = otherEllipse.d; } -bool EllipseCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { - return false; +bool EllipseCanvasComponent::collides_within_coords_point(const Vector2f& checkAgainst) const { + bool intersectsAABB = ellipsePath.getBounds().contains(checkAgainst.x(), checkAgainst.y()); + if(!intersectsAABB) + return false; + return ellipsePath.contains(checkAgainst.x(), checkAgainst.y()); } bool EllipseCanvasComponent::collides_within_coords_skpath(const SkPath& checkAgainst) const { - return checkAgainst.getBounds().intersects(ellipsePath.getBounds()) && Op(checkAgainst, ellipsePath, SkPathOp::kIntersect_SkPathOp); + bool intersectsAABB = ellipsePath.getBounds().intersects(checkAgainst.getBounds()); + if(!intersectsAABB) + return false; + std::optional pathIntersectCheck = Op(checkAgainst, ellipsePath, SkPathOp::kIntersect_SkPathOp); + return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty(); } SCollision::AABB EllipseCanvasComponent::get_obj_coord_bounds() const { diff --git a/src/CanvasComponents/EllipseCanvasComponent.hpp b/src/CanvasComponents/EllipseCanvasComponent.hpp index 5fe985e..2f35c0e 100644 --- a/src/CanvasComponents/EllipseCanvasComponent.hpp +++ b/src/CanvasComponents/EllipseCanvasComponent.hpp @@ -48,7 +48,7 @@ class EllipseCanvasComponent : public CanvasComponent { void create_draw_data(); virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; - virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + virtual bool collides_within_coords_point(const Vector2f& checkAgainst) const override; virtual bool collides_within_coords_skpath(const SkPath& checkAgainst) const override; virtual SCollision::AABB get_obj_coord_bounds() const override; void create_triangles(const std::function& passTriangleFunc); diff --git a/src/CanvasComponents/ImageCanvasComponent.cpp b/src/CanvasComponents/ImageCanvasComponent.cpp index bae3328..87dc5c0 100644 --- a/src/CanvasComponents/ImageCanvasComponent.cpp +++ b/src/CanvasComponents/ImageCanvasComponent.cpp @@ -78,7 +78,7 @@ void ImageCanvasComponent::draw_download_progress_bar(SkCanvas* canvas, const Dr return; if(compContainer->should_draw(drawData)) { canvas->save(); - compContainer->canvas_do_transform(canvas, compContainer->calculate_draw_transform(drawData)); + compContainer->canvas_do_transform(canvas, compContainer->calculate_draw_transform(drawData.cam.c)); SkPaint p; p.setStroke(true); p.setStrokeWidth(10.0f); @@ -157,8 +157,12 @@ void ImageCanvasComponent::update(DrawingProgram& drawP) { void ImageCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { } -bool ImageCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { - return false; +bool ImageCanvasComponent::collides_within_coords_point(const Vector2f& checkAgainst) const { + SkPath p = SkPath::Rect(SCollision::AABB(d.p1, d.p2).get_sk_rect()); + bool intersectsAABB = p.getBounds().contains(checkAgainst.x(), checkAgainst.y()); + if(!intersectsAABB) + return false; + return p.contains(checkAgainst.x(), checkAgainst.y()); } bool ImageCanvasComponent::collides_within_coords_skpath(const SkPath& checkAgainst) const { @@ -168,7 +172,11 @@ bool ImageCanvasComponent::collides_within_coords_skpath(const SkPath& checkAgai else startingRect = get_cropped_rectangle(d.p1, d.p2, d.cropP1, d.cropP2); SkPath p = SkPath::Rect(startingRect); - return startingRect.intersects(checkAgainst.getBounds()) && Op(checkAgainst, p, SkPathOp::kIntersect_SkPathOp); + bool intersectsAABB = p.getBounds().intersects(checkAgainst.getBounds()); + if(!intersectsAABB) + return false; + std::optional pathIntersectCheck = Op(checkAgainst, p, SkPathOp::kIntersect_SkPathOp); + return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty(); } SCollision::AABB ImageCanvasComponent::get_obj_coord_bounds() const { diff --git a/src/CanvasComponents/ImageCanvasComponent.hpp b/src/CanvasComponents/ImageCanvasComponent.hpp index e8e8c45..9ed79c7 100644 --- a/src/CanvasComponents/ImageCanvasComponent.hpp +++ b/src/CanvasComponents/ImageCanvasComponent.hpp @@ -55,7 +55,7 @@ class ImageCanvasComponent : public CanvasComponent { private: virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; - virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + virtual bool collides_within_coords_point(const Vector2f& checkAgainst) const override; virtual bool collides_within_coords_skpath(const SkPath& checkAgainst) const override; virtual SCollision::AABB get_obj_coord_bounds() const override; }; diff --git a/src/CanvasComponents/MeshCanvasComponent.cpp b/src/CanvasComponents/MeshCanvasComponent.cpp index 10de05d..40fbe6c 100644 --- a/src/CanvasComponents/MeshCanvasComponent.cpp +++ b/src/CanvasComponents/MeshCanvasComponent.cpp @@ -19,8 +19,10 @@ #include "MeshCanvasComponent.hpp" #include #include +#include #include #include +#include "BrushComponentCode.hpp" #include "Eigen/Core" #include "Helpers/MathExtras.hpp" #include "../TimePoint.hpp" @@ -34,26 +36,75 @@ #define BVH_LEVELS 3 +template void skpath_write(const SkPath& p, Archive& a) { + std::vector contourSizes; + uint32_t contourSize = 0; + auto verbs = p.verbs(); + auto points = p.points(); + for(size_t verbIndex = 0; verbIndex < verbs.size(); verbIndex++) { + switch(verbs[verbIndex]) { + case SkPathVerb::kClose: + contourSizes.emplace_back(contourSize); + contourSize = 0; + break; + case SkPathVerb::kMove: + case SkPathVerb::kLine: + contourSize++; + break; + default: + throw std::runtime_error("[skpath_write] Illegal verb " + std::to_string(static_cast(verbs[verbIndex]))); + break; + } + } + a(contourSizes); + for(const SkPoint& p : points) + a(p); +} + +template SkPath skpath_read(Archive& a) { + SkPathBuilder builder; + std::vector contourSizes; + a(contourSizes); + for(uint32_t contourSize : contourSizes) { + SkPoint p; + if(contourSize == 0) + continue; + a(p); + builder.moveTo(p); + for(uint32_t i = 1; i < contourSize; i++) { + a(p); + builder.lineTo(p); + } + builder.close(); + } + return builder.detach(); +} + void MeshCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const { - a(d.points, d.color); + skpath_write(d.meshPath, a); + a(d.color); } void MeshCanvasComponent::load(cereal::PortableBinaryInputArchive& a) { - a(d.points, d.color); + d.meshPath = skpath_read(a); + a(d.color); } void MeshCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const { - a(d.points, d.color); + skpath_write(d.meshPath, a); + a(d.color); } void MeshCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) { - if(version >= VersionNumber(0, 6, 0)) - a(d.points, d.color); + if(version >= VersionNumber(0, 6, 0)) { + d.meshPath = skpath_read(a); + a(d.color); + } else { std::vector brushPoints; bool hasRoundCaps; a(brushPoints, d.color, hasRoundCaps); - d.points = BrushComponentCode::brush_stroke_to_mesh_points(brushPoints, hasRoundCaps); + d.meshPath = BrushComponentCode::brush_stroke_to_skpath(brushPoints, hasRoundCaps); } } @@ -79,7 +130,7 @@ void MeshCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData, const SkPaint paint; paint.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), d.color.w()}); paint.setAntiAlias(drawData.skiaAA); - canvas->drawPath(meshPath, paint); + canvas->drawPath(d.meshPath, paint); //SkPaint paint2; //paint2.setColor4f(SkColor4f{1.0f, 0.0f, 0.0f, 1.0f}); @@ -187,35 +238,31 @@ void MeshCanvasComponent::set_data_from(const CanvasComponent& other) { } void MeshCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { - if(!d.points.contours.empty()) { - SkPathBuilder meshPathBuilder; - - for(auto& contour : d.points.contours) { - if(!contour.empty()) - meshPathBuilder.moveTo(contour.front().x(), contour.front().y()); - for(size_t i = 1; i < contour.size(); i++) { - auto& p = contour[i]; - meshPathBuilder.lineTo(p.x(), p.y()); - } - meshPathBuilder.close(); - } - - meshPath = meshPathBuilder.detach(); - } } -bool MeshCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { - return false; +bool MeshCanvasComponent::collides_within_coords_point(const Vector2f& checkAgainst) const { + bool intersectsAABB = d.meshPath.getBounds().contains(checkAgainst.x(), checkAgainst.y()); + if(!intersectsAABB) + return false; + return d.meshPath.contains(checkAgainst.x(), checkAgainst.y()); +} + +bool MeshCanvasComponent::collides_within_coords_skpath(const SkPath& checkAgainst) const { + bool intersectsAABB = d.meshPath.getBounds().intersects(checkAgainst.getBounds()); + if(!intersectsAABB) + return false; + std::optional pathIntersectCheck = Op(checkAgainst, d.meshPath, SkPathOp::kIntersect_SkPathOp); + return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty(); } bool MeshCanvasComponent::should_draw_extra(const DrawData& drawData, const CoordSpaceHelper& coords) const { SCollision::AABB viewGenerousColliderInObjSpace = coords.world_collider_to_coords>(drawData.cam.viewingAreaGenerousCollider); viewGenerousColliderInObjSpace.min -= Vector2f{1.0f, 1.0f}; viewGenerousColliderInObjSpace.max += Vector2f{1.0f, 1.0f}; - return false; + return true; //return collisionTree.is_collide(viewGenerousColliderInObjSpace); } SCollision::AABB MeshCanvasComponent::get_obj_coord_bounds() const { - return meshPath.getBounds(); + return d.meshPath.getBounds(); } diff --git a/src/CanvasComponents/MeshCanvasComponent.hpp b/src/CanvasComponents/MeshCanvasComponent.hpp index ba60aba..02bf6e4 100644 --- a/src/CanvasComponents/MeshCanvasComponent.hpp +++ b/src/CanvasComponents/MeshCanvasComponent.hpp @@ -37,7 +37,7 @@ class MeshCanvasComponent : public CanvasComponent { virtual void set_data_from(const CanvasComponent& other) override; struct Data { - MeshShapeData points; + SkPath meshPath; Vector4f color; } d; private: @@ -45,11 +45,10 @@ class MeshCanvasComponent : public CanvasComponent { virtual bool accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords, const std::shared_ptr& predrawData) const override; virtual std::shared_ptr get_predraw_data_accurate(const DrawData& drawData, const CoordSpaceHelper& coords) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; - virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + virtual bool collides_within_coords_point(const Vector2f& checkAgainst) const override; virtual bool collides_within_coords_skpath(const SkPath& checkAgainst) const override; bool should_draw_extra(const DrawData& drawData, const CoordSpaceHelper& coords) const override; virtual SCollision::AABB get_obj_coord_bounds() const override; - SkPath meshPath; SCollision::BVHContainer collisionTree; }; diff --git a/src/CanvasComponents/RectangleCanvasComponent.cpp b/src/CanvasComponents/RectangleCanvasComponent.cpp index 647b6e4..eda5cc3 100644 --- a/src/CanvasComponents/RectangleCanvasComponent.cpp +++ b/src/CanvasComponents/RectangleCanvasComponent.cpp @@ -87,10 +87,6 @@ void RectangleCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { create_draw_data(); } -bool RectangleCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { - return false; -} - void RectangleCanvasComponent::create_draw_data() { SkPathBuilder rectPathBuilder; if(d.p1.x() == d.p2.x() || d.p1.y() == d.p2.y()) { @@ -105,8 +101,19 @@ void RectangleCanvasComponent::create_draw_data() { } } +bool RectangleCanvasComponent::collides_within_coords_point(const Vector2f& checkAgainst) const { + bool intersectsAABB = rectPath.getBounds().contains(checkAgainst.x(), checkAgainst.y()); + if(!intersectsAABB) + return false; + return rectPath.contains(checkAgainst.x(), checkAgainst.y()); +} + bool RectangleCanvasComponent::collides_within_coords_skpath(const SkPath& checkAgainst) const { - return checkAgainst.getBounds().intersects(rectPath.getBounds()) && Op(checkAgainst, rectPath, SkPathOp::kIntersect_SkPathOp); + bool intersectsAABB = rectPath.getBounds().intersects(checkAgainst.getBounds()); + if(!intersectsAABB) + return false; + std::optional pathIntersectCheck = Op(checkAgainst, rectPath, SkPathOp::kIntersect_SkPathOp); + return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty(); } SCollision::AABB RectangleCanvasComponent::get_obj_coord_bounds() const { diff --git a/src/CanvasComponents/RectangleCanvasComponent.hpp b/src/CanvasComponents/RectangleCanvasComponent.hpp index 20c0a75..81d738b 100644 --- a/src/CanvasComponents/RectangleCanvasComponent.hpp +++ b/src/CanvasComponents/RectangleCanvasComponent.hpp @@ -49,7 +49,7 @@ class RectangleCanvasComponent : public CanvasComponent { private: virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; - virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + virtual bool collides_within_coords_point(const Vector2f& checkAgainst) const override; virtual bool collides_within_coords_skpath(const SkPath& checkAgainst) const override; void create_draw_data(); virtual SCollision::AABB get_obj_coord_bounds() const override; diff --git a/src/CanvasComponents/TextBoxCanvasComponent.cpp b/src/CanvasComponents/TextBoxCanvasComponent.cpp index 2d86226..6439e53 100644 --- a/src/CanvasComponents/TextBoxCanvasComponent.cpp +++ b/src/CanvasComponents/TextBoxCanvasComponent.cpp @@ -128,13 +128,21 @@ void TextBoxCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { init_text_box(drawP); } -bool TextBoxCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { - return false; +bool TextBoxCanvasComponent::collides_within_coords_point(const Vector2f& checkAgainst) const { + SkPath p = SkPath::Rect(SCollision::AABB(d.p1, d.p2).get_sk_rect()); + bool intersectsAABB = p.getBounds().contains(checkAgainst.x(), checkAgainst.y()); + if(!intersectsAABB) + return false; + return p.contains(checkAgainst.x(), checkAgainst.y()); } bool TextBoxCanvasComponent::collides_within_coords_skpath(const SkPath& checkAgainst) const { SkPath p = SkPath::Rect(SCollision::AABB(d.p1, d.p2).get_sk_rect()); - return checkAgainst.getBounds().intersects(p.getBounds()) && Op(checkAgainst, p, SkPathOp::kIntersect_SkPathOp); + bool intersectsAABB = p.getBounds().intersects(checkAgainst.getBounds()); + if(!intersectsAABB) + return false; + std::optional pathIntersectCheck = Op(checkAgainst, p, SkPathOp::kIntersect_SkPathOp); + return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty(); } SCollision::AABB TextBoxCanvasComponent::get_obj_coord_bounds() const { diff --git a/src/CanvasComponents/TextBoxCanvasComponent.hpp b/src/CanvasComponents/TextBoxCanvasComponent.hpp index d8262fa..736d678 100644 --- a/src/CanvasComponents/TextBoxCanvasComponent.hpp +++ b/src/CanvasComponents/TextBoxCanvasComponent.hpp @@ -50,7 +50,7 @@ class TextBoxCanvasComponent : public CanvasComponent { virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; - virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + virtual bool collides_within_coords_point(const Vector2f& checkAgainst) const override; virtual bool collides_within_coords_skpath(const SkPath& checkAgainst) const override; void init_text_box(DrawingProgram& drawP); Vector2f get_mouse_pos(DrawingProgram& drawP) const; diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index 51cb60a..90b3bab 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -143,12 +143,12 @@ void DrawingProgramCache::clear_own_cached_surfaces() { windowCache.attachedDrawingProgramCache = nullptr; } -CanvasComponentContainer::ObjInfo* DrawingProgramCache::get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection& cC) { - auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); +CanvasComponentContainer::ObjInfo* DrawingProgramCache::get_front_object_colliding_with_in_editing_layer(const SkPath& cC) { + auto cCWorldBounds = drawP.world.drawData.cam.c.collider_to_world, SCollision::AABB>(cC.getBounds()); CanvasComponentContainer::ObjInfo* p = nullptr; - traverse_bvh_run_function(cCWorld.bounds, [&](const std::shared_ptr& bvhNode) { + traverse_bvh_run_function(cCWorldBounds, [&](const std::shared_ptr& bvhNode) { node_loop_components(bvhNode, [&](const auto& c) { - if(drawP.layerMan.component_passes_layer_selector(c, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED) && (!p || c->pos >= p->pos) && c->obj->collides_with_world_coords(drawP.world.drawData.cam.c, cCWorld)) + if(drawP.layerMan.component_passes_layer_selector(c, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED) && (!p || c->pos >= p->pos) && c->obj->collides_with(drawP.world.drawData.cam.c, cC)) p = c; }); return true; diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index 385c4a6..a1fc072 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -59,7 +59,7 @@ class DrawingProgramCache { void invalidate_cache_at_optional_aabb(const std::optional>& aabb); static void delete_all_draw_cache(); - CanvasComponentContainer::ObjInfo* get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection& cC); + CanvasComponentContainer::ObjInfo* get_front_object_colliding_with_in_editing_layer(const SkPath& cC); ~DrawingProgramCache(); private: struct NodeCache { diff --git a/src/DrawingProgram/DrawingProgramSelection.cpp b/src/DrawingProgram/DrawingProgramSelection.cpp index fc9af1f..6176384 100644 --- a/src/DrawingProgram/DrawingProgramSelection.cpp +++ b/src/DrawingProgram/DrawingProgramSelection.cpp @@ -252,7 +252,7 @@ void DrawingProgramSelection::check_add_stroke_color_change_undo() { } } -void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) { +void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SkPath& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) { check_add_stroke_color_change_undo(); std::vector selectedComponents; @@ -264,13 +264,13 @@ void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCo } } else { - auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); - drawP.drawCache.traverse_bvh_run_function(cCWorld.bounds, erase_select_objects_in_bvh_func(selectedComponents, cC, cCWorld, layerSelector)); + auto cCWorldBounds = drawP.world.drawData.cam.c.collider_to_world, SCollision::AABB>(cC.getBounds()); + drawP.drawCache.traverse_bvh_run_function(cCWorldBounds, erase_select_objects_in_bvh_func(selectedComponents, cC, layerSelector)); } add_to_selection(selectedComponents); } -void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) { +void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const SkPath& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) { check_add_stroke_color_change_undo(); if(frontObjectOnly) { @@ -281,9 +281,8 @@ void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const } } else { - auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); std::erase_if(selectedSet, [&](auto& c) { - if(drawP.layerMan.component_passes_layer_selector(c, layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) { + if(drawP.layerMan.component_passes_layer_selector(c, layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cC)) { drawP.drawCache.add_component(c); return true; } @@ -294,13 +293,13 @@ void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const calculate_aabb(); } -std::function&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::vector& selectedComponents, const SCollision::ColliderCollection& cC, const SCollision::ColliderCollection& cCWorld, DrawingProgramLayerManager::LayerSelector layerSelector) { +std::function&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::vector& selectedComponents, const SkPath& cC, DrawingProgramLayerManager::LayerSelector layerSelector) { auto toRet = [&](const auto& bvhNode) { if(bvhNode && (bvhNode->coords.inverseScale << 9) < drawP.world.drawData.cam.c.inverseScale && - SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) && - SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max)) && - SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right())) && - SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left()))) { + cC.contains(convert_vec2(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min))) && + cC.contains(convert_vec2(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max))) && + cC.contains(convert_vec2(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right()))) && + cC.contains(convert_vec2(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left())))) { drawP.drawCache.invalidate_cache_at_aabb(bvhNode->bounds); drawP.drawCache.traverse_bvh_run_function_starting_at_node_no_collision_check(bvhNode, [&](const auto& bvhNodeChild) { drawP.drawCache.node_loop_erase_if_components(bvhNodeChild, [&](auto c) { @@ -315,7 +314,7 @@ std::function&)> DrawingP return false; } drawP.drawCache.node_loop_erase_if_components(bvhNode, [&](auto c) { - if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) { + if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cC)) { selectedComponents.emplace_back(c); drawP.drawCache.invalidate_cache_at_optional_aabb(c->obj->get_world_bounds()); return true; @@ -741,11 +740,10 @@ void DrawingProgramSelection::erase_component(CanvasComponentContainer::ObjInfo* reset_all(); } -CanvasComponentContainer::ObjInfo* DrawingProgramSelection::get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection& cC) { - auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); +CanvasComponentContainer::ObjInfo* DrawingProgramSelection::get_front_object_colliding_with_in_editing_layer(const SkPath& cC) { CanvasComponentContainer::ObjInfo* p = nullptr; for(auto& c : selectedSet) { - if(drawP.layerMan.component_passes_layer_selector(c, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED) && (!p || c->pos >= p->pos) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) + if(drawP.layerMan.component_passes_layer_selector(c, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED) && (!p || c->pos >= p->pos) && c->obj->collides_with(drawP.world.drawData.cam.c, cC)) p = c; } return p; diff --git a/src/DrawingProgram/DrawingProgramSelection.hpp b/src/DrawingProgram/DrawingProgramSelection.hpp index b286daa..7e42748 100644 --- a/src/DrawingProgram/DrawingProgramSelection.hpp +++ b/src/DrawingProgram/DrawingProgramSelection.hpp @@ -32,8 +32,8 @@ class DrawingProgramSelection { Vector4f* color_picker_color(Vector4f* oldColor); void phone_selection_gui(PhoneDrawingProgramScreen& t); void phone_selection_bottom_toolbar(PhoneDrawingProgramScreen& t); - void add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly); - void remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly); + void add_from_cam_coord_collider_to_selection(const SkPath& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly); + void remove_from_cam_coord_collider_to_selection(const SkPath& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly); void erase_component(CanvasComponentContainer::ObjInfo* objToCheck); bool is_something_selected(); bool is_selected(CanvasComponentContainer::ObjInfo* objToCheck); @@ -50,7 +50,7 @@ class DrawingProgramSelection { void push_selection_to_back(); void delete_all(); void selection_to_clipboard(); - CanvasComponentContainer::ObjInfo* get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection& cC); + CanvasComponentContainer::ObjInfo* get_front_object_colliding_with_in_editing_layer(const SkPath& cC); void input_key_callback_modify_selection(const InputManager::KeyCallbackArgs& key); void input_key_callback_display_selection(const InputManager::KeyCallbackArgs& key); void input_mouse_button_on_canvas_callback_modify_selection(const InputManager::MouseButtonCallbackArgs& button); @@ -85,7 +85,7 @@ class DrawingProgramSelection { void calculate_aabb(); void reset_all(); void reset_transform_data(); - std::function&)> erase_select_objects_in_bvh_func(std::vector& selectedComponents, const SCollision::ColliderCollection& cC, const SCollision::ColliderCollection& cCWorld, DrawingProgramLayerManager::LayerSelector layerSelector); + std::function&)> erase_select_objects_in_bvh_func(std::vector& selectedComponents, const SkPath& cC, DrawingProgramLayerManager::LayerSelector layerSelector); SCollision::ColliderCollection camSpaceSelection; std::array selectionRectPoints; diff --git a/src/DrawingProgram/Tools/BrushTool.cpp b/src/DrawingProgram/Tools/BrushTool.cpp index cddecd8..3621890 100644 --- a/src/DrawingProgram/Tools/BrushTool.cpp +++ b/src/DrawingProgram/Tools/BrushTool.cpp @@ -117,7 +117,7 @@ void BrushTool::commit_data(bool final) { if(objInfoBeingEdited) { NetworkingObjects::NetObjOwnerPtr& containerPtr = objInfoBeingEdited->obj; MeshCanvasComponent& newMesh = static_cast(containerPtr->get_comp()); - newMesh.d.points = BrushComponentCode::brush_stroke_to_mesh_points(brushPoints, drawP.world.main.toolConfig.brush.hasRoundCaps); + newMesh.d.meshPath = BrushComponentCode::brush_stroke_to_skpath(brushPoints, drawP.world.main.toolConfig.brush.hasRoundCaps); containerPtr->commit_update(drawP); containerPtr->send_comp_update(drawP, final); } diff --git a/src/DrawingProgram/Tools/EditTool.cpp b/src/DrawingProgram/Tools/EditTool.cpp index 03705c2..2be0047 100644 --- a/src/DrawingProgram/Tools/EditTool.cpp +++ b/src/DrawingProgram/Tools/EditTool.cpp @@ -108,19 +108,11 @@ void EditTool::input_mouse_button_on_canvas_callback(const InputManager::MouseBu if(!objInfoBeingEdited) { WorldVec mouseWorldPos = drawP.world.drawData.cam.c.from_space(button.pos); - SCollision::AABB mouseAABB{mouseWorldPos - WorldVec{0.5f, 0.5f}, mouseWorldPos + WorldVec{0.5f, 0.5f}}; - SCollision::ColliderCollection cMouseAABB; - cMouseAABB.aabb.emplace_back(mouseAABB); - cMouseAABB.recalculate_bounds(); - - SCollision::AABB camMouseAABB{drawP.world.main.input.mouse.pos - Vector2f{0.5f, 0.5f}, button.pos + Vector2f{0.5f, 0.5f}}; - SCollision::ColliderCollection camCMouseAABB; - camCMouseAABB.aabb.emplace_back(camMouseAABB); - camCMouseAABB.recalculate_bounds(); + SkPath camMouseAABB = SkPath::Rect(SkRect::MakeLTRB(drawP.world.main.input.mouse.pos.x() - 0.5f, drawP.world.main.input.mouse.pos.y() - 0.5f, drawP.world.main.input.mouse.pos.x() + 0.5f, drawP.world.main.input.mouse.pos.y() + 0.5f)); bool modifySelection = !drawP.selection.is_being_transformed(); if(button.clicks >= 2 && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) { - CanvasComponentContainer::ObjInfo* selectedObjectToEdit = drawP.selection.get_front_object_colliding_with_in_editing_layer(camCMouseAABB); + CanvasComponentContainer::ObjInfo* selectedObjectToEdit = drawP.selection.get_front_object_colliding_with_in_editing_layer(camMouseAABB); if(selectedObjectToEdit && is_editable(selectedObjectToEdit)) { drawP.selection.deselect_all(); @@ -130,43 +122,38 @@ void EditTool::input_mouse_button_on_canvas_callback(const InputManager::MouseBu } if(modifySelection) { if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) - drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true); + drawP.selection.add_from_cam_coord_collider_to_selection(camMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true); else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) - drawP.selection.remove_from_cam_coord_collider_to_selection(camCMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true); + drawP.selection.remove_from_cam_coord_collider_to_selection(camMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true); else { drawP.selection.deselect_all(); - drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true); + drawP.selection.add_from_cam_coord_collider_to_selection(camMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true); } } } else { - SCollision::Circle mouseCircle{button.pos, 1.0f}; - SCollision::ColliderCollection cMouseCircle; - cMouseCircle.circle.emplace_back(mouseCircle); - cMouseCircle.recalculate_bounds(); - bool isMovingPoint = false; bool clickedAway = false; if(!pointDragging) { for(HandleData& h : pointHandles) { - if(SCollision::collide(mouseCircle, SCollision::Circle(drawP.world.drawData.cam.c.to_space(objInfoBeingEdited->obj->coords.from_space(h.coordMatrix * (*h.p))), drawP.drag_point_radius()))) { + if(SCollision::collide(button.pos, SCollision::Circle(drawP.world.drawData.cam.c.to_space(objInfoBeingEdited->obj->coords.from_space(h.coordMatrix * (*h.p))), drawP.drag_point_radius()))) { pointDragging = &h; isMovingPoint = true; } } - if(!isMovingPoint && !objInfoBeingEdited->obj->collides_with_cam_coords(drawP.world.drawData.cam.c, cMouseCircle)) + if(!isMovingPoint && !objInfoBeingEdited->obj->collides_with_point(drawP.world.drawData.cam.c, button.pos)) clickedAway = true; } for(HandleData& h : pointHandles) { - if(SCollision::collide(mouseCircle, SCollision::Circle(drawP.world.drawData.cam.c.to_space(objInfoBeingEdited->obj->coords.from_space(h.coordMatrix * (*h.p))), drawP.drag_point_radius()))) { + if(SCollision::collide(button.pos, SCollision::Circle(drawP.world.drawData.cam.c.to_space(objInfoBeingEdited->obj->coords.from_space(h.coordMatrix * (*h.p))), drawP.drag_point_radius()))) { pointDragging = &h; isMovingPoint = true; break; } } - if(!isMovingPoint && !objInfoBeingEdited->obj->collides_with_cam_coords(drawP.world.drawData.cam.c, cMouseCircle)) + if(!isMovingPoint && !objInfoBeingEdited->obj->collides_with_point(drawP.world.drawData.cam.c, button.pos)) clickedAway = true; if(clickedAway) diff --git a/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp b/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp index 302701b..aea01bd 100644 --- a/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp +++ b/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp @@ -591,13 +591,11 @@ void TextBoxEditTool::input_mouse_button_on_canvas_callback(const InputManager:: if(button.button == InputManager::MouseButton::LEFT && !isDraggingPoint && userInput) { auto& a = static_cast(comp->obj->get_comp()); - SCollision::ColliderCollection mousePointCollection; - mousePointCollection.circle.emplace_back(button.pos, 1.0f); - mousePointCollection.recalculate_bounds(); + SkPath mousePoint = SkPath::Circle(button.pos.x(), button.pos.y(), 1.0f); InputManager& input = drawP.world.main.input; - bool collidesWithBox = comp->obj->collides_with_cam_coords(drawP.world.drawData.cam.c, mousePointCollection); + bool collidesWithBox = comp->obj->collides_with(drawP.world.drawData.cam.c, mousePoint); auto oldCursor = *a.cursor; diff --git a/src/DrawingProgram/Tools/EraserTool.cpp b/src/DrawingProgram/Tools/EraserTool.cpp index 8ed6d79..2eb12ba 100644 --- a/src/DrawingProgram/Tools/EraserTool.cpp +++ b/src/DrawingProgram/Tools/EraserTool.cpp @@ -22,6 +22,7 @@ #include "../../DrawData.hpp" #include #include +#include "../../CanvasComponents/BrushComponentCode.hpp" #include "../../GUIStuff/ElementHelpers/TextLabelHelpers.hpp" #include "../../GUIStuff/ElementHelpers/RadioButtonHelpers.hpp" @@ -74,7 +75,7 @@ void EraserTool::input_mouse_button_on_canvas_callback(const InputManager::Mouse if(button.button == InputManager::MouseButton::LEFT) { if(button.down && !isErasing && !drawP.world.main.g.gui.cursor_obstructed()) { isErasing = true; - erase_between_points(button.pos, button.pos); + start = end = button.pos; } else if(!button.down && isErasing) switch_tool(get_type()); @@ -82,29 +83,17 @@ void EraserTool::input_mouse_button_on_canvas_callback(const InputManager::Mouse } void EraserTool::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) { - if(isErasing) - erase_between_points(motion.pos, motion.pos - motion.move); + end = motion.pos; } -void EraserTool::erase_between_points(const Vector2f& start, const Vector2f& end) { - auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); - if(!relativeWidthResult.first.has_value()) { - drawP.world.main.toolConfig.print_relative_width_fail_message(relativeWidthResult.second); - isErasing = false; - return; - } - float width = relativeWidthResult.first.value() * 0.5f; - - SCollision::ColliderCollection cC; - SCollision::generate_wide_line(cC, start, end, width * 2.0f, true); - auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); - - drawP.drawCache.traverse_bvh_run_function(cCWorld.bounds, [&](const auto& bvhNode) { +void EraserTool::erase_between_points() { + auto cCWorldBounds = drawP.world.drawData.cam.c.collider_to_world, SCollision::AABB>(erasePath.getBounds()); + drawP.drawCache.traverse_bvh_run_function(cCWorldBounds, [&](const auto& bvhNode) { if(bvhNode && - SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) && - SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max)) && - SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right())) && - SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left()))) { + erasePath.contains(convert_vec2(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min))) && + erasePath.contains(convert_vec2(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max))) && + erasePath.contains(convert_vec2(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right()))) && + erasePath.contains(convert_vec2(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left())))) { drawP.drawCache.invalidate_cache_at_aabb(bvhNode->bounds); drawP.drawCache.traverse_bvh_run_function_starting_at_node_no_collision_check(bvhNode, [&](const auto& bvhNodeChild) { drawP.drawCache.node_loop_erase_if_components(bvhNodeChild, [&](auto c) { @@ -119,7 +108,7 @@ void EraserTool::erase_between_points(const Vector2f& start, const Vector2f& end return false; } drawP.drawCache.node_loop_erase_if_components(bvhNode, [&](auto c) { - if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) { + if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, erasePath)) { erasedComponents.emplace(c); drawP.drawCache.invalidate_cache_at_optional_aabb(c->obj->get_world_bounds()); return true; @@ -142,6 +131,31 @@ void EraserTool::switch_tool(DrawingProgramToolType newTool) { void EraserTool::tool_update() { if(!drawP.world.main.g.gui.cursor_obstructed()) drawP.world.main.input.hideCursor = true; + + auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); + if(!relativeWidthResult.first.has_value()) { + isErasing = false; + erasePath = eraseBorderPath = SkPath(); + return; + } + + float width = relativeWidthResult.first.value() * 0.5f; + using namespace BrushComponentCode; + std::vector brushPoints; + brushPoints.emplace_back(start, width); + if(start != end) + brushPoints.emplace_back(end, width); + erasePath = brush_stroke_to_skpath(brushPoints, true); + brushPoints.clear(); + brushPoints.emplace_back(start, width + 1.0f); + if(start != end) + brushPoints.emplace_back(end, width + 1.0f); + eraseBorderPath = brush_stroke_to_skpath(brushPoints, true); + + if(isErasing) + erase_between_points(); + + start = end; } bool EraserTool::prevent_undo_or_redo() { @@ -149,34 +163,14 @@ bool EraserTool::prevent_undo_or_redo() { } void EraserTool::draw(SkCanvas* canvas, const DrawData& drawData) { - if(!drawP.world.main.input.isTouchDevice && !drawData.main->g.gui.cursor_obstructed()) { - if(!lastPosOpt.has_value()) - lastPosOpt = drawData.main->input.mouse.pos; - auto& lastPos = lastPosOpt.value(); + if(!drawP.world.main.input.isTouchDevice && !drawData.main->g.gui.cursor_obstructed() && !erasePath.isEmpty() && !eraseBorderPath.isEmpty()) { + SkPaint linePaint; + linePaint.setAntiAlias(drawData.skiaAA); + linePaint.setColor4f({0.0f, 0.0f, 0.0f, 0.4f}); + linePaint.setStyle(SkPaint::kFill_Style); + canvas->drawPath(erasePath, linePaint); - auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); - if(relativeWidthResult.first.has_value()) { - float width = relativeWidthResult.first.value() * 0.5f; - SkPaint linePaint; - linePaint.setAntiAlias(drawData.skiaAA); - linePaint.setColor4f({0.0f, 0.0f, 0.0f, 0.4f}); - linePaint.setStyle(SkPaint::kStroke_Style); - linePaint.setStrokeCap(SkPaint::kRound_Cap); - linePaint.setStrokeWidth(width * 2.0f + 1.0f); - SkPathBuilder erasePath; - erasePath.moveTo(convert_vec2(lastPos)); - erasePath.lineTo(convert_vec2(drawData.main->input.mouse.pos)); - - SkPath erasePathDetach = erasePath.detach(); - canvas->drawPath(erasePathDetach, linePaint); - - linePaint.setStrokeWidth(width * 2.0f); - linePaint.setColor4f({1.0f, 1.0f, 1.0f, 0.4f}); - canvas->drawPath(erasePathDetach, linePaint); - } - - lastPos = drawData.main->input.mouse.pos; + linePaint.setColor4f({1.0f, 1.0f, 1.0f, 0.4f}); + canvas->drawPath(eraseBorderPath, linePaint); } - else - lastPosOpt = std::nullopt; } diff --git a/src/DrawingProgram/Tools/EraserTool.hpp b/src/DrawingProgram/Tools/EraserTool.hpp index 5f02eec..560b3d2 100644 --- a/src/DrawingProgram/Tools/EraserTool.hpp +++ b/src/DrawingProgram/Tools/EraserTool.hpp @@ -40,7 +40,12 @@ class EraserTool : public DrawingProgramToolBase { std::unordered_set erasedComponents; // Pointers will be erased from this set if theyre erased in the main list (done by callback) private: - void erase_between_points(const Vector2f& start, const Vector2f& end); - std::optional lastPosOpt; + SkPath erasePath; + SkPath eraseBorderPath; + + Vector2f start; + Vector2f end; + + void erase_between_points(); bool isErasing = false; }; diff --git a/src/DrawingProgram/Tools/FillTool.cpp b/src/DrawingProgram/Tools/FillTool.cpp index c3034fb..776bc19 100644 --- a/src/DrawingProgram/Tools/FillTool.cpp +++ b/src/DrawingProgram/Tools/FillTool.cpp @@ -59,10 +59,10 @@ void FillTool::input_mouse_button_on_canvas_callback(const InputManager::MouseBu MeshCanvasComponent& newMesh = static_cast(newContainer->get_comp()); newMesh.d.color = toolConfig.globalConf.foregroundColor; - newMesh.d.points.fill.emplace_back(); - newMesh.d.points.fill.back().emplace_back(button.pos + Vector2f{0.0f, 100.0f}); - newMesh.d.points.fill.back().emplace_back(button.pos + Vector2f{-100.0f, -100.0f}); - newMesh.d.points.fill.back().emplace_back(button.pos + Vector2f{100.0f, -100.0f}); + //newMesh.d.points.contours.emplace_back(); + //newMesh.d.points.contours.back().emplace_back(button.pos + Vector2f{0.0f, 100.0f}); + //newMesh.d.points.contours.back().emplace_back(button.pos + Vector2f{-100.0f, -100.0f}); + //newMesh.d.points.contours.back().emplace_back(button.pos + Vector2f{100.0f, -100.0f}); newContainer->coords = drawP.world.drawData.cam.c; objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newContainer); diff --git a/src/DrawingProgram/Tools/LassoSelectTool.cpp b/src/DrawingProgram/Tools/LassoSelectTool.cpp index 0b5b33d..6d5c60f 100644 --- a/src/DrawingProgram/Tools/LassoSelectTool.cpp +++ b/src/DrawingProgram/Tools/LassoSelectTool.cpp @@ -71,25 +71,14 @@ void LassoSelectTool::input_mouse_button_on_canvas_callback(const InputManager:: } else if(controls.isSelecting && button.button == InputManager::MouseButton::LEFT && !button.down) { if(controls.lassoPoints.size() > 3) { - SCollision::ColliderCollection cC; + SkPathBuilder cCBuild; - std::vector> polygon; - polygon.emplace_back(controls.lassoPoints); - auto& poly = polygon[0]; - for(auto& v : poly) - v = controls.coords.from_this_to_cam_space(drawP.world, v); + cCBuild.moveTo(convert_vec2(controls.coords.from_this_to_cam_space(drawP.world, controls.lassoPoints[0]))); + for(size_t i = 1; i < controls.lassoPoints.size(); i++) + cCBuild.lineTo(convert_vec2(controls.coords.from_this_to_cam_space(drawP.world, controls.lassoPoints[i]))); + cCBuild.close(); - std::vector indices = mapbox::earcut(polygon); - - for(size_t i = 0; i < indices.size(); i += 3) { - cC.triangle.emplace_back( - poly[indices[i]], - poly[indices[i + 1]], - poly[indices[i + 2]] - ); - } - - cC.recalculate_bounds(); + SkPath cC = cCBuild.detach(); if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) drawP.selection.add_from_cam_coord_collider_to_selection(cC, drawP.controls.layerSelector, false); diff --git a/src/DrawingProgram/Tools/RectSelectTool.cpp b/src/DrawingProgram/Tools/RectSelectTool.cpp index 5009493..1e73abb 100644 --- a/src/DrawingProgram/Tools/RectSelectTool.cpp +++ b/src/DrawingProgram/Tools/RectSelectTool.cpp @@ -68,12 +68,8 @@ void RectSelectTool::input_mouse_button_on_canvas_callback(const InputManager::M controls.selectEndAt = controls.coords.from_cam_space_to_this(drawP.world, button.pos); Vector2f newP1 = controls.coords.from_this_to_cam_space(drawP.world, cwise_vec_min(controls.selectStartAt, controls.selectEndAt)); Vector2f newP2 = controls.coords.from_this_to_cam_space(drawP.world, cwise_vec_max(controls.selectStartAt, controls.selectEndAt)); - std::array newT = triangle_from_rect_points(newP1, newP2); - ColliderCollection cC; - cC.triangle.emplace_back(newT[0], newT[1], newT[2]); - cC.triangle.emplace_back(newT[2], newT[3], newT[0]); - cC.recalculate_bounds(); + SkPath cC = SkPath::Rect(SkRect::MakeLTRB(newP1.x(), newP1.y(), newP2.x(), newP2.y())); if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) drawP.selection.add_from_cam_coord_collider_to_selection(cC, drawP.controls.layerSelector, false); diff --git a/src/VersionConstants.cpp b/src/VersionConstants.cpp index 096db9a..621b35d 100644 --- a/src/VersionConstants.cpp +++ b/src/VersionConstants.cpp @@ -29,6 +29,7 @@ VersionNumber header_to_version_number(const std::string& header) { m["INFPNT000003"] = VersionNumber(0, 2, 0); m["INFPNT000004"] = VersionNumber(0, 3, 0); m["INFPNT000005"] = VersionNumber(0, 4, 0); + m["INFPNT000006"] = VersionNumber(0, 6, 0); } auto it = m.find(header); if(it == m.end()) diff --git a/src/VersionConstants.hpp b/src/VersionConstants.hpp index 35693df..781a436 100644 --- a/src/VersionConstants.hpp +++ b/src/VersionConstants.hpp @@ -28,7 +28,7 @@ namespace VersionConstants { VersionNumber header_to_version_number(const std::string& header); constexpr int SAVEFILE_HEADER_LEN = 12; // DO NOT CHANGE THIS HEADER LENGTH - const std::string CURRENT_SAVEFILE_HEADER = "INFPNT000005"; // Change whenever the save file is incompatible with the previous version - const std::string CURRENT_VERSION_STRING = "0.5.0"; - constexpr VersionNumber CURRENT_VERSION_NUMBER(0, 5, 0); + const std::string CURRENT_SAVEFILE_HEADER = "INFPNT000006"; // Change whenever the save file is incompatible with the previous version + const std::string CURRENT_VERSION_STRING = "0.6.0"; + constexpr VersionNumber CURRENT_VERSION_NUMBER(0, 6, 0); } diff --git a/windowsinstall/infinipaint.manifest b/windowsinstall/infinipaint.manifest index 36b0687..81997c6 100644 --- a/windowsinstall/infinipaint.manifest +++ b/windowsinstall/infinipaint.manifest @@ -1,6 +1,6 @@ - +