Rewrite collision detection

This commit is contained in:
Yousef Khadadeh 2026-05-24 11:58:52 +03:00
parent 7b472fe18d
commit 60ab8f3c73
35 changed files with 292 additions and 258 deletions

View file

@ -27,39 +27,9 @@
namespace BrushComponentCode {
MeshShapeData brush_stroke_to_mesh_points(const std::vector<BrushPoint>& brushPoints, bool hasRoundCaps) {
SkPath brush_stroke_to_skpath(const std::vector<BrushPoint>& brushPoints, bool hasRoundCaps) {
std::vector<BrushPoint> points = smooth_points(brushPoints, 0, brushPoints.size() - 1, DEFAULT_SMOOTHNESS);
std::vector<Vector2f> 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<size_t> get_wedge_indices(const std::vector<BrushPoint>& points) {
@ -119,7 +89,8 @@ std::vector<BrushPoint> smooth_points(const std::vector<BrushPoint>& points, siz
return toRet;
}
void create_triangles(std::vector<Vector2f>& brushMeshPointList, const std::vector<BrushPoint>& regularPoints, const std::vector<BrushPoint>& smoothedPoints, bool hasRoundCaps) {
SkPath create_triangles(const std::vector<BrushPoint>& regularPoints, const std::vector<BrushPoint>& smoothedPoints, bool hasRoundCaps) {
SkPathBuilder pathBuilder;
const int ARC_SMOOTHNESS = 10;
const int CIRCLE_SMOOTHNESS = 20;
const std::vector<BrushPoint>& pointsN = regularPoints;
@ -135,10 +106,10 @@ void create_triangles(std::vector<Vector2f>& brushMeshPointList, const std::vect
if(pointsN.size() < 2) {
if(!topPoints.empty()) {
for(auto& p : topPoints)
brushMeshPointList.emplace_back(convert_vec2<Vector2f>(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<Vector2f>& brushMeshPointList, const std::vect
if(!topPoints.empty()) {
topPoints.insert(topPoints.begin(), bottomPoints.rbegin(), bottomPoints.rend());
for(auto& p : topPoints)
brushMeshPointList.emplace_back(convert_vec2<Vector2f>(p));
pathBuilder.addPolygon({topPoints.data(), topPoints.size()}, true);
}
return pathBuilder.detach();
}
}

View file

@ -36,8 +36,8 @@ namespace BrushComponentCode {
}
};
MeshShapeData brush_stroke_to_mesh_points(const std::vector<BrushPoint>& brushPoints, bool hasRoundCaps);
void create_triangles(std::vector<Vector2f>& brushMeshPointList, const std::vector<BrushPoint>& regularPoints, const std::vector<BrushPoint>& smoothedPoints, bool hasRoundCaps);
SkPath brush_stroke_to_skpath(const std::vector<BrushPoint>& brushPoints, bool hasRoundCaps);
SkPath create_triangles(const std::vector<BrushPoint>& regularPoints, const std::vector<BrushPoint>& smoothedPoints, bool hasRoundCaps);
std::vector<size_t> get_wedge_indices(const std::vector<BrushPoint>& points);
std::vector<BrushPoint> smooth_points(const std::vector<BrushPoint>& points, size_t beginIndex, size_t endIndex, unsigned numOfDivisions);
}

View file

@ -56,7 +56,7 @@ class CanvasComponent {
virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<void>& predrawData) const = 0;
virtual void initialize_draw_data(DrawingProgram& drawP) = 0;
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& 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<float> get_obj_coord_bounds() const = 0;

View file

@ -23,6 +23,7 @@
#include "../World.hpp"
#include "CanvasComponent.hpp"
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
#include <include/core/SkScalar.h>
#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<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 {
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<SkPoint>(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);
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<SkPath> 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<SkPoint>(checkAgainstCam));
return get_comp().collides_within_coords_point(convert_vec2<Vector2f>(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<float>(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<float>(coords.inverseScale / camCoords.inverseScale), COMP_MAX_BEFORE_STOP_SCALING);
return toRet;
}

View file

@ -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<WorldScalar>& checkAgainstWorld) const;
bool collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<float>& checkAgainstCam) const;
bool collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection<WorldScalar>& checkAgainstWorld, const SCollision::ColliderCollection<float>& 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);

View file

@ -90,12 +90,19 @@ void EllipseCanvasComponent::set_data_from(const CanvasComponent& other) {
d = otherEllipse.d;
}
bool EllipseCanvasComponent::collides_within_coords(const SCollision::ColliderCollection<float>& 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<SkPath> pathIntersectCheck = Op(checkAgainst, ellipsePath, SkPathOp::kIntersect_SkPathOp);
return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty();
}
SCollision::AABB<float> EllipseCanvasComponent::get_obj_coord_bounds() const {

View file

@ -48,7 +48,7 @@ class EllipseCanvasComponent : public CanvasComponent {
void create_draw_data();
virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<void>& predrawData) const override;
virtual void initialize_draw_data(DrawingProgram& drawP) override;
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& 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<float> get_obj_coord_bounds() const override;
void create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc);

View file

@ -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<float>& checkAgainst) const {
return false;
bool ImageCanvasComponent::collides_within_coords_point(const Vector2f& checkAgainst) const {
SkPath p = SkPath::Rect(SCollision::AABB<float>(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<SkPath> pathIntersectCheck = Op(checkAgainst, p, SkPathOp::kIntersect_SkPathOp);
return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty();
}
SCollision::AABB<float> ImageCanvasComponent::get_obj_coord_bounds() const {

View file

@ -55,7 +55,7 @@ class ImageCanvasComponent : public CanvasComponent {
private:
virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<void>& predrawData) const override;
virtual void initialize_draw_data(DrawingProgram& drawP) override;
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& 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<float> get_obj_coord_bounds() const override;
};

View file

@ -19,8 +19,10 @@
#include "MeshCanvasComponent.hpp"
#include <Helpers/Serializers.hpp>
#include <cereal/types/vector.hpp>
#include <include/core/SkPathTypes.h>
#include <limits>
#include <Eigen/Geometry>
#include "BrushComponentCode.hpp"
#include "Eigen/Core"
#include "Helpers/MathExtras.hpp"
#include "../TimePoint.hpp"
@ -34,26 +36,75 @@
#define BVH_LEVELS 3
template <typename Archive> void skpath_write(const SkPath& p, Archive& a) {
std::vector<uint32_t> 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<unsigned>(verbs[verbIndex])));
break;
}
}
a(contourSizes);
for(const SkPoint& p : points)
a(p);
}
template <typename Archive> SkPath skpath_read(Archive& a) {
SkPathBuilder builder;
std::vector<uint32_t> 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<BrushComponentCode::BrushPoint> 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<float>& 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<SkPath> 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<float> viewGenerousColliderInObjSpace = coords.world_collider_to_coords<SCollision::AABB<float>>(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<float> MeshCanvasComponent::get_obj_coord_bounds() const {
return meshPath.getBounds();
return d.meshPath.getBounds();
}

View file

@ -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<void>& predrawData) const override;
virtual std::shared_ptr<void> 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<float>& 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<float> get_obj_coord_bounds() const override;
SkPath meshPath;
SCollision::BVHContainer<float> collisionTree;
};

View file

@ -87,10 +87,6 @@ void RectangleCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
create_draw_data();
}
bool RectangleCanvasComponent::collides_within_coords(const SCollision::ColliderCollection<float>& 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<SkPath> pathIntersectCheck = Op(checkAgainst, rectPath, SkPathOp::kIntersect_SkPathOp);
return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty();
}
SCollision::AABB<float> RectangleCanvasComponent::get_obj_coord_bounds() const {

View file

@ -49,7 +49,7 @@ class RectangleCanvasComponent : public CanvasComponent {
private:
virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<void>& predrawData) const override;
virtual void initialize_draw_data(DrawingProgram& drawP) override;
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& 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<float> get_obj_coord_bounds() const override;

View file

@ -128,13 +128,21 @@ void TextBoxCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
init_text_box(drawP);
}
bool TextBoxCanvasComponent::collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const {
return false;
bool TextBoxCanvasComponent::collides_within_coords_point(const Vector2f& checkAgainst) const {
SkPath p = SkPath::Rect(SCollision::AABB<float>(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<float>(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<SkPath> pathIntersectCheck = Op(checkAgainst, p, SkPathOp::kIntersect_SkPathOp);
return pathIntersectCheck.has_value() && !pathIntersectCheck.value().isEmpty();
}
SCollision::AABB<float> TextBoxCanvasComponent::get_obj_coord_bounds() const {

View file

@ -50,7 +50,7 @@ class TextBoxCanvasComponent : public CanvasComponent {
virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<void>& predrawData) const override;
virtual void initialize_draw_data(DrawingProgram& drawP) override;
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& 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;

View file

@ -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<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(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<WorldScalar>, SCollision::AABB<float>>(cC.getBounds());
CanvasComponentContainer::ObjInfo* p = nullptr;
traverse_bvh_run_function(cCWorld.bounds, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
traverse_bvh_run_function(cCWorldBounds, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& 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;

View file

@ -59,7 +59,7 @@ class DrawingProgramCache {
void invalidate_cache_at_optional_aabb(const std::optional<SCollision::AABB<WorldScalar>>& aabb);
static void delete_all_draw_cache();
CanvasComponentContainer::ObjInfo* get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection<float>& cC);
CanvasComponentContainer::ObjInfo* get_front_object_colliding_with_in_editing_layer(const SkPath& cC);
~DrawingProgramCache();
private:
struct NodeCache {

View file

@ -252,7 +252,7 @@ void DrawingProgramSelection::check_add_stroke_color_change_undo() {
}
}
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& 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<CanvasComponentContainer::ObjInfo*> 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<WorldScalar>, SCollision::ColliderCollection<float>>(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<WorldScalar>, SCollision::AABB<float>>(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<float>& 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<WorldScalar>, SCollision::ColliderCollection<float>>(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<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::vector<CanvasComponentContainer::ObjInfo*>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld, DrawingProgramLayerManager::LayerSelector layerSelector) {
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::vector<CanvasComponentContainer::ObjInfo*>& 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<SkPoint>(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min))) &&
cC.contains(convert_vec2<SkPoint>(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max))) &&
cC.contains(convert_vec2<SkPoint>(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right()))) &&
cC.contains(convert_vec2<SkPoint>(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<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> 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<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(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;

View file

@ -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<float>& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly);
void remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& 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<float>& 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<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> erase_select_objects_in_bvh_func(std::vector<CanvasComponentContainer::ObjInfo*>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld, DrawingProgramLayerManager::LayerSelector layerSelector);
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> erase_select_objects_in_bvh_func(std::vector<CanvasComponentContainer::ObjInfo*>& selectedComponents, const SkPath& cC, DrawingProgramLayerManager::LayerSelector layerSelector);
SCollision::ColliderCollection<float> camSpaceSelection;
std::array<WorldVec, 4> selectionRectPoints;

View file

@ -117,7 +117,7 @@ void BrushTool::commit_data(bool final) {
if(objInfoBeingEdited) {
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
MeshCanvasComponent& newMesh = static_cast<MeshCanvasComponent&>(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);
}

View file

@ -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<WorldScalar> mouseAABB{mouseWorldPos - WorldVec{0.5f, 0.5f}, mouseWorldPos + WorldVec{0.5f, 0.5f}};
SCollision::ColliderCollection<WorldScalar> cMouseAABB;
cMouseAABB.aabb.emplace_back(mouseAABB);
cMouseAABB.recalculate_bounds();
SCollision::AABB<float> camMouseAABB{drawP.world.main.input.mouse.pos - Vector2f{0.5f, 0.5f}, button.pos + Vector2f{0.5f, 0.5f}};
SCollision::ColliderCollection<float> 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<float> mouseCircle{button.pos, 1.0f};
SCollision::ColliderCollection<float> 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<float>(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<float>(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<float>(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<float>(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)

View file

@ -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<TextBoxCanvasComponent&>(comp->obj->get_comp());
SCollision::ColliderCollection<float> 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;

View file

@ -22,6 +22,7 @@
#include "../../DrawData.hpp"
#include <include/core/SkBlendMode.h>
#include <include/core/SkPathBuilder.h>
#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<float> cC;
SCollision::generate_wide_line(cC, start, end, width * 2.0f, true);
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(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<WorldScalar>, SCollision::AABB<float>>(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<SkPoint>(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min))) &&
erasePath.contains(convert_vec2<SkPoint>(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max))) &&
erasePath.contains(convert_vec2<SkPoint>(drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right()))) &&
erasePath.contains(convert_vec2<SkPoint>(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<BrushPoint> 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<SkPoint>(lastPos));
erasePath.lineTo(convert_vec2<SkPoint>(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;
}

View file

@ -40,7 +40,12 @@ class EraserTool : public DrawingProgramToolBase {
std::unordered_set<CanvasComponentContainer::ObjInfo*> 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<Vector2f> lastPosOpt;
SkPath erasePath;
SkPath eraseBorderPath;
Vector2f start;
Vector2f end;
void erase_between_points();
bool isErasing = false;
};

View file

@ -59,10 +59,10 @@ void FillTool::input_mouse_button_on_canvas_callback(const InputManager::MouseBu
MeshCanvasComponent& newMesh = static_cast<MeshCanvasComponent&>(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);

View file

@ -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<float> cC;
SkPathBuilder cCBuild;
std::vector<std::vector<Vector2f>> 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<SkPoint>(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<SkPoint>(controls.coords.from_this_to_cam_space(drawP.world, controls.lassoPoints[i])));
cCBuild.close();
std::vector<unsigned> 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);

View file

@ -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<Vector2f, 4> newT = triangle_from_rect_points(newP1, newP2);
ColliderCollection<float> 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);

View file

@ -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())

View file

@ -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);
}