Merge branch 'main' into exturuder-tab-multi-switch

This commit is contained in:
SoftFever
2026-10-06 17:56:06 +08:00
committed by GitHub
195 changed files with 204610 additions and 39303 deletions
+2
View File
@@ -153,6 +153,8 @@ using namespace nlohmann;
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Jobs/SendJob.hpp"
#include <boost/nowide/convert.hpp>
#include <stdio.h>
namespace fs = boost::filesystem;
+1
View File
@@ -35,6 +35,7 @@ extern "C"
#include <boost/algorithm/string/classification.hpp>
#include <stdio.h>
#include <boost/algorithm/string/constants.hpp>
#ifdef SLIC3R_GUI
class OpenGLVersionCheck
+8
View File
@@ -11,6 +11,14 @@
#include "git_commit_hash.h"
#include "libslic3r_version.h"
#include "StackWalker.h"
#include <algorithm>
#include <atomic>
#include <boost/nowide/fstream.hpp>
#include <cstdarg>
#include <cstddef>
#include <ctime>
#include <excpt.h>
static std::string g_log_folder;
static std::atomic<int> g_crash_log_count = 0;
+2
View File
@@ -3,6 +3,8 @@
#include <boost/nowide/fstream.hpp>
#include "StackWalker.h"
#include <eh.h>
#include <cstddef>
#include <string>
class CBaseException : public CStackWalker
{
+3
View File
@@ -2,6 +2,9 @@
#include <strsafe.h>
//#include <atlconv.h>
#include <dbghelp.h>
#include <cstdarg>
#include <cstddef>
#include <cstring>
#pragma comment(lib, "version.lib")
#pragma comment( lib, "dbghelp.lib" )
+1
View File
@@ -2,6 +2,7 @@
#include <Windows.h>
#include <tchar.h>
#include <vector>
#include <cstddef>
namespace textconv_helper
{
+3
View File
@@ -42,6 +42,9 @@
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <cassert>
#include <iterator>
#include <string_view>
#ifdef WIN32
//FIXME replace the two following includes with <boost/md5.hpp> after it becomes mainstream.
@@ -3,6 +3,9 @@
#include <cstdio>
#include <boost/filesystem/path.hpp>
#include <boost/nowide/convert.hpp>
#include <algorithm>
#include <string>
#include <vector>
#ifdef WIN32
#include <psapi.h>
File diff suppressed because it is too large Load Diff
+18 -16
View File
@@ -1,6 +1,7 @@
#include "libslic3r/CAD/GeometryEngine.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/I18N.hpp"
#include <BRepMesh_IncrementalMesh.hxx>
#include <BRep_Tool.hxx>
@@ -73,19 +74,19 @@ std::vector<TopoDS_Shape> GeometryEngine::read_step_solids(const std::string& pa
try {
STEPControl_Reader reader;
if (reader.ReadFile(path.c_str()) != IFSelect_RetDone) {
err = "cannot read STEP file";
err = _u8L("cannot read STEP file");
return out;
}
reader.TransferRoots();
const TopoDS_Shape shape = reader.OneShape();
if (shape.IsNull()) { err = "STEP file has no geometry"; return out; }
if (shape.IsNull()) { err = _u8L("STEP file has no geometry"); return out; }
// One body per top-level solid; fall back to the whole shape (shells/faces) if none.
for (TopExp_Explorer ex(shape, TopAbs_SOLID); ex.More(); ex.Next())
out.push_back(ex.Current());
if (out.empty())
out.push_back(shape);
} catch (const Standard_Failure& e) {
err = *e.what() ? e.what() : "OCCT failed to read STEP";
err = *e.what() ? e.what() : _u8L("OCCT failed to read STEP");
out.clear();
}
return out;
@@ -115,7 +116,7 @@ TopoDS_Shape GeometryEngine::mesh_to_brep(const indexed_triangle_set& its,
if (tolerance <= 0.0)
throw std::runtime_error("mesh_to_brep: tolerance must be > 0");
if (its.indices.empty())
throw std::runtime_error("mesh_to_brep: mesh has no triangles");
throw std::runtime_error(_u8L("mesh_to_brep: mesh has no triangles"));
// 1. Tolerance-quantized vertex dedup. A merged vertex keeps the exact coordinates of the
// first input occurrence — vertices are grouped by a cell, never snapped onto its grid.
@@ -150,8 +151,8 @@ TopoDS_Shape GeometryEngine::mesh_to_brep(const indexed_triangle_set& its,
}
stats.kept_tris = int(tris.size());
if (tris.empty())
throw std::runtime_error("mesh_to_brep: every triangle was rejected as degenerate "
"(try a smaller tolerance)");
throw std::runtime_error(_u8L("mesh_to_brep: every triangle was rejected as degenerate "
"(try a smaller tolerance)"));
// 3. One face per triangle, sharing vertices and edges through the caches.
std::vector<TopoDS_Vertex> vertex_cache(verts.size());
@@ -361,7 +362,7 @@ TopoDS_Shape GeometryEngine::apply_fillet(const TopoDS_Shape& solid, double radi
// A too-large radius (e.g. >= half the smallest spanned dimension) makes the
// operation degenerate; OCCT leaves IsDone() false. Report it instead of
// silently returning the unfilleted solid (which reads as a false success).
if (!fillet.IsDone()) throw std::runtime_error("fillet radius too large for this geometry");
if (!fillet.IsDone()) throw std::runtime_error(_u8L("fillet radius too large for this geometry"));
return fillet.Shape();
}
@@ -377,7 +378,7 @@ TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double dis
chamfer.Add(distance, edge); // symmetric chamfer
chamfer.Build();
if (!chamfer.IsDone()) throw std::runtime_error("chamfer distance too large for this geometry");
if (!chamfer.IsDone()) throw std::runtime_error(_u8L("chamfer distance too large for this geometry"));
return chamfer.Shape();
}
@@ -404,7 +405,7 @@ TopoDS_Shape GeometryEngine::apply_fillet(const TopoDS_Shape& solid, double radi
}
mk.Build();
if (!mk.IsDone()) throw std::runtime_error("apply_fillet: OCCT fillet failed");
if (!mk.IsDone()) throw std::runtime_error(_u8L("apply_fillet: OCCT fillet failed"));
return mk.Shape();
}
@@ -421,7 +422,7 @@ TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double dis
}
mk.Build();
if (!mk.IsDone()) throw std::runtime_error("apply_chamfer: OCCT chamfer failed");
if (!mk.IsDone()) throw std::runtime_error(_u8L("apply_chamfer: OCCT chamfer failed"));
return mk.Shape();
}
@@ -547,12 +548,13 @@ GeometryEngine::MassProps GeometryEngine::mass_properties(const TopoDS_Shape& sh
std::string GeometryEngine::primitive_name(PrimitiveType type)
{
switch (type) {
case PrimitiveType::Box: return "Box";
case PrimitiveType::Cylinder: return "Cylinder";
case PrimitiveType::Sphere: return "Sphere";
case PrimitiveType::Cone: return "Cone";
case PrimitiveType::Torus: return "Torus";
default: return "Unknown";
// TRN Default name of an object created from the box primitive shape.
case PrimitiveType::Box: return _u8L("Box");
case PrimitiveType::Cylinder: return _u8L("Cylinder");
case PrimitiveType::Sphere: return _u8L("Sphere");
case PrimitiveType::Cone: return _u8L("Cone");
case PrimitiveType::Torus: return _u8L("Torus");
default: return _u8L("Unknown");
}
}
+30 -29
View File
@@ -1,6 +1,7 @@
#include "libslic3r/CAD/SketchEngine.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/I18N.hpp"
#include <Standard_Handle.hxx>
#include <GeomAbs_SurfaceType.hxx>
@@ -138,7 +139,7 @@ Vec3d SketchPlane::to_world(const Vec2d& pt) const
TopoDS_Wire SketchProfile::to_occt_wire(const SketchPlane& plane) const
{
if (points.size() < 2)
throw std::runtime_error("Profile has fewer than 2 points");
throw std::runtime_error(_u8L("Profile has fewer than 2 points"));
BRepBuilderAPI_MakeWire builder;
for (size_t i = 0; i < points.size(); ++i) {
@@ -150,7 +151,7 @@ TopoDS_Wire SketchProfile::to_occt_wire(const SketchPlane& plane) const
}
builder.Build();
if (!builder.IsDone())
throw std::runtime_error("Failed to build wire from profile");
throw std::runtime_error(_u8L("Failed to build wire from profile"));
return builder.Wire();
}
@@ -158,9 +159,9 @@ TopoDS_Wire SketchProfile::to_occt_wire(const SketchPlane& plane) const
TopoDS_Shape SketchEngine::make_prism(const TopoDS_Shape& base, const gp_Vec& vec)
{
if (vec.Magnitude() < 1e-9) throw std::runtime_error("extrude depth is zero");
if (vec.Magnitude() < 1e-9) throw std::runtime_error(_u8L("extrude depth is zero"));
BRepPrimAPI_MakePrism prism(base, vec);
if (!prism.IsDone()) throw std::runtime_error("extrude failed");
if (!prism.IsDone()) throw std::runtime_error(_u8L("extrude failed"));
return prism.Shape();
}
@@ -169,7 +170,7 @@ static TopoDS_Shape extrude_face_internal(const TopoDS_Face& face, const gp_Dir&
if (symmetric) {
gp_Vec halfVec = gp_Vec(dir) * (length / 2.0);
BRepAlgoAPI_Fuse fuse(SketchEngine::make_prism(face, halfVec), SketchEngine::make_prism(face, -halfVec));
if (!fuse.IsDone()) throw std::runtime_error("Fuse failed");
if (!fuse.IsDone()) throw std::runtime_error(_u8L("Fuse failed"));
return fuse.Shape();
}
return SketchEngine::make_prism(face, gp_Vec(dir) * length);
@@ -179,7 +180,7 @@ TopoDS_Shape SketchEngine::make_extrude(const TopoDS_Wire& wire, const SketchPla
double length, bool symmetric, double taper_deg)
{
BRepBuilderAPI_MakeFace fm(wire);
if (!fm.IsDone()) throw std::runtime_error("Failed to make face from wire");
if (!fm.IsDone()) throw std::runtime_error(_u8L("Failed to make face from wire"));
return make_extrude(fm.Face(), plane, length, symmetric, taper_deg);
}
@@ -194,7 +195,7 @@ TopoDS_Shape SketchEngine::make_extrude_two_sided(const TopoDS_Wire& wire, const
double up, double down)
{
BRepBuilderAPI_MakeFace fm(wire);
if (!fm.IsDone()) throw std::runtime_error("Failed to make face from wire");
if (!fm.IsDone()) throw std::runtime_error(_u8L("Failed to make face from wire"));
return make_extrude_two_sided(fm.Face(), plane, up, down);
}
@@ -206,7 +207,7 @@ TopoDS_Shape SketchEngine::make_extrude_two_sided(const TopoDS_Face& face, const
if (d < 1e-9) return make_prism(face, gp_Vec(dir) * u);
if (u < 1e-9) return make_prism(face, gp_Vec(dir) * -d);
BRepAlgoAPI_Fuse fuse(make_prism(face, gp_Vec(dir) * u), make_prism(face, gp_Vec(dir) * -d));
if (!fuse.IsDone()) throw std::runtime_error("two-sided extrude fuse failed");
if (!fuse.IsDone()) throw std::runtime_error(_u8L("two-sided extrude fuse failed"));
return fuse.Shape();
}
@@ -216,7 +217,7 @@ TopoDS_Shape SketchEngine::make_extrude_taper(const TopoDS_Wire& wire, const Ske
gp_Dir dir(plane.normal.x(), plane.normal.y(), plane.normal.z());
auto straight = [&]() -> TopoDS_Shape {
BRepBuilderAPI_MakeFace fm(wire);
if (!fm.IsDone()) throw std::runtime_error("Failed to make face from wire");
if (!fm.IsDone()) throw std::runtime_error(_u8L("Failed to make face from wire"));
return make_prism(fm.Face(), gp_Vec(dir) * length);
};
if (std::abs(taper_deg) >= 89.0 || std::abs(length) < 1e-9) return straight();
@@ -262,7 +263,7 @@ TopoDS_Shape SketchEngine::make_extrude_regions(
const SketchPlane& plane, double length, bool symmetric)
{
// The loop below skips regions that fail, so a zero depth is rejected before it.
if (std::abs(length) < 1e-9) throw std::runtime_error("extrude depth is zero");
if (std::abs(length) < 1e-9) throw std::runtime_error(_u8L("extrude depth is zero"));
// Drop consecutive coincident points and the closing duplicate. FreeType /
// SVG flattening routinely emits repeated points which would build a
@@ -348,7 +349,7 @@ TopoDS_Shape SketchEngine::make_extrude_regions(
}
}
if (count == 0) throw std::runtime_error("imported regions produced no extrudable geometry");
if (count == 0) throw std::runtime_error(_u8L("imported regions produced no extrudable geometry"));
return count == 1 ? last : TopoDS_Shape(comp); // avoid a compound-of-one
}
@@ -356,7 +357,7 @@ TopoDS_Shape SketchEngine::make_revolve(const TopoDS_Wire& wire, const gp_Ax1& a
{
BRepBuilderAPI_MakeFace faceMaker(wire);
if (!faceMaker.IsDone())
throw std::runtime_error("Failed to make face from wire");
throw std::runtime_error(_u8L("Failed to make face from wire"));
TopoDS_Face face = faceMaker.Face();
// A profile on both sides of the axis sweeps through itself; MakeRevol then fails with no
@@ -387,7 +388,7 @@ TopoDS_Shape SketchEngine::make_revolve(const TopoDS_Wire& wire, const gp_Ax1& a
if (angle_rad < 0) { axis.Reverse(); angle_rad = -angle_rad; }
BRepPrimAPI_MakeRevol rev(face, axis, angle_rad);
if (!rev.IsDone())
throw std::runtime_error("Failed to revolve");
throw std::runtime_error(_u8L("Failed to revolve"));
if (!BRepCheck_Analyzer(rev.Shape()).IsValid())
throw std::runtime_error("the profile crosses the revolve axis — it must lie on one side of it");
return rev.Shape();
@@ -397,29 +398,29 @@ TopoDS_Shape SketchEngine::make_sweep(const TopoDS_Wire& profile, const TopoDS_W
{
BRepBuilderAPI_MakeFace faceMaker(profile);
if (!faceMaker.IsDone())
throw std::runtime_error("Failed to make face from sweep profile");
throw std::runtime_error(_u8L("Failed to make face from sweep profile"));
TopoDS_Face face = faceMaker.Face();
BRepOffsetAPI_MakePipe pipe(path, face);
pipe.Build();
if (!pipe.IsDone())
throw std::runtime_error("Failed to sweep profile along path");
throw std::runtime_error(_u8L("Failed to sweep profile along path"));
return pipe.Shape();
}
TopoDS_Shape SketchEngine::make_loft(const std::vector<TopoDS_Wire>& profiles, bool ruled)
{
if (profiles.size() < 2)
throw std::runtime_error("loft needs at least 2 profiles");
throw std::runtime_error(_u8L("loft needs at least 2 profiles"));
BRepOffsetAPI_ThruSections loft(true /*solid*/, ruled);
for (const TopoDS_Wire& w : profiles) {
if (w.IsNull()) throw std::runtime_error("loft: null profile wire");
loft.AddWire(w);
}
loft.Build();
if (!loft.IsDone()) throw std::runtime_error("loft failed");
if (!loft.IsDone()) throw std::runtime_error(_u8L("loft failed"));
TopoDS_Shape s = loft.Shape();
if (s.IsNull()) throw std::runtime_error("loft produced no solid");
if (s.IsNull()) throw std::runtime_error(_u8L("loft produced no solid"));
return s;
}
@@ -427,16 +428,16 @@ TopoDS_Shape SketchEngine::make_loft(const std::vector<TopoDS_Wire>& profiles, b
TopoDS_Shape SketchEngine::make_loft_surface(const std::vector<TopoDS_Wire>& profiles, bool ruled)
{
if (profiles.size() < 2)
throw std::runtime_error("loft needs at least 2 profiles");
throw std::runtime_error(_u8L("loft needs at least 2 profiles"));
BRepOffsetAPI_ThruSections loft(false /*shell, no end caps*/, ruled);
for (const TopoDS_Wire& w : profiles) {
if (w.IsNull()) throw std::runtime_error("loft: null profile wire");
loft.AddWire(w);
}
loft.Build();
if (!loft.IsDone()) throw std::runtime_error("loft failed");
if (!loft.IsDone()) throw std::runtime_error(_u8L("loft failed"));
TopoDS_Shape s = loft.Shape();
if (s.IsNull()) throw std::runtime_error("loft produced no shape");
if (s.IsNull()) throw std::runtime_error(_u8L("loft produced no shape"));
return s;
}
@@ -444,11 +445,11 @@ TopoDS_Shape SketchEngine::make_pocket(const TopoDS_Wire& wire, const SketchPlan
const TopoDS_Shape& target, double depth)
{
BRepBuilderAPI_MakeFace fm(wire);
if (!fm.IsDone()) throw std::runtime_error("Pocket face failed");
if (!fm.IsDone()) throw std::runtime_error(_u8L("Pocket face failed"));
TopoDS_Shape tool = extrude_face_internal(fm.Face(),
gp_Dir(plane.normal.x(), plane.normal.y(), plane.normal.z()), depth + 1.0, false);
BRepAlgoAPI_Cut cut(target, tool);
if (!cut.IsDone()) throw std::runtime_error("Pocket cut failed");
if (!cut.IsDone()) throw std::runtime_error(_u8L("Pocket cut failed"));
return cut.Shape();
}
@@ -929,7 +930,7 @@ static TopoDS_Face checked_profile_face(const TopoDS_Face& f)
TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
const SketchPlane& plane)
{
if (wires.empty()) throw std::runtime_error("sketch has no closed loop");
if (wires.empty()) throw std::runtime_error(_u8L("sketch has no closed loop"));
// The ASSEMBLED face below is built on the SKETCH's own plane rather than on a surface OCCT
// infers from the outer wire. The inferred plane has no reason to share the sketch's normal,
@@ -944,7 +945,7 @@ TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
if (wires.size() == 1) {
BRepBuilderAPI_MakeFace fm(wires[0]);
if (!fm.IsDone()) throw std::runtime_error("sketch loop does not bound a face");
if (!fm.IsDone()) throw std::runtime_error(_u8L("sketch loop does not bound a face"));
return checked_profile_face(fm.Face());
}
@@ -956,7 +957,7 @@ TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
areas.reserve(wires.size());
for (const TopoDS_Wire& w : wires) {
BRepBuilderAPI_MakeFace fm(w);
if (!fm.IsDone()) throw std::runtime_error("sketch loop does not bound a face");
if (!fm.IsDone()) throw std::runtime_error(_u8L("sketch loop does not bound a face"));
faces.push_back(fm.Face());
GProp_GProps props;
BRepGProp::SurfaceProperties(faces.back(), props);
@@ -982,10 +983,10 @@ TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
got = true;
break;
}
if (!got) throw std::runtime_error("sketch loop does not bound a face");
if (!got) throw std::runtime_error(_u8L("sketch loop does not bound a face"));
BRepClass_FaceClassifier fc(faces[outer], p, 1e-7);
if (fc.State() != TopAbs_IN)
throw std::runtime_error("sketch has two disjoint regions; put each in its own sketch");
throw std::runtime_error(_u8L("sketch has two disjoint regions; put each in its own sketch"));
// Add the hole loop AS-IS and let ShapeFix_Face sort the orientations out below.
// Reversing it here only works when the sketch happened to wind both loops the same
// way: a circle drawn clockwise inside a counter-clockwise rectangle comes out matching
@@ -995,7 +996,7 @@ TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
// 147520 — a body larger than its own bounding box, which is the signature of it.
fm.Add(wires[i]);
}
if (!fm.IsDone()) throw std::runtime_error("sketch loop does not bound a face");
if (!fm.IsDone()) throw std::runtime_error(_u8L("sketch loop does not bound a face"));
// Winding-independent classification of outer vs holes — the same idiom make_extrude_regions
// already uses for imported glyphs, which is why holed TEXT extruded correctly all along
// while a holed SKETCH did not.
+1
View File
@@ -43,6 +43,7 @@
#include "libslic3r/AABBTreeLines.hpp" // search structure for found close points
#include "libslic3r/Line.hpp"
#include "libslic3r/BoundingBox.hpp"
#include <sstream>
// Experimentaly suggested ration of font ascent by multiple fonts
// to get approx center of normal text line
@@ -28,6 +28,7 @@
#include "FuzzySkin.hpp"
#include "libnoise/noise.h"
#include <functional>
// #define DEBUG_FUZZY
+8
View File
@@ -301,6 +301,9 @@ struct SurfaceFillParams
float skin_infill_depth = 0;
bool symmetric_infill_y_axis = false;
// Top fill for 3D honeycomb
bool infill_complete_top = false;
// Params for Lateral honeycomb
float infill_overhang_angle = 60.f;
@@ -344,6 +347,7 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(lateral_lattice_angle_1);
RETURN_COMPARE_NON_EQUAL(lateral_lattice_angle_2);
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
RETURN_COMPARE_NON_EQUAL(infill_complete_top);
RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
RETURN_COMPARE_NON_EQUAL(skin_infill_depth);
RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
@@ -931,6 +935,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
} else if (params.pattern == ipZigZag) {
params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
} else if (params.pattern == ip3DHoneycomb) {
params.infill_complete_top = region_config.infill_complete_top;
}
if (surface.is_solid()) {
@@ -1430,6 +1436,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
} else if (surface_fill.params.pattern == ipZigZag) {
params.symmetric_infill_y_axis = surface_fill.params.symmetric_infill_y_axis;
} else if (surface_fill.params.pattern == ip3DHoneycomb) {
params.infill_complete_top = surface_fill.params.infill_complete_top;
}
if (surface_fill.params.pattern == ipGrid)
params.can_reverse = false;
+243 -127
View File
@@ -61,7 +61,7 @@ static coordf_t troctWave(coordf_t pos, coordf_t gridSize, coordf_t Zpos)
// Identify the important points of curve change within a truncated
// octahedron wave (as waveform fraction t):
// 1. Start of wave (always 0.0)
// 1. Start of wave (always 0.0; not needed if the pattern base starts here)
// 2. Transition to upper "horizontal" part
// 3. Transition from upper "horizontal" part
// 4. Transition to lower "horizontal" part
@@ -73,17 +73,11 @@ static coordf_t troctWave(coordf_t pos, coordf_t gridSize, coordf_t Zpos)
* \ /
* o---o
*/
static std::vector<coordf_t> getCriticalPoints(coordf_t Zpos, coordf_t gridSize)
static std::vector<coordf_t> getCriticalPoints(coordf_t Zpos, coordf_t gridSize)
{
std::vector<coordf_t> res = {0.};
std::vector<coordf_t> res;
coordf_t perpOffset = abs(triWave(Zpos, gridSize) / 2.);
coordf_t normalisedOffset = perpOffset / gridSize;
// // for debugging: just generate evenly-distributed points
// for(coordf_t i = 0; i < 2; i += 0.05){
// res.push_back(gridSize * i);
// }
// note: 0 == straight line
if(normalisedOffset > 0){
res.push_back(gridSize * (0. + normalisedOffset));
res.push_back(gridSize * (1. - normalisedOffset));
@@ -93,113 +87,251 @@ static std::vector<coordf_t> getCriticalPoints(coordf_t Zpos, coordf_t gridSize)
return(res);
}
// Generate an array of points that are in the same direction as the
// basic printing line (i.e. Y points for columns, X points for rows)
// Note: a negative offset only causes a change in the perpendicular
// direction
static std::vector<coordf_t> colinearPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
const size_t baseLocation, size_t gridLength)
// Add additional dense fill in line with the pattern direction to
// cover the top squares of the pattern
static Polylines addTops(coordf_t Zpos, coordf_t gridSize, coordf_t lengthX, coordf_t lengthY, coordf_t spacing,
size_t multiline_count, size_t topDistance)
{
std::vector<coordf_t> points;
points.push_back(baseLocation);
for (coordf_t cLoc = baseLocation; cLoc < gridLength; cLoc+= (gridSize*2)) {
for(size_t pi = 0; pi < critPoints.size(); pi++){
points.push_back(baseLocation + cLoc + critPoints[pi]);
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
coordf_t zHalfCycle = fmod(zCycle, 0.5) * 2.;
bool printVert = zCycle < 0.5;
coordf_t offsetX = multiline_count;
coordf_t offsetY = multiline_count;
coordf_t perpOffset = abs(triWave(Zpos, gridSize) / 2.);
coordf_t gridPoint = gridSize * (0. + perpOffset / gridSize);
coordf_t topOffset = gridSize / 2.0 - abs(troctWave(gridPoint, gridSize, Zpos));
coordf_t multilineAdjust = (sqrt(2) - 1.0) / 2.;
Polylines lines;
size_t pointCount = 0;
coordf_t gridStartL = gridSize * 0.5 - topOffset;
coordf_t gridEndL = gridSize * 0.5 + topOffset;
if((topDistance == 0) && (multiline_count == 1)){
// extend out a little bit on the first layer to help fuse the cover
gridStartL -= spacing;
gridEndL += spacing;
} else if(multiline_count > 1) {
// match start point to the corner edge
gridStartL -= spacing * multiline_count * multilineAdjust;
gridEndL += spacing * multiline_count * multilineAdjust;
}
// top cover extents perpendicular to the direction of travel
coordf_t gridStartP = gridSize * 0.5 - topOffset + spacing * multiline_count / 2. + spacing / 2.;
coordf_t gridEndP = gridSize * 0.5 + topOffset - spacing * multiline_count / 2. - spacing / 2.;
coordf_t x, y;
int xm, ym;
// if the print direction needs to be rotated, then swap the extents
if((topDistance % 2) == 0){
std::swap(gridStartL, gridStartP);
std::swap(gridEndL, gridEndP);
}
// adjust spacing so that it starts and ends on exactly the right place
// and increase fill density slightly to reduce gaps
coordf_t region_count = floor((gridEndP - gridStartP) / (spacing / sqrt(2)));
if(region_count <= 0){
return lines;
}
spacing = (gridEndP - gridStartP) / region_count;
for (x = offsetX, xm = 0; x <= (lengthX); x+= gridSize, xm = xm ^ 1) {
for (y = offsetY, ym = 0; y <= (lengthY); y += gridSize, ym = ym ^ 1) {
if(((xm ^ ym) == 1) == printVert){
continue;
}
// // For debugging: remove 0,0 -> 1,1 top to help understand orientation
// if((x <= (gridSize + EPSILON)) && (y <= (gridSize + EPSILON)) && ((y - x) < EPSILON)){
// continue;
// }
Polyline newPoints;
int dirMod = xm ^ ym;
if(printVert == (topDistance % 2)){
if(y < (lengthY - spacing * multiline_count * 1.5)){
coordf_t endPMod = std::min(lengthX - (multiline_count * (spacing + 1) / 2.), x + gridEndP) - x;
coordf_t endLMod = std::min(lengthY - (multiline_count * (spacing + 1) / 2.), y + gridEndL) - y;
for(coordf_t xi = gridStartP; xi < (endPMod + EPSILON); xi += spacing, dirMod = dirMod ^ 1){
newPoints.points.push_back((dirMod == 0) ? Point(x + xi, y + gridStartL) : Point(x + xi, y + endLMod));
newPoints.points.push_back((dirMod == 0) ? Point(x + xi, y + endLMod) : Point(x + xi, y + gridStartL));
pointCount += 2;
}
}
} else {
if(x < (lengthX - spacing * multiline_count * 1.5)){
coordf_t endPMod = std::min(lengthY - (multiline_count * (spacing + 1) / 2.), y + gridEndP) - y;
coordf_t endLMod = std::min(lengthX - (multiline_count * (spacing + 1) / 2.), x + gridEndL) - x;
for(coordf_t yi = gridStartP; yi < (endPMod + EPSILON); yi += spacing, dirMod = dirMod ^ 1){
newPoints.points.push_back((dirMod == 0) ? Point(x + gridStartL, y + yi) : Point(x + endLMod, y + yi));
newPoints.points.push_back((dirMod == 0) ? Point(x + endLMod, y + yi) : Point(x + gridStartL, y + yi));
pointCount += 2;
}
}
}
lines.push_back(newPoints);
}
}
points.push_back(gridLength);
return points;
return lines;
}
// Generate an array of points for the dimension that is perpendicular to
// the basic printing line (i.e. X points for columns, Y points for rows)
static std::vector<coordf_t> perpendPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
size_t baseLocation, size_t gridLength,
size_t offsetBase, coordf_t perpDir)
// Generate a set of polylines that complete octahedron curves on the
// extremities of a pattern
static Polylines makeEndPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
coordf_t lengthX, coordf_t lengthY, coordf_t spacing, size_t multiline_count)
{
std::vector<coordf_t> points;
points.push_back(offsetBase);
for (coordf_t cLoc = baseLocation; cLoc < gridLength; cLoc+= gridSize*2) {
for(size_t pi = 0; pi < critPoints.size(); pi++){
Polylines lines;
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
bool printVert = zCycle < 0.5;
bool printHoriz = zCycle >= 0.5;
int zFlipOffset = ((sgn(fmod(zCycle, 0.5) - 0.25) > 0) == printVert) ? 0 : 1;
// create templates for copying
Polylines startLines, endLines;
for(size_t li = 0; li < multiline_count; li++){
coordf_t oAdj = (li - ((multiline_count - 1) / 2.)) * spacing; // orthogonal line adjustment
coordf_t dAdj = oAdj * sqrt(2); // diagonal line adjustment
Polyline startLine, endLine;
// Left Bottom; Bottom Left
startLine.points.push_back(printHoriz ? Point(oAdj, -dAdj) : Point(-dAdj, oAdj));
// Right Bottom; Top Left
endLine.points.push_back(printHoriz ? Point(-oAdj, -dAdj) : Point(-dAdj, -oAdj));
for(size_t pi = 0; pi < 2; pi++){
int pDir = pi * 2 - 1;
coordf_t pAdj = pDir * (sqrt(2) - 1) * oAdj;
coordf_t troctOffset = abs(troctWave(critPoints[pi], gridSize, Zpos));
startLine.points.push_back(printHoriz ?
Point(-troctOffset, critPoints[pi] + pAdj) :
Point(critPoints[pi] + pAdj, -troctOffset));
endLine.points.push_back(printHoriz ?
Point(troctOffset, critPoints[pi] + pAdj) :
Point(critPoints[pi] + pAdj, troctOffset));
}
// Left Top; Bottom Right
startLine.points.push_back(printHoriz ? Point(oAdj, gridSize + dAdj) : Point(gridSize + dAdj, oAdj));
// Right Top; Top Right
endLine.points.push_back(printHoriz ? Point(-oAdj, gridSize + dAdj) : Point(gridSize + dAdj, -oAdj));
startLines.push_back(startLine);
endLines.push_back(endLine);
}
coordf_t gridMaxX = ceil((lengthX - EPSILON) / gridSize) * gridSize;
coordf_t gridMaxY = ceil((lengthY - EPSILON) / gridSize) * gridSize;
for(size_t li = 0; li < multiline_count; li++){
coordf_t mlFactor = (li - ((multiline_count - 1) / 2.)) * spacing;
for (coordf_t cLoc = zFlipOffset * gridSize; cLoc < ((printHoriz ? gridMaxY : gridMaxX) - EPSILON); cLoc += gridSize * 2) {
Polyline tsLine(startLines[li]);
Polyline teLine(endLines[li]);
tsLine.translate(printVert ? Point(cLoc, -mlFactor) : Point(-mlFactor, cLoc));
teLine.translate(printVert ? Point(cLoc, gridMaxY + mlFactor) : Point(gridMaxX + mlFactor, cLoc));
lines.push_back(tsLine);
lines.push_back(teLine);
}
}
return lines;
}
// Generate a polyline that describes a single path segment through
// the infill in the same direction as the basic printing line (i.e. X
// points for columns, Y points for rows)
static Polyline patternPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
coordf_t gridLength, coordf_t perpDir, int print_dir, coordf_t oAdj)
{
Polyline line;
coordf_t dAdj = oAdj * (sqrt(2) - 1); // additional diagonal adjustment
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
int zFlipDirection = sgn(fmod(zCycle, 0.5) - 0.25);
bool hitEnd = false;
int endPi = -1;
size_t pi = 0;
size_t piOfs = 0;
line.points.push_back((print_dir == 1) ? Point(dAdj, 0.) : Point(0., dAdj));
coordf_t gridMax = ceil((gridLength - EPSILON) / gridSize) * gridSize;
for (coordf_t cLoc = 0; cLoc < gridMax; cLoc += gridSize, piOfs = (piOfs + 2) % 4) {
for(pi = piOfs; pi < (piOfs + 2); pi++){
coordf_t offset = troctWave(critPoints[pi], gridSize, Zpos);
points.push_back(offsetBase + (offset * perpDir));
coordf_t offsetFlip = sgn(offset);
coordf_t posFlip = floor(((pi + 1) % 4) / 2) * 2 - 1;
coordf_t posLin = cLoc - (piOfs * gridSize / 2.) + critPoints[pi];
coordf_t posPerp = offset * perpDir;
line.points.push_back((print_dir == 1) ?
Point(posPerp, posLin + posFlip * dAdj * perpDir * zFlipDirection * print_dir) :
Point(posLin + posFlip * dAdj * perpDir * zFlipDirection * print_dir, posPerp));
}
}
points.push_back(offsetBase);
return points;
}
static inline Pointfs zip(const std::vector<coordf_t> &x, const std::vector<coordf_t> &y)
{
assert(x.size() == y.size());
Pointfs out;
out.reserve(x.size());
for (size_t i = 0; i < x.size(); ++ i)
out.push_back(Vec2d(x[i], y[i]));
return out;
line.points.push_back((print_dir == 1) ? Point(dAdj, gridMax) : Point(gridMax, dAdj));
return line;
}
// Generate a set of curves (array of array of 2d points) that describe a
// horizontal slice of a truncated regular octahedron.
static std::vector<Pointfs> makeActualGrid(coordf_t Zpos, coordf_t gridSize, size_t boundsX, size_t boundsY)
static Polylines makeZigZag(coordf_t Zpos, coordf_t gridSize, coordf_t lengthX, coordf_t lengthY,
coordf_t spacing, size_t multiline_count)
{
std::vector<Pointfs> points;
Polylines lines;
std::vector<coordf_t> critPoints = getCriticalPoints(Zpos, gridSize);
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
bool printVert = zCycle < 0.5;
if (printVert) {
int perpDir = -1;
for (coordf_t x = 0; x <= (boundsX); x+= gridSize, perpDir *= -1) {
points.push_back(Pointfs());
Pointfs &newPoints = points.back();
newPoints = zip(
perpendPoints(Zpos, gridSize, critPoints, 0, boundsY, x, perpDir),
colinearPoints(Zpos, gridSize, critPoints, 0, boundsY));
BoundingBox extents;
int perpDir = -1;
int perpDirPattern = -1;
coordf_t gridMax = ceil(((printVert ? lengthX : lengthY) - EPSILON) / gridSize) * gridSize;
for (coordf_t pPos = 0; pPos < gridMax; pPos += gridSize, perpDirPattern *= -1) {
for (size_t li = 0; li < multiline_count; li++){
coordf_t oAdj = (li - ((multiline_count - 1) / 2.)) * spacing; // orthogonal line adjustment
Polyline newPoints;
newPoints = patternPoints(Zpos, gridSize, critPoints,
printVert ? lengthY : lengthX,
perpDirPattern, printVert ? 1 : -1, oAdj);
if (perpDir == 1)
std::reverse(newPoints.begin(), newPoints.end());
}
} else {
int perpDir = 1;
for (coordf_t y = gridSize; y <= (boundsY); y+= gridSize, perpDir *= -1) {
points.push_back(Pointfs());
Pointfs &newPoints = points.back();
newPoints = zip(
colinearPoints(Zpos, gridSize, critPoints, 0, boundsX),
perpendPoints(Zpos, gridSize, critPoints, 0, boundsX, y, perpDir));
if (perpDir == -1)
std::reverse(newPoints.begin(), newPoints.end());
std::reverse(newPoints.points.begin(), newPoints.points.end());
newPoints.translate(printVert ? Point(pPos + oAdj, 0.) : Point(0., pPos + oAdj));
extents.merge(newPoints.points);
lines.push_back(newPoints);
perpDir *= -1;
}
}
return points;
return lines;
}
// Generate a set of curves (array of array of 2d points) that describe a
// horizontal slice of a truncated regular octahedron with a specified
// grid square size.
// gridWidth and gridHeight define the width and height of the bounding box respectively
static Polylines makeGrid(coordf_t z, coordf_t gridSize, coordf_t boundWidth, coordf_t boundHeight, bool fillEvenly)
// Note: this uses the 'complete' infill parameter to determine if the
// square tops should be enclosed (true) or open (false). Alternatively,
// a rotation angle of 180 degrees or greater can be used.
static Polylines makeGrid(coordf_t z, coordf_t zLast, coordf_t gridSize,
coordf_t lengthX, coordf_t lengthY,
bool completeTops, coordf_t spacing, size_t multiline_count, size_t layer_count)
{
std::vector<Pointfs> polylines = makeActualGrid(z, gridSize, boundWidth, boundHeight);
coordf_t zCycle = fmod(z + gridSize/2, gridSize * 2.) / (gridSize * 2.);
bool printVert = zCycle < 0.5;
coordf_t zCycleLast = fmod(zLast + gridSize/2, gridSize * 2.) / (gridSize * 2.);
bool printVertLast = zCycleLast < 0.5;
Polylines result;
result.reserve(polylines.size());
for (std::vector<Pointfs>::const_iterator it_polylines = polylines.begin();
it_polylines != polylines.end(); ++ it_polylines) {
result.push_back(Polyline());
Polyline &polyline = result.back();
for (Pointfs::const_iterator it = it_polylines->begin(); it != it_polylines->end(); ++ it)
polyline.points.push_back(Point(coord_t((*it)(0)), coord_t((*it)(1))));
Polylines polyZag = makeZigZag(z, gridSize, lengthX, lengthY, spacing, multiline_count);
result.insert(result.end(), polyZag.begin(), polyZag.end());
// add end connectors
std::vector<coordf_t> critPoints = getCriticalPoints(z, gridSize);
Polylines endPoints = makeEndPoints(z, gridSize, critPoints, lengthX, lengthY, spacing, multiline_count);
result.insert(result.end(), endPoints.begin(), endPoints.end());
// add tops for the first <multiline_count> layers in each cycle
if(completeTops && (printVert != printVertLast)){
coordf_t layerHeight = (z - zLast) / (multiline_count * layer_count);
size_t top_distance = 0;
for(coordf_t zCheck = z; zCheck >= (zLast + EPSILON); zCheck -= layerHeight * layer_count, top_distance++){
coordf_t zCheckCycle = fmod(zCheck + gridSize/2, gridSize * 2.) / (gridSize * 2.);
if(printVert != (zCheckCycle < 0.5)){
break;
}
}
Polylines polytops = addTops(z, gridSize, lengthX, lengthY, spacing, multiline_count, top_distance);
result.insert(result.end(), polytops.begin(), polytops.end());
}
return result;
}
// FillParams has the following useful information:
// density <0 .. 1> [proportion of space to fill]
// anchor_length [???]
// anchor_length_max [???]
// dont_connect() [avoid connect lines]
// dont_adjust [avoid filling space evenly]
// monotonic [fill strictly left to right]
// complete [complete each loop]
// multiline [number of lines to draw for each pattern line]
// complete_top [should the top surfaces of the pattern be filled]
void Fill3DHoneycomb::_fill_surface_single(
const FillParams &params,
@@ -208,78 +340,62 @@ void Fill3DHoneycomb::_fill_surface_single(
ExPolygon expolygon,
Polylines &polylines_out)
{
// no rotation is supported for this infill pattern
// Support infill angle
auto infill_angle = float(this->angle);
if (std::abs(infill_angle) >= EPSILON) expolygon.rotate(-infill_angle);
BoundingBox bb = expolygon.contour.bounding_box();
// Expand the bounding box to avoid artifacts at the edges
coord_t expand = 5 * (scale_(this->spacing));
bb.offset(expand);
// Increase the bounding box outwards to avoid edge clipping artefacts
coord_t expandSize = 5. * scale_(this->spacing);
bb.offset(expandSize);
// Adjustment for combining infill setting
size_t layersPerSlice = 1;
if(thickness_layers > 0){
layersPerSlice = thickness_layers;
}
// Note: with equally-scaled X/Y/Z, the pattern will create a vertically-stretched
// truncated octahedron; so Z is pre-adjusted first by scaling by sqrt(2)
coordf_t zScale = sqrt(2);
// adjustment to account for the additional distance of octagram curves
// note: this only strictly applies for a rectangular area where the total
// Z travel distance is a multiple of the spacing... but it should
// be at least better than the prevous estimate which assumed straight
// lines
// Density adjustment to account for the additional distance of
// octagram curves. [This only strictly applies for a rectangular
// area where the total Z travel distance is a multiple of the
// spacing]
// = 4 * integrate(func=4*x(sqrt(2) - 1) + 1, from=0, to=0.25)
// = (sqrt(2) + 1) / 2 [... I think]
// make a first guess at the preferred grid Size
coordf_t gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) * params.multiline / params.density);
// This density calculation is incorrect for many values > 25%, possibly
// due to quantisation error, so this value is used as a first guess, then the
// Z scale is adjusted to make the layer patterns consistent / symmetric
// This means that the resultant infill won't be an ideal truncated octahedron,
// but it should look better than the equivalent quantised version
//Orca: uses a fixed layer height to avoid inconsistent bridges and variable layer height artifacts.
//coordf_t layerHeight = scale_(thickness_layers);
coordf_t layerHeight = scale_(1.0);
// ceiling to an integer value of layers per Z
// (with a little nudge in case it's close to perfect)
// make a first guess at the preferred grid Size (in unscaled units)
coordf_t gridSize = (scale_(this->spacing) *
((zScale + 1.) / 2.) * params.multiline / params.density);
coordf_t layerHeight = scale_(params.layer_height);
coordf_t layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05);
if(params.density > 0.42){ // exact layer pattern for >42% density
// If a density over 42% is requested, set an exact layer pattern
if((params.density > 0.42) || (layersPerModule < 2)){
layersPerModule = 2;
// re-adjust the grid size for a partial octahedral path
// (scale of 1.1 guessed based on modeling)
gridSize = (scale_(this->spacing) * 1.1 * params.multiline / params.density);
// re-adjust zScale to make layering consistent
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
} else {
if(layersPerModule < 2){
layersPerModule = 2;
}
// re-adjust zScale to make layering consistent
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
// re-adjust the grid size to account for the new zScale
gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) * params.multiline / params.density);
// re-calculate layersPerModule and zScale
layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05);
if(layersPerModule < 2){
layersPerModule = 2;
}
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
}
// align bounding box to a multiple of our honeycomb grid module
// (a module is 2*$gridSize since one $gridSize half-module is
// growing while the other $gridSize half-module is shrinking)
bb.merge(align_to_grid(bb.min, Point(gridSize*4, gridSize*4)));
// align bounding box to a multiple of the octahedron grid so that
// layers with different starting points have matching origins
bb.merge(align_to_grid(bb.min, Point(gridSize * 2., gridSize * 2.)));
// Z adjustment to start at the widest point for the lowest layer
coordf_t startOffset = gridSize / 2. + scale_(params.layer_height / 2.);
// generate pattern
Polylines polylines =
makeGrid(
scale_(this->z) * zScale,
gridSize,
bb.size()(0),
bb.size()(1),
!params.dont_adjust);
scale_(this->z) * zScale + startOffset,
scale_(this->z - (params.layer_height * params.multiline * layersPerSlice)) * zScale + startOffset,
gridSize, bb.size()(0), bb.size()(1),
params.infill_complete_top,
scale_(this->spacing),
params.multiline,
layersPerSlice);
// move pattern in place
for (Polyline &pl : polylines){
@@ -290,8 +406,8 @@ void Fill3DHoneycomb::_fill_surface_single(
smooth_polyline_corners(pl, params.smooth_factor, scaled<double>(params.resolution));
}
// Apply multiline offset if needed
multiline_fill(polylines, params, spacing);
// Note: multiline fill adjustment is carried out in this code,
// rather than using the multiline_fill function
// clip pattern to boundaries, chain the clipped polylines
polylines = intersection_pl(std::move(polylines), to_polygons(expolygon));
+1
View File
@@ -118,6 +118,7 @@ struct FillParams
float horiz_move{0.0}; //move infill to get cross zag pattern
bool symmetric_infill_y_axis{false};
bool infill_complete_top{false};
coord_t symmetric_y_axis{0};
bool locked_zag{false};
float infill_lock_depth{0.0};
+1
View File
@@ -119,6 +119,7 @@
#endif
#include <Shiny/Shiny.h>
#include <stdio.h>
using namespace std::literals::string_view_literals;
+1
View File
@@ -12,6 +12,7 @@
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/nowide/fstream.hpp>
#include <ios>
#ifdef _WIN32
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/nowide/convert.hpp>
+3 -3
View File
@@ -378,7 +378,7 @@ void Layer::simplify_support_entity_collection(ExtrusionEntityCollection* entity
//BBS: method to simplify support path
void Layer::simplify_support_path(ExtrusionPath * path)
{
const auto print_config = this->object()->print()->config();
const auto &print_config = this->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
@@ -393,7 +393,7 @@ void Layer::simplify_support_path(ExtrusionPath * path)
//BBS: method to simplify support path
void Layer::simplify_support_multi_path(ExtrusionMultiPath* multipath)
{
const auto print_config = this->object()->print()->config();
const auto &print_config = this->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
@@ -410,7 +410,7 @@ void Layer::simplify_support_multi_path(ExtrusionMultiPath* multipath)
//BBS: method to simplify support path
void Layer::simplify_support_loop(ExtrusionLoop* loop)
{
const auto print_config = this->object()->print()->config();
const auto &print_config = this->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
+3 -3
View File
@@ -1101,7 +1101,7 @@ void LayerRegion::simplify_entity_collection(ExtrusionEntityCollection* entity_c
void LayerRegion::simplify_path(ExtrusionPath* path)
{
const auto print_config = this->layer()->object()->print()->config();
const auto &print_config = this->layer()->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
@@ -1119,7 +1119,7 @@ void LayerRegion::simplify_path(ExtrusionPath* path)
void LayerRegion::simplify_multi_path(ExtrusionMultiPath* multipath)
{
const auto print_config = this->layer()->object()->print()->config();
const auto &print_config = this->layer()->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
@@ -1139,7 +1139,7 @@ void LayerRegion::simplify_multi_path(ExtrusionMultiPath* multipath)
void LayerRegion::simplify_loop(ExtrusionLoop* loop)
{
const auto print_config = this->layer()->object()->print()->config();
const auto &print_config = this->layer()->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
+1
View File
@@ -12,6 +12,7 @@
#include <system_error>
#include <fast_float/fast_float.h>
#include <clocale>
// Defined where the floating point std::to_chars can be called, which with Apple's libc++ runtime is from macOS 13.3.
#if defined(_LIBCPP_VERSION)
+1
View File
@@ -1209,6 +1209,7 @@ static std::vector<std::string> s_Preset_print_options{
"infill_lock_depth",
"skin_infill_depth",
"skin_infill_density",
"infill_complete_top",
"align_infill_direction_to_model",
"extra_solid_infills",
"center_of_surface_pattern",
+13 -6
View File
@@ -4574,6 +4574,13 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("infill_complete_top", coBool);
def->label = L("Fill pattern tops");
def->category = L("Strength");
def->tooltip = L("Choose this option if you want to completely fill in the tops of the infill pattern");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
// Orca: max layer height for combined infill
def = this->add("infill_combination_max_layer_height", coFloatOrPercent);
def->label = L("Infill combination - Max layer height");
@@ -5301,7 +5308,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionEnum<InputShaperType>(InputShaperType::Default));
def = this->add("input_shaping_freq_x", coFloat);
def->label = L("X");
def->label = L_CONTEXT("X", "Axis");
def->tooltip = L("Resonant frequency for the X axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal.");
def->sidetext = L("Hz"); // Hertz, CIS languages need translation
def->min = 0;
@@ -5310,7 +5317,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("input_shaping_freq_y", coFloat);
def->label = L("Y");
def->label = L_CONTEXT("Y", "Axis");
def->tooltip = L("Resonant frequency for the Y axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type.");
def->sidetext = L("Hz"); // Hertz, CIS languages need translation
def->min = 0;
@@ -5319,7 +5326,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("input_shaping_damp_x", coFloat);
def->label = L("X");
def->label = L_CONTEXT("X", "Axis");
def->tooltip = L("Damping ratio for the X axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal.");
def->min = 0;
def->max = 1;
@@ -5327,7 +5334,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloat(0.1));
def = this->add("input_shaping_damp_y", coFloat);
def->label = L("Y");
def->label = L_CONTEXT("Y", "Axis");
def->tooltip = L("Damping ratio for the Y axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type.");
def->min = 0;
def->max = 1;
@@ -8530,14 +8537,14 @@ void PrintConfigDef::init_sla_params()
def = this->add("display_pixels_x", coInt);
//def->full_label = L("");
def->label = ("X");
def->label = L_CONTEXT("X", "Axis");
//def->tooltip = L("");
def->min = 100;
def->set_default_value(new ConfigOptionInt(2560));
def = this->add("display_pixels_y", coInt);
//def->full_label = L("");
def->label = ("Y");
def->label = L_CONTEXT("Y", "Axis");
//def->tooltip = L("");
def->min = 100;
def->set_default_value(new ConfigOptionInt(1440));
+1
View File
@@ -1358,6 +1358,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, bottom_layer_direction))
((ConfigOptionString, solid_infill_rotate_template))
((ConfigOptionBool, symmetric_infill_y_axis))
((ConfigOptionBool, infill_complete_top))
((ConfigOptionFloat, infill_shift_step))
((ConfigOptionString, sparse_infill_rotate_template))
((ConfigOptionPercent, sparse_infill_density))
+7 -7
View File
@@ -3450,14 +3450,14 @@ void PrintObject::bridge_over_infill()
const bool turning_pattern = region_config.sparse_infill_pattern == ipHilbertCurve ||
region_config.sparse_infill_pattern == ipOctagramSpiral;
const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing());
area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area);
area_to_be_bridge.erase(std::remove_if(area_to_be_bridge.begin(), area_to_be_bridge.end(),
[internal_unsupported_area](const Polygon &p) {
return intersection({p}, internal_unsupported_area).empty();
ExPolygons bridge_components = intersection_ex(expand(candidate.new_polys, flow.scaled_spacing()), deep_infill_area);
// Orca: Filter whole bridge areas so their holes remain holes.
bridge_components.erase(std::remove_if(bridge_components.begin(), bridge_components.end(),
[&internal_unsupported_area](const ExPolygon &component) {
return intersection_ex(component, internal_unsupported_area).empty();
}),
area_to_be_bridge.end());
bridge_components.end());
Polygons area_to_be_bridge = to_polygons(std::move(bridge_components));
Polygons limiting_area = union_(area_to_be_bridge, expansion_area);
+2 -2
View File
@@ -1944,7 +1944,7 @@ Polygons TreeSupport::get_trim_support_regions(
static const double no_overlap_xy_gap = 0.2f;
double gap_xy_scaled = scale_(gap_xy);
SupportLayer& support_layer = *support_layer_ptr;
auto m_print_config = object.print()->config();
const PrintConfig& print_config = object.print()->config();
size_t idx_object_layer_overlapping = size_t(-1);
@@ -1991,7 +1991,7 @@ Polygons TreeSupport::get_trim_support_regions(
const Layer& object_layer = *object.layers()[i];
bool some_region_overlaps = false;
for (LayerRegion* region : object_layer.regions()) {
coordf_t bridging_height = region->region().bridging_height_avg(m_print_config);
coordf_t bridging_height = region->region().bridging_height_avg(print_config);
if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
break;
some_region_overlaps = true;
+1
View File
@@ -22,6 +22,7 @@
#include <tbb/task_arena.h>
#include "Thread.hpp"
#include <cstring>
namespace Slic3r {
+1
View File
@@ -119,6 +119,7 @@
// We are using quite an old TBB 2017 U7, which does not support global control API officially.
// Before we update our build servers, let's use the old API, which is deprecated in up to date TBB.
#include <tbb/tbb.h>
#include <string.h>
namespace boost::posix_time { class ptime; }
#if ! defined(TBB_VERSION_MAJOR)
+3 -3
View File
@@ -1228,11 +1228,11 @@ void AMSDryCtrWin::update_normal_description(DevAms* dev_ams)
for (const auto& lim : ams_limits) {
if (dev_ams->GetAmsType() == lim.type) {
if (temp_val > lim.max_temp) {
wxString msg = wxString::Format(_L("%s maximum drying temperature is %d°C."), wxString(lim.name), lim.max_temp);
wxString msg = wxString::Format(_L("%s maximum drying temperature is %d\u2103." /* °C */), wxString(lim.name), lim.max_temp);
warning_text += msg + "\n";
can_enable_button = false;
} else if (temp_val < lim.min_temp) {
wxString msg = wxString::Format(_L("%s minimum drying temperature is %d°C."), wxString(lim.name), lim.min_temp);
wxString msg = wxString::Format(_L("%s minimum drying temperature is %d\u2103." /* °C */), wxString(lim.name), lim.min_temp);
warning_text += msg + "\n";
can_enable_button = false;
}
@@ -1260,7 +1260,7 @@ void AMSDryCtrWin::update_normal_description(DevAms* dev_ams)
auto limit_temperature = preset.value().filament_dev_ams_drying_heat_distortion_temperature;
if (temp_val > limit_temperature) {
warning_text += _L("The temperature shall not exceed the filament's heat distortion temperature") + "(" +
wxString::Format(wxT("%d"), static_cast<int>(limit_temperature)) + wxString::FromUTF8("°C)\n");
wxString::Format(wxT("%d"), static_cast<int>(limit_temperature)) + wxString::FromUTF8(u8"\u2103" /* °C */) + ")\n";
can_enable_button = false;
}
}
+3 -2
View File
@@ -264,7 +264,8 @@ AboutDialog::AboutDialog()
vesizer->Add(0, 0, 1, wxEXPAND, FromDIP(5));
auto version_string = std::string(SoftFever_VERSION); // _L("Orca Slicer ") + " " + std::string(SoftFever_VERSION);
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
wxStaticText* credits_string = new wxStaticText(this, wxID_ANY, wxString::Format("Build %s", build_commit_label), wxDefaultPosition, wxDefaultSize);
// TRN %s is the commit the application was built from
wxStaticText* credits_string = new wxStaticText(this, wxID_ANY, wxString::Format(_L("Build %s"), build_commit_label), wxDefaultPosition, wxDefaultSize);
credits_string->SetFont(_build_string_font);
wxFont version_font = GetFont();
version_font = version_font.Scaled(1.85f); // SetPointSize(20) not works on macOS because it uses a 72 PPI reference
@@ -330,7 +331,7 @@ AboutDialog::AboutDialog()
copyright_hor_sizer->Add(copyright_ver_sizer, 0, wxLEFT, FromDIP(20));
wxStaticText *html_text = new wxStaticText(this, wxID_ANY, "Copyright(C) 2026 OrcaSlicer Pte Ltd All Rights Reserved", wxDefaultPosition, wxDefaultSize);
wxStaticText *html_text = new wxStaticText(this, wxID_ANY, _L("Copyright(C) 2026 OrcaSlicer Pte Ltd All Rights Reserved"), wxDefaultPosition, wxDefaultSize);
html_text->SetForegroundColour(wxColour(107, 107, 107));
copyright_ver_sizer->Add(html_text, 0, wxALL , 0);
+7 -6
View File
@@ -329,7 +329,8 @@ void AmsMapingPopup::update_mapping_items(MachineObject* obj, const std::vector<
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
const auto& shown_name = td_opt->ams_id == VIRTUAL_TRAY_MAIN_ID ? "Ext-R" : "Ext-L";
// TRN Short labels of the external spool of the right and left nozzle
const auto& shown_name = td_opt->ams_id == VIRTUAL_TRAY_MAIN_ID ? _L("Ext-R") : _L("Ext-L");
auto ams_mapping_item_container = new MappingContainer(m_right_marea_panel, shown_name, 1);
ams_mapping_item_container->SetName(m_right_marea_panel->GetName());
ams_mapping_item_container->SetSizer(sizer_mapping_list);
@@ -506,7 +507,7 @@ void AmsMapingPopup::update_ams_tips(MachineObject* obj)
}
if (obj && obj->GetFilaSwitch()->IsInstalled()) {
const auto& msg = _L("External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
const auto& msg = _L("External spools are not supported since Filament Track Switch has been installed. If you want to use an external spool, please uninstall it.");
m_ams_tips_msg_panel->AddMessage(msg, "#FF6F00", "");
}
@@ -588,7 +589,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
// slots Ext-R / Ext-L inline in the two-nozzle left+right views (matches that overload's result).
if ((m_show_type == ShowType::LEFT_AND_RIGHT || m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC)
&& (tray_data[i].id == VIRTUAL_TRAY_MAIN_ID || tray_data[i].id == VIRTUAL_TRAY_DEPUTY_ID)) {
m_mapping_item->set_tray_index(tray_data[i].id == VIRTUAL_TRAY_MAIN_ID ? wxString("Ext-R") : wxString("Ext-L"));
m_mapping_item->set_tray_index(tray_data[i].id == VIRTUAL_TRAY_MAIN_ID ? _L("Ext-R") : _L("Ext-L"));
} else {
m_mapping_item->set_tray_index(wxGetApp().transition_tridid(tray_data[i].id));
}
@@ -607,7 +608,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
if (!can_pick_the_item) {
item_tooltip_msg = _L(
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
"External spools are not supported since Filament Track Switch has been installed. If you want to use an external spool, please uninstall it.");
}
}
}
@@ -624,7 +625,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
if (!can_pick_the_item) {
item_tooltip_msg = _L(
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
"External spools are not supported since Filament Track Switch has been installed. If you want to use an external spool, please uninstall it.");
}
} else if (m_show_type != ShowType::RIGHT && m_show_type != ShowType::LEFT_AND_RIGHT) {
can_pick_the_item = false;
@@ -718,7 +719,7 @@ void AmsMapingPopup::add_ext_ams_mapping(TrayData tray_data, MappingItem* item)
});
}
item->set_tray_index("Ext");
item->set_tray_index(_L("Ext"));
}
} // namespace Slic3r::GUI
+3
View File
@@ -65,6 +65,8 @@
#include "Widgets/Label.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Plater.hpp"
#include <wx/dcgraph.h>
#include <wx/dcmemory.h>
namespace fs = boost::filesystem;
@@ -1152,6 +1154,7 @@ void AuxiliaryPanel::update_all_cover()
m_sizer_description->Add(m_text_description, 0, wxALIGN_TOP | wxRIGHT, FromDIP(10));
m_input_description = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxSize(FromDIP(450), FromDIP(300)), wxTE_MULTILINE | wxTE_PROCESS_ENTER);
m_input_description->SetBackgroundColour(*wxWHITE);
m_input_description->SetFont(::Label::Body_14);
m_sizer_description->Add(m_input_description, 0, wxALIGN_CENTER, 0);
+1 -1
View File
@@ -20,7 +20,7 @@ namespace GUI {
AuxiliaryDialog::AuxiliaryDialog(wxWindow * parent)
: DPIDialog(parent, wxID_ANY, _L("Auxiliaryies"), wxDefaultPosition,
: DPIDialog(parent, wxID_ANY, _L("Auxiliaries"), wxDefaultPosition,
wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
m_aux_list = new AuxiliaryList(this);
@@ -60,6 +60,9 @@
#include "libslic3r/PresetBundle.hpp"
#include "slic3r/GUI/PartPlate.hpp"
#include "slic3r/Utils/PrintHost.hpp"
#ifdef _WIN32
#include <excpt.h>
#endif
namespace Slic3r {
+96 -93
View File
@@ -13,6 +13,9 @@
#ifndef L
#define L(s) s // gettext marker, as in slic3r/GUI/I18N.hpp
#endif
#ifndef L_CONTEXT
#define L_CONTEXT(s, context) s
#endif
namespace Slic3r { namespace GUI {
@@ -52,7 +55,7 @@ inline uint32_t offer_bit(OfferSel s) { return 1u << int(s); }
// nullptr -> kernel support exists, no GUI path yet (row shows disabled)
struct OfferVerb {
const char* id;
const char* name; // drawing-office word (L10); marked L(); translated at use
const char* name; // drawing-office word (L10); msgctxt "Design", translated at use
int row; // index into kOfferRowNames, the ratified address — NEVER reorder
const char* key; // shortcut shown in the row, or nullptr
const char* action;
@@ -99,107 +102,107 @@ static const bool kOfferRowFlat[] = {
};
static const OfferVerb kOfferVerbs[] = {
{"sketch", L("Sketch"), 0, "Shift+S", "key:S+S", L("Click a face or a reference plane in the viewport, then a sketch tool"), 0x00000403u, 0, 0, false, false, nullptr, "design_sketch", L("Click a face or a reference plane, then pick a drawing tool")},
{"extrude", L("Extrude"), 1, "Shift+E", "key:S+E", L("Create a sketch, or pick a solid face, first"), 0x00004002u, 0, 0, false, false, nullptr, "design_extrude", L("Extrude a sketch profile, or push/pull a picked face")},
{"revolve", L("Revolve"), 1, "Shift+R", "key:S+R", L("Create a sketch profile to revolve first"), 0x00004000u, 0, 0, false, false, nullptr, "design_revolve", L("Revolve a profile about an axis")},
{"sweep", L("Sweep"), 1, "Shift+W", "key:S+W", L("Create a profile sketch to sweep first"), 0x00004000u, 0, 2, false, false, nullptr, "design_sweep", L("Sweep a profile along a path")},
{"loft", L("Loft"), 1, "Shift+L", "key:S+L", L("Create at least two profile sketches to loft"), 0x00004000u, 0, 2, false, false, nullptr, "design_loft", L("Loft (skin) between two or more profiles")},
{"thicken", L("Thicken"), 1, nullptr, "fly:material#4", L("Thicken needs a solid body — add or import one first"), 0x0000000au, 1, 0, false, false, nullptr, "design_thicken", L("Offset a solid face into a thin plate (new body)")},
{"rib", L("Rib"), 1, "R", "fly:material#5", L("Rib needs a solid body — add or import one first"), 0x00010000u, 1, 0, false, false, nullptr, "design_rib", L("Grow a thin wall from an open sketch line, fused to a body")},
{"boolean", L("Join"), 1, "Shift+B", "btn:bool#0", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Fuse the tool body into the target — one solid, no seam")},
{"bool_subtract", L("Subtract"), 1, nullptr, "btn:bool#1", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Cut the tool body out of the target")},
{"bool_intersect", L("Intersect"), 1, nullptr, "btn:bool#2", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Keep only where the two bodies overlap")},
{"surf_extrude", L("Surface Extrude"), 1, "Shift+G", "key:S+G", L("Create a sketch first"), 0x00004000u, 0, 0, false, false, nullptr, "design_extrude", L("Extrude a sketch into a sheet body (no end caps)")},
{"surf_revolve", L("Surface Revolve"), 1, nullptr, "fly:surface#1", L("Create a sketch profile to revolve first"), 0x00004000u, 0, 0, false, false, nullptr, "design_revolve", L("Revolve a sketch profile into a sheet body")},
{"surf_loft", L("Surface Loft"), 1, nullptr, "fly:surface#2", L("Create at least two profile sketches to loft"), 0x00004000u, 0, 2, false, false, nullptr, "design_loft", L("Loft (skin) between 2+ profiles, open (no end caps)")},
{"surf_fill", L("Surface Fill"), 1, nullptr, "fly:surface#3", L("Create a closed sketch first"), 0x00004000u, 0, 0, false, false, nullptr, "design_surface", L("Fill a sketch boundary with a smooth face")},
{"thicken_surf", L("Thicken Surface"), 1, nullptr, "fly:surface#5", L("Thicken Surface needs a surface body — make one with a Surface tool first"), 0x00000100u, 0, 0, true, false, nullptr, "design_thicken", L("Thicken a sheet body into a solid")},
{"hole", L("Hole"), 2, "Shift+H", "key:S+H", L("Pick a face or a plane to drill into"), 0x00000402u, 1, 0, false, false, nullptr, "design_hole", L("Drill a hole, centerd on a picked face or placed on a plane")},
{"thread", L("Thread"), 2, "Shift+T", "key:S+T", L("Pick a cylindrical surface (bore / outer) or a circular edge for a thread"), 0x00000024u, 1, 0, false, false, nullptr, "design_thread", L("Thread a cylindrical surface (inner bore / outer) or a circular edge")},
{"shell", L("Shell"), 2, "Shift+K", "key:S+K", L("Shell needs a solid body — add or import one first"), 0x00000082u, 1, 0, false, false, nullptr, "design_shell", L("Hollow the body to a wall thickness, opening a picked face")},
{"cut", L("Cut"), 2, "Shift+X", "key:S+X", L("Cut needs a solid body — add or import one first"), 0x000004feu, 1, 0, false, false, nullptr, "design_cut", L("Trim the body with a plane — drag the offset arrow; keep one half or both")},
{"split", L("Split"), 2, nullptr, nullptr, L("Split needs a solid body — add or import one first"), 0x000000feu, 1, 0, false, false, nullptr, nullptr, L("Split the body along a picked face into two solids")},
{"fillet", L("Fillet"), 3, "Shift+F", "btn:dress#0", L("Pick an edge to round"), 0x000000b2u, 1, 0, false, false, nullptr, "design_filletedge", L("Pick an edge, then drag the radius arrow or type it")},
{"chamfer", L("Chamfer"), 3, nullptr, "btn:dress#1", L("Pick an edge to bevel"), 0x000000b2u, 1, 0, false, false, nullptr, "design_chamfer", L("Pick an edge, then drag the distance arrow or type it")},
{"draft", L("Draft"), 3, "Shift+D", "key:S+D", L("Pick a face to taper"), 0x0000000au, 1, 0, false, false, nullptr, "design_draft", L("Tilt a picked face by a draft angle")},
{"surf_offset", L("Surface Offset"), 3, nullptr, "fly:surface#4", L("Surface Offset needs a surface body — make one with a Surface tool first"), 0x00000100u, 0, 0, true, false, nullptr, "design_offset", L("Offset a sheet body's shell by a signed distance")},
{"pattern", L("Linear pattern"), 4, "Shift+N", "btn:pat#0", L("Pattern needs a solid body — add or import one first"), 0x00006082u, 1, 0, false, false, nullptr, "design_array", L("Repeat the body along a direction — drag the spacing, set the count")},
{"pattern_circular", L("Circular pattern"), 4, nullptr, "btn:pat#1", L("Pattern needs a solid body — add or import one first"), 0x00006082u, 1, 0, false, false, nullptr, "design_polararray", L("Repeat the body around an axis — set the count and sweep")},
{"mirror", L("Mirror"), 4, "Shift+Z", "key:S+Z", L("Mirror needs a body — add or import one first"), 0x000004feu, 1, 0, false, false, nullptr, "design_mirror", L("Reflect a body about a plane")},
{"pat_curve", L("Pattern on Curve"), 4, nullptr, nullptr, L("Pattern on Curve needs a body and a curve"), 0x00000090u, 1, 0, false, false, nullptr, nullptr, L("Repeat the body along a picked curve")},
{"transform", L("Move"), 5, "Shift+Y", "key:S+Y", L("Transform needs a body — add or import one first"), 0x000021feu, 1, 0, false, false, nullptr, "design_move", L("Move and/or rotate an existing body")},
{"mate", L("Mate"), 5, nullptr, "fly:placement#2", L("Mate needs two coordinate systems — create them first"), 0x00001202u, 2, 0, false, false, nullptr, "design_c_coincident", L("Assembly: align two coordinate systems (fastened, planar, revolute, slider, cylindrical)")},
{"align", L("Align to"), 5, nullptr, nullptr, L("Align needs a body — add or import one first"), 0x00000002u, 1, 0, false, false, nullptr, nullptr, L("Align the body to a picked face or plane")},
{"plane", L("Plane"), 6, "Shift+P", "key:S+P", nullptr, 0x00000453u, 0, 0, false, false, nullptr, "design_plane", L("Reference plane (offset / tilt / midplane / tangent / two edges / coincident)")},
{"axis", L("Axis"), 6, "Shift+A", "key:S+A", nullptr, 0x00000057u, 0, 0, false, false, nullptr, "design_line", L("Datum axis (two points, face normal, cylinder centerline, two planes, along edge)")},
{"coordsys_v", L("Coordinate system"), 6, "Shift+C", "key:S+C", nullptr, 0x00000043u, 0, 0, false, false, nullptr, "design_point", L("Datum coordinate system (world point, or face + direction edge)")},
{"helix", L("Helix"), 6, nullptr, "fly:plane#3", nullptr, 0x00000405u, 0, 0, false, false, nullptr, "design_thread", L("Helical curve (spring path) — use as a sweep path for coils / springs / augers")},
{"project", L("Project"), 6, nullptr, "fly:plane#4", L("Project needs a body — add or import one first"), 0x00000482u, 1, 0, false, false, nullptr, "design_sketch", L("Project body edges onto a plane as sketch entities")},
{"measure", L("Measure"), 6, nullptr, nullptr, nullptr, 0x000b03feu, 0, 0, false, false, nullptr, nullptr, L("Measure between the picked points, edges or faces")},
{"mass_props", L("Volume and area"), 6, nullptr, "btn:mass", nullptr, 0x000000feu, 1, 0, false, false, nullptr, "info", L("Report the volume and surface area of the selected body")},
{"interference", L("Interference"), 6, nullptr, "btn:interference", L("Interference needs at least two bodies"), 0x00000280u, 2, 0, false, false, nullptr, nullptr, L("Check whether two bodies overlap — reports, changes nothing")},
{"edit_feature", L("Edit"), 7, nullptr, "btn:edit", nullptr, 0x00007d8eu, 0, 0, false, false, nullptr, "design_edit", L("Reopen the selected feature to change what it was made from")},
{"rename", L("Rename…"), 8, "F2", "btn:rename", L("Select a feature, or a body, to rename it"), 0x00004080u, 0, 0, false, false, nullptr, nullptr, L("Give this feature a name you will recognise in the tree (a body takes its name from the feature that makes it)")},
{"delete_face", L("Delete Face"), 7, nullptr, "fly:dressup#3", L("Delete Face needs a body — add or import one first"), 0x0000000eu, 1, 0, false, false, nullptr, "design_delete", L("Remove faces from a body and heal the solid")},
{"colour", L("Color"), 8, nullptr, "btn:colour", nullptr, 0x000001feu, 1, 0, false, false, nullptr, "color_palette", L("Set the selected body's display color")},
{"delete", L("Delete"), 7, "Del", "btn:delete", nullptr, 0x000f7c00u, 0, 0, false, false, nullptr, "design_delete", L("Delete what is selected")},
{"delete_body", L("Delete Body"), 7, nullptr, "btn:delete_body", nullptr, 0x000001feu, 1, 0, false, false, nullptr, "design_delete", L("Delete this whole body — removes the feature it was made from")},
{"sk_line_t", L("Line"), 0, "L", "key:L", nullptr, 0x000f8000u, 0, 0, false, true, L("Line"), "design_line", L("Line — click start, then end")},
{"sk_polyline", L("Polyline"), 0, nullptr, "fly:design_line#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Line"), "design_polyline", L("Click points; click the first point to close the loop, right-click to end it open")},
{"sk_rect", L("Corner rectangle"), 0, "R", "key:R", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect", L("Rectangle — click two opposite corners")},
{"sk_rect_center", L("Center rectangle"), 0, nullptr, "fly:design_rect#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_crect", L("Click center, then a corner")},
{"sk_rect_oblique", L("Oblique rectangle"), 0, nullptr, "fly:design_rect#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect_oblique", L("Click two corners of one edge, then a point for the width")},
{"sk_rect_rounded", L("Rounded rectangle"), 0, nullptr, "fly:design_rect#3", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect_rounded", L("Click two opposite corners, then a point for the corner radius")},
{"sk_circle", L("Center circle"), 0, "C", "key:C", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle", L("Circle — click center, then radius")},
{"sk_circle_2pt", L("2-point circle"), 0, nullptr, "fly:design_circle#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle2pt", L("Click two ends of the diameter")},
{"sk_circle_3pt", L("3-point circle"), 0, nullptr, "fly:design_circle#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle3pt", L("Click three points on the circle")},
{"sk_arc_t", L("3-point arc"), 0, "A", "key:A", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_arc3pt", L("Arc — click start, end, then a point")},
{"sk_arc_tangent", L("Tangent arc"), 0, nullptr, "fly:design_arc3pt#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_tangentarc", L("Click start (on the last entity) then end")},
{"sk_arc_center", L("Center-point arc"), 0, nullptr, "fly:design_arc3pt#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_arc_center", L("Click center, then start, then a point for the end angle")},
{"sk_slot", L("Slot"), 0, "S", "key:S", nullptr, 0x000f8000u, 0, 0, false, true, L("Slot"), "design_slot", L("Slot — two centerline ends, then the width")},
{"sk_slot_arc", L("Arc slot"), 0, nullptr, "fly:design_slot#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Slot"), "design_slot_arc", L("Click center, start, end, then a point for the width")},
{"sk_ellipse", L("Ellipse"), 0, "E", "key:E", nullptr, 0x000f8000u, 0, 0, false, true, L("Ellipse"), "design_ellipse", L("Ellipse — center, major end, minor point")},
{"sk_ellipse_arc", L("Elliptical arc"), 0, nullptr, "fly:design_ellipse#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Ellipse"), "design_ellipse_arc", L("Click center, major-axis end, minor point, then arc start and end")},
{"sk_spline", L("Spline"), 0, "B", "key:B", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_bspline", L("Spline — click control points")},
{"sk_poly_3", L("Triangle"), 0, nullptr, "btn:poly#3", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Triangle — click center, then a vertex")},
{"sk_poly_4", L("Square"), 0, nullptr, "btn:poly#4", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Square — click center, then a vertex")},
{"sk_poly_5", L("Pentagon"), 0, nullptr, "btn:poly#5", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Pentagon — click center, then a vertex")},
{"sk_polygon", L("Hexagon"), 0, "G", "btn:poly#6", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Hexagon — click center, then a vertex")},
{"sk_poly_8", L("Octagon"), 0, nullptr, "btn:poly#8", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Octagon — click center, then a vertex")},
{"sk_poly_12", L("Dodecagon"), 0, nullptr, "btn:poly#12", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Dodecagon — click center, then a vertex")},
{"sk_poly_inscribed", L("Inscribed"), 0, nullptr, "btn:polyfit#0", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Measure the polygon to its corners (inscribed)")},
{"sk_poly_circumscribed", L("Circumscribed"), 0, nullptr, "btn:polyfit#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Measure the polygon to its flats (circumscribed)")},
{"sk_point_t", L("Point"), 0, "P", "key:P", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_point", L("Point — click to place")},
{"sk_text", L("Text"), 0, nullptr, "btn:text", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_text", L("Type text; it becomes a Text feature on this sketch's plane, editable later")},
{"sk_svg", L("SVG"), 0, nullptr, "btn:svg", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_svg", L("Import an SVG outline into this sketch as editable lines")},
{"sk_offset", L("Offset"), 1, "O", "key:O", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_offset", L("Offset — pick an entity, drag the distance")},
{"sk_trim", L("Trim"), 2, "T", "key:T", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_trim", L("Trim — click a segment to trim it")},
{"sk_fillet", L("Fillet"), 3, "F", "key:F", nullptr, 0x00090000u, 0, 0, false, true, nullptr, "design_filletedge", L("Fillet — pick two lines, set the radius")},
{"sk_chamfer", L("Chamfer"), 3, "H", "key:H", nullptr, 0x00090000u, 0, 0, false, true, nullptr, "design_chamfer", L("Chamfer — pick two lines, set the distance")},
{"sk_array", L("Linear array"), 4, nullptr, "fly:design_array#0", nullptr, 0x000b0000u, 0, 0, false, true, L("Array"), "design_array", L("Pick entities, drag the spacing handle, click the count; click empty to apply")},
{"sk_array_polar", L("Polar array"), 4, nullptr, "fly:design_array#1", nullptr, 0x000b0000u, 0, 0, false, true, L("Array"), "design_polararray", L("Pick entities, drag the sweep handle, click the count; click empty to apply")},
{"sk_mirror", L("Mirror"), 4, "M", "key:M", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_mirror", L("Mirror — pick axis, then entities")},
{"sk_move", L("Move"), 5, nullptr, "fly:design_move#0", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_move", L("Pick entities, then drag the handle or click the distance; click empty to apply")},
{"sk_rotate", L("Rotate"), 5, nullptr, "fly:design_move#1", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_rotate", L("Pick entities, then drag around the pivot or click the angle; click empty to apply")},
{"sk_scale", L("Scale"), 5, nullptr, "fly:design_move#2", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_scale", L("Pick entities, then drag the handle or click the factor; click empty to apply")},
{"sk_dimension", L("Dimension"), 6, "D", "key:D", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_dimension", L("Dimension — click 2 points or an entity")},
{"sk_constrain", L("Constrain"), 6, "K", "key:K", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_constrain", L("Constrain the selected sketch entities to each other")},
{"sketch", L_CONTEXT("Sketch", "Design"), 0, "Shift+S", "key:S+S", L("Click a face or a reference plane in the viewport, then a sketch tool"), 0x00000403u, 0, 0, false, false, nullptr, "design_sketch", L("Click a face or a reference plane, then pick a drawing tool")},
{"extrude", L_CONTEXT("Extrude", "Design"), 1, "Shift+E", "key:S+E", L("Create a sketch, or pick a solid face, first"), 0x00004002u, 0, 0, false, false, nullptr, "design_extrude", L("Extrude a sketch profile, or push/pull a picked face")},
{"revolve", L_CONTEXT("Revolve", "Design"), 1, "Shift+R", "key:S+R", L("Create a sketch profile to revolve first"), 0x00004000u, 0, 0, false, false, nullptr, "design_revolve", L("Revolve a profile about an axis")},
{"sweep", L_CONTEXT("Sweep", "Design"), 1, "Shift+W", "key:S+W", L("Create a profile sketch to sweep first"), 0x00004000u, 0, 2, false, false, nullptr, "design_sweep", L("Sweep a profile along a path")},
{"loft", L_CONTEXT("Loft", "Design"), 1, "Shift+L", "key:S+L", L("Create at least two profile sketches to loft"), 0x00004000u, 0, 2, false, false, nullptr, "design_loft", L("Loft (skin) between two or more profiles")},
{"thicken", L_CONTEXT("Thicken", "Design"), 1, nullptr, "fly:material#4", L("Thicken needs a solid body — add or import one first"), 0x0000000au, 1, 0, false, false, nullptr, "design_thicken", L("Offset a solid face into a thin plate (new body)")},
{"rib", L_CONTEXT("Rib", "Design"), 1, "R", "fly:material#5", L("Rib needs a solid body — add or import one first"), 0x00010000u, 1, 0, false, false, nullptr, "design_rib", L("Grow a thin wall from an open sketch line, fused to a body")},
{"boolean", L_CONTEXT("Join", "Design"), 1, "Shift+B", "btn:bool#0", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Fuse the tool body into the target — one solid, no seam")},
{"bool_subtract", L_CONTEXT("Subtract", "Design"), 1, nullptr, "btn:bool#1", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Cut the tool body out of the target")},
{"bool_intersect", L_CONTEXT("Intersect", "Design"), 1, nullptr, "btn:bool#2", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Keep only where the two bodies overlap")},
{"surf_extrude", L_CONTEXT("Surface Extrude", "Design"), 1, "Shift+G", "key:S+G", L("Create a sketch first"), 0x00004000u, 0, 0, false, false, nullptr, "design_extrude", L("Extrude a sketch into a sheet body (no end caps)")},
{"surf_revolve", L_CONTEXT("Surface Revolve", "Design"), 1, nullptr, "fly:surface#1", L("Create a sketch profile to revolve first"), 0x00004000u, 0, 0, false, false, nullptr, "design_revolve", L("Revolve a sketch profile into a sheet body")},
{"surf_loft", L_CONTEXT("Surface Loft", "Design"), 1, nullptr, "fly:surface#2", L("Create at least two profile sketches to loft"), 0x00004000u, 0, 2, false, false, nullptr, "design_loft", L("Loft (skin) between 2+ profiles, open (no end caps)")},
{"surf_fill", L_CONTEXT("Surface Fill", "Design"), 1, nullptr, "fly:surface#3", L("Create a closed sketch first"), 0x00004000u, 0, 0, false, false, nullptr, "design_surface", L("Fill a sketch boundary with a smooth face")},
{"thicken_surf", L_CONTEXT("Thicken Surface", "Design"), 1, nullptr, "fly:surface#5", L("Thicken Surface needs a surface body — make one with a Surface tool first"), 0x00000100u, 0, 0, true, false, nullptr, "design_thicken", L("Thicken a sheet body into a solid")},
{"hole", L_CONTEXT("Hole", "Design"), 2, "Shift+H", "key:S+H", L("Pick a face or a plane to drill into"), 0x00000402u, 1, 0, false, false, nullptr, "design_hole", L("Drill a hole, centerd on a picked face or placed on a plane")},
{"thread", L_CONTEXT("Thread", "Design"), 2, "Shift+T", "key:S+T", L("Pick a cylindrical surface (bore / outer) or a circular edge for a thread"), 0x00000024u, 1, 0, false, false, nullptr, "design_thread", L("Thread a cylindrical surface (inner bore / outer) or a circular edge")},
{"shell", L_CONTEXT("Shell", "Design"), 2, "Shift+K", "key:S+K", L("Shell needs a solid body — add or import one first"), 0x00000082u, 1, 0, false, false, nullptr, "design_shell", L("Hollow the body to a wall thickness, opening a picked face")},
{"cut", L_CONTEXT("Cut", "Design"), 2, "Shift+X", "key:S+X", L("Cut needs a solid body — add or import one first"), 0x000004feu, 1, 0, false, false, nullptr, "design_cut", L("Trim the body with a plane — drag the offset arrow; keep one half or both")},
{"split", L_CONTEXT("Split", "Design"), 2, nullptr, nullptr, L("Split needs a solid body — add or import one first"), 0x000000feu, 1, 0, false, false, nullptr, nullptr, L("Split the body along a picked face into two solids")},
{"fillet", L_CONTEXT("Fillet", "Design"), 3, "Shift+F", "btn:dress#0", L("Pick an edge to round"), 0x000000b2u, 1, 0, false, false, nullptr, "design_filletedge", L("Pick an edge, then drag the radius arrow or type it")},
{"chamfer", L_CONTEXT("Chamfer", "Design"), 3, nullptr, "btn:dress#1", L("Pick an edge to bevel"), 0x000000b2u, 1, 0, false, false, nullptr, "design_chamfer", L("Pick an edge, then drag the distance arrow or type it")},
{"draft", L_CONTEXT("Draft", "Design"), 3, "Shift+D", "key:S+D", L("Pick a face to taper"), 0x0000000au, 1, 0, false, false, nullptr, "design_draft", L("Tilt a picked face by a draft angle")},
{"surf_offset", L_CONTEXT("Surface Offset", "Design"), 3, nullptr, "fly:surface#4", L("Surface Offset needs a surface body — make one with a Surface tool first"), 0x00000100u, 0, 0, true, false, nullptr, "design_offset", L("Offset a sheet body's shell by a signed distance")},
{"pattern", L_CONTEXT("Linear pattern", "Design"), 4, "Shift+N", "btn:pat#0", L("Pattern needs a solid body — add or import one first"), 0x00006082u, 1, 0, false, false, nullptr, "design_array", L("Repeat the body along a direction — drag the spacing, set the count")},
{"pattern_circular", L_CONTEXT("Circular pattern", "Design"), 4, nullptr, "btn:pat#1", L("Pattern needs a solid body — add or import one first"), 0x00006082u, 1, 0, false, false, nullptr, "design_polararray", L("Repeat the body around an axis — set the count and sweep")},
{"mirror", L_CONTEXT("Mirror", "Design"), 4, "Shift+Z", "key:S+Z", L("Mirror needs a body — add or import one first"), 0x000004feu, 1, 0, false, false, nullptr, "design_mirror", L("Reflect a body about a plane")},
{"pat_curve", L_CONTEXT("Pattern on Curve", "Design"), 4, nullptr, nullptr, L("Pattern on Curve needs a body and a curve"), 0x00000090u, 1, 0, false, false, nullptr, nullptr, L("Repeat the body along a picked curve")},
{"transform", L_CONTEXT("Move", "Design"), 5, "Shift+Y", "key:S+Y", L("Transform needs a body — add or import one first"), 0x000021feu, 1, 0, false, false, nullptr, "design_move", L("Move and/or rotate an existing body")},
{"mate", L_CONTEXT("Mate", "Design"), 5, nullptr, "fly:placement#2", L("Mate needs two coordinate systems — create them first"), 0x00001202u, 2, 0, false, false, nullptr, "design_c_coincident", L("Assembly: align two coordinate systems (fastened, planar, revolute, slider, cylindrical)")},
{"align", L_CONTEXT("Align to", "Design"), 5, nullptr, nullptr, L("Align needs a body — add or import one first"), 0x00000002u, 1, 0, false, false, nullptr, nullptr, L("Align the body to a picked face or plane")},
{"plane", L_CONTEXT("Plane", "Design"), 6, "Shift+P", "key:S+P", nullptr, 0x00000453u, 0, 0, false, false, nullptr, "design_plane", L("Reference plane (offset / tilt / midplane / tangent / two edges / coincident)")},
{"axis", L_CONTEXT("Axis", "Design"), 6, "Shift+A", "key:S+A", nullptr, 0x00000057u, 0, 0, false, false, nullptr, "design_line", L("Datum axis (two points, face normal, cylinder centerline, two planes, along edge)")},
{"coordsys_v", L_CONTEXT("Coordinate system", "Design"), 6, "Shift+C", "key:S+C", nullptr, 0x00000043u, 0, 0, false, false, nullptr, "design_point", L("Datum coordinate system (world point, or face + direction edge)")},
{"helix", L_CONTEXT("Helix", "Design"), 6, nullptr, "fly:plane#3", nullptr, 0x00000405u, 0, 0, false, false, nullptr, "design_thread", L("Helical curve (spring path) — use as a sweep path for coils / springs / augers")},
{"project", L_CONTEXT("Project", "Design"), 6, nullptr, "fly:plane#4", L("Project needs a body — add or import one first"), 0x00000482u, 1, 0, false, false, nullptr, "design_sketch", L("Project body edges onto a plane as sketch entities")},
{"measure", L_CONTEXT("Measure", "Design"), 6, nullptr, nullptr, nullptr, 0x000b03feu, 0, 0, false, false, nullptr, nullptr, L("Measure between the picked points, edges or faces")},
{"mass_props", L_CONTEXT("Volume and area", "Design"), 6, nullptr, "btn:mass", nullptr, 0x000000feu, 1, 0, false, false, nullptr, "info", L("Report the volume and surface area of the selected body")},
{"interference", L_CONTEXT("Interference", "Design"), 6, nullptr, "btn:interference", L("Interference needs at least two bodies"), 0x00000280u, 2, 0, false, false, nullptr, nullptr, L("Check whether two bodies overlap — reports, changes nothing")},
{"edit_feature", L_CONTEXT("Edit", "Design"), 7, nullptr, "btn:edit", nullptr, 0x00007d8eu, 0, 0, false, false, nullptr, "design_edit", L("Reopen the selected feature to change what it was made from")},
{"rename", L_CONTEXT("Rename…", "Design"), 8, "F2", "btn:rename", L("Select a feature, or a body, to rename it"), 0x00004080u, 0, 0, false, false, nullptr, nullptr, L("Give this feature a name you will recognise in the tree (a body takes its name from the feature that makes it)")},
{"delete_face", L_CONTEXT("Delete Face", "Design"), 7, nullptr, "fly:dressup#3", L("Delete Face needs a body — add or import one first"), 0x0000000eu, 1, 0, false, false, nullptr, "design_delete", L("Remove faces from a body and heal the solid")},
{"colour", L_CONTEXT("Color", "Design"), 8, nullptr, "btn:colour", nullptr, 0x000001feu, 1, 0, false, false, nullptr, "color_palette", L("Set the selected body's display color")},
{"delete", L_CONTEXT("Delete", "Design"), 7, "Del", "btn:delete", nullptr, 0x000f7c00u, 0, 0, false, false, nullptr, "design_delete", L("Delete what is selected")},
{"delete_body", L_CONTEXT("Delete Body", "Design"), 7, nullptr, "btn:delete_body", nullptr, 0x000001feu, 1, 0, false, false, nullptr, "design_delete", L("Delete this whole body — removes the feature it was made from")},
{"sk_line_t", L_CONTEXT("Line", "Design"), 0, "L", "key:L", nullptr, 0x000f8000u, 0, 0, false, true, L("Line"), "design_line", L("Line — click start, then end")},
{"sk_polyline", L_CONTEXT("Polyline", "Design"), 0, nullptr, "fly:design_line#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Line"), "design_polyline", L("Click points; click the first point to close the loop, right-click to end it open")},
{"sk_rect", L_CONTEXT("Corner rectangle", "Design"), 0, "R", "key:R", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect", L("Rectangle — click two opposite corners")},
{"sk_rect_center", L_CONTEXT("Center rectangle", "Design"), 0, nullptr, "fly:design_rect#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_crect", L("Click center, then a corner")},
{"sk_rect_oblique", L_CONTEXT("Oblique rectangle", "Design"), 0, nullptr, "fly:design_rect#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect_oblique", L("Click two corners of one edge, then a point for the width")},
{"sk_rect_rounded", L_CONTEXT("Rounded rectangle", "Design"), 0, nullptr, "fly:design_rect#3", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect_rounded", L("Click two opposite corners, then a point for the corner radius")},
{"sk_circle", L_CONTEXT("Center circle", "Design"), 0, "C", "key:C", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle", L("Circle — click center, then radius")},
{"sk_circle_2pt", L_CONTEXT("2-point circle", "Design"), 0, nullptr, "fly:design_circle#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle2pt", L("Click two ends of the diameter")},
{"sk_circle_3pt", L_CONTEXT("3-point circle", "Design"), 0, nullptr, "fly:design_circle#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle3pt", L("Click three points on the circle")},
{"sk_arc_t", L_CONTEXT("3-point arc", "Design"), 0, "A", "key:A", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_arc3pt", L("Arc — click start, end, then a point")},
{"sk_arc_tangent", L_CONTEXT("Tangent arc", "Design"), 0, nullptr, "fly:design_arc3pt#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_tangentarc", L("Click start (on the last entity) then end")},
{"sk_arc_center", L_CONTEXT("Center-point arc", "Design"), 0, nullptr, "fly:design_arc3pt#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_arc_center", L("Click center, then start, then a point for the end angle")},
{"sk_slot", L_CONTEXT("Slot", "Design"), 0, "S", "key:S", nullptr, 0x000f8000u, 0, 0, false, true, L("Slot"), "design_slot", L("Slot — two centerline ends, then the width")},
{"sk_slot_arc", L_CONTEXT("Arc slot", "Design"), 0, nullptr, "fly:design_slot#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Slot"), "design_slot_arc", L("Click center, start, end, then a point for the width")},
{"sk_ellipse", L_CONTEXT("Ellipse", "Design"), 0, "E", "key:E", nullptr, 0x000f8000u, 0, 0, false, true, L("Ellipse"), "design_ellipse", L("Ellipse — center, major end, minor point")},
{"sk_ellipse_arc", L_CONTEXT("Elliptical arc", "Design"), 0, nullptr, "fly:design_ellipse#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Ellipse"), "design_ellipse_arc", L("Click center, major-axis end, minor point, then arc start and end")},
{"sk_spline", L_CONTEXT("Spline", "Design"), 0, "B", "key:B", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_bspline", L("Spline — click control points")},
{"sk_poly_3", L_CONTEXT("Triangle", "Design"), 0, nullptr, "btn:poly#3", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Triangle — click center, then a vertex")},
{"sk_poly_4", L_CONTEXT("Square", "Design"), 0, nullptr, "btn:poly#4", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Square — click center, then a vertex")},
{"sk_poly_5", L_CONTEXT("Pentagon", "Design"), 0, nullptr, "btn:poly#5", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Pentagon — click center, then a vertex")},
{"sk_polygon", L_CONTEXT("Hexagon", "Design"), 0, "G", "btn:poly#6", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Hexagon — click center, then a vertex")},
{"sk_poly_8", L_CONTEXT("Octagon", "Design"), 0, nullptr, "btn:poly#8", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Octagon — click center, then a vertex")},
{"sk_poly_12", L_CONTEXT("Dodecagon", "Design"), 0, nullptr, "btn:poly#12", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Dodecagon — click center, then a vertex")},
{"sk_poly_inscribed", L_CONTEXT("Inscribed", "Design"), 0, nullptr, "btn:polyfit#0", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Measure the polygon to its corners (inscribed)")},
{"sk_poly_circumscribed", L_CONTEXT("Circumscribed", "Design"), 0, nullptr, "btn:polyfit#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Measure the polygon to its flats (circumscribed)")},
{"sk_point_t", L_CONTEXT("Point", "Design"), 0, "P", "key:P", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_point", L("Point — click to place")},
{"sk_text", L_CONTEXT("Text", "Design"), 0, nullptr, "btn:text", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_text", L("Type text; it becomes a Text feature on this sketch's plane, editable later")},
{"sk_svg", L_CONTEXT("SVG", "Design"), 0, nullptr, "btn:svg", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_svg", L("Import an SVG outline into this sketch as editable lines")},
{"sk_offset", L_CONTEXT("Offset", "Design"), 1, "O", "key:O", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_offset", L("Offset — pick an entity, drag the distance")},
{"sk_trim", L_CONTEXT("Trim", "Design"), 2, "T", "key:T", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_trim", L("Trim — click a segment to trim it")},
{"sk_fillet", L_CONTEXT("Fillet", "Design"), 3, "F", "key:F", nullptr, 0x00090000u, 0, 0, false, true, nullptr, "design_filletedge", L("Fillet — pick two lines, set the radius")},
{"sk_chamfer", L_CONTEXT("Chamfer", "Design"), 3, "H", "key:H", nullptr, 0x00090000u, 0, 0, false, true, nullptr, "design_chamfer", L("Chamfer — pick two lines, set the distance")},
{"sk_array", L_CONTEXT("Linear array", "Design"), 4, nullptr, "fly:design_array#0", nullptr, 0x000b0000u, 0, 0, false, true, L("Array"), "design_array", L("Pick entities, drag the spacing handle, click the count; click empty to apply")},
{"sk_array_polar", L_CONTEXT("Polar array", "Design"), 4, nullptr, "fly:design_array#1", nullptr, 0x000b0000u, 0, 0, false, true, L("Array"), "design_polararray", L("Pick entities, drag the sweep handle, click the count; click empty to apply")},
{"sk_mirror", L_CONTEXT("Mirror", "Design"), 4, "M", "key:M", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_mirror", L("Mirror — pick axis, then entities")},
{"sk_move", L_CONTEXT("Move", "Design"), 5, nullptr, "fly:design_move#0", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_move", L("Pick entities, then drag the handle or click the distance; click empty to apply")},
{"sk_rotate", L_CONTEXT("Rotate", "Design"), 5, nullptr, "fly:design_move#1", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_rotate", L("Pick entities, then drag around the pivot or click the angle; click empty to apply")},
{"sk_scale", L_CONTEXT("Scale", "Design"), 5, nullptr, "fly:design_move#2", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_scale", L("Pick entities, then drag the handle or click the factor; click empty to apply")},
{"sk_dimension", L_CONTEXT("Dimension", "Design"), 6, "D", "key:D", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_dimension", L("Dimension — click 2 points or an entity")},
{"sk_constrain", L_CONTEXT("Constrain", "Design"), 6, "K", "key:K", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_constrain", L("Constrain the selected sketch entities to each other")},
// Same verb, model-mode vocabulary: offered when a SKETCH is selected (bit 14, SkLoop), the
// state a user is in right after finishing one. Without this row the only way in was the
// toolbar icon, and constraints read as absent — see the Onshape-comparison report.
// It sits in the Reference row, the address the sketch-mode Constrain has: one verb, one row.
{"constrain", L("Constrain sketch"), 6, nullptr, "btn:constrain", L("Select a sketch to constrain it"), 0x00004000u, 0, 1, false, false, nullptr, "design_constrain", L("Add dimensions and relations (coincident, tangent, parallel...) to the selected sketch")},
{"sk_construct", L("Construction"), 6, "Q", "key:Q", nullptr, 0x000b8000u, 0, 0, false, true, nullptr, nullptr, L("Toggle construction: geometry that guides but is never built")},
{"sk_extend", L("Extend"), 7, "X", "key:X", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_extend", L("Extend — click a line/arc to extend it")},
{"sk_delete", L("Delete"), 7, "Del", "btn:sk_delete", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_delete", L("Delete the selected sketch entities")},
{"constrain", L_CONTEXT("Constrain sketch", "Design"), 6, nullptr, "btn:constrain", L("Select a sketch to constrain it"), 0x00004000u, 0, 1, false, false, nullptr, "design_constrain", L("Add dimensions and relations (coincident, tangent, parallel...) to the selected sketch")},
{"sk_construct", L_CONTEXT("Construction", "Design"), 6, "Q", "key:Q", nullptr, 0x000b8000u, 0, 0, false, true, nullptr, nullptr, L("Toggle construction: geometry that guides but is never built")},
{"sk_extend", L_CONTEXT("Extend", "Design"), 7, "X", "key:X", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_extend", L("Extend — click a line/arc to extend it")},
{"sk_delete", L_CONTEXT("Delete", "Design"), 7, "Del", "btn:sk_delete", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_delete", L("Delete the selected sketch entities")},
// Typing the defining number of the element you pointed at. Three rows rather than one so
// each names the quantity in the drawing-office word for THAT element; all three land on
// the same handler, because dimension_kind() already resolves the quantity from the
// selection. Without these, an element's own numbers were reachable only by arming the
// Dimension tool and re-picking geometry that was already selected.
{"sk_length", L("Length…"), 7, "V", "key:V", nullptr, 0x00010000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the length of this line")},
{"sk_radius", L("Radius / diameter…"), 7, "V", "key:V", nullptr, 0x00020000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the radius of this arc, or the diameter of this circle")},
{"sk_angdist", L("Angle / distance…"), 7, "V", "key:V", nullptr, 0x00080000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the angle between two lines, or the distance between the two picks")},
{"sk_length", L_CONTEXT("Length…", "Design"), 7, "V", "key:V", nullptr, 0x00010000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the length of this line")},
{"sk_radius", L_CONTEXT("Radius / diameter…", "Design"), 7, "V", "key:V", nullptr, 0x00020000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the radius of this arc, or the diameter of this circle")},
{"sk_angdist", L_CONTEXT("Angle / distance…", "Design"), 7, "V", "key:V", nullptr, 0x00080000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the angle between two lines, or the distance between the two picks")},
};
static const int kOfferVerbCount = 92;
+158 -144
View File
@@ -110,15 +110,6 @@
#include "slic3r/GUI/Shortcuts.hpp"
#include "slic3r/GUI/I18N.hpp"
// English-only pin for the Design tab (see design-ux-contract): one lever
// de-translates this whole TU so our strings never half-translate against the host's
// localized chrome. Host UI still follows the app locale; only this tab is pinned EN.
// GOTCHA: every _L(...) in this file must take a STRING LITERAL (FromUTF8 wants const char*).
#ifdef _L
#undef _L
#endif
#define _L(s) wxString::FromUTF8(s)
namespace Slic3r { namespace GUI {
// Mesh -> B-rep import defaults (see GeometryEngine::mesh_to_brep).
@@ -498,7 +489,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
}
m_viewport->set_sketch_construction(m_construction->GetValue());
set_status(StatusKind::Info, m_sketch_on.IsEmpty() ? hint
: wxString::Format(_L("%s · on %s"), hint, m_sketch_on));
: wxString::Format(_L("%s · plane: %s"), hint, m_sketch_on));
};
// Sketch-tool shortcuts (single letters, active only while a sketch is open). Family tools
@@ -731,11 +722,11 @@ DesignPanel::DesignPanel(wxWindow* parent)
wxString where;
const bool have_plane = sketch_plane_target(where);
set_status(StatusKind::Info, have_plane
? wxString::Format(_L("Sketching on %s — pick a tool"), where)
? wxString::Format(_L("Sketch plane: %s — pick a tool"), where)
: _L("Click a face or a reference plane in the viewport, then a sketch tool"));
if (m_sketch_hint) { // the card must agree with the status line, not argue with it
m_sketch_hint->SetLabel(have_plane
? wxString::Format(_L("Drawing on %s.\nPick a tool, or press Menu for the list."), where)
? wxString::Format(_L("Sketch plane: %s.\nPick a tool, or press Menu for the list."), where)
: _L("Click a face or a reference plane, then a sketch tool."));
m_sketch_hint->Refresh();
m_cards->Layout();
@@ -752,7 +743,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
// Add material: every feature that grows new solid material — from a profile
// (extrude/revolve/sweep/loft), from a face (thicken) or from a line (rib).
feat_dropdown("material", "design_extrude", _L("Add material (extrude / revolve / sweep / loft / thicken / rib)"), {
{"design_extrude", _L("Extrude"), _L("Extrude a sketch profile, or push/pull a picked face"),
{"design_extrude", _L_CONTEXT("Extrude", "Design"), _L("Extrude a sketch profile, or push/pull a picked face"),
[this] {
// Onshape push/pull: an explicitly picked solid face (Face-level cycle, no loop
// selected) is extruded as the profile — this takes priority over re-extruding an
@@ -833,7 +824,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_sel_solid_face = -1;
open_tool(Tool::Thicken); // syncs m_thicken_face_label from m_sel_solid_face
}, 0},
{"design_rib", _L("Rib"), _L("Grow a thin wall from an open sketch line, fused to a body"),
{"design_rib", _L_CONTEXT("Rib", "Design"), _L("Grow a thin wall from an open sketch line, fused to a body"),
[this] {
if (m_doc.bodies.empty()) {
set_status(StatusKind::Error, _L("Rib needs a solid body — add or import one first"));
@@ -873,7 +864,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
// shortcut at all, which also made its card unreachable to the rig.
});
auto* b_pattern = icon_btn("design_pattern", _L("Pattern"));
auto* b_pattern = icon_btn("design_pattern", _L_CONTEXT("Pattern", "Design"));
std::function<void()> act_pattern = [this] {
// Pattern replicates an existing body — needs at least one solid.
if (m_doc.bodies.empty()) {
@@ -979,7 +970,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
}, 0},
// Project belongs here, not in Dress-up: it consumes a body but PRODUCES sketch
// geometry, so it is reference/curve creation like the four above, not a finishing op.
{"design_sketch", _L("Project"), _L("Project body edges onto a plane as sketch entities"),
{"design_sketch", _L_CONTEXT("Project", "Design"), _L("Project body edges onto a plane as sketch entities"),
[this] {
if (m_doc.bodies.empty()) {
set_status(StatusKind::Error, _L("Project needs a body — add or import one first"));
@@ -1139,7 +1130,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
[this] { open_tool(Tool::Dressup); }, SHIFT('F')},
{"design_draft", _L("Draft (taper a face)"), _L("Tilt a picked face by a draft angle"),
[this] { open_tool(Tool::Draft); }, SHIFT('D')},
{"design_shell", _L("Shell"), _L("Hollow the body to a wall thickness, opening a picked face"),
{"design_shell", _L_CONTEXT("Shell", "Design"), _L("Hollow the body to a wall thickness, opening a picked face"),
[this] { open_tool(Tool::Shell); }, SHIFT('K')},
{"design_delete", _L("Delete Face"), _L("Remove faces from a body and heal the solid"),
[this] {
@@ -1165,7 +1156,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
// Hole / Thread — drilling into a solid (both face-aware)
feat_dropdown("hole", "design_hole", _L("Hole / thread"), {
{"design_hole", _L("Hole"), _L("Drill a hole, centerd on a picked face or placed on a plane"),
{"design_hole", _L("Hole"), _L("Drill a hole, centered on a picked face or placed on a plane"),
[this] {
// #2: drill on the picked solid face, centred on it (origin = face centroid,
// normal = inward). Otherwise fall back to the plane dropdown. m_hole_x/y then
@@ -1542,8 +1533,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
if (n > 0) {
m_construction->SetValue(!m_construction->GetValue()); // the mode did not move
set_status(StatusKind::Info, wxString::Format(
_L("Converted %d entit%s between construction and real geometry"),
n, n == 1 ? "y" : "ies"));
_L_PLURAL("Converted %d entity between construction and real geometry",
"Converted %d entities between construction and real geometry", n),
n));
return;
}
m_viewport->set_sketch_construction(m_construction->GetValue()); });
@@ -1574,26 +1566,26 @@ DesignPanel::DesignPanel(wxWindow* parent)
b->Bind(wxEVT_BUTTON, [this, type](wxCommandEvent&) { apply_constraint(type); });
cadd(b);
};
cbtn("design_c_horizontal", _L("Horizontal"), SketchConstraintType::Horizontal);
cbtn("design_c_vertical", _L("Vertical"), SketchConstraintType::Vertical);
cbtn("design_c_parallel", _L("Parallel"), SketchConstraintType::Parallel);
cbtn("design_c_perpendicular", _L("Perpendicular"), SketchConstraintType::Perpendicular);
cbtn("design_c_coincident", _L("Coincident"), SketchConstraintType::Coincident);
cbtn("design_c_equal", _L("Equal length"), SketchConstraintType::EqualLength);
cbtn("design_c_equal_radius", _L("Equal radius"), SketchConstraintType::EqualRadius);
cbtn("design_c_collinear", _L("Collinear"), SketchConstraintType::Collinear);
cbtn("design_c_concentric", _L("Concentric"), SketchConstraintType::Concentric);
cbtn("design_c_tangent", _L("Tangent"), SketchConstraintType::Tangent);
cbtn("design_c_midpoint", _L("Midpoint"), SketchConstraintType::Midpoint);
cbtn("design_c_symmetric", _L("Symmetric"), SketchConstraintType::Symmetric);
cbtn("design_c_sym_v", _L("Symmetric about the vertical axis"), SketchConstraintType::SymmetricAboutY);
cbtn("design_c_sym_h", _L("Symmetric about the horizontal axis"), SketchConstraintType::SymmetricAboutX);
cbtn("design_c_angle", _L("Angle"), SketchConstraintType::Angle);
cbtn("design_c_radius", _L("Radius"), SketchConstraintType::Radius);
cbtn("design_c_diameter", _L("Diameter"), SketchConstraintType::Diameter);
cbtn("design_c_fix", _L("Fix point (anchor in place)"), SketchConstraintType::Fix);
cbtn("design_c_dist_x", _L("Horizontal distance"), SketchConstraintType::DistanceX);
cbtn("design_c_dist_y", _L("Vertical distance"), SketchConstraintType::DistanceY);
cbtn("design_c_horizontal", _L_CONTEXT("Horizontal", "Sketch Constraint"), SketchConstraintType::Horizontal);
cbtn("design_c_vertical", _L_CONTEXT("Vertical", "Sketch Constraint"), SketchConstraintType::Vertical);
cbtn("design_c_parallel", _L_CONTEXT("Parallel", "Sketch Constraint"), SketchConstraintType::Parallel);
cbtn("design_c_perpendicular", _L_CONTEXT("Perpendicular", "Sketch Constraint"), SketchConstraintType::Perpendicular);
cbtn("design_c_coincident", _L_CONTEXT("Coincident", "Sketch Constraint"), SketchConstraintType::Coincident);
cbtn("design_c_equal", _L_CONTEXT("Equal length", "Sketch Constraint"), SketchConstraintType::EqualLength);
cbtn("design_c_equal_radius", _L_CONTEXT("Equal radius", "Sketch Constraint"), SketchConstraintType::EqualRadius);
cbtn("design_c_collinear", _L_CONTEXT("Collinear", "Sketch Constraint"), SketchConstraintType::Collinear);
cbtn("design_c_concentric", _L_CONTEXT("Concentric", "Sketch Constraint"), SketchConstraintType::Concentric);
cbtn("design_c_tangent", _L_CONTEXT("Tangent", "Sketch Constraint"), SketchConstraintType::Tangent);
cbtn("design_c_midpoint", _L_CONTEXT("Midpoint", "Sketch Constraint"), SketchConstraintType::Midpoint);
cbtn("design_c_symmetric", _L_CONTEXT("Symmetric", "Sketch Constraint"), SketchConstraintType::Symmetric);
cbtn("design_c_sym_v", _L_CONTEXT("Symmetric about the vertical axis", "Sketch Constraint"), SketchConstraintType::SymmetricAboutY);
cbtn("design_c_sym_h", _L_CONTEXT("Symmetric about the horizontal axis", "Sketch Constraint"), SketchConstraintType::SymmetricAboutX);
cbtn("design_c_angle", _L_CONTEXT("Angle", "Sketch Constraint"), SketchConstraintType::Angle);
cbtn("design_c_radius", _L_CONTEXT("Radius", "Sketch Constraint"), SketchConstraintType::Radius);
cbtn("design_c_diameter", _L_CONTEXT("Diameter", "Sketch Constraint"), SketchConstraintType::Diameter);
cbtn("design_c_fix", _L_CONTEXT("Fix point (anchor in place)", "Sketch Constraint"), SketchConstraintType::Fix);
cbtn("design_c_dist_x", _L_CONTEXT("Horizontal distance", "Sketch Constraint"), SketchConstraintType::DistanceX);
cbtn("design_c_dist_y", _L_CONTEXT("Vertical distance", "Sketch Constraint"), SketchConstraintType::DistanceY);
// Trim/Extend are now standalone SKETCH scissors (Mode::Trim/Extend) in the sketch
// toolbar, NOT Constrain buttons. The other edit ops (Mirror/Offset/Fillet/Chamfer/
// Move/…) are first-class sketch tools too. Done constraining = the action-bar ✓.
@@ -1860,9 +1852,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
mform->Add(spin_frame(m_move_dz), 0, wxEXPAND);
m_move_axis = make_combo(m_cards);
m_move_axis->Append(_L("X"));
m_move_axis->Append(_L("Y"));
m_move_axis->Append(_L("Z"));
m_move_axis->Append(_L_CONTEXT("X", "Axis"));
m_move_axis->Append(_L_CONTEXT("Y", "Axis"));
m_move_axis->Append(_L_CONTEXT("Z", "Axis"));
m_move_axis->SetSelection(2);
mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Rotation axis")), 0, wxALIGN_CENTER_VERTICAL);
mform->Add(m_move_axis, 0, wxEXPAND);
@@ -1894,7 +1886,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
// --- Extrude dialog (consumes the selected sketch) ---
m_box_extrude = new wxBoxSizer(wxVERTICAL);
m_box_extrude->Add(card_header(m_cards, "design_extrude", _L("Extrude"), m_hdr_extrude), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_extrude->Add(card_header(m_cards, "design_extrude", _L_CONTEXT("Extrude", "Design"), m_hdr_extrude), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_extrude->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
m_extrude_sketch_label = new wxStaticText(m_cards, wxID_ANY, _L("Sketch: —"));
m_box_extrude->Add(m_extrude_sketch_label, 0, wxLEFT | wxRIGHT | wxTOP, 12);
@@ -1912,9 +1904,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
// End condition (order MUST match ExtrudeEnd: Blind/Symmetric/TwoSided/ThroughAll/
// UpToFace/UpToVertex). Up-to-face uses the currently click-selected solid face.
m_extrude_end = make_combo(m_cards);
for (const char* s : { "Blind", "Symmetric", "Two-sided", "Through all",
"Up to face", "Up to vertex" })
m_extrude_end->Append(s);
for (const char* s : { L("Blind"), L("Symmetric"), L("Two-sided"), L("Through all"),
L("Up to face"), L("Up to vertex") })
m_extrude_end->Append(_L(s));
m_extrude_end->SetSelection(0);
eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("End")), 0, wxALIGN_CENTER_VERTICAL);
eform->Add(m_extrude_end, 0, wxEXPAND);
@@ -1931,8 +1923,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
// Order is load-bearing: index maps to BooleanMode (New=0, Add=1, Cut=2, Intersect=3).
// Labels use Onshape wording so the choice reads as the user thinks of it.
m_mode->Append(_L("New body")); // separate coexisting solid
m_mode->Append(_L("Join")); // fuse into the target body (was "Add")
m_mode->Append(_L("Cut")); // subtract from the target body
m_mode->Append(_L_CONTEXT("Join", "Boolean Mode")); // fuse into the target body (was "Add")
m_mode->Append(_L_CONTEXT("Cut", "Boolean Mode")); // subtract from the target body
m_mode->Append(_L("Intersect")); // keep only the overlap
m_mode->SetSelection(0);
eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
@@ -1968,6 +1960,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_face_group = make_combo(m_cards);
m_face_group->Append(_L("Top")); // index 0 -> FaceGroup::Top
m_face_group->Append(_L("Bottom")); // 1 -> Bottom
// TRN Fillet/Chamfer edge group used when no edge is picked: the side edges, as opposed to the top and bottom ones.
m_face_group->Append(_L("Lateral")); // 2 -> Lateral
m_face_group->Append(_L("All")); // 3 -> All
m_face_group->SetSelection(3);
@@ -1988,9 +1981,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
auto* hform = two_col_form();
m_hole_plane = make_combo(m_cards);
m_hole_plane->Append(_L("XY"));
m_hole_plane->Append(_L("XZ"));
m_hole_plane->Append(_L("YZ"));
m_hole_plane->Append(_L_CONTEXT("XY", "Axis"));
m_hole_plane->Append(_L_CONTEXT("XZ", "Axis"));
m_hole_plane->Append(_L_CONTEXT("YZ", "Axis"));
m_hole_plane->SetSelection(0);
// Picking a plane here is an explicit choice: drop any on-face hijack (a stale face pick
// could keep m_hole_on_face true, so the dropdown was ignored and the hole drilled on the
@@ -2041,9 +2034,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
auto* tform = two_col_form();
m_thread_plane = make_combo(m_cards);
m_thread_plane->Append(_L("XY"));
m_thread_plane->Append(_L("XZ"));
m_thread_plane->Append(_L("YZ"));
m_thread_plane->Append(_L_CONTEXT("XY", "Axis"));
m_thread_plane->Append(_L_CONTEXT("XZ", "Axis"));
m_thread_plane->Append(_L_CONTEXT("YZ", "Axis"));
m_thread_plane->SetSelection(0);
tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Thread plane")), 0, wxALIGN_CENTER_VERTICAL);
tform->Add(m_thread_plane, 0, wxEXPAND);
@@ -2129,8 +2122,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_revolve_mode = make_combo(m_cards);
m_revolve_mode->Append(_L("New body")); // same four words as Extrude
m_revolve_mode->Append(_L("Join"));
m_revolve_mode->Append(_L("Cut"));
m_revolve_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
m_revolve_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
m_revolve_mode->Append(_L("Intersect"));
m_revolve_mode->SetSelection(0);
rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
@@ -2159,8 +2152,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_sweep_mode = make_combo(m_cards);
m_sweep_mode->Append(_L("New body")); // same four words as Extrude
m_sweep_mode->Append(_L("Join"));
m_sweep_mode->Append(_L("Cut"));
m_sweep_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
m_sweep_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
m_sweep_mode->Append(_L("Intersect"));
m_sweep_mode->SetSelection(0);
sform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
@@ -2172,14 +2165,14 @@ DesignPanel::DesignPanel(wxWindow* parent)
// --- Pattern (replicate the target body: linear or circular) ---
m_box_pattern = new wxBoxSizer(wxVERTICAL);
m_box_pattern->Add(card_header(m_cards, "design_pattern", _L("Pattern"), m_hdr_pattern), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_pattern->Add(card_header(m_cards, "design_pattern", _L_CONTEXT("Pattern", "Design"), m_hdr_pattern), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_pattern->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
{
auto* pform = two_col_form();
m_pattern_type = make_combo(m_cards);
m_pattern_type->Append(_L("Linear"));
m_pattern_type->Append(_L("Circular"));
m_pattern_type->Append(_L_CONTEXT("Linear", "Design"));
m_pattern_type->Append(_L_CONTEXT("Circular", "Design"));
m_pattern_type->SetSelection(0);
pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Type")), 0, wxALIGN_CENTER_VERTICAL);
pform->Add(m_pattern_type, 0, wxEXPAND);
@@ -2215,7 +2208,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
auto* bform = two_col_form();
m_bool_op = make_combo(m_cards);
m_bool_op->Append(_L("Join")); // the Extrude result word; the offer row says the same
m_bool_op->Append(_L_CONTEXT("Join", "Boolean Mode")); // the Extrude result word; the offer row says the same
m_bool_op->Append(_L("Subtract"));
m_bool_op->Append(_L("Intersect"));
m_bool_op->SetSelection(0);
@@ -2255,7 +2248,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
// --- Cut (split a body with a plane): parameters only; ✓/✗ live on the ribbon ---
m_box_cut = new wxBoxSizer(wxVERTICAL);
m_box_cut->Add(card_header(m_cards, "design_cut", _L("Cut"), m_hdr_cut), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_cut->Add(card_header(m_cards, "design_cut", _L_CONTEXT("Cut", "Design"), m_hdr_cut), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_cut->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
{
auto* cform = two_col_form();
@@ -2314,6 +2307,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_plane_base = make_combo(m_cards);
populate_plane_choices(m_plane_base); // XY/XZ/YZ + any existing datum planes
// TRN Plane tool: the reference plane (XY, XZ, YZ or a datum) the new plane is built from.
plform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Base")), 0, wxALIGN_CENTER_VERTICAL);
plform->Add(m_plane_base, 0, wxEXPAND);
@@ -2371,8 +2365,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_loft_mode = make_combo(m_cards);
m_loft_mode->Append(_L("New body")); // same four words as Extrude
m_loft_mode->Append(_L("Join"));
m_loft_mode->Append(_L("Cut"));
m_loft_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
m_loft_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
m_loft_mode->Append(_L("Intersect"));
m_loft_mode->SetSelection(0);
lform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
@@ -2515,7 +2509,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
sform->Add(m_shell_face_label, 0, wxALIGN_CENTER_VERTICAL);
m_box_shell = new wxBoxSizer(wxVERTICAL);
m_box_shell->Add(card_header(m_cards, "design_shell", _L("Shell"), m_hdr_shell), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_shell->Add(card_header(m_cards, "design_shell", _L_CONTEXT("Shell", "Design"), m_hdr_shell), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_shell->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
m_box_shell->Add(new wxStaticText(m_cards, wxID_ANY,
_L("Pick a solid face to open it, then set the wall thickness.")),
@@ -2564,9 +2558,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Translate Z")), 0, wxALIGN_CENTER_VERTICAL);
xform->Add(spin_frame(m_xf_dz), 0, wxEXPAND);
m_xf_axis = make_combo(m_cards);
m_xf_axis->Append(_L("X"));
m_xf_axis->Append(_L("Y"));
m_xf_axis->Append(_L("Z"));
m_xf_axis->Append(_L_CONTEXT("X", "Axis"));
m_xf_axis->Append(_L_CONTEXT("Y", "Axis"));
m_xf_axis->Append(_L_CONTEXT("Z", "Axis"));
m_xf_axis->SetSelection(2); // default Z
m_xf_axis->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Rotate axis")), 0, wxALIGN_CENTER_VERTICAL);
@@ -2663,7 +2657,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
// --- Rib (thin wall from an open sketch line, fused to a body) ---
m_box_rib = new wxBoxSizer(wxVERTICAL);
m_box_rib->Add(card_header(m_cards, "design_rib", _L("Rib"), m_hdr_rib), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_rib->Add(card_header(m_cards, "design_rib", _L_CONTEXT("Rib", "Design"), m_hdr_rib), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_rib->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
{
auto* rform = two_col_form();
@@ -2699,7 +2693,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
// --- Project (project body edges onto a plane as sketch entities) ---
m_box_project = new wxBoxSizer(wxVERTICAL);
m_box_project->Add(card_header(m_cards, "design_sketch", _L("Project"), m_hdr_project), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_project->Add(card_header(m_cards, "design_sketch", _L_CONTEXT("Project", "Design"), m_hdr_project), 0, wxLEFT | wxRIGHT | wxTOP, 12);
m_box_project->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
{
auto* pform = two_col_form();
@@ -2904,11 +2898,11 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_cs_x = make_spin(m_cards, 0.0, -100000.0, 100000.0);
m_cs_y = make_spin(m_cards, 0.0, -100000.0, 100000.0);
m_cs_z = make_spin(m_cards, 0.0, -100000.0, 100000.0);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("X", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(spin_frame(m_cs_x), 0, wxEXPAND);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Y")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Y", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(spin_frame(m_cs_y), 0, wxEXPAND);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Z")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Z", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(spin_frame(m_cs_z), 0, wxEXPAND);
auto cs_pick = [&](const wxString& label, ::Button*& btn, wxStaticText*& lbl, CoordSysPick target) {
@@ -2937,11 +2931,11 @@ DesignPanel::DesignPanel(wxWindow* parent)
m_cs_hx = make_spin(m_cards, 1.0, -100000.0, 100000.0);
m_cs_hy = make_spin(m_cards, 0.0, -100000.0, 100000.0);
m_cs_hz = make_spin(m_cards, 0.0, -100000.0, 100000.0);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X hint X")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction X")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(spin_frame(m_cs_hx), 0, wxEXPAND);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X hint Y")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction Y")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(spin_frame(m_cs_hy), 0, wxEXPAND);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X hint Z")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction Z")), 0, wxALIGN_CENTER_VERTICAL);
csform->Add(spin_frame(m_cs_hz), 0, wxEXPAND);
m_box_coordsys->Add(csform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
@@ -3190,7 +3184,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
// one click away on a row the user has just selected — the message points at it instead
// of describing a problem with no way out.
if (const std::string* why = (sel >= 0) ? mate_conflict_reason(sel) : nullptr) {
set_status(StatusKind::Error, wxString::FromUTF8(*why) + _L(" — the eye suppresses this mate"));
set_status(StatusKind::Error, wxString::Format(_L("%s. Click the eye icon to suppress this mate."), wxString::FromUTF8(*why)));
} else if (sel >= 0 && sel < int(m_doc.features.size())) {
// Name the two gestures the row supports, because neither is visible on it.
set_status(StatusKind::Info, wxString::Format(
@@ -3882,7 +3876,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
if (got && m_viewport) m_viewport->set_escalate_on_repick(true);
}
const int nb = int(m_doc.bodies.size());
const wxString bodytag = (nb > 1) ? wxString::Format(_L("Body %d "), body + 1) : wxString();
const bool bodytag = nb > 1;
// Each sub-element line ends by naming the NEXT click (gem). Escalation to the
// whole body is a gesture nothing on screen would otherwise reveal, and the status line
// is the only surface that can teach it at the moment it applies. It REPLACES the old
@@ -3894,13 +3888,22 @@ DesignPanel::DesignPanel(wxWindow* parent)
// into the panel. Say "what applies to it", never "verbs" — that is this codebase's
// word for a tool-offer entry, not a word the drawing office uses, and the plane and
// bodies-list lines already say it the right way.
set_status(level == 4 ? bodytag + _L("vertex selected — click again for the whole body")
: level == 1 ? bodytag + _L("selected (whole body) — right-click for what applies to it")
: level == 2 ? bodytag + wxString::Format(_L("face %d selected — right-click to push/pull it, or click again for the whole body"), face)
: level == 3 && m_sel_solid_edges.size() > 1
? bodytag + wxString::Format(_L_PLURAL("%zu edge selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all", "%zu edges selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all", m_sel_solid_edges.size()), m_sel_solid_edges.size())
: level == 3 ? bodytag + wxString::Format(_L("edge %d selected — Shift+click to add edges, or click again for the whole body"), edge)
: _L("Nothing selected"));
set_status(level == 4 ? (bodytag ? wxString::Format(_L("Body %d: vertex selected — click again for the whole body"), body + 1)
: _L("Vertex selected — click again for the whole body"))
: level == 1 ? (bodytag ? wxString::Format(_L("Body %d selected (whole body) — right-click for what applies to it"), body + 1)
: _L("Whole body selected — right-click for what applies to it"))
: level == 2 ? (bodytag ? wxString::Format(_L("Body %d: face %d selected — right-click to push/pull it, or click again for the whole body"), body + 1, face)
: wxString::Format(_L("Face %d selected — right-click to push/pull it, or click again for the whole body"), face))
: level == 3 && m_sel_solid_edges.size() > 1
? (bodytag ? wxString::Format(_L_PLURAL("Body %d: %zu edge selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
"Body %d: %zu edges selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
unsigned(m_sel_solid_edges.size())), body + 1, m_sel_solid_edges.size())
: wxString::Format(_L_PLURAL("%zu edge selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
"%zu edges selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
unsigned(m_sel_solid_edges.size())), m_sel_solid_edges.size()))
: level == 3 ? (bodytag ? wxString::Format(_L("Body %d: edge %d selected — Shift+click to add edges, or click again for the whole body"), body + 1, edge)
: wxString::Format(_L("Edge %d selected — Shift+click to add edges, or click again for the whole body"), edge))
: _L("Nothing selected"));
m_status->Refresh();
});
// A click on nothing drops a list row as it drops a pick. Not while a card is open: the
@@ -3995,7 +3998,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
if (m_viewport && m_viewport->is_sketching())
m_viewport->set_sketch_plane(plane_from_choice(m_ref_plane));
}
const char* nm = (base == 0) ? "XY" : (base == 1) ? "XZ" : (base == 2) ? "YZ" : "datum";
const wxString nm = ref_plane_name(base);
// Both halves named the TOOLBAR, which no longer carries either button: the tools
// moved to the offer. Name the gesture that actually works in each mode, and say
// what a plain click does, since the two are easy to confuse on a plane.
@@ -4036,10 +4039,12 @@ DesignPanel::DesignPanel(wxWindow* parent)
if (m_xf_angle) m_xf_angle->SetValue(sign * aa.angle() * 180.0 / M_PI);
}
const int nb = int(m_doc.bodies.size());
const wxString tag = (nb > 1) ? wxString::Format(_L("Body %d "), body + 1) : wxString();
const Vec3d t = xform.translation();
set_status(StatusKind::Info, tag + wxString::Format(_L("placed (%.1f, %.1f, %.1f) mm — drag arrows to move, rings to rotate"),
t.x(), t.y(), t.z()));
set_status(StatusKind::Info, nb > 1
? wxString::Format(_L("Body %d placed at (%.1f, %.1f, %.1f) mm — drag arrows to move, rings to rotate"),
body + 1, t.x(), t.y(), t.z())
: wxString::Format(_L("Placed at (%.1f, %.1f, %.1f) mm — drag arrows to move, rings to rotate"),
t.x(), t.y(), t.z()));
});
// Fillet/Chamfer radius gizmo: dragging (or editing) the edge-anchored arrow writes the
@@ -4126,7 +4131,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
set_status(error ? StatusKind::Error : StatusKind::Info, wxString::FromUTF8(msg));
});
m_viewport->set_on_sketch_exit_refused([this]() {
set_status(StatusKind::Info, _L("Sketch kept — Finish to commit it, Cancel to discard"));
set_status(StatusKind::Info, _L("Sketch kept — Confirm to commit it, Cancel to discard"));
});
// Ctrl+Z / Ctrl+Shift+Z (Ctrl+Y) from the viewport → feature-history undo/redo.
@@ -4444,7 +4449,7 @@ void DesignPanel::apply_dof_status(int dof, bool ok, bool has_constraints)
m_dof_status->SetLabel(_L("✓ Fully constrained"));
} else if (dof > 0) {
m_dof_status->SetForegroundColour(dp_ctl_text());
m_dof_status->SetLabel(wxString::Format(_L("%d degrees of freedom"), dof));
m_dof_status->SetLabel(wxString::Format(_L_PLURAL("%d degree of freedom", "%d degrees of freedom", dof), dof));
} else {
m_dof_status->SetLabel(wxString());
}
@@ -4521,7 +4526,7 @@ void DesignPanel::set_ui_mode(UiMode m)
if (sketching && m_sketch_banner_txt != nullptr)
m_sketch_banner_txt->SetLabel(
wxString::Format(_L("Editing: Sketch %d · N = look normal to the plane · "
"Finish or Cancel in the toolbar"),
"Confirm or Cancel in the toolbar"),
m_feature_counter + 1));
m_sketch_banner->Show(sketching);
m_sketch_banner->GetParent()->Layout();
@@ -4778,7 +4783,7 @@ void DesignPanel::text_dialog_done(bool accepted)
void DesignPanel::on_import_svg()
{
wxFileDialog dlg(this, _L("Import SVG"), wxEmptyString, wxEmptyString,
"SVG files (*.svg)|*.svg|All files|*.*",
_L("SVG files (*.svg)|*.svg|All files|*.*"),
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg.ShowModal() != wxID_OK)
return;
@@ -4890,7 +4895,7 @@ bool DesignPanel::recompute_guarded(const wxString& message)
void DesignPanel::on_import_step()
{
wxFileDialog dlg(this, _L("Import STEP"), wxEmptyString, wxEmptyString,
"STEP files (*.step;*.stp)|*.step;*.stp|All files|*.*",
_L("STEP files (*.step;*.stp)|*.step;*.stp|All files|*.*"),
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg.ShowModal() != wxID_OK)
return;
@@ -4938,7 +4943,9 @@ void DesignPanel::on_import_step()
set_tree_selection(int(m_doc.features.size()) - 1);
set_status_ok(); // canonical post-recompute viewport/pick/parts refresh
set_status(StatusKind::Info, wxString::Format(
_L("Imported %d solid(s) — pick a face or edge, then Fillet / Cut / Shell to modify"),
_L_PLURAL("Imported %d solid — pick a face or edge, then Fillet / Cut / Shell to modify",
"Imported %d solids — pick a face or edge, then Fillet / Cut / Shell to modify",
unsigned(solids.size())),
int(solids.size())));
}
@@ -4949,7 +4956,7 @@ void DesignPanel::on_import_step()
void DesignPanel::on_import_mesh()
{
wxFileDialog dlg(this, _L("Import mesh"), wxEmptyString, wxEmptyString,
"Mesh files (*.stl;*.obj)|*.stl;*.obj|All files|*.*",
_L("Mesh files (*.stl;*.obj)|*.stl;*.obj|All files|*.*"),
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg.ShowModal() != wxID_OK)
return;
@@ -5032,8 +5039,8 @@ void DesignPanel::on_import_mesh()
stats.kept_tris, stats.faces_final, stats.volume));
} else {
set_status(StatusKind::Warning, wxString::Format(
_L("Imported as an open shell (not watertight): %d boundary edge(s), %d non-manifold "
"edge(s) — %d triangles → %d faces. The source mesh has holes or duplicated "
_L("Imported as an open shell (not watertight). Boundary edges: %d, non-manifold "
"edges: %d, triangles: %d → faces: %d. The source mesh has holes or duplicated "
"geometry; boolean features may fail on it"),
stats.boundary_edges, stats.nonmanifold_edges, stats.kept_tris, stats.faces_final));
}
@@ -5144,7 +5151,7 @@ void DesignPanel::cancel_insert()
set_ui_mode(UiMode::Feature);
sync_sketch_display();
refresh_tree();
set_status(StatusKind::Info, _L("Insert cancelled"));
set_status(StatusKind::Info, _L("Insert canceled"));
}
void DesignPanel::on_transform_imported(int feat_idx)
@@ -5171,7 +5178,7 @@ void DesignPanel::on_add_sketch()
m_doc.add_sketch(shape, plane, m_width->GetValue(), m_height->GetValue(),
m_radius->GetValue(), feature_name(_L("Sketch")));
m_doc.recompute(); // a lone sketch yields an empty body; that is expected
set_status(StatusKind::Info, wxString::Format(_L("Sketch added on %s — select it, then right-click to Extrude"), where));
set_status(StatusKind::Info, wxString::Format(_L("Sketch added (plane: %s) — select it, then right-click to Extrude"), where));
refresh_tree();
}
@@ -5882,7 +5889,8 @@ void DesignPanel::on_check_interference()
body_name(sorted[k].body_b), sorted[k].volume);
if (sorted.size() > shown)
list += wxString::FromUTF8(", …");
set_status(StatusKind::Warning, wxString::Format(_L("%zu overlapping pairs: %s"), sorted.size(), list));
set_status(StatusKind::Warning, wxString::Format(_L_PLURAL("%zu overlapping pair: %s", "%zu overlapping pairs: %s",
unsigned(sorted.size())), sorted.size(), list));
}
// Volume and surface area of the selected body. A report, not a feature: it never checkpoints,
@@ -6082,7 +6090,7 @@ void DesignPanel::populate_plane_choices(ComboBox* c) const
if (!c) return;
const int keep = c->GetSelection();
c->Clear();
c->Append(_L("XY")); c->Append(_L("XZ")); c->Append(_L("YZ"));
c->Append(_L_CONTEXT("XY", "Axis")); c->Append(_L_CONTEXT("XZ", "Axis")); c->Append(_L_CONTEXT("YZ", "Axis"));
for (const auto& dp : m_doc.resolve_datum_planes())
c->Append(wxString::FromUTF8(dp.first));
c->SetSelection((keep >= 0 && keep < int(c->GetCount())) ? keep : 0);
@@ -6131,8 +6139,8 @@ SketchPlane DesignPanel::sketch_plane_from_selection(wxString& what) const
const int fi = (m_sel_solid_face >= 0 && m_sel_solid_body >= 0) ? m_sel_solid_face : m_pick_face;
if (fb >= 0 && fi >= 0 && m_doc.plane_of_face(fb, fi, p)) {
what = (m_doc.bodies.size() > 1)
? wxString::Format(_L("the picked face of Body %d"), fb + 1)
: _L("the picked face");
? wxString::Format(_L("Picked face (Body %d)"), fb + 1)
: _L("Picked face");
return p;
}
what = ref_plane_name(m_ref_plane);
@@ -6150,8 +6158,8 @@ bool DesignPanel::sketch_plane_target(wxString& what) const
SketchPlane p;
if (fb >= 0 && fi >= 0 && m_doc.plane_of_face(fb, fi, p)) {
what = (m_doc.bodies.size() > 1)
? wxString::Format(_L("the picked face of Body %d"), fb + 1)
: _L("the picked face");
? wxString::Format(_L("Picked face (Body %d)"), fb + 1)
: _L("Picked face");
return true;
}
if (m_plane_picked) { what = ref_plane_name(m_ref_plane); return true; }
@@ -6377,8 +6385,10 @@ static wxString sketch_step_prompt(DesignSketchTool::Mode m, int step, int picks
switch (m) {
case Mode::Select:
return picks > 0
? wxString::Format(_L("%d selected · Del removes them · Shift-click adds · "
"double-click takes the whole loop"), picks)
? wxString::Format(_L_PLURAL("%d selected · Del removes it · Shift-click adds · "
"double-click takes the whole loop",
"%d selected · Del removes them · Shift-click adds · "
"double-click takes the whole loop", picks), picks)
: _L("Select — click an entity to pick it · drag an endpoint or center to move it · "
"Shift-click adds · Del removes");
case Mode::Constrain:
@@ -6627,8 +6637,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
return (v.accepts & bit) && v.need_bodies <= bodies && v.need_sketches <= sketches
&& (!v.need_sheet || sheet);
};
// Names and reasons live in the generated table as plain literals; they are the same strings
// the toolbar already ships, so the catalogue already carries their translations.
// Row names, verb names, refusals, families and hints are L()-marked in the generated table.
// Scope the lookup to the APPLICATION catalog. A bare wxGetTranslation() searches every
// loaded catalog, wxWidgets' own wxstd included — so on a non-English system the two row
// names that happen to be wx standard strings came back translated while the other six,
@@ -6642,7 +6651,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
return wxGetTranslation(wxString::FromUTF8(s), SLIC3R_APP_KEY);
};
auto label = [&](const OfferVerb& v) {
wxString s = tr(v.name);
wxString s = I18N::translate(v.name, "Design");
if (v.key && *v.key) s += "\t" + wxString::FromUTF8(v.key);
return s;
};
@@ -7630,8 +7639,8 @@ void DesignPanel::flip_section_view()
if (!m_viewport || !m_section_on) return;
m_section_upper = !m_section_upper;
m_viewport->set_section_plane(true, m_section_cut_z, m_section_upper);
set_status(StatusKind::Info, wxString::Format(_L("Section view — showing the %s half"),
m_section_upper ? _L("upper") : _L("lower")));
set_status(StatusKind::Info, m_section_upper ? _L("Section view — showing the upper half")
: _L("Section view — showing the lower half"));
}
void DesignPanel::sync_body_visible()
@@ -8295,8 +8304,10 @@ void DesignPanel::apply_live_constraint(SketchConstraintType type)
// tool auto-selects only the entity it just drew — so after drawing two lines exactly
// one is selected and the button correctly refuses. Say how many are picked, or the
// refusal is indistinguishable from a dead button.
fail(wxString::Format(_L("%s — %d selected. Press Esc for the Select tool, click one "
"line, then Shift- or Ctrl-click the other."),
fail(wxString::Format(_L_PLURAL("%s — %d selected. Press Esc for the Select tool, click one "
"line, then Shift- or Ctrl-click the other.",
"%s — %d selected. Press Esc for the Select tool, click one "
"line, then Shift- or Ctrl-click the other.", unsigned(sel.size())),
constraint_reject_text(plan.reason, type), int(sel.size())));
return;
case ConstraintPlan::Kind::AskValue:
@@ -8364,7 +8375,7 @@ void DesignPanel::refresh_constrain_dof()
m_dof_status->SetLabel(_L("✓ Fully constrained")); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
} else if (r.dof > 0) {
m_dof_status->SetForegroundColour(dp_ctl_text());
m_dof_status->SetLabel(wxString::Format(_L("%d degrees of freedom"), r.dof)); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
m_dof_status->SetLabel(wxString::Format(_L_PLURAL("%d degree of freedom", "%d degrees of freedom", r.dof), r.dof)); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
} else {
m_dof_status->SetLabel(wxString()); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
}
@@ -8398,7 +8409,8 @@ wxString DesignPanel::constraint_label(const SketchEntityConstraintDef& d) const
// an empty string, or a NULL format string that crashes wxString::Format.
wxString s; s << wxUniChar(c) << ei; // avoid %c assert in Unicode build
if (r == SketchPointRole::P1) s += wxString::FromUTF8("·P1");
else if (r == SketchPointRole::Center) s += wxString::FromUTF8("·Ctr");
// TRN Design sketch: abbreviation of "center", appended to a point tag such as "L3·Ctr"
else if (r == SketchPointRole::Center) s += _L("·Ctr");
else if (r == SketchPointRole::P0) s += wxString::FromUTF8("·P0");
return s;
};
@@ -8408,27 +8420,27 @@ wxString DesignPanel::constraint_label(const SketchEntityConstraintDef& d) const
};
switch (d.type) {
case T::Fix: return wxString::Format(_L("Fix %s"), tag(d.ea, d.ra));
case T::Coincident: return two(_L("Coincident"));
case T::Horizontal: return two(_L("Horizontal"));
case T::Vertical: return two(_L("Vertical"));
case T::Coincident: return two(_L_CONTEXT("Coincident", "Sketch Constraint"));
case T::Horizontal: return two(_L_CONTEXT("Horizontal", "Sketch Constraint"));
case T::Vertical: return two(_L_CONTEXT("Vertical", "Sketch Constraint"));
case T::Distance: return wxString::Format("%s = %s", two(_L("Distance")), en_format(d.value));
case T::LockX: return wxString::Format(_L("Lock X %s"), tag(d.ea, d.ra));
case T::LockY: return wxString::Format(_L("Lock Y %s"), tag(d.ea, d.ra));
case T::EqualLength: return two(_L("Equal"));
case T::Parallel: return two(_L("Parallel"));
case T::Perpendicular: return two(_L("Perpendicular"));
case T::Concentric: return two(_L("Concentric"));
case T::Tangent: return two(_L("Tangent"));
case T::Midpoint: return two(_L("Midpoint"));
case T::EqualLength: return two(_L_CONTEXT("Equal", "Sketch Constraint"));
case T::Parallel: return two(_L_CONTEXT("Parallel", "Sketch Constraint"));
case T::Perpendicular: return two(_L_CONTEXT("Perpendicular", "Sketch Constraint"));
case T::Concentric: return two(_L_CONTEXT("Concentric", "Sketch Constraint"));
case T::Tangent: return two(_L_CONTEXT("Tangent", "Sketch Constraint"));
case T::Midpoint: return two(_L_CONTEXT("Midpoint", "Sketch Constraint"));
case T::Symmetric: return wxString::Format(_L("Symmetric %s — %s / %s"),
tag(d.ea, d.ra), tag(d.eb, d.rb), tag(d.ec, d.rc));
case T::SymmetricAboutY: return wxString::Format(_L("Symmetric about Y axis %s — %s"),
tag(d.ea, d.ra), tag(d.eb, d.rb));
case T::SymmetricAboutX: return wxString::Format(_L("Symmetric about X axis %s — %s"),
tag(d.ea, d.ra), tag(d.eb, d.rb));
case T::Angle: return wxString::Format(wxString::FromUTF8("%s = %s°"), two(_L("Angle")), en_format(d.value * 180.0 / M_PI, 1));
case T::Radius: return wxString::Format("%s %s = %s", _L("Radius"), tag(d.ea, d.ra), en_format(d.value));
case T::Diameter: return wxString::Format("%s %s = %s", _L("Diameter"), tag(d.ea, d.ra), en_format(d.value));
case T::Angle: return wxString::Format(wxString::FromUTF8("%s = %s°"), two(_L_CONTEXT("Angle", "Sketch Constraint")), en_format(d.value * 180.0 / M_PI, 1));
case T::Radius: return wxString::Format("%s %s = %s", _L_CONTEXT("Radius", "Sketch Constraint"), tag(d.ea, d.ra), en_format(d.value));
case T::Diameter: return wxString::Format("%s %s = %s", _L_CONTEXT("Diameter", "Sketch Constraint"), tag(d.ea, d.ra), en_format(d.value));
case T::PointOnLine: return two(_L("On line"));
case T::PointOnObject: return two(_L("On edge"));
}
@@ -9702,7 +9714,7 @@ void DesignPanel::load_feature_into_dialog(const CadFeature& f)
wxString s;
for (size_t i = 0; i < m_del_faces.size(); ++i) {
if (i > 0) s += ", ";
s += wxString::Format("Face %d", m_del_faces[i]);
s += wxString::Format(_L("Face %d"), m_del_faces[i]);
}
m_del_face_list->SetLabel(s.empty() ? _L("(none)") : s);
}
@@ -9768,7 +9780,7 @@ void DesignPanel::on_edit_feature()
m_viewport->set_display_sketches({});
m_viewport->edit_sketch(f.entities, f.entity_constraints, f.plane);
}
set_status(StatusKind::Info, _L("Editing sketch — drag a handle or click a quote to edit"));
set_status(StatusKind::Info, _L("Editing sketch — drag a handle or double-click a dimension to edit it"));
} else {
load_feature_into_dialog(f);
open_tool(Tool::Sketch);
@@ -9963,7 +9975,7 @@ void DesignPanel::on_export_step()
return;
}
wxFileDialog dlg(this, _L("Export STEP"), wxEmptyString, "model.step",
"STEP files (*.step;*.stp)|*.step;*.stp",
_L("STEP files (*.step;*.stp)|*.step;*.stp"),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg.ShowModal() != wxID_OK)
return;
@@ -11480,20 +11492,20 @@ void DesignPanel::open_tool(Tool t)
};
switch (t) {
case Tool::Sketch: m_hdr_sketch->SetLabel(title(_L("Sketch"))); break;
case Tool::Extrude: m_hdr_extrude->SetLabel(title(_L("Extrude"))); break;
case Tool::Extrude: m_hdr_extrude->SetLabel(title(_L_CONTEXT("Extrude", "Design"))); break;
case Tool::Dressup: m_hdr_dressup->SetLabel(title(
m_dressup_type->GetSelection() == 0 ? _L("Fillet") : _L("Chamfer"))); break;
case Tool::Hole: m_hdr_hole->SetLabel(title(_L("Hole"))); break;
case Tool::Thread: m_hdr_thread->SetLabel(title(_L("Thread"))); break;
case Tool::Shell: m_hdr_shell->SetLabel(title(_L("Shell"))); break;
case Tool::Shell: m_hdr_shell->SetLabel(title(_L_CONTEXT("Shell", "Design"))); break;
case Tool::Revolve: m_hdr_revolve->SetLabel(title(_L("Revolve"))); break;
case Tool::Sweep: m_hdr_sweep->SetLabel(title(_L("Sweep"))); break;
case Tool::Pattern: m_hdr_pattern->SetLabel(title(_L("Pattern"))); break;
case Tool::Pattern: m_hdr_pattern->SetLabel(title(_L_CONTEXT("Pattern", "Design"))); break;
case Tool::Plane: m_hdr_plane->SetLabel(title(_L("Plane"))); break;
case Tool::Loft: m_hdr_loft->SetLabel(title(_L("Loft"))); break;
case Tool::Draft: m_hdr_draft->SetLabel(title(_L("Draft"))); break;
case Tool::Boolean: m_hdr_boolean->SetLabel(title(_L("Boolean"))); break;
case Tool::Cut: m_hdr_cut->SetLabel(title(_L("Cut"))); break;
case Tool::Cut: m_hdr_cut->SetLabel(title(_L_CONTEXT("Cut", "Design"))); break;
case Tool::Axis: m_hdr_axis->SetLabel(title(_L("Axis"))); break;
case Tool::CoordSys: m_hdr_coordsys->SetLabel(title(_L("Coordinate system"))); break;
case Tool::SurfaceExtrude: m_hdr_surf_extrude->SetLabel(title(_L("Surface Extrude"))); break;
@@ -11505,8 +11517,8 @@ void DesignPanel::open_tool(Tool t)
case Tool::Transform: m_hdr_transform->SetLabel(title(_L("Transform"))); break;
case Tool::Mirror: m_hdr_mirror->SetLabel(title(_L("Mirror"))); break;
case Tool::Thicken: m_hdr_thicken->SetLabel(title(_L("Thicken"))); break;
case Tool::Rib: m_hdr_rib->SetLabel(title(_L("Rib"))); break;
case Tool::Project: m_hdr_project->SetLabel(title(_L("Project"))); break;
case Tool::Rib: m_hdr_rib->SetLabel(title(_L_CONTEXT("Rib", "Design"))); break;
case Tool::Project: m_hdr_project->SetLabel(title(_L_CONTEXT("Project", "Design"))); break;
case Tool::DeleteFace: m_hdr_delete_face->SetLabel(title(_L("Delete Face"))); break;
case Tool::Helix: m_hdr_helix->SetLabel(title(_L("Helix"))); break;
case Tool::Mate: m_hdr_mate->SetLabel(title(_L("Mate"))); break;
@@ -11743,7 +11755,7 @@ void DesignPanel::tool_cancel()
show_move_card(false);
feed_bodies(); // re-render the reverted placement
update_action_bar();
set_status(StatusKind::Info, _L("Move cancelled"));
set_status(StatusKind::Info, _L("Move canceled"));
return;
}
if (m_active == Tool::Insert) { cancel_insert(); return; }
@@ -11873,7 +11885,7 @@ void DesignPanel::escape()
return;
}
if (m_ui_mode == UiMode::Sketch) {
set_status(StatusKind::Info, _L("Sketch kept — Finish to commit it, Cancel to discard"));
set_status(StatusKind::Info, _L("Sketch kept — Confirm to commit it, Cancel to discard"));
}
return;
}
@@ -11956,8 +11968,10 @@ void DesignPanel::do_undo_redo(bool redo)
drop_selection();
reset_edit_state();
after_tree_edit(true); // refresh tree + viewport meshes + status from the restored doc
set_status(StatusKind::Info, wxString::Format(redo ? _L("Redo (%zu more)") : _L("Undo (%zu more)"),
redo ? m_doc.redo_depth() : m_doc.undo_depth()));
const size_t depth = redo ? m_doc.redo_depth() : m_doc.undo_depth();
set_status(StatusKind::Info, wxString::Format(redo ? _L_PLURAL("Redo (%zu more step)", "Redo (%zu more steps)", unsigned(depth))
: _L_PLURAL("Undo (%zu more step)", "Undo (%zu more steps)", unsigned(depth)),
depth));
}
// --- Document variables panel ---------------------------------------------------------
+2 -1
View File
@@ -1725,6 +1725,7 @@ void DesignSketchTool::open_polygon_side_editor(int fi)
if (f.begin < 0 || f.begin >= int(m_entities.size())) return;
const double side = (m_entities[f.begin].p1 - m_entities[f.begin].p0).norm();
const wxPoint px(m_last_mouse_x, m_last_mouse_y);
// TRN Design sketch: length of one side of a regular polygon (inline value editor title)
on_inline_edit(px, side, _u8L("Side"),
[this, fi](double v) { set_polygon_side(fi, v); },
[]() {});
@@ -9844,7 +9845,7 @@ std::string DesignSketchTool::build_readout() const
std::string out = b;
// Tell the user how to end a polyline chain — there's no other affordance for it.
if (m_mode == Mode::Polyline && m_points.size() >= 2)
out += " right-click or double-click to finish, click start to close";
out += " " + _u8L("right-click or double-click to finish, click start to close");
return out;
}
if (!m_active) return std::string();
+5 -4
View File
@@ -2,6 +2,7 @@
#include "CalibUtils.hpp"
#include "GUI.hpp"
#include "I18N.hpp"
#include "format.hpp"
#include "Widgets/Label.hpp"
#include "MsgDialog.hpp"
#include "DeviceCore/DevConfigUtil.h"
@@ -67,13 +68,13 @@ static wxString get_default_name(wxString filament_name, CalibMode mode){
case Slic3r::CalibMode::Calib_PA_Tower:
break;
case Slic3r::CalibMode::Calib_Flow_Rate:
filament_name += " Flow Rate Calibrated";
filament_name = format_wxstr(_L("%1% Flow Rate Calibrated"), filament_name);
break;
case Slic3r::CalibMode::Calib_Temp_Tower:
filament_name += " Temperature Calibrated";
filament_name = format_wxstr(_L("%1% Temperature Calibrated"), filament_name);
break;
case Slic3r::CalibMode::Calib_Vol_speed_Tower:
filament_name += " Max Vol Speed Calibrated";
filament_name = format_wxstr(_L("%1% Max Vol Speed Calibrated"), filament_name);
break;
case Slic3r::CalibMode::Calib_VFA_Tower:
break;
@@ -95,7 +96,7 @@ static wxString get_tray_name_by_tray_id(int tray_id)
{
wxString tray_name;
if (tray_id == VIRTUAL_TRAY_MAIN_ID || tray_id == VIRTUAL_TRAY_DEPUTY_ID) {
tray_name = "Ext";
tray_name = _L("Ext");
}
else {
int ams_id = tray_id / 4;
+5 -1
View File
@@ -777,6 +777,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
bool have_infill = config->option<ConfigOptionPercent>("sparse_infill_density")->value > 0;
// sparse_infill_filament_id uses the same logic as in Print::extruders()
for (auto el : { "sparse_infill_pattern", "infill_combination", "fill_multiline","infill_direction",
"infill_complete_top",
"minimum_sparse_infill_area", "sparse_infill_filament_id","infill_shift_step","sparse_infill_rotate_template","symmetric_infill_y_axis"})
toggle_line(el, have_infill);
@@ -831,6 +832,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_line("symmetric_infill_y_axis", is_zig_zag || is_cross_zag || is_locked_zig);
bool is_3Dhoneycomb = config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value == InfillPattern::ip3DHoneycomb;
toggle_line("infill_complete_top", have_infill && is_3Dhoneycomb);
bool has_spiral_vase = config->opt_bool("spiral_mode");
toggle_line("spiral_mode_smooth", has_spiral_vase);
toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && config->opt_bool("spiral_mode_smooth"));
@@ -840,7 +844,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
bool has_top_shell = has_top_shell_layers && config->option<ConfigOptionPercent>("top_surface_density")->value > 0;
bool has_bottom_shell = config->opt_int("bottom_shell_layers") > 0;
bool has_solid_infill = has_top_shell_layers || has_bottom_shell;
toggle_line("sparse_infill_smooth_factor", is_smoothable_infill_pattern(pattern, config->opt_int("fill_multiline")));
toggle_line("sparse_infill_smooth_factor", have_infill && is_smoothable_infill_pattern(pattern, config->opt_int("fill_multiline")));
toggle_field("top_surface_pattern", has_top_shell);
toggle_field("bottom_surface_pattern", has_bottom_shell);
toggle_field("top_surface_density", has_top_shell_layers);
+4
View File
@@ -53,6 +53,8 @@ wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConf
}
return _L("Undefined");
}
if (val < 0 || val >= int(names.size()))
return _L("Undefined");
return from_u8(_utf8(names[val]));
}
@@ -215,6 +217,8 @@ wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig&
;
}
case coEnums: {
if (!opt_vector || opt_idx >= opt_vector->size())
return _L("Undefined");
return get_string_from_enum(pure_key, config,
pure_key == "top_surface_pattern" ||
pure_key == "bottom_surface_pattern" ||
+9 -9
View File
@@ -1130,7 +1130,7 @@ void PageMaterials::sort_list_data(StringList* list, bool add_All_item, bool mat
for (const auto& item : prusa_profiles)
list->append(item, &const_cast<std::string&>(item.get()));
for (const auto& item : other_profiles)
list->append(item, &const_cast<std::string&>(item.get()));
list->append(item.get() == Materials::UNKNOWN ? _L(item.get()) : from_u8(item.get()), &const_cast<std::string&>(item.get()));
}
void PageMaterials::sort_list_data(PresetList* list, const std::vector<ProfilePrintData>& data)
@@ -1210,13 +1210,13 @@ void PageMaterials::on_activate()
}
const char *PageCustom::default_profile_name = "My Settings";
const char *PageCustom::default_profile_name = L("My Settings");
PageCustom::PageCustom(ConfigWizard *parent)
: ConfigWizardPage(parent, _L("Custom Printer Setup"), _L("Custom Printer"))
{
cb_custom = new wxCheckBox(this, wxID_ANY, _L("Define a custom printer profile"));
tc_profile_name = new wxTextCtrl(this, wxID_ANY, default_profile_name);
tc_profile_name = new wxTextCtrl(this, wxID_ANY, _L(default_profile_name));
auto *label = new wxStaticText(this, wxID_ANY, _L("Custom profile name:"));
wxGetApp().UpdateDarkUI(tc_profile_name);
@@ -1224,7 +1224,7 @@ PageCustom::PageCustom(ConfigWizard *parent)
tc_profile_name->Enable(false);
tc_profile_name->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &evt) {
if (tc_profile_name->GetValue().IsEmpty()) {
if (profile_name_prev.IsEmpty()) { tc_profile_name->SetValue(default_profile_name); }
if (profile_name_prev.IsEmpty()) { tc_profile_name->SetValue(_L(default_profile_name)); }
else { tc_profile_name->SetValue(profile_name_prev); }
} else {
profile_name_prev = tc_profile_name->GetValue();
@@ -1463,7 +1463,7 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent)
auto *default_bed = def_bed.get_default_value<ConfigOptionInts>();
spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0);
append_text(_L("Enter the nozzle_temperature needed for extruding your filament."));
append_text(_L("Enter the nozzle temperature needed for extruding your filament."));
append_text(_L("A rule of thumb is 160 to 230℃ for PLA, and 215 to 250℃ for ABS."));
#endif
@@ -1720,7 +1720,7 @@ void ConfigWizardIndex::msw_rescale()
// Materials
const std::string Materials::UNKNOWN = "(Unknown)";
const std::string Materials::UNKNOWN = L("(Unknown)");
void Materials::push(const Preset *preset)
{
@@ -2188,8 +2188,8 @@ void ConfigWizard::priv::select_default_materials_for_printer_models(Technology
}
printer_names += "\n\n";
std::string message = (technology & T_FFF ?
GUI::format(_L("Following printer profiles has no default filament: %1%Please select one manually."), printer_names) :
GUI::format(_L("Following printer profiles has no default material: %1%Please select one manually."), printer_names));
GUI::format(_L("The following printer profiles have no default filament: %1%Please select one manually."), printer_names) :
GUI::format(_L("The following printer profiles have no default material: %1%Please select one manually."), printer_names));
MessageDialog msg(q, message, _L("Notice"), wxOK);
msg.ShowModal();
}
@@ -2449,7 +2449,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
}
if (!check_unsaved_preset_changes)
if ((check_unsaved_preset_changes = install_bundles.size() > 0))
header = _L_PLURAL("A new vendor was installed and one of its printers will be activated", "New vendors were installed and one of theirs printers will be activated", install_bundles.size());
header = _L_PLURAL("A new vendor was installed and one of its printers will be activated", "New vendors were installed and one of their printers will be activated", install_bundles.size());
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
bool snapshot = true;
+1
View File
@@ -40,6 +40,7 @@
#include "GUI.hpp"
#include "wxExtensions.hpp"
#include "Widgets/Button.hpp"
#include <wx/types.h>
namespace fs = boost::filesystem;
+2 -2
View File
@@ -39,7 +39,7 @@ CrealityDiscoveryDialog::CrealityDiscoveryDialog(wxWindow* parent)
m_list->AppendColumn(_L("Hostname"), wxLIST_FORMAT_LEFT, 14 * em);
m_list->AppendColumn(_L("IP"), wxLIST_FORMAT_LEFT, 14 * em);
auto* dlg_btns = new DialogButtons(this, {"Scan", "OK", "Cancel"}, "", 1 /*left_aligned*/);
auto* dlg_btns = new DialogButtons(this, {L("Scan"), "OK", "Cancel"}, "", 1 /*left_aligned*/);
auto* ok_btn = dlg_btns->GetOK();
ok_btn->SetLabel(_L("Use Selected"));
ok_btn->Disable();
@@ -91,7 +91,7 @@ void CrealityDiscoveryDialog::run_discovery()
if (!h.model_name.empty())
row.model = h.model_name;
else if (h.cfs_capable)
row.model = "(unknown K-series)";
row.model = _u8L("(unknown K-series)");
else
row.model = "Creality";
m_rows.push_back(std::move(row));
+1 -1
View File
@@ -652,7 +652,7 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent)
wxString text = _L("Desktop Integration sets this binary to be searchable by the system.\n\nPress \"Perform\" to proceed.");
if (can_undo)
text += "\nPress \"Undo\" to remove previous integration.";
text += "\n" + _L("Press \"Undo\" to remove previous integration.");
vbox->Add(
new wxStaticText( this, wxID_ANY, text),
+1 -1
View File
@@ -154,7 +154,7 @@ void calib_fail_message(MachineObject* obj, std::string cali_mode, std::string r
} else if (reason == "nozzle_diameter is not matched") {
info = _L("Selected diameter and machine diameter do not match");
} else if (reason == "generate auto filament cali gcode failure") {
info = _L("Failed to generate cali gcode");
info = _L("Failed to generate calibration G-code");
} else {
info = wxString(reason);
}
+1 -1
View File
@@ -4313,7 +4313,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
info = _L("Selected diameter and machine diameter do not match");
}
else if (reason == "generate auto filament cali gcode failure") {
info = _L("Failed to generate cali G-code");
info = _L("Failed to generate calibration G-code");
}
else {
info = reason;
@@ -981,7 +981,7 @@ int ReselectMachineDialog::CaculateSwitcherDistribution(MachineObject* obj, cons
}
if (can.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY)
{
material = "Empty";
material = L("Empty");
}
auto itOK = std::find_if(posOK.begin(), posOK.end(), [&](const trayHelper& tray){
+1 -1
View File
@@ -60,7 +60,7 @@ void wgtMsgPanelItem::CreateGui()
text_sizer->Add(m_text_label, 0, wxALIGN_CENTER_VERTICAL);
if (!m_wiki_url.IsEmpty()) {
m_wiki_link = new wxHyperlinkCtrl(this, wxID_ANY, "Wiki->", m_wiki_url);
m_wiki_link = new wxHyperlinkCtrl(this, wxID_ANY, _L("Wiki->"), m_wiki_url);
m_wiki_link->SetNormalColour(m_colour);
m_wiki_link->SetHoverColour(wxColour(0, 0, 200));
m_wiki_link->SetVisitedColour(*wxBLUE);
+3 -1
View File
@@ -19,8 +19,10 @@
#include "libslic3r/Utils.hpp"
#include <functional>
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoBase.hpp"
#include <wx/arrstr.h>
#include <wx/string.h>
namespace Slic3r {
namespace GUI {
@@ -165,7 +167,7 @@ void Downloader::start_download(const std::string& full_url)
// Orca: show error
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
ntf_mngr->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::ErrorNotificationLevel,
"Could not start download due to malformed URL");
_u8L("Could not start download due to malformed URL"));
return;
}
size_t id = get_next_id();
+2 -2
View File
@@ -161,7 +161,7 @@ void FileGet::priv::get_perform()
}
if (!found) {
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
evt->SetString(GUI::format_wxstr(L"Failed to find suitable filename. Last name: %1%." , (m_dest_folder / final_filename).string()));
evt->SetString(GUI::format_wxstr(_L("Failed to find suitable filename. Last name: %1%."), (m_dest_folder / final_filename).string()));
evt->SetInt(m_id);
m_evt_handler->QueueEvent(evt);
return;
@@ -367,7 +367,7 @@ void FileGet::priv::get_perform()
//TODO: report?
//error_message = GUI::format("Failed to write and move %1% to %2%", tmp_path, dest_path);
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
evt->SetString("Failed to write and move.");
evt->SetString(_L("Failed to write and move."));
evt->SetInt(m_id);
m_evt_handler->QueueEvent(evt);
return;
+3
View File
@@ -78,6 +78,7 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const
m_search_bar->ShowCancelButton(true);
m_search_bar->SetDescriptiveText(_L("Search G-code placeholders"));
m_search_bar->SetForegroundColour(*wxBLACK);
m_search_bar->SetBackgroundColour(*wxWHITE);
wxGetApp().UpdateDarkUI(m_search_bar);
m_search_bar->Bind(wxEVT_SET_FOCUS, [](wxFocusEvent&) {
@@ -90,6 +91,7 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const
param_sizer->Add(m_search_bar, 0, wxEXPAND | wxALL, border);
m_params_list = new ParamsViewCtrl(this, wxDefaultSize);
m_params_list->SetBackgroundColour(*wxWHITE);
m_params_list->SetFont(wxGetApp().code_font());
wxGetApp().UpdateDarkUI(m_params_list);
param_sizer->Add(m_params_list, 1, wxEXPAND | wxALL, border);
@@ -104,6 +106,7 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const
);
m_gcode_editor->SetFont(wxGetApp().code_font());
m_gcode_editor->SetBackgroundColour(*wxWHITE);
m_gcode_editor->SetInsertionPointEnd();
wxGetApp().UpdateDarkUI(m_gcode_editor);
+2 -1
View File
@@ -11,6 +11,7 @@
#include <utility>
#include "GUI_App.hpp"
#include "I18N.hpp"
using json = nlohmann::json;
@@ -165,7 +166,7 @@ wxString FilamentColorCode::GetFilaColorName() const
if (it != m_fila_color_names.end() && !it->second.empty()) { return it->second; }
it = m_fila_color_names.find("en");// retry with English as fallback
return (it != m_fila_color_names.end()) ? it->second : "Unknown";
return (it != m_fila_color_names.end()) ? it->second : _L("Unknown");
}
FilamentColorCode::FilamentColorCode(const wxString& color_code, FilamentColorCodes* owner, FilamentColor&& color, std::unordered_map<wxString, wxString>&& name_map)
+1 -1
View File
@@ -75,7 +75,7 @@ void ExportPresetBundleDialog::on_script_message(const nlohmann::json& j)
OnRequestPresets();
} else if (strCmd == "export_local") {
wxFileDialog dlg(this, _L("Save preset bundle"), "", "export.orca_bundle",
"Orca Preset Bundle (*.orca_bundle)|*.orca_bundle", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
_L("Orca Preset Bundle (*.orca_bundle)|*.orca_bundle"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
wxString path;
wxString name;
if (dlg.ShowModal() == wxID_OK) {
+1
View File
@@ -22,6 +22,7 @@
#include <wx/event.h>
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Widgets/DropDown.hpp"
#include <wx/dvrenderers.h>
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
#include "wx/generic/private/markuptext.h"
#include "wx/generic/private/rowheightcache.h"
+9 -4
View File
@@ -81,6 +81,7 @@
#include "../Utils/ColorSpaceConvert.hpp"
#include "../Utils/NetworkAgentFactory.hpp"
#include <wx/sizer.h>
#ifdef __WXOSX__
#define wxOSX true
#else
@@ -1015,8 +1016,11 @@ void TextCtrl::BUILD() {
if (m_opt.is_code)
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
if(m_opt.multiline){
temp->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#262E30"))); // only effects wxTextCtrl
temp->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
}
temp->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
wxGetApp().UpdateDarkUI(temp);
if (! m_opt.multiline && !wxOSX)
@@ -2176,7 +2180,8 @@ void PrinterAgentChoice::set_value(const std::string& value, bool change_event)
if (match == wxNOT_FOUND)
{
field->SetSelection(wxNOT_FOUND); // nothing shows as selected in the dropdown
field->SetValue(from_u8(value + " (missing)")); // set a value not in the selection (upper display field)
// TRN %1% is the ID of a printer agent that is no longer available
field->SetValue(format_wxstr(_L("%1% (missing)"), value)); // set a value not in the selection (upper display field)
}
else
{
@@ -2505,7 +2510,7 @@ nlohmann::json plugin_overrides_as_json(const std::string& text)
void PluginConfigField::BUILD()
{
m_button = new ::Button(m_parent, _L("Configure"));
m_button = new ::Button(m_parent, _L("Configure") + dots);
// ButtonType::Parameter gives the button the same height as the parameter fields above it.
m_button->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
@@ -2535,7 +2540,7 @@ void PluginConfigField::update_button_label()
const nlohmann::json entries = plugin_overrides_as_json(m_json);
const size_t count = entries.is_array() ? entries.size() : 0;
m_button->SetLabel(count == 0 ? _L("Configure")
m_button->SetLabel(count == 0 ? _L("Configure" + dots)
: wxString::Format(_L("Configure (%d)"), int(count)));
}
+1 -1
View File
@@ -340,7 +340,7 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent,
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
}
main_sizer->Add(bottom_panel, 0, wxEXPAND);
main_sizer->Add(bottom_panel, 0, wxEXPAND | wxTOP, FromDIP(15));
m_ok_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_ok, this);
m_cancel_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_cancel, this);
+14 -9
View File
@@ -454,7 +454,8 @@ void FilamentMapManualPanel::UpdateNozzleCountDisplay()
if (m_right_panel->IsUseSeparation()) {
int standard_count = getExtruderNozzleCount(preset_bundle, 1, NozzleVolumeType::nvtStandard);
int highflow_count = getExtruderNozzleCount(preset_bundle, 1, NozzleVolumeType::nvtHighFlow);
wxString right_title = _L("Right Nozzle") + wxString::Format("(Std: %d, HF: %d)", standard_count, highflow_count);
// TRN Nozzle counts: Std = standard flow, HF = high flow
wxString right_title = _L("Right Nozzle") + wxString::Format(_L("(Std: %d, HF: %d)"), standard_count, highflow_count);
m_right_panel->UpdateLabel(right_title);
} else {
int right_count = getExtruderNozzleCountTotal(preset_bundle, 1);
@@ -524,22 +525,26 @@ GUI::FilamentMapBtnPanel::FilamentMapBtnPanel(wxWindow *parent, const wxString &
m_btn = new wxBitmapButton(this, wxID_ANY, icon_enabled, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
m_btn->SetBackgroundStyle(wxBG_STYLE_PAINT);
auto icon_sizer = new wxBoxSizer(wxVERTICAL);
icon_sizer->Add(m_btn , 0, wxLEFT, horizontal_margin);
m_label = new wxStaticText(this, wxID_ANY, label);
m_label->SetFont(Label::Head_14);
m_label->SetForegroundColour(TextNormalBlackColor);
auto label_sizer = new wxBoxSizer(wxHORIZONTAL);
label_sizer->AddStretchSpacer();
label_sizer->Add(m_btn, 0, wxEXPAND | wxLEFT, FromDIP(1));
label_sizer->Add(m_label, 0, wxEXPAND| wxALL, FromDIP(3));
label_sizer->AddStretchSpacer();
auto label_sizer = new wxBoxSizer(wxVERTICAL);
label_sizer->Add(m_label, 0, wxLEFT, horizontal_margin);
m_disable_tip = new Label(this, _L("(Sync with printer)"));
m_disable_tip->SetFont(Label::Body_12);
label_sizer->AddSpacer(FromDIP(2));
label_sizer->Add(m_disable_tip, 0, wxLEFT, horizontal_margin);
sizer->AddSpacer(FromDIP(32));
sizer->AddSpacer(FromDIP(15));
sizer->Add(icon_sizer, 0, wxEXPAND);
sizer->AddSpacer(FromDIP(10));
sizer->Add(label_sizer, 0, wxEXPAND);
sizer->Add(m_disable_tip, 0, wxALIGN_CENTER);
sizer->AddSpacer(FromDIP(3));
sizer->AddSpacer(FromDIP(6));
auto detail_sizer = new wxBoxSizer(wxHORIZONTAL);
m_detail = new Label(this, detail);
+1
View File
@@ -34,6 +34,7 @@
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <cstddef>
#ifdef _WIN32
#include <windows.h>
+1 -1
View File
@@ -209,7 +209,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar
m_avc = new ArchiveViewCtrl(this, wxSize(45 * em, 30 * em));
wxDataViewColumn* toggle_column = m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);
m_avc->AppendTextColumn("filename", 1);
m_avc->AppendTextColumn(_L("filename"), 1);
std::vector<std::shared_ptr<ArchiveViewNode>> stack;
+8 -1
View File
@@ -675,7 +675,14 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(9.f, 1.f) * m_scale);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, 0.f));
ImVec4 scroll_col = ImVec4(0.77f, 0.77f, 0.77f, m_is_dark ? .6f : 1.0f); // same color with sliced plates toolbar scrollbar
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImVec4(0.f, 0.f, 0.f, 0.f)); // ORCA using background color with opacity creates a second color. This prevents secondary color
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, scroll_col);
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, scroll_col);
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, scroll_col);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered , style.Colors[ImGuiCol_TableHeaderBg]);
const int hover_id = m_actual_speed_imgui_widget.plot("##ActualSpeedProfile", { -1.f, plot_height});
const ImGuiTableFlags table_flags = ImGuiTableFlags_Borders | (needs_scroll ? ImGuiTableFlags_ScrollY : 0);
if (ImGui::BeginTable("ToolPositionTable", 2, table_flags, ImVec2(0.0f, needs_scroll ? table_view_h : 0.0f))) {
@@ -703,7 +710,7 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
ImGui::EndTable();
}
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(1);
ImGui::PopStyleColor(5);
imgui.end();
}
+7 -6
View File
@@ -6322,9 +6322,9 @@ void GLCanvas3D::_render_3d_navigator()
style.Colors[ImGuizmo::COLOR::DIRECTION_Z] = ImGuiWrapper::to_ImVec4(ColorRGBA::X());
style.Colors[ImGuizmo::COLOR::TEXT] = m_is_dark ? ImVec4(224 / 255.f, 224 / 255.f, 224 / 255.f, 1.f) : ImVec4(.2f, .2f, .2f, 1.0f);
style.Colors[ImGuizmo::COLOR::FACE] = m_is_dark ? ImVec4(0.23f, 0.23f, 0.23f, 1.f) : ImVec4(0.77f, 0.77f, 0.77f, 1);
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_X], "Y"); // ORCA use uppercase to match text on tranform widgets
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Y], "Z"); // ORCA use uppercase to match text on tranform widgets
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Z], "X"); // ORCA use uppercase to match text on tranform widgets
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_X], _u8L_CONTEXT("Y", "Axis").c_str()); // ORCA use uppercase to match text on tranform widgets
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Y], _u8L_CONTEXT("Z", "Axis").c_str()); // ORCA use uppercase to match text on tranform widgets
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Z], _u8L_CONTEXT("X", "Axis").c_str()); // ORCA use uppercase to match text on tranform widgets
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_FRONT], _u8L_CONTEXT("Front", "Camera View").c_str());
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_BACK], _u8L_CONTEXT("Back", "Camera View").c_str());
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_TOP], _u8L_CONTEXT("Top", "Camera View").c_str());
@@ -9627,7 +9627,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
int item_count = m_sel_plate_toolbar.m_items.size() + (m_sel_plate_toolbar.show_stats_item ? 1 : 0);
float window_height_calc = (item_count * (button_height + (margin_size + button_margin) * 2.0f) + (item_count - 1) * ImGui::GetStyle().ItemSpacing.y + ImGui::GetStyle().WindowPadding.y * 2.0f);
bool show_scroll = m_sel_plate_toolbar.is_display_scrollbar && (window_height_calc > window_height_max);
float scrollbar_size = 10.0f * f_scale ;
float scrollbar_size = 14.0f * f_scale;
float window_height = std::min(window_height_calc, window_height_max);
float window_width = button_width + (margin_size + button_margin + ImGui::GetStyle().WindowPadding.x) * 2 + (show_scroll ? scrollbar_size : 0);
@@ -9653,7 +9653,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, scrollbar_size);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, show_scroll ? (4.0f * f_scale) : (button_radius + margin_size + frame_padding + ImGui::GetStyle().WindowPadding.x));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, show_scroll ? (6.f * f_scale) : (button_radius + margin_size + frame_padding + ImGui::GetStyle().WindowPadding.x));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, button_radius + margin_size);
imgui.set_next_window_pos(canvas_w * 0 + 5.0f * f_scale, canvas_h * 0 + y_offset, ImGuiCond_Always, 0, 0); // ORCA Add slight gap on left edge so toolbar looks like floating and it creates separation with sidebar
@@ -10132,7 +10132,7 @@ void GLCanvas3D::_render_canvas_toolbar()
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding , ImVec2(4.f, 10.f) * sc);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing , ImVec2(0.f, 8.f ) * sc);
if (ImGui::BeginPopup("CanvasToolbarMenu")) {
if (ImGui::BeginPopup("CanvasToolbarMenu", ImGuiWindowFlags_NoMove)) {
ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
Plater* p = wxGetApp().plater();
@@ -10190,6 +10190,7 @@ void GLCanvas3D::_render_canvas_toolbar()
[this, p]{p->toggle_show_wireframe(); m_dirty = true;}
);
// TRN View toggle that draws models see-through, so hidden parts show.
create_menu_item( _utf8(L("X-Ray")),
m_canvas_type != ECanvasType::CanvasPreview, // not work on preview
p->is_show_xray(),
+7 -1
View File
@@ -255,6 +255,11 @@
#include "slic3r/GUI/wxExtensions.hpp"
#include "slic3r/plugin/host/PluginPages.hpp"
#include <wx/defs.h>
#include "slic3r/GUI/Widgets/WebView.hpp"
#include <cwchar>
#include <wx/dataview.h>
#include <wx/itemattr.h>
#include <wx/version.h>
//#ifdef WIN32
//#include "BaseException.h"
@@ -283,6 +288,7 @@ typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS2)(
#endif
#ifdef _WIN32
#include <boost/dll/runtime_symbol_info.hpp>
#include <direct.h>
#endif
#ifdef WIN32
@@ -8242,7 +8248,7 @@ bool GUI_App::load_language(wxString language, bool initial)
message += _L("\nYou may need to reconfigure the missing locales, likely by running the \"locale-gen\" and \"dpkg-reconfigure locales\" commands.\n");
#endif
if (initial)
message + "\n\nApplication will close.";
message += "\n\n" + _L("Application will close.");
wxMessageBox(message, _L("Orca Slicer - Switching language failed"), wxOK | wxICON_ERROR);
if (initial)
std::exit(EXIT_FAILURE);
+26 -27
View File
@@ -41,19 +41,20 @@ namespace GUI
ObjectLayers::ObjectLayers(wxWindow* parent) :
OG_Settings(parent, true)
{
m_grid_sizer = new wxFlexGridSizer(5, 0, wxGetApp().em_unit()); // Title, Min Z, "to", Max Z, unit & buttons sizer
m_grid_sizer = new wxFlexGridSizer(5, parent ? parent->FromDIP(2) : 2, wxGetApp().em_unit()); // Title, Min Z, "to", Max Z, buttons sizer
m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
m_grid_sizer->AddGrowableCol(1);
m_grid_sizer->AddGrowableCol(3);
m_og->activate();
m_og->sizer->Clear(true);
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 5);
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, parent ? parent->FromDIP(2) : 2);
if (auto stb = dynamic_cast<LabeledStaticBox*>(m_og->stb))
stb->SetCornerRadius(0);
m_bmp_delete = ScalableBitmap(parent, "delete");
m_bmp_add = ScalableBitmap(parent, "add");
m_bmp_layer = ScalableBitmap(parent, "height_range_layer");
}
void ObjectLayers::select_editor(LayerRangeEditor* editor, const bool is_last_edited_range)
@@ -95,10 +96,14 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
};
// Add text
auto head_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("Height Range"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
auto title_sizer = new wxBoxSizer(wxHORIZONTAL);
auto head_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("Range"));
head_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
head_text->SetFont(wxGetApp().normal_font());
m_grid_sizer->Add(head_text, 0, wxALIGN_CENTER_VERTICAL);
auto icon = new wxStaticBitmap(m_og->ctrl_parent(), wxID_ANY, m_bmp_layer.bmp());
title_sizer->Add(icon, 0, wxALIGN_CENTER_VERTICAL);
title_sizer->Add(head_text, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, m_og->ctrl_parent()->FromDIP(5));
m_grid_sizer->Add(title_sizer, 0, wxALIGN_CENTER_VERTICAL);
// Add control for the "Min Z"
@@ -126,7 +131,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
m_grid_sizer->Add(editor, 1, wxEXPAND);
auto middle_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("to"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
auto middle_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, "-");
middle_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
middle_text->SetFont(wxGetApp().normal_font());
m_grid_sizer->Add(middle_text, 0, wxALIGN_CENTER_VERTICAL);
@@ -156,11 +161,6 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
m_grid_sizer->Add(editor, 1, wxEXPAND);
auto sizer2 = new wxBoxSizer(wxHORIZONTAL);
auto unit_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("mm"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
unit_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
unit_text->SetFont(wxGetApp().normal_font());
sizer2->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL);
m_grid_sizer->Add(sizer2, 0, wxALIGN_CENTER_VERTICAL);
// BBS
@@ -205,8 +205,9 @@ void ObjectLayers::create_layers_list()
auto sizer = create_layer(range, del_btn, add_btn);
auto b_sizer = new wxBoxSizer(wxHORIZONTAL);
b_sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
b_sizer->Add(add_btn);
b_sizer->Add(del_btn, 0, wxLEFT, m_og->ctrl_parent()->FromDIP(5));
b_sizer->AddSpacer(m_og->ctrl_parent()->FromDIP(15));
b_sizer->Add(add_btn, 0, wxRIGHT, m_og->ctrl_parent()->FromDIP(5));
sizer->Add(b_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, m_parent->FromDIP(1)); // aligns +/- buttons vertically since we got 1px gap on bottom of icons
del_btn->Bind(wxEVT_BUTTON, [del_btn](wxEvent &) {
@@ -277,6 +278,7 @@ void ObjectLayers::msw_rescale()
{
m_bmp_delete.msw_rescale();
m_bmp_add.msw_rescale();
m_bmp_layer.msw_rescale();
m_grid_sizer->SetHGap(wxGetApp().em_unit());
@@ -367,20 +369,17 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
m_valid_value(value),
m_type(type),
m_set_focus_data(set_focus_data_fn),
wxTextCtrl(parent->m_og->ctrl_parent(), wxID_ANY, value, wxDefaultPosition,
wxSize(em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER
#ifdef _WIN32
| wxBORDER_SIMPLE
#endif
)
TextInput(parent->m_og->ctrl_parent(), value, _L("mm"), "", wxDefaultPosition, wxSize(em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
{
this->SetFont(wxGetApp().normal_font());
wxGetApp().UpdateDarkUI(this);
wxTextCtrl* ctrl = GetTextCtrl();
// Reset m_enter_pressed flag to _false_, when value is editing
this->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, this->GetId());
ctrl->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, ctrl->GetId());
this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
ctrl->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxCommandEvent& e)
{
m_enter_pressed = true;
// If LayersList wasn't updated/recreated, we can call wxEVT_KILL_FOCUS.Skip()
@@ -395,9 +394,9 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
SetValue(m_valid_value);
m_call_kill_focus = true;
}
}, this->GetId());
}, ctrl->GetId());
this->Bind(wxEVT_KILL_FOCUS, [this, edit_fn](wxFocusEvent& e)
ctrl->Bind(wxEVT_KILL_FOCUS, [this, edit_fn](wxFocusEvent& e)
{
if (!m_enter_pressed) {
#ifndef __WXGTK__
@@ -426,14 +425,14 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
m_call_kill_focus = false;
e.Skip();
}
}, this->GetId());
}, ctrl->GetId());
this->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
ctrl->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
{
set_focus_data();
parent->update_scene_from_editor_selection();
e.Skip();
}, this->GetId());
}, ctrl->GetId());
#ifdef __WXGTK__ // Workaround! To take information about selectable range
this->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e)
@@ -447,7 +446,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
{
// select all text using Ctrl+A
if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
this->SetSelection(-1, -1); //select all
GetTextCtrl()->SetSelection(-1, -1); //select all
event.Skip();
}));
}
@@ -477,7 +476,7 @@ coordf_t LayerRangeEditor::get_value()
void LayerRangeEditor::msw_rescale()
{
SetMinSize(wxSize(wxGetApp().em_unit(), wxDefaultCoord));
Rescale();
}
} //namespace GUI
+6 -1
View File
@@ -9,6 +9,8 @@
#include <wx/sizer.h>
#include <wx/event.h>
#include <slic3r/GUI/Widgets/TextInput.hpp>
#ifdef __WXOSX__
#include "libslic3r/PrintConfig.hpp"
#endif
@@ -34,7 +36,7 @@ enum EditorType
etLayerHeight = 4,
};
class LayerRangeEditor : public wxTextCtrl
class LayerRangeEditor : public TextInput
{
bool m_enter_pressed { false };
bool m_call_kill_focus { false };
@@ -55,6 +57,8 @@ public:
EditorType type() const {return m_type;}
void set_focus_data() const { m_set_focus_data(m_type);}
void SetValue(const wxString& value) {GetTextCtrl()->SetValue(value);}
wxString GetValue() {return GetTextCtrl()->GetValue();}
void msw_rescale();
private:
@@ -65,6 +69,7 @@ class ObjectLayers : public OG_Settings
{
ScalableBitmap m_bmp_delete;
ScalableBitmap m_bmp_add;
ScalableBitmap m_bmp_layer;
ModelObject* m_object {nullptr};
wxFlexGridSizer* m_grid_sizer;
+11 -5
View File
@@ -2938,10 +2938,10 @@ void ObjectTablePanel::load_data()
m_object_grid->SetColLabelValue(ObjectGridTable::col_printable, _L("Printable"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_printable_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_plate_index, wxString::Format("%S%S", _L("Plate"), wxString::FromUTF8("\u2191\u2193")));
m_object_grid->SetColLabelValue(ObjectGridTable::col_plate_index, wxString::Format("%S %S", _L("Plate"), wxString::FromUTF8("\u2191\u2193")));
/*m_object_grid->SetColLabelValue(ObjectGridTable::col_assemble_name, L("Module"));*/
m_object_grid->SetColLabelValue(ObjectGridTable::col_name, wxString::Format("%S%S", _L("Name"), wxString::FromUTF8("\u2191\u2193")));
m_object_grid->SetColLabelValue(ObjectGridTable::col_filaments, wxString::Format("%S%S", _L("Filament"), wxString::FromUTF8("\u2191\u2193")));
m_object_grid->SetColLabelValue(ObjectGridTable::col_name, wxString::Format("%S %S", _L("Name"), wxString::FromUTF8("\u2191\u2193")));
m_object_grid->SetColLabelValue(ObjectGridTable::col_filaments, wxString::Format("%S %S", _L("Filament"), wxString::FromUTF8("\u2191\u2193")));
m_object_grid->SetColLabelValue(ObjectGridTable::col_filaments_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_layer_height, _L("Layer height"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_layer_height_reset, "");
@@ -2956,8 +2956,8 @@ void ObjectTablePanel::load_data()
m_object_grid->SetColLabelValue(ObjectGridTable::col_speed_perimeter, _L("Outer wall speed"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_speed_perimeter_reset, "");
m_object_grid->SetLabelFont(Label::Head_13);
m_object_grid->SetLabelTextColour(StateColor::darkModeColorFor(wxColour("#303A3C")));
m_object_grid->SetLabelBackgroundColour( wxColour("#FFFFFF"));
m_object_grid->SetLabelTextColour(StateColor::darkModeColorFor(wxColour("#363636")));
m_object_grid->SetLabelBackgroundColour( StateColor::darkModeColorFor(wxColour("#D9D9D9")));
#else
m_object_grid->HideColLabels();
#endif
@@ -2996,6 +2996,12 @@ void ObjectTablePanel::load_data()
//m_object_grid->SetSelectionForeground(wxColour(0xDB,0xFD,0xE7));
//m_object_grid->SetSelectionBackground(*wxWHITE);
m_object_grid->SetDefaultCellBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
m_object_grid->SetSelectionBackground(StateColor::darkModeColorFor(wxColour("#BFE1DE"))); // its not fully working since background of control's covers cell
m_object_grid->SetCellHighlightColour(StateColor::darkModeColorFor(wxColour("#009688")));
m_object_grid->SetCellHighlightPenWidth(FromDIP(1));
m_object_grid->SetCellHighlightROPenWidth(FromDIP(1)); // Highlight for read-only cells
for (int col = 0; col < cols; col++)
{
ObjectGridTable::ObjectGridCol* grid_col = m_object_grid_table->get_grid_col(col);
+36
View File
@@ -665,6 +665,42 @@ void RemoveInputBorder(wxWindow* win)
);
#endif
}
void SetGaugeColor(wxGauge* gauge, const wxString& barColor, const wxString& troughColor)
{
GtkWidget* widget = gauge->GetHandle(); // native GtkWidget*
if (!widget) return;
#if GTK_CHECK_VERSION(3, 0, 0)
// GTK3+: use CSS provider
GtkCssProvider* provider = gtk_css_provider_new();
wxString css = wxString::Format(
"progressbar trough { background-color: %s; }"
"progressbar progress { background-color: %s; background-image: none; }",
troughColor,
barColor
);
#if GTK_CHECK_VERSION(4, 0, 0)
// GTK4
gtk_css_provider_load_from_data(provider, css.utf8_str(), -1);
#else
// GTK3
gtk_css_provider_load_from_data(provider, css.utf8_str(), -1, nullptr);
#endif
GtkStyleContext* ctx = gtk_widget_get_style_context(widget);
gtk_style_context_add_provider(
ctx,
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
g_object_unref(provider);
#endif
}
#endif // __WXGTK__
#ifdef __linux__
+5
View File
@@ -37,6 +37,10 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/Color.hpp"
#ifdef __WXGTK__
#include "wx/gauge.h"
#endif
class wxCheckBox;
class wxTopLevelWindow;
@@ -495,6 +499,7 @@ void set_window_corner_radius(wxWindow* win, int radius);
#ifdef __WXGTK__
void RemoveButtonBorder(wxWindow* win); // for wxButton/wxBitmapToggleButton based controls (SwitchButton, CheckBox)
void RemoveInputBorder(wxWindow* win); // for TextCtrl based controls (TextInput, ComboBox, SpinInput..)
void SetGaugeColor(wxGauge* gauge, const wxString& barColor, const wxString& troughColor);
#endif
#if defined(__WXOSX__) || defined(__linux__)
@@ -57,6 +57,7 @@
#include "slic3r/GUI/Gizmos/GLGizmosManager.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Selection.hpp"
#include "slic3r/GUI/GUI_Utils.hpp"
namespace Slic3r::GUI {
@@ -38,6 +38,7 @@
#include "libslic3r/TriangleSelector.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI_Utils.hpp"
namespace Slic3r::GUI {
@@ -46,6 +46,7 @@
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/PartPlate.hpp"
#include "slic3r/GUI/Selection.hpp"
#include "slic3r/GUI/GUI_Utils.hpp"
namespace Slic3r::GUI {
@@ -1055,7 +1056,7 @@ void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
ImGui::PushStyleColor(ImGuiCol_Border , ImGui::ColorConvertFloat4ToU32(ImGuiWrapper::COL_ORCA));
if (ImGui::BeginPopup(pop_id.c_str())) {
if (ImGui::BeginPopup(pop_id.c_str(), ImGuiWindowFlags_NoMove)) {
m_imgui->text(_L("To:"));
@@ -102,6 +102,7 @@ void GLGizmoPrimitive::on_render_input_window(float x, float y, float bottom_lim
m_params.type = (PrimitiveType)cur;
m_preview_dirty = true;
}
// TRN Primitive gizmo: label in front of the preset cube size buttons
ImGui::TextUnformatted(_u8L("Quick:").c_str());
ImGui::SameLine();
if (ImGui::SmallButton("10mm")) apply_preset("10mm cube", 10, 10, 10);
@@ -133,11 +134,13 @@ void GLGizmoPrimitive::on_render_input_window(float x, float y, float bottom_lim
dim(_u8L("Radius").c_str(), m_params.sph_radius);
break;
case PrimitiveType::Cone:
// TRN Primitive gizmo: bottom radius of a cone ("Top R" is the top radius)
dim(_u8L("Bottom R").c_str(), m_params.cone_r1);
dim(_u8L("Top R").c_str(), m_params.cone_r2);
dim(_u8L("Height").c_str(), m_params.cone_height);
break;
case PrimitiveType::Torus:
// TRN Primitive gizmo: major radius of a torus ("Minor R" is the tube radius)
dim(_u8L("Major R").c_str(), m_params.torus_r1);
dim(_u8L("Minor R").c_str(), m_params.torus_r2, 0.1, 1.0);
break;
+5 -5
View File
@@ -1351,25 +1351,25 @@ void GLGizmoSVG::draw_window()
assert(m_volume_id.valid());
if (m_volume == nullptr ||
m_volume_id.invalid()) {
ImGui::Text("Not valid state please report reproduction steps on github");
ImGui::Text("%s", _u8L("Not valid state please report reproduction steps on github").c_str());
return;
}
assert(m_volume->emboss_shape.has_value());
if (!m_volume->emboss_shape.has_value()) {
ImGui::Text("No embossed file");
ImGui::Text("%s", _u8L("No embossed file").c_str());
return;
}
assert(m_volume->emboss_shape->svg_file.has_value());
if (!m_volume->emboss_shape->svg_file.has_value()){
ImGui::Text("Missing svg file in embossed shape");
ImGui::Text("%s", _u8L("Missing svg file in embossed shape").c_str());
return;
}
assert(m_volume->emboss_shape->svg_file->file_data != nullptr);
if (m_volume->emboss_shape->svg_file->file_data == nullptr){
ImGui::Text("Missing data of svg file");
ImGui::Text("%s", _u8L("Missing data of svg file").c_str());
return;
}
@@ -2143,7 +2143,7 @@ std::string volume_name(const EmbossShape &shape)
std::string file_name = get_file_name(shape.svg_file->path);
if (!file_name.empty())
return file_name;
return "SVG shape";
return _u8L("SVG shape");
}
CreateVolumeParams create_input(GLCanvas3D &canvas, RaycastManager& raycaster, ModelVolumeType volume_type)
+1
View File
@@ -28,6 +28,7 @@
#include "libslic3r/Color.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include <memory>
#include "slic3r/GUI/GUI_Utils.hpp"
namespace Slic3r::GUI {
+23 -20
View File
@@ -3,6 +3,7 @@
#include "slic3r/GUI/ImGuiWrapper.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/GUI/format.hpp"
#include "libslic3r/Model.hpp"
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
@@ -231,11 +232,11 @@ void GLGizmoSketch::draw_canvas()
TopoDS_Shape GLGizmoSketch::build_combined_shape()
{
if (m_profiles.empty() || !m_profiles[0].closed)
throw std::runtime_error("No outer profile");
throw std::runtime_error(_u8L("No outer profile"));
TopoDS_Wire outer_wire = m_profiles[0].to_occt_wire(m_plane);
BRepBuilderAPI_MakeFace face_maker(outer_wire);
if (!face_maker.IsDone()) throw std::runtime_error("Failed to make outer face");
if (!face_maker.IsDone()) throw std::runtime_error(_u8L("Failed to make outer face"));
for (size_t i = 1; i < m_profiles.size(); ++i) {
if (!m_profiles[i].closed) continue;
@@ -243,7 +244,7 @@ TopoDS_Shape GLGizmoSketch::build_combined_shape()
face_maker.Add(inner);
}
face_maker.Build();
if (!face_maker.IsDone()) throw std::runtime_error("Failed to build face with holes");
if (!face_maker.IsDone()) throw std::runtime_error(_u8L("Failed to build face with holes"));
TopoDS_Face face = face_maker.Face();
@@ -253,7 +254,7 @@ TopoDS_Shape GLGizmoSketch::build_combined_shape()
gp_Dir xd(m_plane.x_axis.x(), m_plane.x_axis.y(), m_plane.x_axis.z());
gp_Ax1 axis(o, xd);
BRepPrimAPI_MakeRevol rev(face, axis, m_sp.revolve_deg * M_PI / 180.0);
if (!rev.IsDone()) throw std::runtime_error("Revolve failed");
if (!rev.IsDone()) throw std::runtime_error(_u8L("Revolve failed"));
shape = rev.Shape();
} else {
shape = SketchEngine::make_extrude_face(face, m_plane, m_sp.extrude_len, m_sp.extrude_sym);
@@ -320,8 +321,9 @@ void GLGizmoSketch::on_render_input_window(float x, float y, float bottom_limit)
bool outer = (i == 0);
ImVec4 col = outer ? ImVec4(0,1,0,1) : ImVec4(1,0.3f,0.3f,1);
const std::string label = outer ? _u8L("Outer") : _u8L("Hole");
ImGui::TextColored(col, "%s %d: %zu %s %s", label.c_str(), i+1, p.points.size(),
_u8L("points").c_str(), p.closed ? _u8L("closed").c_str() : "");
const std::string state = p.closed ? _u8L("closed") : std::string();
// TRN Sketch gizmo profile list: "Outer" or "Hole", profile number, point count, "closed" or nothing
ImGui::TextColored(col, "%s", GUI::format(_u8L("%1% %2%: %3% points %4%"), label, i + 1, p.points.size(), state).c_str());
ImGui::SameLine();
if (ImGui::SmallButton("X")) delete_profile(i);
ImGui::PopID();
@@ -336,9 +338,9 @@ void GLGizmoSketch::on_render_input_window(float x, float y, float bottom_limit)
if (ImGui::CollapsingHeader(_u8L("Operation").c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
static int pi = 0;
const std::string planes = "XY (" + _u8L("Top") + ")" + std::string(1, '\0') + "XZ (" + _u8L("Front") + ")"
+ std::string(1, '\0') + "YZ (" + _u8L("Side") + ")" + std::string(2, '\0');
if (ImGui::Combo(_u8L("Plane").c_str(), &pi, planes.c_str()))
const std::string plane_names[] = {_u8L("XY (Top)"), _u8L("XZ (Front)"), _u8L("YZ (Side)")};
const char* planes[] = {plane_names[0].c_str(), plane_names[1].c_str(), plane_names[2].c_str()};
if (ImGui::Combo(_u8L("Plane").c_str(), &pi, planes, 3))
m_plane = (pi==0) ? SketchPlane::XY() : (pi==1) ? SketchPlane::XZ() : SketchPlane::YZ();
is_revolve = (m_sp.revolve_deg > 0 && m_sp.revolve_deg < 360);
@@ -389,7 +391,8 @@ void GLGizmoSketch::on_render_input_window(float x, float y, float bottom_limit)
ImGui::Separator();
bool ok = has_closed_profile();
if (ok) ImGui::TextColored({0,1,0,1}, "%zu %s", m_profiles.size(), _u8L("closed profile(s)").c_str());
if (ok) ImGui::TextColored({0,1,0,1}, "%s", wxString::Format(_L_PLURAL("%zu closed profile", "%zu closed profiles",
unsigned(m_profiles.size())), m_profiles.size()).ToUTF8().data());
else ImGui::TextColored({0.6f,0.6f,0.6f,1}, "%s", _u8L("Draw a closed profile to enable").c_str());
auto btn = [&](const char* label, bool enabled) {
@@ -404,7 +407,7 @@ void GLGizmoSketch::on_render_input_window(float x, float y, float bottom_limit)
} else if (is_revolve) {
if (btn(_u8L("Revolve").c_str(), ok)) apply_revolve();
} else {
if (btn(_u8L("Extrude").c_str(), ok)) apply_extrude();
if (btn(_u8L_CONTEXT("Extrude", "Design").c_str(), ok)) apply_extrude();
}
if (ImGui::Button(_u8L("Clear all").c_str(), {-1,0})) clear_all();
@@ -419,7 +422,7 @@ void GLGizmoSketch::apply_extrude()
try {
TopoDS_Shape shape = build_combined_shape();
TriangleMesh mesh = SketchEngine::tessellate(shape, m_sp.linear_deflection);
if (mesh.its.indices.empty()) throw std::runtime_error("Empty result");
if (mesh.its.indices.empty()) throw std::runtime_error(_u8L("Empty result"));
wxGetApp().plater()->take_snapshot("Sketch Extrude");
ModelObject* mo = wxGetApp().model().add_object();
mo->name = "Extrusion";
@@ -428,7 +431,7 @@ void GLGizmoSketch::apply_extrude()
wxGetApp().plater()->update();
clear_all();
} catch (const std::exception& e) {
wxGetApp().notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::ErrorNotificationLevel, std::string("Extrude: ")+e.what());
wxGetApp().notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::ErrorNotificationLevel, GUI::format(_u8L("Extrude: %1%"), e.what()));
}
}
@@ -437,7 +440,7 @@ void GLGizmoSketch::apply_revolve()
try {
TopoDS_Shape shape = build_combined_shape();
TriangleMesh mesh = SketchEngine::tessellate(shape, m_sp.linear_deflection);
if (mesh.its.indices.empty()) throw std::runtime_error("Empty result");
if (mesh.its.indices.empty()) throw std::runtime_error(_u8L("Empty result"));
wxGetApp().plater()->take_snapshot("Sketch Revolve");
ModelObject* mo = wxGetApp().model().add_object();
mo->name = "Revolve";
@@ -446,7 +449,7 @@ void GLGizmoSketch::apply_revolve()
wxGetApp().plater()->update();
clear_all();
} catch (const std::exception& e) {
wxGetApp().notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::ErrorNotificationLevel, std::string("Revolve: ")+e.what());
wxGetApp().notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::ErrorNotificationLevel, GUI::format(_u8L("Revolve: %1%"), e.what()));
}
}
@@ -455,20 +458,20 @@ void GLGizmoSketch::apply_pocket()
try {
Selection& sel = m_parent.get_selection();
int obj_idx = sel.get_object_idx();
if (obj_idx < 0) throw std::runtime_error("No object selected");
if (obj_idx < 0) throw std::runtime_error(_u8L("No object selected"));
ModelObject* mo = wxGetApp().model().objects[obj_idx];
TopoDS_Wire outer = m_profiles[0].to_occt_wire(m_plane);
BRepBuilderAPI_MakeFace fm(outer);
if (!fm.IsDone()) throw std::runtime_error("Face failed");
if (!fm.IsDone()) throw std::runtime_error(_u8L("Face failed"));
for (size_t i = 1; i < m_profiles.size(); ++i)
if (m_profiles[i].closed) fm.Add(m_profiles[i].to_occt_wire(m_plane));
fm.Build();
if (!fm.IsDone()) throw std::runtime_error("Face with holes failed");
if (!fm.IsDone()) throw std::runtime_error(_u8L("Face with holes failed"));
TopoDS_Shape tool = SketchEngine::make_extrude_face(fm.Face(), m_plane, m_sp.extrude_len + 5.0, false);
TriangleMesh tool_mesh = SketchEngine::tessellate(tool, m_sp.linear_deflection);
if (tool_mesh.its.indices.empty()) throw std::runtime_error("Tool mesh empty");
if (tool_mesh.its.indices.empty()) throw std::runtime_error(_u8L("Tool mesh empty"));
wxGetApp().plater()->take_snapshot("Sketch Pocket");
mo->add_volume(std::move(tool_mesh), ModelVolumeType::NEGATIVE_VOLUME)->set_new_unique_id();
@@ -477,7 +480,7 @@ void GLGizmoSketch::apply_pocket()
clear_all();
wxGetApp().notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::RegularNotificationLevel, _u8L("Pocket added (negative volume)").c_str());
} catch (const std::exception& e) {
wxGetApp().notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::ErrorNotificationLevel, std::string("Pocket: ")+e.what());
wxGetApp().notification_manager()->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::ErrorNotificationLevel, GUI::format(_u8L("Pocket: %1%"), e.what()));
}
}
@@ -411,7 +411,7 @@ bool GLGizmoTextureDisplacement::on_init()
m_desc["circle"] = _L("Circle");
m_desc["sphere"] = _L("Sphere");
m_desc["remove_layer"] = _L("Remove");
m_desc["bake"] = _L("Bake");
m_desc["bake"] = _L_CONTEXT("Bake", "Texture Displacement");
return true;
}
@@ -3509,7 +3509,7 @@ void GLGizmoTextureDisplacement::set_layer_texture(TextureDisplacementLayer &lay
void GLGizmoTextureDisplacement::import_custom_texture(TextureDisplacementLayer &layer)
{
const wxString wildcard = "Images (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp";
const wxString wildcard = _L("Images (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp");
wxFileDialog dialog(nullptr, _L("Choose a texture image (height map)"), wxEmptyString, wxEmptyString, wildcard,
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
@@ -6001,8 +6001,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
// Midlevel: the height that means "don't move". At 0 the surface only ever bulges outwards;
// at 0.5 mid-grey is neutral and darker texels cut inwards.
m_preview_params_dirty |= float_row("##midlevel", _L("Midlevel"), &layer.midlevel, 0.f, 10.f, "%.2f", false, card_pad);
hover_tip(_u8L("Which grey stays where the surface already is. At 0 the texture only pushes "
"outwards; at 0.5 mid-grey stays put, so darker greys cut in and lighter ones "
hover_tip(_u8L("Which gray stays where the surface already is. At 0 the texture only pushes "
"outwards; at 0.5 mid-gray stays put, so darker grays cut in and lighter ones "
"still push out - one image both embosses and engraves.\n\n"
"What cuts in has to fit: inside a sharp corner or through a thin wall, a deep "
"cut can pass through the other side."));
@@ -6045,18 +6045,18 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
const bool has_color = decode_height_texture(layer).has_color();
bool color_enabled = layer.color_enabled && has_color;
m_imgui->disabled_begin(!has_color);
if (m_imgui->bbl_checkbox(_L("Colours"), color_enabled)) {
if (m_imgui->bbl_checkbox(_L("Colors"), color_enabled)) {
layer.color_enabled = color_enabled;
m_preview_params_dirty = true;
}
m_imgui->disabled_end();
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
m_imgui->tooltip(has_color ?
_u8L("Prints the painted area in the texture's colours as well as its "
"relief. Each colour is matched to the nearest of your loaded "
_u8L("Prints the painted area in the texture's colors as well as its "
"relief. Each color is matched to the nearest of your loaded "
"filaments; anything you did not paint keeps the object's own.") :
_u8L("This texture is a grayscale height map, so it has no colours to "
"apply. Import a colour image to use this."),
_u8L("This texture is a grayscale height map, so it has no colors to "
"apply. Import a color image to use this."),
wrap_w);
// The rest of colour belongs to the whole stack, not to this layer, so it only appears once -
@@ -6065,13 +6065,13 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
TextureDisplacementOptions &opts = mv->texture_displacement_options;
if (m_imgui->bbl_checkbox(_L("Mix filaments"), opts.color_mix_enabled))
m_preview_params_dirty = true;
hover_tip(_u8L("Interleaves two filaments to fake the colours in between, so a handful "
"of filaments can cover a photo or a gradient. An image of flat colours "
hover_tip(_u8L("Interleaves two filaments to fake the colors in between, so a handful "
"of filaments can cover a photo or a gradient. An image of flat colors "
"prints the same either way. Off uses one filament per area."));
if (opts.color_mix_enabled) {
slider_label(_L("Mix by"));
const std::string mix_z = _u8L("Layers");
const std::string mix_xy = _u8L("Surface");
const std::string mix_xy = _u8L_CONTEXT("Surface", "Texture Displacement");
const std::string mix_auto = _u8L("Automatic");
const char *mix_items[] = { mix_z.c_str(), mix_xy.c_str(), mix_auto.c_str() };
int mix_mode = int(opts.color_mix_mode);
@@ -6087,12 +6087,12 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
"any angle but can read as texture rather than as a blend.\n"
"Automatic: layers on upright faces; flat-facing faces take the nearer "
"single filament, since a checkerboard there shows as a pattern."));
ImGui::TextDisabled("%s", Slic3r::format(_u8L("%1% printable colours from %2% filaments"),
ImGui::TextDisabled("%s", Slic3r::format(_u8L("%1% printable colors from %2% filaments"),
int(cached_palette().size()), int(m_palette_filaments.size())).c_str());
}
if (int_row("##color_despeckle", _L("Denoise"), &opts.color_despeckle, 0, 6, "%d", card_pad))
m_preview_params_dirty = true;
hover_tip(_u8L("Cleans up single stray triangles of the wrong colour, which detail finer "
hover_tip(_u8L("Cleans up single stray triangles of the wrong color, which detail finer "
"than the mesh leaves behind. Raise it if the result looks speckled, "
"lower it if small features are being swallowed."));
}
@@ -6102,7 +6102,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
{
const float x0 = ImGui::GetCursorPosX();
ImGui::AlignTextToFramePadding();
ImGui::TextDisabled("%s", _u8L("Mapping").c_str());
ImGui::TextDisabled("%s", _u8L_CONTEXT("Mapping", "Texture Displacement").c_str());
ImGui::SameLine();
ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
struct MappingIcon
@@ -6116,9 +6116,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
_L("Triplanar - projects the texture from all three axes at once and blends between them, so a "
"patch wrapping around a sharp edge has no seam.") },
{ "menu_obj_cylinder.svg", _L("Cylindrical"),
_L("Cylindrical - wraps the texture around the painted area's own centre, for round shapes.") },
_L("Cylindrical - wraps the texture around the painted area's own center, for round shapes.") },
{ "menu_obj_sphere.svg", _L("Spherical"),
_L("Spherical - wraps the texture around the painted area's own centre in both directions.") },
_L("Spherical - wraps the texture around the painted area's own center in both directions.") },
{ "texture_displacement_map_unwrap.svg", _L("Unwrap (LSCM)"),
_L("Unwrap - flattens the painted area and maps the texture onto it with as little stretching as "
"possible. The area is cut into pieces at its sharp edges first (see Seam angle), so each "
@@ -6261,9 +6261,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
ImGui::TextDisabled("%s", _u8L("Blend").c_str());
ImGui::SameLine();
ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
const std::string blend_add = _u8L("Add");
const std::string blend_subtract = _u8L("Subtract");
const std::string blend_multiply = _u8L("Multiply");
const std::string blend_add = _u8L_CONTEXT("Add", "Texture Displacement");
const std::string blend_subtract = _u8L_CONTEXT("Subtract", "Texture Displacement");
const std::string blend_multiply = _u8L_CONTEXT("Multiply", "Texture Displacement");
const std::string blend_divide = _u8L("Divide");
const char *blend_items[] = { blend_add.c_str(), blend_subtract.c_str(), blend_multiply.c_str(), blend_divide.c_str() };
// The first layer has nothing before it to combine with - build_texture_displacement() makes it
@@ -6535,11 +6535,11 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
// Only worth showing when a layer is actually colouring: with no colour there is no boundary for it
// to refine and the control would do nothing whatever it is set to.
if (mv != nullptr && any_layer_colors(*mv)) {
if (float_row("##subdiv_color", _L("Colour detail"), &m_subdivide_color_mm, 0.f, 5.f, "%.3f mm", false, 0.f))
if (float_row("##subdiv_color", _L("Color detail"), &m_subdivide_color_mm, 0.f, 5.f, "%.3f mm", false, 0.f))
preview_live();
hover_tip(_u8L("Triangle size where two colours meet. Each triangle prints in one filament, so a "
"colour edge can only be as sharp as the triangles along it - and nothing else "
"refines there, since the surface is flat across a change of colour. 0 turns it off."));
hover_tip(_u8L("Triangle size where two colors meet. Each triangle prints in one filament, so a "
"color edge can only be as sharp as the triangles along it - and nothing else "
"refines there, since the surface is flat across a change of color. 0 turns it off."));
}
ImGui::TextDisabled("%s", _u8L("Triangle budget: set with Triangles, below.").c_str());
@@ -6657,7 +6657,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
"on a layer is a different thing: it blurs the image before it is used."));
if (opts.smooth_enabled) {
float percent = opts.smooth_strength * 100.f;
if (float_row("##dispsmooth", _L("Strength"), &percent, 1.f, 100.f, "%.0f %%", false, 0.f)) {
if (float_row("##dispsmooth", _L_CONTEXT("Strength", "Texture Displacement"), &percent, 1.f, 100.f, "%.0f %%", false, 0.f)) {
opts.smooth_strength = std::clamp(percent / 100.f, 0.01f, 1.f);
m_preview_params_dirty = true;
}
@@ -6699,16 +6699,17 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
}
hover_tip(_u8L("Bake by moving the vertices the mesh already has, after preparing it (remesh, "
"adaptive subdivision, and a cut along sharp steps in the texture). Keeps the "
"topology, which is what colours need. The default pipeline instead refines, "
"topology, which is what colors need. The default pipeline instead refines, "
"cleans up sliver triangles, displaces and simplifies in one run; nothing needs "
"preparing first, but it does not produce colours yet."));
"preparing first, but it does not produce colors yet."));
}
if (opts.pipeline_v2) {
// -1 is "auto": the row shows the recommendation, greyed; editing it makes it a fixed value.
const bool auto_budget = opts.v2_max_triangles_k < 0;
int shown_k = auto_budget ? v2_recommendation(*mv).budget_k : opts.v2_max_triangles_k;
m_imgui->disabled_begin(auto_budget);
if (int_row("##v2budget", _L("Budget"), &shown_k, 0, 4000, auto_budget ? "%d k (auto)" : "%d k", 0.f)) {
// TRN Slider value: %d is the triangle budget in thousands
if (int_row("##v2budget", _L("Budget"), &shown_k, 0, 4000, auto_budget ? _u8L("%d k (auto)").c_str() : "%d k", 0.f)) {
opts.v2_max_triangles_k = shown_k;
m_preview_params_dirty = true;
}
@@ -6827,7 +6828,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
ImGui::SetNextItemWidth(x0 + panel_w - ImGui::GetCursorPosX());
float shown = auto_res ? rec.edge_mm : opts.v2_refine_mm;
m_imgui->disabled_begin(busy || auto_res);
if (ImGui::SliderFloat("##v2edge", &shown, 0.02f, 2.f, auto_res ? "%.2f mm (auto)" : "%.2f mm",
if (ImGui::SliderFloat("##v2edge", &shown, 0.02f, 2.f, auto_res ? _u8L("%.2f mm (auto)").c_str() : "%.2f mm",
ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic)) {
opts.v2_refine_mm = shown;
m_preview_params_dirty = true;
+1
View File
@@ -11,6 +11,7 @@
#include <cstddef>
#include <wx/app.h>
#include <boost/algorithm/string.hpp>
#include "slic3r/GUI/GUI_Utils.hpp"
#ifdef WIN32
#include <wx/msw/winundef.h>
@@ -39,6 +39,7 @@
#include "libslic3r/TriangleMesh.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI_Utils.hpp"
#define MAX_NUM 9999.99
#define MAX_SIZE std::string_view{"9999.99"}
+7 -6
View File
@@ -10,6 +10,7 @@
#include <boost/log/trivial.hpp>
#include "CloudProvider.hpp"
#include "GUI_App.hpp"
#include "I18N.hpp"
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
@@ -248,10 +249,10 @@ std::shared_ptr<HttpServer::Response> HttpServer::auth_handle_request(const std:
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
}
const std::string title = login_ok ? "Authentication complete" : "Authentication failed";
const std::string title = login_ok ? _u8L("Authentication complete") : _u8L("Authentication failed");
const std::string message = login_ok
? "You can return to OrcaSlicer. This window will close automatically."
: "Something went wrong. Please return to OrcaSlicer and try again.";
? _u8L("You can return to OrcaSlicer. This window will close automatically.")
: _u8L("Something went wrong. Please return to OrcaSlicer and try again.");
const std::string html =
"<html><head><meta charset=\"utf-8\">"
"<style>body{font-family:Arial,sans-serif;background:#f7f7f7;color:#222;margin:32px;}"
@@ -434,9 +435,9 @@ void HttpServer::ResponseRedirect::write_response(std::stringstream& ssOut)
"<style>body{font-family:Arial,sans-serif;background:#f7f7f7;color:#222;margin:32px;}"
"a.button{display:inline-block;padding:10px 16px;margin-top:12px;background:#0f8bff;color:#fff;text-decoration:none;border-radius:6px;}"
"</style></head><body><div class=\"container\">"
"<h2>Authentication complete</h2>"
"<p>You can return to OrcaSlicer. If your browser does not redirect automatically, use the button below.</p>"
"<a class=\"button\" href=\"" + location_str + "\">Continue</a>"
"<h2>" + _u8L("Authentication complete") + "</h2>"
"<p>" + _u8L("You can return to OrcaSlicer. If your browser does not redirect automatically, use the button below.") + "</p>"
"<a class=\"button\" href=\"" + location_str + "\">" + _u8L("Continue") + "</a>"
"<script>setTimeout(function(){try{window.close();}catch(e){}},1500);</script>"
"</div></body></html>";
ssOut << "HTTP/1.1 302 Found" << std::endl;
+2 -2
View File
@@ -1524,7 +1524,7 @@ void IMSlider::render_add_menu()
ImGui::OpenPopup("slider_add_menu_popup");
m_show_menu = false;
}
if (ImGui::BeginPopup("slider_add_menu_popup")) {
if (ImGui::BeginPopup("slider_add_menu_popup", ImGuiWindowFlags_NoMove)) {
bool menu_item_enable = m_draw_mode != dmSequentialFffPrint;
bool hovered = false;
{
@@ -1579,7 +1579,7 @@ void IMSlider::render_edit_menu(const TickCode& tick)
ImGui::OpenPopup("slider_edit_menu_popup");
m_show_menu = false;
}
if (ImGui::BeginPopup("slider_edit_menu_popup")) {
if (ImGui::BeginPopup("slider_edit_menu_popup", ImGuiWindowFlags_NoMove)) {
switch (tick.type)
{
case CustomGCode::PausePrint:
+2
View File
@@ -35,6 +35,8 @@
#include <errno.h>
#include <optional>
#include <cstdint>
#include <cwchar>
#include <memory>
#ifdef _WIN32
#include <strsafe.h>
+1
View File
@@ -14,6 +14,7 @@
#include <string>
#include <boost/filesystem.hpp>
#include <cstddef>
#if __linux__
#include <boost/thread.hpp>
+1 -1
View File
@@ -527,7 +527,7 @@ void ArrangeJob::check_unprintable()
#endif
if (it->poly.area() < 0.001) {
auto msg = (boost::format(
_utf8("Object %s has zero size and can't be arranged."))
_u8L("Object %s has zero size and can't be arranged."))
% _utf8(it->name)).str();
m_plater->get_notification_manager()->push_notification(NotificationType::BBLPlateInfo,
NotificationManager::NotificationLevel::WarningNotificationLevel, msg);
+6 -6
View File
@@ -318,7 +318,7 @@ void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
if (!::finalize(canceled, eptr, *m_input.base))
return;
if (m_result.its.empty())
return create_message("Can't create empty volume.");
return create_message(_u8L("Can't create empty volume."));
create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, *m_input.base, m_input.gizmo);
}
@@ -376,7 +376,7 @@ void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr)
// only for sure
if (m_result.empty())
return create_message("Can't create empty object.");
return create_message(_u8L("Can't create empty object."));
GUI_App &app = wxGetApp();
Plater *plater = app.plater();
@@ -995,7 +995,7 @@ TriangleMesh create_mesh(DataBase &input, const Fnc& was_canceled, Job::Ctl& ctl
return {};
// only info
ctl.call_on_main_thread([]() {
create_message("It is used default volume for embossed text, try to change text or font to fix it.");
create_message(_u8L("It is used default volume for embossed text, try to change text or font to fix it."));
});
}
@@ -1056,7 +1056,7 @@ void update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3
{
// for sure that some object will be created
if (mesh.its.empty())
return create_message("Empty mesh can't be created.");
return create_message(_u8L("Empty mesh can't be created."));
Plater *plater = wxGetApp().plater();
// Check gizmo is still open otherwise job should be canceled
@@ -1115,10 +1115,10 @@ void create_volume(TriangleMesh &&mesh,
// Parent object for text volume was propably removed.
// Assumption: User know what he does, so text volume is no more needed.
if (obj == nullptr)
return create_message("Bad object to create volume.");
return create_message(_u8L("Bad object to create volume."));
if (mesh.its.empty())
return create_message("Can't create empty volume.");
return create_message(_u8L("Can't create empty volume."));
plater->take_snapshot(_u8L("Add Emboss text Volume"));
+1 -1
View File
@@ -487,7 +487,7 @@ ShortcutCaptureDialog::ShortcutCaptureDialog(wxWindow* parent, Shortcut shortcut
m_status_colour = m_status->GetForegroundColour();
sizer->Add(m_status, 0, wxLEFT | wxRIGHT | wxTOP, FromDIP(20));
auto dlg_btns = new DialogButtons(this, {"Unbind", "OK", "Cancel"}, "", 1 /*left_aligned*/);
auto dlg_btns = new DialogButtons(this, {L("Unbind"), "OK", "Cancel"}, "", 1 /*left_aligned*/);
dlg_btns->GetFIRST()->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
m_chord = KeyChord{};
m_conflicts.clear();
+3
View File
@@ -150,6 +150,9 @@
#include "slic3r/plugin/host/PluginPages.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include <wx/defs.h>
#include <cassert>
#include <wx/display.h>
#include <wx/window.h>
namespace Slic3r {
+1
View File
@@ -73,6 +73,7 @@
#include <wx/clipbrd.h>
#include "wx/evtloop.h"
#include <boost/nowide/convert.hpp>
class wxImage;
+2 -2
View File
@@ -1676,7 +1676,7 @@ void MixedFilamentDialog::update_ok_button_state()
wxString parts;
for (auto it = type_groups.begin(); it != type_groups.end(); ++it) {
if (!parts.empty())
parts += _L(" and ");
parts += "; ";
wxString slots;
for (size_t j = 0; j < it->second.size(); ++j) {
if (!slots.empty()) slots += ", ";
@@ -1684,7 +1684,7 @@ void MixedFilamentDialog::update_ok_button_state()
}
parts += wxString::Format(_L("Slot %s (%s)"), slots, wxString::FromUTF8(it->first));
}
m_type_mismatch_msg = parts + " " + _L("cannot be mixed. Please select the same filament type.");
m_type_mismatch_msg = wxString::Format(_L("Different filament types cannot be mixed: %s. Please select the same filament type."), parts);
} else {
m_type_mismatch_msg.clear();
}
+2
View File
@@ -33,6 +33,8 @@
#include "I18N.hpp"
#include <bitset>
#include <cstdio>
#include <cstring>
//unofficial linux lib
#ifdef HAVE_SPNAV
+3 -1
View File
@@ -31,6 +31,7 @@
#include <algorithm>
#include <cmath>
#include <vector>
#include <wx/dcgraph.h>
namespace Slic3r {
namespace GUI {
@@ -493,7 +494,8 @@ MultiMachineManagerPage::MultiMachineManagerPage(wxWindow* parent)
page_num_enter_evt();
});
m_page_num_enter = new Button(m_flipping_panel, _("Go"));
// TRN Button that jumps to the page number typed next to it
m_page_num_enter = new Button(m_flipping_panel, _L("Go"));
m_page_num_enter->SetMinSize(wxSize(FromDIP(25), FromDIP(25)));
m_page_num_enter->SetMaxSize(wxSize(FromDIP(25), FromDIP(25)));
m_page_num_enter->SetBackgroundColor(ctrl_bg);
+1
View File
@@ -26,6 +26,7 @@
#include "slic3r/GUI/MsgDialog.hpp"
#include <string>
#include <algorithm>
#include <wx/dcgraph.h>
namespace Slic3r {
namespace GUI {
+2 -1
View File
@@ -44,6 +44,7 @@
#include <wx/timer.h>
#include "DeviceCore/DevManager.h"
#include <wx/dcgraph.h>
namespace Slic3r {
namespace GUI {
@@ -1166,7 +1167,7 @@ CloudTaskManagerPage::CloudTaskManagerPage(wxWindow* parent)
page_num_enter_evt();
});
m_page_num_enter = new Button(m_flipping_panel, _("Go"));
m_page_num_enter = new Button(m_flipping_panel, _L("Go"));
m_page_num_enter->SetMinSize(wxSize(FromDIP(25), FromDIP(25)));
m_page_num_enter->SetMaxSize(wxSize(FromDIP(25), FromDIP(25)));
m_page_num_enter->SetBackgroundColor(ctrl_bg);
+3 -3
View File
@@ -128,7 +128,7 @@ void NetworkPluginDownloadDialog::create_missing_plugin_ui()
main_sizer->AddSpacer(15);
auto dlg_btns = new DialogButtons(this,
{"Download and Install", "Skip for Now"},
{L("Download and Install"), L("Skip for Now")},
_L("Download and Install") // Primary button
);
@@ -190,7 +190,7 @@ void NetworkPluginDownloadDialog::create_update_available_ui(const std::string&
main_sizer->AddSpacer(10);
auto dlg_btns = new DialogButtons(this,
{"Update Now", "Remind Later", "Skip Version"},
{L("Update Now"), L("Remind Later"), L("Skip Version")},
_L("Update Now")
);
@@ -319,7 +319,7 @@ NetworkPluginRestartDialog::NetworkPluginRestartDialog(wxWindow* parent)
main_sizer->AddSpacer(15);
auto dlg_btns = new DialogButtons(this,
{"Restart Now", "Restart Later"},
{L("Restart Now"), L("Restart Later")},
_L("Restart Now") // Primary button
);
+5 -4
View File
@@ -273,7 +273,8 @@ void NetworkTestDialog::start_all_job_sequence()
void NetworkTestDialog::start_test_url(TestJob job, wxString name, wxString url)
{
m_in_testing[job] = true;
wxString info = wxString::Format("test %s start...", name);
// TRN %s is the name of the tested website
wxString info = wxString::Format(_L("test %s start..."), name);
update_status(job, info);
@@ -295,16 +296,16 @@ void NetworkTestDialog::start_test_url(TestJob job, wxString name, wxString url)
}
})
.on_ip_resolve([this,name,job](std::string ip) {
wxString ip_report = wxString::Format("test %s ip resolved = %s", name, ip);
wxString ip_report = wxString::Format(_L("test %s ip resolved = %s"), name, ip);
update_status(job, ip_report);
})
.on_error([this,name,job](std::string body, std::string error, unsigned int status) {
wxString info = wxString::Format("status=%u, body=%s, error=%s", status, body, error);
this->update_status(job, wxString::Format("test %s failed", name));
this->update_status(job, wxString::Format(_L("test %s failed"), name));
this->update_status(-1, info);
}).perform_sync();
if (result == 0) {
update_status(job, wxString::Format("test %s ok", name));
update_status(job, wxString::Format(_L("test %s ok"), name));
}
m_in_testing[job] = false;
}
+3 -5
View File
@@ -1272,7 +1272,7 @@ bool NotificationManager::ExportFinishedNotification::on_text_click()
}
void NotificationManager::ExportFinishedNotification::on_eject_click()
{
NotificationData data{ get_data().type, get_data().level , 0, _utf8("Ejecting.") };
NotificationData data{ get_data().type, get_data().level , 0, _u8L("Ejecting.") };
m_eject_pending = true;
m_multiline = false;
update(data);
@@ -1671,9 +1671,7 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper&
{
ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
float uploaded = m_file_size * m_percentage;
std::stringstream stream;
stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded";
text = stream.str();
text = into_u8(wxString::Format(_L("%d%% - %.2f of %.2fMB uploaded"), (int)(m_percentage * 100), uploaded, m_file_size));
ImGui::SetCursorPosX(m_left_indentation);
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
break;
@@ -3595,7 +3593,7 @@ void NotificationManager::bbl_show_bed_filament_incompatible_notification(const
wxGetApp().open_browser_with_warning_dialog(bed_filament_compatibility_wiki);
return false;
};
push_notification_data({ NotificationType::BBLBedFilamentIncompatible,NotificationLevel::ErrorNotificationLevel,0,_u8L("Error:") + "\n" + text,"Click for more.",callback }, 0);
push_notification_data({ NotificationType::BBLBedFilamentIncompatible,NotificationLevel::ErrorNotificationLevel,0,_u8L("Error:") + "\n" + text,_u8L("Click for more."),callback }, 0);
}
void NotificationManager::bbl_close_bed_filament_incompatible_notification()
+5 -4
View File
@@ -134,13 +134,13 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
m_name = "Settings to modified";
}
else if (type == itInstanceRoot) {
m_name = _(_devL("Instances"));
m_name = _L("Instances");
m_extruder = parent->m_extruder;
}
else if (type == itInstance)
{
m_idx = parent->GetChildCount();
m_name = wxString::Format(_(_devL("Instance %d")), m_idx + 1);
m_name = wxString::Format(_L("Instance %d"), m_idx + 1);
m_extruder = parent->GetParent()->m_extruder;
set_icons();
}
@@ -398,10 +398,11 @@ void ObjectDataViewModelNode::SetIdx(const int &idx)
// update name if this node is instance
if (m_type == itInstance) {
if (m_plate_idx > 0) {
m_name = wxString::Format(_(_devL("[P%d]Instance %d")), m_plate_idx, m_idx + 1);
// TRN Object list item: plate number, then instance number
m_name = wxString::Format(_L("[P%d]Instance %d"), m_plate_idx, m_idx + 1);
}
else {
m_name = wxString::Format(_(_devL("Instance %d")), m_idx + 1);
m_name = wxString::Format(_L("Instance %d"), m_idx + 1);
}
}
}
+2 -2
View File
@@ -83,7 +83,7 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent) :
m_presets = tab->get_presets();
const Preset &sel_preset = m_presets->get_selected_preset();
std::string suffix = _u8L_CONTEXT(L_CONTEXT("Copy", "PresetName"), "PresetName");
std::string preset_name = sel_preset.is_default ? "Untitled" : sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() : sel_preset.name;
std::string preset_name = sel_preset.is_default ? _u8L("Untitled") : sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() : sel_preset.name;
auto input_sizer = new wxBoxSizer(wxVERTICAL);
@@ -306,7 +306,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
result = host->test(msg); // using test with special input because I don't want to create the generate_auth_creds func for every printer
// Prompt user to approve access on the machine.
show_info(this, "API Key created. Go to the physical printer and hit \"authorize\" on the screen, then run \"Test\" again.\n"+msg, "API Key created.");
show_info(this, _L("API Key created. Go to the physical printer and hit \"authorize\" on the screen, then run \"Test\" again.") + "\n" + msg, _L("API Key created."));
}
+30 -27
View File
@@ -936,7 +936,7 @@ struct Sidebar::priv
// that Orca's sidebar has no counterpart for, so these are parented to p->scrolled.
wxPanel* m_btn_add_mixed_filament{nullptr}; // "+ Add Mixed Filament" full-width button
wxPanel* m_panel_mixed_title{nullptr}; // title row: "Mixed Filament" + add/del buttons
wxStaticText* m_text_mixed_title{nullptr};
StaticLine* m_text_mixed_title{nullptr};
ScalableButton* m_btn_mixed_add{nullptr};
ScalableButton* m_btn_mixed_del{nullptr};
wxScrolledWindow* m_mixed_scroll_area{nullptr}; // independent scrollbar for mixed rows
@@ -3000,6 +3000,7 @@ Sidebar::Sidebar(Plater *parent)
});
// "Variant", not "Nozzle": picking one switches the printer preset (see panel_nozzle_dia).
// TRN Sidebar label of the printer variant (nozzle) selector; picking one switches the printer preset.
p->label_nozzle_title = new Label(p->panel_nozzle_dia, _L("Variant"), LB_PROPAGATE_MOUSE_EVENT);
p->label_nozzle_title->SetFont(Label::Body_10);
@@ -3032,7 +3033,7 @@ Sidebar::Sidebar(Plater *parent)
p->combo_nozzle_dia->Bind(wxEVT_SET_FOCUS, [nozzle_focus_bg](auto& e) {nozzle_focus_bg(true ); e.Skip();});
p->combo_nozzle_dia->Bind(wxEVT_KILL_FOCUS, [nozzle_focus_bg](auto& e) {nozzle_focus_bg(false); e.Skip();});
p->label_nozzle_type = new Label(p->panel_nozzle_dia, "Brass", LB_PROPAGATE_MOUSE_EVENT | wxST_ELLIPSIZE_END | wxALIGN_CENTRE_HORIZONTAL);
p->label_nozzle_type = new Label(p->panel_nozzle_dia, _L("Brass"), LB_PROPAGATE_MOUSE_EVENT | wxST_ELLIPSIZE_END | wxALIGN_CENTRE_HORIZONTAL);
p->label_nozzle_type->SetFont(Label::Body_10);
p->label_nozzle_type->SetMinSize(FromDIP(wxSize(56, -1)));
p->label_nozzle_type->SetMaxSize(FromDIP(wxSize(56, -1)));
@@ -3436,10 +3437,10 @@ Sidebar::Sidebar(Plater *parent)
p->m_panel_mixed_title->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
{
auto* title_sizer = new wxBoxSizer(wxHORIZONTAL);
p->m_text_mixed_title = new wxStaticText(p->m_panel_mixed_title, wxID_ANY, _L("Mixed Filament"));
p->m_text_mixed_title = new StaticLine(p->m_panel_mixed_title, false, _L("Mixed Filament"));
p->m_text_mixed_title->SetFont(::Label::Head_14);
title_sizer->Add(p->m_text_mixed_title, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(SidebarProps::TitlebarMargin()));
title_sizer->AddStretchSpacer();
p->m_text_mixed_title->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
title_sizer->Add(p->m_text_mixed_title, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(SidebarProps::TitlebarMargin()));
p->m_btn_mixed_del = new ScalableButton(p->m_panel_mixed_title, wxID_ANY, "delete_filament");
p->m_btn_mixed_del->SetToolTip(_L("Remove last mixed filament"));
@@ -3579,6 +3580,7 @@ Sidebar::Sidebar(Plater *parent)
search_sizer->Fit(p->m_search_bar);
p->m_object_list = new ObjectList(p->scrolled);
p->m_object_list->SetBackgroundColour(*wxWHITE);
p->m_object_list->Bind(wxCUSTOMEVT_EXIT_SEARCH, [this](wxCommandEvent&) {
#ifdef __WXGTK__
this->p->m_search_item->Enable(true);
@@ -4306,7 +4308,7 @@ void Sidebar::update_mixed_filament_list()
wxWindowUpdateLocker noUpdates(this);
const wxColour mc_bg = StateColor::darkModeColorFor(*wxWHITE);
const wxColour mc_border = StateColor::darkModeColorFor(wxColour("#CECECE"));
const wxColour mc_border = StateColor::darkModeColorFor(wxColour("#DBDBDB")); // same color with filament combo box border
const wxColour mc_text = StateColor::darkModeColorFor(wxColour("#262E30"));
const wxColour mc_dim = StateColor::darkModeColorFor(wxColour("#ACACAC"));
@@ -4420,8 +4422,9 @@ void Sidebar::update_mixed_filament_list()
for (size_t i = 0; i < mixed_indices.size(); ++i) {
size_t cfg_idx = mixed_indices[i];
auto* combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL);
combo_and_btn_sizer->Add(FromDIP(10), 0, 0, 0, 0);
if (i == 0 || i % 2 == 0) // dont add left margin for right column items so all horizontal margins will be equal like on filaments section
combo_and_btn_sizer->AddSpacer(FromDIP(SidebarProps::ContentMargin()));
// Parse components and ratios from config strings (supports 2-N components)
std::vector<unsigned int> comp_ids;
@@ -4511,10 +4514,12 @@ void Sidebar::update_mixed_filament_list()
dc.DrawText(txt, (sz.GetWidth() - txt_sz.GetWidth()) / 2,
(sz.GetHeight() - txt_sz.GetHeight()) / 2);
});
combo_and_btn_sizer->Add(grad_panel, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(4));
grad_panel->Bind(wxEVT_LEFT_UP, [this, i](wxMouseEvent&) { edit_mixed_filament(i); }); // ORCA also open edit color dialog with clicking swatch
combo_and_btn_sizer->Add(grad_panel, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(SidebarProps::ElementSpacing()) - FromDIP(2)); // ElementSpacing - 2 (from combo box))
} else {
combo_and_btn_sizer->Add(make_swatch_panel(p->m_panel_mixed_content, mix_col, mix_num),
0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(4));
auto swatch_panel = make_swatch_panel(p->m_panel_mixed_content, mix_col, mix_num);
swatch_panel->Bind(wxEVT_LEFT_UP, [this, i](wxMouseEvent&) { edit_mixed_filament(i); }); // ORCA also open edit color dialog with clicking swatch
combo_and_btn_sizer->Add(swatch_panel, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(SidebarProps::ElementSpacing()) - FromDIP(2)); // ElementSpacing - 2 (from combo box))
}
auto* content_panel = new wxPanel(p->m_panel_mixed_content, wxID_ANY);
@@ -4738,9 +4743,9 @@ void Sidebar::update_mixed_filament_list()
PopupMenu(&menu);
});
combo_and_btn_sizer->Add(menu_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(4));
combo_and_btn_sizer->Add(menu_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(SidebarProps::ElementSpacing()) - FromDIP(2)); // ElementSpacing - 2 (from combo box))
combo_and_btn_sizer->Add(FromDIP(16), 0, 0, 0, 0);
combo_and_btn_sizer->Add(FromDIP(SidebarProps::ContentMargin()), 0, 0, 0, 0);
int side = i % 2;
auto* col = (side == 0) ? left_col : right_col;
@@ -16318,7 +16323,7 @@ bool Plater::add_model(bool imperial_units, std::string fname)
void Plater::calib_pa(const Calib_Params& params)
{
const auto calib_pa_name = wxString::Format(L"Pressure Advance Test");
const auto calib_pa_name = _L("Pressure Advance Test");
new_project(false, false, calib_pa_name);
wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
@@ -16795,11 +16800,9 @@ void Plater::calib_flowrate(bool is_linear, int pass, InfillPattern pattern) {
return;
wxString calib_name;
if (is_linear) {
calib_name = L"Orca YOLO Flow Calibration";
if (pass == 2)
calib_name += L" - Perfectionist version";
calib_name = pass == 2 ? _L("Orca YOLO Flow Calibration - Perfectionist version") : _L("Orca YOLO Flow Calibration");
} else
calib_name = wxString::Format(L"Flowrate Test - Pass%d", pass);
calib_name = wxString::Format(_L("Flowrate Test - Pass%d"), pass);
if (new_project(false, false, calib_name) == wxID_CANCEL)
return;
@@ -16839,7 +16842,7 @@ void Plater::calib_temp(const Calib_Params& params) {
constexpr double base_temp_tower_block_height = 10.0;
constexpr int base_temp_tower_temp_step = 5;
const auto calib_temp_name = wxString::Format(L"Nozzle temperature test");
const auto calib_temp_name = _L("Nozzle temperature test");
new_project(false, false, calib_temp_name);
wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
if (params.mode != CalibMode::Calib_Temp_Tower) return;
@@ -16919,7 +16922,7 @@ void Plater::calib_temp(const Calib_Params& params) {
void Plater::calib_max_vol_speed(const Calib_Params& params)
{
const auto calib_vol_speed_name = wxString::Format(L"Max volumetric speed test");
const auto calib_vol_speed_name = _L("Max volumetric speed test");
new_project(false, false, calib_vol_speed_name);
wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
if (params.mode != CalibMode::Calib_Vol_speed_Tower)
@@ -16998,7 +17001,7 @@ void Plater::calib_max_vol_speed(const Calib_Params& params)
void Plater::calib_retraction(const Calib_Params& params)
{
const auto calib_retraction_name = wxString::Format(L"Retraction");
const auto calib_retraction_name = _L("Retraction");
new_project(false, false, calib_retraction_name);
wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
if (params.mode != CalibMode::Calib_Retraction_tower)
@@ -17060,7 +17063,7 @@ void Plater::calib_retraction(const Calib_Params& params)
void Plater::calib_VFA(const Calib_Params& params)
{
const auto calib_vfa_name = wxString::Format(L"VFA test");
const auto calib_vfa_name = _L("VFA test");
new_project(false, false, calib_vfa_name);
wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
if (params.mode != CalibMode::Calib_VFA_Tower)
@@ -17143,7 +17146,7 @@ void Plater::calib_VFA(const Calib_Params& params)
void Plater::calib_input_shaping_freq(const Calib_Params& params)
{
const auto calib_input_shaping_name = wxString::Format(L"Input shaping Frequency test");
const auto calib_input_shaping_name = _L("Input shaping Frequency test");
new_project(false, false, calib_input_shaping_name);
wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
if (params.mode != CalibMode::Calib_Input_shaping_freq)
@@ -17205,7 +17208,7 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params)
void Plater::calib_input_shaping_damp(const Calib_Params& params)
{
const auto calib_input_shaping_name = wxString::Format(L"Input shaping Damping test");
const auto calib_input_shaping_name = _L("Input shaping Damping test");
new_project(false, false, calib_input_shaping_name);
wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
if (params.mode != CalibMode::Calib_Input_shaping_damp)
@@ -17266,7 +17269,7 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params)
void Plater::Calib_Cornering(const Calib_Params& params)
{
const auto Calib_Cornering = wxString::Format(L"Cornering test");
const auto Calib_Cornering = _L("Cornering test");
new_project(false, false, Calib_Cornering);
wxGetApp().mainframe->select_tab(TAB_ID_PREPARE);
if (params.mode != CalibMode::Calib_Cornering)
@@ -17454,7 +17457,7 @@ void Plater::load_gcode(const wxString& filename)
if (p->preview->get_canvas3d()->get_gcode_layers_zs().empty()) {
MessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("Does not contain valid G-code."),
wxString(GCODEVIEWER_APP_NAME) + " - " + _L("An Error has occurred while loading the G-code file."), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal();
set_project_filename(DEFAULT_PROJECT_NAME);
set_project_filename(_L(DEFAULT_PROJECT_NAME));
} else {
set_project_filename(filename);
}
@@ -17805,7 +17808,7 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
// Orca: hide the "Don't show again" checkbox, people keeps accidentally checked this then forgot
// wxBoxSizer *m_sizer_left = new wxBoxSizer(wxHORIZONTAL);
//
// auto dont_show_again = create_remember_checkbox(_L("Remember my choice."), this, _L("This option can be changed later in preferences, under 'Load Behaviour'."));
// auto dont_show_again = create_remember_checkbox(_L("Remember my choice."), this, _L("This option can be changed later in preferences, under 'Load Behavior'."));
// m_sizer_left->Add(dont_show_again, 0, wxALL, 5);
//
// m_sizer_bottom->Add(m_sizer_left, 0, wxEXPAND, 5);
+2 -1
View File
@@ -17,6 +17,7 @@
#include <atomic>
#include <memory>
#include "slic3r/GUI/Event.hpp"
#include "slic3r/GUI/I18N.hpp"
#include <string>
#include "slic3r/GUI/ObjectDataViewModel.hpp"
#include "slic3r/GUI/SettingsIndex.hpp"
@@ -140,7 +141,7 @@ wxDECLARE_EVENT(EVT_NOTICE_CHILDE_SIZE_CHANGED, SimpleEvent);
wxDECLARE_EVENT(EVT_NOTICE_FULL_SCREEN_CHANGED, IntEvent);
using ColorEvent = Event<wxColour>;
wxDECLARE_EVENT(EVT_ADD_CUSTOM_FILAMENT, ColorEvent);
const wxString DEFAULT_PROJECT_NAME = "Untitled";
const wxString DEFAULT_PROJECT_NAME = L("Untitled");
class SidebarProps
{
+18 -18
View File
@@ -265,25 +265,25 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
available_actions.can_toggle = !is_loading && (has_local || available_actions.toggle_installs_cloud_plugin);
auto add_action = [&available_actions](const char* id, const char* label, bool enabled = true, bool danger = false) {
available_actions.context_actions.push_back(PluginContextAction{id, label, enabled, danger});
available_actions.context_actions.push_back(PluginContextAction{id, _u8L(label), enabled, danger});
};
// Owned cloud plugins fall through to the local delete: it removes the installed package only.
// Deleting a plugin from the cloud is a plugin hub operation and is never offered here.
if (is_cloud && !is_orphaned && !is_mine) {
add_action("unsubscribe_plugin", "Unsubscribe", true, true);
add_action("unsubscribe_plugin", L("Unsubscribe"), true, true);
} else if (has_local) {
add_action("delete_plugin", "Delete", true, true);
add_action("delete_plugin", L("Delete"), true, true);
}
add_action("open_folder", "Show in folder", has_local);
add_action("open_folder", L("Show in folder"), has_local);
if (!is_orphaned) {
if (is_cloud) {
add_action("reinstall_plugin", "Reinstall");
add_action("reinstall_plugin", L("Reinstall"));
} else {
add_action("reload_plugin", "Reload");
add_action("clear_cache_reload_plugin", "Delete cache and reload");
add_action("reload_plugin", L("Reload"));
add_action("clear_cache_reload_plugin", L("Delete cache and reload"));
}
}
@@ -811,7 +811,7 @@ void PluginsDialog::toggle_plugin(const std::string& plugin_key, bool enabled)
if (!available_actions.can_toggle) {
if (dialog_item.unauthorized && available_actions.toggle_installs_cloud_plugin == false && row_data.has_local_package() == false) {
const std::string install_error = "Unauthorized cloud plugins cannot be installed.";
const std::string install_error = _u8L("Unauthorized cloud plugins cannot be installed.");
manager.set_plugin_error(plugin_key, install_error);
show_status(from_u8(install_error), "warn");
}
@@ -1071,7 +1071,7 @@ void PluginsDialog::run_script_plugin_capability(const std::string& plugin_key,
ExecutionResult result;
auto complete_with_error = [this, &manager, &plugin_key](const std::string& plugin_error, const wxString& status_message) {
const std::string normalized_error = plugin_error.empty() ? "Script plugin failed." : plugin_error;
const std::string normalized_error = plugin_error.empty() ? _u8L("Script plugin failed.") : plugin_error;
if (!manager.set_plugin_error(plugin_key, normalized_error))
BOOST_LOG_TRIVIAL(warning) << "Failed to record plugin error. plugin_key=" << plugin_key;
@@ -1262,34 +1262,34 @@ void PluginsDialog::reload_local_plugin(const std::string& plugin_key, bool clea
if (clear_cache) {
PluginDescriptor descriptor;
if (!manager.try_get_plugin_descriptor(plugin_key, descriptor))
return {false, "Plugin not found."};
return {false, _u8L("Plugin not found.")};
boost::filesystem::path resolved_root;
std::string resolve_error;
if (!resolve_allowed_plugin_root(descriptor, {get_orca_plugins_dir()},
"Refusing to clear a plugin cache outside the local plugin directory.",
_u8L("Refusing to clear a plugin cache outside the local plugin directory."),
resolved_root, resolve_error))
return {false, resolve_error};
cache_dir = resolved_root / "__whl_extracted__";
}
if (!manager.unload_plugin(plugin_key))
return {false, "Failed to unload plugin."};
return {false, _u8L("Failed to unload plugin.")};
if (clear_cache) {
boost::system::error_code ec;
boost::filesystem::remove_all(cache_dir, ec);
if (ec)
return {false, "Failed to clear plugin cache: " + ec.message()};
return {false, GUI::format(_u8L("Failed to clear plugin cache: %1%"), ec.message())};
}
manager.load_plugin(plugin_key, false);
std::string error;
if (!manager.wait_for_plugin_load(plugin_key, std::chrono::minutes(5), error) || !manager.is_plugin_loaded(plugin_key))
return {false, error.empty() ? "Plugin failed to load." : error};
return {false, error.empty() ? _u8L("Plugin failed to load.") : error};
if (!was_loaded && !manager.unload_plugin(plugin_key))
return {false, "Plugin reloaded, but failed to restore the inactive state."};
return {false, _u8L("Plugin reloaded, but failed to restore the inactive state.")};
return {true, {}};
},
@@ -1297,7 +1297,7 @@ void PluginsDialog::reload_local_plugin(const std::string& plugin_key, bool clea
} catch (const std::exception& ex) {
reload_result = {false, ex.what()};
} catch (...) {
reload_result = {false, "Unknown plugin reload error."};
reload_result = {false, _u8L("Unknown plugin reload error.")};
}
if (!reload_result.first) {
@@ -1344,14 +1344,14 @@ void PluginsDialog::reinstall_cloud_plugin(const PluginDescriptor& plugin)
manager.load_plugin(plugin_key);
std::string error;
if (!manager.wait_for_plugin_load(plugin_key, std::chrono::minutes(5), error) || !manager.is_plugin_loaded(plugin_key))
return {false, error.empty() ? "Plugin failed to load." : error};
return {false, error.empty() ? _u8L("Plugin failed to load.") : error};
return {true, {}};
},
_L("Reloading plugin"), _L("Reloading plugin"));
} catch (const std::exception& ex) {
reload_result = {false, ex.what()};
} catch (...) {
reload_result = {false, "Unknown plugin reload error."};
reload_result = {false, _u8L("Unknown plugin reload error.")};
}
if (!reload_result.first) {
+22 -17
View File
@@ -42,6 +42,10 @@
#include <string>
#include <vector>
#include <utility>
#include <boost/algorithm/string/predicate.hpp>
#include <cassert>
#include <cwchar>
#include <stdlib.h>
#ifdef WIN32
@@ -120,9 +124,10 @@ static DWORD execute_process_winapi(const std::wstring& command_line)
if (!::CreateProcessW(nullptr /* lpApplicationName */, (LPWSTR) command_line.c_str(), nullptr /* lpProcessAttributes */,
nullptr /* lpThreadAttributes */, false /* bInheritHandles */,
CREATE_UNICODE_ENVIRONMENT /* | CREATE_NEW_CONSOLE */ /* dwCreationFlags */, (LPVOID) envstr.c_str(),
nullptr /* lpCurrentDirectory */, &startup_info, &process_info))
throw Slic3r::RuntimeError(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) +
", Win32 error: " + std::to_string(int(::GetLastError())));
nullptr /* lpCurrentDirectory */, &startup_info, &process_info)) {
const int error = int(::GetLastError());
throw Slic3r::RuntimeError(Slic3r::format(_u8L("Failed starting the script %1%, Win32 error: %2%"), boost::nowide::narrow(command_line), error));
}
::WaitForSingleObject(process_info.hProcess, INFINITE);
ULONG rc = 0;
::GetExitCodeProcess(process_info.hProcess, &rc);
@@ -141,14 +146,14 @@ static int run_script(const std::string& script, const std::string& gcode, std::
LPWSTR* szArglist = CommandLineToArgvW(boost::nowide::widen(script).c_str(), &nArgs);
if (szArglist == nullptr || nArgs <= 0) {
// CommandLineToArgvW failed. Maybe the command line escapment is invalid?
throw Slic3r::RuntimeError(std::string("Post processing script ") + script + " on file " + gcode +
" failed. CommandLineToArgvW() refused to parse the command line path.");
throw Slic3r::RuntimeError(Slic3r::format(_u8L("Post processing script %1% on file %2% failed. CommandLineToArgvW() refused to parse the command line path."),
script, gcode));
}
std::wstring command_line;
std::wstring command = szArglist[0];
if (!boost::filesystem::exists(boost::filesystem::path(command)))
throw Slic3r::RuntimeError(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command));
throw Slic3r::RuntimeError(Slic3r::format(_u8L("The configured post-processing script does not exist: %1%"), boost::nowide::narrow(command)));
if (boost::iends_with(command, L".pl")) {
// This is a perl script. Run it through the perl interpreter.
// The current process may be slic3r.exe or slic3r-console.exe.
@@ -159,7 +164,7 @@ static int run_script(const std::string& script, const std::string& gcode, std::
boost::filesystem::path path_perl = path_exe.parent_path() / "perl" / "perl.exe";
if (!boost::filesystem::exists(path_perl)) {
LocalFree(szArglist);
throw Slic3r::RuntimeError(std::string("Perl interpreter ") + path_perl.string() + " does not exist.");
throw Slic3r::RuntimeError(Slic3r::format(_u8L("Perl interpreter %1% does not exist."), path_perl.string()));
}
// Replace it with the current perl interpreter.
quote_argv_winapi(boost::nowide::widen(path_perl.string()), command_line);
@@ -294,14 +299,14 @@ static void run_post_process_plugins(const ConfigOptionStrings& capabilities,
exec_result = cap->execute(ctx);
} catch (const std::exception& ex) {
const std::string msg =
(boost::format("Post-processing plugin %1% raised an exception.\nError: %2%") % ref.capability_name % ex.what()).str();
(boost::format(_u8L("Post-processing plugin %1% raised an exception.\nError: %2%")) % ref.capability_name % ex.what()).str();
BOOST_LOG_TRIVIAL(error) << msg;
throw Slic3r::RuntimeError(msg);
}
if (exec_result.status == PluginResult::RecoverableError || exec_result.status == PluginResult::FatalError) {
const std::string msg =
(boost::format("Post-processing plugin %1% failed.\nError: %2%") % ref.capability_name % exec_result.message).str();
(boost::format(_u8L("Post-processing plugin %1% failed.\nError: %2%")) % ref.capability_name % exec_result.message).str();
BOOST_LOG_TRIVIAL(error) << msg;
throw Slic3r::RuntimeError(msg);
}
@@ -370,7 +375,7 @@ bool run_post_process_scripts(
std::string error_message;
if (copy_file(src_path, path, error_message, false) != SUCCESS)
throw Slic3r::RuntimeError(
Slic3r::format("Failed making a temporary copy of G-code file %1% before running a post-processing script: %2%", src_path,
Slic3r::format(_u8L("Failed making a temporary copy of G-code file %1% before running a post-processing script: %2%"), src_path,
error_message));
} else {
// Don't make a copy of the G-code before running the post-processing script.
@@ -390,7 +395,7 @@ bool run_post_process_scripts(
auto gcode_file = boost::filesystem::path(path);
if (!boost::filesystem::exists(gcode_file))
throw Slic3r::RuntimeError(std::string("Post-processor can't find exported gcode file"));
throw Slic3r::RuntimeError(_u8L("Post-processor can't find exported gcode file"));
// Store print configuration into environment variables.
config.setenv_();
@@ -431,11 +436,11 @@ bool run_post_process_scripts(
const int result = run_script(script, gcode_file.string(), std_err);
if (result != 0) {
const std::string msg = std_err.empty() ?
(boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") %
(boost::format(_u8L("Post-processing script %1% on file %2% failed.\nError code: %3%")) %
script % path % result)
.str() :
(boost::format(
"Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") %
_u8L("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%")) %
script % path % result % std_err)
.str();
BOOST_LOG_TRIVIAL(error) << msg;
@@ -476,19 +481,19 @@ bool run_post_process_scripts(
new_output_name = outpath.string();
} else {
if (!op.is_absolute() || !op.has_filename())
throw Slic3r::RuntimeError("Unable to parse desired new path from output name file");
throw Slic3r::RuntimeError(_u8L("Unable to parse desired new path from output name file"));
}
if (!fs::exists(fs::path(new_output_name).parent_path()))
throw Slic3r::RuntimeError(
Slic3r::format("Output directory does not exist: %1%", fs::path(new_output_name).parent_path().string()));
Slic3r::format(_u8L("Output directory does not exist: %1%"), fs::path(new_output_name).parent_path().string()));
}
BOOST_LOG_TRIVIAL(trace) << "Post-processing script changed the file name from " << output_name << " to "
<< new_output_name;
output_name = new_output_name;
} catch (const std::exception& err) {
throw Slic3r::RuntimeError(Slic3r::format("run_post_process_scripts: Failed reading a file %1% "
"carrying the final name / path of a G-code file: %2%",
throw Slic3r::RuntimeError("run_post_process_scripts: " +
Slic3r::format(_u8L("Failed reading a file %1% carrying the final name / path of a G-code file: %2%"),
path_output_name, err.what()));
}
remove_output_name_file();

Some files were not shown because too many files have changed in this diff Show More