Can edit stroke colors through selection
This commit is contained in:
parent
f6c4eeb9f3
commit
a73f5e2122
18 changed files with 283 additions and 51 deletions
|
|
@ -83,6 +83,7 @@ set(sources "src/main.cpp"
|
|||
"src/DrawingProgram/Tools/EditTools/EllipseDrawEditTool.cpp"
|
||||
"src/DrawingProgram/Tools/EditTools/RectDrawEditTool.cpp"
|
||||
"src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp"
|
||||
"src/DrawingProgram/Tools/EditTools/BrushEditTool.cpp"
|
||||
"src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp"
|
||||
"src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp"
|
||||
"src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp"
|
||||
|
|
|
|||
|
|
@ -14,19 +14,19 @@
|
|||
#define DEFAULT_SMOOTHNESS 5
|
||||
|
||||
void BrushStrokeCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
a(*d.points, d.color, d.hasRoundCaps);
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
a(*d.points, d.color, d.hasRoundCaps);
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
a(*d.points, d.color, d.hasRoundCaps);
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
|
||||
a(d->points, d->color, d->hasRoundCaps);
|
||||
a(*d.points, d.color, d.hasRoundCaps);
|
||||
}
|
||||
|
||||
std::unique_ptr<CanvasComponent> BrushStrokeCanvasComponent::get_data_copy() const {
|
||||
|
|
@ -39,9 +39,17 @@ CanvasComponentType BrushStrokeCanvasComponent::get_type() const {
|
|||
return CanvasComponentType::BRUSHSTROKE;
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::change_stroke_color(const Vector4f& newStrokeColor) {
|
||||
d.color = newStrokeColor;
|
||||
}
|
||||
|
||||
std::optional<Vector4f> BrushStrokeCanvasComponent::get_stroke_color() const {
|
||||
return d.color;
|
||||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::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.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), d.color.w()});
|
||||
|
||||
unsigned mipmapLevel = compContainer ? compContainer->get_mipmap_level(drawData) : 0;
|
||||
|
||||
|
|
@ -57,8 +65,8 @@ void BrushStrokeCanvasComponent::set_data_from(const CanvasComponent& other) {
|
|||
}
|
||||
|
||||
void BrushStrokeCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
|
||||
if(!d->points.empty()) {
|
||||
std::vector<BrushStrokeCanvasComponentPoint> points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS);
|
||||
if(!d.points->empty()) {
|
||||
std::vector<BrushStrokeCanvasComponentPoint> points = smooth_points(0, d.points->size() - 1, DEFAULT_SMOOTHNESS);
|
||||
auto triangleFunc = [&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
return false;
|
||||
};
|
||||
|
|
@ -94,7 +102,7 @@ std::vector<size_t> BrushStrokeCanvasComponent::get_wedge_indices(const std::vec
|
|||
}
|
||||
|
||||
std::vector<BrushStrokeCanvasComponentPoint> BrushStrokeCanvasComponent::smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const {
|
||||
std::vector<BrushStrokeCanvasComponentPoint>& points = d->points;
|
||||
std::vector<BrushStrokeCanvasComponentPoint>& points = *d.points;
|
||||
|
||||
size_t pointsSize = endIndex - beginIndex + 1;
|
||||
if(pointsSize < 2)
|
||||
|
|
@ -138,7 +146,7 @@ std::vector<BrushStrokeCanvasComponentPoint> BrushStrokeCanvasComponent::smooth_
|
|||
}
|
||||
|
||||
std::vector<BrushStrokeCanvasComponentPoint> BrushStrokeCanvasComponent::smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const {
|
||||
std::vector<BrushStrokeCanvasComponentPoint>& points = d->points;
|
||||
std::vector<BrushStrokeCanvasComponentPoint>& points = *d.points;
|
||||
|
||||
size_t pointsSize = endIndex - beginIndex + 1;
|
||||
if(pointsSize < 2)
|
||||
|
|
@ -180,11 +188,11 @@ std::vector<BrushStrokeCanvasComponentPoint> BrushStrokeCanvasComponent::every_n
|
|||
void BrushStrokeCanvasComponent::create_triangles(const std::function<bool(Vector2f, Vector2f, Vector2f)>& passTriangleFunc, const std::vector<BrushStrokeCanvasComponentPoint>& smoothedPoints, size_t skipVertexCount, std::shared_ptr<SkPathBuilder> bPathBuilder) const {
|
||||
const int ARC_SMOOTHNESS = 10;
|
||||
const int CIRCLE_SMOOTHNESS = 20;
|
||||
const std::vector<BrushStrokeCanvasComponentPoint>& pointsN = d->points;
|
||||
const std::vector<BrushStrokeCanvasComponentPoint>& pointsN = *d.points;
|
||||
std::vector<SkPoint> topPoints;
|
||||
std::vector<SkPoint> bottomPoints;
|
||||
|
||||
if(pointsN.size() == 1 && d->hasRoundCaps) {
|
||||
if(pointsN.size() == 1 && d.hasRoundCaps) {
|
||||
std::vector<Vector2f> circlePoints = gen_circle_points(pointsN.front().pos, pointsN.front().width / 2.0f, CIRCLE_SMOOTHNESS);
|
||||
for(size_t i = 0; i < circlePoints.size(); i++) {
|
||||
if(passTriangleFunc(pointsN.front().pos, circlePoints[i], circlePoints[(i + 1) % circlePoints.size()])) return;
|
||||
|
|
@ -218,7 +226,7 @@ void BrushStrokeCanvasComponent::create_triangles(const std::function<bool(Vecto
|
|||
bottomPoints.emplace_back(convert_vec2<SkPoint>(vertArray[1]));
|
||||
newIndex = 2;
|
||||
|
||||
if(d->hasRoundCaps && pointsBegin == 0) {
|
||||
if(d.hasRoundCaps && pointsBegin == 0) {
|
||||
Vector2f arcDirStart = (convert_vec2<Vector2f>(vertArray[0]) - points[0].pos).normalized();
|
||||
Vector2f arcDirEnd = (convert_vec2<Vector2f>(vertArray[1]) - points[0].pos).normalized();
|
||||
Vector2f arcDirCenter = (points[0].pos - points[1].pos).normalized();
|
||||
|
|
@ -326,7 +334,7 @@ void BrushStrokeCanvasComponent::create_triangles(const std::function<bool(Vecto
|
|||
else
|
||||
bottomPoints.emplace_back(convert_vec2<SkPoint>(arcPoints.back()));
|
||||
}
|
||||
else if(d->hasRoundCaps) { // It's the last point, cap it off
|
||||
else if(d.hasRoundCaps) { // It's the last point, cap it off
|
||||
Vector2f arcDirStart = (convert_vec2<Vector2f>(vertArray[(newIndex + 1) % 3]) - points.back().pos).normalized();
|
||||
Vector2f arcDirEnd = (convert_vec2<Vector2f>(vertArray[(newIndex + 2) % 3]) - points.back().pos).normalized();
|
||||
Vector2f arcDirCenter = -rectDir;
|
||||
|
|
@ -349,10 +357,10 @@ void BrushStrokeCanvasComponent::create_triangles(const std::function<bool(Vecto
|
|||
|
||||
void BrushStrokeCanvasComponent::create_collider() {
|
||||
using namespace SCollision;
|
||||
if(d->points.empty())
|
||||
if(d.points->empty())
|
||||
return;
|
||||
ColliderCollection<float> strokeObjects;
|
||||
std::vector<BrushStrokeCanvasComponentPoint> points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS);
|
||||
std::vector<BrushStrokeCanvasComponentPoint> points = smooth_points(0, d.points->size() - 1, DEFAULT_SMOOTHNESS);
|
||||
create_triangles([&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
strokeObjects.triangle.emplace_back(a, b, c);
|
||||
return false;
|
||||
|
|
@ -392,7 +400,7 @@ bool BrushStrokeCanvasComponent::collides_within_coords(const SCollision::Collid
|
|||
return false;
|
||||
|
||||
bool toRet = false;
|
||||
std::vector<BrushStrokeCanvasComponentPoint> points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS);
|
||||
std::vector<BrushStrokeCanvasComponentPoint> points = smooth_points(0, d.points->size() - 1, DEFAULT_SMOOTHNESS);
|
||||
create_triangles([&](Vector2f a, Vector2f b, Vector2f c) {
|
||||
toRet = SCollision::collide(checkAgainst, SCollision::Triangle<float>{a, b, c});
|
||||
return toRet;
|
||||
|
|
|
|||
|
|
@ -23,13 +23,14 @@ class BrushStrokeCanvasComponent : public CanvasComponent {
|
|||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override;
|
||||
virtual std::unique_ptr<CanvasComponent> get_data_copy() const override;
|
||||
virtual void change_stroke_color(const Vector4f& newStrokeColor) override;
|
||||
virtual std::optional<Vector4f> get_stroke_color() const override;
|
||||
|
||||
struct Data {
|
||||
std::vector<BrushStrokeCanvasComponentPoint> points;
|
||||
std::shared_ptr<std::vector<BrushStrokeCanvasComponentPoint>> points = std::make_shared<std::vector<BrushStrokeCanvasComponentPoint>>(); // It's a pointer here since brush strokes cant be edited
|
||||
Vector4f color;
|
||||
bool hasRoundCaps;
|
||||
};
|
||||
std::shared_ptr<Data> d = std::make_shared<Data>(); // It's a pointer here since brush strokes cant be edited
|
||||
} d;
|
||||
|
||||
virtual void set_data_from(const CanvasComponent& other) override;
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -30,4 +30,11 @@ CanvasComponent* CanvasComponent::allocate_comp(CanvasComponentType type) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void CanvasComponent::change_stroke_color(const Vector4f& newStrokeColor) {
|
||||
}
|
||||
|
||||
std::optional<Vector4f> CanvasComponent::get_stroke_color() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CanvasComponent::~CanvasComponent() {}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ class CanvasComponent {
|
|||
virtual void update(DrawingProgram& drawP);
|
||||
virtual void get_used_resources(std::unordered_set<NetworkingObjects::NetObjID>& resourceSet) const;
|
||||
virtual void remap_resource_ids(const std::unordered_map<NetworkingObjects::NetObjID, NetworkingObjects::NetObjID>& resourceOldToNewMap);
|
||||
virtual void change_stroke_color(const Vector4f& newStrokeColor);
|
||||
virtual std::optional<Vector4f> get_stroke_color() const;
|
||||
|
||||
virtual void set_data_from(const CanvasComponent& other) = 0;
|
||||
virtual std::unique_ptr<CanvasComponent> get_data_copy() const = 0;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ void EllipseCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, Ve
|
|||
a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::change_stroke_color(const Vector4f& newStrokeColor) {
|
||||
d.strokeColor = newStrokeColor;
|
||||
}
|
||||
|
||||
std::optional<Vector4f> EllipseCanvasComponent::get_stroke_color() const {
|
||||
return d.strokeColor;
|
||||
}
|
||||
|
||||
void EllipseCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const {
|
||||
SkPaint p;
|
||||
if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ class EllipseCanvasComponent : public CanvasComponent {
|
|||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override;
|
||||
virtual void change_stroke_color(const Vector4f& newStrokeColor) override;
|
||||
virtual std::optional<Vector4f> get_stroke_color() const override;
|
||||
virtual std::unique_ptr<CanvasComponent> get_data_copy() const override;
|
||||
virtual void set_data_from(const CanvasComponent& other) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,14 @@ void RectangleCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a,
|
|||
a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode);
|
||||
}
|
||||
|
||||
void RectangleCanvasComponent::change_stroke_color(const Vector4f& newStrokeColor) {
|
||||
d.strokeColor = newStrokeColor;
|
||||
}
|
||||
|
||||
std::optional<Vector4f> RectangleCanvasComponent::get_stroke_color() const {
|
||||
return d.strokeColor;
|
||||
}
|
||||
|
||||
std::unique_ptr<CanvasComponent> RectangleCanvasComponent::get_data_copy() const {
|
||||
auto toRet = std::make_unique<RectangleCanvasComponent>();
|
||||
toRet->d = d;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ class RectangleCanvasComponent : public CanvasComponent {
|
|||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override;
|
||||
virtual void change_stroke_color(const Vector4f& newStrokeColor) override;
|
||||
virtual std::optional<Vector4f> get_stroke_color() const override;
|
||||
virtual CanvasComponentType get_type() const override;
|
||||
std::unique_ptr<CanvasComponent> get_data_copy() const override;
|
||||
virtual void set_data_from(const CanvasComponent& other) override;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,106 @@ DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP):
|
|||
drawP(initDrawP)
|
||||
{}
|
||||
|
||||
void DrawingProgramSelection::selection_gui() {
|
||||
auto& t = drawP.world.main.toolbar;
|
||||
t.gui.push_id("general selection gui");
|
||||
t.gui.text_label("Select from:");
|
||||
if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED))
|
||||
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED;
|
||||
if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS))
|
||||
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS;
|
||||
if(is_something_selected()) {
|
||||
t.gui.left_to_right_line_layout([&]() {
|
||||
if(t.gui.color_button_big("Stroke Color Button", &strokeColorChangeData.newColor, &strokeColorChangeData.newColor == t.colorRight)) {
|
||||
if(strokeColorChangeData.newColor.w() == 0.0f)
|
||||
strokeColorChangeData.newColor.w() = 1.0f;
|
||||
t.color_selector_right(&strokeColorChangeData.newColor == t.colorRight ? nullptr : &strokeColorChangeData.newColor);
|
||||
}
|
||||
t.gui.text_label("Stroke Color");
|
||||
});
|
||||
update_selection_stroke_color();
|
||||
}
|
||||
t.gui.pop_id();
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::update_selection_stroke_color() {
|
||||
if(strokeColorChangeData.oldColor != strokeColorChangeData.newColor) {
|
||||
for(auto& c : selectedSet) {
|
||||
if(c->obj->get_comp().get_stroke_color() != std::nullopt) {
|
||||
strokeColorChangeData.oldColorData.emplace(c->obj.get_net_id(), c->obj->get_comp().get_stroke_color().value());
|
||||
c->obj->get_comp().change_stroke_color(strokeColorChangeData.newColor);
|
||||
c->obj->commit_update_dont_invalidate_cache(drawP); // Object isn't in the cache since it's selected
|
||||
c->obj->send_comp_update(drawP, false);
|
||||
}
|
||||
}
|
||||
strokeColorChangeData.oldColor = strokeColorChangeData.newColor;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::check_add_stroke_color_change_undo() {
|
||||
if(!strokeColorChangeData.oldColorData.empty()) {
|
||||
class EditCanvasComponentsStrokeColorWorldUndoAction : public WorldUndoAction {
|
||||
public:
|
||||
EditCanvasComponentsStrokeColorWorldUndoAction(std::vector<WorldUndoManager::UndoObjectID> initUndoIDs, std::vector<Vector4f> initOldColors):
|
||||
undoIDs(std::move(initUndoIDs)),
|
||||
colors(std::move(initOldColors))
|
||||
{}
|
||||
std::string get_name() const override {
|
||||
return "Edit Stroke Colors";
|
||||
}
|
||||
bool undo(WorldUndoManager& undoMan) override {
|
||||
return undo_redo(undoMan);
|
||||
}
|
||||
bool redo(WorldUndoManager& undoMan) override {
|
||||
return undo_redo(undoMan);
|
||||
}
|
||||
bool undo_redo(WorldUndoManager& undoMan) {
|
||||
std::vector<NetworkingObjects::NetObjID> netIDs;
|
||||
|
||||
if(!undoMan.fill_netid_list_from_undoid_list(netIDs, undoIDs))
|
||||
return false;
|
||||
|
||||
undoMan.world.netObjMan.send_multi_update_messsage([&]() {
|
||||
for(size_t i = 0; i < netIDs.size(); i++) {
|
||||
auto objPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id<CanvasComponentContainer>(netIDs[i]);
|
||||
if(objPtr) {
|
||||
Vector4f oldColor = objPtr->get_comp().get_stroke_color().value();
|
||||
objPtr->get_comp().change_stroke_color(colors[i]);
|
||||
colors[i] = oldColor;
|
||||
objPtr->commit_update(undoMan.world.drawProg);
|
||||
objPtr->send_comp_update(undoMan.world.drawProg, true);
|
||||
}
|
||||
}
|
||||
}, NetworkingObjects::NetObjManager::SendUpdateType::SEND_TO_ALL, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
~EditCanvasComponentsStrokeColorWorldUndoAction() {}
|
||||
|
||||
std::vector<WorldUndoManager::UndoObjectID> undoIDs;
|
||||
std::vector<Vector4f> colors;
|
||||
};
|
||||
|
||||
std::vector<WorldUndoManager::UndoObjectID> undoIDs;
|
||||
std::vector<Vector4f> oldColors;
|
||||
|
||||
for(auto& [netID, strokeColor] : strokeColorChangeData.oldColorData) {
|
||||
undoIDs.emplace_back(drawP.world.undo.get_undoid_from_netid(netID));
|
||||
oldColors.emplace_back(strokeColor);
|
||||
}
|
||||
|
||||
drawP.world.undo.push(std::make_unique<EditCanvasComponentsStrokeColorWorldUndoAction>(std::move(undoIDs), std::move(oldColors)));
|
||||
strokeColorChangeData.oldColorData.clear();
|
||||
|
||||
auto& t = drawP.world.main.toolbar;
|
||||
if(&strokeColorChangeData.newColor == t.colorRight)
|
||||
t.colorRight = nullptr; // Just making sure that the color selector doesn't leak
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
std::vector<CanvasComponentContainer::ObjInfo*> selectedComponents;
|
||||
if(frontObjectOnly) {
|
||||
CanvasComponentContainer::ObjInfo* a = drawP.drawCache.get_front_object_colliding_with_in_editing_layer(cC);
|
||||
|
|
@ -44,6 +143,8 @@ void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCo
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
if(frontObjectOnly) {
|
||||
CanvasComponentContainer::ObjInfo* p = get_front_object_colliding_with_in_editing_layer(cC);
|
||||
if(p) {
|
||||
|
|
@ -139,11 +240,15 @@ bool DrawingProgramSelection::is_selected(CanvasComponentContainer::ObjInfo* obj
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::reset_all() {
|
||||
check_add_stroke_color_change_undo();
|
||||
selectedSet.clear();
|
||||
reset_transform_data();
|
||||
strokeColorChangeData = StrokeColorChangeData();
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::reset_transform_data() {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
selectionTransformCoords = CoordSpaceHelperTransform();
|
||||
transformOpHappening = TransformOperation::NONE;
|
||||
translateData = TranslationData();
|
||||
|
|
@ -168,6 +273,8 @@ bool DrawingProgramSelection::mouse_collided_with_rotate_handle_point() {
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::commit_transform_selection() {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
if(!is_empty_transform()) {
|
||||
std::vector<WorldUndoManager::UndoObjectID> undoIDList;
|
||||
std::vector<std::pair<CoordSpaceHelper, CoordSpaceHelper>> transformDataList;
|
||||
|
|
@ -267,17 +374,22 @@ void DrawingProgramSelection::update() {
|
|||
scaleData.currentPos = scaleData.startPos = selectionTransformCoords.from_space_world(initialSelectionAABB.max);
|
||||
scaleData.centerPos = selectionTransformCoords.from_space_world(initialSelectionAABB.center());
|
||||
transformOpHappening = TransformOperation::SCALE;
|
||||
check_add_stroke_color_change_undo();
|
||||
}
|
||||
else if(mouse_collided_with_rotate_center_handle_point())
|
||||
else if(mouse_collided_with_rotate_center_handle_point()) {
|
||||
transformOpHappening = TransformOperation::ROTATE_RELOCATE_CENTER;
|
||||
check_add_stroke_color_change_undo();
|
||||
}
|
||||
else if(mouse_collided_with_rotate_handle_point()) {
|
||||
rotateData.rotationAngle = 0.0;
|
||||
transformOpHappening = TransformOperation::ROTATE;
|
||||
check_add_stroke_color_change_undo();
|
||||
}
|
||||
else if(mouse_collided_with_selection_aabb()) {
|
||||
translateData.startPos = drawP.world.get_mouse_world_pos();
|
||||
transformOpHappening = TransformOperation::TRANSLATE;
|
||||
translateData.translateWithKeys = false;
|
||||
check_add_stroke_color_change_undo();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -297,6 +409,7 @@ void DrawingProgramSelection::update() {
|
|||
selectionTransformCoords = CoordSpaceHelperTransform(translateData.startPos);
|
||||
transformOpHappening = TransformOperation::TRANSLATE;
|
||||
translateData.keyTranslateLastMoveTime = std::chrono::steady_clock::now();
|
||||
check_add_stroke_color_change_undo();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -410,6 +523,8 @@ void DrawingProgramSelection::update() {
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::deselect_all() {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
if(is_something_selected()) {
|
||||
commit_transform_selection();
|
||||
for(auto& obj : selectedSet)
|
||||
|
|
@ -419,18 +534,24 @@ void DrawingProgramSelection::deselect_all() {
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::push_selection_to_front() {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
auto selectedVec = selectedSet;
|
||||
deselect_all();
|
||||
drawP.layerMan.push_components_to(selectedVec, false);
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::push_selection_to_back() {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
auto selectedVec = selectedSet;
|
||||
deselect_all();
|
||||
drawP.layerMan.push_components_to(selectedVec, true);
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::delete_all() {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
if(is_something_selected()) {
|
||||
auto selectedSetTemp = selectedSet;
|
||||
reset_all(); // Clear set before erasing, since calling erase_component_set will run a check to see if each object is selected and erase them one by one, which is slower
|
||||
|
|
@ -439,6 +560,8 @@ void DrawingProgramSelection::delete_all() {
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::erase_component(CanvasComponentContainer::ObjInfo* objToCheck) {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
std::erase(selectedSet, objToCheck);
|
||||
if(selectedSet.empty())
|
||||
reset_all();
|
||||
|
|
@ -455,6 +578,8 @@ CanvasComponentContainer::ObjInfo* DrawingProgramSelection::get_front_object_col
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::selection_to_clipboard() {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
auto& clipboard = drawP.world.main.clipboard;
|
||||
std::unordered_set<NetworkingObjects::NetObjID> resourceSet;
|
||||
for(auto& c : selectedSet)
|
||||
|
|
@ -468,6 +593,8 @@ void DrawingProgramSelection::selection_to_clipboard() {
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::paste_clipboard(Vector2f pasteScreenPos) {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
if(drawP.layerMan.is_a_layer_being_edited()) {
|
||||
auto& clipboard = drawP.world.main.clipboard;
|
||||
|
||||
|
|
@ -511,6 +638,8 @@ void DrawingProgramSelection::paste_clipboard(Vector2f pasteScreenPos) {
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::paste_image(Vector2f pasteScreenPos) {
|
||||
check_add_stroke_color_change_undo();
|
||||
|
||||
if(drawP.layerMan.is_a_layer_being_edited()) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
struct PasteData {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ class DrawingProgram;
|
|||
class DrawingProgramSelection {
|
||||
public:
|
||||
DrawingProgramSelection(DrawingProgram& initDrawP);
|
||||
void selection_gui();
|
||||
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 erase_component(CanvasComponentContainer::ObjInfo* objToCheck);
|
||||
|
|
@ -38,6 +39,15 @@ class DrawingProgramSelection {
|
|||
void rebuild_cam_space();
|
||||
Vector2f get_rotation_point_pos_from_angle(double angle);
|
||||
|
||||
void update_selection_stroke_color();
|
||||
|
||||
void check_add_stroke_color_change_undo();
|
||||
struct StrokeColorChangeData {
|
||||
Vector4f newColor = {1.0f, 1.0f, 1.0f, 0.0f};
|
||||
Vector4f oldColor = {1.0f, 1.0f, 1.0f, 0.0f};
|
||||
std::unordered_map<NetworkingObjects::NetObjID, Vector4f> oldColorData;
|
||||
} strokeColorChangeData;
|
||||
|
||||
void set_to_selection(const std::vector<CanvasComponentContainer::ObjInfo*>& newSelection);
|
||||
void add_to_selection(const std::vector<CanvasComponentContainer::ObjInfo*>& newSelection);
|
||||
void sort_selection();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ void BrushTool::erase_component(CanvasComponentContainer::ObjInfo* erasedComp) {
|
|||
}
|
||||
|
||||
bool BrushTool::extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) {
|
||||
auto& points = brushStroke.d->points;
|
||||
auto& points = *brushStroke.d.points;
|
||||
if(points.size() >= 1 && (newPoint - points[points.size() - 1].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
|
||||
return false;
|
||||
if(points.size() >= 2 && (newPoint - points[points.size() - 2].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
|
||||
|
|
@ -43,7 +43,7 @@ bool BrushTool::extensive_point_checking(const BrushStrokeCanvasComponent& brush
|
|||
}
|
||||
|
||||
bool BrushTool::extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) {
|
||||
auto& points = brushStroke.d->points;
|
||||
auto& points = *brushStroke.d.points;
|
||||
if(points.size() >= 2 && (newPoint - points[points.size() - 2].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
|
||||
return false;
|
||||
if(points.size() >= 3 && (newPoint - points[points.size() - 3].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
|
||||
|
|
@ -98,9 +98,9 @@ void BrushTool::tool_update() {
|
|||
p.pos = drawP.world.main.input.mouse.pos;
|
||||
p.width = toolConfig.get_relative_width(toolConfig.brush.relativeWidth) * penWidth;
|
||||
prevPointUnaltered = p.pos;
|
||||
newBrushStroke.d->points.emplace_back(p);
|
||||
newBrushStroke.d->color = toolConfig.globalConf.foregroundColor;
|
||||
newBrushStroke.d->hasRoundCaps = toolConfig.brush.hasRoundCaps;
|
||||
newBrushStroke.d.points->emplace_back(p);
|
||||
newBrushStroke.d.color = toolConfig.globalConf.foregroundColor;
|
||||
newBrushStroke.d.hasRoundCaps = toolConfig.brush.hasRoundCaps;
|
||||
newBrushStrokeContainer->coords = drawP.world.drawData.cam.c;
|
||||
objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newBrushStrokeContainer);
|
||||
addedTemporaryPoint = false;
|
||||
|
|
@ -109,6 +109,7 @@ void BrushTool::tool_update() {
|
|||
else {
|
||||
NetworkingObjects::NetObjOwnerPtr<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;
|
||||
BrushStrokeCanvasComponent& brushStroke = static_cast<BrushStrokeCanvasComponent&>(containerPtr->get_comp());
|
||||
auto& brushPoints = *brushStroke.d.points;
|
||||
|
||||
BrushStrokeCanvasComponentPoint p;
|
||||
p.pos = containerPtr->coords.get_mouse_pos(drawP.world);
|
||||
|
|
@ -116,28 +117,28 @@ void BrushTool::tool_update() {
|
|||
|
||||
if(!addedTemporaryPoint) {
|
||||
if(extensive_point_checking(brushStroke, p.pos)) {
|
||||
brushStroke.d->points.emplace_back(p);
|
||||
brushPoints.emplace_back(p);
|
||||
addedTemporaryPoint = true;
|
||||
}
|
||||
else
|
||||
brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width);
|
||||
brushPoints.back().width = std::max(brushPoints.back().width, p.width);
|
||||
}
|
||||
|
||||
if(addedTemporaryPoint) {
|
||||
const BrushStrokeCanvasComponentPoint& prevP = brushStroke.d->points[brushStroke.d->points.size() - 2];
|
||||
const BrushStrokeCanvasComponentPoint& prevP = brushPoints[brushPoints.size() - 2];
|
||||
float distToPrev = (p.pos - prevP.pos).norm();
|
||||
if(extensive_point_checking_back(brushStroke, p.pos)) {
|
||||
brushStroke.d->points.back().pos = p.pos;
|
||||
brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width);
|
||||
brushStroke.d->points[brushStroke.d->points.size() - 2].width = std::max(brushStroke.d->points[brushStroke.d->points.size() - 2].width, p.width);
|
||||
brushPoints.back().pos = p.pos;
|
||||
brushPoints.back().width = std::max(brushPoints.back().width, p.width);
|
||||
brushPoints[brushPoints.size() - 2].width = std::max(brushPoints[brushPoints.size() - 2].width, p.width);
|
||||
}
|
||||
if((!drawingMinimumRelativeToSize && distToPrev >= 10.0) || (drawingMinimumRelativeToSize && distToPrev >= toolConfig.get_relative_width(toolConfig.brush.relativeWidth) * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) {
|
||||
brushStroke.d->points.back() = p;
|
||||
brushPoints.back() = p;
|
||||
addedTemporaryPoint = false;
|
||||
|
||||
if(midwayInterpolation) {
|
||||
if(brushStroke.d->points.size() != 2) // Don't interpolate the first point
|
||||
brushStroke.d->points[brushStroke.d->points.size() - 2].pos = (prevPointUnaltered + p.pos) * 0.5;
|
||||
if(brushPoints.size() != 2) // Don't interpolate the first point
|
||||
brushPoints[brushPoints.size() - 2].pos = (prevPointUnaltered + p.pos) * 0.5;
|
||||
prevPointUnaltered = p.pos;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "EditTools/RectDrawEditTool.hpp"
|
||||
#include "EditTools/ImageEditTool.hpp"
|
||||
#include "EditTools/EllipseDrawEditTool.hpp"
|
||||
#include "EditTools/BrushEditTool.hpp"
|
||||
|
||||
EditTool::EditTool(DrawingProgram& initDrawP):
|
||||
DrawingProgramToolBase(initDrawP)
|
||||
|
|
@ -124,6 +125,10 @@ void EditTool::edit_start(CanvasComponentContainer::ObjInfo* comp, bool initUndo
|
|||
compEditTool = std::make_unique<ImageEditTool>(drawP);
|
||||
break;
|
||||
}
|
||||
case CanvasComponentType::BRUSHSTROKE: {
|
||||
compEditTool = std::make_unique<BrushEditTool>(drawP);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
isEditing = false;
|
||||
break;
|
||||
|
|
@ -138,7 +143,7 @@ void EditTool::edit_start(CanvasComponentContainer::ObjInfo* comp, bool initUndo
|
|||
}
|
||||
|
||||
bool EditTool::is_editable(CanvasComponentContainer::ObjInfo* comp) {
|
||||
return comp->obj->get_comp().get_type() != CanvasComponentType::BRUSHSTROKE;
|
||||
return true; // Brush strokes used to not be editable, but now they are so this always returns true
|
||||
}
|
||||
|
||||
void EditTool::tool_update() {
|
||||
|
|
|
|||
38
src/DrawingProgram/Tools/EditTools/BrushEditTool.cpp
Normal file
38
src/DrawingProgram/Tools/EditTools/BrushEditTool.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include "BrushEditTool.hpp"
|
||||
#include "../../DrawingProgram.hpp"
|
||||
#include "../../../World.hpp"
|
||||
#include "../../../MainProgram.hpp"
|
||||
#include "../EditTool.hpp"
|
||||
|
||||
BrushEditTool::BrushEditTool(DrawingProgram& initDrawP):
|
||||
DrawingProgramEditToolBase(initDrawP)
|
||||
{}
|
||||
|
||||
bool BrushEditTool::edit_gui(CanvasComponentContainer::ObjInfo* comp) {
|
||||
auto& a = static_cast<BrushStrokeCanvasComponent&>(comp->obj->get_comp());
|
||||
Toolbar& t = drawP.world.main.toolbar;
|
||||
t.gui.push_id("edit tool brush");
|
||||
t.gui.text_label_centered("Edit Brush Stroke");
|
||||
t.gui.left_to_right_line_layout([&]() {
|
||||
if(t.gui.color_button_big("Outline Color", &a.d.color, &a.d.color == t.colorRight))
|
||||
t.color_selector_right(&a.d.color == t.colorRight ? nullptr : &a.d.color);
|
||||
t.gui.text_label("Outline Color");
|
||||
});
|
||||
t.gui.pop_id();
|
||||
|
||||
bool editHappened = a.d.color != oldColor;
|
||||
oldColor = a.d.color;
|
||||
return editHappened;
|
||||
}
|
||||
|
||||
void BrushEditTool::edit_start(EditTool& editTool, CanvasComponentContainer::ObjInfo* comp, std::any& prevData) {
|
||||
auto& a = static_cast<BrushStrokeCanvasComponent&>(comp->obj->get_comp());
|
||||
oldColor = a.d.color;
|
||||
}
|
||||
|
||||
void BrushEditTool::commit_edit_updates(CanvasComponentContainer::ObjInfo* comp, std::any& prevData) {
|
||||
}
|
||||
|
||||
bool BrushEditTool::edit_update(CanvasComponentContainer::ObjInfo* comp) {
|
||||
return true;
|
||||
}
|
||||
18
src/DrawingProgram/Tools/EditTools/BrushEditTool.hpp
Normal file
18
src/DrawingProgram/Tools/EditTools/BrushEditTool.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include "DrawingProgramEditToolBase.hpp"
|
||||
#include "../../../CanvasComponents/BrushStrokeCanvasComponent.hpp"
|
||||
#include <Helpers/SCollision.hpp>
|
||||
#include <any>
|
||||
|
||||
class DrawingProgram;
|
||||
|
||||
class BrushEditTool : public DrawingProgramEditToolBase {
|
||||
public:
|
||||
BrushEditTool(DrawingProgram& initDrawP);
|
||||
virtual void edit_start(EditTool& editTool, CanvasComponentContainer::ObjInfo* comp, std::any& prevData) override;
|
||||
virtual void commit_edit_updates(CanvasComponentContainer::ObjInfo* comp, std::any& prevData) override;
|
||||
virtual bool edit_update(CanvasComponentContainer::ObjInfo* comp) override;
|
||||
virtual bool edit_gui(CanvasComponentContainer::ObjInfo* comp) override;
|
||||
private:
|
||||
Vector4f oldColor;
|
||||
};
|
||||
|
|
@ -42,11 +42,7 @@ void LassoSelectTool::gui_toolbox() {
|
|||
auto& t = drawP.world.main.toolbar;
|
||||
t.gui.push_id("lasso select tool");
|
||||
t.gui.text_label_centered("Lasso Select");
|
||||
t.gui.text_label("Select from:");
|
||||
if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED))
|
||||
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED;
|
||||
if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS))
|
||||
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS;
|
||||
drawP.selection.selection_gui();
|
||||
t.gui.pop_id();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@ void LineDrawTool::tool_update() {
|
|||
BrushStrokeCanvasComponentPoint p;
|
||||
p.pos = drawP.world.main.input.mouse.pos;
|
||||
p.width = toolConfig.get_relative_width(toolConfig.lineDraw.relativeWidth);
|
||||
newBrushStroke.d->points.emplace_back(p);
|
||||
newBrushStroke.d.points->emplace_back(p);
|
||||
p.pos = ensure_points_have_distance(p.pos, p.pos, 1.0f);
|
||||
newBrushStroke.d->points.emplace_back(p);
|
||||
newBrushStroke.d->color = toolConfig.globalConf.foregroundColor;
|
||||
newBrushStroke.d->hasRoundCaps = toolConfig.lineDraw.hasRoundCaps;
|
||||
newBrushStroke.d.points->emplace_back(p);
|
||||
newBrushStroke.d.color = toolConfig.globalConf.foregroundColor;
|
||||
newBrushStroke.d.hasRoundCaps = toolConfig.lineDraw.hasRoundCaps;
|
||||
newBrushStrokeContainer->coords = drawP.world.drawData.cam.c;
|
||||
objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newBrushStrokeContainer);
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ void LineDrawTool::tool_update() {
|
|||
if(drawP.controls.leftClickHeld) {
|
||||
BrushStrokeCanvasComponent& brushStroke = static_cast<BrushStrokeCanvasComponent&>(containerPtr->get_comp());
|
||||
Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world);
|
||||
Vector2f oldPos = brushStroke.d->points.front().pos;
|
||||
Vector2f oldPos = brushStroke.d.points->front().pos;
|
||||
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
|
||||
Vector2f diff = (newPos - oldPos);
|
||||
float diffLength = diff.norm();
|
||||
|
|
@ -76,7 +76,7 @@ void LineDrawTool::tool_update() {
|
|||
angle = (angle * std::numbers::pi) / SNAP_DIVISION_COUNT;
|
||||
newPos = oldPos + diffLength * Vector2f{cos(angle), sin(angle)};
|
||||
}
|
||||
brushStroke.d->points.back().pos = ensure_points_have_distance(oldPos, newPos, 1.0f);
|
||||
brushStroke.d.points->back().pos = ensure_points_have_distance(oldPos, newPos, 1.0f);
|
||||
containerPtr->send_comp_update(drawP, false);
|
||||
containerPtr->commit_update(drawP);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,7 @@ void RectSelectTool::gui_toolbox() {
|
|||
auto& t = drawP.world.main.toolbar;
|
||||
t.gui.push_id("rectangle select tool");
|
||||
t.gui.text_label_centered("Rectangle Select");
|
||||
t.gui.text_label("Select from:");
|
||||
if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED))
|
||||
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED;
|
||||
if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS))
|
||||
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS;
|
||||
drawP.selection.selection_gui();
|
||||
t.gui.pop_id();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue