diff --git a/src/DrawingProgram/ToolConfiguration.cpp b/src/DrawingProgram/ToolConfiguration.cpp index 25400c4..62bf03a 100644 --- a/src/DrawingProgram/ToolConfiguration.cpp +++ b/src/DrawingProgram/ToolConfiguration.cpp @@ -5,32 +5,83 @@ #include "Helpers/FixedPoint.hpp" #include #include "Helpers/Logger.hpp" +#include "Tools/DrawingProgramToolBase.hpp" -float ToolConfiguration::get_relative_width(DrawingProgram& drawP, const WorldScalar& camInverseScale, float relativeWidthLocal) const { - auto& lockedCameraScale = drawP.controls.lockedCameraScale; - float relativeWidth = globalConf.useGlobalRelativeWidth ? globalConf.relativeWidth : relativeWidthLocal; - if(lockedCameraScale.has_value()) { - float lockMultiplier = static_cast(WorldMultiplier(lockedCameraScale.value()) / WorldMultiplier(camInverseScale)); - if(lockMultiplier < 0.001f) { - Logger::get().log("USERINFO", "Zoomed out too much, size unlocked"); - lockedCameraScale = std::nullopt; - return relativeWidth; - } - else if(lockMultiplier > 1000.0f) { - Logger::get().log("USERINFO", "Zoomed in too much, size unlocked"); - lockedCameraScale = std::nullopt; - return relativeWidth; - } - return relativeWidth * lockMultiplier; +float& ToolConfiguration::get_stroke_size_relative_width_ref(DrawingProgramToolType toolType) { + if(globalConf.useGlobalRelativeWidth) + return globalConf.relativeWidth; + switch(toolType) { + case DrawingProgramToolType::LINE: + return lineDraw.relativeWidth; + case DrawingProgramToolType::BRUSH: + return brush.relativeWidth; + case DrawingProgramToolType::ELLIPSE: + return ellipseDraw.relativeWidth; + case DrawingProgramToolType::RECTANGLE: + return rectDraw.relativeWidth; + case DrawingProgramToolType::ERASER: + return eraser.relativeWidth; + default: + return globalConf.relativeWidth; } - return relativeWidth; + return globalConf.relativeWidth; } -void ToolConfiguration::relative_width_gui(DrawingProgram& drawP, const char* label, float* relativeWidthLocal) { +const float& ToolConfiguration::get_stroke_size_relative_width_ref(DrawingProgramToolType toolType) const { + if(globalConf.useGlobalRelativeWidth) + return globalConf.relativeWidth; + switch(toolType) { + case DrawingProgramToolType::LINE: + return lineDraw.relativeWidth; + case DrawingProgramToolType::BRUSH: + return brush.relativeWidth; + case DrawingProgramToolType::ELLIPSE: + return ellipseDraw.relativeWidth; + case DrawingProgramToolType::RECTANGLE: + return rectDraw.relativeWidth; + case DrawingProgramToolType::ERASER: + return eraser.relativeWidth; + default: + return globalConf.relativeWidth; + } + return globalConf.relativeWidth; +} + +std::pair, ToolConfiguration::RelativeWidthFailCode> ToolConfiguration::get_relative_width_from_value(DrawingProgram& drawP, const WorldScalar& camInverseScale, float relativeWidth) const { + auto& lockedCameraScale = drawP.controls.lockedCameraScale; + if(lockedCameraScale.has_value()) { + float lockMultiplier = static_cast(WorldMultiplier(lockedCameraScale.value()) / WorldMultiplier(camInverseScale)); + if(lockMultiplier < 0.001f) + return {std::nullopt, RelativeWidthFailCode::TOO_ZOOMED_OUT}; + else if(lockMultiplier > 1000.0f) + return {std::nullopt, RelativeWidthFailCode::TOO_ZOOMED_IN}; + return {relativeWidth * lockMultiplier, RelativeWidthFailCode::SUCCESS}; + } + return {relativeWidth, RelativeWidthFailCode::SUCCESS}; +} + +std::pair, ToolConfiguration::RelativeWidthFailCode> ToolConfiguration::get_relative_width_stroke_size(DrawingProgram& drawP, const WorldScalar& camInverseScale) const { + return get_relative_width_from_value(drawP, camInverseScale, get_stroke_size_relative_width_ref(drawP.drawTool->get_type())); +} + +void ToolConfiguration::print_relative_width_fail_message(RelativeWidthFailCode failCode) { + switch(failCode) { + case RelativeWidthFailCode::SUCCESS: + break; + case RelativeWidthFailCode::TOO_ZOOMED_IN: + Logger::get().log("USERINFO", "Zoomed in too much! Unlock size or zoom out"); + break; + case RelativeWidthFailCode::TOO_ZOOMED_OUT: + Logger::get().log("USERINFO", "Zoomed out too much! Unlock size or zoom in"); + break; + } +} + +void ToolConfiguration::relative_width_gui(DrawingProgram& drawP, const char* label) { auto& gui = drawP.world.main.toolbar.gui; auto& lockedCameraScale = drawP.controls.lockedCameraScale; - gui.slider_scalar_field("relstrokewidth", label, globalConf.useGlobalRelativeWidth ? &globalConf.relativeWidth : relativeWidthLocal, 3.0f, 40.0f); - if(gui.text_button_wide("lock brush size", lockedCameraScale.has_value() ? "Unlock Brush Size" : "Lock Brush Size")) { + gui.slider_scalar_field("relstrokewidth", label, &get_stroke_size_relative_width_ref(drawP.drawTool->get_type()), 3.0f, 40.0f); + if(gui.text_button_wide("lock brush size", lockedCameraScale.has_value() ? "Unlock Size" : "Lock Size to Zoom")) { if(lockedCameraScale.has_value()) lockedCameraScale = std::nullopt; else diff --git a/src/DrawingProgram/ToolConfiguration.hpp b/src/DrawingProgram/ToolConfiguration.hpp index 050bcf6..188d242 100644 --- a/src/DrawingProgram/ToolConfiguration.hpp +++ b/src/DrawingProgram/ToolConfiguration.hpp @@ -56,8 +56,18 @@ class ToolConfiguration { NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GlobalConfig, useGlobalRelativeWidth, foregroundColor, backgroundColor, relativeWidth) } globalConf; - float get_relative_width(DrawingProgram& drawP, const WorldScalar& camInverseScale, float relativeWidthLocal) const; - void relative_width_gui(DrawingProgram& drawP, const char* label, float* relativeWidthLocal); + enum class RelativeWidthFailCode { + SUCCESS, + TOO_ZOOMED_IN, + TOO_ZOOMED_OUT + }; + + float& get_stroke_size_relative_width_ref(DrawingProgramToolType toolType); + const float& get_stroke_size_relative_width_ref(DrawingProgramToolType toolType) const; + std::pair, RelativeWidthFailCode> get_relative_width_from_value(DrawingProgram& drawP, const WorldScalar& camInverseScale, float relativeWidth) const; + std::pair, RelativeWidthFailCode> get_relative_width_stroke_size(DrawingProgram& drawP, const WorldScalar& camInverseScale) const; + void print_relative_width_fail_message(RelativeWidthFailCode failCode); + void relative_width_gui(DrawingProgram& drawP, const char* label); NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ToolConfiguration, brush, eraser, ellipseDraw, rectDraw, eyeDropper, lineDraw, screenshot, globalConf) }; diff --git a/src/DrawingProgram/Tools/BrushTool.cpp b/src/DrawingProgram/Tools/BrushTool.cpp index f59c88c..a984348 100644 --- a/src/DrawingProgram/Tools/BrushTool.cpp +++ b/src/DrawingProgram/Tools/BrushTool.cpp @@ -90,13 +90,21 @@ void BrushTool::tool_update() { if(isPenDown) { if(!objInfoBeingEdited) { - if(drawP.layerMan.is_a_layer_being_edited()) { + if(drawP.controls.leftClick && drawP.layerMan.is_a_layer_being_edited()) { + auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); + if(!relativeWidthResult.first.has_value()) { + if(drawP.controls.leftClick) + drawP.world.main.toolConfig.print_relative_width_fail_message(relativeWidthResult.second); + return; + } + float width = relativeWidthResult.first.value() * penWidth; + CanvasComponentContainer* newBrushStrokeContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::BRUSHSTROKE); BrushStrokeCanvasComponent& newBrushStroke = static_cast(newBrushStrokeContainer->get_comp()); BrushStrokeCanvasComponentPoint p; p.pos = drawP.world.main.input.mouse.pos; - p.width = toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, toolConfig.brush.relativeWidth) * penWidth; + p.width = width; prevPointUnaltered = p.pos; newBrushStroke.d.points->emplace_back(p); newBrushStroke.d.color = toolConfig.globalConf.foregroundColor; @@ -110,10 +118,11 @@ void BrushTool::tool_update() { NetworkingObjects::NetObjOwnerPtr& containerPtr = objInfoBeingEdited->obj; BrushStrokeCanvasComponent& brushStroke = static_cast(containerPtr->get_comp()); auto& brushPoints = *brushStroke.d.points; + float width = toolConfig.get_relative_width_stroke_size(drawP, containerPtr->coords.inverseScale).first.value() * penWidth; BrushStrokeCanvasComponentPoint p; p.pos = containerPtr->coords.get_mouse_pos(drawP.world); - p.width = toolConfig.get_relative_width(drawP, containerPtr->coords.inverseScale, toolConfig.brush.relativeWidth) * penWidth; + p.width = width; if(!addedTemporaryPoint) { if(extensive_point_checking(brushStroke, p.pos)) { @@ -132,7 +141,7 @@ void BrushTool::tool_update() { 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(drawP, containerPtr->coords.inverseScale, toolConfig.brush.relativeWidth) * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) { + if((!drawingMinimumRelativeToSize && distToPrev >= 10.0) || (drawingMinimumRelativeToSize && distToPrev >= width * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) { brushPoints.back() = p; addedTemporaryPoint = false; @@ -167,7 +176,7 @@ void BrushTool::gui_toolbox() { t.gui.push_id("brush tool"); t.gui.text_label_centered("Brush"); t.gui.checkbox_field("hasroundcaps", "Round Caps", &drawP.world.main.toolConfig.brush.hasRoundCaps); - drawP.world.main.toolConfig.relative_width_gui(drawP, "Size", &drawP.world.main.toolConfig.brush.relativeWidth); + drawP.world.main.toolConfig.relative_width_gui(drawP, "Size"); t.gui.pop_id(); } @@ -183,16 +192,19 @@ bool BrushTool::prevent_undo_or_redo() { void BrushTool::draw(SkCanvas* canvas, const DrawData& drawData) { if(!drawData.main->toolbar.io->hoverObstructed) { - SkPaint linePaint; - linePaint.setColor4f(drawP.world.canvasTheme.get_tool_front_color()); - linePaint.setStroke(true); - linePaint.setStrokeWidth(0.0f); - float width = drawP.world.main.toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, drawP.world.main.toolConfig.brush.relativeWidth); - if(objInfoBeingEdited) - width *= penWidth * 0.5f; - else - width *= 0.5f; - Vector2f pos = drawData.main->input.mouse.pos; - canvas->drawCircle(pos.x(), pos.y(), width, linePaint); + auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); + if(relativeWidthResult.first.has_value()) { + SkPaint linePaint; + linePaint.setColor4f(drawP.world.canvasTheme.get_tool_front_color()); + linePaint.setStroke(true); + linePaint.setStrokeWidth(0.0f); + float width = relativeWidthResult.first.value(); + if(objInfoBeingEdited) + width *= penWidth * 0.5f; + else + width *= 0.5f; + Vector2f pos = drawData.main->input.mouse.pos; + canvas->drawCircle(pos.x(), pos.y(), width, linePaint); + } } } diff --git a/src/DrawingProgram/Tools/EllipseDrawTool.cpp b/src/DrawingProgram/Tools/EllipseDrawTool.cpp index ba008a4..0f8d922 100644 --- a/src/DrawingProgram/Tools/EllipseDrawTool.cpp +++ b/src/DrawingProgram/Tools/EllipseDrawTool.cpp @@ -27,7 +27,7 @@ void EllipseDrawTool::gui_toolbox() { if(t.gui.radio_button_field("outlineonly", "Outline only", fillStrokeMode == 1)) fillStrokeMode = 1; if(t.gui.radio_button_field("filloutline", "Fill and Outline", fillStrokeMode == 2)) fillStrokeMode = 2; if(fillStrokeMode == 1 || fillStrokeMode == 2) - toolConfig.relative_width_gui(drawP, "Outline Size", &toolConfig.ellipseDraw.relativeWidth); + toolConfig.relative_width_gui(drawP, "Outline Size"); t.gui.pop_id(); } @@ -51,13 +51,21 @@ void EllipseDrawTool::tool_update() { if(!objInfoBeingEdited) { if(drawP.controls.leftClick && drawP.layerMan.is_a_layer_being_edited()) { + auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); + if(!relativeWidthResult.first.has_value()) { + if(drawP.controls.leftClick) + drawP.world.main.toolConfig.print_relative_width_fail_message(relativeWidthResult.second); + return; + } + float width = relativeWidthResult.first.value(); + CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::ELLIPSE); EllipseCanvasComponent& newEllipse = static_cast(newContainer->get_comp()); startAt = drawP.world.main.input.mouse.pos; newEllipse.d.strokeColor = toolConfig.globalConf.foregroundColor; newEllipse.d.fillColor = toolConfig.globalConf.backgroundColor; - newEllipse.d.strokeWidth = toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, toolConfig.ellipseDraw.relativeWidth); + newEllipse.d.strokeWidth = width; newEllipse.d.p1 = startAt; newEllipse.d.p2 = startAt; newEllipse.d.p2 = ensure_points_have_distance(newEllipse.d.p1, newEllipse.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS); diff --git a/src/DrawingProgram/Tools/EraserTool.cpp b/src/DrawingProgram/Tools/EraserTool.cpp index 9834caf..4e0b068 100644 --- a/src/DrawingProgram/Tools/EraserTool.cpp +++ b/src/DrawingProgram/Tools/EraserTool.cpp @@ -16,7 +16,7 @@ void EraserTool::gui_toolbox() { Toolbar& t = drawP.world.main.toolbar; t.gui.push_id("eraser tool"); t.gui.text_label_centered("Eraser"); - drawP.world.main.toolConfig.relative_width_gui(drawP, "Size", &drawP.world.main.toolConfig.eraser.relativeWidth); + drawP.world.main.toolConfig.relative_width_gui(drawP, "Size"); t.gui.text_label("Erase 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; @@ -44,9 +44,16 @@ void EraserTool::tool_update() { drawP.world.main.input.hideCursor = true; if(drawP.controls.leftClickHeld) { + auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); + if(!relativeWidthResult.first.has_value()) { + if(drawP.controls.leftClick) + drawP.world.main.toolConfig.print_relative_width_fail_message(relativeWidthResult.second); + return; + } + float width = relativeWidthResult.first.value() * 0.5f; + const Vector2f& prevMousePos = drawP.controls.leftClick ? drawP.world.main.input.mouse.pos : drawP.world.main.input.mouse.lastPos; SCollision::ColliderCollection cC; - float width = drawP.world.main.toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, drawP.world.main.toolConfig.eraser.relativeWidth); SCollision::generate_wide_line(cC, prevMousePos, drawP.world.main.input.mouse.pos, width * 2.0f, true); auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); @@ -90,18 +97,21 @@ bool EraserTool::prevent_undo_or_redo() { void EraserTool::draw(SkCanvas* canvas, const DrawData& drawData) { if(!drawData.main->toolbar.io->hoverObstructed) { - SkColor4f c = drawP.world.canvasTheme.get_tool_front_color(); - c.fA = 0.5f; + 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; + SkColor4f c = drawP.world.canvasTheme.get_tool_front_color(); + c.fA = 0.5f; - SkPaint linePaint; - linePaint.setColor4f(c); - linePaint.setStyle(SkPaint::kStroke_Style); - linePaint.setStrokeCap(SkPaint::kRound_Cap); - float width = drawP.world.main.toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, drawP.world.main.toolConfig.eraser.relativeWidth); - linePaint.setStrokeWidth(width * 2.0f); - SkPathBuilder erasePath; - erasePath.moveTo(convert_vec2(drawData.main->input.mouse.lastPos)); - erasePath.lineTo(convert_vec2(drawData.main->input.mouse.pos)); - canvas->drawPath(erasePath.detach(), linePaint); + SkPaint linePaint; + linePaint.setColor4f(c); + linePaint.setStyle(SkPaint::kStroke_Style); + linePaint.setStrokeCap(SkPaint::kRound_Cap); + linePaint.setStrokeWidth(width * 2.0f); + SkPathBuilder erasePath; + erasePath.moveTo(convert_vec2(drawData.main->input.mouse.lastPos)); + erasePath.lineTo(convert_vec2(drawData.main->input.mouse.pos)); + canvas->drawPath(erasePath.detach(), linePaint); + } } } diff --git a/src/DrawingProgram/Tools/LineDrawTool.cpp b/src/DrawingProgram/Tools/LineDrawTool.cpp index e1fbeb1..5dc1539 100644 --- a/src/DrawingProgram/Tools/LineDrawTool.cpp +++ b/src/DrawingProgram/Tools/LineDrawTool.cpp @@ -21,7 +21,7 @@ void LineDrawTool::gui_toolbox() { auto& toolConfig = drawP.world.main.toolConfig; t.gui.push_id("rect draw tool"); t.gui.text_label_centered("Draw Line"); - toolConfig.relative_width_gui(drawP, "Size", &toolConfig.lineDraw.relativeWidth); + toolConfig.relative_width_gui(drawP, "Size"); t.gui.checkbox_field("hasroundcaps", "Round Caps", &toolConfig.lineDraw.hasRoundCaps); t.gui.pop_id(); } @@ -45,12 +45,20 @@ void LineDrawTool::tool_update() { auto& toolConfig = drawP.world.main.toolConfig; if(!objInfoBeingEdited) { if(drawP.controls.leftClick && drawP.layerMan.is_a_layer_being_edited()) { + auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); + if(!relativeWidthResult.first.has_value()) { + if(drawP.controls.leftClick) + drawP.world.main.toolConfig.print_relative_width_fail_message(relativeWidthResult.second); + return; + } + float width = relativeWidthResult.first.value(); + CanvasComponentContainer* newBrushStrokeContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::BRUSHSTROKE); BrushStrokeCanvasComponent& newBrushStroke = static_cast(newBrushStrokeContainer->get_comp()); BrushStrokeCanvasComponentPoint p; p.pos = drawP.world.main.input.mouse.pos; - p.width = toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, toolConfig.lineDraw.relativeWidth); + p.width = width; newBrushStroke.d.points->emplace_back(p); p.pos = ensure_points_have_distance(p.pos, p.pos, 1.0f); newBrushStroke.d.points->emplace_back(p); diff --git a/src/DrawingProgram/Tools/RectDrawTool.cpp b/src/DrawingProgram/Tools/RectDrawTool.cpp index aa90be8..71ac547 100644 --- a/src/DrawingProgram/Tools/RectDrawTool.cpp +++ b/src/DrawingProgram/Tools/RectDrawTool.cpp @@ -26,7 +26,7 @@ void RectDrawTool::gui_toolbox() { if(t.gui.radio_button_field("outlineonly", "Outline only", fillStrokeMode == 1)) fillStrokeMode = 1; if(t.gui.radio_button_field("filloutline", "Fill and Outline", fillStrokeMode == 2)) fillStrokeMode = 2; if(fillStrokeMode == 1 || fillStrokeMode == 2) - toolConfig.relative_width_gui(drawP, "Outline Size", &toolConfig.rectDraw.relativeWidth); + toolConfig.relative_width_gui(drawP, "Outline Size"); t.gui.pop_id(); } @@ -52,14 +52,24 @@ void RectDrawTool::tool_update() { if(!objInfoBeingEdited) { if(drawP.controls.leftClick && drawP.layerMan.is_a_layer_being_edited()) { + auto relativeWidthResult = drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, drawP.world.drawData.cam.c.inverseScale); + auto relativeRadiusWidthResult = drawP.world.main.toolConfig.get_relative_width_from_value(drawP, drawP.world.drawData.cam.c.inverseScale, relativeRadiusWidth); + if(!relativeWidthResult.first.has_value() || !relativeRadiusWidthResult.first.has_value()) { + if(drawP.controls.leftClick) + drawP.world.main.toolConfig.print_relative_width_fail_message(relativeWidthResult.second); + return; + } + float width = relativeWidthResult.first.value(); + float radiusWidth = relativeRadiusWidthResult.first.value(); + CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::RECTANGLE); RectangleCanvasComponent& newRectangle = static_cast(newContainer->get_comp()); startAt = drawP.world.main.input.mouse.pos; newRectangle.d.strokeColor = toolConfig.globalConf.foregroundColor; newRectangle.d.fillColor = toolConfig.globalConf.backgroundColor; - newRectangle.d.cornerRadius = relativeRadiusWidth; - newRectangle.d.strokeWidth = toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, toolConfig.rectDraw.relativeWidth); + newRectangle.d.cornerRadius = radiusWidth; + newRectangle.d.strokeWidth = width; newRectangle.d.p1 = startAt; newRectangle.d.p2 = startAt; newRectangle.d.p2 = ensure_points_have_distance(newRectangle.d.p1, newRectangle.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);