diff --git a/CMakeLists.txt b/CMakeLists.txt index 575fc3e..62b4052 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(Fry3DX VERSION 0.7.0 LANGUAGES C CXX) +project(Fry3DX VERSION 0.8.0 LANGUAGES C CXX) set(CMAKE_CXX_SCAN_FOR_MODULES OFF) # (no C++ modules; the scan can pick up a different clang than the NDK's) if(UNIX AND NOT APPLE) add_link_options(-rdynamic) # function names in crash reports diff --git a/README.md b/README.md index 46cd424..eb16ad6 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ comes inside the app (MoltenVK, on Metal), and OpenGL 4.1 is the fallback. Five tabs along the top: **Model**, **Paint**, **Layout**, **Render**, **Print** (Ctrl+1 to 5). +**Finding things** +- **Any command by name**: F3 or Ctrl+Space, type a few letters. **Round the mouse**: hold Space + for the tools, Shift+Space for the views. Give any command **your own key** (Help > Keys and + mouse). **Undo history** (Edit > Undo history): go back to any step. **Workspaces**: the panels as + you had them, kept under a name. + **Making shapes** - **Shapes that stay editable**: cube, rounded cube, plane, circle, cylinder, tube, cone, pyramid, sphere, capsule, torus, wedge, stairs, arch, and starter models (table, chair, shelf, barrel, @@ -46,7 +52,13 @@ Five tabs along the top: **Model**, **Paint**, **Layout**, **Render**, **Print** - **Draw shape** (D): click an outline on the ground, on a side view's plane, or straight on any face of another object: a new part standing on it, added to it, or cut into it. - **Move, turn, scale** (W, E, R): the arrows turn with the object. Scale keeps the other side - where it is; Shift scales both sides at once. Ctrl snaps. + where it is; Shift scales both sides at once. Ctrl snaps: to steps, or to points and the middles + of edges and faces. +- **Exact sizes**: type a number while dragging (move, turn, scale, extrude, inset, round edges) + and Enter for exactly that. Dimensions in Properties; measure anything (View > Measure). Drawing + a shape shows each line's length: type one for exactly that, Shift holds it to 15° steps. +- **3D text** from any font (Add > Text). **Spin**: an outline turned round an axis into a vase or + a wheel, or rising into a screw thread. **Import** STL, OBJ, 3MF, glTF and GLB (Ctrl+Shift+O). - **Round, smooth, mirror** any object, changeable any time, baked in when you like. - **Materials**: gold, chrome, steel, wood, ceramic, rubber and more, or how metal and rough it is; shown in the view (Solid too), exported with it. **Your own**: "Save its look as a @@ -55,7 +67,9 @@ Five tabs along the top: **Model**, **Paint**, **Layout**, **Render**, **Print** **Editing the shape** (Tab, or double-click) - Points, edges, faces (1, 2, 3). Extrude (X), inset (I), round edges (B), loop cut (C), knife (K), cut in four, smooth, merge (M), make a face, flip, separate (P), smooth points, push out / - pull in along the surface (Alt+S), **bridge** two faces with a tunnel. + pull in along the surface (Alt+S), **bridge** two faces with a tunnel, **edge slide** (Alt+G). +- **Crease** (Shift+E): picked edges stay sharp when the shape is smoothed. **Mirror editing**: + both sides change together. **Select similar** (Shift+G), loops and rings. - **Soft selection** (Shift+O): points near what you move follow it, less the further they are; the wheel while dragging sets how far (Blender's proportional editing, Maya's soft select). - **Hole** (O): round (M2–M8 screw sizes ready), rectangle, triangle or hexagon (M2–M8 nut @@ -64,9 +78,12 @@ Five tabs along the top: **Model**, **Paint**, **Layout**, **Render**, **Print** edge's ring, Ctrl+click adds or takes one. **Whole objects** (the Object menu) -- **Add together, take one out of another, keep only where they overlap**: always closed, so - always printable (Ctrl+Shift+ and Ctrl+Shift−). -- **Copies** in a row or round a circle; **make it thick** (a surface gets a back, a solid goes +- **Add together, take one out of another, keep only where they overlap**: exact + ([Manifold](https://github.com/elalish/manifold)) and always closed, so always printable + (Ctrl+Shift+ and Ctrl+Shift−). +- **Cut out, editable**: the cutters stay objects. Move one, turn it, change its shape, and the + hole follows; they go with what they cut. Apply makes it for good. +- **Copies** in a row, round a circle, or **along a path** (turning as it turns); **make it thick** (a surface gets a back, a solid goes hollow); join objects; join flat faces. - **Layers** with folders inside folders, drag and drop, hide and lock whole groups, and a button on every row that brings the camera to it. **Show only the selected** (/ or Shift+H; again: @@ -78,7 +95,8 @@ Five tabs along the top: **Model**, **Paint**, **Layout**, **Render**, **Print** keeping its shape. **Painting** -- Paint on the model or on its flat picture. **Paste any picture** (Ctrl+V): it goes on like a +- Paint on the model or on its flat picture, on **both sides** at once, or only inside a + **stencil** you drag round an area. **Paste any picture** (Ctrl+V): it goes on like a sticker that bends over edges and corners; Enter locks it as a layer, clicking it in the Picture panel unlocks it again. - **Layout tab**: move, turn and size the flat frames of the surface yourself; the paint follows. @@ -87,11 +105,16 @@ Five tabs along the top: **Model**, **Paint**, **Layout**, **Render**, **Print** - **Solid** view for modelling, **Rendered** with a real sky, sun and soft shadows (Z). Wireframe (Shift+Z), X-ray (Alt+Z). The Render tab: the sun, sky, ground, brightness, shadows, glow; F12 saves a picture of just the view. +- **Reference pictures** for the front, side and top views, see-through, shown only in their own + view if you like. **3D printing** (the Print tab) - Your printer's plate in the view (Prusa, Bambu, Creality, Elegoo, Anycubic, or your own), - sizes in millimetres, checks for holes and flipped faces with buttons that fix them, overhangs - in red, weight and filament, measuring, lay flat on a face, save 3MF or STL. + sizes in millimetres, checks for holes and flipped faces with **one repair button**, overhangs + in red, **walls too thin** in yellow, weight and filament, measuring, lay flat on a face, save + 3MF or STL. +- **Size it**: by percent, ×½, ×2, inches or centimetres to millimetres, or as big as fits the + plate. **Slice preview**: one layer as the printer makes it, at any layer height. **Scripts** (the Script panel, F5) - Lua with a `fry` library: add shapes and change their settings, combine them, move points by a @@ -106,7 +129,10 @@ Five tabs along the top: **Model**, **Paint**, **Layout**, **Render**, **Print** - Scripts that stay loaded: put a `.lua` in the plug-ins folder and it can add **commands to the Add, Object or Script menu** (`fry.command{name, menu, settings, run}`; the settings are asked for in a small form first) and **materials** to the picker. Turn each on or off in the Plug-ins - window. Three come with it: stairs (straight and spiral), scatter copies, and more materials. + window, look for one, or make your own copy of one to change. **Mouse tools** too + (`fry.tool{name, click}`): listed in the Toolbox, a click in the view calls it with the spot. + Four come with it: stairs (straight and spiral), scatter copies, stamp (a mouse tool), and more + materials. ```lua fry.plugin{name = "Shelves", about = "A bookcase from the Add menu"} @@ -130,7 +156,8 @@ fry.command{name = "Bookcase", menu = "add", settings = {{"shelves", 5}, {"width **On a phone or tablet**: everything bigger, the side panels when you want them, a bar of big buttons (undo, redo, Shift and Ctrl that stay down, edit the shape, delete, frame). One finger turns the view (or drags the arrows), two fingers move it and pinch to zoom, holding a finger -still is a right-click. Files open and save through the phone's own file picker. +still is a right-click. Files open and save through the phone's own file picker. It stays +sideways, and going to another app and back keeps your work. F1 in the app lists every key. diff --git a/docs/content.js b/docs/content.js index 51350d1..955a666 100644 --- a/docs/content.js +++ b/docs/content.js @@ -29,20 +29,24 @@ window.FRY = { features: [ { group: "make", icon: "cube", title: "Shapes that stay editable", text: "Cube, cylinder, sphere, torus, stairs, arch and more, plus starter models like a table, chair or barrel. Change their sizes any time in Properties." }, { group: "make", icon: "pen", title: "Draw a shape anywhere", text: "Click an outline on the ground or straight on the face of another object: it becomes a new part, gets added to it, or cuts into it." }, - { group: "make", icon: "combine", title: "Add, subtract, overlap", text: "Combine objects: add them together, take one out of another, or keep only where they overlap. The result is always closed and ready to print." }, - { group: "make", icon: "points", title: "Real shape editing", text: "Points, edges and faces. Extrude, inset, round edges, loop cut, knife, bridge, push and pull. Soft selection as in Blender and Maya: near points follow (Shift+O)." }, + { group: "make", icon: "combine", title: "Add, subtract, overlap", text: "Combine objects: add them together, take one out of another, or keep only where they overlap. Exact, and always closed, so always ready to print." }, + { group: "make", icon: "hole", title: "Cuts you can still move", text: "Cut one object out of another and keep the cutter: move it, turn it, change its shape, and the hole follows. It goes with what it cuts. Apply when you're done." }, + { group: "make", icon: "ruler", title: "Exact sizes", text: "Type a number while you drag for exactly that: extrude, inset, round edges, move, turn, scale, a drawn line's length. Dimensions in Properties, snapping to points and the middles of edges and faces, measuring." }, + { group: "make", icon: "text", title: "Text, spin, import", text: "3D text from any font. An outline spun round an axis into a vase or a wheel, or up into a screw thread. STL, OBJ, 3MF and glTF brought in, the right way up." }, + { group: "make", icon: "points", title: "Real shape editing", text: "Points, edges and faces. Extrude, inset, round edges, loop cut, knife, bridge, push and pull, edge slide, creases that stay sharp when smoothed, both sides at once. Soft selection as in Blender and Maya: near points follow (Shift+O)." }, { group: "make", icon: "hole", title: "Holes for screws and nuts", text: "Round, rectangle, triangle or hexagon. M2 to M8 screw and nut sizes ready, with a little room added so the print actually fits." }, - { group: "make", icon: "copies", title: "Copies and thickness", text: "Copies in a row or round in a circle. Make a surface thick, or a solid hollow with walls exactly as thick as you need." }, + { group: "make", icon: "copies", title: "Copies and thickness", text: "Copies in a row, round in a circle, or along a path, turning as it turns. Make a surface thick, or a solid hollow with walls exactly as thick as you need." }, { group: "paint", icon: "sculpt", title: "Sculpt it like clay", text: "Raise, puff out, smooth, flatten, pinch and grab the surface with a brush, the same on both sides if you like. A shape with too few points is made finer first, keeping its shape." }, { group: "print", icon: "sphere", title: "Materials you can see", text: "Gold, chrome, brushed steel, wood, ceramic, rubber and more, or set how metal and rough it is yourself; save a look as your own material for every file. Shown in the view, and exported with it." }, - { group: "paint", icon: "brush", title: "Paint and stickers", text: "Paint on the model or on its flat picture. Paste any picture (Ctrl+V) and it goes on like a sticker that bends over edges and corners." }, + { group: "paint", icon: "brush", title: "Paint and stickers", text: "Paint on the model or on its flat picture, on both sides at once, or only inside a stencil. Paste any picture (Ctrl+V) and it goes on like a sticker that bends over edges and corners." }, { group: "paint", icon: "layout", title: "Layout tab", text: "Move, turn and size the flat frames of the surface by hand. The paint follows, so the model looks the same." }, - { group: "print", icon: "print", title: "Made for 3D printing", text: "Your printer's plate in the view, sizes in millimetres, checks for holes and flipped faces with fix buttons, overhangs shown red, weight and filament." }, + { group: "print", icon: "print", title: "Made for 3D printing", text: "Your printer's plate in the view, sizes in millimetres and a scaler (percent, inches to mm, fit the plate), checks with one repair button, overhangs in red, walls too thin in yellow, a layer-by-layer slice preview, weight and filament." }, { group: "print", icon: "sun", title: "See it rendered", text: "Solid view for modelling, a real renderer with sky, sun and soft shadows when you want to see it properly. Save a picture with F12." }, + { group: "work", icon: "search", title: "Find anything", text: "F3 or Ctrl+Space finds any command by name. Hold Space for the tools round the mouse, Shift+Space for the views. Your own keys for any command, the undo history, and workspaces: the panels as you had them." }, { group: "work", icon: "folder", title: "Folders and groups", text: "Layers with folders inside folders, drag and drop, show/hide and lock whole groups, and a button on every row that brings the camera to it." }, { group: "work", icon: "phone", title: "On your phone too", text: "Not a viewer: the whole modeller. Big buttons, one finger turns the view, two move and pinch to zoom, holding still is a right-click. Files through the phone's own picker." }, { group: "work", icon: "code", title: "Scripts, if you want them", text: "Lua in a Script panel: build a town in a loop, turn a profile into a vase, move points by a formula. Kept in the .fry with the model; F5 runs one, Ctrl+Z takes it all back." }, - { group: "work", icon: "plug", title: "Plug-ins", text: "A script in the plug-ins folder stays loaded: it adds commands to the Add, Object or Script menu (with a little form for their settings) and materials to the picker. Stairs, scatter and more materials come with it." }, + { group: "work", icon: "plug", title: "Plug-ins", text: "A script in the plug-ins folder stays loaded: it adds commands to the Add, Object or Script menu (with a little form for their settings) and materials to the picker. Or a mouse tool in the Toolbox. Stairs, scatter, stamp and more materials come with it." }, { group: "work", icon: "save", title: "Never lose work", text: "Its own packed .fry file, autosave in the background, recovery after a crash, and a Save / Don't save / Cancel when closing." }, ], @@ -124,6 +128,7 @@ fry.frame()`, }, versions: [ + { v: "0.8", date: "2026-10-21", items: ["Quicker to use, exact to size", "Find any command by name (F3), the tools round the mouse (Space), your own keys, the undo history, workspaces", "Typed sizes while dragging, snapping, measuring, a line's length while drawing", "Cuts you can still move; combining that's exact", "3D text, spin and screw, copies along a path, crease, edge slide, mirror editing", "Import STL, OBJ, 3MF and glTF", "Print: a scaler, thin walls, a slice preview, repair all at once", "Painting both sides and inside a stencil; plug-ins can add mouse tools", "Far away things no longer vanish; steadier and smoother on phones, which stay sideways"] }, { v: "0.7", date: "2026-10-21", items: ["Steadier and tidier", "Cutting a shape inside an earlier cut no longer crashes, and cuts come out closed", "Edges hidden behind the surface stay hidden on big or cut-up shapes", "Every panel has a least size, so it stays readable; the first layout fits small windows", "Eye and padlock buttons in Layers; the tabs fit narrow windows", "A failed tool keeps your redo"] }, { v: "0.6", date: "2026-10-20", items: ["Sculpting, scripts and a Mac app", "Sculpting: raise, puff out, smooth, flatten, pinch and grab", "Materials shown in the view, sixteen to start from", "Scripts: Lua and a Script panel, with examples, kept in the .fry", "Plug-ins: menu commands and materials, three to start with", "Your own materials", "Soft selection, bridge, push / pull, smooth points, show only the selected", "macOS: one app for Apple silicon and Intel", "A new look (Niveus): light and dark, following the desktop", "A new logo", "Drop a file on the window to open it"] }, { v: "0.5", date: "2026-10-19", items: ["Builds for Windows, Linux (AppImage) and Android", "A phone layout: big buttons, finger gestures, the phone's file picker", "Apache License 2.0"] }, diff --git a/docs/site.js b/docs/site.js index 1a6158e..1c04a17 100644 --- a/docs/site.js +++ b/docs/site.js @@ -25,6 +25,9 @@ phone: '', save: '', code: '', + search: '', + ruler: '', + text: '', sculpt: '', plug: '', sphere: '', diff --git a/src/app.h b/src/app.h index baf6141..8983237 100644 --- a/src/app.h +++ b/src/app.h @@ -1240,7 +1240,7 @@ private: // ───────────────────────────── commands ───────────────────────────── // Everything that can be done, by name: for the command search, keys of your own, the pies. - void buildCommands(); void openSearch(); void runCommand(const std::string& id); void commandSearch(); + void buildCommands(); void openSearch(); void runCommand(const std::string& id); void commandItem(const char* id); void commandSearch(); // The pies: the tools (Space), or the views (Shift+Space), round the mouse while it's held. void pieFrame(ImGuiIO& io); // Keys of your own: kept in keys.txt (a line each: the command's id, a tab, the key). diff --git a/src/app_panels.cpp b/src/app_panels.cpp index b0f4332..6056aaf 100644 --- a/src/app_panels.cpp +++ b/src/app_panels.cpp @@ -551,6 +551,17 @@ auto App::runCommand(const std::string& id) -> void { } } +// A menu item for a command: its name, its key (your own if you gave it one), greyed when it can't be done now. +auto App::commandItem(const char* id) -> void { + for (const fry::Command& c : commands_) + if (c.id == id) { + const auto mine = myKeys_.find(c.id); + const std::string keys = mine != myKeys_.end() ? fry::chordName(mine->second) : c.keys; + if (ImGui::MenuItem(c.name.c_str(), keys.empty() ? nullptr : keys.c_str(), false, c.usable())) runCommand(c.id); + return; + } +} + auto App::commandSearch() -> void { if (!search_.open) return; if (const int i = search_.draw(commands_, recentCommands_); i >= 0) runCommand(commands_[size_t(i)].id); @@ -623,6 +634,7 @@ auto App::menus() -> void { if (ImGui::BeginMenu("Edit")) { if (ImGui::MenuItem("Undo", "Ctrl+Z", false, !undo_.empty())) undo(); if (ImGui::MenuItem("Redo", "Ctrl+Y", false, !redo_.empty())) redo(); + commandItem("edit.history"); ImGui::Separator(); if (ImGui::MenuItem("Duplicate", "Ctrl+D", false, mode_ == Mode::Object)) duplicateSelected(); if (ImGui::MenuItem("Join into one", "Ctrl+J", false, mode_ == Mode::Object && selected_.size() > 1)) joinSelected(); @@ -640,6 +652,9 @@ auto App::menus() -> void { if (ImGui::MenuItem("More (grow)", "Ctrl +")) growSelection(); if (ImGui::MenuItem("Less (shrink)", "Ctrl -")) shrinkSelection(); if (ImGui::MenuItem("Everything joined to it", "L")) selectLinked(); + commandItem("select.similar"); + commandItem("select.loop"); + commandItem("select.ring"); ImGui::MenuItem("A loop: Alt+click or double-click", nullptr, false, false); ImGui::EndDisabled(); ImGui::Separator(); @@ -669,6 +684,7 @@ auto App::menus() -> void { if (ImGui::MenuItem("Join (just as they are)", "Ctrl+J", false, many)) joinSelected(); ImGui::Separator(); if (ImGui::MenuItem("Make it thick (or hollow)...", nullptr, false, any)) showThicken_ = true; + commandItem("object.spin"); if (ImGui::MenuItem("Join flat faces side by side", nullptr, false, any)) cleanFlatFaces(); pluginMenuItems("object"); ImGui::EndMenu(); @@ -692,6 +708,11 @@ auto App::menus() -> void { if (ImGui::MenuItem("Push out / pull in...", "Alt+S")) startPush(); if (ImGui::MenuItem("Bridge two faces")) bridgeNow(); ImGui::MenuItem("Soft selection", "Shift+O", &softSel_); + ImGui::EndDisabled(); + commandItem("shape.crease"); + commandItem("shape.slide"); + commandItem("shape.mirror"); + ImGui::BeginDisabled(!ed); if (ImGui::MenuItem("Make into its own object", "P")) separateSelected(); if (ImGui::MenuItem("Delete", "Del")) deleteElements(); ImGui::EndDisabled(); @@ -722,6 +743,9 @@ auto App::menus() -> void { if (ImGui::MenuItem("Frame selected", "F")) frameSelected(); if (ImGui::MenuItem("Frame everything", "Home")) frameAll(); if (ImGui::MenuItem("Show only the selected (again: all)", "/", false, !selected_.empty())) showOnly(selected_); + ImGui::Separator(); + commandItem("tools.measure"); + commandItem("view.workspace"); ImGui::EndMenu(); } if (ImGui::BeginMenu("Script")) { @@ -755,6 +779,7 @@ auto App::menus() -> void { } if (ImGui::BeginMenu("Help")) { if (ImGui::MenuItem("Keys and mouse", "F1")) showHelp_ = true; + commandItem("help.search"); if (ImGui::MenuItem("About Fry3DX")) showAbout_ = true; ImGui::EndMenu(); } @@ -813,6 +838,7 @@ auto App::addMenuItems() -> void { setTool(Tool::Draw); } if (ImGui::MenuItem("Reference picture...")) dialog(4); + commandItem("add.text"); } // How the last item's key looks: filled when it's the one in use, sunk while held. @@ -917,7 +943,7 @@ auto App::toolbox() -> void { for (const fry::PluginTool& t : plugins_[i].p->tools) { if (std::exchange(first, false)) ImGui::SeparatorText("Plug-in tools"); const bool on = pluginTool_ == std::pair(plugins_[i].file, t.name); - if (ImGui::Selectable(std::format("{}##{}", t.name, plugins_[i].file).c_str(), on, 0, ImVec2(w, 0))) + if (ImGui::Selectable(std::format("{}##{}", t.name, plugins_[i].file).c_str(), on, 0, ImVec2(w, touch() ? 34.0f : 0.0f))) on ? setTool(Tool::Select) : void((pluginTool_ = {plugins_[i].file, t.name}, tool_ = Tool::Select, say(t.name + ": click in the view (Esc stops)"))); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s%s(%s)", t.help.c_str(), t.help.empty() ? "" : "\n", plugins_[i].p->name.c_str()); }