Merge pull request #22217 from PaddleStroke/sk_texttool
Sketcher: Text tool
@@ -7,6 +7,22 @@ macro(SetupFreetype)
|
||||
message("===============================================================\n"
|
||||
"FreeType2 not found. Part module will lack of makeWireString().\n"
|
||||
"===============================================================\n")
|
||||
else()
|
||||
# find_package(harfbuzz CONFIG) fails on windows
|
||||
# lets do it the complicated way instead.
|
||||
find_path(HARFBUZZ_INCLUDE_DIR hb.h PATH_SUFFIXES harfbuzz)
|
||||
find_library(HARFBUZZ_LIBRARY NAMES harfbuzz)
|
||||
if(HARFBUZZ_INCLUDE_DIR AND HARFBUZZ_LIBRARY)
|
||||
if(NOT TARGET harfbuzz::harfbuzz)
|
||||
add_library(harfbuzz::harfbuzz UNKNOWN IMPORTED)
|
||||
set_target_properties(harfbuzz::harfbuzz PROPERTIES
|
||||
IMPORTED_LOCATION "${HARFBUZZ_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${HARFBUZZ_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "HarfBuzz not found")
|
||||
endif()
|
||||
endif(NOT FREETYPE_FOUND)
|
||||
endif(FREECAD_USE_FREETYPE)
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ if(FREETYPE_FOUND)
|
||||
${Part_LIBS}
|
||||
${FREETYPE_LIBRARIES}
|
||||
)
|
||||
|
||||
set(Part_LIBS ${Part_LIBS} harfbuzz::harfbuzz)
|
||||
endif(FREETYPE_FOUND)
|
||||
|
||||
generate_from_py(Arc)
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <BRepLib.hxx>
|
||||
#include <BSplCLib.hxx>
|
||||
#include <GC_MakeArcOfCircle.hxx>
|
||||
#include <GC_MakeArcOfEllipse.hxx>
|
||||
@@ -37,6 +40,7 @@
|
||||
#include <GC_MakeEllipse.hxx>
|
||||
#include <GC_MakeHyperbola.hxx>
|
||||
#include <GC_MakeSegment.hxx>
|
||||
#include <GCE2d_MakeSegment.hxx>
|
||||
#include <GCPnts_AbscissaPoint.hxx>
|
||||
#include <gce_ErrorType.hxx>
|
||||
#include <gce_MakeParab.hxx>
|
||||
@@ -63,6 +67,8 @@
|
||||
#include <Geom_SurfaceOfRevolution.hxx>
|
||||
#include <Geom_ToroidalSurface.hxx>
|
||||
#include <Geom_TrimmedCurve.hxx>
|
||||
#include <Geom2d_BezierCurve.hxx>
|
||||
#include <Geom2d_TrimmedCurve.hxx>
|
||||
#include <GeomAPI_ExtremaCurveCurve.hxx>
|
||||
#include <GeomAPI_Interpolate.hxx>
|
||||
#include <GeomAPI_PointsToBSpline.hxx>
|
||||
@@ -86,6 +92,8 @@
|
||||
#include <gp_Pnt.hxx>
|
||||
#include <gp_Sphere.hxx>
|
||||
#include <gp_Torus.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <gp_Vec.hxx>
|
||||
#include <LProp_NotDefined.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <ShapeConstruct_Curve.hxx>
|
||||
@@ -96,6 +104,8 @@
|
||||
#include <TColgp_HArray1OfPnt.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_HArray1OfBoolean.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <ShapeFix_Wire.hxx>
|
||||
|
||||
#if OCC_VERSION_HEX < 0x070600
|
||||
# include <GeomAdaptor_HSurface.hxx>
|
||||
@@ -105,11 +115,31 @@
|
||||
#include <boost/random.hpp>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// FreeType Headers
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
#include FT_OUTLINE_H
|
||||
|
||||
#include <hb.h>
|
||||
|
||||
// headers to scale text correctly
|
||||
#include <BRepBndLib.hxx>
|
||||
#include <Bnd_Box.hxx>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/PropertyStandard.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/FileInfo.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Writer.h>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
@@ -7305,4 +7335,402 @@ std::unique_ptr<GeomCurve> makeFromCurveAdaptor(const Adaptor3d_Curve& adapt, bo
|
||||
return geoCurve;
|
||||
}
|
||||
|
||||
|
||||
// ===== TEXT TO EDGES (adapted from FT2FC.cpp) =====
|
||||
namespace
|
||||
{
|
||||
// Context for FreeType decomposition callbacks
|
||||
struct FTDC_Ctx
|
||||
{
|
||||
std::vector<TopoDS_Wire> Wires;
|
||||
std::vector<TopoDS_Edge> Edges;
|
||||
FT_Vector LastVert;
|
||||
FT_Vector StartVert;
|
||||
Handle(Geom_Surface) surf;
|
||||
};
|
||||
|
||||
// Make a TopoDS_Wire from a list of TopoDS_Edges
|
||||
TopoDS_Wire edgesToWire(std::vector<TopoDS_Edge>& Edges)
|
||||
{
|
||||
if (Edges.empty()) {
|
||||
return TopoDS_Wire();
|
||||
}
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
for (const auto& edge : Edges) {
|
||||
mkWire.Add(edge);
|
||||
}
|
||||
if (mkWire.IsDone()) {
|
||||
TopoDS_Wire wire = mkWire.Wire();
|
||||
BRepLib::BuildCurves3d(wire);
|
||||
// Ensure the wire is topologically closed and valid
|
||||
ShapeFix_Wire sfw;
|
||||
sfw.Load(wire);
|
||||
sfw.FixClosed();
|
||||
wire = sfw.Wire();
|
||||
return wire;
|
||||
}
|
||||
else {
|
||||
Base::Console().warning("edgesToWire: Failed to build a valid wire from edges.\n");
|
||||
return TopoDS_Wire();
|
||||
}
|
||||
}
|
||||
// Helper to close the current contour if needed and flush to Wires list
|
||||
void flushContour(FTDC_Ctx* dc)
|
||||
{
|
||||
if (dc->Edges.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the contour is geometrically closed.
|
||||
// If not, add a closing segment from LastVert to StartVert.
|
||||
gp_Pnt2d pStart(dc->StartVert.x, dc->StartVert.y);
|
||||
gp_Pnt2d pEnd(dc->LastVert.x, dc->LastVert.y);
|
||||
|
||||
if (!pStart.IsEqual(pEnd, Precision::Confusion())) {
|
||||
Handle(Geom2d_TrimmedCurve) lseg = GCE2d_MakeSegment(pEnd, pStart);
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(lseg, dc->surf);
|
||||
dc->Edges.push_back(edge);
|
||||
}
|
||||
|
||||
TopoDS_Wire newWire = edgesToWire(dc->Edges);
|
||||
if (!newWire.IsNull()) {
|
||||
dc->Wires.push_back(newWire);
|
||||
}
|
||||
dc->Edges.clear();
|
||||
}
|
||||
|
||||
// FT Decompose callbacks
|
||||
int move_cb(const FT_Vector* pt, void* p)
|
||||
{
|
||||
FTDC_Ctx* dc = static_cast<FTDC_Ctx*>(p);
|
||||
flushContour(dc);
|
||||
dc->StartVert = *pt;
|
||||
dc->LastVert = *pt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int line_cb(const FT_Vector* pt, void* p)
|
||||
{
|
||||
FTDC_Ctx* dc = static_cast<FTDC_Ctx*>(p);
|
||||
gp_Pnt2d v1(dc->LastVert.x, dc->LastVert.y);
|
||||
gp_Pnt2d v2(pt->x, pt->y);
|
||||
if (!v1.IsEqual(v2, Precision::Confusion())) {
|
||||
Handle(Geom2d_TrimmedCurve) lseg = GCE2d_MakeSegment(v1, v2);
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(lseg, dc->surf);
|
||||
dc->Edges.push_back(edge);
|
||||
}
|
||||
dc->LastVert = *pt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int quad_cb(const FT_Vector* pt0, const FT_Vector* pt1, void* p)
|
||||
{
|
||||
FTDC_Ctx* dc = static_cast<FTDC_Ctx*>(p);
|
||||
TColgp_Array1OfPnt2d Poles(1, 3);
|
||||
Poles.SetValue(1, gp_Pnt2d(dc->LastVert.x, dc->LastVert.y));
|
||||
Poles.SetValue(2, gp_Pnt2d(pt0->x, pt0->y));
|
||||
Poles.SetValue(3, gp_Pnt2d(pt1->x, pt1->y));
|
||||
Handle(Geom2d_BezierCurve) bcseg = new Geom2d_BezierCurve(Poles);
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(bcseg, dc->surf);
|
||||
dc->Edges.push_back(edge);
|
||||
dc->LastVert = *pt1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cubic_cb(const FT_Vector* pt0, const FT_Vector* pt1, const FT_Vector* pt2, void* p)
|
||||
{
|
||||
FTDC_Ctx* dc = static_cast<FTDC_Ctx*>(p);
|
||||
TColgp_Array1OfPnt2d Poles(1, 4);
|
||||
Poles.SetValue(1, gp_Pnt2d(dc->LastVert.x, dc->LastVert.y));
|
||||
Poles.SetValue(2, gp_Pnt2d(pt0->x, pt0->y));
|
||||
Poles.SetValue(3, gp_Pnt2d(pt1->x, pt1->y));
|
||||
Poles.SetValue(4, gp_Pnt2d(pt2->x, pt2->y));
|
||||
Handle(Geom2d_BezierCurve) bcseg = new Geom2d_BezierCurve(Poles);
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(bcseg, dc->surf);
|
||||
dc->Edges.push_back(edge);
|
||||
dc->LastVert = *pt2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
/**
|
||||
* @brief Takes a set of base shapes, transforms them to fit a two-point
|
||||
* definition, and converts them to a vector of Part::Geometry.
|
||||
*
|
||||
* This is the core transformation logic shared by textToEdges and the Symbol tool.
|
||||
*
|
||||
* @param geos Output vector of unique_ptr to Part::Geometry.
|
||||
* @param baseShapes Input vector of raw TopoDS_Shape objects at origin.
|
||||
* @param p1 The start point (typically bottom-left) of placement.
|
||||
* @param p2 The end point, which defines the size and orientation.
|
||||
* @param height If true, the distance p1-p2 defines the height.
|
||||
* If false, it defines the width.
|
||||
*/
|
||||
void transformAndConvertToGeometry(
|
||||
std::vector<std::unique_ptr<Part::Geometry>>& geos,
|
||||
const std::vector<TopoDS_Shape>& baseShapes,
|
||||
const Base::Vector3d& p1,
|
||||
const Base::Vector3d& p2,
|
||||
bool height
|
||||
)
|
||||
{
|
||||
if (baseShapes.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Base::Vector3d dir = p2 - p1;
|
||||
double length = dir.Length();
|
||||
if (length < Precision::Confusion()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Calculate the bounding box of the base shapes
|
||||
Bnd_Box bndBox;
|
||||
for (const auto& shape : baseShapes) {
|
||||
if (!shape.IsNull()) {
|
||||
BRepBndLib::Add(shape, bndBox);
|
||||
}
|
||||
}
|
||||
|
||||
if (bndBox.IsVoid()) {
|
||||
Base::Console().warning(
|
||||
"transformAndConvertToGeometry: Could not determine bounds of generated geometry.\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
|
||||
bndBox.Get(xmin, ymin, zmin, xmax, ymax, zmax);
|
||||
double baseWidth = xmax - xmin;
|
||||
double baseHeight = ymax - ymin;
|
||||
|
||||
// This transform will move the geometry's bottom-left corner to the origin (0,0,0)
|
||||
gp_Vec initialTranslationVec(-xmin, -ymin, 0.0);
|
||||
|
||||
// 2. Determine scale and rotation
|
||||
double angle;
|
||||
double scale;
|
||||
|
||||
if (height) {
|
||||
if (baseHeight < Precision::Confusion()) {
|
||||
return;
|
||||
}
|
||||
scale = length / baseHeight;
|
||||
angle = std::atan2(dir.y, dir.x) - 0.5 * M_PI;
|
||||
}
|
||||
else { // Width mode
|
||||
if (baseWidth < Precision::Confusion()) {
|
||||
return;
|
||||
}
|
||||
scale = length / baseWidth;
|
||||
angle = std::atan2(dir.y, dir.x);
|
||||
}
|
||||
|
||||
// 3. Construct the final transformation matrix
|
||||
gp_Trsf initialTranslate;
|
||||
initialTranslate.SetTranslation(initialTranslationVec);
|
||||
gp_Trsf scaleTrsf;
|
||||
scaleTrsf.SetScale(gp::Origin(), scale);
|
||||
gp_Trsf rotateTrsf;
|
||||
rotateTrsf.SetRotation(gp::XOY().Axis(), angle);
|
||||
gp_Trsf finalTranslate;
|
||||
finalTranslate.SetTranslation(gp_Vec(p1.x, p1.y, 0.0));
|
||||
gp_Trsf finalTrsf = finalTranslate * rotateTrsf * scaleTrsf * initialTranslate;
|
||||
|
||||
// 4. Apply transformation and convert to Sketcher geometry
|
||||
for (const auto& shape : baseShapes) {
|
||||
BRepBuilderAPI_Transform performer(shape, finalTrsf, true);
|
||||
if (!performer.IsDone()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (TopExp_Explorer explorer(performer.Shape(), TopAbs_EDGE); explorer.More();
|
||||
explorer.Next()) {
|
||||
Standard_Real first, last;
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(explorer.Current());
|
||||
Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, first, last);
|
||||
if (curve.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<Part::GeomCurve> newGeo;
|
||||
|
||||
if (BRep_Tool::IsClosed(edge)) {
|
||||
newGeo = Part::makeFromCurve(curve);
|
||||
}
|
||||
else {
|
||||
if (curve->IsKind(STANDARD_TYPE(Geom_TrimmedCurve))) {
|
||||
Handle(Geom_TrimmedCurve) trc = Handle(Geom_TrimmedCurve)::DownCast(curve);
|
||||
curve = trc->BasisCurve();
|
||||
}
|
||||
|
||||
if (curve->IsKind(STANDARD_TYPE(Geom_BezierCurve))) {
|
||||
Handle(Geom_TrimmedCurve) tcurve
|
||||
= new Geom_TrimmedCurve(curve, first, last, true, false);
|
||||
Part::GeomTrimmedCurve geomcurve(tcurve);
|
||||
newGeo.reset(geomcurve.toBSpline(first, last));
|
||||
}
|
||||
else {
|
||||
newGeo = Part::makeFromTrimmedCurve(curve, first, last);
|
||||
}
|
||||
}
|
||||
|
||||
if (!newGeo) {
|
||||
Base::Console().warning(
|
||||
"transformAndConvertToGeometry: Could not create geometry from curve.\n"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
geos.emplace_back(std::move(newGeo));
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Base::Console().warning("BSpline conversion failed: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The core logic, refactored from FT2FC to be Python-independent
|
||||
std::vector<TopoDS_Shape> makeTextWires(
|
||||
std::string& text,
|
||||
std::string& fontFile,
|
||||
double height,
|
||||
double tracking
|
||||
)
|
||||
{
|
||||
if (text.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (fontFile.empty()) {
|
||||
#if defined(FC_OS_LINUX)
|
||||
fontFile = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf";
|
||||
#elif defined(FC_OS_WIN32)
|
||||
fontFile = "C:/Windows/Fonts/Arial.ttf";
|
||||
#elif defined(FC_OS_MAC)
|
||||
fontFile = "/System/Library/Fonts/Helvetica.ttc"; // Common system default
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<TopoDS_Shape> allWires;
|
||||
FT_Library ftLib;
|
||||
if (FT_Init_FreeType(&ftLib) != 0) {
|
||||
Base::Console().error("makeTextWires: Could not initialize FreeType library\n");
|
||||
return allWires;
|
||||
}
|
||||
|
||||
std::ifstream fontStream(fontFile, std::ios::binary);
|
||||
if (!fontStream) {
|
||||
Base::Console().error("makeTextWires: Cannot open font file: %s\n", fontFile.c_str());
|
||||
FT_Done_FreeType(ftLib);
|
||||
return allWires;
|
||||
}
|
||||
std::vector<char> fontBuffer(
|
||||
(std::istreambuf_iterator<char>(fontStream)),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
|
||||
FT_Face ftFace;
|
||||
if (FT_New_Memory_Face(
|
||||
ftLib,
|
||||
reinterpret_cast<const FT_Byte*>(fontBuffer.data()),
|
||||
fontBuffer.size(),
|
||||
0,
|
||||
&ftFace
|
||||
)
|
||||
!= 0) {
|
||||
Base::Console().error("makeTextWires: Failed to load font face from %s\n", fontFile.c_str());
|
||||
FT_Done_FreeType(ftLib);
|
||||
return allWires;
|
||||
}
|
||||
|
||||
// Use the font's native units for maximum precision
|
||||
double unitsPerEM = static_cast<double>(ftFace->units_per_EM);
|
||||
if (unitsPerEM < 1.0) {
|
||||
unitsPerEM = 2048.0; // Fallback
|
||||
}
|
||||
|
||||
// We want a nominal height of 1.0 for the base shapes
|
||||
double scaleFactor = (height / unitsPerEM);
|
||||
FT_Outline_Funcs ftCallbacks = {move_cb, line_cb, quad_cb, cubic_cb, 0, 0};
|
||||
FT_UInt ftLoadFlags = FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP;
|
||||
|
||||
// Use HarfBuzz for text shaping. Positions are in font design units (upem),
|
||||
// matching our FT_LOAD_NO_SCALE outline decomposition.
|
||||
auto* hbBlob = hb_blob_create(
|
||||
fontBuffer.data(),
|
||||
static_cast<unsigned int>(fontBuffer.size()),
|
||||
HB_MEMORY_MODE_READONLY,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
auto* hbFace = hb_face_create(hbBlob, 0);
|
||||
auto* hbFont = hb_font_create(hbFace);
|
||||
|
||||
auto* hbBuf = hb_buffer_create();
|
||||
hb_buffer_add_utf8(hbBuf, text.c_str(), -1, 0, -1);
|
||||
hb_buffer_guess_segment_properties(hbBuf);
|
||||
|
||||
hb_shape(hbFont, hbBuf, nullptr, 0);
|
||||
|
||||
unsigned int glyphCount = 0;
|
||||
auto* glyphInfos = hb_buffer_get_glyph_infos(hbBuf, &glyphCount);
|
||||
auto* glyphPositions = hb_buffer_get_glyph_positions(hbBuf, &glyphCount);
|
||||
|
||||
double penPos = 0.0;
|
||||
double currentTracking = 0.0;
|
||||
|
||||
for (unsigned int i = 0; i < glyphCount; ++i) {
|
||||
FT_UInt glyphIndex = glyphInfos[i].codepoint;
|
||||
double xOffset = glyphPositions[i].x_offset;
|
||||
double xAdvance = glyphPositions[i].x_advance;
|
||||
|
||||
if (FT_Load_Glyph(ftFace, glyphIndex, ftLoadFlags) != 0) {
|
||||
penPos += xAdvance;
|
||||
currentTracking += tracking;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ftFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE
|
||||
&& ftFace->glyph->outline.n_contours > 0) {
|
||||
FTDC_Ctx ctx;
|
||||
ctx.surf = new Geom_Plane(gp::Origin(), gp::DZ());
|
||||
FT_Outline_Decompose(&ftFace->glyph->outline, &ftCallbacks, &ctx);
|
||||
|
||||
flushContour(&ctx);
|
||||
|
||||
if (!ctx.Wires.empty()) {
|
||||
gp_Trsf charTransform;
|
||||
charTransform.SetScale(gp::Origin(), scaleFactor);
|
||||
gp_Vec translation((penPos + xOffset) * scaleFactor + currentTracking, 0.0, 0.0);
|
||||
charTransform.SetTranslationPart(translation);
|
||||
|
||||
for (const auto& wire : ctx.Wires) {
|
||||
BRepBuilderAPI_Transform performer(charTransform);
|
||||
performer.Perform(wire, true); // true = create a copy
|
||||
if (performer.IsDone()) {
|
||||
allWires.push_back(performer.Shape());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
penPos += xAdvance;
|
||||
currentTracking += tracking;
|
||||
}
|
||||
|
||||
hb_buffer_destroy(hbBuf);
|
||||
hb_font_destroy(hbFont);
|
||||
hb_face_destroy(hbFace);
|
||||
hb_blob_destroy(hbBlob);
|
||||
|
||||
FT_Done_Face(ftFace);
|
||||
FT_Done_FreeType(ftLib);
|
||||
return allWires;
|
||||
}
|
||||
|
||||
} // namespace Part
|
||||
|
||||
@@ -1417,4 +1417,34 @@ PartExport std::unique_ptr<GeomCurve> makeFromTrimmedCurve(
|
||||
);
|
||||
|
||||
PartExport std::unique_ptr<GeomCurve> makeFromCurveAdaptor(const Adaptor3d_Curve&, bool silent = false);
|
||||
|
||||
/**
|
||||
* @brief Creates a series of edges representing a text string.
|
||||
*
|
||||
* This function generates geometric edges for a given text string using a specified font file.
|
||||
* The text is scaled and positioned within a bounding box defined by two points.
|
||||
*
|
||||
* @param geos Output vector to be populated with geometry (GeomTrimmedCurve). The vector is cleared
|
||||
* first.
|
||||
* @param p1 The origin point for the text's baseline.
|
||||
* @param p2 A point used to define the height and orientation. The text height will be
|
||||
* (p2-p1).Length() and its orientation angle will be the angle of the vector (p2-p1).
|
||||
* @param plainText The string to be rendered.
|
||||
* @param fontFile The absolute path to the TTF, OTF, etc., font file.
|
||||
* @param tracking Additional spacing between characters.
|
||||
*/
|
||||
PartExport void transformAndConvertToGeometry(
|
||||
std::vector<std::unique_ptr<Part::Geometry>>& geos,
|
||||
const std::vector<TopoDS_Shape>& baseShapes,
|
||||
const Base::Vector3d& p1,
|
||||
const Base::Vector3d& p2,
|
||||
bool height
|
||||
);
|
||||
|
||||
PartExport std::vector<TopoDS_Shape> makeTextWires(
|
||||
std::string& text,
|
||||
std::string& fontFile,
|
||||
double height = 1.0,
|
||||
double tracking = 0.0
|
||||
);
|
||||
} // namespace Part
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
include_directories(
|
||||
SYSTEM
|
||||
${CMAKE_SOURCE_DIR}/src/3rdParty/json/single_include/nlohmann/
|
||||
)
|
||||
|
||||
add_library(Sketcher SHARED)
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include <Base/Reader.h>
|
||||
@@ -90,6 +92,7 @@ Constraint* Constraint::copy() const
|
||||
temp->isActive = this->isActive;
|
||||
temp->elements = this->elements;
|
||||
// Do not copy tag, otherwise it is considered a clone, and a "rename" by the expression engine.
|
||||
temp->MetaData = this->MetaData;
|
||||
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
temp->First = this->First;
|
||||
@@ -149,8 +152,10 @@ unsigned int Constraint::getMemSize() const
|
||||
void Constraint::Save(Writer& writer) const
|
||||
{
|
||||
std::string encodeName = encodeAttribute(Name);
|
||||
std::string encodeMetaData = encodeAttribute(MetaData);
|
||||
writer.Stream() << writer.ind() << "<Constrain "
|
||||
<< "Name=\"" << encodeName << "\" "
|
||||
<< "MetaData=\"" << encodeMetaData << "\" "
|
||||
<< "Type=\"" << (int)Type << "\" ";
|
||||
if (this->Type == InternalAlignment) {
|
||||
writer.Stream() << "InternalAlignmentType=\"" << (int)AlignmentType << "\" "
|
||||
@@ -195,6 +200,7 @@ void Constraint::Restore(XMLReader& reader)
|
||||
{
|
||||
reader.readElement("Constrain");
|
||||
Name = reader.getAttribute<const char*>("Name");
|
||||
MetaData = reader.hasAttribute("MetaData") ? reader.getAttribute<const char*>("MetaData") : "";
|
||||
Type = reader.getAttribute<ConstraintType>("Type");
|
||||
Value = reader.getAttribute<double>("Value");
|
||||
|
||||
@@ -391,32 +397,31 @@ GeoElementId Constraint::getElement(size_t index) const
|
||||
#endif
|
||||
return elements[index];
|
||||
}
|
||||
|
||||
void Constraint::setElement(size_t index, GeoElementId element)
|
||||
{
|
||||
if (index >= elements.size()) {
|
||||
throw Base::IndexError("Constraint::getElement index out of range");
|
||||
}
|
||||
|
||||
elements[index] = element;
|
||||
if (ensureElementExists(index)) {
|
||||
elements[index] = element;
|
||||
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
First = element.GeoId;
|
||||
FirstPos = element.Pos;
|
||||
break;
|
||||
case 1:
|
||||
Second = element.GeoId;
|
||||
SecondPos = element.Pos;
|
||||
break;
|
||||
case 2:
|
||||
Third = element.GeoId;
|
||||
ThirdPos = element.Pos;
|
||||
break;
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
First = element.GeoId;
|
||||
FirstPos = element.Pos;
|
||||
break;
|
||||
case 1:
|
||||
Second = element.GeoId;
|
||||
SecondPos = element.Pos;
|
||||
break;
|
||||
case 2:
|
||||
Third = element.GeoId;
|
||||
ThirdPos = element.Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
size_t Constraint::getElementsSize() const
|
||||
@@ -434,3 +439,249 @@ void Constraint::addElement(GeoElementId element)
|
||||
elements.push_back(element);
|
||||
#endif
|
||||
}
|
||||
|
||||
int Constraint::getGeoId(int index) const
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return First;
|
||||
case 1:
|
||||
return Second;
|
||||
case 2:
|
||||
return Third;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return hasElement(index) ? elements[index].GeoId : GeoEnum::GeoUndef;
|
||||
}
|
||||
|
||||
PointPos Constraint::getPosId(int index) const
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return FirstPos;
|
||||
case 1:
|
||||
return SecondPos;
|
||||
case 2:
|
||||
return ThirdPos;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return hasElement(index) ? elements[index].Pos : PointPos::none;
|
||||
}
|
||||
|
||||
int Constraint::getPosIdAsInt(int index) const
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return (int)FirstPos;
|
||||
case 1:
|
||||
return (int)SecondPos;
|
||||
case 2:
|
||||
return (int)ThirdPos;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return hasElement(index) ? elements[index].posIdAsInt() : 0;
|
||||
}
|
||||
|
||||
bool Constraint::hasElement(int index) const
|
||||
{
|
||||
return index >= 0 && index < elements.size();
|
||||
}
|
||||
|
||||
void Constraint::setGeoId(int index, int geoId)
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
First = geoId;
|
||||
break;
|
||||
case 1:
|
||||
Second = geoId;
|
||||
break;
|
||||
case 2:
|
||||
Third = geoId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ensureElementExists(index)) {
|
||||
elements[index].GeoId = geoId;
|
||||
}
|
||||
}
|
||||
|
||||
void Constraint::setPosId(int index, PointPos pos)
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
FirstPos = pos;
|
||||
break;
|
||||
case 1:
|
||||
SecondPos = pos;
|
||||
break;
|
||||
case 2:
|
||||
ThirdPos = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ensureElementExists(index)) {
|
||||
elements[index].Pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
void Constraint::setPosId(int index, int pos)
|
||||
{
|
||||
#if SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
if (index < 3) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
FirstPos = static_cast<PointPos>(pos);
|
||||
break;
|
||||
case 1:
|
||||
SecondPos = static_cast<PointPos>(pos);
|
||||
break;
|
||||
case 2:
|
||||
ThirdPos = static_cast<PointPos>(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ensureElementExists(index)) {
|
||||
elements[index].Pos = static_cast<PointPos>(pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool Constraint::ensureElementExists(int index)
|
||||
{
|
||||
if (index < 0) {
|
||||
return false; // Indicate failure for an invalid index
|
||||
}
|
||||
if (index >= elements.size()) {
|
||||
elements.resize(index + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Constraint::swapElements(int index1, int index2)
|
||||
{
|
||||
if (index1 == index2) {
|
||||
return;
|
||||
}
|
||||
if (ensureElementExists(index1) && ensureElementExists(index2)) {
|
||||
std::swap(elements[index1], elements[index2]);
|
||||
}
|
||||
}
|
||||
|
||||
bool Constraint::isElementsEmpty() const
|
||||
{
|
||||
return elements.empty();
|
||||
}
|
||||
|
||||
void Constraint::truncateElements(size_t newSize)
|
||||
{
|
||||
if (newSize < elements.size()) {
|
||||
elements.resize(newSize);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Constraint::getText() const
|
||||
{
|
||||
if (MetaData.empty()) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
auto j = nlohmann::json::parse(MetaData);
|
||||
if (j.contains("text")) {
|
||||
return j["text"].get<std::string>();
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// Handle JSON parsing errors or type mismatches silently
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void Constraint::setText(const std::string& text)
|
||||
{
|
||||
nlohmann::json j;
|
||||
if (!MetaData.empty()) {
|
||||
try {
|
||||
j = nlohmann::json::parse(MetaData);
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
}
|
||||
j["text"] = text;
|
||||
MetaData = j.dump();
|
||||
}
|
||||
|
||||
std::string Constraint::getFont() const
|
||||
{
|
||||
if (MetaData.empty()) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
auto j = nlohmann::json::parse(MetaData);
|
||||
if (j.contains("font")) {
|
||||
return j["font"].get<std::string>();
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void Constraint::setFont(const std::string& font)
|
||||
{
|
||||
nlohmann::json j;
|
||||
if (!MetaData.empty()) {
|
||||
try {
|
||||
j = nlohmann::json::parse(MetaData);
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
}
|
||||
j["font"] = font;
|
||||
MetaData = j.dump();
|
||||
}
|
||||
|
||||
bool Constraint::getIsTextHeight() const
|
||||
{
|
||||
if (MetaData.empty()) {
|
||||
return true; // Default value
|
||||
}
|
||||
try {
|
||||
auto j = nlohmann::json::parse(MetaData);
|
||||
if (j.contains("isTextHeight")) {
|
||||
return j["isTextHeight"].get<bool>();
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
return true; // Default value
|
||||
}
|
||||
|
||||
void Constraint::setIsTextHeight(bool isHeight)
|
||||
{
|
||||
nlohmann::json j;
|
||||
if (!MetaData.empty()) {
|
||||
try {
|
||||
j = nlohmann::json::parse(MetaData);
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
}
|
||||
j["isTextHeight"] = isHeight;
|
||||
MetaData = j.dump();
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ enum ConstraintType : int
|
||||
Block = 17,
|
||||
Diameter = 18,
|
||||
Weight = 19,
|
||||
Group = 20,
|
||||
Text = 21,
|
||||
NumConstraintTypes // must be the last item!
|
||||
};
|
||||
|
||||
@@ -189,7 +191,9 @@ private:
|
||||
"SnellsLaw",
|
||||
"Block",
|
||||
"Diameter",
|
||||
"Weight"}};
|
||||
"Weight",
|
||||
"Group",
|
||||
"Text"}};
|
||||
// clang-format on
|
||||
|
||||
constexpr static std::array<const char*, InternalAlignmentType::NumInternalAlignmentType>
|
||||
@@ -212,6 +216,7 @@ public:
|
||||
ConstraintType Type {None};
|
||||
InternalAlignmentType AlignmentType {Undef};
|
||||
std::string Name;
|
||||
std::string MetaData;
|
||||
float LabelDistance {10.F};
|
||||
float LabelPosition {0.F};
|
||||
bool isDriving {true};
|
||||
@@ -225,8 +230,26 @@ public:
|
||||
|
||||
GeoElementId getElement(size_t index) const;
|
||||
void setElement(size_t index, GeoElementId element);
|
||||
size_t getElementsSize() const;
|
||||
void addElement(GeoElementId element);
|
||||
bool hasElement(int index) const;
|
||||
size_t getElementsSize() const;
|
||||
bool isElementsEmpty() const;
|
||||
void truncateElements(size_t newSize);
|
||||
int getGeoId(int index) const;
|
||||
PointPos getPosId(int index) const;
|
||||
int getPosIdAsInt(int index) const;
|
||||
void setGeoId(int index, int geoId);
|
||||
void setPosId(int index, PointPos pos);
|
||||
void setPosId(int index, int pos);
|
||||
void swapElements(int index1, int index2);
|
||||
bool ensureElementExists(int index);
|
||||
|
||||
std::string getText() const;
|
||||
void setText(const std::string& text);
|
||||
std::string getFont() const;
|
||||
void setFont(const std::string& font);
|
||||
bool getIsTextHeight() const;
|
||||
void setIsTextHeight(bool val);
|
||||
|
||||
#ifdef SKETCHER_CONSTRAINT_USE_LEGACY_ELEMENTS
|
||||
// Deprecated, use getElement/setElement instead
|
||||
|
||||
@@ -97,13 +97,104 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
|
||||
PyObject* index_or_value;
|
||||
PyObject* oNumArg4;
|
||||
PyObject* oNumArg5;
|
||||
PyObject* py_elements_list = nullptr;
|
||||
char* text_str = nullptr; // Variable for the text content
|
||||
char* font_str = nullptr; // Variable for the font name
|
||||
int any_index;
|
||||
|
||||
PyObject* activated;
|
||||
PyObject* driving;
|
||||
PyObject* py_is_height = nullptr;
|
||||
|
||||
Sketcher::Constraint* constraint = this->getConstraintPtr();
|
||||
|
||||
auto parseElementsList = [](PyObject* list, Sketcher::Constraint* constr_ptr) -> bool {
|
||||
Py_ssize_t list_size = PyList_Size(list);
|
||||
|
||||
// The list should contain pairs of (geoId, posId), so its size must be even.
|
||||
if (list_size % 2 != 0) {
|
||||
PyErr_SetString(
|
||||
PyExc_ValueError,
|
||||
"Element list must have an even number of items (pairs of GeoId, PosId)."
|
||||
);
|
||||
return false; // Failure
|
||||
}
|
||||
|
||||
constr_ptr->truncateElements(0);
|
||||
|
||||
for (Py_ssize_t i = 0; i < list_size; i += 2) {
|
||||
PyObject* py_geoId_obj = PyList_GetItem(list, i);
|
||||
PyObject* py_posId_obj = PyList_GetItem(list, i + 1);
|
||||
|
||||
// Perform crucial type checking on list items
|
||||
if (!py_geoId_obj || !py_posId_obj || !PyLong_Check(py_geoId_obj)
|
||||
|| !PyLong_Check(py_posId_obj)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Element list items must be integers.");
|
||||
return false; // Failure
|
||||
}
|
||||
|
||||
int geoId = PyLong_AsLong(py_geoId_obj);
|
||||
int posId = PyLong_AsLong(py_posId_obj);
|
||||
|
||||
// Use the C++ API to populate the constraint
|
||||
constr_ptr->setElement(
|
||||
i / 2,
|
||||
Sketcher::GeoElementId(geoId, static_cast<Sketcher::PointPos>(posId))
|
||||
);
|
||||
}
|
||||
|
||||
return true; // Success
|
||||
};
|
||||
|
||||
// Attempt to parse (string, list) for 'Group'
|
||||
if (PyArg_ParseTuple(args, "sO!", &ConstraintType, &PyList_Type, &py_elements_list)) {
|
||||
if (strcmp(ConstraintType, "Group") == 0) {
|
||||
constraint->Type = Sketcher::Group;
|
||||
if (!parseElementsList(py_elements_list, constraint)) {
|
||||
return -1; // The lambda set the Python error, so just return.
|
||||
}
|
||||
return 0; // Success!
|
||||
}
|
||||
}
|
||||
PyErr_Clear();
|
||||
|
||||
// Attempt to parse (string, list, string, string, bool) for 'Text'
|
||||
if (PyArg_ParseTuple(
|
||||
args,
|
||||
"sO!ss|O",
|
||||
&ConstraintType,
|
||||
&PyList_Type,
|
||||
&py_elements_list,
|
||||
&text_str,
|
||||
&font_str,
|
||||
&py_is_height
|
||||
)) {
|
||||
|
||||
if (strcmp(ConstraintType, "Text") == 0) {
|
||||
constraint->Type = Sketcher::Text;
|
||||
|
||||
// Call the shared lambda for list parsing
|
||||
if (!parseElementsList(py_elements_list, constraint)) {
|
||||
return -1; // The lambda set the Python error.
|
||||
}
|
||||
|
||||
// Set the specific members for the Text constraint
|
||||
constraint->setText(text_str);
|
||||
constraint->setFont(font_str);
|
||||
|
||||
// Check and set the optional boolean
|
||||
if (py_is_height && PyBool_Check(py_is_height)) {
|
||||
constraint->setIsTextHeight(py_is_height == Py_True);
|
||||
}
|
||||
else {
|
||||
constraint->setIsTextHeight(true);
|
||||
}
|
||||
|
||||
return 0; // Success!
|
||||
}
|
||||
}
|
||||
PyErr_Clear();
|
||||
|
||||
auto handleSi = [&]() -> bool {
|
||||
if (strcmp("Horizontal", ConstraintType) == 0) {
|
||||
constraint->Type = Horizontal;
|
||||
@@ -904,6 +995,12 @@ std::string ConstraintPy::representation() const
|
||||
result << "'PointOnObject' (" << getConstraintPtr()->First << ","
|
||||
<< getConstraintPtr()->Second << ")>";
|
||||
break;
|
||||
case Group:
|
||||
result << "'Group'>";
|
||||
break;
|
||||
case Text:
|
||||
result << "'Text'>";
|
||||
break;
|
||||
default:
|
||||
result << "'?'>";
|
||||
break;
|
||||
@@ -974,6 +1071,12 @@ Py::String ConstraintPy::getType() const
|
||||
case PointOnObject:
|
||||
return Py::String("PointOnObject");
|
||||
break;
|
||||
case Group:
|
||||
return Py::String("Group");
|
||||
break;
|
||||
case Text:
|
||||
return Py::String("Text");
|
||||
break;
|
||||
default:
|
||||
return Py::String("Undefined");
|
||||
break;
|
||||
|
||||
@@ -197,6 +197,17 @@ int Sketch::setUpSketch(
|
||||
|
||||
clear();
|
||||
|
||||
// The geometries that are in groups are going to be ignored by the solver.
|
||||
std::set<int> inGroupGeoIds;
|
||||
for (const auto& c : ConstraintList) {
|
||||
if (c->Type == Group || c->Type == Text) {
|
||||
// Start from index 1, as 0 is the frame.
|
||||
for (int i = 1; c->hasElement(i); ++i) {
|
||||
inGroupGeoIds.insert(c->getGeoId(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Part::Geometry*> intGeoList, extGeoList;
|
||||
std::copy(GeoList.begin(), GeoList.end() - extGeoCount, std::back_inserter(intGeoList));
|
||||
std::copy(GeoList.end() - extGeoCount, GeoList.end(), std::back_inserter(extGeoList));
|
||||
@@ -248,7 +259,7 @@ int Sketch::setUpSketch(
|
||||
|
||||
buildInternalAlignmentGeometryMap(ConstraintList);
|
||||
|
||||
addGeometry(intGeoList, onlyBlockedGeometry);
|
||||
addGeometry(intGeoList, onlyBlockedGeometry, inGroupGeoIds);
|
||||
int extStart = Geoms.size();
|
||||
addGeometry(extGeoList, true);
|
||||
int extEnd = Geoms.size() - 1;
|
||||
@@ -258,6 +269,22 @@ int Sketch::setUpSketch(
|
||||
|
||||
// The Geoms list might be empty after an undo/redo
|
||||
if (!Geoms.empty()) {
|
||||
// Disable any constraint that act on geometries that are in a group.
|
||||
for (size_t i = 0; i < ConstraintList.size(); ++i) {
|
||||
const auto& c = ConstraintList[i];
|
||||
|
||||
if (c->Type == Group || c->Type == Text) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; c->hasElement(j); ++j) {
|
||||
if (inGroupGeoIds.count(c->getGeoId(j))) {
|
||||
unenforceableConstraints[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addConstraints(ConstraintList, unenforceableConstraints);
|
||||
}
|
||||
clearTemporaryConstraints();
|
||||
@@ -731,18 +758,33 @@ int Sketch::addGeometry(const std::vector<Part::Geometry*>& geos, bool fixed)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Sketch::addGeometry(const std::vector<Part::Geometry*>& geos, const std::vector<bool>& blockedGeometry)
|
||||
int Sketch::addGeometry(
|
||||
const std::vector<Part::Geometry*>& geos,
|
||||
const std::vector<bool>& blockedGeometry,
|
||||
const std::set<int>& inGroupGeoIds
|
||||
)
|
||||
{
|
||||
assert(geos.size() == blockedGeometry.size());
|
||||
|
||||
int ret = -1;
|
||||
int geoIdCounter = 0;
|
||||
std::vector<Part::Geometry*>::const_iterator it;
|
||||
std::vector<bool>::const_iterator bit;
|
||||
|
||||
for (it = geos.begin(), bit = blockedGeometry.begin();
|
||||
it != geos.end() && bit != blockedGeometry.end();
|
||||
++it, ++bit) {
|
||||
ret = addGeometry(*it, *bit);
|
||||
++it, ++bit, ++geoIdCounter) {
|
||||
|
||||
// Check if the current geometry is in group.
|
||||
bool isInGroup = inGroupGeoIds.count(geoIdCounter);
|
||||
if (isInGroup) {
|
||||
GeoDef def;
|
||||
def.geo = (*it)->clone();
|
||||
Geoms.push_back(def);
|
||||
}
|
||||
else {
|
||||
ret = addGeometry(*it, *bit);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1866,6 +1908,7 @@ int Sketch::checkGeoId(int geoId) const
|
||||
geoId += Geoms.size(); // convert negative external-geometry index to index into Geoms
|
||||
}
|
||||
if (!(geoId >= 0 && geoId < int(Geoms.size()))) {
|
||||
Base::Console().warning("geoId %d Geoms.size %d\n", geoId, int(Geoms.size()));
|
||||
throw Base::IndexError("Sketch::checkGeoId. GeoId index out range.");
|
||||
}
|
||||
return geoId;
|
||||
@@ -2471,6 +2514,19 @@ int Sketch::addConstraint(const Constraint* constraint)
|
||||
c.driving
|
||||
);
|
||||
} break;
|
||||
case Text:
|
||||
case Group: {
|
||||
if (constraint->isElementsEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
// Check that the first element is correctly the group construction line
|
||||
if (Geoms[checkGeoId(constraint->getGeoId(0))].type != Line) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rtn = ++ConstraintsCounter;
|
||||
break;
|
||||
}
|
||||
case Sketcher::None: // ambiguous enum value
|
||||
case Sketcher::Block: // handled separately while adding geometry
|
||||
case NumConstraintTypes:
|
||||
@@ -4759,6 +4815,8 @@ bool Sketch::updateNonDrivingConstraints()
|
||||
|
||||
int Sketch::solve()
|
||||
{
|
||||
captureGroupStates();
|
||||
|
||||
Base::TimeElapsed start_time;
|
||||
std::string solvername;
|
||||
|
||||
@@ -4777,6 +4835,10 @@ int Sketch::solve()
|
||||
|
||||
SolveTime = Base::TimeElapsed::diffTimeF(start_time, end_time);
|
||||
|
||||
if (result == GCS::Success) {
|
||||
applyGroupTransformations();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -5513,3 +5575,112 @@ void Sketch::Save(Writer&) const
|
||||
|
||||
void Sketch::Restore(XMLReader&)
|
||||
{}
|
||||
|
||||
// Group functions related -------------------------------------------------
|
||||
|
||||
Sketch::GroupLineState Sketch::getGroupLineState(int geoId) const
|
||||
{
|
||||
GroupLineState state;
|
||||
state.startPoint = getPoint(geoId, PointPos::start);
|
||||
state.endPoint = getPoint(geoId, PointPos::end);
|
||||
return state;
|
||||
}
|
||||
|
||||
void Sketch::captureGroupStates()
|
||||
{
|
||||
preSolveGroupStates.clear();
|
||||
|
||||
// A set to keep track of which parameters we've already moved.
|
||||
std::set<double*> movedParams;
|
||||
|
||||
for (const auto& constrDef : Constrs) {
|
||||
const Constraint* c = constrDef.constr;
|
||||
|
||||
if ((c->Type != Group && c->Type != Text) || !c->hasElement(1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- Capture Frame State ---
|
||||
int frameGeoId = c->getGeoId(0);
|
||||
preSolveGroupStates[frameGeoId] = getGroupLineState(frameGeoId);
|
||||
}
|
||||
}
|
||||
|
||||
void Sketch::applyGroupTransformations()
|
||||
{
|
||||
if (preSolveGroupStates.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& constrDef : Constrs) {
|
||||
const Constraint* c = constrDef.constr;
|
||||
if ((c->Type != Group && c->Type != Text) || !c->hasElement(1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int frameGeoId = c->getGeoId(0);
|
||||
|
||||
// Get the "before" and "after" states of the frame line
|
||||
GroupLineState preSolveFrame = preSolveGroupStates.at(frameGeoId);
|
||||
GroupLineState postSolveFrame = getGroupLineState(frameGeoId);
|
||||
|
||||
// --- Calculate the Transformation ---
|
||||
Base::Vector3d preVec = preSolveFrame.getVec();
|
||||
Base::Vector3d postVec = postSolveFrame.getVec();
|
||||
|
||||
// Handle potential zero-length lines to avoid division by zero
|
||||
double preLen = preVec.Length();
|
||||
double scale = (preLen > Precision::Confusion()) ? postVec.Length() / preLen : 1.0;
|
||||
|
||||
// --- Create the Transformation Matrix ---
|
||||
|
||||
// 1. T1: Matrix to translate the group to the origin (using pre-solve start point)
|
||||
Base::Matrix4D T1; // Identity
|
||||
T1[0][3] = -preSolveFrame.startPoint.x;
|
||||
T1[1][3] = -preSolveFrame.startPoint.y;
|
||||
T1[2][3] = 0;
|
||||
|
||||
// 2. S: Matrix for scaling
|
||||
Base::Matrix4D S; // Identity
|
||||
S[0][0] = scale;
|
||||
S[1][1] = scale;
|
||||
S[2][2] = scale;
|
||||
|
||||
// 3. R: Matrix for rotation
|
||||
Base::Matrix4D R; // Identity
|
||||
if (preLen > Precision::Confusion()) {
|
||||
// We can get the axis and angle from the two vectors and use rotLine
|
||||
Base::Vector3d rotationAxis = preVec.Cross(postVec);
|
||||
double rotationAngle = preVec.GetAngle(postVec);
|
||||
// Only apply rotation if the vectors are not collinear
|
||||
if (rotationAxis.Length() > Precision::Confusion()) {
|
||||
R.rotLine(rotationAxis, rotationAngle);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. T2: Matrix to translate the group to its new final position
|
||||
Base::Matrix4D T2; // Identity
|
||||
T2[0][3] = postSolveFrame.startPoint.x;
|
||||
T2[1][3] = postSolveFrame.startPoint.y;
|
||||
T2[2][3] = 0;
|
||||
|
||||
// 5. Combine the matrices in the correct order: T_final = T2 * R * S * T1
|
||||
Base::Matrix4D transform = T2 * R * S * T1;
|
||||
|
||||
// --- Loop through grouped elements and apply the transform ---
|
||||
for (int i = 1; c->hasElement(i); ++i) {
|
||||
int groupedGeoId = c->getGeoId(i);
|
||||
if (groupedGeoId == GeoEnum::GeoUndef) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the slave's current (pre-solve) state
|
||||
Part::Geometry* groupedGeo = Geoms[checkGeoId(groupedGeoId)].geo;
|
||||
|
||||
// Apply the calculated transformation
|
||||
groupedGeo->transform(transform);
|
||||
}
|
||||
}
|
||||
|
||||
preSolveGroupStates.clear();
|
||||
}
|
||||
|
||||
@@ -88,7 +88,11 @@ public:
|
||||
int addGeometry(const std::vector<Part::Geometry*>& geos, bool fixed = false);
|
||||
/// add unspecified geometry, where each element's "fixed" status is given by the
|
||||
/// blockedGeometry array
|
||||
int addGeometry(const std::vector<Part::Geometry*>& geos, const std::vector<bool>& blockedGeometry);
|
||||
int addGeometry(
|
||||
const std::vector<Part::Geometry*>& geos,
|
||||
const std::vector<bool>& blockedGeometry,
|
||||
const std::set<int>& inGroupGeoIds
|
||||
);
|
||||
/// get boolean list indicating whether the geometry is to be blocked or not
|
||||
void getBlockedGeometry(
|
||||
std::vector<bool>& blockedGeometry,
|
||||
@@ -626,6 +630,34 @@ private:
|
||||
Base::Vector3d initToPoint;
|
||||
double moveStep;
|
||||
|
||||
// Group related things :
|
||||
/// container to store information about groups
|
||||
struct GroupLineState
|
||||
{
|
||||
Base::Vector3d startPoint;
|
||||
Base::Vector3d endPoint;
|
||||
|
||||
// Convenience method to get the length (scale)
|
||||
double getLength() const
|
||||
{
|
||||
return (endPoint - startPoint).Length();
|
||||
}
|
||||
|
||||
// Convenience method to get the orientation vector
|
||||
Base::Vector3d getVec() const
|
||||
{
|
||||
return endPoint - startPoint;
|
||||
}
|
||||
};
|
||||
// This map stores the state of group lines just BEFORE a solve.
|
||||
// We will use this to calculate the transformation AFTER the solve.
|
||||
// Key: GeoId of the frame line.
|
||||
// Value: it's initial position.
|
||||
std::map<int, GroupLineState> preSolveGroupStates;
|
||||
void captureGroupStates();
|
||||
void applyGroupTransformations();
|
||||
GroupLineState getGroupLineState(int geoId) const;
|
||||
|
||||
public:
|
||||
GCS::Algorithm defaultSolver;
|
||||
GCS::Algorithm defaultSolverRedundant;
|
||||
|
||||
@@ -906,6 +906,118 @@ double SketchObject::getDatum(int ConstrId) const
|
||||
return this->Constraints[ConstrId]->getValue();
|
||||
}
|
||||
|
||||
int SketchObject::setTextAndFont(int ConstrId, std::string& newText, std::string& newFont, bool isHeight, bool isConstruction)
|
||||
{
|
||||
; // no need to check input data validity as this is an sketchobject managed operation.
|
||||
Base::StateLocker lock(managedoperation, true);
|
||||
|
||||
// set the changed value for the constraint
|
||||
if (this->Constraints.hasInvalidGeometry()) {
|
||||
return -6;
|
||||
}
|
||||
const std::vector<Constraint*>& vals = this->Constraints.getValues();
|
||||
if (ConstrId < 0 || ConstrId >= int(vals.size())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto* constr = vals[ConstrId];
|
||||
if (constr->Type != Text || !constr->hasElement(0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// First we replace the old geometries by the new text.
|
||||
const std::string oldText = constr->getText();
|
||||
const std::string oldFont = constr->getFont();
|
||||
const bool oldIsHeight = constr->getIsTextHeight();
|
||||
int handleGeoId = constr->getGeoId(0);
|
||||
int firstTextGeoId = constr->getGeoId(1);
|
||||
bool hasExistingText = firstTextGeoId != GeoEnum::GeoUndef;
|
||||
bool handleLast = handleGeoId > firstTextGeoId;
|
||||
|
||||
if (hasExistingText) {
|
||||
// Check if text is construction or normal geos
|
||||
auto* geo1 = getGeometry(firstTextGeoId);
|
||||
isConstruction = GeometryFacade::getConstruction(geo1);
|
||||
|
||||
// Delete all the old text geos. Not the handle!
|
||||
std::vector<int> geoIdsToDelete;
|
||||
for (int i = 1; constr->hasElement(i); ++i) {
|
||||
if (constr->getGeoId(i) == GeoEnum::GeoUndef) {
|
||||
continue;
|
||||
}
|
||||
geoIdsToDelete.push_back(constr->getGeoId(i));
|
||||
if (handleLast) {
|
||||
--handleGeoId; // handle line is added after all text geos.
|
||||
}
|
||||
}
|
||||
|
||||
delGeometries(geoIdsToDelete);
|
||||
}
|
||||
|
||||
auto* line = dynamic_cast<const Part::GeomLineSegment*>(getGeometry(handleGeoId));
|
||||
if (!line) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Generate text geos based on new text/font :
|
||||
std::vector<std::unique_ptr<Part::Geometry>> newGeos;
|
||||
std::vector<TopoDS_Shape> shapes = Part::makeTextWires(newText, newFont);
|
||||
Part::transformAndConvertToGeometry(newGeos,
|
||||
shapes,
|
||||
line->getStartPoint(),
|
||||
line->getEndPoint(),
|
||||
isHeight);
|
||||
|
||||
// Add the geometries to sketch
|
||||
int lastGeoid = getHighestCurveIndex();
|
||||
std::vector<Part::Geometry*> newGeosRawPtrs;
|
||||
newGeosRawPtrs.reserve(newGeos.size());
|
||||
|
||||
// Populate the raw pointer vector and release ownership from the unique_ptrs.
|
||||
for (auto& geo_ptr : newGeos) {
|
||||
if (isConstruction) {
|
||||
Sketcher::GeometryFacade::setConstruction(geo_ptr.get(), isConstruction);
|
||||
}
|
||||
// Add the raw pointer to the new vector.
|
||||
newGeosRawPtrs.push_back(geo_ptr.get());
|
||||
// Release ownership from the unique_ptr. The SketchObject will now manage this memory.
|
||||
geo_ptr.release();
|
||||
}
|
||||
newGeos.clear();
|
||||
addGeometry(newGeosRawPtrs);
|
||||
|
||||
int newLastGeoid = getHighestCurveIndex();
|
||||
|
||||
// If there was text geos, they were deleted, which deleted the text constraint.
|
||||
// In this case create a new constraint to replace it.
|
||||
if (hasExistingText) {
|
||||
constr = new Constraint();
|
||||
constr->Type = Text;
|
||||
constr->truncateElements(0); // remove the First/Second/Third that are created automatically
|
||||
constr->addElement(GeoElementId(handleGeoId));
|
||||
}
|
||||
for (int i = lastGeoid + 1; i <= newLastGeoid; ++i) {
|
||||
constr->addElement(GeoElementId(i));
|
||||
}
|
||||
constr->setText(newText);
|
||||
constr->setFont(newFont);
|
||||
constr->setIsTextHeight(isHeight);
|
||||
|
||||
if (hasExistingText) {
|
||||
addConstraint(constr);
|
||||
}
|
||||
|
||||
int err = solve();
|
||||
|
||||
if (err) {
|
||||
constr->setText(oldText);
|
||||
constr->setFont(oldFont);
|
||||
constr->setIsTextHeight(oldIsHeight);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int SketchObject::setDriving(int ConstrId, bool isdriving)
|
||||
{
|
||||
// no need to check input data validity as this is an sketchobject managed operation.
|
||||
@@ -1006,6 +1118,26 @@ int SketchObject::getActive(int ConstrId, bool& isactive)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SketchObject::isConstraintActiveInSketch(const Sketcher::Constraint* cstr) const
|
||||
{
|
||||
// If the constraint is deactivated then it's over
|
||||
if (!cstr || !cstr->isActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cstr->Type == Group || cstr->Type == Text) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the constraint is not deactivated, it could still constraint something in a group
|
||||
for (int j = 0; cstr->hasElement(j); ++j) {
|
||||
if (isInGroup(cstr->getGeoId(j), false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int SketchObject::toggleActive(int ConstrId)
|
||||
{
|
||||
// no need to check input data validity as this is an sketchobject managed operation.
|
||||
@@ -3084,6 +3216,14 @@ void SketchObject::changeConstraintAfterDeletingGeo(Constraint* constr,
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; constr->hasElement(i); ++i) {
|
||||
if (constr->getGeoId(i) == deletedGeoId){
|
||||
constr->Type = ConstraintType::None;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// legacy to make sure we're not missing something...
|
||||
if (constr->involvesGeoId(deletedGeoId)) {
|
||||
constr->Type = ConstraintType::None;
|
||||
return;
|
||||
@@ -3100,14 +3240,10 @@ void SketchObject::changeConstraintAfterDeletingGeo(Constraint* constr,
|
||||
};
|
||||
}
|
||||
|
||||
if (needsUpdate(constr->First)) {
|
||||
constr->First -= step;
|
||||
}
|
||||
if (needsUpdate(constr->Second)) {
|
||||
constr->Second -= step;
|
||||
}
|
||||
if (needsUpdate(constr->Third)) {
|
||||
constr->Third -= step;
|
||||
for (int i = 0; constr->hasElement(i); ++i) {
|
||||
if (needsUpdate(constr->getGeoId(i))) {
|
||||
constr->setGeoId(i, constr->getGeoId(i) - step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10108,8 +10244,10 @@ bool SketchObject::evaluateConstraint(const Constraint* constraint) const
|
||||
case Equal:
|
||||
case PointOnObject:
|
||||
case Angle:
|
||||
case Text:
|
||||
break;
|
||||
case Tangent:
|
||||
case Group:
|
||||
requireSecond = true;
|
||||
break;
|
||||
case Symmetric:
|
||||
@@ -10256,6 +10394,59 @@ std::string SketchObject::validateExpression(const App::ObjectIdentifier& path,
|
||||
return "";
|
||||
}
|
||||
|
||||
bool SketchObject::isInGroup(int geoId, bool includeHandle) const
|
||||
{
|
||||
const std::vector<Sketcher::Constraint*>& vals = Constraints.getValues();
|
||||
|
||||
for (const auto& constr : vals) {
|
||||
if (constr->Type == Group || constr->Type == Text) {
|
||||
// First is the group construction line. We include it or not in our search.
|
||||
int iStart = includeHandle ? 0 : 1;
|
||||
for (int i = iStart; constr->hasElement(i); ++i) {
|
||||
if (constr->getGeoId(i) == geoId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SketchObject::isGroupHandle(int geoId) const
|
||||
{
|
||||
const std::vector<Sketcher::Constraint*>& vals = Constraints.getValues();
|
||||
|
||||
for (const auto& constr : vals) {
|
||||
if (constr->Type == Group || constr->Type == Text) {
|
||||
if (constr->getGeoId(0) == geoId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int SketchObject::getGroupHandleIfInGroup(int geoId)
|
||||
{
|
||||
const std::vector<Sketcher::Constraint*>& vals = Constraints.getValues();
|
||||
|
||||
for (const auto& constr : vals) {
|
||||
if (constr->Type == Group || constr->Type == Text) {
|
||||
// First is the group construction line.
|
||||
int groupHandleGeoId = -1;
|
||||
for (int i = 0; constr->hasElement(i); ++i) {
|
||||
if (i == 0) {
|
||||
groupHandleGeoId = constr->getGeoId(i);
|
||||
}
|
||||
else if (constr->getGeoId(i) == geoId) {
|
||||
return groupHandleGeoId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return geoId;
|
||||
}
|
||||
|
||||
// This function is necessary for precalculation of an angle when adding
|
||||
// an angle constraint. It is also used here, in SketchObject, to
|
||||
// lock down the type of tangency/perpendicularity.
|
||||
|
||||
@@ -142,6 +142,21 @@ public:
|
||||
\retval bool - true if the geometry is supported
|
||||
*/
|
||||
bool isSupportedGeometry(const Part::Geometry* geo) const;
|
||||
|
||||
/*!
|
||||
\brief Returns true if the geometry is in a group
|
||||
\param geoId - the geometry id in the sketch
|
||||
\param includeHandle - return true if geoId is the group construction line handle
|
||||
\retval bool - true if the geometry is supported
|
||||
*/
|
||||
bool isInGroup(int geoId, bool includeHandle = true) const;
|
||||
bool isGroupHandle(int geoId) const;
|
||||
/*!
|
||||
\brief Returns geoId if it's not in a group. Or the group handle if it is in a group.
|
||||
\param geoId - the geometry id in the sketch
|
||||
*/
|
||||
int getGroupHandleIfInGroup(int geoId);
|
||||
|
||||
/*!
|
||||
\brief Add geometry to a sketch - It adds a copy with a different uuid (internally uses copy()
|
||||
instead of clone()) \param geo - geometry to add \param construction - true for construction
|
||||
@@ -338,6 +353,14 @@ public:
|
||||
int setDatum(int ConstrId, double Datum);
|
||||
/// get the datum of a Distance or Angle constraint
|
||||
double getDatum(int ConstrId) const;
|
||||
/// set the text and font of a text constraint
|
||||
int setTextAndFont(
|
||||
int ConstrId,
|
||||
std::string& newText,
|
||||
std::string& newFont,
|
||||
bool isHeight,
|
||||
bool isConstruction = false
|
||||
);
|
||||
/// set the driving status of this constraint and solve
|
||||
int setDriving(int ConstrId, bool isdriving);
|
||||
/// get the driving status of this constraint
|
||||
@@ -352,6 +375,8 @@ public:
|
||||
int setActive(int ConstrId, bool isactive);
|
||||
/// get the driving status of this constraint
|
||||
int getActive(int ConstrId, bool& isactive);
|
||||
// Return true if the constraint is active, includes checking if it's not in a group
|
||||
bool isConstraintActiveInSketch(const Sketcher::Constraint* cstr) const;
|
||||
/// toggle the driving status of this constraint
|
||||
int toggleActive(int ConstrId);
|
||||
|
||||
|
||||
@@ -409,6 +409,23 @@ class SketchObject(Part2DObject):
|
||||
"""
|
||||
...
|
||||
|
||||
def setTextAndFont(
|
||||
self, constraint: int, text: str, font: str, isheight: bool, isConstruction: bool
|
||||
) -> None:
|
||||
"""
|
||||
Set the text and font of a Text constraint.
|
||||
|
||||
setTextAndFont(constraint: int, text: str, font: str, isHeight: bool, isConstruction: bool)
|
||||
|
||||
Args:
|
||||
constraint: The index of the Text constraint.
|
||||
text: The text string to display.
|
||||
font: The full path to the font file (.ttf, .otf, etc.).
|
||||
isHeight: Is the line handle of the group the height of the text.
|
||||
isConstruction: Are text geometry construction of not.
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getDatum(self, constraint: Union[int, str], /) -> Quantity:
|
||||
"""
|
||||
|
||||
@@ -756,6 +756,61 @@ PyObject* SketchObjectPy::delConstraintsToExternal()
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* SketchObjectPy::setTextAndFont(PyObject* args, PyObject* kwd)
|
||||
{
|
||||
int constrIndex = -1;
|
||||
char* textStr;
|
||||
char* fontStr;
|
||||
PyObject* isHeightObj = Py_True;
|
||||
PyObject* isConstrObj = Py_False; // Default to null (parameter not provided)
|
||||
|
||||
// "iss|O!O!" (int, str, str, | bool, bool)
|
||||
if (!PyArg_ParseTuple(
|
||||
args,
|
||||
"iss|O!O!",
|
||||
&constrIndex,
|
||||
&textStr,
|
||||
&fontStr,
|
||||
&PyBool_Type,
|
||||
&isHeightObj,
|
||||
&PyBool_Type,
|
||||
&isConstrObj
|
||||
)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string text(textStr);
|
||||
std::string font(fontStr);
|
||||
|
||||
// Call the C++ implementation
|
||||
int err = this->getSketchObjectPtr()->setTextAndFont(
|
||||
constrIndex,
|
||||
text,
|
||||
font,
|
||||
Base::asBoolean(isHeightObj),
|
||||
Base::asBoolean(isConstrObj)
|
||||
);
|
||||
|
||||
// Handle errors returned from the C++ function
|
||||
if (err) {
|
||||
std::stringstream str;
|
||||
if (err == -1) {
|
||||
str << "Invalid constraint index or not a Text constraint: " << constrIndex;
|
||||
}
|
||||
else if (err == -6) {
|
||||
str << "Cannot set text/font because of invalid geometry in the sketch";
|
||||
}
|
||||
else { // Generic error for solver failures etc.
|
||||
str << "Failed to set text/font for constraint with index " << constrIndex
|
||||
<< ". The operation would result in an invalid sketch.";
|
||||
}
|
||||
PyErr_SetString(PyExc_ValueError, str.str().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* SketchObjectPy::setDatum(PyObject* args)
|
||||
{
|
||||
double Datum;
|
||||
|
||||
@@ -21,6 +21,7 @@ set(SketcherGui_UIC_SRCS
|
||||
TaskSketcherSolverAdvanced.ui
|
||||
TaskSketcherValidation.ui
|
||||
InsertDatum.ui
|
||||
EditTextDialog.ui
|
||||
SketchOrientationDialog.ui
|
||||
SketchMirrorDialog.ui
|
||||
SketcherSettings.ui
|
||||
@@ -53,6 +54,7 @@ SET(SketcherGui_SRCS
|
||||
DrawSketchHandlerArcOfHyperbola.h
|
||||
DrawSketchHandlerArcOfParabola.h
|
||||
DrawSketchHandlerArcSlot.h
|
||||
DrawSketchHandlerText.h
|
||||
DrawSketchHandlerBSpline.h
|
||||
DrawSketchHandlerPoint.h
|
||||
DrawSketchHandlerFillet.h
|
||||
@@ -136,6 +138,9 @@ SET(SketcherGui_SRCS
|
||||
Workbench.h
|
||||
EditDatumDialog.cpp
|
||||
EditDatumDialog.h
|
||||
EditTextDialog.ui
|
||||
EditTextDialog.cpp
|
||||
EditTextDialog.h
|
||||
PropertyVisualLayerList.cpp
|
||||
PropertyVisualLayerList.h
|
||||
SketchOrientationDialog.cpp
|
||||
|
||||
@@ -87,6 +87,7 @@ CmdSketcherToggleConstruction::CmdSketcherToggleConstruction()
|
||||
rcCmdMgr.addCommandMode("ToggleConstruction", "Sketcher_CreateSlot");
|
||||
rcCmdMgr.addCommandMode("ToggleConstruction", "Sketcher_CompSlot");
|
||||
rcCmdMgr.addCommandMode("ToggleConstruction", "Sketcher_CreateArc");
|
||||
rcCmdMgr.addCommandMode("ToggleConstruction", "Sketcher_CreateText");
|
||||
rcCmdMgr.addCommandMode("ToggleConstruction", "Sketcher_Create3PointArc");
|
||||
rcCmdMgr.addCommandMode("ToggleConstruction", "Sketcher_CreateEllipseByCenter");
|
||||
rcCmdMgr.addCommandMode("ToggleConstruction", "Sketcher_CreateEllipseBy3Points");
|
||||
|
||||
@@ -25,7 +25,12 @@
|
||||
#include <limits>
|
||||
|
||||
#include <Precision.hxx>
|
||||
#include <Bnd_Box.hxx>
|
||||
#include <QPainter>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <BRepBndLib.hxx>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
@@ -10440,6 +10445,246 @@ bool CmdSketcherConstrainSnellsLaw::isActive()
|
||||
return isCreateConstraintActive(getActiveGuiDocument());
|
||||
}
|
||||
|
||||
// ======================================================================================
|
||||
|
||||
DEF_STD_CMD_A(CmdSketcherConstrainGroup)
|
||||
|
||||
CmdSketcherConstrainGroup::CmdSketcherConstrainGroup()
|
||||
: Command("Sketcher_ConstrainGroup")
|
||||
{
|
||||
sAppModule = "Sketcher";
|
||||
sGroup = "Sketcher";
|
||||
sMenuText = QT_TR_NOOP("Group Constrain");
|
||||
sToolTipText = QT_TR_NOOP("Constrains the selected geometries together as a single entity."
|
||||
"The position and size of the grouped geometries can be defined by constraining the construction line that is generated."
|
||||
"Constraints applied to grouped edges are ignored as long as the Group constraint is here.");
|
||||
sWhatsThis = "Sketcher_ConstrainGroup";
|
||||
sStatusTip = sToolTipText;
|
||||
sPixmap = "Constraint_Group";
|
||||
sAccel = "K, G";
|
||||
eType = ForEdit;
|
||||
}
|
||||
|
||||
void CmdSketcherConstrainGroup::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
// get the selection
|
||||
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
||||
|
||||
// only one sketch with its subelements are allowed to be selected
|
||||
if (selection.size() != 1
|
||||
|| !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
||||
const char dmbg[] = "Constraint_Group";
|
||||
|
||||
QString strError = QObject::tr("Selected objects are not just geometry "
|
||||
"from one sketch.",
|
||||
dmbg);
|
||||
|
||||
Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(),
|
||||
QObject::tr("Wrong selection"),
|
||||
std::move(strError));
|
||||
}
|
||||
|
||||
// get the needed lists and objects
|
||||
auto* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
||||
const std::vector<std::string>& SubNames = selection[0].getSubNames();
|
||||
|
||||
if (SubNames.empty()) {
|
||||
Gui::TranslatedUserWarning(Obj,
|
||||
QObject::tr("Wrong selection"),
|
||||
QObject::tr("No geometries selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Sketcher::GeoElementId> elts;
|
||||
|
||||
for (auto& subName : SubNames) {
|
||||
int geoId;
|
||||
Sketcher::PointPos posId;
|
||||
getIdsFromName(subName, Obj, geoId, posId);
|
||||
|
||||
bool alreadyAdded = std::any_of(elts.begin(), elts.end(),
|
||||
[geoId](const Sketcher::GeoElementId& elem) {
|
||||
return elem.GeoId == geoId;
|
||||
});
|
||||
|
||||
if (geoId < 0 || alreadyAdded || Obj->getGeometryFacade(geoId)->isInternalAligned()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
elts.push_back(Sketcher::GeoElementId(geoId, Sketcher::PointPos::none));
|
||||
}
|
||||
|
||||
if (elts.size() < 2) {
|
||||
Base::Console().warning("Cannot create group : minimum 2 geometries must be selected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Add Group constraint"));
|
||||
|
||||
if (!addListConstraint(Obj, elts, "Group")) {
|
||||
abortCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
tryAutoRecompute(Obj);
|
||||
|
||||
commitCommand();
|
||||
|
||||
getSelection().clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Escapes a string for safe embedding within a single-quoted Python string literal.
|
||||
*
|
||||
* This function handles backslashes and single quotes.
|
||||
*
|
||||
* @param input The raw string to escape.
|
||||
* @return A new string with special characters escaped.
|
||||
*/
|
||||
std::string SketcherGui::escapeForPython(const std::string& input)
|
||||
{
|
||||
std::string result;
|
||||
// Pre-allocating can be a small optimization if strings are long
|
||||
result.reserve(input.length());
|
||||
|
||||
for (char c : input) {
|
||||
if (c == '\\') {
|
||||
result += "\\\\";
|
||||
} else if (c == '\'') {
|
||||
result += "\\'";
|
||||
} else {
|
||||
result += c;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SketcherGui::addListConstraint(Sketcher::SketchObject* Obj,
|
||||
std::vector<Sketcher::GeoElementId>& elts,
|
||||
const std::string& constraintType,
|
||||
Base::Vector2d frame_p1,
|
||||
Base::Vector2d frame_p2,
|
||||
bool isTextHeight,
|
||||
const std::string& text,
|
||||
const std::string& font)
|
||||
{
|
||||
|
||||
std::vector<int> geoIdsWithInternalGeos;
|
||||
// The lambda defines the condition for REMOVAL.
|
||||
// It returns 'true' if an element should be erased.
|
||||
auto new_end = std::remove_if(elts.begin(), elts.end(),
|
||||
[&](const Sketcher::GeoElementId& element) -> bool {
|
||||
int geoId = element.GeoId;
|
||||
|
||||
// Condition 1: Check for invalid or already-aligned geometries.
|
||||
// If true, this element should be removed.
|
||||
if (geoId < 0 || Obj->getGeometryFacade(geoId)->isInternalAligned()) {
|
||||
return true; // Mark for removal
|
||||
}
|
||||
|
||||
// Condition 2: Check for internal geometries that need cleanup later.
|
||||
// This does not mark the element for removal, but collects its ID.
|
||||
const Part::Geometry* geo = Obj->getGeometry(geoId);
|
||||
if (Obj->hasInternalGeometry(geo)) {
|
||||
// Collect the ID for later processing.
|
||||
geoIdsWithInternalGeos.push_back(geoId);
|
||||
}
|
||||
|
||||
// If we reached here, the element is valid and should be kept.
|
||||
return false; // Do not remove
|
||||
});
|
||||
|
||||
// Actually erase the elements that were moved to the end.
|
||||
elts.erase(new_end, elts.end());
|
||||
|
||||
if (elts.size() < 2) {
|
||||
Base::Console().warning("Cannot create %s constraint: minimum 2 geometries.\n", constraintType.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((frame_p1 - frame_p2).Length() < Precision::Confusion()) {
|
||||
// --- 1. Calculate Bounding Box ---
|
||||
Bnd_Box totalBBox;
|
||||
for (const auto& element : elts) {
|
||||
const Part::Geometry* geo = Obj->getGeometry(element.GeoId);
|
||||
if (geo) {
|
||||
BRepBndLib::Add(geo->toShape(), totalBBox, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!totalBBox.HasFinitePart()) {
|
||||
Base::Console().warning("Cannot create %s constraint: bounding box is infinite\n", constraintType.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
gp_Pnt min_pnt = totalBBox.CornerMin();
|
||||
gp_Pnt max_pnt = totalBBox.CornerMax();
|
||||
|
||||
// --- 2. Define and create the Construction Line "Frame" ---
|
||||
frame_p1 = Base::Vector2d(min_pnt.X(), min_pnt.Y());
|
||||
frame_p2 = Base::Vector2d(min_pnt.X(), max_pnt.Y());
|
||||
}
|
||||
|
||||
Gui::cmdAppObjectArgs(Obj,
|
||||
"addGeometry(Part.LineSegment(App.Vector(%f,%f,0), App.Vector(%f,%f,0)), True)",
|
||||
frame_p1.x, frame_p1.y, frame_p2.x, frame_p2.y);
|
||||
|
||||
int frameGeoId = Obj->getHighestCurveIndex();
|
||||
|
||||
// --- 3. Prepend the Frame to the Element List ---
|
||||
elts.insert(elts.begin(), Sketcher::GeoElementId(frameGeoId, Sketcher::PointPos::none));
|
||||
|
||||
// --- 4. Create the Python list of elements as a string ---
|
||||
std::stringstream elements_list_ss;
|
||||
elements_list_ss << "[";
|
||||
if (!elts.empty()) {
|
||||
for (size_t i = 0; i < elts.size() - 1; ++i) {
|
||||
elements_list_ss << elts[i].GeoId << ", " << elts[i].posIdAsInt() << ", ";
|
||||
}
|
||||
elements_list_ss << elts.back().GeoId << ", " << elts.back().posIdAsInt();
|
||||
}
|
||||
elements_list_ss << "]";
|
||||
std::string elements_list_string = elements_list_ss.str();
|
||||
|
||||
// --- 5. Add the appropriate constraint via Python command ---
|
||||
if (constraintType == "Group") {
|
||||
Gui::cmdAppObjectArgs(
|
||||
Obj,
|
||||
"addConstraint(Sketcher.Constraint('Group', %s))",
|
||||
elements_list_string.c_str());
|
||||
}
|
||||
else if (constraintType == "Text") {
|
||||
std::string escaped_text = escapeForPython(text);
|
||||
std::string escaped_font = escapeForPython(font);
|
||||
Gui::cmdAppObjectArgs(
|
||||
Obj,
|
||||
"addConstraint(Sketcher.Constraint('Text', %s, '%s', '%s', %s))",
|
||||
elements_list_string.c_str(),
|
||||
escaped_text.c_str(),
|
||||
escaped_font.c_str(),
|
||||
isTextHeight ? "True" : "False");
|
||||
}
|
||||
else {
|
||||
Base::Console().error("Unsupported list constraint type: %s\n", constraintType.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// We remove the internal alignment of the geometries that were grouped.
|
||||
std::sort(geoIdsWithInternalGeos.begin(), geoIdsWithInternalGeos.end(), std::greater<>());
|
||||
for (auto& geoId : geoIdsWithInternalGeos) {
|
||||
Obj->deleteUnusedInternalGeometry(geoId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CmdSketcherConstrainGroup::isActive()
|
||||
{
|
||||
return isCreateConstraintActive(getActiveGuiDocument());
|
||||
}
|
||||
|
||||
// ======================================================================================
|
||||
DEF_STD_CMD_A(CmdSketcherChangeDimensionConstraint)
|
||||
|
||||
@@ -10765,6 +11010,7 @@ void CreateSketcherCommandsConstraints()
|
||||
rcCmdMgr.addCommand(new CmdSketcherConstrainPointOnObject());
|
||||
rcCmdMgr.addCommand(new CmdSketcherConstrainSymmetric());
|
||||
rcCmdMgr.addCommand(new CmdSketcherConstrainSnellsLaw());
|
||||
rcCmdMgr.addCommand(new CmdSketcherConstrainGroup());
|
||||
rcCmdMgr.addCommand(new CmdSketcherChangeDimensionConstraint());
|
||||
rcCmdMgr.addCommand(new CmdSketcherToggleDrivingConstraint());
|
||||
rcCmdMgr.addCommand(new CmdSketcherToggleActiveConstraint());
|
||||
|
||||
@@ -137,4 +137,15 @@ void doEndpointToEdgeTangency(
|
||||
/// notifications
|
||||
void notifyConstraintSubstitutions(const QString& message);
|
||||
|
||||
std::string escapeForPython(const std::string& input);
|
||||
bool addListConstraint(
|
||||
Sketcher::SketchObject* Obj,
|
||||
std::vector<Sketcher::GeoElementId>& elts,
|
||||
const std::string& constraintType,
|
||||
Base::Vector2d frame_p1 = Base::Vector2d(),
|
||||
Base::Vector2d frame_p2 = Base::Vector2d(),
|
||||
bool isTextHeight = true,
|
||||
const std::string& text = "",
|
||||
const std::string& font = ""
|
||||
);
|
||||
} // namespace SketcherGui
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "DrawSketchHandlerRectangle.h"
|
||||
#include "DrawSketchHandlerSlot.h"
|
||||
#include "DrawSketchHandlerSplitting.h"
|
||||
#include "DrawSketchHandlerText.h"
|
||||
#include "DrawSketchHandlerTrimming.h"
|
||||
|
||||
|
||||
@@ -1356,6 +1357,43 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Text ================================================================
|
||||
|
||||
DEF_STD_CMD_AU(CmdSketcherCreateText)
|
||||
|
||||
CmdSketcherCreateText::CmdSketcherCreateText()
|
||||
: Command("Sketcher_CreateText")
|
||||
{
|
||||
sAppModule = "Sketcher";
|
||||
sGroup = "Sketcher";
|
||||
sMenuText = QT_TR_NOOP("Text");
|
||||
sToolTipText = QT_TR_NOOP(
|
||||
"Creates text geometries controlled by a Text constraint.\n"
|
||||
"To Edit: Double-click the Text constraint to change the text content and font.\n"
|
||||
"To Position/Size: Apply constraints to the group's construction line.\n"
|
||||
"Note: While the Text constraint is active, any constraints applied directly to the text "
|
||||
"geometries will be ignored.\n"
|
||||
);
|
||||
sWhatsThis = "Sketcher_CreateText";
|
||||
sStatusTip = sToolTipText;
|
||||
sPixmap = "Sketcher_CreateText";
|
||||
sAccel = "G, T";
|
||||
eType = ForEdit;
|
||||
}
|
||||
|
||||
CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateText, "Sketcher_CreateText")
|
||||
|
||||
void CmdSketcherCreateText::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
ActivateHandler(getActiveGuiDocument(), std::make_unique<DrawSketchHandlerText>());
|
||||
}
|
||||
|
||||
bool CmdSketcherCreateText::isActive()
|
||||
{
|
||||
return isCommandActive(getActiveGuiDocument());
|
||||
}
|
||||
|
||||
// B-spline ================================================================
|
||||
|
||||
DEF_STD_CMD_AU(CmdSketcherCreateBSpline)
|
||||
@@ -1936,6 +1974,7 @@ void CreateSketcherCommandsCreateGeo()
|
||||
rcCmdMgr.addCommand(new CmdSketcherCreateRegularPolygon());
|
||||
rcCmdMgr.addCommand(new CmdSketcherCreateSlot());
|
||||
rcCmdMgr.addCommand(new CmdSketcherCreateArcSlot());
|
||||
rcCmdMgr.addCommand(new CmdSketcherCreateText());
|
||||
rcCmdMgr.addCommand(new CmdSketcherCreateFillet());
|
||||
rcCmdMgr.addCommand(new CmdSketcherCreateChamfer());
|
||||
// rcCmdMgr.addCommand(new CmdSketcherCreateText());
|
||||
|
||||
@@ -54,18 +54,20 @@ enum class FilterValue
|
||||
Equality = 9,
|
||||
Symmetric = 10,
|
||||
Block = 11,
|
||||
InternalAlignment = 12,
|
||||
Datums = 13,
|
||||
HorizontalDistance = 14,
|
||||
VerticalDistance = 15,
|
||||
Distance = 16,
|
||||
Radius = 17,
|
||||
Weight = 18,
|
||||
Diameter = 19,
|
||||
Angle = 20,
|
||||
SnellsLaw = 21,
|
||||
Named = 22,
|
||||
NonDriving = 23,
|
||||
Group = 12,
|
||||
Text = 13,
|
||||
InternalAlignment = 14,
|
||||
Datums = 15,
|
||||
HorizontalDistance = 16,
|
||||
VerticalDistance = 17,
|
||||
Distance = 18,
|
||||
Radius = 19,
|
||||
Weight = 20,
|
||||
Diameter = 21,
|
||||
Angle = 22,
|
||||
SnellsLaw = 23,
|
||||
Named = 24,
|
||||
NonDriving = 25,
|
||||
NumFilterValue // SpecialFilterValue shall start at the same index as this
|
||||
};
|
||||
|
||||
@@ -75,8 +77,8 @@ constexpr auto FilterValueLength = static_cast<std::underlying_type_t<FilterValu
|
||||
|
||||
enum class SpecialFilterValue
|
||||
{
|
||||
Selection = FilterValueLength, // = 24
|
||||
AssociatedConstraints, // = 25
|
||||
Selection = FilterValueLength, // = 26
|
||||
AssociatedConstraints, // = 27
|
||||
NumSpecialFilterValue
|
||||
};
|
||||
|
||||
@@ -127,6 +129,8 @@ constexpr std::array<FilterValueBitset, FilterValueLength> filterAggregates {
|
||||
FilterValue::Equality,
|
||||
FilterValue::Symmetric,
|
||||
FilterValue::Block,
|
||||
FilterValue::Group,
|
||||
FilterValue::Text,
|
||||
FilterValue::Datums,
|
||||
FilterValue::Distance,
|
||||
FilterValue::HorizontalDistance,
|
||||
@@ -152,6 +156,8 @@ constexpr std::array<FilterValueBitset, FilterValueLength> filterAggregates {
|
||||
FilterValue::Equality,
|
||||
FilterValue::Symmetric,
|
||||
FilterValue::Block,
|
||||
FilterValue::Group,
|
||||
FilterValue::Text,
|
||||
FilterValue::InternalAlignment
|
||||
), // Geometric = All others not being datums (1)
|
||||
|
||||
@@ -165,6 +171,8 @@ constexpr std::array<FilterValueBitset, FilterValueLength> filterAggregates {
|
||||
buildBitset(FilterValue::Equality), // Equality = Just this (9)
|
||||
buildBitset(FilterValue::Symmetric), // Symmetric = Just this (10)
|
||||
buildBitset(FilterValue::Block), // Block = Just this (11)
|
||||
buildBitset(FilterValue::Group), // Group = Just this (11)
|
||||
buildBitset(FilterValue::Text), // Text = Just this (11)
|
||||
buildBitset(FilterValue::InternalAlignment), // InternalAlignment = Just this (12)
|
||||
|
||||
buildBitset(
|
||||
|
||||
@@ -52,6 +52,12 @@ class WidgetComboboxes: public ControlAmount<sizes...>
|
||||
{
|
||||
};
|
||||
|
||||
/** @brief Type encapsulating the number of line edits in the widget*/
|
||||
template<int... sizes> // Initial sizes for each mode
|
||||
class WidgetLineEdits: public ControlAmount<sizes...>
|
||||
{
|
||||
};
|
||||
|
||||
namespace sp = std::placeholders;
|
||||
|
||||
/** @brief Class defining a handler controller making use of parameters provided by a widget of type
|
||||
@@ -74,6 +80,7 @@ template<
|
||||
typename WidgetParametersT, // The number of parameter spinboxes in the default widget
|
||||
typename WidgetCheckboxesT, // The number of checkboxes in the default widget
|
||||
typename WidgetComboboxesT, // The number of comboboxes in the default widget
|
||||
typename WidgetLineEditsT, // The number of line edits in the default widget
|
||||
typename ConstructionMethodT = ConstructionMethods::DefaultConstructionMethod,
|
||||
bool PFirstComboboxIsConstructionMethod = false> // The handler template or class having this
|
||||
// as inner class
|
||||
@@ -91,6 +98,7 @@ private:
|
||||
int nParameter = WidgetParametersT::defaultMethodSize();
|
||||
int nCheckbox = WidgetCheckboxesT::defaultMethodSize();
|
||||
int nCombobox = WidgetComboboxesT::defaultMethodSize();
|
||||
int nLineEdit = WidgetLineEditsT::defaultMethodSize();
|
||||
|
||||
SketcherToolDefaultWidget* toolWidget;
|
||||
|
||||
@@ -100,12 +108,14 @@ private:
|
||||
Connection connectionParameterValueChanged;
|
||||
Connection connectionCheckboxCheckedChanged;
|
||||
Connection connectionComboboxSelectionChanged;
|
||||
Connection connectionLineEditTextChanged;
|
||||
|
||||
/** @name Named indices for controls of the default widget (SketcherToolDefaultWidget) */
|
||||
//@{
|
||||
using WParameter = SketcherToolDefaultWidget::Parameter;
|
||||
using WCheckbox = SketcherToolDefaultWidget::Checkbox;
|
||||
using WCombobox = SketcherToolDefaultWidget::Combobox;
|
||||
using WLineEdit = SketcherToolDefaultWidget::LineEdit;
|
||||
//@}
|
||||
|
||||
using SelectMode = SelectModeT;
|
||||
@@ -128,6 +138,7 @@ public:
|
||||
connectionParameterValueChanged.disconnect();
|
||||
connectionCheckboxCheckedChanged.disconnect();
|
||||
connectionComboboxSelectionChanged.disconnect();
|
||||
connectionLineEditTextChanged.disconnect();
|
||||
}
|
||||
|
||||
/** @name functions NOT intended for specialisation offering specialisation interface for
|
||||
@@ -179,6 +190,24 @@ public:
|
||||
}
|
||||
//@}
|
||||
|
||||
/** boost slot triggering when a line edit has changed in the widget
|
||||
* It is intended to remote control the DrawSketchDefaultWidgetHandler
|
||||
*/
|
||||
void lineEditTextChanged(int lineeditindex, const QString& value)
|
||||
{
|
||||
adaptDrawingToLineEditTextChange(lineeditindex, value); // specialisation interface
|
||||
|
||||
// Temporarily disable auto-passing focus to OVP.
|
||||
// This prevents the focus from being stolen from the LineEdit when the text changes.
|
||||
ControllerBase::focusAutoPassing = false;
|
||||
|
||||
ControllerBase::finishControlsChanged();
|
||||
|
||||
// Restore the default behavior.
|
||||
ControllerBase::focusAutoPassing = true;
|
||||
}
|
||||
//@}
|
||||
|
||||
/** @name Specialisation Interface */
|
||||
/** These functions offer a specialisation interface. Non-virtual functions are specific to
|
||||
* this controller. Virtual functions may depend on input from a derived controller, and thus
|
||||
@@ -202,8 +231,6 @@ public:
|
||||
/// Change DSH to reflect a comboBox changed in the widget
|
||||
void adaptDrawingToComboboxChange(int comboboxindex, [[maybe_unused]] int value)
|
||||
{
|
||||
Q_UNUSED(comboboxindex);
|
||||
|
||||
if constexpr (PFirstComboboxIsConstructionMethod == true) {
|
||||
|
||||
if (comboboxindex == WCombobox::FirstCombo && handler->ConstructionMethodsCount() > 1) {
|
||||
@@ -212,6 +239,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Change DSH to reflect a line edit changed in the widget
|
||||
void adaptDrawingToLineEditTextChange(int lineeditindex, const QString& value)
|
||||
{
|
||||
Q_UNUSED(lineeditindex);
|
||||
Q_UNUSED(value);
|
||||
}
|
||||
|
||||
/// function to create constraints based on widget information.
|
||||
void addConstraints() override
|
||||
{}
|
||||
@@ -303,14 +337,19 @@ protected:
|
||||
void setFocusToParameter(unsigned int parameterindex)
|
||||
{
|
||||
// To be able to cycle through OVP and widget, we use a parameter index that goes from
|
||||
// 0 to (onViewParameters.size() + nParameter)
|
||||
// 0 to (onViewParameters.size() + nParameter + nLineEdit)
|
||||
if (!ControllerBase::setFocusToOnViewParameter(parameterindex)) {
|
||||
parameterindex = parameterindex - ControllerBase::onViewParameters.size();
|
||||
unsigned int widgetIndex = parameterindex - ControllerBase::onViewParameters.size();
|
||||
|
||||
if (parameterindex < static_cast<unsigned int>(nParameter)) {
|
||||
toolWidget->setParameterFocus(parameterindex);
|
||||
ControllerBase::parameterWithFocus = ControllerBase::onViewParameters.size()
|
||||
+ parameterindex;
|
||||
if (widgetIndex < static_cast<unsigned int>(nParameter)) {
|
||||
toolWidget->setParameterFocus(widgetIndex);
|
||||
ControllerBase::parameterWithFocus = parameterindex;
|
||||
}
|
||||
// Check if the index corresponds to a LineEdit
|
||||
else if (widgetIndex < static_cast<unsigned int>(nParameter + nLineEdit)) {
|
||||
unsigned int lineEditIndex = widgetIndex - nParameter;
|
||||
toolWidget->setLineEditFocus(lineEditIndex);
|
||||
ControllerBase::parameterWithFocus = parameterindex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,7 +359,8 @@ protected:
|
||||
{
|
||||
unsigned int index = ControllerBase::parameterWithFocus + 1;
|
||||
|
||||
if (index >= ControllerBase::onViewParameters.size() + nParameter) {
|
||||
// The total number of focusable items now includes LineEdits.
|
||||
if (index >= ControllerBase::onViewParameters.size() + nParameter + nLineEdit) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
@@ -333,9 +373,21 @@ protected:
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
// Check SpinBoxes
|
||||
if (idx < ControllerBase::onViewParameters.size() + nParameter) {
|
||||
setFocusToParameter(idx);
|
||||
return true;
|
||||
if (nParameter > 0) {
|
||||
setFocusToParameter(idx);
|
||||
return true;
|
||||
}
|
||||
// If no spinboxes, update index to check line edits
|
||||
idx = ControllerBase::onViewParameters.size() + nParameter;
|
||||
}
|
||||
// Check LineEdits
|
||||
if (idx < ControllerBase::onViewParameters.size() + nParameter + nLineEdit) {
|
||||
if (nLineEdit > 0) {
|
||||
setFocusToParameter(idx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -372,6 +424,10 @@ private:
|
||||
connectionComboboxSelectionChanged = toolWidget->registerComboboxSelectionChanged(
|
||||
std::bind(&DrawSketchDefaultWidgetController::comboboxSelectionChanged, this, sp::_1, sp::_2)
|
||||
);
|
||||
|
||||
connectionLineEditTextChanged = toolWidget->registerLineEditTextChanged(
|
||||
std::bind(&DrawSketchDefaultWidgetController::lineEditTextChanged, this, sp::_1, sp::_2)
|
||||
);
|
||||
}
|
||||
|
||||
/// Resets the widget
|
||||
@@ -383,14 +439,17 @@ private:
|
||||
fastsignals::shared_connection_block parameter_block(connectionParameterValueChanged);
|
||||
fastsignals::shared_connection_block checkbox_block(connectionCheckboxCheckedChanged);
|
||||
fastsignals::shared_connection_block combobox_block(connectionComboboxSelectionChanged);
|
||||
fastsignals::shared_connection_block lineedit_block(connectionLineEditTextChanged);
|
||||
|
||||
nParameter = WidgetParametersT::size(handler->constructionMethod());
|
||||
nCheckbox = WidgetCheckboxesT::size(handler->constructionMethod());
|
||||
nCombobox = WidgetComboboxesT::size(handler->constructionMethod());
|
||||
nLineEdit = WidgetLineEditsT::size(handler->constructionMethod());
|
||||
|
||||
toolWidget->initNParameters(nParameter, ControllerBase::getKeyManager());
|
||||
toolWidget->initNCheckboxes(nCheckbox);
|
||||
toolWidget->initNComboboxes(nCombobox);
|
||||
toolWidget->initNLineEdits(nLineEdit);
|
||||
|
||||
configureToolWidget();
|
||||
|
||||
|
||||
@@ -62,7 +62,8 @@ using DSHArcController = DrawSketchDefaultWidgetController<
|
||||
/*OnViewParametersT =*/OnViewParameters<5, 6>, // NOLINT
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0, 0>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT,
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0>,
|
||||
ConstructionMethods::CircleEllipseConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ using DSHArcSlotController = DrawSketchDefaultWidgetController<
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0, 0>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0>,
|
||||
ConstructionMethods::ArcSlotConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ using DSHBSplineController = DrawSketchDefaultWidgetController<
|
||||
/*WidgetParametersT =*/WidgetParameters<1, 1>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<1, 1>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0>,
|
||||
ConstructionMethods::BSplineConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ using DSHCircleController = DrawSketchDefaultWidgetController<
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0, 0>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0>,
|
||||
ConstructionMethods::CircleEllipseConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ using DSHEllipseController = DrawSketchDefaultWidgetController<
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0, 0>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0>,
|
||||
ConstructionMethods::CircleEllipseConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ using DSHFilletController = DrawSketchDefaultWidgetController<
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<1, 1>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0>,
|
||||
ConstructionMethods::FilletConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ using DSHLineController = DrawSketchDefaultWidgetController<
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0, 0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0, 0, 0>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1, 1>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0, 0>,
|
||||
ConstructionMethods::LineConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -877,4 +877,5 @@ protected:
|
||||
dirVec.Normalize();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
@@ -105,6 +105,7 @@ using DSHOffsetController = DrawSketchDefaultWidgetController<
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0>,
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<2, 2>,
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1>,
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0>,
|
||||
ConstructionMethods::OffsetConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ using DSHPolygonController = DrawSketchDefaultWidgetController<
|
||||
/*OnViewParametersT =*/OnViewParameters<4>,
|
||||
/*WidgetParametersT =*/WidgetParameters<1>,
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0>,
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>>;
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>,
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0>>;
|
||||
|
||||
using DSHPolygonControllerBase = DSHPolygonController::ControllerBase;
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ using DSHRectangleController = DrawSketchDefaultWidgetController<
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0, 0, 0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<2, 2, 2, 2>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<1, 1, 1, 1>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0, 0, 0, 0>, // NOLINT
|
||||
ConstructionMethods::RectangleConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
|
||||
@@ -53,7 +53,8 @@ using DSHRotateController = DrawSketchDefaultWidgetController<
|
||||
/*OnViewParametersT =*/OnViewParameters<4>,
|
||||
/*WidgetParametersT =*/WidgetParameters<1>,
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<1>,
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>>;
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>,
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0>>;
|
||||
|
||||
using DSHRotateControllerBase = DSHRotateController::ControllerBase;
|
||||
|
||||
|
||||
@@ -60,7 +60,8 @@ using DSHScaleController = DrawSketchDefaultWidgetController<
|
||||
/*OnViewParametersT =*/OnViewParameters<3>,
|
||||
/*WidgetParametersT =*/WidgetParameters<0>,
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<1>,
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>>;
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>,
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0>>;
|
||||
|
||||
using DSHScaleControllerBase = DSHScaleController::ControllerBase;
|
||||
|
||||
|
||||
@@ -55,10 +55,11 @@ using DSHSlotController = DrawSketchDefaultWidgetController<
|
||||
DrawSketchHandlerSlot,
|
||||
StateMachines::ThreeSeekEnd,
|
||||
/*PAutoConstraintSize =*/2,
|
||||
/*OnViewParametersT =*/OnViewParameters<5>, // NOLINT
|
||||
/*WidgetParametersT =*/WidgetParameters<0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>>; // NOLINT
|
||||
/*OnViewParametersT =*/OnViewParameters<5>, // NOLINT
|
||||
/*WidgetParametersT =*/WidgetParameters<0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0>>; // NOLINT
|
||||
|
||||
using DSHSlotControllerBase = DSHSlotController::ControllerBase;
|
||||
|
||||
|
||||
@@ -54,10 +54,11 @@ using DSHSymmetryController = DrawSketchDefaultWidgetController<
|
||||
DrawSketchHandlerSymmetry,
|
||||
StateMachines::OneSeekEnd,
|
||||
/*PAutoConstraintSize =*/0,
|
||||
/*OnViewParametersT =*/OnViewParameters<0>,
|
||||
/*WidgetParametersT =*/WidgetParameters<0>,
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<2>,
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>>;
|
||||
/*OnViewParametersT =*/OnViewParameters<0>, // NOLINT
|
||||
/*WidgetParametersT =*/WidgetParameters<0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<2>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0>>; // NOLINT
|
||||
|
||||
using DSHSymmetryControllerBase = DSHSymmetryController::ControllerBase;
|
||||
|
||||
|
||||
@@ -0,0 +1,771 @@
|
||||
// SPDX - License - Identifier: LGPL - 2.1 - or -later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Pierre-Louis Boyer *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef SKETCHERGUI_DrawSketchHandlerText_H
|
||||
#define SKETCHERGUI_DrawSketchHandlerText_H
|
||||
|
||||
#include <QMap>
|
||||
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Gui/Notifications.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/CommandT.h>
|
||||
#include <Gui/InputHint.h>
|
||||
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "DrawSketchDefaultWidgetController.h"
|
||||
#include "DrawSketchControllableHandler.h"
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
#include "CommandConstraints.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace SketcherGui
|
||||
{
|
||||
|
||||
class DrawSketchHandlerText;
|
||||
|
||||
namespace ConstructionMethods
|
||||
{
|
||||
|
||||
enum class TextConstructionMethod
|
||||
{
|
||||
Width,
|
||||
Height,
|
||||
End // Must be the last one
|
||||
};
|
||||
|
||||
} // namespace ConstructionMethods
|
||||
|
||||
using DSHTextController = DrawSketchDefaultWidgetController<
|
||||
DrawSketchHandlerText,
|
||||
/*SelectModeT*/ StateMachines::TwoSeekEnd,
|
||||
/*PAutoConstraintSize =*/2,
|
||||
/*OnViewParametersT =*/OnViewParameters<4, 4>, // NOLINT
|
||||
/*WidgetParametersT =*/WidgetParameters<0, 0>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<0, 0>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<2, 2>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<1, 1>, // NOLINT
|
||||
ConstructionMethods::TextConstructionMethod,
|
||||
/*bool PFirstComboboxIsConstructionMethod =*/true>;
|
||||
|
||||
using DSHTextControllerBase = DSHTextController::ControllerBase;
|
||||
|
||||
using DrawSketchHandlerTextBase = DrawSketchControllableHandler<DSHTextController>;
|
||||
|
||||
|
||||
class DrawSketchHandlerText: public DrawSketchHandlerTextBase
|
||||
{
|
||||
friend DSHTextController;
|
||||
friend DSHTextControllerBase;
|
||||
|
||||
public:
|
||||
explicit DrawSketchHandlerText(ConstructionMethod constrMethod = ConstructionMethod::Width)
|
||||
: DrawSketchHandlerTextBase(constrMethod)
|
||||
, length(0.0)
|
||||
, handleId(0)
|
||||
, text(QObject::tr("Text").toStdString())
|
||||
, font("")
|
||||
, cachedTextName("")
|
||||
, cachedFontName("")
|
||||
, cachedBaseShapes({}) {};
|
||||
~DrawSketchHandlerText() override = default;
|
||||
|
||||
private:
|
||||
void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override
|
||||
{
|
||||
switch (state()) {
|
||||
case SelectMode::SeekFirst: {
|
||||
toolWidgetManager.drawPositionAtCursor(onSketchPos);
|
||||
|
||||
startPoint = onSketchPos;
|
||||
|
||||
seekAndRenderAutoConstraint(sugConstraints[0], onSketchPos, Base::Vector2d(0.f, 0.f));
|
||||
} break;
|
||||
case SelectMode::SeekSecond: {
|
||||
toolWidgetManager.drawDirectionAtCursor(onSketchPos, startPoint);
|
||||
|
||||
endPoint = onSketchPos;
|
||||
|
||||
try {
|
||||
CreateAndDrawShapeGeometry();
|
||||
}
|
||||
catch (const Base::ValueError&) {
|
||||
} // equal points while hovering raise an objection that can be safely ignored
|
||||
|
||||
seekAndRenderAutoConstraint(sugConstraints[1], onSketchPos, onSketchPos - startPoint);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void executeCommands() override
|
||||
{
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add sketch Text"));
|
||||
|
||||
// Add the Handle Line
|
||||
Gui::cmdAppObjectArgs(
|
||||
getSketchObject(),
|
||||
"addGeometry(Part.LineSegment(App.Vector(%f, %f,0), App.Vector(%f, %f,0)), True)",
|
||||
startPoint.x,
|
||||
startPoint.y,
|
||||
endPoint.x,
|
||||
endPoint.y
|
||||
);
|
||||
handleId = getHighestCurveIndex();
|
||||
|
||||
std::string escText = escapeForPython(text);
|
||||
std::string escFont = escapeForPython(font);
|
||||
bool isHeight = constructionMethod() == ConstructionMethod::Height;
|
||||
const char* constrBoolStr = isConstructionMode() ? "True" : "False";
|
||||
const char* heightBoolStr = isHeight ? "True" : "False";
|
||||
|
||||
// Add the 'Text' Constraint (Empty)
|
||||
// We initialize the constraint containing ONLY the handle (element 0).
|
||||
// We do not add the text geometry manually to avoid floating-point precision loss
|
||||
// associated with Python serialization.
|
||||
Gui::cmdAppObjectArgs(
|
||||
getSketchObject(),
|
||||
"addConstraint(Sketcher.Constraint('Text', [%d, 0], '%s', '%s', %s))",
|
||||
handleId,
|
||||
escText.c_str(),
|
||||
escFont.c_str(),
|
||||
heightBoolStr
|
||||
);
|
||||
|
||||
// Generate Text Geometry by calling setTextAndFont on the new constraint.
|
||||
// This triggers the C++ logic to generate the exact geometry and insert it
|
||||
// into the sketch, ensuring closed wires and perfect precision.
|
||||
Gui::cmdAppObjectArgs(
|
||||
getSketchObject(),
|
||||
"setTextAndFont(len(App.ActiveDocument.getObject('%s').Constraints)-1, '%s', '%s', "
|
||||
"%s, %s)",
|
||||
getSketchObject()->getNameInDocument(),
|
||||
escText.c_str(),
|
||||
escFont.c_str(),
|
||||
heightBoolStr,
|
||||
constrBoolStr
|
||||
);
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Gui::NotifyError(
|
||||
sketchgui,
|
||||
QT_TRANSLATE_NOOP("Notifications", "Error"),
|
||||
QT_TRANSLATE_NOOP("Notifications", "Failed to add text")
|
||||
);
|
||||
|
||||
Gui::Command::abortCommand();
|
||||
}
|
||||
}
|
||||
|
||||
void generateAutoConstraints() override
|
||||
{
|
||||
// Generate temporary autoconstraints (but do not actually add them to the sketch)
|
||||
if (avoidRedundants) {
|
||||
removeRedundantHorizontalVertical(getSketchObject(), sugConstraints[0], sugConstraints[1]);
|
||||
}
|
||||
|
||||
auto& ac1 = sugConstraints[0];
|
||||
auto& ac2 = sugConstraints[1];
|
||||
|
||||
generateAutoConstraintsOnElement(ac1, handleId, Sketcher::PointPos::start);
|
||||
generateAutoConstraintsOnElement(ac2, handleId, Sketcher::PointPos::end);
|
||||
|
||||
// Ensure temporary autoconstraints do not generate a redundancy and that the geometry
|
||||
// parameters are accurate This is particularly important for adding widget mandated
|
||||
// constraints.
|
||||
removeRedundantAutoConstraints();
|
||||
}
|
||||
|
||||
void createAutoConstraints() override
|
||||
{
|
||||
// execute python command to create autoconstraints
|
||||
createGeneratedAutoConstraints(true);
|
||||
|
||||
sugConstraints[0].clear();
|
||||
sugConstraints[1].clear();
|
||||
}
|
||||
|
||||
std::string getToolName() const override
|
||||
{
|
||||
return "DSH_Text";
|
||||
}
|
||||
|
||||
QString getCrosshairCursorSVGName() const override
|
||||
{
|
||||
return QStringLiteral("Sketcher_Pointer_Text.svg");
|
||||
}
|
||||
|
||||
std::unique_ptr<QWidget> createWidget() const override
|
||||
{
|
||||
return std::make_unique<SketcherToolDefaultWidget>();
|
||||
}
|
||||
|
||||
bool isWidgetVisible() const override
|
||||
{
|
||||
return true; // Text tool must show the line edit to make sense
|
||||
};
|
||||
|
||||
QPixmap getToolIcon() const override
|
||||
{
|
||||
return Gui::BitmapFactory().pixmap("Sketcher_CreateText");
|
||||
}
|
||||
|
||||
QString getToolWidgetText() const override
|
||||
{
|
||||
return QString(QObject::tr("Text parameters"));
|
||||
}
|
||||
|
||||
bool canGoToNextMode() override
|
||||
{
|
||||
if (state() == SelectMode::SeekSecond && length < Precision::Confusion()) {
|
||||
// Prevent validation of null Text.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void angleSnappingControl() override
|
||||
{
|
||||
if (state() == SelectMode::SeekSecond) {
|
||||
setAngleSnapping(true, startPoint);
|
||||
}
|
||||
|
||||
else {
|
||||
setAngleSnapping(false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QMap<QString, QString> fontPathMap;
|
||||
Base::Vector2d startPoint, endPoint;
|
||||
double length;
|
||||
int handleId;
|
||||
|
||||
std::string text;
|
||||
std::string font;
|
||||
std::string cachedTextName;
|
||||
std::string cachedFontName;
|
||||
std::vector<TopoDS_Shape> cachedBaseShapes;
|
||||
|
||||
void createShape(bool onlyeditoutline) override
|
||||
{
|
||||
ShapeGeometry.clear();
|
||||
|
||||
Base::Vector2d vecL = endPoint - startPoint;
|
||||
length = vecL.Length();
|
||||
if (length < Precision::Confusion()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Check if the cache is valid. If the user selected a new file,
|
||||
// or if the cache is empty, we need to re-load from the SVG.
|
||||
if (cachedTextName != text || cachedFontName != font || cachedBaseShapes.empty()) {
|
||||
if (!font.empty()) {
|
||||
cachedTextName = text;
|
||||
cachedFontName = font;
|
||||
// This is the one-time slow operation to get the template shapes.
|
||||
cachedBaseShapes = Part::makeTextWires(text, font);
|
||||
}
|
||||
else {
|
||||
cachedBaseShapes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Call the generic helper to transform and create the final geometry.
|
||||
transformAndConvertToGeometry(
|
||||
ShapeGeometry,
|
||||
cachedBaseShapes,
|
||||
toVector3d(startPoint),
|
||||
toVector3d(endPoint),
|
||||
constructionMethod() == ConstructionMethod::Height
|
||||
);
|
||||
|
||||
// 3. Set construction mode on the newly created geometry
|
||||
if (isConstructionMode() && !onlyeditoutline) {
|
||||
for (auto& geo : ShapeGeometry) {
|
||||
Sketcher::GeometryFacade::setConstruction(geo.get(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::list<Gui::InputHint> getToolHints() const override
|
||||
{
|
||||
return lookupTextHints(static_cast<int>(constructionMethod()), static_cast<int>(state()));
|
||||
}
|
||||
|
||||
struct HintEntry
|
||||
{
|
||||
int constructionMethod;
|
||||
int state;
|
||||
std::list<Gui::InputHint> hints;
|
||||
};
|
||||
|
||||
using HintTable = std::vector<HintEntry>;
|
||||
|
||||
static Gui::InputHint switchModeHint();
|
||||
static HintTable getTextHintTable();
|
||||
static std::list<Gui::InputHint> lookupTextHints(int method, int state);
|
||||
};
|
||||
|
||||
template<>
|
||||
auto DSHTextControllerBase::getState(int labelindex) const
|
||||
{
|
||||
switch (labelindex) {
|
||||
case OnViewParameter::First:
|
||||
case OnViewParameter::Second:
|
||||
return SelectMode::SeekFirst;
|
||||
break;
|
||||
case OnViewParameter::Third:
|
||||
case OnViewParameter::Fourth:
|
||||
return SelectMode::SeekSecond;
|
||||
break;
|
||||
default:
|
||||
THROWM(Base::ValueError, "Label index without an associated machine state")
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DSHTextController::configureToolWidget()
|
||||
{
|
||||
if (!init) { // Code to be executed only upon initialisation
|
||||
QStringList names = {
|
||||
QApplication::translate("TaskSketcherTool_c1_text", "Width"),
|
||||
QApplication::translate("TaskSketcherTool_c1_text", "Height")
|
||||
};
|
||||
toolWidget->setComboboxElements(WCombobox::FirstCombo, names);
|
||||
|
||||
toolWidget->setLineEditLabel(
|
||||
WLineEdit::FirstEdit,
|
||||
QApplication::translate("TaskSketcherTool_Text", "Text")
|
||||
);
|
||||
toolWidget->setLineEditText(WLineEdit::FirstEdit, QString::fromStdString(handler->text));
|
||||
|
||||
toolWidget->setComboboxLabel(
|
||||
WCombobox::SecondCombo,
|
||||
QApplication::translate("TaskSketcherTool_Text", "Font")
|
||||
);
|
||||
|
||||
// 1. Scan for font files and store the map
|
||||
handler->fontPathMap = findAvailableFontFiles();
|
||||
|
||||
// 2. Populate combobox with friendly names (the keys of the map)
|
||||
QStringList fontNames = handler->fontPathMap.keys();
|
||||
fontNames.sort(Qt::CaseInsensitive);
|
||||
toolWidget->setComboboxElements(WCombobox::SecondCombo, fontNames);
|
||||
|
||||
// 3. Set a sensible default font
|
||||
QString defaultFontName;
|
||||
if (fontNames.contains(QString::fromUtf8("osifont-lgpl3fe"), Qt::CaseInsensitive)) {
|
||||
defaultFontName = QString::fromUtf8("osifont-lgpl3fe");
|
||||
}
|
||||
else if (fontNames.contains(QString::fromUtf8("DejaVu Sans"), Qt::CaseInsensitive)) {
|
||||
defaultFontName = QString::fromUtf8("DejaVu Sans");
|
||||
}
|
||||
else if (fontNames.contains(QString::fromUtf8("Arial"), Qt::CaseInsensitive)) {
|
||||
defaultFontName = QString::fromUtf8("Arial");
|
||||
}
|
||||
else if (!fontNames.isEmpty()) {
|
||||
defaultFontName = fontNames.first();
|
||||
}
|
||||
|
||||
if (!defaultFontName.isEmpty()) {
|
||||
// Find the actual case-sensitive key
|
||||
for (const auto& key : fontNames) {
|
||||
if (key.compare(defaultFontName, Qt::CaseInsensitive) == 0) {
|
||||
handler->font = handler->fontPathMap.value(key).toStdString();
|
||||
toolWidget->setComboboxCurrentText(WCombobox::SecondCombo, key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onViewParameters[OnViewParameter::First]->setLabelType(Gui::SoDatumLabel::DISTANCEX);
|
||||
onViewParameters[OnViewParameter::Second]->setLabelType(Gui::SoDatumLabel::DISTANCEY);
|
||||
|
||||
onViewParameters[OnViewParameter::Third]->setLabelType(
|
||||
Gui::SoDatumLabel::DISTANCE,
|
||||
Gui::EditableDatumLabel::Function::Dimensioning
|
||||
);
|
||||
onViewParameters[OnViewParameter::Fourth]->setLabelType(
|
||||
Gui::SoDatumLabel::ANGLE,
|
||||
Gui::EditableDatumLabel::Function::Dimensioning
|
||||
);
|
||||
}
|
||||
|
||||
toolWidget->setLineEditText(
|
||||
SketcherToolDefaultWidget::LineEdit::FirstEdit,
|
||||
QString::fromStdString(handler->text)
|
||||
);
|
||||
}
|
||||
|
||||
template<>
|
||||
void DSHTextController::adaptDrawingToLineEditTextChange(int lineeditindex, const QString& value)
|
||||
{
|
||||
if (lineeditindex == WLineEdit::FirstEdit) {
|
||||
handler->text = value.toStdString();
|
||||
// The redraw is handled by the controller's finishControlsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DSHTextController::adaptDrawingToComboboxChange(int comboboxindex, int value)
|
||||
{
|
||||
if (comboboxindex == WCombobox::FirstCombo) {
|
||||
handler->setConstructionMethod(static_cast<ConstructionMethod>(value));
|
||||
}
|
||||
else if (comboboxindex == WCombobox::SecondCombo) {
|
||||
// Get the selected friendly name
|
||||
QString fontName = toolWidget->getComboboxCurrentText(WCombobox::SecondCombo);
|
||||
// Look up the full path in our map and update the handler
|
||||
if (handler->fontPathMap.contains(fontName)) {
|
||||
handler->font = handler->fontPathMap.value(fontName).toStdString();
|
||||
}
|
||||
// The redraw is handled by the controller's finishControlsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DSHTextControllerBase::doEnforceControlParameters(Base::Vector2d& onSketchPos)
|
||||
{
|
||||
switch (handler->state()) {
|
||||
case SelectMode::SeekFirst: {
|
||||
auto& firstParam = onViewParameters[OnViewParameter::First];
|
||||
auto& secondParam = onViewParameters[OnViewParameter::Second];
|
||||
|
||||
if (firstParam->isSet) {
|
||||
onSketchPos.x = firstParam->getValue();
|
||||
}
|
||||
|
||||
if (secondParam->isSet) {
|
||||
onSketchPos.y = secondParam->getValue();
|
||||
}
|
||||
} break;
|
||||
case SelectMode::SeekSecond: {
|
||||
auto& thirdParam = onViewParameters[OnViewParameter::Third];
|
||||
auto& fourthParam = onViewParameters[OnViewParameter::Fourth];
|
||||
|
||||
Base::Vector2d dir = onSketchPos - handler->startPoint;
|
||||
if (dir.Length() < Precision::Confusion()) {
|
||||
dir.x = 1.0; // if direction null, default to (1,0)
|
||||
}
|
||||
double length = dir.Length();
|
||||
|
||||
if (thirdParam->isSet) {
|
||||
length = thirdParam->getValue();
|
||||
if (length < Precision::Confusion()) {
|
||||
unsetOnViewParameter(thirdParam.get());
|
||||
return;
|
||||
}
|
||||
|
||||
onSketchPos = handler->startPoint + length * dir.Normalize();
|
||||
}
|
||||
|
||||
if (fourthParam->isSet) {
|
||||
double angle = Base::toRadians(fourthParam->getValue());
|
||||
if (handler->constructionMethod() == ConstructionMethod::Height) {
|
||||
angle += M_PI * 0.5;
|
||||
}
|
||||
Base::Vector2d dir(cos(angle), sin(angle));
|
||||
onSketchPos.ProjectToLine(onSketchPos - handler->startPoint, dir);
|
||||
onSketchPos += handler->startPoint;
|
||||
}
|
||||
|
||||
if (thirdParam->isSet && fourthParam->isSet
|
||||
&& (onSketchPos - handler->startPoint).Length() < Precision::Confusion()) {
|
||||
unsetOnViewParameter(thirdParam.get());
|
||||
unsetOnViewParameter(fourthParam.get());
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DSHTextController::adaptParameters(Base::Vector2d onSketchPos)
|
||||
{
|
||||
switch (handler->state()) {
|
||||
case SelectMode::SeekFirst: {
|
||||
auto& firstParam = onViewParameters[OnViewParameter::First];
|
||||
auto& secondParam = onViewParameters[OnViewParameter::Second];
|
||||
|
||||
if (!firstParam->isSet) {
|
||||
setOnViewParameterValue(OnViewParameter::First, onSketchPos.x);
|
||||
}
|
||||
|
||||
if (!secondParam->isSet) {
|
||||
setOnViewParameterValue(OnViewParameter::Second, onSketchPos.y);
|
||||
}
|
||||
|
||||
bool sameSign = onSketchPos.x * onSketchPos.y > 0.;
|
||||
firstParam->setLabelAutoDistanceReverse(!sameSign);
|
||||
secondParam->setLabelAutoDistanceReverse(sameSign);
|
||||
firstParam->setPoints(Base::Vector3d(), toVector3d(onSketchPos));
|
||||
secondParam->setPoints(Base::Vector3d(), toVector3d(onSketchPos));
|
||||
} break;
|
||||
case SelectMode::SeekSecond: {
|
||||
auto& thirdParam = onViewParameters[OnViewParameter::Third];
|
||||
auto& fourthParam = onViewParameters[OnViewParameter::Fourth];
|
||||
|
||||
Base::Vector3d start = toVector3d(handler->startPoint);
|
||||
Base::Vector3d end = toVector3d(handler->endPoint);
|
||||
Base::Vector3d vec = end - start;
|
||||
|
||||
if (!thirdParam->isSet) {
|
||||
setOnViewParameterValue(OnViewParameter::Third, vec.Length());
|
||||
}
|
||||
|
||||
double range;
|
||||
if (handler->constructionMethod() == ConstructionMethod::Height) {
|
||||
Base::Vector2d norm(vec.y, -vec.x);
|
||||
Base::Vector2d textAlignPoint = handler->startPoint + norm;
|
||||
range = (textAlignPoint - handler->startPoint).Angle();
|
||||
}
|
||||
else {
|
||||
range = (handler->endPoint - handler->startPoint).Angle();
|
||||
}
|
||||
|
||||
|
||||
if (!fourthParam->isSet) {
|
||||
setOnViewParameterValue(
|
||||
OnViewParameter::Fourth,
|
||||
Base::toDegrees(range),
|
||||
Base::Unit::Angle
|
||||
);
|
||||
}
|
||||
else if (vec.Length() > Precision::Confusion()) {
|
||||
double ovpRange = Base::toRadians(fourthParam->getValue());
|
||||
if (fabs(range - ovpRange) > Precision::Confusion()) {
|
||||
setOnViewParameterValue(
|
||||
OnViewParameter::Fourth,
|
||||
Base::toDegrees(range),
|
||||
Base::Unit::Angle
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
thirdParam->setPoints(start, end);
|
||||
fourthParam->setPoints(start, Base::Vector3d());
|
||||
fourthParam->setLabelRange(range);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DSHTextController::computeNextDrawSketchHandlerMode()
|
||||
{
|
||||
switch (handler->state()) {
|
||||
case SelectMode::SeekFirst: {
|
||||
auto& firstParam = onViewParameters[OnViewParameter::First];
|
||||
auto& secondParam = onViewParameters[OnViewParameter::Second];
|
||||
|
||||
if (firstParam->isSet && secondParam->isSet) {
|
||||
handler->setNextState(SelectMode::SeekSecond);
|
||||
}
|
||||
} break;
|
||||
case SelectMode::SeekSecond: {
|
||||
auto& thirdParam = onViewParameters[OnViewParameter::Third];
|
||||
auto& fourthParam = onViewParameters[OnViewParameter::Fourth];
|
||||
|
||||
if (thirdParam->hasFinishedEditing && fourthParam->hasFinishedEditing) {
|
||||
handler->setNextState(SelectMode::End);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DSHTextController::addConstraints()
|
||||
{
|
||||
App::DocumentObject* obj = handler->sketchgui->getObject();
|
||||
|
||||
int firstCurve = handler->handleId;
|
||||
|
||||
auto x0 = onViewParameters[OnViewParameter::First]->getValue();
|
||||
auto y0 = onViewParameters[OnViewParameter::Second]->getValue();
|
||||
auto p3 = onViewParameters[OnViewParameter::Third]->getValue();
|
||||
auto p4 = onViewParameters[OnViewParameter::Fourth]->getValue();
|
||||
|
||||
auto x0set = onViewParameters[OnViewParameter::First]->isSet;
|
||||
auto y0set = onViewParameters[OnViewParameter::Second]->isSet;
|
||||
auto p3set = onViewParameters[OnViewParameter::Third]->isSet;
|
||||
auto p4set = onViewParameters[OnViewParameter::Fourth]->isSet;
|
||||
|
||||
using namespace Sketcher;
|
||||
|
||||
auto constraintToOrigin = [&]() {
|
||||
ConstraintToAttachment(GeoElementId(firstCurve, PointPos::start), GeoElementId::RtPnt, x0, obj);
|
||||
};
|
||||
|
||||
auto constraintx0 = [&]() {
|
||||
ConstraintToAttachment(GeoElementId(firstCurve, PointPos::start), GeoElementId::VAxis, x0, obj);
|
||||
};
|
||||
|
||||
auto constrainty0 = [&]() {
|
||||
ConstraintToAttachment(GeoElementId(firstCurve, PointPos::start), GeoElementId::HAxis, y0, obj);
|
||||
};
|
||||
|
||||
auto constraintp3length = [&]() {
|
||||
Gui::cmdAppObjectArgs(
|
||||
obj,
|
||||
"addConstraint(Sketcher.Constraint('Distance',%d,%f)) ",
|
||||
firstCurve,
|
||||
fabs(p3)
|
||||
);
|
||||
};
|
||||
|
||||
auto constraintp4angle = [&]() {
|
||||
double angle = Base::toRadians(p4);
|
||||
if (handler->constructionMethod() == ConstructionMethod::Height) {
|
||||
angle += M_PI * 0.5;
|
||||
}
|
||||
|
||||
ConstraintLineByAngle(firstCurve, angle, obj);
|
||||
};
|
||||
|
||||
if (handler->AutoConstraints.empty()) { // No valid diagnosis. Every constraint can be added.
|
||||
|
||||
if (x0set && y0set && x0 == 0. && y0 == 0.) {
|
||||
constraintToOrigin();
|
||||
}
|
||||
else {
|
||||
if (x0set) {
|
||||
constraintx0();
|
||||
}
|
||||
|
||||
if (y0set) {
|
||||
constrainty0();
|
||||
}
|
||||
}
|
||||
|
||||
if (p3set) {
|
||||
constraintp3length();
|
||||
}
|
||||
|
||||
if (p4set) {
|
||||
constraintp4angle();
|
||||
}
|
||||
}
|
||||
else { // Valid diagnosis. Must check which constraints may be added.
|
||||
auto startpointinfo = handler->getPointInfo(GeoElementId(firstCurve, PointPos::start));
|
||||
|
||||
if (x0set && startpointinfo.isXDoF()) {
|
||||
constraintx0();
|
||||
|
||||
handler->diagnoseWithAutoConstraints(); // ensure we have recalculated parameters after
|
||||
// each constraint addition
|
||||
|
||||
startpointinfo = handler->getPointInfo(
|
||||
GeoElementId(firstCurve, PointPos::start)
|
||||
); // get updated point position
|
||||
}
|
||||
|
||||
if (y0set && startpointinfo.isYDoF()) {
|
||||
constrainty0();
|
||||
|
||||
handler->diagnoseWithAutoConstraints(); // ensure we have recalculated parameters after
|
||||
// each constraint addition
|
||||
|
||||
startpointinfo = handler->getPointInfo(
|
||||
GeoElementId(firstCurve, PointPos::start)
|
||||
); // get updated point position
|
||||
}
|
||||
|
||||
auto endpointinfo = handler->getPointInfo(GeoElementId(firstCurve, PointPos::end));
|
||||
|
||||
int DoFs = startpointinfo.getDoFs();
|
||||
DoFs += endpointinfo.getDoFs();
|
||||
|
||||
if (p3set && DoFs > 0) {
|
||||
constraintp3length();
|
||||
DoFs--;
|
||||
}
|
||||
|
||||
if (p4set && DoFs > 0) {
|
||||
constraintp4angle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Gui::InputHint DrawSketchHandlerText::switchModeHint()
|
||||
{
|
||||
return {QObject::tr("%1 switch mode"), {Gui::InputHint::UserInput::KeyM}};
|
||||
}
|
||||
|
||||
DrawSketchHandlerText::HintTable DrawSketchHandlerText::getTextHintTable()
|
||||
{
|
||||
const auto switchHint = switchModeHint();
|
||||
return {
|
||||
// Structure: {constructionMethod, state, {hints...}}
|
||||
{static_cast<int>(ConstructionMethod::Height),
|
||||
0,
|
||||
{{QObject::tr("%1 pick bottom-left point"), {Gui::InputHint::UserInput::MouseLeft}},
|
||||
switchHint}},
|
||||
{static_cast<int>(ConstructionMethod::Height),
|
||||
1,
|
||||
{{QObject::tr("%1 pick top-left point"), {Gui::InputHint::UserInput::MouseLeft}},
|
||||
switchHint}},
|
||||
{static_cast<int>(ConstructionMethod::Width),
|
||||
0,
|
||||
{{QObject::tr("%1 pick bottom-left point"), {Gui::InputHint::UserInput::MouseLeft}},
|
||||
switchHint}},
|
||||
{static_cast<int>(ConstructionMethod::Width),
|
||||
1,
|
||||
{{QObject::tr("%1 pick bottom-right point"), {Gui::InputHint::UserInput::MouseLeft}},
|
||||
switchHint}}
|
||||
};
|
||||
}
|
||||
|
||||
std::list<Gui::InputHint> DrawSketchHandlerText::lookupTextHints(int method, int state)
|
||||
{
|
||||
const auto TextHintTable = getTextHintTable();
|
||||
|
||||
auto it = std::find_if(
|
||||
TextHintTable.begin(),
|
||||
TextHintTable.end(),
|
||||
[method, state](const HintEntry& entry) {
|
||||
return entry.constructionMethod == method && entry.state == state;
|
||||
}
|
||||
);
|
||||
|
||||
return (it != TextHintTable.end()) ? it->hints : std::list<Gui::InputHint> {};
|
||||
}
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_DrawSketchHandlerText_H
|
||||
@@ -57,10 +57,11 @@ using DSHTranslateController = DrawSketchDefaultWidgetController<
|
||||
DrawSketchHandlerTranslate,
|
||||
StateMachines::ThreeSeekEnd,
|
||||
/*PAutoConstraintSize =*/0,
|
||||
/*OnViewParametersT =*/OnViewParameters<6>,
|
||||
/*WidgetParametersT =*/WidgetParameters<2>,
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<1>,
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>>;
|
||||
/*OnViewParametersT =*/OnViewParameters<6>, // NOLINT
|
||||
/*WidgetParametersT =*/WidgetParameters<2>, // NOLINT
|
||||
/*WidgetCheckboxesT =*/WidgetCheckboxes<1>, // NOLINT
|
||||
/*WidgetComboboxesT =*/WidgetComboboxes<0>, // NOLINT
|
||||
/*WidgetLineEditsT =*/WidgetLineEdits<0>>; // NOLINT
|
||||
|
||||
using DSHTranslateControllerBase = DSHTranslateController::ControllerBase;
|
||||
|
||||
|
||||
@@ -451,6 +451,14 @@ bool hasVisualFeature(App::DocumentObject* obj, App::DocumentObject* rootObj, Gu
|
||||
|
||||
void EditDatumDialog::performAutoScale(double newDatum)
|
||||
{
|
||||
const std::vector<Sketcher::Constraint*>& constraints = sketch->Constraints.getValues();
|
||||
for (auto* constr : constraints) {
|
||||
if (constr->Type == Sketcher::Group || constr->Type == Sketcher::Text) {
|
||||
// Do not attempt to scale if there's a group
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/Mod/Sketcher/dimensioning"
|
||||
);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <QPainter>
|
||||
#include <QRegularExpression>
|
||||
#include <Bnd_Box.hxx>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
@@ -47,6 +48,8 @@
|
||||
#include <Inventor/nodes/SoSeparator.h>
|
||||
#include <Inventor/nodes/SoTranslation.h>
|
||||
|
||||
#include <BRepBndLib.hxx>
|
||||
|
||||
#include <Base/Converter.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Tools.h>
|
||||
@@ -792,6 +795,87 @@ Restart:
|
||||
// Reference Position that is scaled according to zoom
|
||||
translation->translation = SbVec3f(relpos2.x - relpos1.x, relpos2.y - relpos1.y, 0);
|
||||
|
||||
} break;
|
||||
case Text:
|
||||
case Group: {
|
||||
if (Constr->isElementsEmpty()) {
|
||||
break; // Nothing to do if the group is empty
|
||||
}
|
||||
|
||||
Bnd_Box totalBBox;
|
||||
int elementIndex = 0;
|
||||
while (Constr->hasElement(elementIndex)) {
|
||||
auto element = Constr->getElement(elementIndex);
|
||||
if (element.GeoId < -extGeoCount || element.GeoId >= intGeoCount) {
|
||||
elementIndex++;
|
||||
continue;
|
||||
}
|
||||
const Part::Geometry* geo = geolistfacade.getGeometryFromGeoId(element.GeoId);
|
||||
if (!geo) {
|
||||
elementIndex++;
|
||||
continue;
|
||||
}
|
||||
TopoDS_Shape shape = geo->toShape();
|
||||
if (!shape.IsNull()) {
|
||||
BRepBndLib::Add(shape, totalBBox, false);
|
||||
}
|
||||
elementIndex++;
|
||||
}
|
||||
|
||||
if (!totalBBox.HasFinitePart() || totalBBox.IsVoid()) {
|
||||
// If no valid box, hide the geometry by setting all points to the origin.
|
||||
SoCoordinate3* coords = static_cast<SoCoordinate3*>(sep->getChild(2));
|
||||
|
||||
// Use startEditing() to get a writable pointer to the internal array.
|
||||
SbVec3f* points = coords->point.startEditing();
|
||||
for (int j = 0; j < 5; ++j) {
|
||||
points[j].setValue(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
coords->point.finishEditing();
|
||||
}
|
||||
|
||||
// 1. Get the original min/max points and dimensions
|
||||
gp_Pnt min_pnt_orig = totalBBox.CornerMin();
|
||||
gp_Pnt max_pnt_orig = totalBBox.CornerMax();
|
||||
double width = max_pnt_orig.X() - min_pnt_orig.X();
|
||||
double height = max_pnt_orig.Y() - min_pnt_orig.Y();
|
||||
|
||||
// 2. Calculate the offset amount
|
||||
// Using the average of width and height is a good heuristic for a uniform
|
||||
// offset.
|
||||
double offset = (width + height) / 2.0 * 0.05; // 5% of the average dimension
|
||||
|
||||
// 3. Create new, "inflated" corner points by applying the offset
|
||||
gp_Pnt min_pnt(
|
||||
min_pnt_orig.X() - offset,
|
||||
min_pnt_orig.Y() - offset,
|
||||
min_pnt_orig.Z()
|
||||
);
|
||||
gp_Pnt max_pnt(
|
||||
max_pnt_orig.X() + offset,
|
||||
max_pnt_orig.Y() + offset,
|
||||
max_pnt_orig.Z()
|
||||
);
|
||||
|
||||
// 4. Define the 4 corners of the rectangle using the inflated points
|
||||
SbVec3f p0(min_pnt.X(), min_pnt.Y(), zConstrH); // bottom-left
|
||||
SbVec3f p1(max_pnt.X(), min_pnt.Y(), zConstrH); // bottom-right
|
||||
SbVec3f p2(max_pnt.X(), max_pnt.Y(), zConstrH); // top-right
|
||||
SbVec3f p3(min_pnt.X(), max_pnt.Y(), zConstrH); // top-left
|
||||
|
||||
// 3. Get the SoCoordinate3 node we created in rebuildConstraintNodes
|
||||
// Index 0: SoMaterial, Index 1: SoDrawStyle, Index 2: SoCoordinate3
|
||||
SoCoordinate3* coords = static_cast<SoCoordinate3*>(sep->getChild(2));
|
||||
|
||||
// 4. Update the points in the node to draw the rectangle
|
||||
SbVec3f* points = coords->point.startEditing();
|
||||
points[0] = p0;
|
||||
points[1] = p1;
|
||||
points[2] = p2;
|
||||
points[3] = p3;
|
||||
points[4] = p0; // Repeat the first point to close the loop
|
||||
coords->point.finishEditing();
|
||||
|
||||
} break;
|
||||
case Distance:
|
||||
case DistanceX:
|
||||
@@ -1795,12 +1879,16 @@ void EditModeConstraintCoinManager::updateConstraintColor(
|
||||
}
|
||||
}
|
||||
else {
|
||||
bool isActive = ViewProviderSketchCoinAttorney::isConstraintActiveInSketch(
|
||||
viewProvider,
|
||||
constraint
|
||||
);
|
||||
if (hasDatumLabel) {
|
||||
SoDatumLabel* l = static_cast<SoDatumLabel*>(
|
||||
s->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex))
|
||||
);
|
||||
|
||||
l->textColor = constraint->isActive
|
||||
l->textColor = isActive
|
||||
? ViewProviderSketchCoinAttorney::constraintHasExpression(viewProvider, i)
|
||||
? drawingParameters.ExprBasedConstrDimColor
|
||||
: (constraint->isDriving ? drawingParameters.ConstrDimColor
|
||||
@@ -1808,7 +1896,7 @@ void EditModeConstraintCoinManager::updateConstraintColor(
|
||||
: drawingParameters.DeactivatedConstrDimColor;
|
||||
}
|
||||
else if (hasMaterial) {
|
||||
m->diffuseColor = constraint->isActive
|
||||
m->diffuseColor = isActive
|
||||
? (constraint->isDriving ? drawingParameters.ConstrDimColor
|
||||
: drawingParameters.NonDrivingConstrDimColor)
|
||||
: drawingParameters.DeactivatedConstrDimColor;
|
||||
@@ -1884,7 +1972,8 @@ void EditModeConstraintCoinManager::rebuildConstraintNodes(
|
||||
// every constrained visual node gets its own material for preselection and selection
|
||||
SoMaterial* mat = new SoMaterial;
|
||||
mat->ref();
|
||||
mat->diffuseColor = (*it)->isActive
|
||||
bool isActive = ViewProviderSketchCoinAttorney::isConstraintActiveInSketch(viewProvider, *it);
|
||||
mat->diffuseColor = isActive
|
||||
? ((*it)->isDriving ? drawingParameters.ConstrDimColor
|
||||
: drawingParameters.NonDrivingConstrDimColor)
|
||||
: drawingParameters.DeactivatedConstrDimColor;
|
||||
@@ -1902,7 +1991,7 @@ void EditModeConstraintCoinManager::rebuildConstraintNodes(
|
||||
SoDatumLabel* text = new SoDatumLabel();
|
||||
text->norm.setValue(norm);
|
||||
text->string = "";
|
||||
text->textColor = (*it)->isActive
|
||||
text->textColor = isActive
|
||||
? ((*it)->isDriving ? drawingParameters.ConstrDimColor
|
||||
: drawingParameters.NonDrivingConstrDimColor)
|
||||
: drawingParameters.DeactivatedConstrDimColor;
|
||||
@@ -1938,6 +2027,31 @@ void EditModeConstraintCoinManager::rebuildConstraintNodes(
|
||||
// remember the type of this constraint node
|
||||
vConstrType.push_back((*it)->Type);
|
||||
} break;
|
||||
case Group:
|
||||
case Text: {
|
||||
// For a group, we will draw a dashed rectangle.
|
||||
// We need a Material, a DrawStyle, Coordinates, and a LineSet.
|
||||
|
||||
// 1. Material (for color, re-using the one already created)
|
||||
sep->addChild(mat);
|
||||
|
||||
// 2. DrawStyle (to make the line dashed)
|
||||
SoDrawStyle* drawStyle = new SoDrawStyle();
|
||||
drawStyle->linePattern = 0x0F0F; // A standard 50% dashed pattern
|
||||
sep->addChild(drawStyle);
|
||||
|
||||
// 3. Coordinates (for the 4 corners + 1 to close the loop)
|
||||
SoCoordinate3* coords = new SoCoordinate3();
|
||||
coords->point.setNum(5); // Pre-allocate 5 points for a closed rectangle
|
||||
sep->addChild(coords);
|
||||
|
||||
// 4. LineSet (to connect the coordinates)
|
||||
SoLineSet* lineSet = new SoLineSet();
|
||||
lineSet->numVertices.set1Value(0, 5); // A single polyline of 5 vertices
|
||||
sep->addChild(lineSet);
|
||||
|
||||
vConstrType.push_back((*it)->Type);
|
||||
} break;
|
||||
case Coincident: // no visual for coincident so far
|
||||
vConstrType.push_back(Coincident);
|
||||
break;
|
||||
@@ -2861,6 +2975,10 @@ QColor EditModeConstraintCoinManager::constrColor(int constraintId)
|
||||
};
|
||||
|
||||
const auto constraints = ViewProviderSketchCoinAttorney::getConstraints(viewProvider);
|
||||
bool isActive = ViewProviderSketchCoinAttorney::isConstraintActiveInSketch(
|
||||
viewProvider,
|
||||
constraints[constraintId]
|
||||
);
|
||||
|
||||
if (ViewProviderSketchCoinAttorney::isConstraintPreselected(viewProvider, constraintId)) {
|
||||
return toQColor(drawingParameters.PreselectColor);
|
||||
@@ -2868,7 +2986,7 @@ QColor EditModeConstraintCoinManager::constrColor(int constraintId)
|
||||
else if (ViewProviderSketchCoinAttorney::isConstraintSelected(viewProvider, constraintId)) {
|
||||
return toQColor(drawingParameters.SelectColor);
|
||||
}
|
||||
else if (!constraints[constraintId]->isActive) {
|
||||
else if (!isActive) {
|
||||
return toQColor(drawingParameters.DeactivatedConstrDimColor);
|
||||
}
|
||||
else if (!constraints[constraintId]->isDriving) {
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "EditModeCoinManagerParameters.h"
|
||||
#include "EditModeGeometryCoinConverter.h"
|
||||
#include "Utils.h"
|
||||
@@ -89,85 +91,59 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli
|
||||
int coinLayer,
|
||||
EditModeGeometryCoinConverter::PointsMode pointmode,
|
||||
int numberCurves,
|
||||
int sublayer
|
||||
int sublayer,
|
||||
bool isGroupMember = false
|
||||
) {
|
||||
// Determine how many vertices this geometry has.
|
||||
int numberPoints = 0;
|
||||
|
||||
if (pointmode == PointsMode::InsertSingle) {
|
||||
numberPoints = 1;
|
||||
|
||||
coinMapping.GeoElementId2SetId.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(geoId, Sketcher::PointPos::start),
|
||||
std::forward_as_tuple(pointCounter[coinLayer]++, coinLayer)
|
||||
);
|
||||
}
|
||||
else if (pointmode == PointsMode::InsertStartEnd) {
|
||||
numberPoints = 2;
|
||||
|
||||
coinMapping.GeoElementId2SetId.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(geoId, Sketcher::PointPos::start),
|
||||
std::forward_as_tuple(pointCounter[coinLayer]++, coinLayer)
|
||||
);
|
||||
|
||||
coinMapping.GeoElementId2SetId.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(geoId, Sketcher::PointPos::end),
|
||||
std::forward_as_tuple(pointCounter[coinLayer]++, coinLayer)
|
||||
);
|
||||
}
|
||||
else if (pointmode == PointsMode::InsertMidOnly) {
|
||||
numberPoints = 1;
|
||||
|
||||
coinMapping.GeoElementId2SetId.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(geoId, Sketcher::PointPos::mid),
|
||||
std::forward_as_tuple(pointCounter[coinLayer]++, coinLayer)
|
||||
);
|
||||
}
|
||||
else if (pointmode == PointsMode::InsertStartEndMid) {
|
||||
numberPoints = 3;
|
||||
|
||||
coinMapping.GeoElementId2SetId.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(geoId, Sketcher::PointPos::start),
|
||||
std::forward_as_tuple(pointCounter[coinLayer]++, coinLayer)
|
||||
);
|
||||
|
||||
coinMapping.GeoElementId2SetId.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(geoId, Sketcher::PointPos::end),
|
||||
std::forward_as_tuple(pointCounter[coinLayer]++, coinLayer)
|
||||
);
|
||||
|
||||
coinMapping.GeoElementId2SetId.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(geoId, Sketcher::PointPos::mid),
|
||||
std::forward_as_tuple(pointCounter[coinLayer]++, coinLayer)
|
||||
);
|
||||
}
|
||||
|
||||
// This loop simulates the creation of vertices for THIS geometry.
|
||||
// It runs for all geometries to keep vertexCounter in sync with SketchObject.
|
||||
for (int i = 0; i < numberPoints; i++) {
|
||||
coinMapping.PointIdToGeoId[coinLayer].push_back(geoId);
|
||||
Sketcher::PointPos pos;
|
||||
if (i == 0) {
|
||||
if (pointmode == PointsMode::InsertMidOnly) {
|
||||
pos = Sketcher::PointPos::mid;
|
||||
// If the point is NOT part of a group member, we add it to the physical
|
||||
// Coin maps that are used for drawing and picking.
|
||||
if (!isGroupMember) {
|
||||
// Determine the PointPos for this specific vertex of the geometry.
|
||||
Sketcher::PointPos pos;
|
||||
if (i == 0) {
|
||||
pos = (pointmode == PointsMode::InsertMidOnly) ? Sketcher::PointPos::mid
|
||||
: Sketcher::PointPos::start;
|
||||
}
|
||||
else if (i == 1) {
|
||||
pos = Sketcher::PointPos::end;
|
||||
}
|
||||
else {
|
||||
pos = Sketcher::PointPos::start;
|
||||
pos = Sketcher::PointPos::mid;
|
||||
}
|
||||
}
|
||||
else if (i == 1) {
|
||||
pos = Sketcher::PointPos::end;
|
||||
}
|
||||
else {
|
||||
pos = Sketcher::PointPos::mid;
|
||||
|
||||
// Map: (GeoId, PosId) -> (physicalIndex, layer)
|
||||
coinMapping.GeoElementId2SetId.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(geoId, pos),
|
||||
std::forward_as_tuple(pointCounter[coinLayer]++, coinLayer)
|
||||
);
|
||||
|
||||
// Map: physicalIndex -> logical info
|
||||
coinMapping.PointIdToGeoId[coinLayer].push_back(geoId);
|
||||
coinMapping.PointIdToPosId[coinLayer].push_back(pos);
|
||||
// This is the key: store the correct, globally-incremented logical VertexId.
|
||||
coinMapping.PointIdToVertexId[coinLayer].push_back(vertexCounter);
|
||||
}
|
||||
|
||||
coinMapping.PointIdToPosId[coinLayer].push_back(pos);
|
||||
coinMapping.PointIdToVertexId[coinLayer].push_back(vertexCounter++);
|
||||
// ALWAYS increment the logical vertex counter to stay in sync with SketchObject.
|
||||
vertexCounter++;
|
||||
}
|
||||
|
||||
if (numberCurves > 0) { // insert the first segment of the curve into the map
|
||||
@@ -198,6 +174,9 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli
|
||||
|
||||
auto coinLayer = geometryLayerParameters.getSafeCoinLayer(layerId);
|
||||
|
||||
auto* obj = viewProvider.getSketchObject();
|
||||
bool isGroupMember = GeoId >= 0 && obj->isInGroup(GeoId, false);
|
||||
|
||||
if (type == Part::GeomPoint::getClassTypeId()) { // add a point
|
||||
convert<
|
||||
Part::GeomPoint,
|
||||
@@ -217,13 +196,19 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli
|
||||
Part::GeomLineSegment,
|
||||
EditModeGeometryCoinConverter::PointsMode::InsertStartEnd,
|
||||
EditModeGeometryCoinConverter::CurveMode::StartEndPointsOnly,
|
||||
EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, GeoId, subLayerId);
|
||||
EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(
|
||||
geom,
|
||||
GeoId,
|
||||
subLayerId,
|
||||
isGroupMember
|
||||
);
|
||||
setTracking(
|
||||
GeoId,
|
||||
coinLayer,
|
||||
EditModeGeometryCoinConverter::PointsMode::InsertStartEnd,
|
||||
1,
|
||||
subLayerId
|
||||
subLayerId,
|
||||
isGroupMember
|
||||
);
|
||||
}
|
||||
else if (type.isDerivedFrom(Part::GeomConic::getClassTypeId())) { // add a closed curve conic
|
||||
@@ -231,13 +216,19 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli
|
||||
Part::GeomConic,
|
||||
EditModeGeometryCoinConverter::PointsMode::InsertMidOnly,
|
||||
EditModeGeometryCoinConverter::CurveMode::ClosedCurve,
|
||||
EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, GeoId, subLayerId);
|
||||
EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(
|
||||
geom,
|
||||
GeoId,
|
||||
subLayerId,
|
||||
isGroupMember
|
||||
);
|
||||
setTracking(
|
||||
GeoId,
|
||||
coinLayer,
|
||||
EditModeGeometryCoinConverter::PointsMode::InsertMidOnly,
|
||||
1,
|
||||
subLayerId
|
||||
subLayerId,
|
||||
isGroupMember
|
||||
);
|
||||
}
|
||||
else if (type.isDerivedFrom(Part::GeomArcOfConic::getClassTypeId())) { // add an arc of conic
|
||||
@@ -245,13 +236,19 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli
|
||||
Part::GeomArcOfConic,
|
||||
EditModeGeometryCoinConverter::PointsMode::InsertStartEndMid,
|
||||
EditModeGeometryCoinConverter::CurveMode::OpenCurve,
|
||||
EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, GeoId, subLayerId);
|
||||
EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(
|
||||
geom,
|
||||
GeoId,
|
||||
subLayerId,
|
||||
isGroupMember
|
||||
);
|
||||
setTracking(
|
||||
GeoId,
|
||||
coinLayer,
|
||||
EditModeGeometryCoinConverter::PointsMode::InsertStartEndMid,
|
||||
1,
|
||||
subLayerId
|
||||
subLayerId,
|
||||
isGroupMember
|
||||
);
|
||||
arcGeoIds.push_back(GeoId);
|
||||
}
|
||||
@@ -264,14 +261,16 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli
|
||||
EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitudeAndBSplineCurvature>(
|
||||
geom,
|
||||
GeoId,
|
||||
subLayerId
|
||||
subLayerId,
|
||||
isGroupMember
|
||||
);
|
||||
setTracking(
|
||||
GeoId,
|
||||
coinLayer,
|
||||
EditModeGeometryCoinConverter::PointsMode::InsertStartEnd,
|
||||
1,
|
||||
subLayerId
|
||||
subLayerId,
|
||||
isGroupMember
|
||||
);
|
||||
bsplineGeoIds.push_back(GeoId);
|
||||
}
|
||||
@@ -325,7 +324,8 @@ template<
|
||||
void EditModeGeometryCoinConverter::convert(
|
||||
const Sketcher::GeometryFacade* geometryfacade,
|
||||
[[maybe_unused]] int geoid,
|
||||
[[maybe_unused]] int subLayer
|
||||
[[maybe_unused]] int subLayer,
|
||||
bool isGroupMember
|
||||
)
|
||||
{
|
||||
auto geo = static_cast<const GeoType*>(geometryfacade->getGeometry());
|
||||
@@ -343,21 +343,23 @@ void EditModeGeometryCoinConverter::convert(
|
||||
};
|
||||
|
||||
// Points
|
||||
if constexpr (pointmode == PointsMode::InsertSingle) {
|
||||
addPoint(Points[coinLayer], geo->getPoint());
|
||||
}
|
||||
else if constexpr (pointmode == PointsMode::InsertStartEnd) {
|
||||
addPoint(Points[coinLayer], geo->getStartPoint());
|
||||
addPoint(Points[coinLayer], geo->getEndPoint());
|
||||
}
|
||||
else if constexpr (pointmode == PointsMode::InsertStartEndMid) {
|
||||
// All in this group are Trimmed Curves (see Geometry.h)
|
||||
addPoint(Points[coinLayer], geo->getStartPoint(/*emulateCCW=*/true));
|
||||
addPoint(Points[coinLayer], geo->getEndPoint(/*emulateCCW=*/true));
|
||||
addPoint(Points[coinLayer], geo->getCenter());
|
||||
}
|
||||
else if constexpr (pointmode == PointsMode::InsertMidOnly) {
|
||||
addPoint(Points[coinLayer], geo->getCenter());
|
||||
if (!isGroupMember) {
|
||||
if constexpr (pointmode == PointsMode::InsertSingle) {
|
||||
addPoint(Points[coinLayer], geo->getPoint());
|
||||
}
|
||||
else if constexpr (pointmode == PointsMode::InsertStartEnd) {
|
||||
addPoint(Points[coinLayer], geo->getStartPoint());
|
||||
addPoint(Points[coinLayer], geo->getEndPoint());
|
||||
}
|
||||
else if constexpr (pointmode == PointsMode::InsertStartEndMid) {
|
||||
// All in this group are Trimmed Curves (see Geometry.h)
|
||||
addPoint(Points[coinLayer], geo->getStartPoint(/*emulateCCW=*/true));
|
||||
addPoint(Points[coinLayer], geo->getEndPoint(/*emulateCCW=*/true));
|
||||
addPoint(Points[coinLayer], geo->getCenter());
|
||||
}
|
||||
else if constexpr (pointmode == PointsMode::InsertMidOnly) {
|
||||
addPoint(Points[coinLayer], geo->getCenter());
|
||||
}
|
||||
}
|
||||
|
||||
// Curves
|
||||
|
||||
@@ -158,7 +158,8 @@ private:
|
||||
void convert(
|
||||
const Sketcher::GeometryFacade* geometryfacade,
|
||||
[[maybe_unused]] int geoId,
|
||||
[[maybe_unused]] int subLayerId = 0
|
||||
[[maybe_unused]] int subLayerId = 0,
|
||||
bool isGroupMember = false
|
||||
);
|
||||
|
||||
private:
|
||||
@@ -168,6 +169,9 @@ private:
|
||||
GeometryLayerNodes& geometryLayerNodes;
|
||||
|
||||
std::vector<std::vector<Base::Vector3d>> Points;
|
||||
// To hide the points of geometries that are grouped, we make them transparent
|
||||
// Just not adding the points would mess the indexes in Points
|
||||
std::vector<std::vector<bool>> PointsHidden;
|
||||
std::vector<std::vector<std::vector<Base::Vector3d>>> Coords;
|
||||
std::vector<std::vector<std::vector<unsigned int>>> Index;
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <Mod/Sketcher/App/GeoList.h>
|
||||
#include <Mod/Sketcher/App/GeometryFacade.h>
|
||||
#include <Mod/Sketcher/App/SolverGeometryExtension.h>
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "EditModeGeometryCoinConverter.h"
|
||||
#include "EditModeGeometryCoinManager.h"
|
||||
@@ -463,8 +464,18 @@ void EditModeGeometryCoinManager::updateGeometryColor(
|
||||
// edit->CurveSet->numVertices => [i] indicates number of vertex for line i.
|
||||
int indexes = (editModeScenegraphNodes.CurveSet[l][t]->numVertices[i]);
|
||||
|
||||
bool selected = ViewProviderSketchCoinAttorney::isCurveSelected(viewProvider, GeoId);
|
||||
bool preselected = (preselectcurve == GeoId);
|
||||
|
||||
auto* obj = viewProvider.getSketchObject();
|
||||
bool isGroupMember = GeoId >= 0 && obj->isInGroup(GeoId, false);
|
||||
if (isGroupMember) {
|
||||
// We use the same color as group handle.
|
||||
GeoId = obj->getGroupHandleIfInGroup(GeoId);
|
||||
}
|
||||
|
||||
bool selected = ViewProviderSketchCoinAttorney::isCurveSelected(viewProvider, GeoId);
|
||||
// if a grouped edge is preselected we still want it to be shown
|
||||
preselected = preselected ? true : (preselectcurve == GeoId);
|
||||
bool constrainedElement = isFullyConstraintElement(GeoId);
|
||||
bool isExternal = GeoId < -1;
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/UnitsApi.h>
|
||||
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "EditModeCoinManagerParameters.h"
|
||||
#include "EditModeInformationOverlayCoinConverter.h"
|
||||
#include "ViewProviderSketchCoinAttorney.h"
|
||||
@@ -59,6 +61,17 @@ EditModeInformationOverlayCoinConverter::EditModeInformationOverlayCoinConverter
|
||||
|
||||
void EditModeInformationOverlayCoinConverter::convert(const Part::Geometry* geometry, int geoid)
|
||||
{
|
||||
if (geoid >= 0) {
|
||||
// Get the SketchObject from the ViewProvider.
|
||||
auto* obj = viewProvider.getSketchObject();
|
||||
const bool isGroupMember = obj->isInGroup(geoid, false);
|
||||
|
||||
if (obj) {
|
||||
if (obj->isInGroup(geoid, false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (geometry->is<Part::GeomBSplineCurve>()) {
|
||||
if (geoid < 0) {
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
// SPDX - License - Identifier: LGPL - 2.1 - or -later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Pierre-Louis Boyer *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <QComboBox>
|
||||
# include <QLineEdit>
|
||||
#endif
|
||||
|
||||
#include <Gui/CommandT.h>
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "CommandConstraints.h"
|
||||
#include "EditTextDialog.h"
|
||||
#include "ViewProviderSketch.h"
|
||||
#include "Utils.h"
|
||||
#include "ui_EditTextDialog.h"
|
||||
|
||||
using namespace SketcherGui;
|
||||
|
||||
EditTextDialog::EditTextDialog(ViewProviderSketch* viewProvider, int constraintIndex, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::EditTextDialog)
|
||||
, sketchView(viewProvider)
|
||||
, constrIndex(constraintIndex)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->comboBox_font->setMaxVisibleItems(20);
|
||||
|
||||
const Sketcher::SketchObject* sketch = sketchView->getSketchObject();
|
||||
const Sketcher::Constraint* constraint = sketch->Constraints[constrIndex];
|
||||
|
||||
// Initialize Text
|
||||
ui->lineEdit_text->setText(QString::fromStdString(constraint->getText()));
|
||||
|
||||
ui->radioButton_height->setChecked(constraint->getIsTextHeight());
|
||||
ui->radioButton_width->setChecked(!constraint->getIsTextHeight());
|
||||
|
||||
// Initialize Font
|
||||
populateFontList();
|
||||
QString currentFontName = findFontNameFromPath(QString::fromStdString(constraint->getFont()));
|
||||
if (!currentFontName.isEmpty()) {
|
||||
ui->comboBox_font->setCurrentText(currentFontName);
|
||||
}
|
||||
}
|
||||
|
||||
EditTextDialog::~EditTextDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void EditTextDialog::populateFontList()
|
||||
{
|
||||
fontPathMap = findAvailableFontFiles();
|
||||
QStringList fontNames = fontPathMap.keys();
|
||||
fontNames.sort(Qt::CaseInsensitive);
|
||||
ui->comboBox_font->addItems(fontNames);
|
||||
}
|
||||
|
||||
QString EditTextDialog::findFontNameFromPath(const QString& path) const
|
||||
{
|
||||
return fontPathMap.key(path, QString());
|
||||
}
|
||||
|
||||
void EditTextDialog::on_buttonBox_accepted()
|
||||
{
|
||||
const Sketcher::SketchObject* sketch = sketchView->getSketchObject();
|
||||
|
||||
// Get new values from the dialog
|
||||
std::string newText = ui->lineEdit_text->text().toStdString();
|
||||
QString selectedFontName = ui->comboBox_font->currentText();
|
||||
std::string newFontPath = fontPathMap.value(selectedFontName).toStdString();
|
||||
bool newIsHeight = ui->radioButton_height->isChecked();
|
||||
|
||||
const Sketcher::Constraint* constraint = sketch->Constraints[constrIndex];
|
||||
|
||||
// Check if anything changed
|
||||
if (newText == constraint->getText() && newFontPath == constraint->getFont()
|
||||
&& newIsHeight == constraint->getIsTextHeight()) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
// Open a command to make the change undo-able
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Modify sketch text constraint"));
|
||||
|
||||
try {
|
||||
// Find if it was construction geometry to preserve that state
|
||||
int firstTextGeoId = constraint->getGeoId(1);
|
||||
bool isConstruction = false;
|
||||
if (firstTextGeoId != Sketcher::GeoEnum::GeoUndef) {
|
||||
isConstruction = Sketcher::GeometryFacade::getConstruction(
|
||||
sketch->getGeometry(firstTextGeoId)
|
||||
);
|
||||
}
|
||||
|
||||
// Send the updated 5-parameter call to Python
|
||||
std::string escText = escapeForPython(newText);
|
||||
std::string escFont = escapeForPython(newFontPath);
|
||||
Gui::cmdAppObjectArgs(
|
||||
sketch,
|
||||
"setTextAndFont(%i, '%s', '%s', %s, %s)",
|
||||
constrIndex,
|
||||
escText.c_str(),
|
||||
escFont.c_str(),
|
||||
newIsHeight ? "True" : "False",
|
||||
isConstruction ? "True" : "False"
|
||||
);
|
||||
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Gui::Command::abortCommand();
|
||||
Base::Console().error("Failed to modify text constraint: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// SPDX - License - Identifier: LGPL - 2.1 - or -later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Pierre-Louis Boyer *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SKETCHERGUI_EDITTEXTDIALOG_H
|
||||
#define SKETCHERGUI_EDITTEXTDIALOG_H
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <QDialog>
|
||||
# include <QMap>
|
||||
# include <QString>
|
||||
#endif
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class EditTextDialog;
|
||||
}
|
||||
|
||||
namespace SketcherGui
|
||||
{
|
||||
|
||||
class ViewProviderSketch;
|
||||
|
||||
class EditTextDialog: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EditTextDialog(
|
||||
ViewProviderSketch* viewProvider,
|
||||
int constraintIndex,
|
||||
QWidget* parent = nullptr
|
||||
);
|
||||
~EditTextDialog() override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void on_buttonBox_accepted();
|
||||
|
||||
private:
|
||||
Ui::EditTextDialog* ui;
|
||||
ViewProviderSketch* sketchView;
|
||||
int constrIndex;
|
||||
QMap<QString, QString> fontPathMap;
|
||||
|
||||
void populateFontList();
|
||||
QString findFontNameFromPath(const QString& path) const;
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
#endif // SKETCHERGUI_EDITTEXTDIALOG_H
|
||||
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EditTextDialog</class>
|
||||
<widget class="QDialog" name="EditTextDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Edit Text</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_text">
|
||||
<property name="text">
|
||||
<string>Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_text"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_font">
|
||||
<property name="text">
|
||||
<string>Font:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBox_font"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="layoutType">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_height">
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButton_width">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>EditTextDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>EditTextDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -19,6 +19,8 @@
|
||||
<file>icons/constraints/Constraint_Ellipse_Radii.svg</file>
|
||||
<file>icons/constraints/Constraint_EqualLength.svg</file>
|
||||
<file>icons/constraints/Constraint_ExternalAngle.svg</file>
|
||||
<file>icons/constraints/Constraint_Group.svg</file>
|
||||
<file>icons/constraints/Constraint_Text.svg</file>
|
||||
<file>icons/constraints/Constraint_Horizontal.svg</file>
|
||||
<file>icons/constraints/Constraint_HorizontalDistance.svg</file>
|
||||
<file>icons/constraints/Constraint_HorizontalDistance_Driven.svg</file>
|
||||
@@ -91,6 +93,8 @@
|
||||
<file>icons/elements/Sketcher_Element_Line_Edge.svg</file>
|
||||
<file>icons/elements/Sketcher_Element_Line_EndPoint.svg</file>
|
||||
<file>icons/elements/Sketcher_Element_Line_StartingPoint.svg</file>
|
||||
<file>icons/elements/Sketcher_Element_Text_EndPoint.svg</file>
|
||||
<file>icons/elements/Sketcher_Element_Text_StartPoint.svg</file>
|
||||
<file>icons/elements/Sketcher_Element_Parabolic_Arc_Centre_Point.svg</file>
|
||||
<file>icons/elements/Sketcher_Element_Parabolic_Arc_Edge.svg</file>
|
||||
<file>icons/elements/Sketcher_Element_Parabolic_Arc_End_Point.svg</file>
|
||||
@@ -188,6 +192,7 @@
|
||||
<file>icons/geometry/Sketcher_CreateSquare.svg</file>
|
||||
<file>icons/geometry/Sketcher_CreateSquare_Constr.svg</file>
|
||||
<file>icons/geometry/Sketcher_CreateText.svg</file>
|
||||
<file>icons/geometry/Sketcher_CreateText_Constr.svg</file>
|
||||
<file>icons/geometry/Sketcher_CreateTriangle.svg</file>
|
||||
<file>icons/geometry/Sketcher_CreateTriangle_Constr.svg</file>
|
||||
<file>icons/geometry/Sketcher_Extend.svg</file>
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2816"
|
||||
version="1.1"
|
||||
sodipodi:docname="Constraint_Group.svg"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="2.6425781"
|
||||
inkscape:cx="79.467851"
|
||||
inkscape:cy="69.818183"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="1571"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2816" />
|
||||
<defs
|
||||
id="defs2818">
|
||||
<linearGradient
|
||||
id="linearGradient3602">
|
||||
<stop
|
||||
style="stop-color:#ff2600;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3604" />
|
||||
<stop
|
||||
style="stop-color:#ff5f00;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3606" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602"
|
||||
id="linearGradient3608"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.818181"
|
||||
y2="14.363636"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-7"
|
||||
id="linearGradient3608-5"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3602-7">
|
||||
<stop
|
||||
style="stop-color:#c51900;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3604-1" />
|
||||
<stop
|
||||
style="stop-color:#ff5f00;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3606-3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-5"
|
||||
id="linearGradient3608-1"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3602-5">
|
||||
<stop
|
||||
style="stop-color:#c51900;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3604-9" />
|
||||
<stop
|
||||
style="stop-color:#ff5f00;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3606-9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="14.363636"
|
||||
x2="24.81818"
|
||||
y1="14.363636"
|
||||
x1="3.909091"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3686"
|
||||
xlink:href="#linearGradient3602-5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-58"
|
||||
id="linearGradient3608-8"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3602-58">
|
||||
<stop
|
||||
style="stop-color:#c51900;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3604-2" />
|
||||
<stop
|
||||
style="stop-color:#ff5f00;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3606-2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="14.363636"
|
||||
x2="24.81818"
|
||||
y1="14.363636"
|
||||
x1="3.909091"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3726"
|
||||
xlink:href="#linearGradient3602-58" />
|
||||
<linearGradient
|
||||
id="linearGradient3787">
|
||||
<stop
|
||||
style="stop-color:#0619c0;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3789" />
|
||||
<stop
|
||||
style="stop-color:#379cfb;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3791" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="100.10708"
|
||||
x2="609.54919"
|
||||
y1="126.79625"
|
||||
x1="581.26331"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3524"
|
||||
xlink:href="#linearGradient3602-58"
|
||||
gradientTransform="matrix(1.3310616,0,0,1.2539521,-770.75488,-101.53511)" />
|
||||
<linearGradient
|
||||
id="linearGradient3144-6">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
id="stop3146-9" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
id="stop3148-2" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="34.345188"
|
||||
fy="672.79736"
|
||||
fx="225.26402"
|
||||
cy="672.79736"
|
||||
cx="225.26402"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3688"
|
||||
xlink:href="#linearGradient3144-6" />
|
||||
<linearGradient
|
||||
id="linearGradient3377">
|
||||
<stop
|
||||
style="stop-color:#ffaa00;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3379" />
|
||||
<stop
|
||||
style="stop-color:#faff2b;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3381" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5048">
|
||||
<stop
|
||||
id="stop5050"
|
||||
offset="0"
|
||||
style="stop-color:black;stop-opacity:0" />
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:1"
|
||||
offset="0.5"
|
||||
id="stop5056" />
|
||||
<stop
|
||||
id="stop5052"
|
||||
offset="1"
|
||||
style="stop-color:black;stop-opacity:0" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="aigrd2"
|
||||
cx="20.892099"
|
||||
cy="114.5684"
|
||||
r="5.256"
|
||||
fx="20.892099"
|
||||
fy="114.5684"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#F0F0F0"
|
||||
id="stop15566" />
|
||||
<stop
|
||||
offset="1.0000000"
|
||||
style="stop-color:#9a9a9a;stop-opacity:1"
|
||||
id="stop15568" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="aigrd3"
|
||||
cx="20.892099"
|
||||
cy="64.567902"
|
||||
r="5.257"
|
||||
fx="20.892099"
|
||||
fy="64.567902"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#F0F0F0"
|
||||
id="stop15573" />
|
||||
<stop
|
||||
offset="1.0000000"
|
||||
style="stop-color:#9a9a9a;stop-opacity:1"
|
||||
id="stop15575" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
id="linearGradient15662">
|
||||
<stop
|
||||
id="stop15664"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#ffffff;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop15666"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#f8f8f8;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient259"
|
||||
id="radialGradient4452"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.96049297,0,0,1.041132,-52.144249,-702.33158)"
|
||||
cx="33.966679"
|
||||
cy="35.736916"
|
||||
fx="33.966679"
|
||||
fy="35.736916"
|
||||
r="86.70845" />
|
||||
<linearGradient
|
||||
id="linearGradient259">
|
||||
<stop
|
||||
id="stop260"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#fafafa;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop261"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#bbbbbb;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient269"
|
||||
id="radialGradient4454"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.96827297,0,0,1.032767,-48.790699,-701.68513)"
|
||||
cx="8.824419"
|
||||
cy="3.7561285"
|
||||
fx="8.824419"
|
||||
fy="3.7561285"
|
||||
r="37.751713" />
|
||||
<linearGradient
|
||||
id="linearGradient269">
|
||||
<stop
|
||||
id="stop270"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#a3a3a3;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop271"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#4c4c4c;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="1190.875"
|
||||
x2="1267.9062"
|
||||
y1="1190.875"
|
||||
x1="901.1875"
|
||||
gradientTransform="matrix(0.10456791,0,0,0.10456791,420.90006,-32.97638)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4937"
|
||||
xlink:href="#linearGradient4095" />
|
||||
<linearGradient
|
||||
id="linearGradient4095">
|
||||
<stop
|
||||
id="stop4097"
|
||||
offset="0"
|
||||
style="stop-color:#005bff;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4099"
|
||||
offset="1"
|
||||
style="stop-color:#c1e3f7;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="14.363636"
|
||||
x2="24.818181"
|
||||
y1="14.363636"
|
||||
x1="3.909091"
|
||||
id="linearGradient4396"
|
||||
xlink:href="#linearGradient3602" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-7"
|
||||
id="linearGradient4400"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-7"
|
||||
id="linearGradient4403"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3787"
|
||||
id="linearGradient4429"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.3180277,0,0,1.2416733,-743.43621,-121.30586)"
|
||||
x1="581.26331"
|
||||
y1="126.79625"
|
||||
x2="609.54919"
|
||||
y2="100.10708" />
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="14.363636"
|
||||
x2="24.818181"
|
||||
y1="14.363636"
|
||||
x1="3.909091"
|
||||
id="linearGradient4527"
|
||||
xlink:href="#linearGradient3602" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-7"
|
||||
id="linearGradient4531"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-7"
|
||||
id="linearGradient4534"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636" />
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="14.363636"
|
||||
x2="24.818181"
|
||||
y1="14.363636"
|
||||
x1="3.909091"
|
||||
id="linearGradient3483"
|
||||
xlink:href="#linearGradient3602" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-7"
|
||||
id="linearGradient3487"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3602-7"
|
||||
id="linearGradient3490"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.909091"
|
||||
y1="14.363636"
|
||||
x2="24.81818"
|
||||
y2="14.363636" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata2821">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[Abdullah Tahiri]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:date>2015-05-26</dc:date>
|
||||
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_ToggleConstraint.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="g6"
|
||||
transform="translate(-4.2813147,13.579795)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#cc0000;fill-opacity:1;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 10.691188,-10.079592 A 2.7002703,2.7002703 0 0 0 7.990017,-7.3784198 V 4.1625958 a 2.7000003,2.7000003 0 0 0 2.701171,2.6992188 2.7000003,2.7000003 0 0 0 2.699219,-2.6992188 v -8.8417969 h 8.179688 a 2.7000003,2.7000003 0 0 0 2.699218,-2.6992187 2.7000003,2.7000003 0 0 0 -2.699218,-2.7011722 z"
|
||||
id="path5" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 9.8336448,4.6826881 V -8.2281518 H 22.108977"
|
||||
id="path6" />
|
||||
</g>
|
||||
<g
|
||||
id="g6-8"
|
||||
transform="rotate(90,27.48765,22.71893)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#cc0000;fill-opacity:1;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 10.691188,-10.079592 A 2.7002703,2.7002703 0 0 0 7.990017,-7.3784198 V 4.1625958 a 2.7000003,2.7000003 0 0 0 2.701171,2.6992188 2.7000003,2.7000003 0 0 0 2.699219,-2.6992188 v -8.8417969 h 8.179688 a 2.7000003,2.7000003 0 0 0 2.699218,-2.6992187 2.7000003,2.7000003 0 0 0 -2.699218,-2.7011722 z"
|
||||
id="path5-8" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 9.8336448,4.6826881 V -8.2281518 H 22.108977"
|
||||
id="path6-2" />
|
||||
</g>
|
||||
<g
|
||||
id="g6-8-5"
|
||||
transform="rotate(180,34.21529,25.143605)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#cc0000;fill-opacity:1;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 10.691188,-10.079592 A 2.7002703,2.7002703 0 0 0 7.990017,-7.3784198 V 4.1625958 a 2.7000003,2.7000003 0 0 0 2.701171,2.6992188 2.7000003,2.7000003 0 0 0 2.699219,-2.6992188 v -8.8417969 h 8.179688 a 2.7000003,2.7000003 0 0 0 2.699218,-2.6992187 2.7000003,2.7000003 0 0 0 -2.699218,-2.7011722 z"
|
||||
id="path5-8-1" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 9.8336448,4.6826881 V -8.2281518 H 22.108977"
|
||||
id="path6-2-7" />
|
||||
</g>
|
||||
<g
|
||||
id="g6-8-52"
|
||||
transform="rotate(-90,40.965184,27.49795)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#cc0000;fill-opacity:1;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 10.691188,-10.079592 A 2.7002703,2.7002703 0 0 0 7.990017,-7.3784198 V 4.1625958 a 2.7000003,2.7000003 0 0 0 2.701171,2.6992188 2.7000003,2.7000003 0 0 0 2.699219,-2.6992188 v -8.8417969 h 8.179688 a 2.7000003,2.7000003 0 0 0 2.699218,-2.6992187 2.7000003,2.7000003 0 0 0 -2.699218,-2.7011722 z"
|
||||
id="path5-8-7" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 9.8336448,4.6826881 V -8.2281518 H 22.108977"
|
||||
id="path6-2-6" />
|
||||
</g>
|
||||
<g
|
||||
id="g1942"
|
||||
transform="matrix(0.56082348,0.00382887,-0.00382887,0.56082348,-30.824178,24.889141)">
|
||||
<g
|
||||
id="g1639">
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 85.705652,39.254404 36.510728,0.03213 -0.0643,-36.0755714"
|
||||
id="path3042" />
|
||||
<g
|
||||
id="g5444"
|
||||
transform="matrix(0.12582856,0,0,0.13656888,-116.075,-78.506633)">
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#2e3436;stroke-width:61.1235;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3579"
|
||||
width="320.34793"
|
||||
height="290.3475"
|
||||
x="1581.9229"
|
||||
y="577.53864"
|
||||
ry="72.587051" />
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:15.2809;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3579-9-4"
|
||||
width="334.35117"
|
||||
height="306.85471"
|
||||
x="1574.9211"
|
||||
y="569.28497"
|
||||
ry="76.713875" />
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#d3d7cf;stroke-width:15.2809;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3579-9"
|
||||
width="308.51923"
|
||||
height="283.05432"
|
||||
x="1587.837"
|
||||
y="581.18518"
|
||||
ry="70.763763" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g1893"
|
||||
transform="matrix(0.92702216,0,0,0.92702216,60.747574,-46.780667)">
|
||||
<path
|
||||
style="fill:none;stroke:#2e3436;stroke-width:7.99999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.9;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3064"
|
||||
d="m 87.99875,70.15496 -24,12.819254 L 40.058512,69.793468 40.118273,43.793467 64.118272,30.974213 88.058511,44.15496 Z" />
|
||||
<path
|
||||
style="fill:none;stroke:#d3d7cf;stroke-width:4.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.9;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3064-3"
|
||||
d="m 87.99875,70.15496 -24,12.819254 L 40.058512,69.793468 40.118273,43.793467 64.118272,30.974213 88.058511,44.15496 Z" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.9;stroke-opacity:1"
|
||||
d="m 39.058511,70.286516 0.06225,-27 25,-13.312301 24.93775,13.687698"
|
||||
id="path3064-3-6" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.9;stroke-opacity:1"
|
||||
d="m 87.058511,44.648008 -0.05727,25 -23,12.326206 -22.94273,-12.673794"
|
||||
id="path3064-3-6-7" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,330 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
id="svg2869"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
sodipodi:docname="Constraint_Text.svg"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#50505000"
|
||||
bordercolor="#eeeeeeff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050ff"
|
||||
inkscape:zoom="5.2851563"
|
||||
inkscape:cx="12.487805"
|
||||
inkscape:cy="43.612712"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="1571"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2869" />
|
||||
<defs
|
||||
id="defs2871">
|
||||
<linearGradient
|
||||
id="linearGradient5">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop19" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop20" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch18">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop18" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch15">
|
||||
<stop
|
||||
style="stop-color:#3d0000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5-1">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3836-9">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-3">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-5" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-7">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-0" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082-3"
|
||||
xlink:href="#linearGradient3836-9-7" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient9"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.76342439,0,0,0.75750425,-4.596389,2.7525637)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.84956703,0,0,0.84301394,-2.927337,1.7790378)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3838"
|
||||
id="linearGradient3844"
|
||||
x1="36"
|
||||
y1="1039.3622"
|
||||
x2="32"
|
||||
y2="1003.3622"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(2.0563921e-6,-988.36218)" />
|
||||
<linearGradient
|
||||
id="linearGradient3838">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3840" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3842" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3830"
|
||||
id="linearGradient3836"
|
||||
x1="36"
|
||||
y1="1037.3622"
|
||||
x2="32"
|
||||
y2="1005.3622"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(2.0563921e-6,-988.36218)" />
|
||||
<linearGradient
|
||||
id="linearGradient3830">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3832" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3834" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(12.126952,12.126971)"
|
||||
y2="1734.2576"
|
||||
x2="4157.8677"
|
||||
y1="1473.5258"
|
||||
x1="4115.4229"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3009"
|
||||
xlink:href="#linearGradient3010" />
|
||||
<linearGradient
|
||||
id="linearGradient3010">
|
||||
<stop
|
||||
id="stop3012"
|
||||
offset="0"
|
||||
style="stop-color:#ef2929;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3014"
|
||||
offset="1"
|
||||
style="stop-color:#a40000;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.1649204,0,0,0.1649204,-632.71718,-223.01448)"
|
||||
y2="1734.2576"
|
||||
x2="4157.8677"
|
||||
y1="1473.5258"
|
||||
x1="4115.4229"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3009-2"
|
||||
xlink:href="#linearGradient3010-2" />
|
||||
<linearGradient
|
||||
id="linearGradient3010-2">
|
||||
<stop
|
||||
id="stop3012-1"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3014-6"
|
||||
offset="1"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata2874">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[maxwxyz]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreateArc.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:date>2023-12-19</dc:date>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="g2"
|
||||
transform="translate(-2.1206965,1.6845105)">
|
||||
<g
|
||||
id="g6"
|
||||
transform="translate(-2.0747215,12.048077)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#cc0000;fill-opacity:1;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 10.691188,-10.079592 A 2.7002703,2.7002703 0 0 0 7.990017,-7.3784198 V 4.1625958 a 2.7000003,2.7000003 0 0 0 2.701171,2.6992188 2.7000003,2.7000003 0 0 0 2.699219,-2.6992188 v -8.8417969 h 8.179688 a 2.7000003,2.7000003 0 0 0 2.699218,-2.6992187 2.7000003,2.7000003 0 0 0 -2.699218,-2.7011722 z"
|
||||
id="path5" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 9.8336448,4.6826881 V -8.2281518 H 22.108977"
|
||||
id="path6-23" />
|
||||
</g>
|
||||
<g
|
||||
id="g6-8"
|
||||
transform="rotate(90,29.356805,23.056368)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#cc0000;fill-opacity:1;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 10.691188,-10.079592 A 2.7002703,2.7002703 0 0 0 7.990017,-7.3784198 V 4.1625958 a 2.7000003,2.7000003 0 0 0 2.701171,2.6992188 2.7000003,2.7000003 0 0 0 2.699219,-2.6992188 v -8.8417969 h 8.179688 a 2.7000003,2.7000003 0 0 0 2.699218,-2.6992187 2.7000003,2.7000003 0 0 0 -2.699218,-2.7011722 z"
|
||||
id="path5-8" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 9.8336448,4.6826881 V -8.2281518 H 22.108977"
|
||||
id="path6-2" />
|
||||
</g>
|
||||
<g
|
||||
id="g6-8-5"
|
||||
transform="rotate(180,35.318587,24.377746)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#cc0000;fill-opacity:1;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 10.691188,-10.079592 A 2.7002703,2.7002703 0 0 0 7.990017,-7.3784198 V 4.1625958 a 2.7000003,2.7000003 0 0 0 2.701171,2.6992188 2.7000003,2.7000003 0 0 0 2.699219,-2.6992188 v -8.8417969 h 8.179688 a 2.7000003,2.7000003 0 0 0 2.699218,-2.6992187 2.7000003,2.7000003 0 0 0 -2.699218,-2.7011722 z"
|
||||
id="path5-8-1" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 9.8336448,4.6826881 V -8.2281518 H 22.108977"
|
||||
id="path6-2-7" />
|
||||
</g>
|
||||
<g
|
||||
id="g6-8-52"
|
||||
transform="rotate(-90,41.302622,25.628794)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#cc0000;fill-opacity:1;stroke:#280000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 10.691188,-10.079592 A 2.7002703,2.7002703 0 0 0 7.990017,-7.3784198 V 4.1625958 a 2.7000003,2.7000003 0 0 0 2.701171,2.6992188 2.7000003,2.7000003 0 0 0 2.699219,-2.6992188 v -8.8417969 h 8.179688 a 2.7000003,2.7000003 0 0 0 2.699218,-2.6992187 2.7000003,2.7000003 0 0 0 -2.699218,-2.7011722 z"
|
||||
id="path5-8-7" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#ef2929;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 9.8336448,4.6826881 V -8.2281518 H 22.108977"
|
||||
id="path6-2-6" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.67985712,0,0,0.67985712,3.8942753,5.1844051)">
|
||||
<path
|
||||
id="text2714"
|
||||
d="M 52.000007,50.000005 H 34.000001 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 h -16 v -2.000006 c 0.901917,-0.171781 2.463152,-0.570509 3.000001,-0.999995 0.536849,-0.429485 0.999995,-0.999994 2.000006,-2.000006 L 41.999993,24.000007 c 0.730086,-1.073664 0.871126,-1.355744 1.000011,-2.000006 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08587,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:url(#linearGradient3009-2);fill-opacity:1;stroke:#2e0000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-1"
|
||||
d="M 53.27692,47.999999 H 33.148154 l -5.964082,7.216455 c -1.480672,1.914033 -1.084253,2.914242 -1.253082,3.124467 0,0.931816 1.519016,1.409822 2.746634,1.749986 l 0.204551,-0.02274 -12.882164,-0.06818 -0.613636,0.477279 c 0.818401,-0.160071 3.848715,-0.895254 4.335857,-1.295449 0.487142,-0.400213 1.225573,-1.386371 2.019368,-2.545481 L 41.066494,28.909094 c 0.662485,-1.000473 2.608645,-3.695124 3.134675,-4.704552 0.55847,-0.920405 1.508461,-3.713398 0.456236,-4.113626 l -1.681842,-0.09092 H 57.01682 l -2.159105,-0.113615 c -1.814784,0 -1.770899,0.86415 -1.904039,1.52271 -0.116912,0.580339 0.106819,1.286907 0.340709,2.027235 l 9.167925,31.189218 c 0.389674,1.340655 0.401268,1.601493 0.631975,2.465379 0.594192,1.639325 0.217134,2.044469 0.996614,2.20454 l 2.909097,0.70454 H 53.000002 l 2.068168,-1.022721 c 0.821749,-1.4701 0.851418,-1.070415 1.046321,-2.09091 0.07791,-0.420217 -0.42533,-2.088667 -0.659121,-2.90908 l -2.132998,-5.84092 m 0.735776,-3.036333 -5.501761,-20.213667 -14.812359,20.190941 20.125154,0.004"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#ffffff;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-7"
|
||||
d="M 52.000007,50.000005 H 34.000001 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 h -16 v -2.000006 c 0.901917,-0.171781 2.463152,-0.570509 3.000001,-0.999995 0.536849,-0.429485 0.999995,-0.999994 2.000006,-2.000006 L 41.999993,24.000007 c 0.730086,-1.073664 0.871126,-1.355744 1.000011,-2.000006 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08586,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#280000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,587 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
id="svg2869"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
sodipodi:docname="Sketcher_Element_Text_EndPoint.svg"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#50505000"
|
||||
bordercolor="#eeeeeeff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050ff"
|
||||
inkscape:zoom="3.7371698"
|
||||
inkscape:cx="88.302115"
|
||||
inkscape:cy="-27.962336"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="1571"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2869" />
|
||||
<defs
|
||||
id="defs2871">
|
||||
<linearGradient
|
||||
id="linearGradient34">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop33" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop34" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient32">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop31" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop32" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient17">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop16" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop17" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop19" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop20" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch18">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop18" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch15">
|
||||
<stop
|
||||
style="stop-color:#3d0000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5-1">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3836-9">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-3">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-5" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-7">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-0" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082-3"
|
||||
xlink:href="#linearGradient3836-9-7" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.76342439,0,0,0.75750425,-4.596389,2.7525637)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.84956703,0,0,0.84301394,-2.927337,1.7790378)" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3809"
|
||||
id="radialGradient3815"
|
||||
cx="225.93762"
|
||||
cy="91.956673"
|
||||
fx="225.93762"
|
||||
fy="91.956673"
|
||||
r="22"
|
||||
gradientTransform="matrix(-1.4090915,3.8636359,-0.97565325,-0.35582669,437.08461,-816.22007)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3809">
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3811" />
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3813" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3444"
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
gradientTransform="matrix(0.90206746,0,0,0.90216902,-1.9060863,1.1084289)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3857"
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
gradientTransform="matrix(0.89262616,0,0,0.89258466,72.894067,1.2176306)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientTransform="matrix(0.93724177,0,0,0.93725692,-1.2227671,0.70650014)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3148"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient17"
|
||||
id="linearGradient3898"
|
||||
x1="37.429146"
|
||||
y1="41.590584"
|
||||
x2="24.483221"
|
||||
y2="4.9104676"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3892">
|
||||
<stop
|
||||
style="stop-color:#bdd2e9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3894" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3896" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3892"
|
||||
id="linearGradient3856"
|
||||
x1="22.84341"
|
||||
y1="4.8241611"
|
||||
x2="30.783579"
|
||||
y2="28.644661"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
r="22"
|
||||
fy="91.956673"
|
||||
fx="225.93762"
|
||||
cy="91.956673"
|
||||
cx="225.93762"
|
||||
gradientTransform="matrix(-1.7064667,4.6731721,-1.1815555,-0.43038201,776.9032,-933.08315)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3163"
|
||||
xlink:href="#linearGradient3809" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4104"
|
||||
id="linearGradient4110"
|
||||
x1="-23.070524"
|
||||
y1="18.383886"
|
||||
x2="-24.194258"
|
||||
y2="6.534451"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4104">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4106" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4108" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4096"
|
||||
id="linearGradient4102"
|
||||
x1="-24.035076"
|
||||
y1="16.85112"
|
||||
x2="-23.821426"
|
||||
y2="7.2881389"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4096">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4098" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4100" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient4216"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.71430595,0,0,0.71426219,259.5032,71.709694)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<radialGradient
|
||||
r="22"
|
||||
fy="91.956673"
|
||||
fx="225.93762"
|
||||
cy="91.956673"
|
||||
cx="225.93762"
|
||||
gradientTransform="matrix(-1.7064667,4.6731721,-1.1815555,-0.43038201,776.9032,-933.08315)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3163-9"
|
||||
xlink:href="#linearGradient3809" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4104"
|
||||
id="linearGradient4347"
|
||||
x1="-21.31983"
|
||||
y1="18.008659"
|
||||
x2="-24.907471"
|
||||
y2="6.9377007"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4096"
|
||||
id="linearGradient4339"
|
||||
x1="-21.141161"
|
||||
y1="17.489555"
|
||||
x2="-24.733845"
|
||||
y2="7.0083036"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientTransform="matrix(0.65313239,0,0,0.65304994,258.31758,72.40809)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4235-2"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-7"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient32"
|
||||
id="linearGradient3901"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="30.202745"
|
||||
y1="56.729507"
|
||||
x2="35.013981"
|
||||
y2="19.843365"
|
||||
gradientTransform="matrix(0.62354045,-0.62354046,0.62354039,0.62354042,-12.205772,28.459494)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient34"
|
||||
id="linearGradient3903"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="29.993565"
|
||||
y1="54.846851"
|
||||
x2="36.094769"
|
||||
y2="20.854424"
|
||||
gradientTransform="matrix(0.57365718,-0.57365723,0.57365712,0.57365719,-8.6693108,28.742734)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3922"
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
gradientTransform="matrix(0.85221122,0,0,0.85228409,-2.8500258,1.6945624)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3096"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-7"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="translate(-90,-5.9999999)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082-3-8"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-7-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-7-3-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3203"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient47"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient49"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient2"
|
||||
id="linearGradient6"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
id="linearGradient2">
|
||||
<stop
|
||||
style="stop-color:#73d216;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop1" />
|
||||
<stop
|
||||
style="stop-color:#8ae234;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.1649204,0,0,0.1649204,-641.71718,-230.01448)"
|
||||
y2="1734.2576"
|
||||
x2="4157.8677"
|
||||
y1="1473.5258"
|
||||
x1="4115.4229"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3009"
|
||||
xlink:href="#linearGradient3010" />
|
||||
<linearGradient
|
||||
id="linearGradient3010">
|
||||
<stop
|
||||
id="stop3012"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3014"
|
||||
offset="1"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata2874">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[maxwxyz]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreateArc.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:date>2023-12-19</dc:date>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="g1"
|
||||
transform="translate(-5.05e-6,-3.000006)">
|
||||
<path
|
||||
id="text2714"
|
||||
d="M 43.000006,43.000004 H 25 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 H 5.0000041 V 53.000002 C 5.9019207,52.828221 7.4631562,52.429493 8.0000051,52.000007 8.536854,51.570522 8.9999999,51.000013 10.000011,50.000001 L 32.999992,17.000006 C 33.730078,15.926342 33.871118,15.644262 34.000003,15 c 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08587,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:url(#linearGradient3009);fill-opacity:1;stroke:#2e0000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-1"
|
||||
d="M 44.276919,40.999998 H 24.148153 l -5.964082,7.216455 c -1.480672,1.914033 -1.084253,2.914242 -1.253082,3.124467 0,0.931816 1.519016,1.409822 2.746634,1.749986 l 0.204551,-0.02274 -12.8821638,-0.06818 -0.6136358,0.477279 c 0.818401,-0.160071 3.8487146,-0.895254 4.3358566,-1.295449 0.487142,-0.400213 1.225573,-1.386371 2.019368,-2.545481 L 32.066493,21.909093 c 0.662485,-1.000473 2.608645,-3.695124 3.134675,-4.704552 0.55847,-0.920405 1.508461,-3.713398 0.456236,-4.113626 l -1.681842,-0.09092 H 48.016819 L 45.857714,12.88638 c -1.814784,0 -1.770899,0.86415 -1.904039,1.52271 -0.116912,0.580339 0.106819,1.286907 0.340709,2.027235 l 9.167925,31.189218 c 0.389674,1.340655 0.401268,1.601493 0.631975,2.465379 0.594192,1.639325 0.217134,2.044469 0.996614,2.20454 l 2.909097,0.70454 H 44.000001 l 2.068168,-1.022721 c 0.821749,-1.4701 0.851418,-1.070415 1.046321,-2.09091 0.07791,-0.420217 -0.42533,-2.088667 -0.659121,-2.90908 l -2.132998,-5.84092 m 0.735776,-3.036333 -5.501761,-20.213667 -14.812359,20.190941 20.125154,0.004"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#ffffff;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-7"
|
||||
d="M 43.000006,43.000004 H 25 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 H 5.0000041 V 53.000002 C 5.9019207,52.828221 7.4631562,52.429493 8.0000051,52.000007 8.536854,51.570522 8.9999999,51.000013 10.000011,50.000001 L 32.999992,17.000006 C 33.730078,15.926342 33.871118,15.644262 34.000003,15 c 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08586,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#280000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="layer1-5"
|
||||
transform="rotate(-135,42.659784,41.549307)">
|
||||
<path
|
||||
style="fill:#3465a4;stroke:#091425;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 24.393399,44.849229 20.150758,40.60659 54.091883,6.6654658 l 4.24264,4.2426392 z"
|
||||
id="path3061-2"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 22.487284,41.09849 54.583793,9.0019854"
|
||||
id="path3063-7"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(-0.55062023,-0.55062023,0.55062013,-0.55062013,-8.8460455,50.079206)"
|
||||
id="g47-6"
|
||||
style="stroke-width:1.2842">
|
||||
<path
|
||||
style="fill:none;stroke:#2e0000;stroke-width:2.56841;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path46-1"
|
||||
d="M -26.310778,5.3580033 A 8.3519646,8.3515832 0.02039876 1 1 -13.623399,16.222662 8.3519646,8.3515832 0.02039876 1 1 -26.310778,5.3580033 Z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient47);fill-opacity:1;stroke:#ef2929;stroke-width:2.56842;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path47-4"
|
||||
d="m -24.358888,7.0362241 a 5.782493,5.7773415 0 1 1 8.784093,7.5158349 5.782493,5.7773415 0 0 1 -8.784093,-7.5158349 z" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(-0.55062023,0.55062023,-0.55062013,-0.55062013,50.287573,71.929186)"
|
||||
id="g6"
|
||||
style="display:inline;stroke-width:1.2842">
|
||||
<path
|
||||
style="fill:none;stroke:#162c02;stroke-width:2.56841;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path5"
|
||||
d="M -26.310778,5.3580033 A 8.3519646,8.3515832 0.02039876 1 1 -13.623399,16.222662 8.3519646,8.3515832 0.02039876 1 1 -26.310778,5.3580033 Z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient6);fill-opacity:1;stroke:#8ae234;stroke-width:2.56842;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path6-7"
|
||||
d="m -24.358888,7.0362241 a 5.782493,5.7773415 0 1 1 8.784093,7.5158349 5.782493,5.7773415 0 0 1 -8.784093,-7.5158349 z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,597 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
id="svg2869"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
sodipodi:docname="Sketcher_Element_Text_StartPoint.svg"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#50505000"
|
||||
bordercolor="#eeeeeeff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050ff"
|
||||
inkscape:zoom="10.570313"
|
||||
inkscape:cx="35.476718"
|
||||
inkscape:cy="26.725795"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="1571"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2869" />
|
||||
<defs
|
||||
id="defs2871">
|
||||
<linearGradient
|
||||
id="linearGradient34">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop33" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop34" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient32">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop31" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop32" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient17">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop16" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop17" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop19" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop20" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch18">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop18" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch15">
|
||||
<stop
|
||||
style="stop-color:#3d0000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5-1">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3836-9">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-3">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-5" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-7">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-0" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082-3"
|
||||
xlink:href="#linearGradient3836-9-7" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.76342439,0,0,0.75750425,-4.596389,2.7525637)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.84956703,0,0,0.84301394,-2.927337,1.7790378)" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3809"
|
||||
id="radialGradient3815"
|
||||
cx="225.93762"
|
||||
cy="91.956673"
|
||||
fx="225.93762"
|
||||
fy="91.956673"
|
||||
r="22"
|
||||
gradientTransform="matrix(-1.4090915,3.8636359,-0.97565325,-0.35582669,437.08461,-816.22007)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3809">
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3811" />
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3813" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3444"
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
gradientTransform="matrix(0.90206746,0,0,0.90216902,-1.9060863,1.1084289)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3857"
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
gradientTransform="matrix(0.89262616,0,0,0.89258466,72.894067,1.2176306)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientTransform="matrix(0.93724177,0,0,0.93725692,-1.2227671,0.70650014)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3148"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient17"
|
||||
id="linearGradient3898"
|
||||
x1="37.429146"
|
||||
y1="41.590584"
|
||||
x2="24.483221"
|
||||
y2="4.9104676"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3892">
|
||||
<stop
|
||||
style="stop-color:#bdd2e9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3894" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3896" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3892"
|
||||
id="linearGradient3856"
|
||||
x1="22.84341"
|
||||
y1="4.8241611"
|
||||
x2="30.783579"
|
||||
y2="28.644661"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
r="22"
|
||||
fy="91.956673"
|
||||
fx="225.93762"
|
||||
cy="91.956673"
|
||||
cx="225.93762"
|
||||
gradientTransform="matrix(-1.7064667,4.6731721,-1.1815555,-0.43038201,776.9032,-933.08315)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3163"
|
||||
xlink:href="#linearGradient3809" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4104"
|
||||
id="linearGradient4110"
|
||||
x1="-23.070524"
|
||||
y1="18.383886"
|
||||
x2="-24.194258"
|
||||
y2="6.534451"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4104">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4106" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4108" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4096"
|
||||
id="linearGradient4102"
|
||||
x1="-24.035076"
|
||||
y1="16.85112"
|
||||
x2="-23.821426"
|
||||
y2="7.2881389"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4096">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4098" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4100" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient4216"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.71430595,0,0,0.71426219,259.5032,71.709694)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<radialGradient
|
||||
r="22"
|
||||
fy="91.956673"
|
||||
fx="225.93762"
|
||||
cy="91.956673"
|
||||
cx="225.93762"
|
||||
gradientTransform="matrix(-1.7064667,4.6731721,-1.1815555,-0.43038201,776.9032,-933.08315)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3163-9"
|
||||
xlink:href="#linearGradient3809" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4104"
|
||||
id="linearGradient4347"
|
||||
x1="-21.31983"
|
||||
y1="18.008659"
|
||||
x2="-24.907471"
|
||||
y2="6.9377007"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4096"
|
||||
id="linearGradient4339"
|
||||
x1="-21.141161"
|
||||
y1="17.489555"
|
||||
x2="-24.733845"
|
||||
y2="7.0083036"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientTransform="matrix(0.65313239,0,0,0.65304994,258.31758,72.40809)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4235-2"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-7"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient32"
|
||||
id="linearGradient3901"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="30.202745"
|
||||
y1="56.729507"
|
||||
x2="35.013981"
|
||||
y2="19.843365"
|
||||
gradientTransform="matrix(0.62354045,-0.62354046,0.62354039,0.62354042,-12.205772,28.459494)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient34"
|
||||
id="linearGradient3903"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="29.993565"
|
||||
y1="54.846851"
|
||||
x2="36.094769"
|
||||
y2="20.854424"
|
||||
gradientTransform="matrix(0.57365718,-0.57365723,0.57365712,0.57365719,-8.6693108,28.742734)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3922"
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
gradientTransform="matrix(0.85221122,0,0,0.85228409,-2.8500258,1.6945624)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3096"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-7"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="translate(-90,-5.9999999)" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082-3-8"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-7-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-7-3-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3203"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient47"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient49"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient2"
|
||||
id="linearGradient6"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
id="linearGradient2">
|
||||
<stop
|
||||
style="stop-color:#73d216;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop1" />
|
||||
<stop
|
||||
style="stop-color:#8ae234;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.1649204,0,0,0.1649204,-641.71718,-230.01448)"
|
||||
y2="1734.2576"
|
||||
x2="4157.8677"
|
||||
y1="1473.5258"
|
||||
x1="4115.4229"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3009"
|
||||
xlink:href="#linearGradient3010" />
|
||||
<linearGradient
|
||||
id="linearGradient3010">
|
||||
<stop
|
||||
id="stop3012"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3014"
|
||||
offset="1"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata2874">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[maxwxyz]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreateArc.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:date>2023-12-19</dc:date>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="g1"
|
||||
transform="translate(-5.05e-6,-3.000006)">
|
||||
<path
|
||||
id="text2714"
|
||||
d="M 43.000006,43.000004 H 25 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 H 5.0000041 V 53.000002 C 5.9019207,52.828221 7.4631562,52.429493 8.0000051,52.000007 8.536854,51.570522 8.9999999,51.000013 10.000011,50.000001 L 32.999992,17.000006 C 33.730078,15.926342 33.871118,15.644262 34.000003,15 c 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08587,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:url(#linearGradient3009);fill-opacity:1;stroke:#2e0000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-1"
|
||||
d="M 44.276919,40.999998 H 24.148153 l -5.964082,7.216455 c -1.480672,1.914033 -1.084253,2.914242 -1.253082,3.124467 0,0.931816 1.519016,1.409822 2.746634,1.749986 l 0.204551,-0.02274 -12.8821638,-0.06818 -0.6136358,0.477279 c 0.818401,-0.160071 3.8487146,-0.895254 4.3358566,-1.295449 0.487142,-0.400213 1.225573,-1.386371 2.019368,-2.545481 L 32.066493,21.909093 c 0.662485,-1.000473 2.608645,-3.695124 3.134675,-4.704552 0.55847,-0.920405 1.508461,-3.713398 0.456236,-4.113626 l -1.681842,-0.09092 H 48.016819 L 45.857714,12.88638 c -1.814784,0 -1.770899,0.86415 -1.904039,1.52271 -0.116912,0.580339 0.106819,1.286907 0.340709,2.027235 l 9.167925,31.189218 c 0.389674,1.340655 0.401268,1.601493 0.631975,2.465379 0.594192,1.639325 0.217134,2.044469 0.996614,2.20454 l 2.909097,0.70454 H 44.000001 l 2.068168,-1.022721 c 0.821749,-1.4701 0.851418,-1.070415 1.046321,-2.09091 0.07791,-0.420217 -0.42533,-2.088667 -0.659121,-2.90908 l -2.132998,-5.84092 m 0.735776,-3.036333 -5.501761,-20.213667 -14.812359,20.190941 20.125154,0.004"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#ffffff;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-7"
|
||||
d="M 43.000006,43.000004 H 25 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 H 5.0000041 V 53.000002 C 5.9019207,52.828221 7.4631562,52.429493 8.0000051,52.000007 8.536854,51.570522 8.9999999,51.000013 10.000011,50.000001 L 32.999992,17.000006 C 33.730078,15.926342 33.871118,15.644262 34.000003,15 c 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08586,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#280000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="layer1-5"
|
||||
transform="rotate(45,-1.3443654,28.861417)">
|
||||
<path
|
||||
style="fill:#3465a4;stroke:#091425;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 24.393399,44.849229 20.150758,40.60659 54.091883,6.6654658 l 4.24264,4.2426392 z"
|
||||
id="path3061-2"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 22.487284,41.09849 54.583793,9.0019854"
|
||||
id="path3063-7"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.55062023,0.55062023,-0.55062013,0.55062013,72.305403,60.418843)"
|
||||
id="g47-6"
|
||||
style="stroke-width:1.2842">
|
||||
<path
|
||||
style="fill:none;stroke:#2e0000;stroke-width:2.56841;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path46-1"
|
||||
d="M -26.310778,5.3580033 A 8.3519646,8.3515832 0.02039876 1 1 -13.623399,16.222662 8.3519646,8.3515832 0.02039876 1 1 -26.310778,5.3580033 Z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient1);fill-opacity:1;stroke:#ef2929;stroke-width:2.56842;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path47-4"
|
||||
d="m -24.358888,7.0362241 a 5.782493,5.7773415 0 1 1 8.784093,7.5158349 5.782493,5.7773415 0 0 1 -8.784093,-7.5158349 z" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.55062023,-0.55062023,0.55062013,0.55062013,13.171784,38.568863)"
|
||||
id="g6"
|
||||
style="display:inline;stroke-width:1.2842">
|
||||
<path
|
||||
style="fill:none;stroke:#162c02;stroke-width:2.56841;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path5"
|
||||
d="M -26.310778,5.3580033 A 8.3519646,8.3515832 0.02039876 1 1 -13.623399,16.222662 8.3519646,8.3515832 0.02039876 1 1 -26.310778,5.3580033 Z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient6);fill-opacity:1;stroke:#8ae234;stroke-width:2.56842;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path6-7"
|
||||
d="m -24.358888,7.0362241 a 5.782493,5.7773415 0 1 1 8.784093,7.5158349 5.782493,5.7773415 0 0 1 -8.784093,-7.5158349 z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 21 KiB |
@@ -7,12 +7,34 @@
|
||||
id="svg2869"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
sodipodi:docname="Sketcher_CreateText.svg"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="10.570313"
|
||||
inkscape:cx="2.9327421"
|
||||
inkscape:cy="33.537324"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="1571"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2869" />
|
||||
<defs
|
||||
id="defs2871">
|
||||
<linearGradient
|
||||
@@ -168,7 +190,7 @@
|
||||
id="stop3834" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(12.126952,12.126971)"
|
||||
gradientTransform="matrix(0.1649204,0,0,0.1649204,-641.71718,-230.01448)"
|
||||
y2="1734.2576"
|
||||
x2="4157.8677"
|
||||
y1="1473.5258"
|
||||
@@ -181,11 +203,11 @@
|
||||
<stop
|
||||
id="stop3012"
|
||||
offset="0"
|
||||
style="stop-color:#ef2929;stop-opacity:1" />
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3014"
|
||||
offset="1"
|
||||
style="stop-color:#a40000;stop-opacity:1" />
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<metadata
|
||||
@@ -217,77 +239,16 @@
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer4">
|
||||
<g
|
||||
id="g1"
|
||||
style="display:inline"
|
||||
transform="translate(-44,13)">
|
||||
<path
|
||||
style="fill:none;stroke:#151819;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
d="M 10,32 H 32"
|
||||
id="path1" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#d3d7cf;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers fill stroke"
|
||||
d="M 10,32 H 32"
|
||||
id="path1-7" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
d="M 10,31 H 32"
|
||||
id="path1-7-0" />
|
||||
</g>
|
||||
<g
|
||||
id="g8"
|
||||
style="display:inline"
|
||||
transform="translate(-44,27)">
|
||||
<path
|
||||
style="fill:none;stroke:#091425;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
d="M 10,32 H 32"
|
||||
id="path6" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#3465a4;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:markers fill stroke"
|
||||
d="M 10,32 H 32"
|
||||
id="path7" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
d="M 10,31 H 32"
|
||||
id="path8" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.77869459,0,0,0.77869445,3.548264,36.597628)"
|
||||
id="g3797-9-5-5"
|
||||
style="stroke-width:1.2842">
|
||||
<path
|
||||
style="fill:none;stroke:#2e0000;stroke-width:2.56841;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-71-6-6"
|
||||
d="M -26.310778,5.3580033 A 8.3519646,8.3515832 0.02039876 1 1 -13.623399,16.222662 8.3519646,8.3515832 0.02039876 1 1 -26.310778,5.3580033 Z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient9);fill-opacity:1;stroke:#ef2929;stroke-width:2.56842;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-7-3-2-2"
|
||||
d="m -24.358888,7.0362241 a 5.782493,5.7773415 0 1 1 8.784093,7.5158349 5.782493,5.7773415 0 0 1 -8.784093,-7.5158349 z" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="layer3"
|
||||
style="display:inline">
|
||||
<g
|
||||
id="layer1">
|
||||
<g
|
||||
id="g3491"
|
||||
transform="matrix(0.1649204,0,0,0.1649204,-643.71716,-232.01446)">
|
||||
<path
|
||||
id="text2714"
|
||||
d="m 4163.931,1667.5588 h -109.1436 l -33.4751,40.7475 c -3.5156,5.2084 -2.3852,4.766 -2.9061,7.7608 0,6.0635 3.9239,9.9135 12.1271,12.127 v 12.1271 h -97.0165 v -12.1271 c 5.4688,-1.0416 14.9354,-3.4593 18.1906,-6.0635 3.2552,-2.6042 6.0635,-6.0635 12.1271,-12.1271 l 139.4611,-200.0965 c 4.4269,-6.5102 5.2821,-8.2206 6.0636,-12.1271 1.3019,-5.9893 0.9676,-9.5226 -6.0636,-12.127 v -12.1271 h 97.0165 v 12.1271 c -12.127,0 -17.1492,0.595 -18.1906,6.0635 -0.7814,3.7763 -0.6531,6.3037 0.9097,11.1211 l 60.3514,205.4688 c 2.6039,8.724 2.8333,10.5692 5.4378,13.8243 2.6038,3.1251 6.9183,5.0219 12.1271,6.0635 v 12.1271 h -97.0165 v -12.1271 c 8.984,-1.4322 10.8247,-5.4864 12.127,-12.127 0.5207,-2.7344 1.5623,-6.7885 0,-12.1271 l -12.127,-36.3812 m 0,-36.3812 -24.2542,-97.0165 -72.7623,97.0165 h 97.0165"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:url(#linearGradient3009);fill-opacity:1;stroke:#2e0000;stroke-width:12.1271;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-1"
|
||||
d="m 4171.6736,1655.4317 h -122.0514 l -36.1634,43.7572 c -8.9781,11.6058 -6.5744,17.6706 -7.5981,18.9453 0,5.6501 9.2106,8.5485 16.6543,10.6111 l 1.2403,-0.1379 -78.1114,-0.4134 -3.7208,2.894 c 4.9624,-0.9706 23.3368,-5.4284 26.2906,-7.855 2.9538,-2.4267 7.4313,-8.4063 12.2445,-15.4346 l 117.1771,-168.125 c 4.017,-6.0664 15.8176,-22.4055 19.0072,-28.5262 3.3863,-5.5809 9.1466,-22.5163 2.7664,-24.9431 l -10.1979,-0.5513 h 85.1396 l -13.0918,-0.6889 c -11.004,0 -10.7379,5.2398 -11.5452,9.233 -0.7089,3.5189 0.6477,7.8032 2.0659,12.2922 l 55.59,189.1168 c 2.3628,8.1291 2.4331,9.7107 3.832,14.9489 3.6029,9.9401 1.3166,12.3967 6.043,13.3673 l 17.6394,4.272 h -84.8894 l 12.5404,-6.2013 c 4.9827,-8.914 5.1626,-6.4905 6.3444,-12.6783 0.4724,-2.548 -2.579,-12.6647 -3.9966,-17.6393 l -12.9335,-35.4166 m 4.4614,-18.4109 -33.3601,-122.5662 -89.8152,122.4284 122.0295,0.024"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#ef2929;stroke-width:12.1271;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-7"
|
||||
d="m 4163.931,1667.5588 h -109.1436 l -33.4751,40.7475 c -3.5156,5.2084 -2.3852,4.766 -2.9061,7.7608 0,6.0635 3.9239,9.9135 12.1271,12.127 v 12.1271 h -97.0165 v -12.1271 c 5.4688,-1.0416 14.9354,-3.4593 18.1906,-6.0635 3.2552,-2.6042 6.0635,-6.0635 12.1271,-12.1271 l 139.4611,-200.0965 c 4.4269,-6.5102 5.2821,-8.2206 6.0636,-12.1271 1.3019,-5.9893 0.9676,-9.5226 -6.0636,-12.127 v -12.1271 h 97.0165 v 12.1271 c -12.127,0 -17.1492,0.595 -18.1906,6.0635 -0.7814,3.7763 -0.6531,6.3037 0.9097,11.1211 l 60.3514,205.4688 c 2.6039,8.724 2.8333,10.5692 5.4378,13.8243 2.6038,3.1251 6.9183,5.0219 12.1271,6.0635 v 12.1271 h -97.0165 v -12.1271 c 8.984,-1.4322 10.8247,-5.4864 12.127,-12.127 0.5206,-2.7344 1.5623,-6.7885 0,-12.1271 l -12.127,-36.3812 m 0,-36.3812 -24.2542,-97.0165 -72.7623,97.0165 h 97.0165"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#280000;stroke-width:12.1271;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
id="text2714"
|
||||
d="M 43.000006,43.000004 H 25 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 H 5.0000041 V 53.000002 C 5.9019207,52.828221 7.4631562,52.429493 8.0000051,52.000007 8.536854,51.570522 8.9999999,51.000013 10.000011,50.000001 L 32.999992,17.000006 C 33.730078,15.926342 33.871118,15.644262 34.000003,15 c 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08587,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:url(#linearGradient3009);fill-opacity:1;stroke:#2e0000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-1"
|
||||
d="M 44.276919,40.999998 H 24.148153 l -5.964082,7.216455 c -1.480672,1.914033 -1.084253,2.914242 -1.253082,3.124467 0,0.931816 1.519016,1.409822 2.746634,1.749986 l 0.204551,-0.02274 -12.8821638,-0.06818 -0.6136358,0.477279 c 0.818401,-0.160071 3.8487146,-0.895254 4.3358566,-1.295449 0.487142,-0.400213 1.225573,-1.386371 2.019368,-2.545481 L 32.066493,21.909093 c 0.662485,-1.000473 2.608645,-3.695124 3.134675,-4.704552 0.55847,-0.920405 1.508461,-3.713398 0.456236,-4.113626 l -1.681842,-0.09092 H 48.016819 L 45.857714,12.88638 c -1.814784,0 -1.770899,0.86415 -1.904039,1.52271 -0.116912,0.580339 0.106819,1.286907 0.340709,2.027235 l 9.167925,31.189218 c 0.389674,1.340655 0.401268,1.601493 0.631975,2.465379 0.594192,1.639325 0.217134,2.044469 0.996614,2.20454 l 2.909097,0.70454 H 44.000001 l 2.068168,-1.022721 c 0.821749,-1.4701 0.851418,-1.070415 1.046321,-2.09091 0.07791,-0.420217 -0.42533,-2.088667 -0.659121,-2.90908 l -2.132998,-5.84092 m 0.735776,-3.036333 -5.501761,-20.213667 -14.812359,20.190941 20.125154,0.004"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#ffffff;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-7"
|
||||
d="M 43.000006,43.000004 H 25 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 H 5.0000041 V 53.000002 C 5.9019207,52.828221 7.4631562,52.429493 8.0000051,52.000007 8.536854,51.570522 8.9999999,51.000013 10.000011,50.000001 L 32.999992,17.000006 C 33.730078,15.926342 33.871118,15.644262 34.000003,15 c 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08586,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#280000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,254 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
id="svg2869"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
sodipodi:docname="Sketcher_CreateText_Constr.svg"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="10.570313"
|
||||
inkscape:cx="-24.691795"
|
||||
inkscape:cy="23.509238"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="1571"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2869" />
|
||||
<defs
|
||||
id="defs2871">
|
||||
<linearGradient
|
||||
id="linearGradient5">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop19" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop20" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch18">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop18" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="swatch15">
|
||||
<stop
|
||||
style="stop-color:#3d0000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5-1">
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3836-9">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-3">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-5" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082"
|
||||
xlink:href="#linearGradient3836-9-3" />
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-7">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-0" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3082-3"
|
||||
xlink:href="#linearGradient3836-9-7" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient9"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.82607043,0,0,0.82533448,-4.0098079,1.346708)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.76342439,0,0,0.75750425,-4.596389,2.7525637)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3836-9-3"
|
||||
id="linearGradient3801-1-3-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5"
|
||||
gradientTransform="matrix(0.84956703,0,0,0.84301394,-2.927337,1.7790378)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3838"
|
||||
id="linearGradient3844"
|
||||
x1="36"
|
||||
y1="1039.3622"
|
||||
x2="32"
|
||||
y2="1003.3622"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(2.0563921e-6,-988.36218)" />
|
||||
<linearGradient
|
||||
id="linearGradient3838">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3840" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3842" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3830"
|
||||
id="linearGradient3836"
|
||||
x1="36"
|
||||
y1="1037.3622"
|
||||
x2="32"
|
||||
y2="1005.3622"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(2.0563921e-6,-988.36218)" />
|
||||
<linearGradient
|
||||
id="linearGradient3830">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3832" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3834" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.1649204,0,0,0.1649204,-641.71718,-230.01448)"
|
||||
y2="1734.2576"
|
||||
x2="4157.8677"
|
||||
y1="1473.5258"
|
||||
x1="4115.4229"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3009"
|
||||
xlink:href="#linearGradient3010" />
|
||||
<linearGradient
|
||||
id="linearGradient3010">
|
||||
<stop
|
||||
id="stop3012"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3014"
|
||||
offset="1"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata2874">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[maxwxyz]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreateArc.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:date>2023-12-19</dc:date>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
id="text2714"
|
||||
d="M 43.000006,43.000004 H 25 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 H 5.0000041 V 53.000002 C 5.9019207,52.828221 7.4631562,52.429493 8.0000051,52.000007 8.536854,51.570522 8.9999999,51.000013 10.000011,50.000001 L 32.999992,17.000006 C 33.730078,15.926342 33.871118,15.644262 34.000003,15 c 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08587,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:#3465a4;fill-opacity:1;stroke:#2e0000;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-1"
|
||||
d="M 44.276919,40.999998 H 24.148153 l -5.964082,7.216455 c -1.480672,1.914033 -1.084253,2.914242 -1.253082,3.124467 0,0.931816 1.519016,1.409822 2.746634,1.749986 l 0.204551,-0.02274 -12.8821638,-0.06818 -0.6136358,0.477279 c 0.818401,-0.160071 3.8487146,-0.895254 4.3358566,-1.295449 0.487142,-0.400213 1.225573,-1.386371 2.019368,-2.545481 L 32.066493,21.909093 c 0.662485,-1.000473 2.608645,-3.695124 3.134675,-4.704552 0.55847,-0.920405 1.508461,-3.713398 0.456236,-4.113626 l -1.681842,-0.09092 H 48.016819 L 45.857714,12.88638 c -1.814784,0 -1.770899,0.86415 -1.904039,1.52271 -0.116912,0.580339 0.106819,1.286907 0.340709,2.027235 l 9.167925,31.189218 c 0.389674,1.340655 0.401268,1.601493 0.631975,2.465379 0.594192,1.639325 0.217134,2.044469 0.996614,2.20454 l 2.909097,0.70454 H 44.000001 l 2.068168,-1.022721 c 0.821749,-1.4701 0.851418,-1.070415 1.046321,-2.09091 0.07791,-0.420217 -0.42533,-2.088667 -0.659121,-2.90908 l -2.132998,-5.84092 m 0.735776,-3.036333 -5.501761,-20.213667 -14.812359,20.190941 20.125154,0.004"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#729fcf;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="text2714-7"
|
||||
d="M 43.000006,43.000004 H 25 l -5.520727,6.720094 c -0.579794,0.858972 -0.393368,0.786011 -0.479275,1.279915 0,0.999994 0.647131,1.634938 2.000006,1.999989 v 2.000006 H 5.0000041 V 53.000002 C 5.9019207,52.828221 7.4631562,52.429493 8.0000051,52.000007 8.536854,51.570522 8.9999999,51.000013 10.000011,50.000001 L 32.999992,17.000006 C 33.730078,15.926342 33.871118,15.644262 34.000003,15 c 0.21471,-0.987758 0.159577,-1.570471 -1.000011,-1.999989 v -2.000007 h 16 v 2.000007 c -1.99999,0 -2.828253,0.09813 -3.000001,0.999994 -0.128869,0.622789 -0.10771,1.039609 0.150028,1.834097 l 9.953177,33.885996 c 0.429436,1.438766 0.467269,1.743077 0.896804,2.279909 0.42942,0.515393 1.140969,0.828214 2.000006,0.999995 v 2.000006 h -16 v -2.000006 c 1.481645,-0.236199 1.785214,-0.904819 1.99999,-1.999989 0.08586,-0.450959 0.257655,-1.119563 0,-2.000007 l -1.99999,-6.000002 m 0,-6.000002 -4.000012,-16 -11.999988,16 h 16"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;font-family:'Copperplate Gothic Bold';-inkscape-font-specification:'Copperplate Gothic Bold, Bold';fill:none;stroke:#091425;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -26,7 +26,7 @@
|
||||
#include <Inventor/events/SoKeyboardEvent.h>
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "ui_SketcherToolDefaultWidget.h"
|
||||
#include <Gui/Application.h>
|
||||
@@ -56,6 +56,10 @@ SketcherToolDefaultWidget::SketcherToolDefaultWidget(QWidget* parent)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->comboBox1->setMaxVisibleItems(25);
|
||||
ui->comboBox2->setMaxVisibleItems(25);
|
||||
ui->comboBox3->setMaxVisibleItems(25);
|
||||
|
||||
// connecting the needed signals
|
||||
setupConnections();
|
||||
|
||||
@@ -66,6 +70,9 @@ SketcherToolDefaultWidget::SketcherToolDefaultWidget(QWidget* parent)
|
||||
ui->parameterFive->installEventFilter(this);
|
||||
ui->parameterSix->installEventFilter(this);
|
||||
|
||||
ui->lineEdit1->installEventFilter(this);
|
||||
ui->lineEdit2->installEventFilter(this);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
@@ -155,6 +162,8 @@ void SketcherToolDefaultWidget::setupConnections()
|
||||
this,
|
||||
&SketcherToolDefaultWidget::comboBox3_currentIndexChanged
|
||||
);
|
||||
connect(ui->lineEdit1, &QLineEdit::textChanged, this, &SketcherToolDefaultWidget::lineEdit1_textChanged);
|
||||
connect(ui->lineEdit2, &QLineEdit::textChanged, this, &SketcherToolDefaultWidget::lineEdit2_textChanged);
|
||||
}
|
||||
|
||||
// preselect the number of the spinbox when it gets the focus.
|
||||
@@ -179,6 +188,13 @@ bool SketcherToolDefaultWidget::eventFilter(QObject* object, QEvent* event)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < nLineEdit; i++) {
|
||||
if (object == getLineEdit(i)) {
|
||||
signalParameterTabOrEnterPressed(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +222,11 @@ void SketcherToolDefaultWidget::reset()
|
||||
setComboboxIndex(i, 0);
|
||||
getComboBox(i)->clear();
|
||||
}
|
||||
for (int i = 0; i < nLineEdit; i++) {
|
||||
setLineEditVisible(i, false);
|
||||
QString str;
|
||||
setLineEditText(i, str);
|
||||
}
|
||||
|
||||
setNoticeVisible(false);
|
||||
}
|
||||
@@ -735,21 +756,18 @@ void SketcherToolDefaultWidget::comboBox1_currentIndexChanged(int val)
|
||||
if (!blockParameterSlots) {
|
||||
signalComboboxSelectionChanged(Combobox::FirstCombo, val);
|
||||
}
|
||||
ui->comboBox1->onSave();
|
||||
}
|
||||
void SketcherToolDefaultWidget::comboBox2_currentIndexChanged(int val)
|
||||
{
|
||||
if (!blockParameterSlots) {
|
||||
signalComboboxSelectionChanged(Combobox::SecondCombo, val);
|
||||
}
|
||||
ui->comboBox2->onSave();
|
||||
}
|
||||
void SketcherToolDefaultWidget::comboBox3_currentIndexChanged(int val)
|
||||
{
|
||||
if (!blockParameterSlots) {
|
||||
signalComboboxSelectionChanged(Combobox::ThirdCombo, val);
|
||||
}
|
||||
ui->comboBox3->onSave();
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::initNComboboxes(int ncombobox)
|
||||
@@ -833,6 +851,116 @@ int SketcherToolDefaultWidget::getComboboxIndex(int comboboxindex)
|
||||
THROWM(Base::IndexError, "ToolWidget combobox index out of range");
|
||||
}
|
||||
|
||||
QString SketcherToolDefaultWidget::getComboboxCurrentText(int comboboxindex)
|
||||
{
|
||||
if (comboboxindex < nCombobox) {
|
||||
return getComboBox(comboboxindex)->currentText();
|
||||
}
|
||||
THROWM(Base::IndexError, "ToolWidget combobox index out of range");
|
||||
}
|
||||
|
||||
int SketcherToolDefaultWidget::setComboboxCurrentText(int comboboxindex, const QString& text)
|
||||
{
|
||||
if (comboboxindex < nCombobox) {
|
||||
int index = getComboBox(comboboxindex)->findText(text, Qt::MatchFixedString);
|
||||
if (index != -1) {
|
||||
getComboBox(comboboxindex)->setCurrentIndex(index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
THROWM(Base::IndexError, "ToolWidget combobox index out of range");
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::lineEdit1_textChanged(const QString& text)
|
||||
{
|
||||
if (!blockParameterSlots) {
|
||||
signalLineEditTextChanged(LineEdit::FirstEdit, text);
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::lineEdit2_textChanged(const QString& text)
|
||||
{
|
||||
if (!blockParameterSlots) {
|
||||
signalLineEditTextChanged(LineEdit::SecondEdit, text);
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::initNLineEdits(int nlineedit)
|
||||
{
|
||||
Base::StateLocker lock(blockParameterSlots, true);
|
||||
for (int i = 0; i < nLineEdit; ++i) {
|
||||
setLineEditVisible(i, i < nlineedit);
|
||||
QString str;
|
||||
setLineEditText(i, str);
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::setLineEditVisible(int lineeditindex, bool visible)
|
||||
{
|
||||
if (lineeditindex < nLineEdit) {
|
||||
getLineEdit(lineeditindex)->setVisible(visible);
|
||||
getLineEditLabel(lineeditindex)->setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::setLineEditText(int lineeditindex, const QString& text)
|
||||
{
|
||||
if (lineeditindex < nLineEdit) {
|
||||
getLineEdit(lineeditindex)->setText(text);
|
||||
return;
|
||||
}
|
||||
THROWM(Base::IndexError, "ToolWidget line edit index out of range");
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::setLineEditLabel(int lineeditindex, const QString& string)
|
||||
{
|
||||
if (lineeditindex < nLineEdit) {
|
||||
getLineEditLabel(lineeditindex)->setText(string);
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::setLineEditFocus(int lineeditindex)
|
||||
{
|
||||
if (lineeditindex < nLineEdit) {
|
||||
QLineEdit* lineEdit = getLineEdit(lineeditindex);
|
||||
lineEdit->setFocus(Qt::OtherFocusReason);
|
||||
lineEdit->selectAll();
|
||||
return;
|
||||
}
|
||||
THROWM(Base::IndexError, "ToolWidget line edit index out of range");
|
||||
}
|
||||
|
||||
QString SketcherToolDefaultWidget::getLineEditText(int lineeditindex)
|
||||
{
|
||||
if (lineeditindex < nLineEdit) {
|
||||
return getLineEdit(lineeditindex)->text();
|
||||
}
|
||||
THROWM(Base::IndexError, "ToolWidget line edit index out of range");
|
||||
}
|
||||
|
||||
QLabel* SketcherToolDefaultWidget::getLineEditLabel(int lineeditindex)
|
||||
{
|
||||
switch (lineeditindex) {
|
||||
case LineEdit::FirstEdit:
|
||||
return ui->lineEditLabel1;
|
||||
case LineEdit::SecondEdit:
|
||||
return ui->lineEditLabel2;
|
||||
default:
|
||||
THROWM(Base::IndexError, "ToolWidget line edit index out of range");
|
||||
}
|
||||
}
|
||||
|
||||
QLineEdit* SketcherToolDefaultWidget::getLineEdit(int lineeditindex)
|
||||
{
|
||||
switch (lineeditindex) {
|
||||
case LineEdit::FirstEdit:
|
||||
return ui->lineEdit1;
|
||||
case LineEdit::SecondEdit:
|
||||
return ui->lineEdit2;
|
||||
default:
|
||||
THROWM(Base::IndexError, "ToolWidget line edit index out of range");
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherToolDefaultWidget::changeEvent(QEvent* ev)
|
||||
{
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
|
||||
class QComboBox;
|
||||
class QLineEdit;
|
||||
|
||||
namespace App
|
||||
{
|
||||
@@ -102,6 +103,14 @@ public:
|
||||
nCombobox // Must Always be the last one
|
||||
};
|
||||
|
||||
/// LineEdit number/label
|
||||
enum LineEdit
|
||||
{
|
||||
FirstEdit,
|
||||
SecondEdit,
|
||||
nLineEdit // Must Always be the last one
|
||||
};
|
||||
|
||||
explicit SketcherToolDefaultWidget(QWidget* parent = nullptr);
|
||||
~SketcherToolDefaultWidget() override;
|
||||
|
||||
@@ -149,11 +158,20 @@ public:
|
||||
void setComboboxIndex(int comboboxindex, int value);
|
||||
void setComboboxLabel(int comboboxindex, const QString& string);
|
||||
int getComboboxIndex(int comboboxindex);
|
||||
QString getComboboxCurrentText(int comboboxindex);
|
||||
int setComboboxCurrentText(int comboboxIndex, const QString& text);
|
||||
void setComboboxElements(int comboboxindex, const QStringList& names);
|
||||
void setComboboxItemIcon(int comboboxindex, int index, QIcon icon);
|
||||
void setComboboxPrefEntry(int comboboxindex, const std::string& prefEntry);
|
||||
void restoreComboboxPref(int comboboxindex);
|
||||
|
||||
void initNLineEdits(int nlineedit);
|
||||
void setLineEditVisible(int lineeditindex, bool visible);
|
||||
void setLineEditText(int lineeditindex, const QString& text);
|
||||
void setLineEditLabel(int lineeditindex, const QString& string);
|
||||
QString getLineEditText(int lineeditindex);
|
||||
void setLineEditFocus(int lineeditindex);
|
||||
|
||||
template<typename F>
|
||||
fastsignals::advanced_connection registerParameterTabOrEnterPressed(F&& fn)
|
||||
{
|
||||
@@ -181,6 +199,11 @@ public:
|
||||
return signalComboboxSelectionChanged.connect(std::forward<F>(fn), fastsignals::advanced_tag());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
fastsignals::advanced_connection registerLineEditTextChanged(F&& fn)
|
||||
{
|
||||
return signalLineEditTextChanged.connect(std::forward<F>(fn), fastsignals::advanced_tag());
|
||||
}
|
||||
|
||||
// Q_SIGNALS:
|
||||
protected Q_SLOTS:
|
||||
@@ -201,6 +224,8 @@ protected Q_SLOTS:
|
||||
void comboBox1_currentIndexChanged(int val);
|
||||
void comboBox2_currentIndexChanged(int val);
|
||||
void comboBox3_currentIndexChanged(int val);
|
||||
void lineEdit1_textChanged(const QString& text);
|
||||
void lineEdit2_textChanged(const QString& text);
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent* ev) override;
|
||||
@@ -212,6 +237,8 @@ private:
|
||||
Gui::PrefCheckBox* getCheckBox(int checkboxindex);
|
||||
Gui::PrefComboBox* getComboBox(int comboboxindex);
|
||||
QLabel* getComboBoxLabel(int comboboxindex);
|
||||
QLabel* getLineEditLabel(int lineeditindex);
|
||||
QLineEdit* getLineEdit(int lineeditindex);
|
||||
|
||||
void setParameterFontStyle(int parameterindex, FontStyle fontStyle);
|
||||
|
||||
@@ -224,6 +251,7 @@ private:
|
||||
fastsignals::signal<void(int parameterindex, double value)> signalParameterValueChanged;
|
||||
fastsignals::signal<void(int checkboxindex, bool value)> signalCheckboxCheckedChanged;
|
||||
fastsignals::signal<void(int comboindex, int value)> signalComboboxSelectionChanged;
|
||||
fastsignals::signal<void(int lineeditindex, const QString& text)> signalLineEditTextChanged;
|
||||
|
||||
/// lock to block QT slots
|
||||
bool blockParameterSlots;
|
||||
|
||||
@@ -86,6 +86,34 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lineEditLayout1">
|
||||
<item>
|
||||
<widget class="QLabel" name="lineEditLabel1">
|
||||
<property name="text">
|
||||
<string>Line edit 1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit1"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lineEditLayout2">
|
||||
<item>
|
||||
<widget class="QLabel" name="lineEditLabel2">
|
||||
<property name="text">
|
||||
<string>Line edit 2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit2"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "EditDatumDialog.h"
|
||||
#include "EditTextDialog.h"
|
||||
#include "TaskSketcherConstraints.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewProviderSketch.h"
|
||||
@@ -318,6 +319,8 @@ public:
|
||||
// Gui::BitmapFactory().iconFromTheme("Constraint_Ellipse_Axis_Angle") );
|
||||
static QIcon equal(Gui::BitmapFactory().iconFromTheme("Constraint_EqualLength"));
|
||||
static QIcon pntoo(Gui::BitmapFactory().iconFromTheme("Constraint_PointOnObject"));
|
||||
static QIcon group(Gui::BitmapFactory().iconFromTheme("Constraint_Group"));
|
||||
static QIcon text(Gui::BitmapFactory().iconFromTheme("Constraint_Text"));
|
||||
static QIcon symm(Gui::BitmapFactory().iconFromTheme("Constraint_Symmetric"));
|
||||
static QIcon snell(Gui::BitmapFactory().iconFromTheme("Constraint_SnellsLaw"));
|
||||
static QIcon iaellipseminoraxis(Gui::BitmapFactory().iconFromTheme(
|
||||
@@ -349,7 +352,7 @@ public:
|
||||
auto selicon = [this](const Sketcher::Constraint* constr,
|
||||
const QIcon& normal,
|
||||
const QIcon& driven) -> QIcon {
|
||||
if (!constr->isActive) {
|
||||
if (!sketch->isConstraintActiveInSketch(constr)) {
|
||||
QIcon darkIcon;
|
||||
int w = listWidget()->style()->pixelMetric(QStyle::PM_ListViewIconSize);
|
||||
darkIcon.addPixmap(normal.pixmap(w, w, QIcon::Disabled, QIcon::Off),
|
||||
@@ -378,6 +381,10 @@ public:
|
||||
return selicon(constraint, block, block);
|
||||
case Sketcher::PointOnObject:
|
||||
return selicon(constraint, pntoo, pntoo);
|
||||
case Sketcher::Group:
|
||||
return selicon(constraint, group, group);
|
||||
case Sketcher::Text:
|
||||
return selicon(constraint, text, text);
|
||||
case Sketcher::Parallel:
|
||||
return selicon(constraint, para, para);
|
||||
case Sketcher::Perpendicular:
|
||||
@@ -461,6 +468,8 @@ public:
|
||||
case Sketcher::Tangent:
|
||||
case Sketcher::Equal:
|
||||
case Sketcher::Symmetric:
|
||||
case Sketcher::Group:
|
||||
case Sketcher::Text:
|
||||
return true;
|
||||
case Sketcher::Distance:
|
||||
case Sketcher::DistanceX:
|
||||
@@ -825,6 +834,10 @@ ConstraintFilterList::ConstraintFilterList(QWidget* parent)
|
||||
it->setCheckState(isChecked ? Qt::Checked : Qt::Unchecked);
|
||||
filterState = filterState >> 1;// shift right to get rid of the used bit.
|
||||
}
|
||||
|
||||
// Text constraint filter is hidden from the user
|
||||
item(static_cast<int>(ConstraintFilter::FilterValue::Text))->setHidden(true);
|
||||
|
||||
languageChange();
|
||||
|
||||
setPartiallyChecked();
|
||||
@@ -1261,6 +1274,11 @@ void TaskSketcherConstraints::onListWidgetConstraintsItemActivated(QListWidgetIt
|
||||
editDatumDialog->exec(false);
|
||||
delete editDatumDialog;
|
||||
}
|
||||
else if (it->constraintType() == Sketcher::Text) {
|
||||
auto* editDialog = new EditTextDialog(this->sketchView, it->ConstraintNbr);
|
||||
editDialog->exec();
|
||||
delete editDialog;
|
||||
}
|
||||
}
|
||||
|
||||
void TaskSketcherConstraints::onListWidgetConstraintsItemChanged(QListWidgetItem* item)
|
||||
@@ -1694,6 +1712,10 @@ bool TaskSketcherConstraints::isConstraintFiltered(QListWidgetItem* item)
|
||||
ConstraintItem* it = static_cast<ConstraintItem*>(item);
|
||||
const Sketcher::Constraint* constraint = vals[it->ConstraintNbr];
|
||||
|
||||
// Text constraint is hidden from the list widget.
|
||||
if (constraint->Type == Sketcher::Text) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
@@ -1716,6 +1738,12 @@ bool TaskSketcherConstraints::isConstraintFiltered(QListWidgetItem* item)
|
||||
case Sketcher::PointOnObject:
|
||||
visible = checkFilterBitset(multiFilterStatus, FilterValue::PointOnObject);
|
||||
break;
|
||||
case Sketcher::Group:
|
||||
visible = checkFilterBitset(multiFilterStatus, FilterValue::Group);
|
||||
break;
|
||||
case Sketcher::Text:
|
||||
visible = checkFilterBitset(multiFilterStatus, FilterValue::Text);
|
||||
break;
|
||||
case Sketcher::Parallel:
|
||||
visible = checkFilterBitset(multiFilterStatus, FilterValue::Parallel);
|
||||
break;
|
||||
|
||||
@@ -119,6 +119,8 @@ private:
|
||||
{QT_TR_NOOP("Equality"), 1},
|
||||
{QT_TR_NOOP("Symmetric"), 1},
|
||||
{QT_TR_NOOP("Block"), 1},
|
||||
{QT_TR_NOOP("Group"), 1},
|
||||
{QT_TR_NOOP("Text"), 1},
|
||||
{QT_TR_NOOP("Internal Alignment"), 1},
|
||||
{QT_TR_NOOP("Datums"), 0},
|
||||
{QT_TR_NOOP("Horizontal Distance"), 1},
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "TaskSketcherElements.h"
|
||||
#include "EditTextDialog.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewProviderSketch.h"
|
||||
#include "ui_TaskSketcherElements.h"
|
||||
@@ -84,6 +85,8 @@ QT_TRANSLATE_NOOP("SketcherGui::ElementView", "Symmetric Constraint");
|
||||
|
||||
QT_TRANSLATE_NOOP("SketcherGui::ElementView", "Block Constraint");
|
||||
|
||||
QT_TRANSLATE_NOOP("SketcherGui::ElementView", "Group Constraint");
|
||||
|
||||
QT_TRANSLATE_NOOP("SketcherGui::ElementView", "Lock Position");
|
||||
|
||||
QT_TRANSLATE_NOOP("SketcherGui::ElementView", "Horizontal Dimension");
|
||||
@@ -201,7 +204,8 @@ public:
|
||||
Base::Type geometryType,
|
||||
GeometryState state,
|
||||
const QString& lab,
|
||||
ViewProviderSketch* sketchView
|
||||
ViewProviderSketch* sketchView,
|
||||
bool isTextHandle = false
|
||||
)
|
||||
: ElementNbr(elementnr)
|
||||
, StartingVertex(startingVertex)
|
||||
@@ -216,6 +220,7 @@ public:
|
||||
, clickedOn(SubElementType::none)
|
||||
, hovered(SubElementType::none)
|
||||
, rightClicked(false)
|
||||
, isTextHandle(isTextHandle)
|
||||
, label(lab)
|
||||
, sketchView(sketchView)
|
||||
{
|
||||
@@ -316,6 +321,7 @@ public:
|
||||
SubElementType clickedOn;
|
||||
SubElementType hovered;
|
||||
bool rightClicked;
|
||||
bool isTextHandle;
|
||||
|
||||
QString label;
|
||||
|
||||
@@ -374,11 +380,12 @@ public:
|
||||
|
||||
static const QIcon&
|
||||
getIcon(Base::Type type, Sketcher::PointPos pos,
|
||||
ElementItem::GeometryState icontype = ElementItem::GeometryState::Normal)
|
||||
ElementItem::GeometryState icontype = ElementItem::GeometryState::Normal,
|
||||
bool isTextHandle = false)
|
||||
{
|
||||
static ElementWidgetIcons elementicons;
|
||||
|
||||
return elementicons.getIconImpl(type, pos, icontype);
|
||||
return elementicons.getIconImpl(type, pos, icontype, isTextHandle);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -502,11 +509,34 @@ private:
|
||||
{Sketcher::PointPos::none,
|
||||
getMultIcon("Sketcher_Element_SelectionTypeInvalid")},
|
||||
}));
|
||||
|
||||
// Text Handle Icons
|
||||
textIcons[Sketcher::PointPos::none] = Gui::BitmapFactory().iconFromTheme("Sketcher_CreateText");
|
||||
textIcons[Sketcher::PointPos::start] = Gui::BitmapFactory().iconFromTheme("Sketcher_Element_Text_StartPoint");
|
||||
textIcons[Sketcher::PointPos::end] = Gui::BitmapFactory().iconFromTheme("Sketcher_Element_Text_EndPoint");
|
||||
}
|
||||
|
||||
const QIcon& getIconImpl(Base::Type type, Sketcher::PointPos pos,
|
||||
ElementItem::GeometryState icontype)
|
||||
ElementItem::GeometryState icontype,
|
||||
bool isTextHandle)
|
||||
{
|
||||
if (isTextHandle) {
|
||||
auto it = textIcons.find(pos);
|
||||
if (it != textIcons.end()) {
|
||||
return it->second;
|
||||
}
|
||||
// Fallback for Midpoint or other positions: use normal line icon without blue filter
|
||||
if (type == Part::GeomLineSegment::getClassTypeId()) {
|
||||
auto typekey = icons.find(type);
|
||||
if (typekey != icons.end()) {
|
||||
auto poskey = typekey->second.find(pos);
|
||||
if (poskey != typekey->second.end()) {
|
||||
// Return the 'Normal' variant (index 0) to avoid blue construction color
|
||||
return std::get<0>(poskey->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto typekey = icons.find(type);
|
||||
|
||||
@@ -582,6 +612,7 @@ private:
|
||||
private:
|
||||
std::map<Base::Type, std::map<Sketcher::PointPos, std::tuple<QIcon, QIcon, QIcon, QIcon>>>
|
||||
icons;
|
||||
std::map<Sketcher::PointPos, QIcon> textIcons;
|
||||
};
|
||||
|
||||
ElementView::ElementView(QWidget* parent)
|
||||
@@ -717,6 +748,14 @@ void ElementView::contextMenuEvent(QContextMenuEvent* event)
|
||||
QMenu menu;
|
||||
QList<QListWidgetItem*> items = selectedItems();
|
||||
|
||||
if (items.size() == 1) {
|
||||
ElementItem* item = static_cast<ElementItem*>(items.first());
|
||||
if (item->isTextHandle) {
|
||||
menu.addSeparator();
|
||||
menu.addAction(tr("Convert to geometries"), this, SLOT(doConvertToGeometries()));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: If extending this context menu, be sure to add the items to the translation block at
|
||||
// the top of this file
|
||||
|
||||
@@ -768,7 +807,8 @@ void ElementView::contextMenuEvent(QContextMenuEvent* event)
|
||||
true)
|
||||
CONTEXT_ITEM(
|
||||
"Constraint_Block", "Block Constraint", "Sketcher_ConstrainBlock", doBlockConstraint, true)
|
||||
|
||||
CONTEXT_ITEM(
|
||||
"Constraint_Group", "Group constraint", "Sketcher_ConstrainGroup", doGroupConstraint, true)
|
||||
CONTEXT_ITEM("Constraint_HorizontalDistance",
|
||||
"Horizontal Dimension",
|
||||
"Sketcher_ConstrainDistanceX",
|
||||
@@ -886,6 +926,7 @@ CONTEXT_MEMBER_DEF("Sketcher_ConstrainTangent", doTangentConstraint)
|
||||
CONTEXT_MEMBER_DEF("Sketcher_ConstrainEqual", doEqualConstraint)
|
||||
CONTEXT_MEMBER_DEF("Sketcher_ConstrainSymmetric", doSymmetricConstraint)
|
||||
CONTEXT_MEMBER_DEF("Sketcher_ConstrainBlock", doBlockConstraint)
|
||||
CONTEXT_MEMBER_DEF("Sketcher_ConstrainGroup", doGroupConstraint)
|
||||
|
||||
CONTEXT_MEMBER_DEF("Sketcher_ConstrainDistanceX", doHorizontalDistance)
|
||||
CONTEXT_MEMBER_DEF("Sketcher_ConstrainDistanceY", doVerticalDistance)
|
||||
@@ -939,6 +980,25 @@ ElementItem* ElementView::itemFromIndex(const QModelIndex& index)
|
||||
return static_cast<ElementItem*>(QListWidget::itemFromIndex(index));
|
||||
}
|
||||
|
||||
void ElementView::doConvertToGeometries()
|
||||
{
|
||||
QList<QListWidgetItem*> items = selectedItems();
|
||||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
ElementItem* item = static_cast<ElementItem*>(items.first());
|
||||
if (!item->isTextHandle)
|
||||
return;
|
||||
|
||||
int geoId = item->ElementNbr;
|
||||
Sketcher::SketchObject* sketch = item->getSketchObject();
|
||||
|
||||
// Deleting the handle geometry will automatically remove the Text constraint
|
||||
// (turning it to None), leaving the generated text geometries intact.
|
||||
Gui::Selection().clearSelection();
|
||||
sketch->delGeometry(geoId);
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
/* ElementItem delegate ---------------------------------------------------- */
|
||||
ElementItemDelegate::ElementItemDelegate(ElementView* parent)
|
||||
@@ -1033,7 +1093,8 @@ void ElementItemDelegate::drawSubControl(
|
||||
auto isHovered = rect.contains(mousePos);
|
||||
|
||||
auto drawSelectIcon = [&](Sketcher::PointPos pos) {
|
||||
auto icon = ElementWidgetIcons::getIcon(item->GeometryType, pos, item->State);
|
||||
auto icon
|
||||
= ElementWidgetIcons::getIcon(item->GeometryType, pos, item->State, item->isTextHandle);
|
||||
|
||||
auto isOptionSelected = option.state & QStyle::State_Selected;
|
||||
auto isOptionHovered = option.state & QStyle::State_MouseOver;
|
||||
@@ -1322,6 +1383,10 @@ void TaskSketcherElements::connectSignals()
|
||||
&ElementView::onItemHovered,
|
||||
this,
|
||||
&TaskSketcherElements::onListWidgetElementsMouseMoveOnItem);
|
||||
QObject::connect(ui->listWidgetElements,
|
||||
&ElementView::itemActivated,
|
||||
this,
|
||||
&TaskSketcherElements::onListWidgetItemActivated);
|
||||
QObject::connect(filterList,
|
||||
&QListWidget::itemChanged,
|
||||
this,
|
||||
@@ -1419,10 +1484,16 @@ void TaskSketcherElements::onListMultiFilterItemChanged(QListWidgetItem* item)
|
||||
updateVisibility();
|
||||
}
|
||||
|
||||
void TaskSketcherElements::setItemVisibility(QListWidgetItem* it)
|
||||
void TaskSketcherElements::setItemVisibility(QListWidgetItem* it, const std::set<int>& groupedGeoIds)
|
||||
{
|
||||
auto* item = static_cast<ElementItem*>(it);
|
||||
|
||||
// First, check if the item is a member of a group. If so, it must be hidden.
|
||||
if (groupedGeoIds.count(item->ElementNbr)) {
|
||||
item->setHidden(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ui->filterBox->checkState() == Qt::Unchecked) {
|
||||
item->setHidden(false);
|
||||
return;
|
||||
@@ -1477,8 +1548,20 @@ void TaskSketcherElements::setItemVisibility(QListWidgetItem* it)
|
||||
|
||||
void TaskSketcherElements::updateVisibility()
|
||||
{
|
||||
// Calculate the set of grouped geometries that should be hidden.
|
||||
std::set<int> groupedGeoIds;
|
||||
const auto& constraints = sketchView->getSketchObject()->Constraints.getValues();
|
||||
for (const auto* c : constraints) {
|
||||
if (c->Type == Sketcher::Group || c->Type == Sketcher::Text) {
|
||||
// Member geometries start from index 1.
|
||||
for (int j = 1; c->hasElement(j); ++j) {
|
||||
groupedGeoIds.insert(c->getGeoId(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ui->listWidgetElements->count(); i++) {
|
||||
setItemVisibility(ui->listWidgetElements->item(i));
|
||||
setItemVisibility(ui->listWidgetElements->item(i), groupedGeoIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1859,6 +1942,34 @@ void TaskSketcherElements::onListWidgetElementsMouseMoveOnItem(QListWidgetItem*
|
||||
previouslyHoveredType = item->hovered;
|
||||
}
|
||||
|
||||
void TaskSketcherElements::onListWidgetItemActivated(QListWidgetItem* item)
|
||||
{
|
||||
auto* elementItem = static_cast<ElementItem*>(item);
|
||||
if (!elementItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sketcher::SketchObject* sketch = sketchView->getSketchObject();
|
||||
int geoId = elementItem->ElementNbr;
|
||||
|
||||
const auto& constraints = sketch->Constraints.getValues();
|
||||
for (size_t i = 0; i < constraints.size(); ++i) {
|
||||
const auto* constraint = constraints[i];
|
||||
|
||||
// Check if the constraint is a Text constraint and if the activated element
|
||||
// is its handle (the geometry at index 0).
|
||||
if (constraint->Type == Sketcher::Text && constraint->hasElement(0)) {
|
||||
if (constraint->getGeoId(0) == geoId) {
|
||||
// The item is a handle for a text constraint. Open the edit dialog.
|
||||
auto* editDialog = new EditTextDialog(this->sketchView, i);
|
||||
editDialog->exec();
|
||||
delete editDialog;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskSketcherElements::leaveEvent(QEvent* event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
@@ -1871,6 +1982,23 @@ void TaskSketcherElements::slotElementsChanged()
|
||||
assert(sketchView);
|
||||
// Build up ListView with the elements
|
||||
Sketcher::SketchObject* sketch = sketchView->getSketchObject();
|
||||
|
||||
// Pre-process constraints to identify grouped elements and their handles
|
||||
const auto& constraints = sketch->Constraints.getValues();
|
||||
std::set<int> groupedGeoIds;
|
||||
std::map<int, Sketcher::ConstraintType> handleIdToType;
|
||||
for (const auto* c : constraints) {
|
||||
if (c->Type == Sketcher::Group || c->Type == Sketcher::Text) {
|
||||
if (c->hasElement(0)) {
|
||||
handleIdToType[c->getGeoId(0)] = c->Type;
|
||||
}
|
||||
// Elements from index 1 onwards are the members.
|
||||
for (int j = 1; c->hasElement(j); ++j) {
|
||||
groupedGeoIds.insert(c->getGeoId(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Part::Geometry*>& vals = sketch->Geometry.getValues();
|
||||
|
||||
ui->listWidgetElements->clear();
|
||||
@@ -1880,15 +2008,14 @@ void TaskSketcherElements::slotElementsChanged()
|
||||
using GeometryState = ElementItem::GeometryState;
|
||||
|
||||
int i = 1;
|
||||
for (std::vector<Part::Geometry*>::const_iterator it = vals.begin(); it != vals.end();
|
||||
++it, ++i) {
|
||||
Base::Type type = (*it)->getTypeId();
|
||||
for (auto* geo : vals) {
|
||||
Base::Type type = geo->getTypeId();
|
||||
GeometryState state = GeometryState::Normal;
|
||||
|
||||
bool construction = Sketcher::GeometryFacade::getConstruction(*it);
|
||||
bool internalAligned = Sketcher::GeometryFacade::isInternalAligned(*it);
|
||||
bool construction = Sketcher::GeometryFacade::getConstruction(geo);
|
||||
bool internalAligned = Sketcher::GeometryFacade::isInternalAligned(geo);
|
||||
|
||||
auto layerId = getSafeGeomLayerId(*it);
|
||||
auto layerId = getSafeGeomLayerId(geo);
|
||||
|
||||
if (internalAligned)
|
||||
state = GeometryState::InternalAlignment;
|
||||
@@ -1903,6 +2030,69 @@ void TaskSketcherElements::slotElementsChanged()
|
||||
return QStringLiteral("(Edge%1#ID%2)").arg(i).arg(i - 1);
|
||||
};
|
||||
|
||||
QString label;
|
||||
// This is a regular geometry. Get its type name.
|
||||
QString baseName;
|
||||
bool isTextHandle = false;
|
||||
if (type == Part::GeomPoint::getClassTypeId()) {
|
||||
baseName = tr("Point");
|
||||
}
|
||||
else if (type == Part::GeomLineSegment::getClassTypeId()) {
|
||||
int geoId = i - 1;
|
||||
auto handle_it = handleIdToType.find(geoId);
|
||||
if (handle_it != handleIdToType.end()) {
|
||||
// This is a group/text handle
|
||||
if (handle_it->second == Sketcher::Group) {
|
||||
baseName = tr("Group");
|
||||
}
|
||||
else {
|
||||
baseName = tr("Text");
|
||||
isTextHandle = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
baseName = tr("Line");
|
||||
}
|
||||
}
|
||||
else if (type == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
baseName = tr("Arc");
|
||||
}
|
||||
else if (type == Part::GeomCircle::getClassTypeId()) {
|
||||
baseName = tr("Circle");
|
||||
}
|
||||
else if (type == Part::GeomEllipse::getClassTypeId()) {
|
||||
baseName = tr("Ellipse");
|
||||
}
|
||||
else if (type == Part::GeomArcOfEllipse::getClassTypeId()) {
|
||||
baseName = tr("Elliptical Arc");
|
||||
}
|
||||
else if (type == Part::GeomArcOfHyperbola::getClassTypeId()) {
|
||||
baseName = tr("Hyperbolic Arc");
|
||||
}
|
||||
else if (type == Part::GeomArcOfParabola::getClassTypeId()) {
|
||||
baseName = tr("Parabolic arc");
|
||||
}
|
||||
else if (type == Part::GeomBSplineCurve::getClassTypeId()) {
|
||||
baseName = tr("B-spline");
|
||||
}
|
||||
else {
|
||||
baseName = tr("Other");
|
||||
}
|
||||
|
||||
// Reconstruct the label using the baseName.
|
||||
if (isNamingBoxChecked) {
|
||||
label = baseName + IdInformation();
|
||||
if (state == GeometryState::Construction) {
|
||||
label += QStringLiteral("-") + tr("Construction");
|
||||
}
|
||||
else if (state == GeometryState::InternalAlignment) {
|
||||
label += QStringLiteral("-") + tr("Internal");
|
||||
}
|
||||
}
|
||||
else {
|
||||
label = QStringLiteral("%1-").arg(i) + baseName;
|
||||
}
|
||||
|
||||
auto* itemN = new ElementItem(
|
||||
i - 1,
|
||||
sketchView->getSketchObject()->getVertexIndexGeoPos(i - 1, Sketcher::PointPos::start),
|
||||
@@ -1910,82 +2100,16 @@ void TaskSketcherElements::slotElementsChanged()
|
||||
sketchView->getSketchObject()->getVertexIndexGeoPos(i - 1, Sketcher::PointPos::end),
|
||||
type,
|
||||
state,
|
||||
type == Part::GeomPoint::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("Point") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Point")))
|
||||
: type == Part::GeomLineSegment::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("Line") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Line")))
|
||||
: type == Part::GeomArcOfCircle::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("Arc") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Arc")))
|
||||
: type == Part::GeomCircle::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("Circle") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Circle")))
|
||||
: type == Part::GeomEllipse::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("Ellipse") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Ellipse")))
|
||||
: type == Part::GeomArcOfEllipse::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("Elliptical Arc") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Elliptical arc")))
|
||||
: type == Part::GeomArcOfHyperbola::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("Hyperbolic Arc") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Hyperbolic arc")))
|
||||
: type == Part::GeomArcOfParabola::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("Parabolic Arc") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Parabolic arc")))
|
||||
: type == Part::GeomBSplineCurve::getClassTypeId()
|
||||
? (isNamingBoxChecked ? (tr("B-spline") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("B-spline")))
|
||||
: (isNamingBoxChecked ? (tr("Other") + IdInformation())
|
||||
+ (construction
|
||||
? (QStringLiteral("-") + tr("Construction"))
|
||||
: (internalAligned ? (QStringLiteral("-") + tr("Internal"))
|
||||
: QStringLiteral("")))
|
||||
: (QStringLiteral("%1-").arg(i) + tr("Other"))),
|
||||
sketchView);
|
||||
label,
|
||||
sketchView,
|
||||
isTextHandle);
|
||||
|
||||
ui->listWidgetElements->addItem(itemN);
|
||||
|
||||
elementMap[itemN->ElementNbr] = itemN;
|
||||
|
||||
setItemVisibility(itemN);
|
||||
setItemVisibility(itemN, groupedGeoIds);
|
||||
++i;
|
||||
}
|
||||
|
||||
const std::vector<Part::Geometry*>& ext_vals =
|
||||
@@ -2091,7 +2215,7 @@ void TaskSketcherElements::slotElementsChanged()
|
||||
|
||||
elementMap[itemN->ElementNbr] = itemN;
|
||||
|
||||
setItemVisibility(itemN);
|
||||
setItemVisibility(itemN, groupedGeoIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ protected Q_SLOTS:
|
||||
void doEqualConstraint();
|
||||
void doSymmetricConstraint();
|
||||
void doBlockConstraint();
|
||||
void doGroupConstraint();
|
||||
void doConvertToGeometries();
|
||||
|
||||
void doLockConstraint();
|
||||
void doHorizontalConstraint();
|
||||
@@ -133,7 +135,7 @@ public:
|
||||
private:
|
||||
void slotElementsChanged();
|
||||
void updateVisibility();
|
||||
void setItemVisibility(QListWidgetItem* item);
|
||||
void setItemVisibility(QListWidgetItem* item, const std::set<int>& groupedGeoIds);
|
||||
void clearWidget();
|
||||
void createFilterButtonActions();
|
||||
void createSettingsButtonActions();
|
||||
@@ -143,6 +145,7 @@ public Q_SLOTS:
|
||||
void onListWidgetElementsItemPressed(QListWidgetItem* item);
|
||||
void onListWidgetElementsItemEntered(QListWidgetItem* item);
|
||||
void onListWidgetElementsMouseMoveOnItem(QListWidgetItem* item);
|
||||
void onListWidgetItemActivated(QListWidgetItem* item);
|
||||
void onSettingsExtendedInformationChanged();
|
||||
void onFilterBoxStateChanged(int val);
|
||||
void onListMultiFilterItemChanged(QListWidgetItem* item);
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include <QCursor>
|
||||
#include <QLocale>
|
||||
#include <QRegularExpression>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <Base/Quantity.h>
|
||||
@@ -994,3 +997,46 @@ int SketcherGui::indexOfGeoId(const std::vector<int>& vec, int elem)
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QMap<QString, QString> SketcherGui::findAvailableFontFiles()
|
||||
{
|
||||
QMap<QString, QString> fontMap;
|
||||
QStringList fontPaths;
|
||||
|
||||
// 0. Include FreeCAD bundled fonts
|
||||
fontPaths << QString::fromStdString(
|
||||
App::Application::getResourceDir() + "Mod/TechDraw/Resources/fonts/"
|
||||
);
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
fontPaths << QString::fromUtf8("C:/Windows/Fonts");
|
||||
#elif defined(Q_OS_MACOS)
|
||||
fontPaths << QString::fromUtf8("/System/Library/Fonts") << QString::fromUtf8("/Library/Fonts")
|
||||
<< QDir::homePath() + QString::fromUtf8("/Library/Fonts");
|
||||
#else // Linux and other Unix-like systems
|
||||
fontPaths << QString::fromUtf8("/usr/share/fonts") << QString::fromUtf8("/usr/local/share/fonts")
|
||||
<< QDir::homePath() + QString::fromUtf8("/.fonts");
|
||||
#endif
|
||||
|
||||
for (const QString& path : fontPaths) {
|
||||
if (!QDir(path).exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QDirIterator it(
|
||||
path,
|
||||
QStringList() << QString::fromUtf8("*.ttf") << QString::fromUtf8("*.otf")
|
||||
<< QString::fromUtf8("*.ttc"),
|
||||
QDir::Files,
|
||||
QDirIterator::Subdirectories
|
||||
);
|
||||
while (it.hasNext()) {
|
||||
QString filePath = it.next();
|
||||
QFileInfo fileInfo(filePath);
|
||||
// Use the base name as a "friendly name".
|
||||
// We store in a map to avoid duplicates from different paths (e.g. ttf vs otf).
|
||||
fontMap[fileInfo.baseName()] = filePath;
|
||||
}
|
||||
}
|
||||
return fontMap;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include <Base/Tools2D.h>
|
||||
#include <Mod/Sketcher/App/GeoEnum.h>
|
||||
#include <QListWidget>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include "AutoConstraint.h"
|
||||
#include "ViewProviderSketchGeometryExtension.h"
|
||||
@@ -248,6 +250,8 @@ inline void scrollTo(QListWidget* list, int i, bool select)
|
||||
}
|
||||
}
|
||||
|
||||
QMap<QString, QString> findAvailableFontFiles();
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
/// converts a 2D vector into a 3D vector in the XY plane
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
|
||||
#include "DrawSketchHandler.h"
|
||||
#include "EditDatumDialog.h"
|
||||
#include "EditTextDialog.h"
|
||||
#include "EditModeCoinManager.h"
|
||||
#include "SnapManager.h"
|
||||
#include "StyleParameters.h"
|
||||
@@ -1335,9 +1336,32 @@ void ViewProviderSketch::editDoubleClicked()
|
||||
Base::Console().log("double click point:%d\n", preselection.PreselectPoint);
|
||||
}
|
||||
else if (preselection.isPreselectCurveValid()) {
|
||||
// We cannot do toggleWireSelelection directly here because the released event with
|
||||
//STATUS_NONE return false which clears the selection.
|
||||
setSketchMode(STATUS_SELECT_Wire);
|
||||
int geoId = preselection.PreselectCurve;
|
||||
Sketcher::SketchObject* sketch = getSketchObject();
|
||||
|
||||
// Check if the preselected edge is the handle of a Text constraint
|
||||
int textConstrId = -1;
|
||||
const auto& constraints = sketch->Constraints.getValues();
|
||||
for (int i = 0; i < static_cast<int>(constraints.size()); ++i) {
|
||||
if (constraints[i]->Type == Sketcher::Text && constraints[i]->hasElement(0)) {
|
||||
if (constraints[i]->getGeoId(0) == geoId) {
|
||||
textConstrId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textConstrId != -1) {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Modify Text constraint"));
|
||||
EditTextDialog editTextDialog(this, textConstrId);
|
||||
editTextDialog.exec();
|
||||
setSketchMode(STATUS_NONE);
|
||||
}
|
||||
else {
|
||||
// We cannot do toggleWireSelelection directly here because the released event with
|
||||
//STATUS_NONE return false which clears the selection.
|
||||
setSketchMode(STATUS_SELECT_Wire);
|
||||
}
|
||||
}
|
||||
else if (preselection.isCrossPreselected()) {
|
||||
Base::Console().log("double click cross:%d\n",
|
||||
@@ -1360,6 +1384,12 @@ void ViewProviderSketch::editDoubleClicked()
|
||||
EditDatumDialog editDatumDialog(this, id);
|
||||
editDatumDialog.exec();
|
||||
}
|
||||
else if (Constr->Type == Sketcher::Text) {
|
||||
Gui::Command::openCommand(
|
||||
QT_TRANSLATE_NOOP("Command", "Modify Text constraint"));
|
||||
EditTextDialog editTextDialog(this, id);
|
||||
editTextDialog.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1601,16 +1631,27 @@ void ViewProviderSketch::initDragging(int geoId, Sketcher::PointPos pos, Gui::Vi
|
||||
return; // don't drag externals
|
||||
}
|
||||
|
||||
// If we are trying to drag an edge that is in a group, we drag the group handle instead.
|
||||
int oldgeoId = geoId;
|
||||
geoId = getSketchObject()->getGroupHandleIfInGroup(geoId);
|
||||
if (oldgeoId != geoId) {
|
||||
// if replaced then we want to move the edge of the handle, not a point.
|
||||
pos = PointPos::none;
|
||||
}
|
||||
|
||||
drag.reset();
|
||||
setSketchMode(STATUS_SKETCH_Drag);
|
||||
drag.Dragged.emplace_back(geoId, pos);
|
||||
|
||||
// Adding selected geos that should be dragged as well.
|
||||
for (auto& geoIdi : selection.SelCurvSet) {
|
||||
for (auto geoIdi : selection.SelCurvSet) {
|
||||
if (geoIdi < 0) {
|
||||
continue; //skip externals
|
||||
}
|
||||
|
||||
// If in a group, we drag the group handle instead.
|
||||
geoIdi = getSketchObject()->getGroupHandleIfInGroup(geoIdi);
|
||||
|
||||
if (geoIdi == geoId) {
|
||||
// geoId is already added because it was the preselected.
|
||||
// 2 cases : either the edge was added or a point of it.
|
||||
@@ -2281,6 +2322,16 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
if (shapetype.size() > 4 && shapetype.substr(0, 4) == "Edge") {
|
||||
int GeoId = std::atoi(&shapetype[4]) - 1;
|
||||
selection.SelCurvSet.insert(GeoId);
|
||||
|
||||
// Check if this is in a group.
|
||||
// If so we cancel this addition and select the group instead
|
||||
int handleId = getSketchObject()->getGroupHandleIfInGroup(GeoId);
|
||||
if (handleId != GeoId) {
|
||||
// Remove the selected edge
|
||||
Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName);
|
||||
std::string sub = "Edge" + std::to_string(handleId + 1);
|
||||
Gui::Selection().addSelection(msg.pDocName, msg.pObjectName, sub.c_str());
|
||||
}
|
||||
}
|
||||
else if (shapetype.size() > 12 && shapetype.substr(0, 12) == "ExternalEdge") {
|
||||
int GeoId = std::atoi(&shapetype[12]) - 1;
|
||||
@@ -2471,6 +2522,16 @@ bool ViewProviderSketch::detectAndShowPreselection(SoPickedPoint* Point)
|
||||
}
|
||||
else if (result.GeoIndex != -1
|
||||
&& result.GeoIndex != preselection.PreselectCurve) {// if a new curve is hit
|
||||
|
||||
// If the picked edge is part of a text/group, treat the handle as the preselected item
|
||||
int handleId = getSketchObject()->getGroupHandleIfInGroup(result.GeoIndex);
|
||||
if (handleId != result.GeoIndex) {
|
||||
if (handleId == preselection.PreselectCurve) {
|
||||
return false;
|
||||
}
|
||||
result.GeoIndex = handleId;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
if (result.GeoIndex >= 0)
|
||||
ss << "Edge" << result.GeoIndex + 1;
|
||||
@@ -4120,6 +4181,8 @@ bool ViewProviderSketch::onDelete(const std::vector<std::string>& subList)
|
||||
Gui::Selection().clearSelection();
|
||||
resetPreselectPoint();
|
||||
|
||||
const auto& constraints = getSketchObject()->Constraints.getValues();
|
||||
|
||||
std::set<int> delInternalGeometries, delExternalGeometries, delCoincidents, delConstraints;
|
||||
// go through the selected subelements
|
||||
for (std::vector<std::string>::const_iterator it = SubNames.begin(); it != SubNames.end();
|
||||
@@ -4128,6 +4191,18 @@ bool ViewProviderSketch::onDelete(const std::vector<std::string>& subList)
|
||||
int GeoId = std::atoi(it->substr(4, 4000).c_str()) - 1;
|
||||
if (GeoId >= 0) {
|
||||
delInternalGeometries.insert(GeoId);
|
||||
|
||||
// Handle group deletion
|
||||
for (const auto* c : constraints) {
|
||||
if ((c->Type == Sketcher::Text || c->Type == Sketcher::Group)
|
||||
&& c->hasElement(0) && c->getGeoId(0) == GeoId) {
|
||||
// This is a group handle. Add all members to the delete list.
|
||||
for (int j = 1; c->hasElement(j); ++j) {
|
||||
delInternalGeometries.insert(c->getGeoId(j));
|
||||
}
|
||||
break; // A geo can only be a handle for one constraint.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
delExternalGeometries.insert(Sketcher::GeoEnum::RefExt - GeoId);
|
||||
@@ -4422,6 +4497,11 @@ Sketcher::Constraint* ViewProviderSketch::getConstraint(int constid) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ViewProviderSketch::isConstraintActiveInSketch(const Sketcher::Constraint* cstr) const
|
||||
{
|
||||
return getSketchObject()->isConstraintActiveInSketch(cstr);
|
||||
}
|
||||
|
||||
const GeoList ViewProviderSketch::getGeoList() const
|
||||
{
|
||||
const std::vector<Part::Geometry*> tempGeo =
|
||||
|
||||
@@ -888,6 +888,9 @@ private:
|
||||
/// or null if it doesn't exist.
|
||||
Sketcher::Constraint* getConstraint(int constid) const;
|
||||
|
||||
// Return true if the constraint is active, includes checking if it's not in a group
|
||||
bool isConstraintActiveInSketch(const Sketcher::Constraint* cstr) const;
|
||||
|
||||
// gets the list of geometry of the sketchobject or of the solver instance
|
||||
const GeoList getGeoList() const;
|
||||
|
||||
|
||||
@@ -85,6 +85,10 @@ class ViewProviderSketchCoinAttorney
|
||||
private:
|
||||
static inline bool constraintHasExpression(const ViewProviderSketch& vp, int constrid);
|
||||
static inline const std::vector<Sketcher::Constraint*> getConstraints(const ViewProviderSketch& vp);
|
||||
static inline bool isConstraintActiveInSketch(
|
||||
const ViewProviderSketch& vp,
|
||||
const Sketcher::Constraint* cstr
|
||||
);
|
||||
static inline const GeoList getGeoList(const ViewProviderSketch& vp);
|
||||
static inline const GeoListFacade getGeoListFacade(const ViewProviderSketch& vp);
|
||||
static inline Base::Placement getEditingPlacement(const ViewProviderSketch& vp);
|
||||
@@ -144,6 +148,14 @@ inline const std::vector<Sketcher::Constraint*> ViewProviderSketchCoinAttorney::
|
||||
return vp.getConstraints();
|
||||
}
|
||||
|
||||
inline bool ViewProviderSketchCoinAttorney::isConstraintActiveInSketch(
|
||||
const ViewProviderSketch& vp,
|
||||
const Sketcher::Constraint* cstr
|
||||
)
|
||||
{
|
||||
return vp.isConstraintActiveInSketch(cstr);
|
||||
}
|
||||
|
||||
inline const GeoList ViewProviderSketchCoinAttorney::getGeoList(const ViewProviderSketch& vp)
|
||||
{
|
||||
return vp.getGeoList();
|
||||
|
||||
@@ -455,7 +455,8 @@ inline void SketcherAddWorkbenchGeometries(T& geom)
|
||||
SketcherAddWorkspaceRectangles(geom);
|
||||
SketcherAddWorkspaceRegularPolygon(geom);
|
||||
SketcherAddWorkspaceslots(geom);
|
||||
geom << "Separator"
|
||||
geom << "Sketcher_CreateText"
|
||||
<< "Separator"
|
||||
<< "Sketcher_ToggleConstruction";
|
||||
/*<< "Sketcher_CreateText"*/
|
||||
/*<< "Sketcher_CreateDraftLine"*/;
|
||||
@@ -487,6 +488,7 @@ inline void SketcherAddWorkbenchConstraints<Gui::MenuItem>(Gui::MenuItem& cons)
|
||||
<< "Sketcher_ConstrainEqual"
|
||||
<< "Sketcher_ConstrainSymmetric"
|
||||
<< "Sketcher_ConstrainBlock"
|
||||
<< "Sketcher_ConstrainGroup"
|
||||
<< "Separator"
|
||||
<< "Sketcher_Dimension"
|
||||
<< "Sketcher_ConstrainDistanceX"
|
||||
@@ -553,7 +555,8 @@ inline void SketcherAddWorkbenchConstraints<Gui::ToolBarItem>(Gui::ToolBarItem&
|
||||
<< "Sketcher_ConstrainTangent"
|
||||
<< "Sketcher_ConstrainEqual"
|
||||
<< "Sketcher_ConstrainSymmetric"
|
||||
<< "Sketcher_ConstrainBlock";
|
||||
<< "Sketcher_ConstrainBlock"
|
||||
<< "Sketcher_ConstrainGroup";
|
||||
|
||||
cons << "Separator"
|
||||
<< "Sketcher_CompToggleConstraints";
|
||||
|
||||