Add size lock

This commit is contained in:
Yousef Khadadeh 2026-01-29 11:48:08 +03:00
parent a73f5e2122
commit 8a4d9a4f07
9 changed files with 55 additions and 19 deletions

View file

@ -52,6 +52,8 @@ void DrawingProgram::server_init_no_file() {
void DrawingProgram::scale_up(const WorldScalar& scaleUpAmount) {
selection.deselect_all();
layerMan.scale_up(scaleUpAmount);
if(controls.lockedCameraScale.has_value())
controls.lockedCameraScale.value() *= scaleUpAmount;
rebuild_cache();
switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true);
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "../DrawData.hpp"
#include "DrawingProgramCache.hpp"
#include "ToolConfiguration.hpp"
#include "Tools/GridModifyTool.hpp"
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
#include "Tools/PanCanvasTool.hpp"
@ -92,6 +93,8 @@ class DrawingProgram {
DrawingProgramToolType toolTypeAfterTempMove;
struct GlobalControls {
std::optional<WorldScalar> lockedCameraScale;
WorldVec previousMouseWorldPos = {0, 0};
WorldVec currentMouseWorldPos = {0, 0};
bool leftClick = false;
@ -143,4 +146,5 @@ class DrawingProgram {
friend class DrawingProgramLayerManager;
friend class DrawingProgramLayer;
friend class DrawingProgramLayerFolder;
friend class ToolConfiguration;
};

View file

@ -1,9 +1,39 @@
#include "ToolConfiguration.hpp"
#include "DrawingProgram.hpp"
#include "../World.hpp"
#include "../MainProgram.hpp"
#include "Helpers/FixedPoint.hpp"
#include <optional>
#include "Helpers/Logger.hpp"
float ToolConfiguration::get_relative_width(float relativeWidthLocal) const {
return globalConf.useGlobalRelativeWidth ? globalConf.relativeWidth : relativeWidthLocal;
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<float>(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;
}
return relativeWidth;
}
void ToolConfiguration::relative_width_slider(GUIStuff::GUIManager& gui, const char* label, float* relativeWidthLocal) {
void ToolConfiguration::relative_width_gui(DrawingProgram& drawP, const char* label, float* relativeWidthLocal) {
auto& gui = drawP.world.main.toolbar.gui;
auto& lockedCameraScale = drawP.controls.lockedCameraScale;
gui.slider_scalar_field<float>("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")) {
if(lockedCameraScale.has_value())
lockedCameraScale = std::nullopt;
else
lockedCameraScale = drawP.world.drawData.cam.c.inverseScale;
}
}

View file

@ -56,8 +56,8 @@ class ToolConfiguration {
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GlobalConfig, useGlobalRelativeWidth, foregroundColor, backgroundColor, relativeWidth)
} globalConf;
float get_relative_width(float relativeWidthLocal) const;
void relative_width_slider(GUIStuff::GUIManager& gui, const char* label, float* relativeWidthLocal);
float get_relative_width(DrawingProgram& drawP, const WorldScalar& camInverseScale, float relativeWidthLocal) const;
void relative_width_gui(DrawingProgram& drawP, const char* label, float* relativeWidthLocal);
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ToolConfiguration, brush, eraser, ellipseDraw, rectDraw, eyeDropper, lineDraw, screenshot, globalConf)
};

View file

@ -96,7 +96,7 @@ void BrushTool::tool_update() {
BrushStrokeCanvasComponentPoint p;
p.pos = drawP.world.main.input.mouse.pos;
p.width = toolConfig.get_relative_width(toolConfig.brush.relativeWidth) * penWidth;
p.width = toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, toolConfig.brush.relativeWidth) * penWidth;
prevPointUnaltered = p.pos;
newBrushStroke.d.points->emplace_back(p);
newBrushStroke.d.color = toolConfig.globalConf.foregroundColor;
@ -113,7 +113,7 @@ void BrushTool::tool_update() {
BrushStrokeCanvasComponentPoint p;
p.pos = containerPtr->coords.get_mouse_pos(drawP.world);
p.width = toolConfig.get_relative_width(toolConfig.brush.relativeWidth) * penWidth;
p.width = toolConfig.get_relative_width(drawP, containerPtr->coords.inverseScale, toolConfig.brush.relativeWidth) * penWidth;
if(!addedTemporaryPoint) {
if(extensive_point_checking(brushStroke, p.pos)) {
@ -132,7 +132,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(toolConfig.brush.relativeWidth) * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) {
if((!drawingMinimumRelativeToSize && distToPrev >= 10.0) || (drawingMinimumRelativeToSize && distToPrev >= toolConfig.get_relative_width(drawP, containerPtr->coords.inverseScale, toolConfig.brush.relativeWidth) * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) {
brushPoints.back() = p;
addedTemporaryPoint = false;
@ -167,7 +167,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_slider(t.gui, "Size", &drawP.world.main.toolConfig.brush.relativeWidth);
drawP.world.main.toolConfig.relative_width_gui(drawP, "Size", &drawP.world.main.toolConfig.brush.relativeWidth);
t.gui.pop_id();
}
@ -187,7 +187,7 @@ void BrushTool::draw(SkCanvas* canvas, const DrawData& drawData) {
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.world.main.toolConfig.brush.relativeWidth);
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

View file

@ -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_slider(t.gui, "Outline Size", &toolConfig.ellipseDraw.relativeWidth);
toolConfig.relative_width_gui(drawP, "Outline Size", &toolConfig.ellipseDraw.relativeWidth);
t.gui.pop_id();
}
@ -57,7 +57,7 @@ void EllipseDrawTool::tool_update() {
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(toolConfig.ellipseDraw.relativeWidth);
newEllipse.d.strokeWidth = toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, toolConfig.ellipseDraw.relativeWidth);
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);

View file

@ -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_slider(t.gui, "Size", &drawP.world.main.toolConfig.eraser.relativeWidth);
drawP.world.main.toolConfig.relative_width_gui(drawP, "Size", &drawP.world.main.toolConfig.eraser.relativeWidth);
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;
@ -46,7 +46,7 @@ void EraserTool::tool_update() {
if(drawP.controls.leftClickHeld) {
const Vector2f& prevMousePos = drawP.controls.leftClick ? drawP.world.main.input.mouse.pos : drawP.world.main.input.mouse.lastPos;
SCollision::ColliderCollection<float> cC;
float width = drawP.world.main.toolConfig.get_relative_width(drawP.world.main.toolConfig.eraser.relativeWidth);
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<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
@ -97,7 +97,7 @@ void EraserTool::draw(SkCanvas* canvas, const DrawData& drawData) {
linePaint.setColor4f(c);
linePaint.setStyle(SkPaint::kStroke_Style);
linePaint.setStrokeCap(SkPaint::kRound_Cap);
float width = drawP.world.main.toolConfig.get_relative_width(drawP.world.main.toolConfig.eraser.relativeWidth);
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<SkPoint>(drawData.main->input.mouse.lastPos));

View file

@ -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_slider(t.gui, "Size", &toolConfig.lineDraw.relativeWidth);
toolConfig.relative_width_gui(drawP, "Size", &toolConfig.lineDraw.relativeWidth);
t.gui.checkbox_field("hasroundcaps", "Round Caps", &toolConfig.lineDraw.hasRoundCaps);
t.gui.pop_id();
}
@ -50,7 +50,7 @@ void LineDrawTool::tool_update() {
BrushStrokeCanvasComponentPoint p;
p.pos = drawP.world.main.input.mouse.pos;
p.width = toolConfig.get_relative_width(toolConfig.lineDraw.relativeWidth);
p.width = toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, toolConfig.lineDraw.relativeWidth);
newBrushStroke.d.points->emplace_back(p);
p.pos = ensure_points_have_distance(p.pos, p.pos, 1.0f);
newBrushStroke.d.points->emplace_back(p);

View file

@ -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_slider(t.gui, "Outline Size", &toolConfig.rectDraw.relativeWidth);
toolConfig.relative_width_gui(drawP, "Outline Size", &toolConfig.rectDraw.relativeWidth);
t.gui.pop_id();
}
@ -59,7 +59,7 @@ void RectDrawTool::tool_update() {
newRectangle.d.strokeColor = toolConfig.globalConf.foregroundColor;
newRectangle.d.fillColor = toolConfig.globalConf.backgroundColor;
newRectangle.d.cornerRadius = relativeRadiusWidth;
newRectangle.d.strokeWidth = toolConfig.get_relative_width(toolConfig.rectDraw.relativeWidth);
newRectangle.d.strokeWidth = toolConfig.get_relative_width(drawP, drawP.world.drawData.cam.c.inverseScale, toolConfig.rectDraw.relativeWidth);
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);