diff --git a/CMakeLists.txt b/CMakeLists.txt
index 59dfe79..3ddd7de 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ set(sources "src/main.cpp"
"src/DrawData.cpp"
"src/FontData.cpp"
"src/GridManager.cpp"
+ "src/WorldGrid.cpp"
"src/TimePoint.cpp"
"src/ConnectionManager.cpp"
"src/InputManager.cpp"
diff --git a/data/icons/grid.svg b/data/icons/grid.svg
new file mode 100644
index 0000000..759bbf9
--- /dev/null
+++ b/data/icons/grid.svg
@@ -0,0 +1,79 @@
+
+
+
+
diff --git a/data/icons/trash.svg b/data/icons/trash.svg
new file mode 100644
index 0000000..90afd2e
--- /dev/null
+++ b/data/icons/trash.svg
@@ -0,0 +1,90 @@
+
+
+
+
diff --git a/src/DrawData.hpp b/src/DrawData.hpp
index 631154b..9d4fce1 100644
--- a/src/DrawData.hpp
+++ b/src/DrawData.hpp
@@ -14,6 +14,7 @@ struct DrawData {
MainProgram* main;
bool dontUseDrawProgCache = false;
bool clampDrawBetween = true;
+ bool drawGrids = true;
WorldScalar clampDrawMinimum;
WorldScalar clampDrawMaximum;
WorldScalar mipMapLevelOne;
diff --git a/src/DrawingProgram/ScreenshotTool.cpp b/src/DrawingProgram/ScreenshotTool.cpp
index dd1b1a8..3bb7e41 100644
--- a/src/DrawingProgram/ScreenshotTool.cpp
+++ b/src/DrawingProgram/ScreenshotTool.cpp
@@ -203,6 +203,15 @@ void ScreenshotTool::take_screenshot(const std::filesystem::path& filePath) {
SkRect canvasBounds = SkRect::MakeLTRB(0.0f, 0.0f, canvasSize.x(), canvasSize.y());
std::unique_ptr canvas = SkSVGCanvas::Make(canvasBounds, &out, SkSVGCanvas::kConvertTextToPaths_Flag | SkSVGCanvas::kNoPrettyXML_Flag);
take_screenshot_svg(canvas.get(), controls.transparentBackground);
+ canvas = nullptr; // Ensure that SVG is completely written into the stream
+ #ifdef __EMSCRIPTEN__
+ auto skData = out.detachAsData();
+ emscripten_browser_file::download(
+ "screenshot.svg",
+ "image/svg+xml",
+ std::string_view((const char*)skData->bytes(), skData->size())
+ );
+ #endif
}
}
@@ -232,16 +241,16 @@ void ScreenshotTool::take_screenshot_svg(SkCanvas* canvas, bool transparentBackg
drawP.world.drawData.cam.set_viewing_area(canvasSize);
drawP.world.drawData.refresh_draw_optimizing_values();
- GridManager::GridType oldGridType = drawP.world.main.grid.gridType;
+ bool oldDrawGrids = drawP.world.drawData.drawGrids;
drawP.world.main.takingScreenshot = true;
drawP.world.main.transparentBackground = transparentBackground;
- drawP.world.main.grid.gridType = GridManager::GRIDTYPE_NONE;
+ drawP.world.drawData.drawGrids = false;
drawP.world.drawData.dontUseDrawProgCache = true;
drawP.world.main.draw(canvas);
drawP.world.main.takingScreenshot = false;
drawP.world.main.transparentBackground = false;
- drawP.world.main.grid.gridType = oldGridType;
+ drawP.world.drawData.drawGrids = oldDrawGrids;
drawP.world.drawData = d;
}
@@ -271,17 +280,16 @@ void ScreenshotTool::take_screenshot_area_hw(const sk_sp& surface, Sk
drawP.world.drawData.cam.set_viewing_area(sectionImageSize.cast());
drawP.world.drawData.refresh_draw_optimizing_values();
- GridManager::GridType oldGridType = drawP.world.main.grid.gridType;
+ bool oldDrawGrids = drawP.world.drawData.drawGrids;
drawP.world.main.takingScreenshot = true;
drawP.world.main.transparentBackground = transparentBackground;
- if(!controls.displayGrid)
- drawP.world.main.grid.gridType = GridManager::GRIDTYPE_NONE;
+ drawP.world.drawData.drawGrids = controls.displayGrid;
drawP.world.drawData.dontUseDrawProgCache = true;
drawP.world.main.draw(canvas);
drawP.world.main.takingScreenshot = false;
drawP.world.main.transparentBackground = false;
- drawP.world.main.grid.gridType = oldGridType;
+ drawP.world.drawData.drawGrids = oldDrawGrids;
drawP.world.drawData = d;
SkImageInfo aaImgInfo = SkImageInfo::Make(sectionImageSize.x(), sectionImageSize.y(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
diff --git a/src/GUIStuff/Elements/TextBox.hpp b/src/GUIStuff/Elements/TextBox.hpp
index 061a416..f7094c0 100644
--- a/src/GUIStuff/Elements/TextBox.hpp
+++ b/src/GUIStuff/Elements/TextBox.hpp
@@ -6,7 +6,7 @@ namespace GUIStuff {
template class TextBox : public Element {
public:
- void update(UpdateInputData& io, T* newData, const std::function(const std::string&)>& newFromStr, const std::function newToStr, bool newSingleLine, const std::function& elemUpdate) {
+ void update(UpdateInputData& io, T* newData, const std::function(const std::string&)>& newFromStr, const std::function newToStr, bool newSingleLine, bool updateEveryEdit, const std::function& elemUpdate) {
data = newData;
fromStr = newFromStr;
toStr = newToStr;
@@ -18,7 +18,7 @@ template class TextBox : public Element {
CLAY({
.layout = {
- .sizing = {.width = CLAY_SIZING_GROW(static_cast(io.fontSize * 2)), .height = CLAY_SIZING_GROW(0)}
+ .sizing = {.width = CLAY_SIZING_GROW(static_cast(io.fontSize * 2)), .height = CLAY_SIZING_GROW(static_cast(io.fontSize))}
},
.custom = { .customData = this }
}) {
@@ -59,12 +59,14 @@ template class TextBox : public Element {
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = textbox.remove(cur.selectionBeginPos, cur.selectionEndPos);
else
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = textbox.remove(cur.pos, textbox.move(CollabTextBox::Movement::kLeft, cur.pos));
+ update_on_edit(updateEveryEdit);
}
if(io.key.del) {
if(cur.selectionBeginPos != cur.selectionEndPos)
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = textbox.remove(cur.selectionBeginPos, cur.selectionEndPos);
else
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = textbox.remove(cur.pos, textbox.move(CollabTextBox::Movement::kRight, cur.pos));
+ update_on_edit(updateEveryEdit);
}
if(io.key.paste) {
if(cur.selectionBeginPos != cur.selectionEndPos)
@@ -76,6 +78,7 @@ template class TextBox : public Element {
}
else
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = textbox.insert(cur.pos, io.clipboard.textInFunc());
+ update_on_edit(updateEveryEdit);
}
if(io.key.enter) {
if(singleLine) {
@@ -91,6 +94,7 @@ template class TextBox : public Element {
cur.pos.fParagraphIndex++;
cur.pos.fTextByteIndex = 0;
cur.selectionBeginPos = cur.selectionEndPos = cur.pos;
+ update_on_edit(updateEveryEdit);
}
}
if(io.key.copy) {
@@ -100,6 +104,7 @@ template class TextBox : public Element {
io.clipboard.textOut = textbox.copy(cur.selectionBeginPos, cur.selectionEndPos);
if(cur.selectionBeginPos != cur.selectionEndPos)
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = textbox.remove(cur.selectionBeginPos, cur.selectionEndPos);
+ update_on_edit(updateEveryEdit);
}
if(io.key.selectAll) {
cur.selectionEndPos.fParagraphIndex = textbox.lineCount() == 0 ? 0 : textbox.lineCount() - 1;
@@ -112,6 +117,7 @@ template class TextBox : public Element {
if(cur.selectionBeginPos != cur.selectionEndPos)
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = textbox.remove(cur.selectionBeginPos, cur.selectionEndPos);
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = textbox.insert(cur.pos, io.textInput);
+ update_on_edit(updateEveryEdit);
}
io.acceptingTextInput = true;
@@ -178,6 +184,16 @@ template class TextBox : public Element {
//oldData = T();
}
+ void update_on_edit(bool updateEveryEdit) {
+ if(updateEveryEdit) {
+ std::optional dataToAssign = fromStr(textbox.get_string());
+ if(dataToAssign) {
+ *data = dataToAssign.value();
+ oldData = *data;
+ }
+ }
+ }
+
T* data = nullptr;
T oldData;
diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp
index dfbfad4..f410965 100644
--- a/src/GUIStuff/GUIManager.cpp
+++ b/src/GUIStuff/GUIManager.cpp
@@ -317,7 +317,7 @@ void GUIManager::input_color_component_255(const std::string& id, float* val, co
std::stringstream ss;
ss << convertTo255;
return ss.str();
- }, true, elemUpdate);
+ }, true, false, elemUpdate);
pop_id();
}
@@ -329,7 +329,7 @@ void GUIManager::input_text(const std::string& id, std::string* val, const std::
},
[&](const std::string& str) {
return str;
- }, true, elemUpdate);
+ }, true, true, elemUpdate);
pop_id();
}
@@ -414,7 +414,7 @@ void GUIManager::input_scalar(const std::string& id, uint8_t* val, uint8_t min,
},
[&](const uint8_t& a) {
return std::to_string(static_cast(a));
- }, true, elemUpdate);
+ }, true, false, elemUpdate);
pop_id();
}
@@ -429,7 +429,7 @@ void GUIManager::input_path(const std::string& id, std::filesystem::path* val, s
},
[&](const std::filesystem::path& a) {
return a.string();
- }, true, elemUpdate);
+ }, true, false, elemUpdate);
pop_id();
}
diff --git a/src/GUIStuff/GUIManager.hpp b/src/GUIStuff/GUIManager.hpp
index 0aef6f6..d05360a 100644
--- a/src/GUIStuff/GUIManager.hpp
+++ b/src/GUIStuff/GUIManager.hpp
@@ -107,9 +107,9 @@ class GUIManager {
return toRet;
}
- template void input_generic(const std::string& id, T* val, const std::function(const std::string&)>& fromStr, const std::function& toStr, bool singleLine, const std::function& elemUpdate = nullptr) {
+ template void input_generic(const std::string& id, T* val, const std::function(const std::string&)>& fromStr, const std::function& toStr, bool singleLine, bool updateEveryEdit, const std::function& elemUpdate = nullptr) {
push_id(id);
- insert_element>()->update(*io, val, fromStr, toStr, singleLine, elemUpdate);
+ insert_element>()->update(*io, val, fromStr, toStr, singleLine, updateEveryEdit, elemUpdate);
pop_id();
}
@@ -135,7 +135,7 @@ class GUIManager {
else
ss << *val;
return ss.str();
- }, true, elemUpdate);
+ }, true, false, elemUpdate);
}
void input_scalar(const std::string& id, uint8_t* val, uint8_t min, uint8_t max, int decimalPrecision, const std::function& elemUpdate);
@@ -184,7 +184,7 @@ class GUIManager {
}
return ss.str();
},
- true, elemUpdate);
+ true, false, elemUpdate);
}
template void color_picker(const std::string& id, T* val, bool selectAlpha, const std::function& elemUpdate = nullptr) {
diff --git a/src/GridManager.cpp b/src/GridManager.cpp
index eefc11a..5a9fd30 100644
--- a/src/GridManager.cpp
+++ b/src/GridManager.cpp
@@ -1,118 +1,34 @@
#include "GridManager.hpp"
-#include "MainProgram.hpp"
-#include
-#include
-#include
-#define GRID_CACHE_SIZE 10.0f
-#define GRID_CACHE_SIZE_INT 10
-#define GRID_DOT_SIZE 0.5f
+GridManager::GridManager(World& w):
+ world(w) {}
-// NOTE: Skia shaders return premultiplied alpha colors
-
-const char* squareDotGridSkSl =
-R"V(
-uniform vec4 pointColor;
-
-vec4 main(float2 fragCoord) {
- float m1 = step(mod(fragCoord.x + 0.25, 10.0), 0.5);
- float m2 = step(mod(fragCoord.y + 0.25, 10.0), 0.5);
- float fin = m1 * m2;
-
- vec4 pointColorUnmultiplied = vec4(pointColor.rgb, 1.0);
-
- return pointColorUnmultiplied * pointColor.a * fin;
-})V";
-
-const char* circleDotGridSkSl =
-R"V(
-uniform vec4 pointColor;
-
-vec4 main(float2 fragCoord) {
- float2 modCoord = mod(fragCoord + float2(5.0), float2(10.0));
- float fin = step(distance(float2(5.0), modCoord), 0.25);
-
- vec4 pointColorUnmultiplied = vec4(pointColor.rgb, 1.0);
-
- return pointColorUnmultiplied * pointColor.a * fin;
-})V";
-
-GridManager::GridManager(MainProgram& initMain):
- main(initMain)
-{
- auto [effect, err] = SkRuntimeEffect::MakeForShader(SkString(circleDotGridSkSl));
- if(!err.isEmpty()) {
- std::cout << "[GridManager::GridManager] Circle dot shader construction error\n";
- std::cout << err.c_str() << std::endl;
- }
- circleDotEffect = effect;
-
- auto [effect2, err2] = SkRuntimeEffect::MakeForShader(SkString(squareDotGridSkSl));
- if(!err2.isEmpty()) {
- std::cout << "[GridManager::GridManager] Square dot shader construction error\n";
- std::cout << err2.c_str() << std::endl;
- }
- squareDotEffect = effect2;
+void GridManager::init_client_callbacks() {
}
-sk_sp GridManager::get_shader(const sk_sp& runtimeEffect, const SkColor4f& pointColor) {
- SkRuntimeShaderBuilder builder(runtimeEffect);
- builder.uniform("pointColor") = pointColor;
- sk_sp s = builder.makeShader();
- return s;
+void GridManager::add_grid(const std::string& name) {
+ WorldGrid g;
+ grids[name] = g;
+ changed = true;
+}
+
+void GridManager::remove_grid(const std::string& name) {
+ grids.erase(name);
+ changed = true;
+}
+
+const std::vector& GridManager::sorted_names() {
+ if(changed) {
+ sortedNames.clear();
+ for(auto& [k, v] : grids)
+ sortedNames.emplace_back(k);
+ std::sort(sortedNames.begin(), sortedNames.end());
+ changed = false;
+ }
+ return sortedNames;
}
void GridManager::draw(SkCanvas* canvas, const DrawData& drawData) {
- if(gridType == GRIDTYPE_NONE)
- return;
- if(oldWindowSize != main.window.size.cast()) {
- gridPath = SkPath();
- float maxDim = std::max(main.window.size.x(), main.window.size.y()) * 2.0f;
- for(float i = 0.0f; i < maxDim; i += GRID_CACHE_SIZE) {
- gridPath.moveTo(i, -maxDim);
- gridPath.lineTo(i, maxDim);
- gridPath.moveTo(-maxDim, i);
- gridPath.lineTo(maxDim, i);
- }
- for(float i = 0.0f; i > -maxDim; i -= GRID_CACHE_SIZE) {
- gridPath.moveTo(i, -maxDim);
- gridPath.lineTo(i, maxDim);
- gridPath.moveTo(-maxDim, i);
- gridPath.lineTo(maxDim, i);
- }
- oldWindowSize = main.window.size.cast();
- }
- float alteredZoom = static_cast(((size / WorldScalar(GRID_CACHE_SIZE_INT)) / drawData.cam.c.inverseScale));
- if(alteredZoom > 0.5f) {
- Vector2f fracCamPos;
-
- fracCamPos.x() = static_cast((drawData.cam.c.pos.x() % size) / drawData.cam.c.inverseScale);
- fracCamPos.y() = static_cast((drawData.cam.c.pos.y() % size) / drawData.cam.c.inverseScale);
-
- SkPaint linePaint;
- if(gridType == GRIDTYPE_CIRCLEDOT || gridType == GRIDTYPE_SQUAREDOT) {
- SkColor4f pointColor = main.world->canvasTheme.toolFrontColor;
- pointColor.fA = std::clamp(alteredZoom / 20.0f, 0.0f, 0.4f);
- linePaint.setShader(get_shader(gridType == 1 ? circleDotEffect : squareDotEffect, pointColor));
- }
- else if(gridType == GRIDTYPE_LINE) {
- linePaint.setColor4f(main.world->canvasTheme.toolFrontColor);
- linePaint.setStyle(SkPaint::kStroke_Style);
- linePaint.setStrokeWidth(0.0f);
- linePaint.setAlphaf(std::clamp(alteredZoom / 35.0f, 0.0f, 0.3f));
- }
-
- canvas->save();
-
- canvas->rotate(-drawData.cam.c.rotation * 180.0 / std::numbers::pi);
- canvas->translate(-fracCamPos.x(), -fracCamPos.y());
- canvas->scale(alteredZoom, alteredZoom);
-
- if(gridType == GRIDTYPE_CIRCLEDOT || gridType == GRIDTYPE_SQUAREDOT)
- canvas->drawPaint(linePaint);
- else if(gridType == GRIDTYPE_LINE)
- canvas->drawPath(gridPath, linePaint);
-
- canvas->restore();
- }
+ for(auto& [str, g] : grids)
+ g.draw(*this, canvas, drawData);
}
diff --git a/src/GridManager.hpp b/src/GridManager.hpp
index f12b3e1..e40321d 100644
--- a/src/GridManager.hpp
+++ b/src/GridManager.hpp
@@ -1,41 +1,20 @@
#pragma once
-#include
-#include
-#include "DrawData.hpp"
-#include
-#include
-#include
+#include "WorldGrid.hpp"
-using namespace Eigen;
-
-class MainProgram;
+class World;
class GridManager {
public:
- GridManager(MainProgram& initMain);
+ GridManager(World& w);
+ void init_client_callbacks();
+ void add_grid(const std::string& name);
+ void remove_grid(const std::string& name);
void draw(SkCanvas* canvas, const DrawData& drawData);
+ const std::vector& sorted_names();
- enum GridType : unsigned {
- GRIDTYPE_NONE = 0,
- GRIDTYPE_CIRCLEDOT,
- GRIDTYPE_SQUAREDOT,
- GRIDTYPE_LINE
- };
-
- GridType gridType = GRIDTYPE_CIRCLEDOT;
+ std::unordered_map grids;
+ World& world;
private:
- sk_sp get_shader(const sk_sp& runtimeEffect, const SkColor4f& pointColor);
- Vector2f oldWindowSize{0.0f, 0.0f};
- WorldScalar size{50000000};
- SkPath gridPath;
- sk_sp circleDotEffect;
- sk_sp squareDotEffect;
- MainProgram& main;
+ bool changed = true;
+ std::vector sortedNames;
};
-
-NLOHMANN_JSON_SERIALIZE_ENUM(GridManager::GridType, {
- {GridManager::GRIDTYPE_NONE, "None"},
- {GridManager::GRIDTYPE_CIRCLEDOT, "Circle Dot"},
- {GridManager::GRIDTYPE_SQUAREDOT, "Square Dot"},
- {GridManager::GRIDTYPE_LINE, "Line"}
-})
diff --git a/src/MainProgram.cpp b/src/MainProgram.cpp
index 54dedac..3a0b7b4 100755
--- a/src/MainProgram.cpp
+++ b/src/MainProgram.cpp
@@ -36,8 +36,7 @@
#include
MainProgram::MainProgram():
- toolbar(*this),
- grid(*this)
+ toolbar(*this)
{
displayName = Random::get().alphanumeric_str(10);
@@ -229,7 +228,6 @@ void MainProgram::load_config() {
void MainProgram::draw(SkCanvas* canvas) {
canvas->clear(transparentBackground ? SkColor4f{0.0f, 0.0f, 0.0f, 0.0f} : world->canvasTheme.backColor);
- grid.draw(canvas, world->drawData);
world->draw(canvas);
toolbar.draw(canvas);
}
diff --git a/src/MainProgram.hpp b/src/MainProgram.hpp
index 3f5833f..a933343 100644
--- a/src/MainProgram.hpp
+++ b/src/MainProgram.hpp
@@ -1,6 +1,5 @@
#pragma once
#include "DrawData.hpp"
-#include "GridManager.hpp"
#include
#include
#include "InputManager.hpp"
@@ -66,7 +65,6 @@ class MainProgram {
FontData fonts;
TimePoint deltaTime;
Toolbar toolbar;
- GridManager grid;
std::shared_ptr world;
std::ofstream* logFile;
diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp
index 51cc84c..c20fe20 100644
--- a/src/Toolbar.cpp
+++ b/src/Toolbar.cpp
@@ -4,7 +4,6 @@
#include "MainProgram.hpp"
#include "GUIStuff/Elements/Element.hpp"
#include "GUIStuff/GUIManager.hpp"
-#include "GridManager.hpp"
#include "InputManager.hpp"
#include "World.hpp"
#include
@@ -212,7 +211,6 @@ nlohmann::json Toolbar::get_config_json() {
toRet["jumpTransitionTime"] = jumpTransitionTime;
toRet["dragZoomSpeed"] = dragZoomSpeed;
toRet["scrollZoomSpeed"] = scrollZoomSpeed;
- toRet["gridType"] = main.grid.gridType;
toRet["guiFontSize"] = io->fontSize;
toRet["showPerformance"] = showPerformance;
toRet["displayName"] = main.displayName;
@@ -263,7 +261,6 @@ void Toolbar::set_config_json(const nlohmann::json& j) {
j.at("dragZoomSpeed").get_to(dragZoomSpeed);
j.at("scrollZoomSpeed").get_to(scrollZoomSpeed);
j.at("guiScale").get_to(guiScale);
- j.at("gridType").get_to(main.grid.gridType);
j.at("guiFontSize").get_to(io->fontSize);
j.at("showPerformance").get_to(showPerformance);
j.at("useNativeFilePicker").get_to(useNativeFilePicker);
@@ -390,6 +387,7 @@ void Toolbar::top_toolbar() {
global_log();
bool menuPopUpJustOpen = false;
bool bookmarkMenuPopUpJustOpen = false;
+ bool gridMenuPopUpJustOpen = false;
if(gui.svg_icon_button("Main Menu Button", "data/icons/menu.svg", menuPopUpOpen)) {
menuPopUpOpen = true;
menuPopUpJustOpen = true;
@@ -401,6 +399,14 @@ void Toolbar::top_toolbar() {
gui.tab_list("file tab list", tabNames, main.worldIndex, closedTab);
if(main.world->network_being_used() && gui.svg_icon_button("Player List Toggle Button", "data/icons/list.svg", playerMenuOpen))
playerMenuOpen = !playerMenuOpen;
+ if(gui.svg_icon_button("Grids Button", "data/icons/grid.svg", gridMenu.popupOpen)) {
+ if(gridMenu.popupOpen)
+ gridMenu.popupOpen = false;
+ else {
+ gridMenu.popupOpen = true;
+ gridMenuPopUpJustOpen = true;
+ }
+ }
if(gui.svg_icon_button("Menu Undo Button", "data/icons/undo.svg"))
main.world->undo_with_checks();
if(gui.svg_icon_button("Menu Redo Button", "data/icons/redo.svg"))
@@ -417,6 +423,8 @@ void Toolbar::top_toolbar() {
if(closedTab)
main.set_tab_to_close(closedTab.value());
+ if(gridMenu.popupOpen)
+ grid_menu(gridMenuPopUpJustOpen);
if(bookMenu.popupOpen)
bookmark_menu(bookmarkMenuPopUpJustOpen);
if(menuPopUpOpen) {
@@ -532,6 +540,87 @@ If you like this app, consider downloading the native version for your system)")
}
}
+void Toolbar::grid_menu(bool justOpened) {
+ gui.push_id("grid menu");
+ CLAY({
+ .layout = {
+ .sizing = {.width = CLAY_SIZING_FIT(300), .height = CLAY_SIZING_FIT(0, 600) },
+ .padding = CLAY_PADDING_ALL(io->theme->padding1),
+ .childGap = io->theme->childGap1,
+ .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP},
+ .layoutDirection = CLAY_TOP_TO_BOTTOM
+ },
+ .backgroundColor = convert_vec4(io->theme->backColor1),
+ .cornerRadius = CLAY_CORNER_RADIUS(io->theme->windowCorners1),
+ .floating = {.attachPoints = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM}, .attachTo = CLAY_ATTACH_TO_PARENT},
+ .border = {.color = convert_vec4(io->theme->backColor2), .width = CLAY_BORDER_OUTSIDE(io->theme->windowBorders1)}
+ }) {
+ gui.obstructing_window();
+ gui.text_label_centered("Grids");
+ float entryHeight = 25.0f;
+ if(main.world->gridMan.sorted_names().empty())
+ gui.text_label_centered("No grids yet...");
+ gui.scroll_bar_many_entries_area("grid menu entries", entryHeight, main.world->gridMan.sorted_names().size(), [&](size_t i, bool isListHovered) {
+ const std::string& gridName = main.world->gridMan.sorted_names()[i];
+ bool selectedEntry = gridName == gridMenu.currentSelectedGrid;
+ CLAY({
+ .layout = {
+ .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(entryHeight)},
+ .childGap = 2,
+ .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER},
+ .layoutDirection = CLAY_LEFT_TO_RIGHT
+ },
+ .backgroundColor = selectedEntry ? convert_vec4(io->theme->backColor1) : convert_vec4(io->theme->backColor2)
+ }) {
+ gui.text_label(gridName);
+ if(Clay_Hovered() && io->mouse.leftClick && isListHovered) {
+ gridMenu.currentSelectedGrid = gridName;
+ gridMenu.newName = gridName;
+ }
+ }
+ });
+ bool gridExists = std::find(main.world->gridMan.sorted_names().begin(), main.world->gridMan.sorted_names().end(), gridMenu.newName) != main.world->gridMan.sorted_names().end();
+ gui.left_to_right_line_layout([&]() {
+ gui.input_text("grid text input", &gridMenu.newName);
+ if(gridExists) {
+ if(gui.svg_icon_button("grid remove button", "data/icons/trash.svg", false, 25.0f)) {
+ main.world->gridMan.remove_grid(gridMenu.newName);
+ gridMenu.newName.clear();
+ gridExists = false;
+ }
+ }
+ else {
+ if(gui.svg_icon_button("grid add button", "data/icons/plus.svg", false, 25.0f) && !gridMenu.newName.empty())
+ main.world->gridMan.add_grid(gridMenu.newName);
+ }
+ });
+ bool dropdownHover = false;
+ if(gridExists) {
+ auto gridFoundIt = main.world->gridMan.grids.find(gridMenu.newName);
+ if(gridFoundIt != main.world->gridMan.grids.end()) {
+ WorldGrid& g = gridFoundIt->second;
+ size_t typeSelected = static_cast(g.gridType);
+ std::vector listOfGridTypes = {
+ "Circle Points",
+ "Square Points",
+ "Square Lines",
+ "Ruled"
+ };
+ gui.left_to_right_line_layout([&]() {
+ gui.text_label("Type");
+ gui.dropdown_select("filepicker select type", &typeSelected, listOfGridTypes, 200.0f, [&]() {
+ dropdownHover = Clay_Hovered();
+ });
+ });
+ g.gridType = static_cast(typeSelected);
+ }
+ }
+ if(io->mouse.leftClick && !Clay_Hovered() && !justOpened && !dropdownHover)
+ gridMenu.popupOpen = false;
+ }
+ gui.pop_id();
+}
+
void Toolbar::bookmark_menu(bool justOpened) {
gui.push_id("bookmark menu");
CLAY({
@@ -575,19 +664,21 @@ void Toolbar::bookmark_menu(bool justOpened) {
}
}
});
- bool serverExists = std::find(main.world->bMan.sorted_names().begin(), main.world->bMan.sorted_names().end(), bookMenu.newName) != main.world->bMan.sorted_names().end();
+ bool bookmarkExists = std::find(main.world->bMan.sorted_names().begin(), main.world->bMan.sorted_names().end(), bookMenu.newName) != main.world->bMan.sorted_names().end();
gui.left_to_right_line_layout([&]() {
gui.input_text("bookmark text input", &bookMenu.newName);
- if(gui.svg_icon_button("bookmark add button", "data/icons/plus.svg", false, 25.0f) && !bookMenu.newName.empty())
- main.world->bMan.add_bookmark(bookMenu.newName);
- if(serverExists) {
+ if(bookmarkExists) {
if(gui.svg_icon_button("bookmark jump button", "data/icons/jump.svg", false, 25.0f))
main.world->bMan.jump_to_bookmark(bookMenu.newName);
- if(gui.svg_icon_button("bookmark remove button", "data/icons/close.svg", false, 25.0f)) {
+ if(gui.svg_icon_button("bookmark remove button", "data/icons/trash.svg", false, 25.0f)) {
main.world->bMan.remove_bookmark(bookMenu.newName);
bookMenu.newName.clear();
}
}
+ else {
+ if(gui.svg_icon_button("bookmark add button", "data/icons/plus.svg", false, 25.0f) && !bookMenu.newName.empty())
+ main.world->bMan.add_bookmark(bookMenu.newName);
+ }
});
if(io->mouse.leftClick && !Clay_Hovered() && !justOpened)
bookMenu.popupOpen = false;
@@ -1173,10 +1264,6 @@ void Toolbar::options_menu() {
}
case GSETTINGS_APPEARANCE: {
gui.push_id("appearance settings");
- if(gui.radio_button_field("grid type no grid", "No Grid", (main.grid.gridType == GridManager::GRIDTYPE_NONE))) main.grid.gridType = GridManager::GRIDTYPE_NONE;
- if(gui.radio_button_field("grid type circle grid", "Circle Dot Grid", (main.grid.gridType == GridManager::GRIDTYPE_CIRCLEDOT))) main.grid.gridType = GridManager::GRIDTYPE_CIRCLEDOT;
- if(gui.radio_button_field("grid type square grid", "Square Dot Grid", (main.grid.gridType == GridManager::GRIDTYPE_SQUAREDOT))) main.grid.gridType = GridManager::GRIDTYPE_SQUAREDOT;
- if(gui.radio_button_field("grid type line grid", "Line Grid", (main.grid.gridType == GridManager::GRIDTYPE_LINE))) main.grid.gridType = GridManager::GRIDTYPE_LINE;
gui.input_scalar_field("GUI Scale", "GUI Scale", &guiScale, 0.5f, 3.0f, 1);
gui.input_scalar_field("GUI Font Size", "GUI Font Size", &io->fontSize, 10, 30);
gui.text_label("Note: Changing font size may break UI in some cases.");
diff --git a/src/Toolbar.hpp b/src/Toolbar.hpp
index fe69b56..d96e402 100644
--- a/src/Toolbar.hpp
+++ b/src/Toolbar.hpp
@@ -87,6 +87,7 @@ class Toolbar {
void chat_box();
void global_log();
void top_toolbar();
+ void grid_menu(bool justOpened);
void bookmark_menu(bool justOpened);
void drawing_program_gui();
void options_menu();
@@ -168,6 +169,12 @@ class Toolbar {
std::string newName;
} bookMenu;
+ struct GridMenu {
+ bool popupOpen = false;
+ std::string currentSelectedGrid;
+ std::string newName;
+ } gridMenu;
+
bool useNativeFilePicker = true;
struct FilePicker {
bool isOpen = false;
diff --git a/src/World.cpp b/src/World.cpp
index cf42740..e44df5d 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -24,7 +24,8 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
main(initMain),
rMan(*this),
drawProg(*this),
- bMan(*this)
+ bMan(*this),
+ gridMan(*this)
{
set_canvas_background_color(main.defaultCanvasBackgroundColor);
displayName = main.displayName;
@@ -360,11 +361,7 @@ WorldScalar World::calculate_zoom_from_uniform_zoom(WorldScalar uniformZoom, Wor
return uniformZoom * ((a1 < a2) ? a1 : a2);
}
-void World::draw(SkCanvas* canvas) {
- drawData.refresh_draw_optimizing_values();
-
- drawProg.draw(canvas, drawData);
-
+void World::draw_other_player_cursors(SkCanvas* canvas, const DrawData& drawData) {
for(auto& [id, data] : clients) {
if(drawData.clampDrawMaximum > data.camCoords.inverseScale && drawData.clampDrawMinimum < data.camCoords.inverseScale) {
canvas->save();
@@ -396,3 +393,11 @@ void World::draw(SkCanvas* canvas) {
}
}
}
+
+void World::draw(SkCanvas* canvas) {
+ drawData.refresh_draw_optimizing_values();
+ if(drawData.drawGrids)
+ gridMan.draw(canvas, drawData);
+ drawProg.draw(canvas, drawData);
+ draw_other_player_cursors(canvas, drawData);
+}
diff --git a/src/World.hpp b/src/World.hpp
index 00cb7b2..de1c9f7 100644
--- a/src/World.hpp
+++ b/src/World.hpp
@@ -6,6 +6,7 @@
#include "DrawingProgram/DrawingProgram.hpp"
#include "Toolbar.hpp"
#include "SharedTypes.hpp"
+#include "GridManager.hpp"
#include
class MainProgram;
@@ -47,6 +48,7 @@ class World {
DrawingProgram drawProg;
BookmarkManager bMan;
ConnectionManager con;
+ GridManager gridMan;
std::deque chatMessages;
@@ -101,6 +103,8 @@ class World {
void init_client_callbacks();
void set_name(const std::string& n);
+ void draw_other_player_cursors(SkCanvas* canvas, const DrawData& drawData);
+
ClientPortionID nextClientID = 0;
WorldVec mousePreviousWorldVec = {0, 0};
WorldVec mouseWorldMove = {0, 0};
diff --git a/src/WorldGrid.cpp b/src/WorldGrid.cpp
new file mode 100644
index 0000000..5fdfc62
--- /dev/null
+++ b/src/WorldGrid.cpp
@@ -0,0 +1,137 @@
+#include "WorldGrid.hpp"
+#include "GridManager.hpp"
+#include "MainProgram.hpp"
+
+sk_sp WorldGrid::circlePointEffect;
+sk_sp WorldGrid::squarePointEffect;
+sk_sp WorldGrid::squareLinesEffect;
+sk_sp WorldGrid::ruledEffect;
+Vector2f WorldGrid::oldWindowSize = Vector2f{0.0f, 0.0f};
+
+#define GRID_CACHE_SIZE 10.0f
+#define GRID_CACHE_SIZE_INT 10
+
+// NOTE: Skia shaders return premultiplied alpha colors
+
+const char* circlePointShaderCode = R"V(
+uniform vec4 gridColor;
+uniform float gridScale;
+
+vec4 main(float2 fragCoord) {
+ float circleRadius = min(5.0 / gridScale, 0.25);
+ float2 modCoord = mod(fragCoord + float2(5.0), float2(10.0));
+ float fin = step(distance(float2(5.0), modCoord), circleRadius);
+
+ vec4 pointColorUnmultiplied = vec4(gridColor.rgb, 1.0);
+
+ return pointColorUnmultiplied * gridColor.a * fin;
+})V";
+
+const char* squarePointShaderCode = R"V(
+uniform vec4 gridColor;
+uniform float gridScale;
+
+vec4 main(float2 fragCoord) {
+ float squareSize = min(10.0 / gridScale, 0.5);
+ float gridUnit = 1.0 / gridScale;
+ float m1 = step(mod(fragCoord.x + gridUnit + squareSize * 0.5, 10.0), squareSize);
+ float m2 = step(mod(fragCoord.y + gridUnit + squareSize * 0.5, 10.0), squareSize);
+ float fin = m1 * m2;
+
+ vec4 pointColorUnmultiplied = vec4(gridColor.rgb, 1.0);
+
+ return pointColorUnmultiplied * gridColor.a * fin;
+})V";
+
+const char* squareLinesShaderCode = R"V(
+uniform vec4 gridColor;
+uniform float gridScale;
+
+vec4 main(float2 fragCoord) {
+ float gridUnit = 1.0 / gridScale;
+ float m1 = step(mod(fragCoord.x + gridUnit, 10.0), gridUnit);
+ float m2 = step(mod(fragCoord.y + gridUnit, 10.0), gridUnit);
+ float fin = max(m1, m2);
+
+ vec4 pointColorUnmultiplied = vec4(gridColor.rgb, 1.0);
+
+ return pointColorUnmultiplied * gridColor.a * fin;
+})V";
+
+const char* ruledShaderCode = R"V(
+uniform vec4 gridColor;
+uniform float gridScale;
+
+vec4 main(float2 fragCoord) {
+ float gridUnit = 1.0 / gridScale;
+ float fin = step(mod(fragCoord.y + gridUnit, 10.0), gridUnit);
+
+ vec4 pointColorUnmultiplied = vec4(gridColor.rgb, 1.0);
+
+ return pointColorUnmultiplied * gridColor.a * fin;
+})V";
+
+sk_sp WorldGrid::compile_effect_shader_init(const char* shaderName, const char* shaderCode) {
+ auto [effect, err] = SkRuntimeEffect::MakeForShader(SkString(shaderCode));
+ if(!err.isEmpty()) {
+ std::cout << "[GridManager::compile_effect_shader_init] " << shaderName << " shader construction error\n";
+ std::cout << err.c_str() << std::endl;
+ throw std::runtime_error("Shader Compile Failure");
+ }
+ return effect;
+}
+
+sk_sp WorldGrid::get_shader(GridType gType, const SkColor4f& gridColor, float gridScale) {
+ sk_sp runtimeEffect;
+ switch(gType) {
+ case GridType::CIRCLE_POINTS:
+ runtimeEffect = circlePointEffect;
+ break;
+ case GridType::SQUARE_POINTS:
+ runtimeEffect = squarePointEffect;
+ break;
+ case GridType::SQUARE_LINES:
+ runtimeEffect = squareLinesEffect;
+ break;
+ case GridType::RULED:
+ runtimeEffect = ruledEffect;
+ break;
+ }
+ SkRuntimeShaderBuilder builder(runtimeEffect);
+ builder.uniform("gridColor") = gridColor;
+ builder.uniform("gridScale") = gridScale;
+ sk_sp s = builder.makeShader();
+ return s;
+}
+
+void WorldGrid::draw(GridManager& gMan, SkCanvas* canvas, const DrawData& drawData) {
+ if(!circlePointEffect) {
+ circlePointEffect = compile_effect_shader_init("Circle Point", circlePointShaderCode);
+ squarePointEffect = compile_effect_shader_init("Square Point", squarePointShaderCode);
+ squareLinesEffect = compile_effect_shader_init("Square Lines", squareLinesShaderCode);
+ ruledEffect = compile_effect_shader_init("Ruled", ruledShaderCode);
+ }
+
+ float alteredZoom = static_cast(((size / WorldScalar(GRID_CACHE_SIZE_INT)) / drawData.cam.c.inverseScale));
+ if(alteredZoom > 0.5f) {
+ Vector2f fracCamPos;
+
+ fracCamPos.x() = static_cast((drawData.cam.c.pos.x() % size) / drawData.cam.c.inverseScale);
+ fracCamPos.y() = static_cast((drawData.cam.c.pos.y() % size) / drawData.cam.c.inverseScale);
+
+ SkPaint linePaint;
+ SkColor4f pointColor = gMan.world.canvasTheme.toolFrontColor;
+ pointColor.fA = std::clamp(alteredZoom / 20.0f, 0.0f, 0.4f);
+ linePaint.setShader(get_shader(gridType, pointColor, alteredZoom));
+
+ canvas->save();
+
+ canvas->rotate(-drawData.cam.c.rotation * 180.0 / std::numbers::pi);
+ canvas->translate(-fracCamPos.x(), -fracCamPos.y());
+ canvas->scale(alteredZoom, alteredZoom);
+
+ canvas->drawPaint(linePaint);
+
+ canvas->restore();
+ }
+}
diff --git a/src/WorldGrid.hpp b/src/WorldGrid.hpp
new file mode 100644
index 0000000..938faf7
--- /dev/null
+++ b/src/WorldGrid.hpp
@@ -0,0 +1,26 @@
+#pragma once
+#include
+#include "DrawData.hpp"
+#include
+
+class GridManager;
+
+class WorldGrid {
+ public:
+ void draw(GridManager& gMan, SkCanvas* canvas, const DrawData& drawData);
+ enum class GridType : unsigned {
+ CIRCLE_POINTS = 0,
+ SQUARE_POINTS,
+ SQUARE_LINES,
+ RULED
+ } gridType = GridType::CIRCLE_POINTS;
+ WorldScalar size{50000000};
+ private:
+ static sk_sp get_shader(GridType gType, const SkColor4f& gridColor, float gridScale);
+ static sk_sp compile_effect_shader_init(const char* shaderName, const char* shaderCode);
+ static Vector2f oldWindowSize;
+ static sk_sp circlePointEffect;
+ static sk_sp squarePointEffect;
+ static sk_sp squareLinesEffect;
+ static sk_sp ruledEffect;
+};