From 80a9703acd7c987331c9e087b2b02532793dc2ec Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Wed, 28 Feb 2024 23:13:45 +0100 Subject: [PATCH 01/59] Update solver.py --- src/Mod/Fem/femsolver/calculix/solver.py | 34 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/solver.py b/src/Mod/Fem/femsolver/calculix/solver.py index f6bfd07bb6..3ccc833dcc 100644 --- a/src/Mod/Fem/femsolver/calculix/solver.py +++ b/src/Mod/Fem/femsolver/calculix/solver.py @@ -198,19 +198,19 @@ def add_attributes(obj, ccx_prefs): ehl = ccx_prefs.GetFloat("EigenmodeHighLimit", 1000000.0) obj.EigenmodeHighLimit = (ehl, 0.0, 1000000.0, 10000.0) - if not hasattr(obj, "IterationsThermoMechMaximum"): - help_string_IterationsThermoMechMaximum = ( - "Maximum Number of thermo mechanical iterations " + if not hasattr(obj, "IterationsMaximum"): + help_string_IterationsMaximum = ( + "Maximum Number of iterations " "in each time step before stopping jobs" ) obj.addProperty( "App::PropertyIntegerConstraint", - "IterationsThermoMechMaximum", + "IterationsMaximum", "Fem", - help_string_IterationsThermoMechMaximum + help_string_IterationsMaximum ) niter = ccx_prefs.GetInt("AnalysisMaxIterations", 200) - obj.IterationsThermoMechMaximum = niter + obj.IterationsMaximum = niter if not hasattr(obj, "BucklingFactors"): obj.addProperty( @@ -242,6 +242,26 @@ def add_attributes(obj, ccx_prefs): eni = ccx_prefs.GetFloat("AnalysisTime", 1.0) obj.TimeEnd = eni + if not hasattr(obj, "TimeMinimumStep"): + obj.addProperty( + "App::PropertyFloatConstraint", + "TimeMinimumStep", + "Fem", + "Minimum time step" + ) + mini = ccx_prefs.GetFloat("AnalysisTimeMinimumStep", 0.00001) + obj.TimeMinimumStep = mini + + if not hasattr(obj, "TimeMaximumStep"): + obj.addProperty( + "App::PropertyFloatConstraint", + "TimeMaximumStep", + "Fem", + "Maximum time step" + ) + maxi = ccx_prefs.GetFloat("AnalysisTimeMaximumStep", 1.0) + obj.TimeMaximumStep = maxi + if not hasattr(obj, "ThermoMechSteadyState"): obj.addProperty( "App::PropertyBool", @@ -332,7 +352,7 @@ def add_attributes(obj, ccx_prefs): if not hasattr(obj, "IterationsUserDefinedTimeStepLength"): help_string_IterationsUserDefinedTimeStepLength = ( "Set to True to use the user defined time steps. " - "The time steps are set with TimeInitialStep and TimeEnd" + "They are set with TimeInitialStep, TimeEnd, TimeMinimum and TimeMaximum" ) obj.addProperty( "App::PropertyBool", From cfb2616c36f22fdb23f77d86092f3db389d5bbaa Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Wed, 28 Feb 2024 23:17:34 +0100 Subject: [PATCH 02/59] Update write_step_equation.py --- .../femsolver/calculix/write_step_equation.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/write_step_equation.py b/src/Mod/Fem/femsolver/calculix/write_step_equation.py index e4beef37c2..02a06c1f1f 100644 --- a/src/Mod/Fem/femsolver/calculix/write_step_equation.py +++ b/src/Mod/Fem/femsolver/calculix/write_step_equation.py @@ -45,12 +45,11 @@ def write_step_equation(f, ccxwriter): "Analysis type frequency and geometrical nonlinear " "analysis are not allowed together, linear is used instead!\n" ) - if ccxwriter.solver_obj.IterationsThermoMechMaximum: - if ccxwriter.analysis_type == "thermomech": - step += ", INC={}".format(ccxwriter.solver_obj.IterationsThermoMechMaximum) + if ccxwriter.solver_obj.IterationsMaximum: + if ccxwriter.analysis_type == "thermomech" or ccxwriter.analysis_type == "static": + step += ", INC={}".format(ccxwriter.solver_obj.IterationsMaximum) elif ( - ccxwriter.analysis_type == "static" - or ccxwriter.analysis_type == "frequency" + ccxwriter.analysis_type == "frequency" or ccxwriter.analysis_type == "buckling" ): # parameter is for thermomechanical analysis only, see ccx manual *STEP @@ -124,9 +123,11 @@ def write_step_equation(f, ccxwriter): if ccxwriter.analysis_type == "static" or ccxwriter.analysis_type == "check": if ccxwriter.solver_obj.IterationsUserDefinedIncrementations is True \ or ccxwriter.solver_obj.IterationsUserDefinedTimeStepLength is True: - analysis_parameter = "{},{}".format( + analysis_parameter = "{},{},{},{}".format( ccxwriter.solver_obj.TimeInitialStep, - ccxwriter.solver_obj.TimeEnd + ccxwriter.solver_obj.TimeEnd, + ccxwriter.solver_obj.TimeMinimumStep, + ccxwriter.solver_obj.TimeMaximumStep ) elif ccxwriter.analysis_type == "frequency": if ccxwriter.solver_obj.EigenmodeLowLimit == 0.0 \ @@ -140,9 +141,11 @@ def write_step_equation(f, ccxwriter): ) elif ccxwriter.analysis_type == "thermomech": # OvG: 1.0 increment, total time 1 for steady state will cut back automatically - analysis_parameter = "{},{}".format( + analysis_parameter = "{},{},{},{}".format( ccxwriter.solver_obj.TimeInitialStep, - ccxwriter.solver_obj.TimeEnd + ccxwriter.solver_obj.TimeEnd, + ccxwriter.solver_obj.TimeMinimumStep, + ccxwriter.solver_obj.TimeMaximumStep ) elif ccxwriter.analysis_type == "buckling": analysis_parameter = "{}\n".format(ccxwriter.solver_obj.BucklingFactors) From b04fd4021569b073588b268c87f866ed2e7f01da Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Wed, 28 Feb 2024 23:29:05 +0100 Subject: [PATCH 03/59] Update DlgSettingsFemCcx.ui --- src/Mod/Fem/Gui/DlgSettingsFemCcx.ui | 186 +++++++++++++++++++++------ 1 file changed, 148 insertions(+), 38 deletions(-) diff --git a/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui b/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui index 393866c37e..7865805b31 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui +++ b/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui @@ -393,14 +393,14 @@ - + Time incrementation control parameter - + Use non ccx defaults @@ -416,6 +416,35 @@ + + + + Maximum number of iterations + + + + + + + 1 + + + 10000000 + + + 10 + + + 2000 + + + AnalysisMaxIterations + + + Mod/Fem/Ccx + + + @@ -445,10 +474,10 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - 3 + 9 - 0.010000000000000 + 0.000000001000000 0.010000000000000 @@ -484,10 +513,10 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - 3 + 9 - 0.010000000000000 + 0.000000001000000 0.010000000000000 @@ -504,20 +533,130 @@ - + s - + + + + Time Minimum Step + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::DefaultContextMenu + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9 + + + 0.000000001000000 + + + 0.010000000000000 + + + 0.000010000000000 + + + AnalysisTimeMinimumStep + + + Mod/Fem/Ccx + + + + + + + s + + + + + + + Time Maximum Step + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::DefaultContextMenu + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9 + + + 0.000000001000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + AnalysisTimeMaximumStep + + + Mod/Fem/Ccx + + + + + + + s + + + + Beam, shell element 3D output format - + 3D Output, unchecked for 2D @@ -569,35 +708,6 @@ - - - - Maximum number of iterations - - - - - - - 1 - - - 10000000 - - - 10 - - - 2000 - - - AnalysisMaxIterations - - - Mod/Fem/Ccx - - - From 30914209ae164e779b6c0b8a71e4ce3ce312297b Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:38:57 +0100 Subject: [PATCH 04/59] Update square_pipe_end_twisted_nodeforces.inp --- .../data/calculix/square_pipe_end_twisted_nodeforces.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_nodeforces.inp b/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_nodeforces.inp index 3c77704977..19bb6e8c53 100644 --- a/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_nodeforces.inp +++ b/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_nodeforces.inp @@ -2561,7 +2561,7 @@ Efaces *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD *STEP -*STATIC +*STATIC, INC=200 *********************************************************** From 51c46d1a2405a8be139d254c5351d911bb537b38 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:41:23 +0100 Subject: [PATCH 05/59] Update thermomech_bimetall.py --- src/Mod/Fem/femexamples/thermomech_bimetall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femexamples/thermomech_bimetall.py b/src/Mod/Fem/femexamples/thermomech_bimetall.py index a34763d9c3..8368eb703a 100644 --- a/src/Mod/Fem/femexamples/thermomech_bimetall.py +++ b/src/Mod/Fem/femexamples/thermomech_bimetall.py @@ -145,7 +145,7 @@ def setup(doc=None, solvertype="ccxtools"): # solver_obj.MatrixSolverType = "default" solver_obj.MatrixSolverType = "spooles" # thomas solver_obj.SplitInputWriter = False - solver_obj.IterationsThermoMechMaximum = 2000 + solver_obj.IterationsMaximum = 2000 # solver_obj.IterationsControlParameterTimeUse = True # thermomech spine analysis.addObject(solver_obj) From 734edd8fd17350890869d091718b14ea263188f2 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:42:08 +0100 Subject: [PATCH 06/59] Update box_static.inp --- src/Mod/Fem/femtest/data/calculix/box_static.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/box_static.inp b/src/Mod/Fem/femtest/data/calculix/box_static.inp index a11e5e3820..015fb40f1c 100644 --- a/src/Mod/Fem/femtest/data/calculix/box_static.inp +++ b/src/Mod/Fem/femtest/data/calculix/box_static.inp @@ -489,7 +489,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 884dc2c30f48d89c5c3cdab4b2a41bdca88ba1fd Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:42:41 +0100 Subject: [PATCH 07/59] Update ccx_cantilever_beam_circle.inp --- .../Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp index 61e6847134..91cf87bf3f 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_circle.inp @@ -62,7 +62,7 @@ Eedges *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From fd26660cd05ff35cbd27e3b3aebe5c1ccdaf164b Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:43:08 +0100 Subject: [PATCH 08/59] Update ccx_cantilever_beam_pipe.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp index 93143cfedd..c60a64673c 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_pipe.inp @@ -62,7 +62,7 @@ Eedges *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From b49a4f144b5e7388c2bf12e15eed621e11f15dd6 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:43:27 +0100 Subject: [PATCH 09/59] Update ccx_cantilever_beam_rect.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp index 0f857c351e..e4109fe5d5 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_beam_rect.inp @@ -62,7 +62,7 @@ Eedges *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 2f7a029ac1eba7c95fa78ef15a19f1c159a23291 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:43:49 +0100 Subject: [PATCH 10/59] Update ccx_cantilever_ele_hexa20.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_hexa20.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_hexa20.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_hexa20.inp index 7e200cbdb4..0730fb3ed2 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_hexa20.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_hexa20.inp @@ -385,7 +385,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From c76dbf44594f9311e8d8a0ebe15fe878eab137a3 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:44:07 +0100 Subject: [PATCH 11/59] Update ccx_cantilever_ele_quad4.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad4.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad4.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad4.inp index e54d1cdaf2..e7ee84403d 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad4.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad4.inp @@ -86,7 +86,7 @@ Efaces *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 9bc2b5c50696855c8e17a5d4186f530829012f7f Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:44:26 +0100 Subject: [PATCH 12/59] Update ccx_cantilever_ele_quad8.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad8.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad8.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad8.inp index 5865f2894c..0f652ebbd1 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad8.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_quad8.inp @@ -74,7 +74,7 @@ Efaces *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 6bddef5c6ccbb9336a54f765b46f78f0b030fd28 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:44:44 +0100 Subject: [PATCH 13/59] Update ccx_cantilever_ele_seg2.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp index 8f32e410b8..50be05ece5 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg2.inp @@ -204,7 +204,7 @@ Eedges *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From b0783477a26381892c956fdbb2156d95500d6bf2 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:45:01 +0100 Subject: [PATCH 14/59] Update ccx_cantilever_ele_seg3.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp index b3da786e1f..476c5968b5 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_seg3.inp @@ -62,7 +62,7 @@ Eedges *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 684c8a53679b074737c9d88c33c0ad81b57846b8 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:45:24 +0100 Subject: [PATCH 15/59] Update ccx_cantilever_ele_tria3.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria3.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria3.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria3.inp index bf2c5b3c22..bec70fd1e5 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria3.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria3.inp @@ -1562,7 +1562,7 @@ Efaces *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From b18afc0dab30325a070d59335ac3d7126d97f38a Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:45:41 +0100 Subject: [PATCH 16/59] Update ccx_cantilever_ele_tria6.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria6.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria6.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria6.inp index 1038b295da..e01d81d920 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria6.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_ele_tria6.inp @@ -292,7 +292,7 @@ Efaces *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 7e9a468ca398d5c721e83c2974fa6a9875911494 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:46:02 +0100 Subject: [PATCH 17/59] Update ccx_cantilever_faceload.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_faceload.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_faceload.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_faceload.inp index 46e8715e3b..33092c5bc9 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_faceload.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_faceload.inp @@ -359,7 +359,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From ba6b9de9596705deac4dd2c364449b45928d099d Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:46:19 +0100 Subject: [PATCH 18/59] Update ccx_cantilever_nodeload.inp --- src/Mod/Fem/femtest/data/calculix/ccx_cantilever_nodeload.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_nodeload.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_nodeload.inp index 1ecad0c387..2004f1a460 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_nodeload.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_nodeload.inp @@ -359,7 +359,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 38dce330607d08d83e1779fdfee7d7f729d72511 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:46:39 +0100 Subject: [PATCH 19/59] Update ccx_cantilever_prescribeddisplacement.inp --- .../data/calculix/ccx_cantilever_prescribeddisplacement.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_prescribeddisplacement.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_prescribeddisplacement.inp index f803012856..815ec60692 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_prescribeddisplacement.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_prescribeddisplacement.inp @@ -377,7 +377,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From da3069562ee156f97f0835bf1fa1bdfe87173076 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:47:19 +0100 Subject: [PATCH 20/59] Update constraint_contact_shell_shell.inp --- .../femtest/data/calculix/constraint_contact_shell_shell.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/constraint_contact_shell_shell.inp b/src/Mod/Fem/femtest/data/calculix/constraint_contact_shell_shell.inp index 4f696b5b7d..8382e59786 100644 --- a/src/Mod/Fem/femtest/data/calculix/constraint_contact_shell_shell.inp +++ b/src/Mod/Fem/femtest/data/calculix/constraint_contact_shell_shell.inp @@ -38373,7 +38373,7 @@ DEPConstraintContact, INDConstraintContact *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 02f3d361cc0ece60db065f9e89abcfd9b3b2f3c9 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:47:37 +0100 Subject: [PATCH 21/59] Update constraint_sectionprint.inp --- src/Mod/Fem/femtest/data/calculix/constraint_sectionprint.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/constraint_sectionprint.inp b/src/Mod/Fem/femtest/data/calculix/constraint_sectionprint.inp index e8ff5f13c9..14176c54ec 100644 --- a/src/Mod/Fem/femtest/data/calculix/constraint_sectionprint.inp +++ b/src/Mod/Fem/femtest/data/calculix/constraint_sectionprint.inp @@ -3401,7 +3401,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 208d58646c9dc2ecf4ca1f7c8d24a3fdb3f18c28 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:47:54 +0100 Subject: [PATCH 22/59] Update constraint_selfweight_cantilever.inp --- .../femtest/data/calculix/constraint_selfweight_cantilever.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/constraint_selfweight_cantilever.inp b/src/Mod/Fem/femtest/data/calculix/constraint_selfweight_cantilever.inp index 1721a498ea..aacd4d1102 100644 --- a/src/Mod/Fem/femtest/data/calculix/constraint_selfweight_cantilever.inp +++ b/src/Mod/Fem/femtest/data/calculix/constraint_selfweight_cantilever.inp @@ -2153,7 +2153,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 1d1841b640d08dafd8c99bdc377509d63b670ff6 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:48:17 +0100 Subject: [PATCH 23/59] Update constraint_tie.inp --- src/Mod/Fem/femtest/data/calculix/constraint_tie.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/constraint_tie.inp b/src/Mod/Fem/femtest/data/calculix/constraint_tie.inp index a201c5415f..c95467c939 100644 --- a/src/Mod/Fem/femtest/data/calculix/constraint_tie.inp +++ b/src/Mod/Fem/femtest/data/calculix/constraint_tie.inp @@ -18612,7 +18612,7 @@ TIE_DEPConstraintTie, TIE_INDConstraintTie *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From e11ccff6e2637a696f2071b2f4933eb76d4d7a41 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:48:42 +0100 Subject: [PATCH 24/59] Update constraint_transform_beam_hinged.inp --- .../femtest/data/calculix/constraint_transform_beam_hinged.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/constraint_transform_beam_hinged.inp b/src/Mod/Fem/femtest/data/calculix/constraint_transform_beam_hinged.inp index a81cedf777..48de5e40b0 100644 --- a/src/Mod/Fem/femtest/data/calculix/constraint_transform_beam_hinged.inp +++ b/src/Mod/Fem/femtest/data/calculix/constraint_transform_beam_hinged.inp @@ -3639,7 +3639,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 5d0ab3910ccf7eb04d7b9d0595b1d530698ddab3 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:49:08 +0100 Subject: [PATCH 25/59] Update constraint_transform_torque.inp --- .../Fem/femtest/data/calculix/constraint_transform_torque.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/constraint_transform_torque.inp b/src/Mod/Fem/femtest/data/calculix/constraint_transform_torque.inp index 80dad5dcea..805dc88c20 100644 --- a/src/Mod/Fem/femtest/data/calculix/constraint_transform_torque.inp +++ b/src/Mod/Fem/femtest/data/calculix/constraint_transform_torque.inp @@ -10980,7 +10980,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From 72f91e0c05b82633c78b486702dbe71f4e133c4f Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:49:32 +0100 Subject: [PATCH 26/59] Update material_multiple_bendingbeam_fiveboxes.inp --- .../data/calculix/material_multiple_bendingbeam_fiveboxes.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/material_multiple_bendingbeam_fiveboxes.inp b/src/Mod/Fem/femtest/data/calculix/material_multiple_bendingbeam_fiveboxes.inp index 1af0d60156..239603709e 100644 --- a/src/Mod/Fem/femtest/data/calculix/material_multiple_bendingbeam_fiveboxes.inp +++ b/src/Mod/Fem/femtest/data/calculix/material_multiple_bendingbeam_fiveboxes.inp @@ -27634,7 +27634,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From a8bcdb2a683ebd7d2e723667b620f987505d0462 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:49:51 +0100 Subject: [PATCH 27/59] Update material_multiple_bendingbeam_fivefaces.inp --- .../data/calculix/material_multiple_bendingbeam_fivefaces.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/material_multiple_bendingbeam_fivefaces.inp b/src/Mod/Fem/femtest/data/calculix/material_multiple_bendingbeam_fivefaces.inp index 44c4b5ab2d..69e3508500 100644 --- a/src/Mod/Fem/femtest/data/calculix/material_multiple_bendingbeam_fivefaces.inp +++ b/src/Mod/Fem/femtest/data/calculix/material_multiple_bendingbeam_fivefaces.inp @@ -2548,7 +2548,7 @@ Efaces *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From c4f6e9a8c3e9f527d0490343a02d6400d15f49de Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:50:11 +0100 Subject: [PATCH 28/59] Update material_multiple_tensionrod_twoboxes.inp --- .../data/calculix/material_multiple_tensionrod_twoboxes.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/material_multiple_tensionrod_twoboxes.inp b/src/Mod/Fem/femtest/data/calculix/material_multiple_tensionrod_twoboxes.inp index 381828eb1b..1e8accdae7 100644 --- a/src/Mod/Fem/femtest/data/calculix/material_multiple_tensionrod_twoboxes.inp +++ b/src/Mod/Fem/femtest/data/calculix/material_multiple_tensionrod_twoboxes.inp @@ -1231,7 +1231,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From b3d182f73de6c8a97acd023cefcb87d1f03bbe85 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:50:33 +0100 Subject: [PATCH 29/59] Update material_nonlinear.inp --- src/Mod/Fem/femtest/data/calculix/material_nonlinear.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/material_nonlinear.inp b/src/Mod/Fem/femtest/data/calculix/material_nonlinear.inp index da02ee8944..def8eb4108 100644 --- a/src/Mod/Fem/femtest/data/calculix/material_nonlinear.inp +++ b/src/Mod/Fem/femtest/data/calculix/material_nonlinear.inp @@ -20004,7 +20004,7 @@ Evolumes *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP, NLGEOM +*STEP, NLGEOM, INC=200 *STATIC From e135e36d5daee619d96932bc6ab4ac11c602a3c9 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:50:52 +0100 Subject: [PATCH 30/59] Update square_pipe_end_twisted_edgeforces.inp --- .../data/calculix/square_pipe_end_twisted_edgeforces.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_edgeforces.inp b/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_edgeforces.inp index c6b3c53ec8..753232bed9 100644 --- a/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_edgeforces.inp +++ b/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_edgeforces.inp @@ -2560,7 +2560,7 @@ Efaces *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP +*STEP, INC=200 *STATIC From b0759519decb413ff7e7547a41fb6b78d7a0d97e Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:52:41 +0100 Subject: [PATCH 31/59] Update square_pipe_end_twisted_nodeforces.inp --- .../data/calculix/square_pipe_end_twisted_nodeforces.inp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_nodeforces.inp b/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_nodeforces.inp index 19bb6e8c53..02759ba8cd 100644 --- a/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_nodeforces.inp +++ b/src/Mod/Fem/femtest/data/calculix/square_pipe_end_twisted_nodeforces.inp @@ -2560,8 +2560,8 @@ Efaces *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP -*STATIC, INC=200 +*STEP, INC=200 +*STATIC *********************************************************** From 529a6da878a22b860ea304359ac4755ec428e0a4 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:32:18 +0100 Subject: [PATCH 32/59] Update thermomech_bimetall.inp --- src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp b/src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp index 2be2695488..d2563c9423 100644 --- a/src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp +++ b/src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp @@ -7079,9 +7079,9 @@ Nall,273.0 *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP, INC=2000 +*STEP, INC=200 *COUPLED TEMPERATURE-DISPLACEMENT, SOLVER=SPOOLES, STEADY STATE -1.0,1.0 +1.0,1.0,1e-05,1.0 *********************************************************** ** Fixed Constraints From 96f3a7e8bcd1de66576f42487ad67e8e6caae401 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:47:18 +0100 Subject: [PATCH 33/59] Update thermomech_bimetall.inp --- src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp b/src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp index d2563c9423..57818a086e 100644 --- a/src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp +++ b/src/Mod/Fem/femtest/data/calculix/thermomech_bimetall.inp @@ -7079,7 +7079,7 @@ Nall,273.0 *********************************************************** ** At least one step is needed to run an CalculiX analysis of FreeCAD -*STEP, INC=200 +*STEP, INC=2000 *COUPLED TEMPERATURE-DISPLACEMENT, SOLVER=SPOOLES, STEADY STATE 1.0,1.0,1e-05,1.0 From 80974bc50319b6da19b840e0a6b2d4385ed276ee Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 14 Mar 2024 10:10:53 +0100 Subject: [PATCH 34/59] Gui: refactor DlgSettings3DViewImp and fix some linter warnings --- .../PreferencePages/DlgSettings3DViewImp.cpp | 106 ++++++++++++------ .../PreferencePages/DlgSettings3DViewImp.h | 11 +- 2 files changed, 80 insertions(+), 37 deletions(-) diff --git a/src/Gui/PreferencePages/DlgSettings3DViewImp.cpp b/src/Gui/PreferencePages/DlgSettings3DViewImp.cpp index 49f175bb2a..ea0ad8abce 100644 --- a/src/Gui/PreferencePages/DlgSettings3DViewImp.cpp +++ b/src/Gui/PreferencePages/DlgSettings3DViewImp.cpp @@ -40,12 +40,6 @@ using namespace Gui::Dialog; /* TRANSLATOR Gui::Dialog::DlgSettings3DViewImp */ -bool DlgSettings3DViewImp::showMsg = true; - -/** - * Constructs a DlgSettings3DViewImp which is a child of 'parent', with the - * name 'name' and widget flags set to 'f' - */ DlgSettings3DViewImp::DlgSettings3DViewImp(QWidget* parent) : PreferencePage( parent ) , ui(new Ui_DlgSettings3DView) @@ -53,29 +47,15 @@ DlgSettings3DViewImp::DlgSettings3DViewImp(QWidget* parent) ui->setupUi(this); } -/** - * Destroys the object and frees any allocated resources - */ DlgSettings3DViewImp::~DlgSettings3DViewImp() = default; void DlgSettings3DViewImp::saveSettings() { - // must be done as very first because we create a new instance of NavigatorStyle - // where we set some attributes afterwards - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath - ("User parameter:BaseApp/Preferences/View"); - - int index = ui->comboAliasing->currentIndex(); - hGrp->SetInt("AntiAliasing", index); - - index = ui->renderCache->currentIndex(); - hGrp->SetInt("RenderCache", index); + saveAntiAliasing(); + saveRenderCache(); + saveMarkerSize(); ui->comboTransparentRender->onSave(); - - QVariant const &vBoxMarkerSize = ui->boxMarkerSize->itemData(ui->boxMarkerSize->currentIndex()); - hGrp->SetInt("MarkerSize", vBoxMarkerSize.toInt()); - ui->CheckBox_CornerCoordSystem->onSave(); ui->SpinBox_CornerCoordSystemSize->onSave(); ui->CheckBox_ShowAxisCross->onSave(); @@ -106,23 +86,70 @@ void DlgSettings3DViewImp::loadSettings() ui->sliderIntensity->onRestore(); ui->radioPerspective->onRestore(); ui->radioOrthographic->onRestore(); + ui->comboTransparentRender->onRestore(); + loadAntiAliasing(); + loadRenderCache(); + loadMarkerSize(); +} + +void DlgSettings3DViewImp::saveAntiAliasing() +{ ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/View"); - int index = hGrp->GetInt("AntiAliasing", int(Gui::View3DInventorViewer::None)); - index = Base::clamp(index, 0, ui->comboAliasing->count()-1); - ui->comboAliasing->setCurrentIndex(index); + int aliasing = ui->comboAliasing->currentIndex(); + hGrp->SetInt("AntiAliasing", aliasing); +} + +void DlgSettings3DViewImp::loadAntiAliasing() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/View"); + + int aliasing = int(hGrp->GetInt("AntiAliasing", int(Gui::View3DInventorViewer::None))); + aliasing = Base::clamp(aliasing, 0, ui->comboAliasing->count()-1); + ui->comboAliasing->setCurrentIndex(aliasing); + // connect after setting current item of the combo box connect(ui->comboAliasing, qOverload(&QComboBox::currentIndexChanged), this, &DlgSettings3DViewImp::onAliasingChanged); +} - index = hGrp->GetInt("RenderCache", 0); - ui->renderCache->setCurrentIndex(index); +void DlgSettings3DViewImp::saveRenderCache() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/View"); - ui->comboTransparentRender->onRestore(); + int cache = ui->renderCache->currentIndex(); + hGrp->SetInt("RenderCache", cache); +} - int const current = hGrp->GetInt("MarkerSize", 9L); +void DlgSettings3DViewImp::loadRenderCache() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/View"); + + long cache = hGrp->GetInt("RenderCache", 0); + ui->renderCache->setCurrentIndex(int(cache)); +} + +void DlgSettings3DViewImp::saveMarkerSize() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/View"); + + QVariant const &vBoxMarkerSize = ui->boxMarkerSize->itemData(ui->boxMarkerSize->currentIndex()); + hGrp->SetInt("MarkerSize", vBoxMarkerSize.toInt()); +} + +void DlgSettings3DViewImp::loadMarkerSize() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/View"); + + // NOLINTBEGIN + int marker = hGrp->GetInt("MarkerSize", 9L); ui->boxMarkerSize->addItem(tr("5px"), QVariant(5)); ui->boxMarkerSize->addItem(tr("7px"), QVariant(7)); ui->boxMarkerSize->addItem(tr("9px"), QVariant(9)); @@ -132,9 +159,12 @@ void DlgSettings3DViewImp::loadSettings() ui->boxMarkerSize->addItem(tr("20px"), QVariant(20)); ui->boxMarkerSize->addItem(tr("25px"), QVariant(25)); ui->boxMarkerSize->addItem(tr("30px"), QVariant(30)); - index = ui->boxMarkerSize->findData(QVariant(current)); - if (index < 0) index = 2; - ui->boxMarkerSize->setCurrentIndex(index); + marker = ui->boxMarkerSize->findData(QVariant(marker)); + if (marker < 0) { + marker = 2; + } + ui->boxMarkerSize->setCurrentIndex(marker); + // NOLINTEND } void DlgSettings3DViewImp::resetSettingsToDefaults() @@ -165,20 +195,24 @@ void DlgSettings3DViewImp::changeEvent(QEvent *e) ui->comboAliasing->blockSignals(false); } else { - QWidget::changeEvent(e); + PreferencePage::changeEvent(e); } } void DlgSettings3DViewImp::onAliasingChanged(int index) { - if (index < 0 || !isVisible()) + if (index < 0 || !isVisible()) { return; + } + // Show this message only once per application session to reduce // annoyance when showing it too often. + static bool showMsg = true; if (showMsg) { showMsg = false; QMessageBox::information(this, tr("Anti-aliasing"), - tr("Open a new viewer or restart %1 to apply anti-aliasing changes.").arg(qApp->applicationName())); + tr("Open a new viewer or restart %1 to apply anti-aliasing changes.") + .arg(qApp->applicationName())); } } diff --git a/src/Gui/PreferencePages/DlgSettings3DViewImp.h b/src/Gui/PreferencePages/DlgSettings3DViewImp.h index 86af22838a..fccecdc39f 100644 --- a/src/Gui/PreferencePages/DlgSettings3DViewImp.h +++ b/src/Gui/PreferencePages/DlgSettings3DViewImp.h @@ -56,9 +56,18 @@ private Q_SLOTS: protected: void changeEvent(QEvent *e) override; +private: + void saveAntiAliasing(); + void loadAntiAliasing(); + void saveRenderCache(); + void loadRenderCache(); + void saveMarkerSize(); + void loadMarkerSize(); + private: std::unique_ptr ui; - static bool showMsg; + + Q_DISABLE_COPY_MOVE(DlgSettings3DViewImp) }; } // namespace Dialog From 1e9d6698b77dadc1a40e03ac467f35b696d885d7 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 14 Mar 2024 11:41:33 +0100 Subject: [PATCH 35/59] Gui: fixes #5609: Add MSAA 6x option --- src/Gui/PreferencePages/DlgSettings3DView.ui | 25 ------ .../PreferencePages/DlgSettings3DViewImp.cpp | 80 ++++++++++++++++++- .../PreferencePages/DlgSettings3DViewImp.h | 1 + src/Gui/View3DInventorViewer.cpp | 2 + src/Gui/View3DInventorViewer.h | 11 +-- 5 files changed, 86 insertions(+), 33 deletions(-) diff --git a/src/Gui/PreferencePages/DlgSettings3DView.ui b/src/Gui/PreferencePages/DlgSettings3DView.ui index 3a85779209..0854dd66a5 100644 --- a/src/Gui/PreferencePages/DlgSettings3DView.ui +++ b/src/Gui/PreferencePages/DlgSettings3DView.ui @@ -281,31 +281,6 @@ but slower response to any scene changes. View - - - None - - - - - Line Smoothing - - - - - MSAA 2x - - - - - MSAA 4x - - - - - MSAA 8x - - diff --git a/src/Gui/PreferencePages/DlgSettings3DViewImp.cpp b/src/Gui/PreferencePages/DlgSettings3DViewImp.cpp index ea0ad8abce..9fc095a637 100644 --- a/src/Gui/PreferencePages/DlgSettings3DViewImp.cpp +++ b/src/Gui/PreferencePages/DlgSettings3DViewImp.cpp @@ -25,6 +25,9 @@ #ifndef _PreComp_ # include # include +# include +# include +# include #endif #include @@ -45,6 +48,7 @@ DlgSettings3DViewImp::DlgSettings3DViewImp(QWidget* parent) , ui(new Ui_DlgSettings3DView) { ui->setupUi(this); + addAntiAliasing(); } DlgSettings3DViewImp::~DlgSettings3DViewImp() = default; @@ -93,12 +97,79 @@ void DlgSettings3DViewImp::loadSettings() loadMarkerSize(); } +namespace { +class GLFormatCheck { +public: + GLFormatCheck() { + context.setFormat(format); + context.create(); + offscreen.setFormat(format); + offscreen.create(); + context.makeCurrent(&offscreen); + } + + bool testSamples(int num) { + QOpenGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QOpenGLFramebufferObject::Depth); + fboFormat.setSamples(num); + QOpenGLFramebufferObject fbo(100, 100, fboFormat); // NOLINT + return fbo.format().samples() == num; + } + +private: + QSurfaceFormat format; + QOpenGLContext context; + QOffscreenSurface offscreen; +}; +} + +void DlgSettings3DViewImp::addAntiAliasing() +{ + QString none = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "None"); + QString line = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "Line Smoothing"); + QString msaa2x = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "MSAA 2x"); + QString msaa4x = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "MSAA 4x"); + QString msaa6x = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "MSAA 6x"); + QString msaa8x = QCoreApplication::translate("Gui::Dialog::DlgSettings3DView", "MSAA 8x"); + ui->comboAliasing->clear(); + ui->comboAliasing->addItem(none, int(Gui::View3DInventorViewer::None)); + ui->comboAliasing->addItem(line, int(Gui::View3DInventorViewer::Smoothing)); + + // Do the samples checks only once + static std::vector> modes; + static bool formatCheck = true; + if (formatCheck) { + formatCheck = false; + + GLFormatCheck check; + // NOLINTBEGIN + if (check.testSamples(2)) { + modes.emplace_back(msaa2x, int(Gui::View3DInventorViewer::MSAA2x)); + } + if (check.testSamples(4)) { + modes.emplace_back(msaa4x, int(Gui::View3DInventorViewer::MSAA4x)); + } + if (check.testSamples(6)) { + modes.emplace_back(msaa6x, int(Gui::View3DInventorViewer::MSAA6x)); + } + if (check.testSamples(8)) { + modes.emplace_back(msaa8x, int(Gui::View3DInventorViewer::MSAA8x)); + } + // NOLINTEND + } + + for (const auto& it : modes) { + ui->comboAliasing->addItem(it.first, it.second); + } +} + void DlgSettings3DViewImp::saveAntiAliasing() { ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/View"); - int aliasing = ui->comboAliasing->currentIndex(); + int index = ui->comboAliasing->currentIndex(); + int aliasing = ui->comboAliasing->itemData(index).toInt(); hGrp->SetInt("AntiAliasing", aliasing); } @@ -108,8 +179,10 @@ void DlgSettings3DViewImp::loadAntiAliasing() ("User parameter:BaseApp/Preferences/View"); int aliasing = int(hGrp->GetInt("AntiAliasing", int(Gui::View3DInventorViewer::None))); - aliasing = Base::clamp(aliasing, 0, ui->comboAliasing->count()-1); - ui->comboAliasing->setCurrentIndex(aliasing); + int index = ui->comboAliasing->findData(aliasing); + if (index != -1) { + ui->comboAliasing->setCurrentIndex(index); + } // connect after setting current item of the combo box connect(ui->comboAliasing, qOverload(&QComboBox::currentIndexChanged), @@ -191,6 +264,7 @@ void DlgSettings3DViewImp::changeEvent(QEvent *e) ui->comboAliasing->blockSignals(true); int aliasing = ui->comboAliasing->currentIndex(); ui->retranslateUi(this); + addAntiAliasing(); ui->comboAliasing->setCurrentIndex(aliasing); ui->comboAliasing->blockSignals(false); } diff --git a/src/Gui/PreferencePages/DlgSettings3DViewImp.h b/src/Gui/PreferencePages/DlgSettings3DViewImp.h index fccecdc39f..5d047f1152 100644 --- a/src/Gui/PreferencePages/DlgSettings3DViewImp.h +++ b/src/Gui/PreferencePages/DlgSettings3DViewImp.h @@ -57,6 +57,7 @@ protected: void changeEvent(QEvent *e) override; private: + void addAntiAliasing(); void saveAntiAliasing(); void loadAntiAliasing(); void saveRenderCache(); diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 62322b8683..671e6ee5b7 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -1963,6 +1963,8 @@ int View3DInventorViewer::getNumSamples() return 2; case View3DInventorViewer::MSAA4x: return 4; + case View3DInventorViewer::MSAA6x: + return 6; case View3DInventorViewer::MSAA8x: return 8; case View3DInventorViewer::Smoothing: diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index 221ad4ba95..eca7f186b0 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -117,11 +117,12 @@ public: */ //@{ enum AntiAliasing { - None, - Smoothing, - MSAA2x, - MSAA4x, - MSAA8x + None = 0, + Smoothing = 1, + MSAA2x = 2, + MSAA4x = 3, + MSAA6x = 5, + MSAA8x = 4 }; //@} From e839733d59374bf140109994e213a8f9ae7acd9d Mon Sep 17 00:00:00 2001 From: qewer33 Date: Sat, 16 Mar 2024 10:39:59 +0300 Subject: [PATCH 36/59] closes #12989; Re-arrange Start Page template buttons --- src/Mod/Start/StartPage/StartPage.py | 4 ++-- src/Mod/Start/StartPage/TranslationTexts.py | 6 ++++-- .../Start/StartPage/images/new_empty_file.png | Bin 1384 -> 6285 bytes 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Mod/Start/StartPage/StartPage.py b/src/Mod/Start/StartPage/StartPage.py index 7025843d2c..879d305298 100644 --- a/src/Mod/Start/StartPage/StartPage.py +++ b/src/Mod/Start/StartPage/StartPage.py @@ -554,13 +554,13 @@ def handle(): SECTION_NEW_FILE = "

" + TranslationTexts.get("T_NEWFILE") + "

" SECTION_NEW_FILE += "
    " - SECTION_NEW_FILE += build_new_file_card("empty_file") - SECTION_NEW_FILE += build_new_file_card("open_file") SECTION_NEW_FILE += build_new_file_card("parametric_part") SECTION_NEW_FILE += build_new_file_card("assembly") # SECTION_NEW_FILE += build_new_file_card("csg_part") SECTION_NEW_FILE += build_new_file_card("2d_draft") SECTION_NEW_FILE += build_new_file_card("architecture") + SECTION_NEW_FILE += build_new_file_card("empty_file") + SECTION_NEW_FILE += build_new_file_card("open_file") SECTION_NEW_FILE += "
" HTML = HTML.replace("SECTION_NEW_FILE", SECTION_NEW_FILE) diff --git a/src/Mod/Start/StartPage/TranslationTexts.py b/src/Mod/Start/StartPage/TranslationTexts.py index 8f6d1b428c..61d1099ce8 100644 --- a/src/Mod/Start/StartPage/TranslationTexts.py +++ b/src/Mod/Start/StartPage/TranslationTexts.py @@ -51,12 +51,14 @@ def get(handle): T_TEMPLATE_EMPTYFILE_DESC = translate("StartPage", "Create an empty FreeCAD file") T_TEMPLATE_OPENFILE_NAME = translate("StartPage", "Open File") T_TEMPLATE_OPENFILE_DESC = translate("StartPage", "Open an existing CAD file or 3D model") - T_TEMPLATE_PARAMETRICPART_NAME = translate("StartPage", "Standard Part") + T_TEMPLATE_PARAMETRICPART_NAME = translate("StartPage", "Parametric Part") T_TEMPLATE_PARAMETRICPART_DESC = translate( "StartPage", "Create a part with the Part Design workbench" ) T_TEMPLATE_ASSEMBLY_NAME = translate("StartPage", "Assembly") - T_TEMPLATE_ASSEMBLY_DESC = translate("StartPage", "Create an assembly project") + T_TEMPLATE_ASSEMBLY_DESC = translate( + "StartPage", "Create an assembly with the Assembly workbench" + ) # T_TEMPLATE_CSGPART_NAME = translate("StartPage", "CSG Part") # T_TEMPLATE_CSGPART_DESC = translate("StartPage", "Create a part with the Part workbench") T_TEMPLATE_2DDRAFT_NAME = translate("StartPage", "2D Draft") diff --git a/src/Mod/Start/StartPage/images/new_empty_file.png b/src/Mod/Start/StartPage/images/new_empty_file.png index 98a739e91a6c8b75c6bb0d070b1f00e3d455ebce..00ec1c5a176f435ac4c1931f1a54a949d6b4283b 100644 GIT binary patch literal 6285 zcmV;87;@){P)L~mntZDnqB9FWb+0000KbVXQnL3MO!Z*l-iVQY0_AX9W@X>Mh5 z=KJ?A0000XbVXQnQ*U*0V`TtnbaZe!FE46oZEay=E^T#lX=7+%Y-}!LdTD0kUH||W zuSrBfRCt{2olB1-S#`&M=SD{NOE8T0W=p)Dy0nSl6QE~#{X?ssjhe}6~j)`mVRYOO^ODevh%|WjA5#U`& zCcrs!&giCRWT8Z~#OEcV6NCcEazryAgUG=iAw%(rgYjh2quv0{q^0dzGFMYbjjwAO zDn8E))Zgif9|0aiBEUIUT#!%C=)*Zqr$|1<>lDe1gM%|YEG~!0!AV31z!5+Kc#ld? zCLO)+2-1@IhD>TgUlUwK*HjeD`II^DXtnp1KLWgs#PN!!crKWf=VW@0&(3jrhI3PV zHbt~V+yv1ATtUzrafySID{4lT!{dX;Ne`hzrA0%Fh6X1!>MNY9$oo0L&GGe|IWxYf zS9@Lkp#gsHz4s=ceDcX(sOq0N=U#{iX>rc(HT}Oys!F<^5W?dBsoHM-7YQ<*R<6ja zY%*zGIq6(kbS}?3SLD6R^VSu4kSy~q&%L-TNaliwQ*i_l35av322k*-1P>uN?R!-p zMElNZ=kb~IAAJ3fKKY05{qcwYib_QYb6h_ocQbr5W6l*{;0ksF?4VZkd+)tBdHM3? zKZ(c>hQJO19RfRCA3kWS9=cPsrRz~uoKq%6L!NaMxsp4@NkE(;qKH#_U?m_9#0f4v z$P#cac}Ui;3Yki@xs)suf{N|yg}N@j_Amebr9b)oXVbs^@t^+BKjK_Y$TGao@TKFt zNrQEH>%uqCK>>dG^5tKO$Pb)z{P2fAg?a%dQ$xkbr}Ii@B$^w8)YWCi?}|tAN*s0Z-4vSeCIpgIq;!x(?05D z)INF%1W+}8Qv(fpgdXUC9#qmnjgwKpquL_`5ViP)F)g-|q3Uc^vbcZ7_rL#5e)sYt zs!FFHeE96!?|!rXXPk2=V)nRK`cgS>0*HrkhSxy>R@Jvfgy+wnr~h1DE+xt~PELcr zZo9kECJC{m3gL3Gz!f4hggNLHB6HhDYD6oDTnsH}gNO%u6pLjT_z)4rY_1H|9JBx_ zac)ZHN}gQU1JZ}=1ulDPG8|+4fRgz@KlNPJo~P2qr*0!1Jd!mWzz_l-fBZ3U8?>}t zmSs#P69Af~870ogw2$q-PM*wNB65Y3OPtK&`V5gIsw)%IQX|w+OxK8Xap|Lows;1r z(JnH8O+=e`h5{&Y&ZNS0+T(qoEc!FVJ;4PCqKFFw0)_NszNe$-6&;`B(`|Qjl2GN% zK~9{MEZ240t!vxVKDK|=b_P_9OW=|8ZH+L;xzskdZ5AN62yN8Gy-85AD1u!&5#SN+ zKpNv<3J@RFK+oAU5P~u(DN&xH&V1+KJ= zE$A7@8Iql0<2}K|Zwg6c7B${PvUEU$h61FHl6Fp62E2#7phVpH;%QLnKw67>NrU<+ zL8o-nDb%L}cpa?+?dAh=bCW3L5V8XI(Jjaj6Hg-(ra$=@A{aA`o#LCz652hM;g zNQoIxIV>`U(Jz(`v;!tFWW5yS(^BzXDKbiv^V*@VMYI8#lDV4TOC&4tQj&!USCl-F ze4RyobPispgNNJh0^%|BuV90oEMW^+Sny(LJaxv|vIHKc#%P>_TmZ(&2J!?nk6fG_ z;pjt4YkfCD)y@@^lNOh0fx0vEzr2AfQWB&@T#59S+z_=qw26%41H6syLkDl=W(^SC z8;OAomw8*4-umd493DXjhqwf01UW5ohR778z=}Nt3!L;oi-u|`zLN)~0dZ&KMN8(G z*a4V=)QC(BT%8bfVu_7V;Iu%d1+VIY&#k__d)2|m0(@KFa2pXbRMW!3kF|is0%(mc z5HTz%=a{}tIGh6)h+NpZoM9Gq9wS~MisuG#IjRP(xEL*?2TDYyxU9`VO4J#sFfovw z<(%;aAu9+{P==hG+|2dc030>YF&xNMZTsrr!;0x1k$_3Ko)n2Pk-hvc$y1>RGDM;= zB?;uHHsu1iz_@uD#k9jXxG|%O+{&~xSiAVB7D@;OnRB_xcquSztpLr*BqvDBH}^RO z8Lj5Hq{wfJ>681xsqN4@cz@6>q$ttK6;VXvZ%i173ya8o_OylYTk!Y9+X(c0&A)4XD5mG5o<|sOc$00Z_l0SBxP9mZc?F;TP10S-8Fs18G z8KBgj+PrZzH^PuHm?1LYXeQC{eHuk}0W?u8n-9Ejqa?aYvOK1kWxcGeMjXL8#M`>y z$ju%^Eark^mk8iR}r$VeQUoCz>T0vTr4-t66ll_O6fmI8KU75Ih+Y5 zsy`R?b2PeSP?M+W7{hGSwe*u!3B+O;06cQ;6xsHj>yw zt}kjMYs?kjF#cT*r1VspF zh%+R_+KPA~zXjgDeUC@+gV!bQ9|E|y`c%Ll%nU7A$wHiH z4X!ZEqyu#^$Wg_mj@z}-inbJ^YELSgM%5!~CcdgCXjqipDgk^TC(y^KFmJPR>dVO@ zS%(Dopstc+Q4eO(I*ve!L)Ac5b%@L2Zw_(h$r9KEZWB|0IRvo>Kx^y)@wSatSoWxj zR&;Co55cyuvaO`j5TTDxma%?@r;}h@P2fN9ynFlhPVOcp8siI}Ch%b~0uh^1B$7qT zIK>X4l^feMWJuO<6Icz^(#26B1uty>7|Ei;xVHwKgVYc#o1|*TRBcgwK(s~atU`d! z*jsue^r$z{p?D_J)8ToSl`|kt5~^IqsmBI)Ea<8c+*Q9W{T68SQ;`X(ZLGtJ!6q$O zxK2vct(GxDqlg!coY`O?S7?}Fx#T+P=o$?T8dAboTSHt8Xbl}INjLOp*F)CxWWn9H z=-!R2Q|aaPeJN;Bpqk-a%;mBSU}$IzWQ_p{nLsQISY(AH>G5cIEx0!hu@16H+E~zo z3UrP}y{w@ogpOdGTcb3fEl!&Vwk7^WT0-C1jVicC;q%%46QLmr`u`%4tdAETdBI$#$atDcsVWq!_e!F>>l{U)9U;_6 zFd44eAllfICN1=q{6|^>9baJWl+%05CXGopoJ>rI0&x>m3xo+O6VM_8Zom>Ih%2Lo zo7^0$%QA4I->W@Hi)tP9_7LEqVYUR>Dmp6KHc&TEmQkq_>RQ@l zy050ASkc2u$$jahMwdizr9`O>TsLLm(`pB@L%d5 zG=wk*ofFi+6|Hbu5u_$i6Os|Iz8(wiv1DiL|9!XF)_${JwIn6nO~fX!mQz*4*>o0l z7(=H0)saeZtlA@`6O&afk;x*!k&9Iw=ox|)4^)*1vB{YZTk6v1mgJtU#cQ0DIcT z)o8dv{1tG8(-}eL1er5+Gki1WoH;)_@!X_M9Q^9lD?a@2L!Lc*#yju4v-Mc_W>M=o z3!Td{GPoApkbx^m;YOu1&R1_PlcX7nz#^udSxr`|pXLIjq-t{JmD04XLj5J^6;7{c zb;cwZH-~mk!<=(YKfBNdNN){h2!UVx;uk!B{+zaLY1@{vEEoO965X5H*Y{;0Pq5J9 zpb$V}A|fW{4WQftAfW-boFA+ULrSAqCW`5l$VhKmitZWbpe5BjqpAW`O@;6osxyLK zF$tFhKO=Wnw9NS2_?i9(x1xbtqPOpRE-o(k(wDx(#l;2AIi}Mov)SzAdU;!)Q5iut zGJs6PE)x2hv5a^Xq}nPCNq5H-pbTdhT7q9=^i0WdT4JTPB~@)4TvbIy&`W~OaN!F4 z6}`J6)H6Qdig(AqdFLb|x-3gxym*0#kmorsUcBJdt5*-k=vvjMdIpOGJ}kkFi3bee zMQ}vZ)X_8vE#-Q{a(}gB*aUEkbE`~2E>I@S=Na?4XI{Gs={`ebhWHtoyCT$A{FY1J z-R(tbN6iNh>Fu}QrYuX&&(E37W^`S5t^b(d?#s=}Igs8&80)TL%-W({HXBj3LBysJ ziSvZeBa4Lo@@NKb)Wd4^SV|esQ&Ker^LgO%v!cTJPeJD7*^I85@svxR?*10Mg9E&Y zdy@W5|95!>!>KLmW&e4!l%w926roBJ%u2JKW5mlubWGZ_w4xVcelce_M?-GO4S52T z%;!0?%Jcir@;SZxjG~z{WyX{%M$G14MF-cx=~YRaXq$_4jNr(&_Dy{j1c_OvP}#4f zl2^+P4Fe)3mInYYPGClsiD-LZlqsraMbE2(`P}pICuN0eKIM!#FdJ)7t)hFI*|4tf zSm55x=mIl}-ip5K!~z)_!WGWNsq3B$GS6Pwmr3&UcJp3NNL%kn66Fn>T5xgMe$rR)Z~^gY*ix(;0d!?&ebgE zlUI}ZA;5(PSoprrwwt!um7B+%%SFTJ3?;>%hxJtPm6SRV(aBqi#Q}>Fp!hnD#KB$H za80b=k&ec}>q3@^#)K+2(S309ioLK{atSCJx;QH_g*Q`zA^O3RqF@e{%<8*biA@|| zALhLaJ&0F099j<_Urevu$}H-^UriaLYmI|{F18(!?xm`7lZ*6Fz}=6dR~^HSGvr4^gz3=qN_kSW!LJ(Av~@YKwZ)c4Hs#Q|jT9 z1v>T}yhDP$OWT;fZQE`ei<|w&(7{KU;lGXU1MFSeQu|#+bvnjaiE#|hzL&{o>-rwL z(RCLZE1q8qEBaV3C0*BdY@=&T|4n)b;u#JsqSI$-#?nIod(-i^iP%lFt9VZIpg9<> zhubz)c-*9YMfIk>V;Eig&^oTY+5X^lFdRlakKN}k7Va)>yPj{8cpmfm#zWG{LzRh! zH+u?Ez3=vSc__DieCrf+lJ{f*^nK4qAANM~Gdrv3A?f8qxk+QL@mO>c4pvp(fB${* zJf|oMzV)qdU7OjkjvkU;9@D?8<$MZ|PZFxE>zX{z`N~(m!gM+%gg{Xg8^4pZiH-&K zrv1lU=gqkCs{Yqn2gB+cn@*=Zd-ja;^K;&M>n)xP=kx z+Qy>SnfoHb*T4RC0G>a8j)*Xu%|;*h*kG^f--Tm$&HtVfE&J8 z$B5@sfqZXn66`0E#~f>y^BltHTDSc+@q7v+>tt@yE_6(=H*I(5sMaZ>WZURuuW;Cx z?#ktN*?%8^?%SxkN%xv;xCf1G3xvl@TbCV8P z&%23EF-ZFwRIB#APcA-rP1Z4KlfFOHaX0onplxyNecZ3tHqZfY9>DA95Ek_*=(P=1-UkiuZOnb{y8SWeC`Q)n4)R!F z_Py6r0{;hRMGtA8f!+rAL!+Z{@O^{54!lEL)4JC$Y}@8O=(RVpjwSB49e0-p`7s|n zf=;2g4kdmck{Ew*I{4I<=PqE3;nZFBd2NgCHqCHY6}R1G7UEs5ZFufYIo)C3s{gy3 zbJPD*(49G0M4X6BMC2@l;2y$F?RxHG=+L_y(|ezb$U&s*-W)9Ux$N|buN=tLK1Fgb zhSnJFT({4dzFnlu7%O`dbzN5i7h>-x?o{`C+dJ$5u-IM3kL!}N{Paj3LU3c=_`Wag z1LV5>xAm<9InkJ6_jP^S!0!8gg1v&a2eJrstExLE+;;*vij4&B@V5Yp5P~~I9`ukv z-c{d@t-l((&6xeX_Y>?ZnH{D^Ta33_)$yOyf!#TH$gz_fOA$hlAqR^H)9G~OAcYVJ zA&_Mmd2W|+bKg}oHXnPJyzQY3zA<8c&Dg|O!6R{59705N7fv1xaMZbJ$yY4#`)+V` zb+x$o^YinS$4f57E-AM$^iU#5t7zMOrcVuZUDspdnLfaj#yx#H?eGAze45;i^|WMd z+x}Oc=imLwPkzGpzV|(zKYzacpbsFW2B*J{n-Aj8e)cn(rUBsNk3aq;*T?qec#kF- z`^1Y#{KV$9r5vt*^{Zd~+IPSE-M@ZGjWTMu-<`OR;B{i|R7>aV=_AM9}4M>6vB`TXyH{_~&z4e8S@x0f{L zqiHk;!$J=)jH(;!!zKUnm%sewr%#`LA)Z4-Bt+xk51Pb2DvXGIu|q_HsT>`-sU1X15l7Jp^F`hjo7cX80uOhgAL3I6rum>lH z1+#}l@Fapes3$cb{(!8`bmx7qyE@f9J#HX9RYLa%ZMQwu)Ahc7@71fXrin6S$dG0< zkOhp4jC|kN*!UeE4)B)aZI`#@=H}+rm6eqe8H7;+#>dC=<#PE~@(i!;OhZGdsj2Ci zNfL|_FgiN=C+`NjySr&-W`=rud#R(NgZB3Js8A@->FKGEaCLR{q*yFokO4H3A;9g) zH-2tzj{5ui#drY6OG`^ai0iuf&d$ymG6Z7;_%Tlt6BE?a(?hqnw{(1bOh-pYba{Cx zZ6+rtRr8<75R4IkCyPCvGH`x=PL)c99LJ%HiwoInUXdFb8p@FY7$ZQTy}e!bFjxV{ z>VFj$IR+VkaRMG1G6E>&$Oxd6!x{lx@zFzoH3GOUh&?uqX0iwv4%=PVAzQFU0E|cR z$c4=n^(z|66L31w!;-x7r+K{WzkANic`{hl>M`k&b4?=Ng@Y`m(~ zAhr^OddnFa0_M+D2jc*UNKIVm1`f9*ifVR*aKbKA?4R|s}0w0Cw=T#oSohWdpN?53z zo}LcaOB%NyK2$xiBmu**=n5NP3v7xjh+>7g;o;#tyZts75qU}&Yinz=-+KL85+*yQ z5HR}krJU!Yg^~u0=TLdJwY61>eimB^q9L>OId|*_@_xLKIJ|sNLCIUs$xxBEU)UR- z#2F9V>vx-*o6EdgrUbxseW=z20s2@T#rpcX5!u+PAOfU%I;__VwqTb4t)jC9d!lF{ zh@zTHsVJ(?6KuhL4p1{hORyRO6q)wgfb|ea@6uS>Lm(IiQ|kf#EHJeM1a$z6Piqo% z)1Cx%UCjVUhq*mXzB3?TZ)G)zw9< zt*!L+Q*XTj8Xgd*-qs7oLV%50WsOnPX1p-*#pB1aDZn%EnU7aw2u)-N2L}f`eSLl7 zZ0KiDnt7d0UYx+f%l`g8&Ck!%$;pWbnB3;aQmOPe8GzBBYko{^zE8ca7i_*ZhecTR q_84M*_4@j{VCrS53>h+H0R91@iqc8Kuiioc0000 Date: Fri, 15 Mar 2024 09:37:08 +0100 Subject: [PATCH 37/59] Update issue-metrics.yml change from full last month to the 15th as report date. --- .github/workflows/issue-metrics.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issue-metrics.yml b/.github/workflows/issue-metrics.yml index 76c947c4f2..0286a31d06 100644 --- a/.github/workflows/issue-metrics.yml +++ b/.github/workflows/issue-metrics.yml @@ -19,7 +19,7 @@ jobs: shell: bash run: | # Calculate the first day of the previous month - first_day=$(date -d "last month" +%Y-%m-01) + first_day=$(date -d "last month" +%Y-%m-15) # Calculate the last day of the previous month last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d) @@ -40,4 +40,4 @@ jobs: title: Monthly issue metrics report token: ${{ secrets.GITHUB_TOKEN }} content-filepath: ./issue_metrics.md - assignees: maxwxyz \ No newline at end of file + assignees: maxwxyz From 8e3e3cbeccffddb485c72cac296b514e58d4449a Mon Sep 17 00:00:00 2001 From: Max Wilfinger <6246609+maxwxyz@users.noreply.github.com> Date: Fri, 15 Mar 2024 10:02:46 +0100 Subject: [PATCH 38/59] also include closed as not planned --- .github/workflows/issue-metrics.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-metrics.yml b/.github/workflows/issue-metrics.yml index 0286a31d06..239d0e92db 100644 --- a/.github/workflows/issue-metrics.yml +++ b/.github/workflows/issue-metrics.yml @@ -32,7 +32,7 @@ jobs: uses: github/issue-metrics@v2 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }} -reason:"not planned"' + SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }}' - name: Create issue uses: peter-evans/create-issue-from-file@v4 From 3dc2c2c7a7c38afc3f86f56b7105c06da3e637ea Mon Sep 17 00:00:00 2001 From: Max Wilfinger <6246609+maxwxyz@users.noreply.github.com> Date: Sat, 16 Mar 2024 07:25:30 +0100 Subject: [PATCH 39/59] only run on FreeCAD repo --- .github/workflows/issue-metrics.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/issue-metrics.yml b/.github/workflows/issue-metrics.yml index 239d0e92db..e080fbf405 100644 --- a/.github/workflows/issue-metrics.yml +++ b/.github/workflows/issue-metrics.yml @@ -12,6 +12,7 @@ jobs: build: name: issue metrics runs-on: ubuntu-latest + if: github.repository_owner == 'FreeCAD' steps: From b19ac278f3a2e3306f22bdc2b1207cfbad7e1066 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:30:01 +0100 Subject: [PATCH 40/59] FEM: Update solver.py --- src/Mod/Fem/femsolver/calculix/solver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Mod/Fem/femsolver/calculix/solver.py b/src/Mod/Fem/femsolver/calculix/solver.py index 3ccc833dcc..b22099ab28 100644 --- a/src/Mod/Fem/femsolver/calculix/solver.py +++ b/src/Mod/Fem/femsolver/calculix/solver.py @@ -212,6 +212,10 @@ def add_attributes(obj, ccx_prefs): niter = ccx_prefs.GetInt("AnalysisMaxIterations", 200) obj.IterationsMaximum = niter + if hasattr(obj, "IterationsThermoMechMaximum"): + obj.IterationsMaximum = obj.IterationsThermoMechMaximum + obj.removeProperty("IterationsThermoMechMaximum") + if not hasattr(obj, "BucklingFactors"): obj.addProperty( "App::PropertyIntegerConstraint", From edbab63c916d74eb31df922a27a5878a02efda68 Mon Sep 17 00:00:00 2001 From: Florian Foinant-Willig Date: Fri, 22 Mar 2024 23:18:22 +0100 Subject: [PATCH 41/59] [Gui] Allow to create ExpLineEdit with python --- src/Gui/resource.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Gui/resource.cpp b/src/Gui/resource.cpp index a5746990a6..05304998a6 100644 --- a/src/Gui/resource.cpp +++ b/src/Gui/resource.cpp @@ -124,5 +124,6 @@ WidgetFactorySupplier::WidgetFactorySupplier() new WidgetProducer; new WidgetProducer; new WidgetProducer; + new WidgetProducer; } // clang-format on From 300bff8b4a96b0d8ba8e7be7e1abba00cabf176a Mon Sep 17 00:00:00 2001 From: bgbsww Date: Fri, 22 Mar 2024 13:13:41 -0400 Subject: [PATCH 42/59] Toponaming/Part: Additional testing for attacher --- tests/src/Mod/Part/App/Attacher.cpp | 252 +++++++++++++++++++++++++++- 1 file changed, 249 insertions(+), 3 deletions(-) diff --git a/tests/src/Mod/Part/App/Attacher.cpp b/tests/src/Mod/Part/App/Attacher.cpp index f279e185a2..4ce0429739 100644 --- a/tests/src/Mod/Part/App/Attacher.cpp +++ b/tests/src/Mod/Part/App/Attacher.cpp @@ -12,9 +12,11 @@ using namespace Attacher; using namespace PartTestHelpers; /* - * Testing note: It looks like there are about 45 different attachment modes, and these tests all - * only look at one of them - to prove that adding elementMap code doesn't break anything. A - * comprehensive test of the Attacher would definitely want to try many more code paths. + * Testing note: It looks like there are about 45 different attachment modes, and these tests + * mostly only look at some of them - to prove that adding elementMap code doesn't break anything. + * While a trivial bounding box test is used to ensure no hard crashes in any of the modes, any + * mode that requires additional shapes beyond a couple of boxes would need a more comprehensive + * test. */ class AttacherTest: public ::testing::Test, public PartTestHelpers::PartTestHelperClass @@ -105,3 +107,247 @@ TEST_F(AttacherTest, TestCalculateAttachedPlacement) EXPECT_EQ(placement.getPosition().y, 0); EXPECT_EQ(placement.getPosition().z, 0); } + +TEST_F(AttacherTest, TestAllStringModesValid) +{ + // Arrange + const char* modes[] = { + "Deactivated", + "Translate", + "ObjectXY", + "ObjectXZ", + "ObjectYZ", + "FlatFace", + "TangentPlane", + "NormalToEdge", + "FrenetNB", + "FrenetTN", + "FrenetTB", + "Concentric", + "SectionOfRevolution", + "ThreePointsPlane", + "ThreePointsNormal", + "Folding", + + "ObjectX", + "ObjectY", + "ObjectZ", + "AxisOfCurvature", + "Directrix1", + "Directrix2", + "Asymptote1", + "Asymptote2", + "Tangent", + "Normal", + "Binormal", + "TangentU", + "TangentV", + "TwoPointLine", + "IntersectionLine", + "ProximityLine", + + "ObjectOrigin", + "Focus1", + "Focus2", + "OnEdge", + "CenterOfCurvature", + "CenterOfMass", + "IntersectionPoint", + "Vertex", + "ProximityPoint1", + "ProximityPoint2", + + "AxisOfInertia1", + "AxisOfInertia2", + "AxisOfInertia3", + + "InertialCS", + + "FaceNormal", + + "OZX", + "OZY", + "OXY", + "OXZ", + "OYZ", + "OYX", + }; + int index = 0; + for (auto mode : modes) { + _boxes[1]->MapMode.setValue(mode); // There are lots of attachment modes! + _boxes[1]->recomputeFeature(); + EXPECT_STREQ(_boxes[1]->MapMode.getValueAsString(), mode); + EXPECT_EQ(_boxes[1]->MapMode.getValue(), index); + index++; + } +} + +TEST_F(AttacherTest, TestAllModesBoundaries) +{ + _boxes[1]->MapMode.setValue(mmTranslate); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 1, 2, 3))); + _boxes[1]->MapMode.setValue(mmObjectXY); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 1, 2, 3))); + _boxes[1]->MapMode.setValue(mmObjectXZ); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, -3, 0, 1, 0, 2))); + _boxes[1]->MapMode.setValue(mmObjectYZ); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + + _boxes[1]->MapMode.setValue(mmFlatFace); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mmTangentPlane); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Normal); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + + _boxes[1]->MapMode.setValue(mmFrenetNB); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mmFrenetTN); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mmFrenetTB); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mmConcentric); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mmRevolutionSection); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mmThreePointsNormal); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mmThreePointsPlane); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mmFolding); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + + _boxes[1]->MapMode.setValue(mm1AxisX); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1AxisY); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1AxisZ); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1AxisCurv); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Directrix1); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Directrix2); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Asymptote1); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Asymptote2); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Tangent); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1TangentU); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1TangentV); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1TwoPoints); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Intersection); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Proximity); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + + _boxes[1]->MapMode.setValue(mm0Origin); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm0Focus1); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm0Focus2); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm0OnEdge); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm0CenterOfCurvature); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm0CenterOfMass); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1Intersection); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm0Vertex); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm0ProximityPoint1); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm0ProximityPoint2); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + + _boxes[1]->MapMode.setValue(mm1AxisInertia1); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1AxisInertia2); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + _boxes[1]->MapMode.setValue(mm1AxisInertia3); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE(boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0, 0, 0, 3, 1, 2))); + + _boxes[1]->MapMode.setValue(mmInertialCS); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE( + boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5))); + + _boxes[1]->MapMode.setValue(mm1FaceNormal); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE( + boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5))); + + _boxes[1]->MapMode.setValue(mmOZX); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE( + boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5))); + _boxes[1]->MapMode.setValue(mmOZY); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE( + boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5))); + _boxes[1]->MapMode.setValue(mmOXY); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE( + boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5))); + _boxes[1]->MapMode.setValue(mmOXZ); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE( + boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5))); + _boxes[1]->MapMode.setValue(mmOYZ); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE( + boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5))); + _boxes[1]->MapMode.setValue(mmOYX); + _boxes[1]->recomputeFeature(); + EXPECT_TRUE( + boxesMatch(_boxes[1]->Shape.getBoundingBox(), Base::BoundBox3d(0.5, 1, 1.5, 3.5, 2, 3.5))); +} From 6337b454e1ed771d8332b4bdedaaeec79740c7f4 Mon Sep 17 00:00:00 2001 From: wandererfan Date: Fri, 22 Mar 2024 18:22:47 -0400 Subject: [PATCH 43/59] [TD]protect against bad pref value - this is a temporary measure to prevent problems caused by a bad value for LineStandard parameter. A previous devel version stored on invalid value. This patch can be removed before moving to production. - this condition can be corrected by editing LineStandard to 0, 1 or 2. a plethora of warning messages is issued until the parameter is corrected. --- src/Mod/TechDraw/App/LineGenerator.cpp | 11 +++++++++++ src/Mod/TechDraw/App/Preferences.cpp | 14 +++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Mod/TechDraw/App/LineGenerator.cpp b/src/Mod/TechDraw/App/LineGenerator.cpp index 21952ffd55..8770c137f1 100644 --- a/src/Mod/TechDraw/App/LineGenerator.cpp +++ b/src/Mod/TechDraw/App/LineGenerator.cpp @@ -362,6 +362,17 @@ std::string LineGenerator::getLineStandardsBody() { int activeStandard = Preferences::lineStandard(); std::vector choices = getAvailableLineStandards(); + if (activeStandard < 0 || + (size_t) activeStandard >= choices.size()) { + // there is a condition where the LineStandard parameter exists, but is -1 (the + // qt value for no current index in a combobox). This is likely caused by an old + // development version writing an unvalidated value. In this case, the existing but + // invalid value will be returned. This is a temporary fix and can be removed for + // production. + // Preferences::lineStandard() will print a message about this every time it is called + // (lots of messages!). + activeStandard = 0; + } return getBodyFromString(choices.at(activeStandard)); } diff --git a/src/Mod/TechDraw/App/Preferences.cpp b/src/Mod/TechDraw/App/Preferences.cpp index ae35b68fa0..3517849a19 100644 --- a/src/Mod/TechDraw/App/Preferences.cpp +++ b/src/Mod/TechDraw/App/Preferences.cpp @@ -23,7 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include - +# include # include #endif @@ -422,6 +422,18 @@ bool Preferences::SectionUsePreviousCut() //! an index into the list of available line standards/version found in LineGroupDirectory int Preferences::lineStandard() { + // there is a condition where the LineStandard parameter exists, but is -1 (the + // qt value for no current index in a combobox). This is likely caused by an old + // development version writing an unvalidated value. In this case, the + // existing but invalid value will be returned. This is a temporary fix and + // can be removed for production. + // this message will appear many times if the parameter is invalid. + int parameterValue = getPreferenceGroup("Standards")->GetInt("LineStandard", 1); + if (parameterValue < 0) { + Base::Console().Warning(qPrintable(QApplication::translate( + "Preferences", "The LineStandard parameter is invalid. Using zero instead.", nullptr))); + return 0; + } return getPreferenceGroup("Standards")->GetInt("LineStandard", 1); } From c7ecfcee433b355f35858cafd4b47de1da6d28c6 Mon Sep 17 00:00:00 2001 From: wandererfan Date: Fri, 22 Mar 2024 18:23:35 -0400 Subject: [PATCH 44/59] [TD]fix no PAT hatch on first paint --- src/Mod/TechDraw/App/DrawViewSection.cpp | 24 ++++++++++-------------- src/Mod/TechDraw/Gui/QGIViewSection.cpp | 12 +++++++----- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawViewSection.cpp b/src/Mod/TechDraw/App/DrawViewSection.cpp index 01ab923556..1b9a53695b 100644 --- a/src/Mod/TechDraw/App/DrawViewSection.cpp +++ b/src/Mod/TechDraw/App/DrawViewSection.cpp @@ -385,7 +385,7 @@ TopoDS_Shape DrawViewSection::getShapeForDetail() const App::DocumentObjectExecReturn* DrawViewSection::execute() { - // Base::Console().Message("DVS::execute() - %s\n", getNameInDocument()); + // Base::Console().Message("DVS::execute() - %s\n", Label.getValue()); if (!keepUpdated()) { return App::DocumentObject::StdReturn; } @@ -445,9 +445,7 @@ bool DrawViewSection::isBaseValid() const void DrawViewSection::sectionExec(TopoDS_Shape& baseShape) { - // Base::Console().Message("DVS::sectionExec() - %s baseShape.IsNull: - // %d\n", - // getNameInDocument(), baseShape.IsNull()); + // Base::Console().Message("DVS::sectionExec() - %s baseShape.IsNull: %d\n", Label.getValue(), baseShape.IsNull()); if (waitingForHlr() || waitingForCut()) { return; @@ -486,9 +484,7 @@ void DrawViewSection::sectionExec(TopoDS_Shape& baseShape) void DrawViewSection::makeSectionCut(const TopoDS_Shape& baseShape) { - // Base::Console().Message("DVS::makeSectionCut() - %s - baseShape.IsNull: - // %d\n", - // getNameInDocument(), baseShape.IsNull()); + // Base::Console().Message("DVS::makeSectionCut() - %s - baseShape.IsNull:%d\n", Label.getValue(), baseShape.IsNull()); showProgressMessage(getNameInDocument(), "is making section cut"); @@ -638,8 +634,7 @@ void DrawViewSection::onSectionCutFinished() // activities that depend on updated geometry object void DrawViewSection::postHlrTasks(void) { - // Base::Console().Message("DVS::postHlrTasks() - %s\n", - // getNameInDocument()); + // Base::Console().Message("DVS::postHlrTasks() - %s\n", Label.getValue()); DrawViewPart::postHlrTasks(); @@ -1172,8 +1167,10 @@ gp_Ax2 DrawViewSection::getProjectionCS(const Base::Vector3d pt) const std::vector DrawViewSection::getDrawableLines(int i) { - // Base::Console().Message("DVS::getDrawableLines(%d) - lineSets: %d\n", i, - // m_lineSets.size()); + // Base::Console().Message("DVS::getDrawableLines(%d) - lineSets: %d\n", i, m_lineSets.size()); + if (m_lineSets.empty()) { + makeLineSets(); + } std::vector result; return DrawGeomHatch::getTrimmedLinesSection(this, m_lineSets, @@ -1236,7 +1233,7 @@ void DrawViewSection::setupObject() // create geometric hatch lines void DrawViewSection::makeLineSets(void) { - // Base::Console().Message("DVS::makeLineSets()\n"); + // Base::Console().Message("DVS::makeLineSets()\n"); if (PatIncluded.isEmpty()) { return; } @@ -1277,8 +1274,7 @@ void DrawViewSection::replaceSvgIncluded(std::string newSvgFile) void DrawViewSection::replacePatIncluded(std::string newPatFile) { - // Base::Console().Message("DVS::replacePatIncluded(%s)\n", - // newPatFile.c_str()); + // Base::Console().Message("DVS::replacePatIncluded(%s)\n", newPatFile.c_str()); if (newPatFile.empty()) { return; } diff --git a/src/Mod/TechDraw/Gui/QGIViewSection.cpp b/src/Mod/TechDraw/Gui/QGIViewSection.cpp index 07f90a1a1a..86a623fb2f 100644 --- a/src/Mod/TechDraw/Gui/QGIViewSection.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewSection.cpp @@ -47,6 +47,7 @@ void QGIViewSection::draw() void QGIViewSection::drawSectionFace() { + // Base::Console().Message("QGIVS::drawSectionFace()\n"); auto section( dynamic_cast(getViewObject()) ); if (!section) { return; @@ -85,13 +86,14 @@ void QGIViewSection::drawSectionFace() return; } - QColor faceColor = (sectionVp->CutSurfaceColor.getValue()).asValue(); - faceColor.setAlpha((100 - sectionVp->CutSurfaceTransparency.getValue())*255/100); - newFace->setFillColor(faceColor); - if (section->CutSurfaceDisplay.isValue("Color")) { + newFace->isHatched(true); + QColor faceColor = (sectionVp->CutSurfaceColor.getValue()).asValue(); + faceColor.setAlpha((100 - sectionVp->CutSurfaceTransparency.getValue())*255/100); + newFace->setFillColor(faceColor); newFace->setFillMode(faceColor.alpha() ? QGIFace::PlainFill : QGIFace::NoFill); } else if (section->CutSurfaceDisplay.isValue("SvgHatch")) { + newFace->isHatched(true); newFace->setFillMode(QGIFace::SvgFill); newFace->setHatchColor(sectionVp->HatchColor.getValue()); newFace->setHatchScale(section->HatchScale.getValue()); @@ -104,9 +106,9 @@ void QGIViewSection::drawSectionFace() newFace->setFillMode(QGIFace::GeomHatchFill); newFace->setHatchColor(sectionVp->GeomHatchColor.getValue()); newFace->setHatchScale(section->HatchScale.getValue()); - newFace->setLineWeight(sectionVp->WeightPattern.getValue()); newFace->setHatchRotation(section->HatchRotation.getValue()); newFace->setHatchOffset(section->HatchOffset.getValue()); + newFace->setLineWeight(sectionVp->WeightPattern.getValue()); std::vector lineSets = section->getDrawableLines(i); if (!lineSets.empty()) { newFace->clearLineSets(); From 961e547161549682eb45b27d5f335630230e4e4b Mon Sep 17 00:00:00 2001 From: Marco Patzer Date: Fri, 22 Mar 2024 10:42:49 +0100 Subject: [PATCH 45/59] Add leading plus (+) in hole/shaft fit limit ISO 286/14405-1 calls for a plus sign if the limit is positive and a minus sign if the limit is negative. A zero limit should have neither a plus nor a minus sign. This commit adds the plus sign. --- src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py b/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py index a96aa0062e..9c085c06a3 100644 --- a/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py +++ b/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py @@ -116,10 +116,20 @@ class TaskHoleShaftFit: mainFormat = dim.FormatSpec dim.FormatSpec = mainFormat+' '+selectedField dim.EqualTolerance = False - dim.FormatSpecOverTolerance = '(%-0.6w)' - dim.FormatSpecUnderTolerance = '(%-0.6w)' dim.OverTolerance = rangeValues[0] dim.UnderTolerance = rangeValues[1] + if dim.OverTolerance < 0: + dim.FormatSpecOverTolerance = '(%-0.6w)' + elif dim.OverTolerance > 0: + dim.FormatSpecOverTolerance = '(+%-0.6w)' + else: + dim.FormatSpecOverTolerance = '( %-0.6w)' + if dim.UnderTolerance < 0: + dim.FormatSpecUnderTolerance = '(%-0.6w)' + elif dim.UnderTolerance > 0: + dim.FormatSpecUnderTolerance = '(+%-0.6w)' + else: + dim.FormatSpecUnderTolerance = '( %-0.6w)' Gui.Control.closeDialog() def reject(self): From 897e9694772f33ed8b00329be9baadcf893825ec Mon Sep 17 00:00:00 2001 From: Marco Patzer Date: Fri, 22 Mar 2024 11:33:19 +0100 Subject: [PATCH 46/59] Code reformat (black) --- .../TechDrawTools/TaskHoleShaftFit.py | 836 ++++++++++++++++-- 1 file changed, 756 insertions(+), 80 deletions(-) diff --git a/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py b/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py index 9c085c06a3..96605f69e1 100644 --- a/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py +++ b/src/Mod/TechDraw/TechDrawTools/TaskHoleShaftFit.py @@ -35,53 +35,86 @@ import os translate = App.Qt.translate + class TaskHoleShaftFit: - def __init__(self,sel): + def __init__(self, sel): loose = translate("TechDraw_HoleShaftFit", "loose fit") snug = translate("TechDraw_HoleShaftFit", "snug fit") press = translate("TechDraw_HoleShaftFit", "press fit") self.isHole = True self.sel = sel - self.holeValues = [["h9","D10",loose],["h9","E9",loose],["h9","F8",loose],["h6","G7",loose], - ["c11","H11",loose],["f7","H8",loose],["h6","H7",loose],["h7","H8",loose], - ["k6","H7",snug],["n6","H7",snug],["r6","H7",press],["s6","H7",press], - ["h6","K7",snug],["h6","N7",snug],["h6","R7",press],["h6","S7",press]] - self.shaftValues = [["H11","c11",loose],["H8","f7",loose],["H7","h6",loose],["H8","h7",loose], - ["D10","h9",loose],["E9","h9",loose],["F8","h9",loose],["G7","h6",loose], - ["K7","h6",snug],["N7","h6",snug],["R7","h6",press],["S7","h6",press], - ["H7","k6",snug],["H7","n6",snug],["H7","r6",press],["H7","s6",press]] + self.holeValues = [ + ["h9", "D10", loose], + ["h9", "E9", loose], + ["h9", "F8", loose], + ["h6", "G7", loose], + ["c11", "H11", loose], + ["f7", "H8", loose], + ["h6", "H7", loose], + ["h7", "H8", loose], + ["k6", "H7", snug], + ["n6", "H7", snug], + ["r6", "H7", press], + ["s6", "H7", press], + ["h6", "K7", snug], + ["h6", "N7", snug], + ["h6", "R7", press], + ["h6", "S7", press], + ] + self.shaftValues = [ + ["H11", "c11", loose], + ["H8", "f7", loose], + ["H7", "h6", loose], + ["H8", "h7", loose], + ["D10", "h9", loose], + ["E9", "h9", loose], + ["F8", "h9", loose], + ["G7", "h6", loose], + ["K7", "h6", snug], + ["N7", "h6", snug], + ["R7", "h6", press], + ["S7", "h6", press], + ["H7", "k6", snug], + ["H7", "n6", snug], + ["H7", "r6", press], + ["H7", "s6", press], + ] self._uiPath = App.getHomePath() - self._uiPath = os.path.join(self._uiPath, "Mod/TechDraw/TechDrawTools/Gui/TaskHoleShaftFit.ui") + self._uiPath = os.path.join( + self._uiPath, "Mod/TechDraw/TechDrawTools/Gui/TaskHoleShaftFit.ui" + ) self.form = Gui.PySideUic.loadUi(self._uiPath) - self.form.setWindowTitle(translate("TechDraw_HoleShaftFit", "Hole / Shaft Fit ISO 286")) + self.form.setWindowTitle( + translate("TechDraw_HoleShaftFit", "Hole / Shaft Fit ISO 286") + ) - self.form.rbHoleBase.clicked.connect(partial(self.on_HoleShaftChanged,True)) - self.form.rbShaftBase.clicked.connect(partial(self.on_HoleShaftChanged,False)) + self.form.rbHoleBase.clicked.connect(partial(self.on_HoleShaftChanged, True)) + self.form.rbShaftBase.clicked.connect(partial(self.on_HoleShaftChanged, False)) self.form.cbField.currentIndexChanged.connect(self.on_FieldChanged) def setHoleFields(self): - '''set hole fields in the combo box''' + """set hole fields in the combo box""" for i in range(self.form.cbField.count()): self.form.cbField.removeItem(0) for value in self.holeValues: self.form.cbField.addItem(value[1]) - self.form.lbBaseField.setText(' '+self.holeValues[0][0]+" /") + self.form.lbBaseField.setText(" " + self.holeValues[0][0] + " /") self.form.lbFitType.setText(self.holeValues[0][2]) def setShaftFields(self): - '''set shaft fields in the combo box''' + """set shaft fields in the combo box""" for i in range(self.form.cbField.count()): self.form.cbField.removeItem(0) for value in self.shaftValues: self.form.cbField.addItem(value[1]) - self.form.lbBaseField.setText(' '+self.shaftValues[0][0]+" /") + self.form.lbBaseField.setText(" " + self.shaftValues[0][0] + " /") self.form.lbFitType.setText(self.shaftValues[0][2]) - def on_HoleShaftChanged(self,isHole): - '''slot: change the used base fit hole/shaft''' + def on_HoleShaftChanged(self, isHole): + """slot: change the used base fit hole/shaft""" if isHole: self.isHole = isHole self.setShaftFields() @@ -90,17 +123,21 @@ class TaskHoleShaftFit: self.setHoleFields() def on_FieldChanged(self): - '''slot: change of the desired field''' + """slot: change of the desired field""" currentIndex = self.form.cbField.currentIndex() if self.isHole: - self.form.lbBaseField.setText(' '+self.shaftValues[currentIndex][0]+" /") + self.form.lbBaseField.setText( + " " + self.shaftValues[currentIndex][0] + " /" + ) self.form.lbFitType.setText(self.shaftValues[currentIndex][2]) else: - self.form.lbBaseField.setText(' '+self.holeValues[currentIndex][0]+" /") + self.form.lbBaseField.setText( + " " + self.holeValues[currentIndex][0] + " /" + ) self.form.lbFitType.setText(self.holeValues[currentIndex][2]) def accept(self): - '''slot: OK pressed''' + """slot: OK pressed""" currentIndex = self.form.cbField.currentIndex() if self.isHole: selectedField = self.shaftValues[currentIndex][1] @@ -111,87 +148,726 @@ class TaskHoleShaftFit: dim = self.sel[0].Object value = dim.getRawValue() iso = ISO286() - iso.calculate(value,fieldChar,quality) + iso.calculate(value, fieldChar, quality) rangeValues = iso.getValues() mainFormat = dim.FormatSpec - dim.FormatSpec = mainFormat+' '+selectedField + dim.FormatSpec = mainFormat + " " + selectedField dim.EqualTolerance = False dim.OverTolerance = rangeValues[0] dim.UnderTolerance = rangeValues[1] if dim.OverTolerance < 0: - dim.FormatSpecOverTolerance = '(%-0.6w)' + dim.FormatSpecOverTolerance = "(%-0.6w)" elif dim.OverTolerance > 0: - dim.FormatSpecOverTolerance = '(+%-0.6w)' + dim.FormatSpecOverTolerance = "(+%-0.6w)" else: - dim.FormatSpecOverTolerance = '( %-0.6w)' + dim.FormatSpecOverTolerance = "( %-0.6w)" if dim.UnderTolerance < 0: - dim.FormatSpecUnderTolerance = '(%-0.6w)' + dim.FormatSpecUnderTolerance = "(%-0.6w)" elif dim.UnderTolerance > 0: - dim.FormatSpecUnderTolerance = '(+%-0.6w)' + dim.FormatSpecUnderTolerance = "(+%-0.6w)" else: - dim.FormatSpecUnderTolerance = '( %-0.6w)' + dim.FormatSpecUnderTolerance = "( %-0.6w)" Gui.Control.closeDialog() def reject(self): return True -class ISO286: - '''This class represents a subset of the ISO 286 standard''' - def getNominalRange(self,measureValue): - '''return index of selected nominal range field, 0 < measureValue < 500 mm''' - measureRanges = [0,3,6,10,14,18,24,30,40,50,65,80,100,120,140,160,180,200,225,250,280,315,355,400,450,500] +class ISO286: + """This class represents a subset of the ISO 286 standard""" + + def getNominalRange(self, measureValue): + """return index of selected nominal range field, 0 < measureValue < 500 mm""" + measureRanges = [ + 0, + 3, + 6, + 10, + 14, + 18, + 24, + 30, + 40, + 50, + 65, + 80, + 100, + 120, + 140, + 160, + 180, + 200, + 225, + 250, + 280, + 315, + 355, + 400, + 450, + 500, + ] index = 1 while measureValue > measureRanges[index]: - index = index+1 - return index-1 + index = index + 1 + return index - 1 - def getITValue(self,valueQuality,valueNominalRange): - '''return IT-value (value of quality in micrometers)''' - '''tables IT6 to IT11 from 0 to 500 mm''' - IT6 = [6,8,9,11,11,13,13,16,16,19,19,22,22,25,25,25,29,29,29,32,32,36,36,40,40] - IT7 = [10,12,15,18,18,21,21,25,25,30,30,35,35,40,40,40,46,46,46,52,52,57,57,63,63] - IT8 = [14,18,22,27,27,33,33,39,39,46,46,54,54,63,63,63,72,72,72,81,81,89,89,97,97] - IT9 = [25,30,36,43,43,52,52,62,62,74,74,87,87,100,100,100,115,115,115,130,130,140,140,155,155] - IT10 = [40,48,58,70,70,84,84,100,100,120,120,140,140,160,160,160,185,185,185,210,210,230,230,250,250] - IT11 = [60,75,90,110,110,130,130,160,160,190,190,220,220,250,250,250,290,290,290,320,320,360,360,400,400] - qualityTable = [IT6,IT7,IT8,IT9,IT10,IT11] - return qualityTable[valueQuality-6][valueNominalRange] + def getITValue(self, valueQuality, valueNominalRange): + """return IT-value (value of quality in micrometers)""" + """tables IT6 to IT11 from 0 to 500 mm""" + IT6 = [ + 6, + 8, + 9, + 11, + 11, + 13, + 13, + 16, + 16, + 19, + 19, + 22, + 22, + 25, + 25, + 25, + 29, + 29, + 29, + 32, + 32, + 36, + 36, + 40, + 40, + ] + IT7 = [ + 10, + 12, + 15, + 18, + 18, + 21, + 21, + 25, + 25, + 30, + 30, + 35, + 35, + 40, + 40, + 40, + 46, + 46, + 46, + 52, + 52, + 57, + 57, + 63, + 63, + ] + IT8 = [ + 14, + 18, + 22, + 27, + 27, + 33, + 33, + 39, + 39, + 46, + 46, + 54, + 54, + 63, + 63, + 63, + 72, + 72, + 72, + 81, + 81, + 89, + 89, + 97, + 97, + ] + IT9 = [ + 25, + 30, + 36, + 43, + 43, + 52, + 52, + 62, + 62, + 74, + 74, + 87, + 87, + 100, + 100, + 100, + 115, + 115, + 115, + 130, + 130, + 140, + 140, + 155, + 155, + ] + IT10 = [ + 40, + 48, + 58, + 70, + 70, + 84, + 84, + 100, + 100, + 120, + 120, + 140, + 140, + 160, + 160, + 160, + 185, + 185, + 185, + 210, + 210, + 230, + 230, + 250, + 250, + ] + IT11 = [ + 60, + 75, + 90, + 110, + 110, + 130, + 130, + 160, + 160, + 190, + 190, + 220, + 220, + 250, + 250, + 250, + 290, + 290, + 290, + 320, + 320, + 360, + 360, + 400, + 400, + ] + qualityTable = [IT6, IT7, IT8, IT9, IT10, IT11] + return qualityTable[valueQuality - 6][valueNominalRange] - def getFieldValue(self,fieldCharacter,valueNominalRange): - '''return es or ES value of the field in micrometers''' - cField = [-60,-70,-80,-95,-95,-110,-110,-120,-130,-140,-150,-170,-180,-200,-210,-230,-240,-260,-280,-300,-330,-360,-400,-440,-480] - fField = [-6,-10,-13,-16,-16,-20,-20,-25,-25,-30,-30,-36,-36,-43,-43,-43,-50,-50,-50,-56,-56,-62,-62,-68,-68] - gField = [-2,-4,-5,-6,-6,-7,-7,-9,-9,-10,-10,-12,-12,-14,-14,-14,-15,-15,-15,-17,-17,-18,-18,-20,-20] - hField = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - kField = [6,9,10,12,12,15,15,18,18,21,21,25,25,28,28,28,33,33,33,36,36,40,40,45,45] - nField = [10,16,19,23,23,28,28,33,33,39,39,45,45,52,52,60,60,66,66,73,73,80,80] - rField = [16,23,28,34,34,41,41,50,50,60,62,73,76,88,90,93,106,109,113,126,130,144,150,166,172] - sField = [20,27,32,39,39,48,48,59,59,72,78,93,101,117,125,133,151,159,169,190,202,226,244,272,292] - DField = [60,78,98,120,120,149,149,180,180,220,220,260,260,305,305,305,355,355,355,400,400,440,440,480,480] - EField = [39,50,61,75,75,92,92,112,112,134,134,159,159,185,185,185,215,215,215,240,240,265,265,290,290] - FField = [20,28,35,43,43,53,53,64,64,76,76,90,90,106,106,106,122,122,122,137,137,151,151,165,165] - GField = [12,16,20,24,24,28,28,34,34,40,40,47,47,54,54,54,61,61,61,69,69,75,75,83,83] - HField = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - KField = [0,3,5,6,6,6,6,7,7,9,9,10,10,12,12,12,13,13,13,16,16,17,17,18,18] - NField = [-4,-4,-4,-5,-5,-7,-7,-8,-8,-9,-9,-10,-10,-12,-12,-12,-14,-14,-14,-14,-14,-16,-16,-17,-17] - RField = [-10,-11,-13,-16,-16,-20,-20,-25,-25,-30,-32,-38,-41,-48,-50,-53,-60,-63,-67,-74,-78,-87,-93,-103,-109] - SField = [-14,-15,-17,-21,-21,-27,-27,-34,-34,-42,-48,-58,-66,-77,-85,-93,-105,-113,-123,-138,-150,-169,-187,-209,-229] - fieldDict = {'c':cField,'f':fField,'g':gField,'h':hField,'k':kField,'n':nField,'r':rField,'s':sField, - 'D':DField,'E':EField,'F':FField,'G':GField,'H':HField,'K':KField,'N':NField,'R':RField,'S':SField} + def getFieldValue(self, fieldCharacter, valueNominalRange): + """return es or ES value of the field in micrometers""" + cField = [ + -60, + -70, + -80, + -95, + -95, + -110, + -110, + -120, + -130, + -140, + -150, + -170, + -180, + -200, + -210, + -230, + -240, + -260, + -280, + -300, + -330, + -360, + -400, + -440, + -480, + ] + fField = [ + -6, + -10, + -13, + -16, + -16, + -20, + -20, + -25, + -25, + -30, + -30, + -36, + -36, + -43, + -43, + -43, + -50, + -50, + -50, + -56, + -56, + -62, + -62, + -68, + -68, + ] + gField = [ + -2, + -4, + -5, + -6, + -6, + -7, + -7, + -9, + -9, + -10, + -10, + -12, + -12, + -14, + -14, + -14, + -15, + -15, + -15, + -17, + -17, + -18, + -18, + -20, + -20, + ] + hField = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ] + kField = [ + 6, + 9, + 10, + 12, + 12, + 15, + 15, + 18, + 18, + 21, + 21, + 25, + 25, + 28, + 28, + 28, + 33, + 33, + 33, + 36, + 36, + 40, + 40, + 45, + 45, + ] + nField = [ + 10, + 16, + 19, + 23, + 23, + 28, + 28, + 33, + 33, + 39, + 39, + 45, + 45, + 52, + 52, + 60, + 60, + 66, + 66, + 73, + 73, + 80, + 80, + ] + rField = [ + 16, + 23, + 28, + 34, + 34, + 41, + 41, + 50, + 50, + 60, + 62, + 73, + 76, + 88, + 90, + 93, + 106, + 109, + 113, + 126, + 130, + 144, + 150, + 166, + 172, + ] + sField = [ + 20, + 27, + 32, + 39, + 39, + 48, + 48, + 59, + 59, + 72, + 78, + 93, + 101, + 117, + 125, + 133, + 151, + 159, + 169, + 190, + 202, + 226, + 244, + 272, + 292, + ] + DField = [ + 60, + 78, + 98, + 120, + 120, + 149, + 149, + 180, + 180, + 220, + 220, + 260, + 260, + 305, + 305, + 305, + 355, + 355, + 355, + 400, + 400, + 440, + 440, + 480, + 480, + ] + EField = [ + 39, + 50, + 61, + 75, + 75, + 92, + 92, + 112, + 112, + 134, + 134, + 159, + 159, + 185, + 185, + 185, + 215, + 215, + 215, + 240, + 240, + 265, + 265, + 290, + 290, + ] + FField = [ + 20, + 28, + 35, + 43, + 43, + 53, + 53, + 64, + 64, + 76, + 76, + 90, + 90, + 106, + 106, + 106, + 122, + 122, + 122, + 137, + 137, + 151, + 151, + 165, + 165, + ] + GField = [ + 12, + 16, + 20, + 24, + 24, + 28, + 28, + 34, + 34, + 40, + 40, + 47, + 47, + 54, + 54, + 54, + 61, + 61, + 61, + 69, + 69, + 75, + 75, + 83, + 83, + ] + HField = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ] + KField = [ + 0, + 3, + 5, + 6, + 6, + 6, + 6, + 7, + 7, + 9, + 9, + 10, + 10, + 12, + 12, + 12, + 13, + 13, + 13, + 16, + 16, + 17, + 17, + 18, + 18, + ] + NField = [ + -4, + -4, + -4, + -5, + -5, + -7, + -7, + -8, + -8, + -9, + -9, + -10, + -10, + -12, + -12, + -12, + -14, + -14, + -14, + -14, + -14, + -16, + -16, + -17, + -17, + ] + RField = [ + -10, + -11, + -13, + -16, + -16, + -20, + -20, + -25, + -25, + -30, + -32, + -38, + -41, + -48, + -50, + -53, + -60, + -63, + -67, + -74, + -78, + -87, + -93, + -103, + -109, + ] + SField = [ + -14, + -15, + -17, + -21, + -21, + -27, + -27, + -34, + -34, + -42, + -48, + -58, + -66, + -77, + -85, + -93, + -105, + -113, + -123, + -138, + -150, + -169, + -187, + -209, + -229, + ] + fieldDict = { + "c": cField, + "f": fField, + "g": gField, + "h": hField, + "k": kField, + "n": nField, + "r": rField, + "s": sField, + "D": DField, + "E": EField, + "F": FField, + "G": GField, + "H": HField, + "K": KField, + "N": NField, + "R": RField, + "S": SField, + } return fieldDict[fieldCharacter][valueNominalRange] - def calculate(self,value,fieldChar,quality): - '''calculate upper and lower field values''' - self.nominalRange = self. getNominalRange(value) - self.upperValue = self.getFieldValue(fieldChar,self.nominalRange) - self.lowerValue = self.upperValue-self.getITValue(quality,self.nominalRange) - if fieldChar == 'H': + def calculate(self, value, fieldChar, quality): + """calculate upper and lower field values""" + self.nominalRange = self.getNominalRange(value) + self.upperValue = self.getFieldValue(fieldChar, self.nominalRange) + self.lowerValue = self.upperValue - self.getITValue(quality, self.nominalRange) + if fieldChar == "H": self.upperValue = -self.lowerValue self.lowerValue = 0 def getValues(self): - '''return range values in mm''' - return (self.upperValue/1000,self.lowerValue/1000) - - + """return range values in mm""" + return (self.upperValue / 1000, self.lowerValue / 1000) From aac48eb2f9ff485af6e7f8b7b7d4977f70fb7789 Mon Sep 17 00:00:00 2001 From: pavltom Date: Sat, 16 Mar 2024 10:50:35 +0100 Subject: [PATCH 47/59] [TechDraw] Issue #5903 - Autofill template information --- src/Mod/TechDraw/App/DrawSVGTemplate.cpp | 33 ++++--- src/Mod/TechDraw/App/DrawTemplate.cpp | 67 ++++++++++++++ src/Mod/TechDraw/App/DrawTemplate.h | 13 +++ src/Mod/TechDraw/App/DrawUtil.cpp | 74 +++++++++++++++ src/Mod/TechDraw/App/DrawUtil.h | 4 + src/Mod/TechDraw/App/DrawViewSymbol.cpp | 4 +- src/Mod/TechDraw/App/XMLQuery.cpp | 3 +- src/Mod/TechDraw/Gui/Command.cpp | 89 ++++++++----------- src/Mod/TechDraw/Gui/QGISVGTemplate.cpp | 4 +- src/Mod/TechDraw/Gui/TaskProjGroup.cpp | 78 +--------------- src/Mod/TechDraw/Gui/TaskProjGroup.h | 1 - src/Mod/TechDraw/Templates/A4_LandscapeTD.svg | 10 +-- .../TechDraw/Templates/ANSIC_Landscape.svg | 12 +-- 13 files changed, 236 insertions(+), 156 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawSVGTemplate.cpp b/src/Mod/TechDraw/App/DrawSVGTemplate.cpp index fe36964dcc..abd145f84c 100644 --- a/src/Mod/TechDraw/App/DrawSVGTemplate.cpp +++ b/src/Mod/TechDraw/App/DrawSVGTemplate.cpp @@ -132,10 +132,10 @@ QString DrawSVGTemplate::processTemplate() query.processItems(QString::fromUtf8( "declare default element namespace \"" SVG_NS_URI "\"; " "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]/tspan"), + "//text[@" FREECAD_ATTR_EDITABLE "]/tspan"), [&substitutions, &templateDocument](QDomElement& tspan) -> bool { // Replace the editable text spans with new nodes holding actual values - QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable")); + QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE)); std::map::iterator item = substitutions.find(editableName.toStdString()); if (item != substitutions.end()) { @@ -296,15 +296,28 @@ std::map DrawSVGTemplate::getEditableTextsFromTemplate query.processItems(QString::fromUtf8( "declare default element namespace \"" SVG_NS_URI "\"; " "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]/tspan"), - [&editables](QDomElement& tspan) -> bool { - QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable")); - QString editableValue = tspan.firstChild().nodeValue(); + "//text[@" FREECAD_ATTR_EDITABLE "]/tspan"), + [this, &editables](QDomElement& tspan) -> bool { + QDomElement parent = tspan.parentNode().toElement(); + QString editableName = parent.attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE)); - editables[std::string(editableName.toUtf8().constData())] = - std::string(editableValue.toUtf8().constData()); - return true; - }); + QString editableValue; + if (parent.hasAttribute(QString::fromUtf8(FREECAD_ATTR_AUTOFILL))) { + QString autofillValue = getAutofillValue(parent.attribute(QString::fromUtf8(FREECAD_ATTR_AUTOFILL))); + if (!autofillValue.isNull()) { + editableValue = autofillValue; + } + } + + // If the autofill value is not specified or unsupported, use the default text value + if (editableValue.isNull()) { + editableValue = tspan.firstChild().nodeValue(); + } + + editables[std::string(editableName.toUtf8().constData())] = + std::string(editableValue.toUtf8().constData()); + return true; + }); return editables; } diff --git a/src/Mod/TechDraw/App/DrawTemplate.cpp b/src/Mod/TechDraw/App/DrawTemplate.cpp index 35709cd26e..7ba1cfbe50 100644 --- a/src/Mod/TechDraw/App/DrawTemplate.cpp +++ b/src/Mod/TechDraw/App/DrawTemplate.cpp @@ -24,13 +24,19 @@ #ifndef _PreComp_ # include +# include +# include #endif #include +#include +#include + #include "DrawTemplate.h" #include "DrawTemplatePy.h" #include "DrawPage.h" +#include "DrawUtil.h" using namespace TechDraw; @@ -94,6 +100,67 @@ DrawPage* DrawTemplate::getParentPage() const return page; } +QString DrawTemplate::getAutofillValue(const QString &id) const +{ + // author + if (id.compare(QString::fromUtf8(Autofill::Author)) == 0) { + std::string value = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences") + ->GetGroup("Document")->GetASCII("prefAuthor"); + if (!value.empty()) { + return QString::fromUtf8(value.c_str()); + } + } + // date + else if (id.compare(QString::fromUtf8(Autofill::Date)) == 0) { + QDateTime date = QDateTime::currentDateTime(); + return date.toString(QLocale().dateFormat(QLocale::ShortFormat)); + } + // organization + else if (id.compare(QString::fromUtf8(Autofill::Organization)) == 0) { + std::string value = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences") + ->GetGroup("Document")->GetASCII("prefCompany"); + if (!value.empty()) { + return QString::fromUtf8(value.c_str()); + } + } + // scale + else if (id.compare(QString::fromUtf8(Autofill::Scale)) == 0) { + DrawPage *page = getParentPage(); + if (page) { + std::pair scale = DrawUtil::nearestFraction(page->Scale.getValue()); + return QString::asprintf("%d : %d", scale.first, scale.second); + } + } + // sheet + else if (id.compare(QString::fromUtf8(Autofill::Sheet)) == 0) { + std::vector pages = getDocument()->getObjectsOfType(TechDraw::DrawPage::getClassTypeId()); + std::vector pageNames; + for (auto page : pages) { + pageNames.push_back(QString::fromUtf8(page->Label.getValue())); + } + + QCollator collator; + std::sort(pageNames.begin(), pageNames.end(), collator); + + int pos = 0; + DrawPage *page = getParentPage(); + if (page) { + auto it = std::find(pageNames.begin(), pageNames.end(), QString::fromUtf8(page->Label.getValue())); + if (it != pageNames.end()) { + pos = it - pageNames.begin() + 1; + } + } + + return QString::asprintf("%d / %d", pos, (int) pageNames.size()); + } + // title + else if (id.compare(QString::fromUtf8(Autofill::Title)) == 0) { + return QString::fromUtf8(getDocument()->Label.getValue()); + } + + return QString(); +} + // Python Template feature --------------------------------------------------------- namespace App { diff --git a/src/Mod/TechDraw/App/DrawTemplate.h b/src/Mod/TechDraw/App/DrawTemplate.h index 4849381aab..f2c6be5390 100644 --- a/src/Mod/TechDraw/App/DrawTemplate.h +++ b/src/Mod/TechDraw/App/DrawTemplate.h @@ -57,6 +57,8 @@ public: virtual DrawPage* getParentPage() const; + virtual QString getAutofillValue(const QString &id) const; + /// returns the type name of the ViewProvider const char* getViewProviderName(void) const override{ return "TechDrawGui::ViewProviderTemplate"; @@ -65,6 +67,17 @@ public: // from base class PyObject *getPyObject(void) override; + class Autofill + { + public: + static constexpr const char *Author = "author"; + static constexpr const char *Date = "date"; + static constexpr const char *Organization = "organization"; + static constexpr const char *Scale = "scale"; + static constexpr const char *Sheet = "sheet"; + static constexpr const char *Title = "title"; + }; + private: static const char* OrientationEnums[]; diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp index e6c6385eb8..915e9e5807 100644 --- a/src/Mod/TechDraw/App/DrawUtil.cpp +++ b/src/Mod/TechDraw/App/DrawUtil.cpp @@ -1312,6 +1312,80 @@ double DrawUtil::angleDifference(double fi1, double fi2, bool reflex) return fi1; } +std::pair DrawUtil::nearestFraction(double val, int maxDenom) +{ +// Find rational approximation to given real number +// David Eppstein / UC Irvine / 8 Aug 1993 +// +// With corrections from Arno Formella, May 2008 +// and additional fiddles by WF 2017 +// usage: a.out r d +// r is real number to approx +// d is the maximum denominator allowed +// +// Based on the theory of continued fractions +// if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...))) +// then best approximation is found by truncating this series +// (with some adjustments in the last term). +// +// Note the fraction can be recovered as the first column of the matrix +// ( a1 1 ) ( a2 1 ) ( a3 1 ) ... +// ( 1 0 ) ( 1 0 ) ( 1 0 ) +// Instead of keeping the sequence of continued fraction terms, +// we just keep the last partial product of these matrices. + std::pair result; + long m[2][2]; + long maxden = maxDenom; + long ai; + double x = val; + double startx = x; + + /* initialize matrix */ + m[0][0] = m[1][1] = 1; + m[0][1] = m[1][0] = 0; + + /* loop finding terms until denom gets too big */ + while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) { + long t; + t = m[0][0] * ai + m[0][1]; + m[0][1] = m[0][0]; + m[0][0] = t; + t = m[1][0] * ai + m[1][1]; + m[1][1] = m[1][0]; + m[1][0] = t; + if(x == (double) ai) + break; // AF: division by zero + x = 1/(x - (double) ai); + if(x > (double) std::numeric_limits::max()) + break; // AF: representation failure + } + + /* now remaining x is between 0 and 1/ai */ + /* approx as either 0 or 1/m where m is max that will fit in maxden */ + /* first try zero */ + double error1 = startx - ((double) m[0][0] / (double) m[1][0]); + int n1 = m[0][0]; + int d1 = m[1][0]; + + /* now try other possibility */ + ai = (maxden - m[1][1]) / m[1][0]; + m[0][0] = m[0][0] * ai + m[0][1]; + m[1][0] = m[1][0] * ai + m[1][1]; + double error2 = startx - ((double) m[0][0] / (double) m[1][0]); + int n2 = m[0][0]; + int d2 = m[1][0]; + + if (std::fabs(error1) <= std::fabs(error2)) { + result.first = n1; + result.second = d1; + } else { + result.first = n2; + result.second = d2; + } + + return result; +} + // Interval marking functions // ========================== diff --git a/src/Mod/TechDraw/App/DrawUtil.h b/src/Mod/TechDraw/App/DrawUtil.h index 503de91462..c6b6bdd069 100644 --- a/src/Mod/TechDraw/App/DrawUtil.h +++ b/src/Mod/TechDraw/App/DrawUtil.h @@ -58,6 +58,9 @@ #define SVG_NS_URI "http://www.w3.org/2000/svg" #define FREECAD_SVG_NS_URI "https://www.freecad.org/wiki/index.php?title=Svg_Namespace" +#define FREECAD_ATTR_EDITABLE "freecad:editable" +#define FREECAD_ATTR_AUTOFILL "freecad:autofill" + //some shapes are being passed in where edges that should be connected are in fact //separated by more than 2*Precision::Confusion (expected tolerance for 2 TopoDS_Vertex) //this value is used in EdgeWalker, DrawProjectSplit and DrawUtil and needs to be in sync in @@ -217,6 +220,7 @@ public: static void angleNormalize(double& fi); static double angleComposition(double fi, double delta); static double angleDifference(double fi1, double fi2, bool reflex = false); + static std::pair nearestFraction(double val, int maxDenom = 999); // Interval marking functions static unsigned int intervalMerge(std::vector>& marking, diff --git a/src/Mod/TechDraw/App/DrawViewSymbol.cpp b/src/Mod/TechDraw/App/DrawViewSymbol.cpp index a7488fe082..d2bfa7d638 100644 --- a/src/Mod/TechDraw/App/DrawViewSymbol.cpp +++ b/src/Mod/TechDraw/App/DrawViewSymbol.cpp @@ -125,7 +125,7 @@ std::vector DrawViewSymbol::getEditableFields() // has "freecad:editable" attribute query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]/tspan"), + "//text[@" FREECAD_ATTR_EDITABLE "]/tspan"), [&editables](QDomElement& tspan) -> bool { QString editableValue = tspan.firstChild().nodeValue(); editables.emplace_back(editableValue.toStdString()); @@ -154,7 +154,7 @@ void DrawViewSymbol::updateFieldsInSymbol() // has "freecad:editable" attribute query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]/tspan"), + "//text[@" FREECAD_ATTR_EDITABLE "]/tspan"), [&symbolDocument, &editText, &count](QDomElement& tspanElement) -> bool { if (count >= editText.size()) { diff --git a/src/Mod/TechDraw/App/XMLQuery.cpp b/src/Mod/TechDraw/App/XMLQuery.cpp index 6a88c3b6a3..adae6bac12 100644 --- a/src/Mod/TechDraw/App/XMLQuery.cpp +++ b/src/Mod/TechDraw/App/XMLQuery.cpp @@ -27,6 +27,7 @@ #include #endif +#include "DrawUtil.h" #include "XMLQuery.h" @@ -51,7 +52,7 @@ static bool processElements(const QDomElement& element, const QString& queryStr, for(int i = 0; i < editable.count(); i++) { QDomNode node = editable.item(i); QDomElement element = node.toElement(); - if (element.hasAttribute(QString(QLatin1String("freecad:editable")))) { + if (element.hasAttribute(QString(QLatin1String(FREECAD_ATTR_EDITABLE)))) { if (find_tspan) { element = element.firstChildElement(); } diff --git a/src/Mod/TechDraw/Gui/Command.cpp b/src/Mod/TechDraw/Gui/Command.cpp index f6ba42655b..dea656de90 100644 --- a/src/Mod/TechDraw/Gui/Command.cpp +++ b/src/Mod/TechDraw/Gui/Command.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -105,40 +106,33 @@ void CmdTechDrawPageDefault::activated(int iMsg) Q_UNUSED(iMsg); QString templateFileName = Preferences::defaultTemplate(); - - std::string PageName = getUniqueObjectName("Page"); - std::string TemplateName = getUniqueObjectName("Template"); - QFileInfo tfi(templateFileName); if (tfi.isReadable()) { Gui::WaitCursor wc; openCommand(QT_TRANSLATE_NOOP("Command", "Drawing create page")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawPage', '%s')", - PageName.c_str()); - doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawPage', 'Page', '%s')", - PageName.c_str(), PageName.c_str()); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawSVGTemplate', '%s')", - TemplateName.c_str()); - doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawSVGTemplate', 'Template', '%s')", - TemplateName.c_str(), TemplateName.c_str()); + auto page = dynamic_cast + (getDocument()->addObject("TechDraw::DrawPage", "Page")); + if (!page) { + throw Base::TypeError("CmdTechDrawPageDefault - page not created"); + } + page->translateLabel("DrawPage", "Page", page->getNameInDocument()); - doCommand(Doc, "App.activeDocument().%s.Template = '%s'", TemplateName.c_str(), - templateFileName.toStdString().c_str()); - doCommand(Doc, "App.activeDocument().%s.Template = App.activeDocument().%s", - PageName.c_str(), TemplateName.c_str()); + auto svgTemplate = dynamic_cast + (getDocument()->addObject("TechDraw::DrawSVGTemplate", "Template")); + if (!svgTemplate) { + throw Base::TypeError("CmdTechDrawPageDefault - template not created"); + } + svgTemplate->translateLabel("DrawSVGTemplate", "Template", svgTemplate->getNameInDocument()); + + page->Template.setValue(svgTemplate); + svgTemplate->Template.setValue(templateFileName.toStdString()); updateActive(); commitCommand(); - TechDraw::DrawPage* fp = - dynamic_cast(getDocument()->getObject(PageName.c_str())); - if (!fp) { - throw Base::TypeError("CmdTechDrawPageDefault fp not found\n"); - } - Gui::ViewProvider* vp = - Gui::Application::Instance->getDocument(getDocument())->getViewProvider(fp); - TechDrawGui::ViewProviderPage* dvp = dynamic_cast(vp); + TechDrawGui::ViewProviderPage *dvp = dynamic_cast + (Gui::Application::Instance->getViewProvider(page)); if (dvp) { dvp->show(); } @@ -182,44 +176,33 @@ void CmdTechDrawPageTemplate::activated(int iMsg) return; } - std::string PageName = getUniqueObjectName("Page"); - std::string TemplateName = getUniqueObjectName("Template"); - QFileInfo tfi(templateFileName); if (tfi.isReadable()) { Gui::WaitCursor wc; openCommand(QT_TRANSLATE_NOOP("Command", "Drawing create page")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawPage', '%s')", - PageName.c_str()); - doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawPage', 'Page', '%s')", - PageName.c_str(), PageName.c_str()); - // Create the Template Object to attach to the page - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawSVGTemplate', '%s')", - TemplateName.c_str()); - doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawSVGTemplate', 'Template', '%s')", - TemplateName.c_str(), TemplateName.c_str()); + auto page = dynamic_cast + (getDocument()->addObject("TechDraw::DrawPage", "Page")); + if (!page) { + throw Base::TypeError("CmdTechDrawPageTemplate - page not created"); + } + page->translateLabel("DrawPage", "Page", page->getNameInDocument()); - //why is "Template" property set twice? -wf - // once to set DrawSVGTemplate.Template to OS template file name - templateFileName = Base::Tools::escapeEncodeFilename(templateFileName); - doCommand(Doc, "App.activeDocument().%s.Template = \"%s\"", TemplateName.c_str(), - templateFileName.toUtf8().constData()); - // once to set Page.Template to DrawSVGTemplate.Name - doCommand(Doc, "App.activeDocument().%s.Template = App.activeDocument().%s", - PageName.c_str(), TemplateName.c_str()); - // consider renaming DrawSVGTemplate.Template property? + auto svgTemplate = dynamic_cast + (getDocument()->addObject("TechDraw::DrawSVGTemplate", "Template")); + if (!svgTemplate) { + throw Base::TypeError("CmdTechDrawPageTemplate - template not created"); + } + svgTemplate->translateLabel("DrawSVGTemplate", "Template", svgTemplate->getNameInDocument()); + + page->Template.setValue(svgTemplate); + svgTemplate->Template.setValue(templateFileName.toStdString()); updateActive(); commitCommand(); - TechDraw::DrawPage* fp = - dynamic_cast(getDocument()->getObject(PageName.c_str())); - if (!fp) { - throw Base::TypeError("CmdTechDrawNewPagePick fp not found\n"); - } - Gui::ViewProvider* vp = - Gui::Application::Instance->getDocument(getDocument())->getViewProvider(fp); - TechDrawGui::ViewProviderPage* dvp = dynamic_cast(vp); + + TechDrawGui::ViewProviderPage *dvp = dynamic_cast + (Gui::Application::Instance->getViewProvider(page)); if (dvp) { dvp->show(); } diff --git a/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp b/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp index 919616c9c1..1762ea90a2 100644 --- a/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp +++ b/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp @@ -178,9 +178,9 @@ void QGISVGTemplate::createClickHandles() // XPath query to select all nodes with "freecad:editable" attribute query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; " "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//text[@freecad:editable]"), + "//text[@" FREECAD_ATTR_EDITABLE "]"), [&](QDomElement& textElement) -> bool { - QString name = textElement.attribute(QString::fromUtf8("freecad:editable")); + QString name = textElement.attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE)); double x = Rez::guiX( textElement.attribute(QString::fromUtf8("x"), QString::fromUtf8("0.0")).toDouble()); double y = Rez::guiX( diff --git a/src/Mod/TechDraw/Gui/TaskProjGroup.cpp b/src/Mod/TechDraw/Gui/TaskProjGroup.cpp index 0916dfc9f5..d2cc2638bb 100644 --- a/src/Mod/TechDraw/Gui/TaskProjGroup.cpp +++ b/src/Mod/TechDraw/Gui/TaskProjGroup.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "TaskProjGroup.h" #include "ui_TaskProjGroup.h" @@ -306,81 +307,6 @@ void TaskProjGroup::spacingChanged() multiView->recomputeFeature(); } -std::pair TaskProjGroup::nearestFraction(const double val, const long int maxDenom) const -{ -/* -** find rational approximation to given real number -** David Eppstein / UC Irvine / 8 Aug 1993 -** -** With corrections from Arno Formella, May 2008 -** and additional fiddles by WF 2017 -** usage: a.out r d -** r is real number to approx -** d is the maximum denominator allowed -** -** based on the theory of continued fractions -** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...))) -** then best approximation is found by truncating this series -** (with some adjustments in the last term). -** -** Note the fraction can be recovered as the first column of the matrix -** ( a1 1 ) ( a2 1 ) ( a3 1 ) ... -** ( 1 0 ) ( 1 0 ) ( 1 0 ) -** Instead of keeping the sequence of continued fraction terms, -** we just keep the last partial product of these matrices. -*/ - std::pair result; - long m[2][2]; - long maxden = maxDenom; - long ai; - double x = val; - double startx = x; - - /* initialize matrix */ - m[0][0] = m[1][1] = 1; - m[0][1] = m[1][0] = 0; - - /* loop finding terms until denom gets too big */ - while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) { - long t; - t = m[0][0] * ai + m[0][1]; - m[0][1] = m[0][0]; - m[0][0] = t; - t = m[1][0] * ai + m[1][1]; - m[1][1] = m[1][0]; - m[1][0] = t; - if(x == (double) ai) - break; // AF: division by zero - x = 1/(x - (double) ai); - if(x > (double) std::numeric_limits::max()) - break; // AF: representation failure - } - - /* now remaining x is between 0 and 1/ai */ - /* approx as either 0 or 1/m where m is max that will fit in maxden */ - /* first try zero */ - double error1 = startx - ((double) m[0][0] / (double) m[1][0]); - int n1 = m[0][0]; - int d1 = m[1][0]; - - /* now try other possibility */ - ai = (maxden - m[1][1]) / m[1][0]; - m[0][0] = m[0][0] * ai + m[0][1]; - m[1][0] = m[1][0] * ai + m[1][1]; - double error2 = startx - ((double) m[0][0] / (double) m[1][0]); - int n2 = m[0][0]; - int d2 = m[1][0]; - - if (std::fabs(error1) <= std::fabs(error2)) { - result.first = n1; - result.second = d1; - } else { - result.first = n2; - result.second = d2; - } - return result; -} - void TaskProjGroup::updateTask() { // Update the scale type @@ -398,7 +324,7 @@ void TaskProjGroup::setFractionalScale(double newScale) { blockUpdate = true; - std::pair fraction = nearestFraction(newScale); + std::pair fraction = DrawUtil::nearestFraction(newScale); ui->sbScaleNum->setValue(fraction.first); ui->sbScaleDen->setValue(fraction.second); diff --git a/src/Mod/TechDraw/Gui/TaskProjGroup.h b/src/Mod/TechDraw/Gui/TaskProjGroup.h index 9e1dbfa3e3..7a3062443c 100644 --- a/src/Mod/TechDraw/Gui/TaskProjGroup.h +++ b/src/Mod/TechDraw/Gui/TaskProjGroup.h @@ -60,7 +60,6 @@ public: QPushButton* btnApply); void updateTask(); - std::pair nearestFraction(double val, long int maxDenom = 999) const; // Sets the numerator and denominator widgets to match newScale void setFractionalScale(double newScale); void setCreateMode(bool mode) { m_createMode = mode;} diff --git a/src/Mod/TechDraw/Templates/A4_LandscapeTD.svg b/src/Mod/TechDraw/Templates/A4_LandscapeTD.svg index 1ea6c239bc..c3b68cf97a 100644 --- a/src/Mod/TechDraw/Templates/A4_LandscapeTD.svg +++ b/src/Mod/TechDraw/Templates/A4_LandscapeTD.svg @@ -192,13 +192,13 @@ - Designed by Name - Date - Scale + Designed by Name + Date + Scale Weight - Title + Title Subtitle Drawing number - Sheet + Sheet diff --git a/src/Mod/TechDraw/Templates/ANSIC_Landscape.svg b/src/Mod/TechDraw/Templates/ANSIC_Landscape.svg index 04981c38a7..50357387ce 100644 --- a/src/Mod/TechDraw/Templates/ANSIC_Landscape.svg +++ b/src/Mod/TechDraw/Templates/ANSIC_Landscape.svg @@ -314,18 +314,18 @@ - AUTHOR NAME - CREATION DATE + AUTHOR NAME + CREATION DATE SUPERVISOR NAME CHECK DATE ANSI C - SCALE + SCALE WEIGHT NUMBER - SHEET - TITLE + SHEET + TITLE SUBTITLE - COMPANY NAME + COMPANY NAME COPYRIGHT _________ _________ From d5b90e50af6a758af748179f289bb8f09e357266 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 23 Mar 2024 14:00:59 +0100 Subject: [PATCH 48/59] Gui: Apply clang-format on DlgProjectionOnSurface and fix linter warnings --- src/Mod/Part/Gui/DlgProjectionOnSurface.cpp | 1789 ++++++++++--------- src/Mod/Part/Gui/DlgProjectionOnSurface.h | 149 +- 2 files changed, 996 insertions(+), 942 deletions(-) diff --git a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp index c45a9841e4..1baf8ccb54 100644 --- a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp +++ b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp @@ -22,25 +22,25 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif #include @@ -60,81 +60,79 @@ using namespace PartGui; ////////////////////////////////////////////////////////////////////////// -class DlgProjectionOnSurface::EdgeSelection : public Gui::SelectionFilterGate +class DlgProjectionOnSurface::EdgeSelection: public Gui::SelectionFilterGate { public: - bool canSelect; + bool canSelect = false; - EdgeSelection() - : Gui::SelectionFilterGate(nullPointer()) - { - canSelect = false; - } - ~EdgeSelection() override = default; + EdgeSelection() + : Gui::SelectionFilterGate(nullPointer()) + {} - bool allow(App::Document* /*pDoc*/, App::DocumentObject* iPObj, const char* sSubName) override - { - Part::Feature* aPart = dynamic_cast(iPObj); - if (!aPart) - return false; - if (!sSubName) - return false; - std::string subName(sSubName); - if (subName.empty()) - return false; + bool allow(App::Document* /*pDoc*/, App::DocumentObject* iPObj, const char* sSubName) override + { + auto aPart = dynamic_cast(iPObj); + if (!aPart) { + return false; + } + if (!sSubName) { + return false; + } + std::string subName(sSubName); + if (subName.empty()) { + return false; + } - auto subShape = aPart->Shape.getShape().getSubShape(sSubName); - if (subShape.IsNull()) - return false; - auto type = subShape.ShapeType(); - if (type != TopAbs_EDGE) - return false; - return true; - } + auto subShape = aPart->Shape.getShape().getSubShape(sSubName); + if (subShape.IsNull()) { + return false; + } + auto type = subShape.ShapeType(); + return (type == TopAbs_EDGE); + } }; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -class DlgProjectionOnSurface::FaceSelection : public Gui::SelectionFilterGate +class DlgProjectionOnSurface::FaceSelection: public Gui::SelectionFilterGate { public: - bool canSelect; + bool canSelect = false; - FaceSelection() - : Gui::SelectionFilterGate(nullPointer()) - { - canSelect = false; - } - ~FaceSelection() override = default; + FaceSelection() + : Gui::SelectionFilterGate(nullPointer()) + {} - bool allow(App::Document* /*pDoc*/, App::DocumentObject* iPObj, const char* sSubName) override - { - Part::Feature* aPart = dynamic_cast(iPObj); - if (!aPart) - return false; - if (!sSubName) - return false; - std::string subName(sSubName); - if (subName.empty()) - return false; + bool allow(App::Document* /*pDoc*/, App::DocumentObject* iPObj, const char* sSubName) override + { + auto aPart = dynamic_cast(iPObj); + if (!aPart) { + return false; + } + if (!sSubName) { + return false; + } + std::string subName(sSubName); + if (subName.empty()) { + return false; + } - auto subShape = aPart->Shape.getShape().getSubShape(sSubName, true); - if (subShape.IsNull()) - return false; - auto type = subShape.ShapeType(); - if (type != TopAbs_FACE) - return false; - return true; - } + auto subShape = aPart->Shape.getShape().getSubShape(sSubName, true); + if (subShape.IsNull()) { + return false; + } + auto type = subShape.ShapeType(); + return (type == TopAbs_FACE); + } }; ////////////////////////////////////////////////////////////////////////// -DlgProjectionOnSurface::DlgProjectionOnSurface(QWidget *parent) - : QWidget(parent) - , ui(new Ui::DlgProjectionOnSurface) - , m_projectionObjectName(tr("Projection Object")) - , filterEdge(nullptr) - , filterFace(nullptr) +DlgProjectionOnSurface::DlgProjectionOnSurface(QWidget* parent) + : QWidget(parent) + , ui(new Ui::DlgProjectionOnSurface) + , m_projectionObjectName(tr("Projection Object")) + , filterEdge(nullptr) + , filterFace(nullptr) { ui->setupUi(this); setupConnections(); @@ -160,16 +158,15 @@ DlgProjectionOnSurface::DlgProjectionOnSurface(QWidget *parent) disable_ui_elements(m_guiObjectVec, ui->pushButtonAddProjFace); m_partDocument = App::GetApplication().getActiveDocument(); - if (!m_partDocument) - { - throw Base::ValueError(QString(tr("Have no active document!!!")).toUtf8()); + if (!m_partDocument) { + throw Base::ValueError(QString(tr("Have no active document!!!")).toUtf8()); } this->attachDocument(m_partDocument); m_partDocument->openTransaction("Project on surface"); - m_projectionObject = dynamic_cast(m_partDocument->addObject("Part::Feature", "Projection Object")); - if (!m_projectionObject) - { - throw Base::ValueError(QString(tr("Can not create a projection object!!!")).toUtf8()); + m_projectionObject = dynamic_cast( + m_partDocument->addObject("Part::Feature", "Projection Object")); + if (!m_projectionObject) { + throw Base::ValueError(QString(tr("Can not create a projection object!!!")).toUtf8()); } m_projectionObject->Label.setValue(std::string(m_projectionObjectName.toUtf8()).c_str()); onRadioButtonShowAllClicked(); @@ -178,965 +175,1013 @@ DlgProjectionOnSurface::DlgProjectionOnSurface(QWidget *parent) DlgProjectionOnSurface::~DlgProjectionOnSurface() { - delete ui; - for (const auto& it : m_projectionSurfaceVec) - { - try { - higlight_object(it.partFeature, it.partName, false, 0); + delete ui; + for (const auto& it : m_projectionSurfaceVec) { + try { + higlight_object(it.partFeature, it.partName, false, 0); + } + catch (Standard_NoSuchObject& e) { + Base::Console().Warning("DlgProjectionOnSurface::~DlgProjectionOnSurface: %s", + e.GetMessageString()); + } + auto vp = dynamic_cast( + Gui::Application::Instance->getViewProvider(it.partFeature)); + if (vp) { + vp->Selectable.setValue(it.is_selectable); + vp->Transparency.setValue(it.transparency); + } } - catch (Standard_NoSuchObject& e) { - Base::Console().Warning("DlgProjectionOnSurface::~DlgProjectionOnSurface: %s", e.GetMessageString()); + for (const auto& it : m_shapeVec) { + try { + higlight_object(it.partFeature, it.partName, false, 0); + } + catch (Standard_NoSuchObject& e) { + Base::Console().Warning("DlgProjectionOnSurface::~DlgProjectionOnSurface: %s", + e.GetMessageString()); + } } - PartGui::ViewProviderPartExt* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(it.partFeature)); - if (vp) - { - vp->Selectable.setValue(it.is_selectable); - vp->Transparency.setValue(it.transparency); - } - } - for (const auto& it : m_shapeVec) - { - try { - higlight_object(it.partFeature, it.partName, false, 0); - } - catch (Standard_NoSuchObject& e) { - Base::Console().Warning("DlgProjectionOnSurface::~DlgProjectionOnSurface: %s", e.GetMessageString()); - } - } - Gui::Selection().rmvSelectionGate(); + Gui::Selection().rmvSelectionGate(); } void PartGui::DlgProjectionOnSurface::setupConnections() { - connect(ui->pushButtonAddFace, &QPushButton::clicked, - this, &DlgProjectionOnSurface::onPushButtonAddFaceClicked); - connect(ui->pushButtonAddEdge, &QPushButton::clicked, - this, &DlgProjectionOnSurface::onPushButtonAddEdgeClicked); - connect(ui->pushButtonGetCurrentCamDir, &QPushButton::clicked, - this, &DlgProjectionOnSurface::onPushButtonGetCurrentCamDirClicked); - connect(ui->pushButtonDirX, &QPushButton::clicked, - this, &DlgProjectionOnSurface::onPushButtonDirXClicked); - connect(ui->pushButtonDirY, &QPushButton::clicked, - this, &DlgProjectionOnSurface::onPushButtonDirYClicked); - connect(ui->pushButtonDirZ, &QPushButton::clicked, - this, &DlgProjectionOnSurface::onPushButtonDirZClicked); - connect(ui->pushButtonAddProjFace, &QPushButton::clicked, - this, &DlgProjectionOnSurface::onPushButtonAddProjFaceClicked); - connect(ui->radioButtonShowAll, &QRadioButton::clicked, - this, &DlgProjectionOnSurface::onRadioButtonShowAllClicked); - connect(ui->radioButtonFaces, &QRadioButton::clicked, - this, &DlgProjectionOnSurface::onRadioButtonFacesClicked); - connect(ui->radioButtonEdges, &QRadioButton::clicked, - this, &DlgProjectionOnSurface::onRadioButtonEdgesClicked); - connect(ui->doubleSpinBoxExtrudeHeight, qOverload(&QDoubleSpinBox::valueChanged), - this, &DlgProjectionOnSurface::onDoubleSpinBoxExtrudeHeightValueChanged); - connect(ui->pushButtonAddWire, &QPushButton::clicked, - this, &DlgProjectionOnSurface::onPushButtonAddWireClicked); - connect(ui->doubleSpinBoxSolidDepth, qOverload(&QDoubleSpinBox::valueChanged), - this, &DlgProjectionOnSurface::onDoubleSpinBoxSolidDepthValueChanged); + connect(ui->pushButtonAddFace, + &QPushButton::clicked, + this, + &DlgProjectionOnSurface::onPushButtonAddFaceClicked); + connect(ui->pushButtonAddEdge, + &QPushButton::clicked, + this, + &DlgProjectionOnSurface::onPushButtonAddEdgeClicked); + connect(ui->pushButtonGetCurrentCamDir, + &QPushButton::clicked, + this, + &DlgProjectionOnSurface::onPushButtonGetCurrentCamDirClicked); + connect(ui->pushButtonDirX, + &QPushButton::clicked, + this, + &DlgProjectionOnSurface::onPushButtonDirXClicked); + connect(ui->pushButtonDirY, + &QPushButton::clicked, + this, + &DlgProjectionOnSurface::onPushButtonDirYClicked); + connect(ui->pushButtonDirZ, + &QPushButton::clicked, + this, + &DlgProjectionOnSurface::onPushButtonDirZClicked); + connect(ui->pushButtonAddProjFace, + &QPushButton::clicked, + this, + &DlgProjectionOnSurface::onPushButtonAddProjFaceClicked); + connect(ui->radioButtonShowAll, + &QRadioButton::clicked, + this, + &DlgProjectionOnSurface::onRadioButtonShowAllClicked); + connect(ui->radioButtonFaces, + &QRadioButton::clicked, + this, + &DlgProjectionOnSurface::onRadioButtonFacesClicked); + connect(ui->radioButtonEdges, + &QRadioButton::clicked, + this, + &DlgProjectionOnSurface::onRadioButtonEdgesClicked); + connect(ui->doubleSpinBoxExtrudeHeight, + qOverload(&QDoubleSpinBox::valueChanged), + this, + &DlgProjectionOnSurface::onDoubleSpinBoxExtrudeHeightValueChanged); + connect(ui->pushButtonAddWire, + &QPushButton::clicked, + this, + &DlgProjectionOnSurface::onPushButtonAddWireClicked); + connect(ui->doubleSpinBoxSolidDepth, + qOverload(&QDoubleSpinBox::valueChanged), + this, + &DlgProjectionOnSurface::onDoubleSpinBoxSolidDepthValueChanged); } void PartGui::DlgProjectionOnSurface::slotDeletedDocument(const App::Document& Doc) { - if (m_partDocument == &Doc) { - m_partDocument = nullptr; - m_projectionObject = nullptr; - } + if (m_partDocument == &Doc) { + m_partDocument = nullptr; + m_projectionObject = nullptr; + } } void PartGui::DlgProjectionOnSurface::slotDeletedObject(const App::DocumentObject& Obj) { - if (m_projectionObject == &Obj) { - m_projectionObject = nullptr; - } + if (m_projectionObject == &Obj) { + m_projectionObject = nullptr; + } } void PartGui::DlgProjectionOnSurface::apply() { - if (m_partDocument) - m_partDocument->commitTransaction(); + if (m_partDocument) { + m_partDocument->commitTransaction(); + } } void PartGui::DlgProjectionOnSurface::reject() { - if (m_partDocument) - m_partDocument->abortTransaction(); + if (m_partDocument) { + m_partDocument->abortTransaction(); + } } void PartGui::DlgProjectionOnSurface::onPushButtonAddFaceClicked() { - if ( ui->pushButtonAddFace->isChecked() ) - { - m_currentSelection = "add_face"; - disable_ui_elements(m_guiObjectVec, ui->pushButtonAddFace); - if (!filterFace) - { - filterFace = new FaceSelection(); - Gui::Selection().addSelectionGate(filterFace); + if (ui->pushButtonAddFace->isChecked()) { + m_currentSelection = "add_face"; + disable_ui_elements(m_guiObjectVec, ui->pushButtonAddFace); + if (!filterFace) { + filterFace = new FaceSelection(); + Gui::Selection().addSelectionGate(filterFace); + } + } + else { + m_currentSelection = ""; + enable_ui_elements(m_guiObjectVec, nullptr); + Gui::Selection().rmvSelectionGate(); + filterFace = nullptr; } - } - else - { - m_currentSelection = ""; - enable_ui_elements(m_guiObjectVec, nullptr); - Gui::Selection().rmvSelectionGate(); - filterFace = nullptr; - } } void PartGui::DlgProjectionOnSurface::onPushButtonAddEdgeClicked() { - if (ui->pushButtonAddEdge->isChecked()) - { - m_currentSelection = "add_edge"; - disable_ui_elements(m_guiObjectVec, ui->pushButtonAddEdge); - if (!filterEdge) - { - filterEdge = new EdgeSelection(); - Gui::Selection().addSelectionGate(filterEdge); + if (ui->pushButtonAddEdge->isChecked()) { + m_currentSelection = "add_edge"; + disable_ui_elements(m_guiObjectVec, ui->pushButtonAddEdge); + if (!filterEdge) { + filterEdge = new EdgeSelection(); + Gui::Selection().addSelectionGate(filterEdge); + } + ui->radioButtonEdges->setChecked(true); + onRadioButtonEdgesClicked(); + } + else { + m_currentSelection = ""; + enable_ui_elements(m_guiObjectVec, nullptr); + Gui::Selection().rmvSelectionGate(); + filterEdge = nullptr; } - ui->radioButtonEdges->setChecked(true); - onRadioButtonEdgesClicked(); - } - else - { - m_currentSelection = ""; - enable_ui_elements(m_guiObjectVec, nullptr); - Gui::Selection().rmvSelectionGate(); - filterEdge = nullptr; - } } void PartGui::DlgProjectionOnSurface::onPushButtonGetCurrentCamDirClicked() { - get_camera_direction(); + get_camera_direction(); } void PartGui::DlgProjectionOnSurface::onPushButtonDirXClicked() { - set_xyz_dir_spinbox(ui->doubleSpinBoxDirX); + set_xyz_dir_spinbox(ui->doubleSpinBoxDirX); } void PartGui::DlgProjectionOnSurface::onPushButtonDirYClicked() { - set_xyz_dir_spinbox(ui->doubleSpinBoxDirY); + set_xyz_dir_spinbox(ui->doubleSpinBoxDirY); } void PartGui::DlgProjectionOnSurface::onPushButtonDirZClicked() { - set_xyz_dir_spinbox(ui->doubleSpinBoxDirZ); + set_xyz_dir_spinbox(ui->doubleSpinBoxDirZ); } void PartGui::DlgProjectionOnSurface::onSelectionChanged(const Gui::SelectionChanges& msg) { - if (msg.Type == Gui::SelectionChanges::AddSelection) - { - if ( m_currentSelection == "add_face" || m_currentSelection == "add_edge" || m_currentSelection == "add_wire") - { - store_current_selected_parts(m_shapeVec, 0xff00ff00); - create_projection_wire(m_shapeVec); - create_projection_face_from_wire(m_shapeVec); - create_face_extrude(m_shapeVec); - show_projected_shapes(m_shapeVec); - } - else if (m_currentSelection == "add_projection_surface") - { - m_projectionSurfaceVec.clear(); - store_current_selected_parts(m_projectionSurfaceVec, 0xffff0000); - if (!m_projectionSurfaceVec.empty()) - { - PartGui::ViewProviderPartExt* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(m_projectionSurfaceVec.back().partFeature)); - if (vp) - { - vp->Selectable.setValue(false); - vp->Transparency.setValue(90); + if (msg.Type == Gui::SelectionChanges::AddSelection) { + if (m_currentSelection == "add_face" || m_currentSelection == "add_edge" + || m_currentSelection == "add_wire") { + store_current_selected_parts(m_shapeVec, 0xff00ff00); + create_projection_wire(m_shapeVec); + create_projection_face_from_wire(m_shapeVec); + create_face_extrude(m_shapeVec); + show_projected_shapes(m_shapeVec); } - } + else if (m_currentSelection == "add_projection_surface") { + m_projectionSurfaceVec.clear(); + store_current_selected_parts(m_projectionSurfaceVec, 0xffff0000); + if (!m_projectionSurfaceVec.empty()) { + auto vp = dynamic_cast( + Gui::Application::Instance->getViewProvider( + m_projectionSurfaceVec.back().partFeature)); + if (vp) { + vp->Selectable.setValue(false); + vp->Transparency.setValue(90); + } + } - ui->pushButtonAddProjFace->setChecked(false); - onPushButtonAddProjFaceClicked(); + ui->pushButtonAddProjFace->setChecked(false); + onPushButtonAddProjFaceClicked(); + } } - } } void PartGui::DlgProjectionOnSurface::get_camera_direction() { - auto mainWindow = Gui::getMainWindow(); + auto mainWindow = Gui::getMainWindow(); - auto mdiObject = dynamic_cast(mainWindow->activeWindow()); - if (!mdiObject) - return; - auto camerRotation = mdiObject->getViewer()->getCameraOrientation(); - - SbVec3f lookAt(0, 0, -1); - camerRotation.multVec(lookAt, lookAt); - - float valX, valY, valZ; - lookAt.getValue(valX, valY, valZ); - - ui->doubleSpinBoxDirX->setValue(valX); - ui->doubleSpinBoxDirY->setValue(valY); - ui->doubleSpinBoxDirZ->setValue(valZ); -} - -void PartGui::DlgProjectionOnSurface::store_current_selected_parts(std::vector& iStoreVec, const unsigned int iColor) -{ - if (!m_partDocument) - return; - std::vector selObj = Gui::Selection().getSelectionEx(); - if (!selObj.empty()) - { - for (auto it = selObj.begin(); it != selObj.end(); ++it) - { - auto aPart = dynamic_cast(it->getObject()); - if (!aPart) continue; - - if (aPart) - { - SShapeStore currentShapeStore; - currentShapeStore.inputShape = aPart->Shape.getShape().getShape(); - currentShapeStore.partFeature = aPart; - currentShapeStore.partName = aPart->getNameInDocument(); - - PartGui::ViewProviderPartExt* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(aPart)); - if (vp) - { - currentShapeStore.is_selectable = vp->Selectable.getValue(); - currentShapeStore.transparency = vp->Transparency.getValue(); - } - if (!it->getSubNames().empty() ) - { - auto parentShape = currentShapeStore.inputShape; - for (const auto & itName : selObj.front().getSubNames()) - { - auto currentShape = aPart->Shape.getShape().getSubShape(itName.c_str()); - - transform_shape_to_global_position(currentShape, aPart); - - currentShapeStore.inputShape = currentShape; - currentShapeStore.partName = itName; - auto store = store_part_in_vector(currentShapeStore, iStoreVec); - higlight_object(aPart, itName, store, iColor); - store_wire_in_vector(currentShapeStore, parentShape, iStoreVec, iColor); - } - } - else - { - transform_shape_to_global_position(currentShapeStore.inputShape,currentShapeStore.partFeature); - auto store = store_part_in_vector(currentShapeStore, iStoreVec); - higlight_object(aPart, aPart->Shape.getName(), store, iColor); - } - Gui::Selection().clearSelection(m_partDocument->getName()); - Gui::Selection().rmvPreselect(); - } - } - } -} - -bool PartGui::DlgProjectionOnSurface::store_part_in_vector(SShapeStore& iCurrentShape, std::vector& iStoreVec) -{ - if (iCurrentShape.inputShape.IsNull()) - return false; - auto currentType = iCurrentShape.inputShape.ShapeType(); - for ( auto it = iStoreVec.begin(); it != iStoreVec.end(); ++it) - { - if ( currentType == TopAbs_FACE ) - { - if (it->aFace.IsSame(iCurrentShape.inputShape)) - { - iStoreVec.erase(it); - return false; - } - } - else if ( currentType == TopAbs_EDGE ) - { - if (it->aEdge.IsSame(iCurrentShape.inputShape)) - { - iStoreVec.erase(it); - return false; - } - } - } - - if (currentType == TopAbs_FACE) - { - iCurrentShape.aFace = TopoDS::Face(iCurrentShape.inputShape); - } - else if (currentType == TopAbs_EDGE) - { - iCurrentShape.aEdge = TopoDS::Edge(iCurrentShape.inputShape); - } - - auto valX = ui->doubleSpinBoxDirX->value(); - auto valY = ui->doubleSpinBoxDirY->value(); - auto valZ = ui->doubleSpinBoxDirZ->value(); - - iCurrentShape.aProjectionDir = gp_Dir(valX, valY, valZ); - if ( !m_projectionSurfaceVec.empty() ) - { - iCurrentShape.surfaceToProject = m_projectionSurfaceVec.front().aFace; - } - iStoreVec.push_back(iCurrentShape); - return true; -} - -void PartGui::DlgProjectionOnSurface::create_projection_wire(std::vector& iCurrentShape) -{ - try - { - if (iCurrentShape.empty()) + auto mdiObject = dynamic_cast(mainWindow->activeWindow()); + if (!mdiObject) { return; - for ( auto &itCurrentShape : iCurrentShape ) - { - if (m_projectionSurfaceVec.empty()) continue;; - if (!itCurrentShape.aProjectedEdgeVec.empty()) continue;; - if (!itCurrentShape.aProjectedFace.IsNull()) continue;; - if (!itCurrentShape.aProjectedWireVec.empty()) continue;; - - if (!itCurrentShape.aFace.IsNull()) - { - get_all_wire_from_face(itCurrentShape); - for (const auto& itWire : itCurrentShape.aWireVec) - { - BRepProj_Projection aProjection(itWire, itCurrentShape.surfaceToProject, itCurrentShape.aProjectionDir); - double minDistance = std::numeric_limits::max(); - TopoDS_Wire wireToTake; - for ( ; aProjection.More(); aProjection.Next() ) - { - auto it = aProjection.Current(); - BRepExtrema_DistShapeShape distanceMeasure(it, itCurrentShape.aFace); - distanceMeasure.Perform(); - auto currentDistance = distanceMeasure.Value(); - if ( currentDistance > minDistance ) continue; - wireToTake = it; - minDistance = currentDistance; - } - auto aWire = sort_and_heal_wire(wireToTake, itCurrentShape.surfaceToProject); - itCurrentShape.aProjectedWireVec.push_back(aWire); - } - } - else if (!itCurrentShape.aEdge.IsNull()) - { - BRepProj_Projection aProjection(itCurrentShape.aEdge, itCurrentShape.surfaceToProject, itCurrentShape.aProjectionDir); - double minDistance = std::numeric_limits::max(); - TopoDS_Wire wireToTake; - for (; aProjection.More(); aProjection.Next()) - { - auto it = aProjection.Current(); - BRepExtrema_DistShapeShape distanceMeasure(it, itCurrentShape.aEdge); - distanceMeasure.Perform(); - auto currentDistance = distanceMeasure.Value(); - if (currentDistance > minDistance) continue; - wireToTake = it; - minDistance = currentDistance; - } - for (TopExp_Explorer aExplorer(wireToTake, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) - { - itCurrentShape.aProjectedEdgeVec.push_back(TopoDS::Edge(aExplorer.Current())); - } - } - } - } - catch (const Standard_Failure& error) - { - std::stringstream ssOcc; - error.Print(ssOcc); - throw Base::ValueError(ssOcc.str().c_str()); - } + auto camerRotation = mdiObject->getViewer()->getCameraOrientation(); + + SbVec3f lookAt(0, 0, -1); + camerRotation.multVec(lookAt, lookAt); + + float valX {}; + float valY {}; + float valZ {}; + lookAt.getValue(valX, valY, valZ); + + ui->doubleSpinBoxDirX->setValue(valX); + ui->doubleSpinBoxDirY->setValue(valY); + ui->doubleSpinBoxDirZ->setValue(valZ); } -TopoDS_Shape PartGui::DlgProjectionOnSurface::create_compound(const std::vector& iShapeVec) +void PartGui::DlgProjectionOnSurface::store_current_selected_parts( + std::vector& iStoreVec, + unsigned int iColor) { - if (iShapeVec.empty()) - return {}; - - TopoDS_Compound aCompound; - TopoDS_Builder aBuilder; - aBuilder.MakeCompound(aCompound); - - for (const auto& it : iShapeVec) - { - if ( m_currentShowType == "edges" ) - { - for (const auto& it2 : it.aProjectedEdgeVec) - { - aBuilder.Add(aCompound, it2); - } - for (const auto& it2 : it.aProjectedWireVec) - { - aBuilder.Add(aCompound, it2); - } - continue; - } - else if ( m_currentShowType == "faces" ) - { - if (it.aProjectedFace.IsNull()) - { - for (const auto& it2 : it.aProjectedWireVec) - { - if (!it2.IsNull()) - { - aBuilder.Add(aCompound, it2); - } - } - } - else aBuilder.Add(aCompound, it.aProjectedFace); - continue; - } - else if ( m_currentShowType == "all" ) - { - if (!it.aProjectedSolid.IsNull()) - { - aBuilder.Add(aCompound, it.aProjectedSolid); - } - else if ( !it.aProjectedFace.IsNull() ) - { - aBuilder.Add(aCompound, it.aProjectedFace); - } - else if (!it.aProjectedWireVec.empty()) - { - for (const auto& itWire : it.aProjectedWireVec ) - { - if ( itWire.IsNull() ) continue; - aBuilder.Add(aCompound, itWire); - } - } - else if (!it.aProjectedEdgeVec.empty()) - { - for (const auto& itEdge : it.aProjectedEdgeVec) - { - if (itEdge.IsNull()) continue; - aBuilder.Add(aCompound, itEdge); - } - } - } - } - return TopoDS_Shape(std::move(aCompound)); -} - -void PartGui::DlgProjectionOnSurface::show_projected_shapes(const std::vector& iShapeStoreVec) -{ - if (!m_projectionObject) - return; - auto aCompound = create_compound(iShapeStoreVec); - if ( aCompound.IsNull() ) - { - if (!m_partDocument) + if (!m_partDocument) { return; - m_projectionObject->Shape.setValue(TopoDS_Shape()); - return; - } - auto currentPlacement = m_projectionObject->Placement.getValue(); - m_projectionObject->Shape.setValue(aCompound); - m_projectionObject->Placement.setValue(currentPlacement); + } + std::vector selObj = Gui::Selection().getSelectionEx(); + if (!selObj.empty()) { + for (auto it = selObj.begin(); it != selObj.end(); ++it) { + auto aPart = dynamic_cast(it->getObject()); + if (!aPart) { + continue; + } - //set color - PartGui::ViewProviderPartExt* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(m_projectionObject)); - if (vp) - { - vp->LineColor.setValue(0x8ae23400); - vp->ShapeColor.setValue(0x8ae23400); - vp->PointColor.setValue(0x8ae23400); - vp->Transparency.setValue(0); - } + if (aPart) { + SShapeStore currentShapeStore; + currentShapeStore.inputShape = aPart->Shape.getShape().getShape(); + currentShapeStore.partFeature = aPart; + currentShapeStore.partName = aPart->getNameInDocument(); + + auto vp = dynamic_cast( + Gui::Application::Instance->getViewProvider(aPart)); + if (vp) { + currentShapeStore.is_selectable = vp->Selectable.getValue(); + currentShapeStore.transparency = vp->Transparency.getValue(); + } + if (!it->getSubNames().empty()) { + auto parentShape = currentShapeStore.inputShape; + for (const auto& itName : selObj.front().getSubNames()) { + auto currentShape = aPart->Shape.getShape().getSubShape(itName.c_str()); + + transform_shape_to_global_position(currentShape, aPart); + + currentShapeStore.inputShape = currentShape; + currentShapeStore.partName = itName; + auto store = store_part_in_vector(currentShapeStore, iStoreVec); + higlight_object(aPart, itName, store, iColor); + store_wire_in_vector(currentShapeStore, parentShape, iStoreVec, iColor); + } + } + else { + transform_shape_to_global_position(currentShapeStore.inputShape, + currentShapeStore.partFeature); + auto store = store_part_in_vector(currentShapeStore, iStoreVec); + higlight_object(aPart, aPart->Shape.getName(), store, iColor); + } + Gui::Selection().clearSelection(m_partDocument->getName()); + Gui::Selection().rmvPreselect(); + } + } + } } -void PartGui::DlgProjectionOnSurface::disable_ui_elements(const std::vector& iObjectVec, QWidget* iExceptThis) +bool PartGui::DlgProjectionOnSurface::store_part_in_vector(SShapeStore& iCurrentShape, + std::vector& iStoreVec) { - for ( auto it : iObjectVec ) - { - if ( !it ) continue; - if ( it == iExceptThis ) continue; - it->setDisabled(true); - } + if (iCurrentShape.inputShape.IsNull()) { + return false; + } + auto currentType = iCurrentShape.inputShape.ShapeType(); + for (auto it = iStoreVec.begin(); it != iStoreVec.end(); ++it) { + if (currentType == TopAbs_FACE) { + if (it->aFace.IsSame(iCurrentShape.inputShape)) { + iStoreVec.erase(it); + return false; + } + } + else if (currentType == TopAbs_EDGE) { + if (it->aEdge.IsSame(iCurrentShape.inputShape)) { + iStoreVec.erase(it); + return false; + } + } + } + + if (currentType == TopAbs_FACE) { + iCurrentShape.aFace = TopoDS::Face(iCurrentShape.inputShape); + } + else if (currentType == TopAbs_EDGE) { + iCurrentShape.aEdge = TopoDS::Edge(iCurrentShape.inputShape); + } + + auto valX = ui->doubleSpinBoxDirX->value(); + auto valY = ui->doubleSpinBoxDirY->value(); + auto valZ = ui->doubleSpinBoxDirZ->value(); + + iCurrentShape.aProjectionDir = gp_Dir(valX, valY, valZ); + if (!m_projectionSurfaceVec.empty()) { + iCurrentShape.surfaceToProject = m_projectionSurfaceVec.front().aFace; + } + iStoreVec.push_back(iCurrentShape); + return true; } -void PartGui::DlgProjectionOnSurface::enable_ui_elements(const std::vector& iObjectVec, QWidget* iExceptThis) +void PartGui::DlgProjectionOnSurface::create_projection_wire( + std::vector& iCurrentShape) { - for (auto it : iObjectVec) - { - if (!it) continue; - if (it == iExceptThis) continue; - it->setEnabled(true); - } + try { + if (iCurrentShape.empty()) { + return; + } + for (auto& itCurrentShape : iCurrentShape) { + if (m_projectionSurfaceVec.empty()) { + continue; + }; + if (!itCurrentShape.aProjectedEdgeVec.empty()) { + continue; + }; + if (!itCurrentShape.aProjectedFace.IsNull()) { + continue; + }; + if (!itCurrentShape.aProjectedWireVec.empty()) { + continue; + }; + + if (!itCurrentShape.aFace.IsNull()) { + get_all_wire_from_face(itCurrentShape); + for (const auto& itWire : itCurrentShape.aWireVec) { + BRepProj_Projection aProjection(itWire, + itCurrentShape.surfaceToProject, + itCurrentShape.aProjectionDir); + double minDistance = std::numeric_limits::max(); + TopoDS_Wire wireToTake; + for (; aProjection.More(); aProjection.Next()) { + auto it = aProjection.Current(); + BRepExtrema_DistShapeShape distanceMeasure(it, itCurrentShape.aFace); + distanceMeasure.Perform(); + auto currentDistance = distanceMeasure.Value(); + if (currentDistance > minDistance) { + continue; + } + wireToTake = it; + minDistance = currentDistance; + } + auto aWire = sort_and_heal_wire(wireToTake, itCurrentShape.surfaceToProject); + itCurrentShape.aProjectedWireVec.push_back(aWire); + } + } + else if (!itCurrentShape.aEdge.IsNull()) { + BRepProj_Projection aProjection(itCurrentShape.aEdge, + itCurrentShape.surfaceToProject, + itCurrentShape.aProjectionDir); + double minDistance = std::numeric_limits::max(); + TopoDS_Wire wireToTake; + for (; aProjection.More(); aProjection.Next()) { + auto it = aProjection.Current(); + BRepExtrema_DistShapeShape distanceMeasure(it, itCurrentShape.aEdge); + distanceMeasure.Perform(); + auto currentDistance = distanceMeasure.Value(); + if (currentDistance > minDistance) { + continue; + } + wireToTake = it; + minDistance = currentDistance; + } + for (TopExp_Explorer aExplorer(wireToTake, TopAbs_EDGE); aExplorer.More(); + aExplorer.Next()) { + itCurrentShape.aProjectedEdgeVec.push_back(TopoDS::Edge(aExplorer.Current())); + } + } + } + } + catch (const Standard_Failure& error) { + std::stringstream ssOcc; + error.Print(ssOcc); + throw Base::ValueError(ssOcc.str().c_str()); + } } -void PartGui::DlgProjectionOnSurface::higlight_object(Part::Feature* iCurrentObject, const std::string& iShapeName, bool iHighlight, const unsigned int iColor) +TopoDS_Shape +PartGui::DlgProjectionOnSurface::create_compound(const std::vector& iShapeVec) { - if (!iCurrentObject) - return; - auto partenShape = iCurrentObject->Shape.getShape().getShape(); - auto subShape = iCurrentObject->Shape.getShape().getSubShape(iShapeName.c_str(), true); - - TopoDS_Shape currentShape = subShape; - if (subShape.IsNull()) currentShape = partenShape; - - auto currentShapeType = currentShape.ShapeType(); - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(partenShape, currentShapeType, anIndices); - if (anIndices.IsEmpty()) - return; - if (!anIndices.Contains(currentShape)) - return; - auto index = anIndices.FindIndex(currentShape); - - //set color - PartGui::ViewProviderPartExt* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(iCurrentObject)); - if (vp) - { - std::vector colors; - App::Color defaultColor; - if (currentShapeType == TopAbs_FACE) - { - colors = vp->DiffuseColor.getValues(); - defaultColor = vp->ShapeColor.getValue(); - } - else if ( currentShapeType == TopAbs_EDGE ) - { - colors = vp->LineColorArray.getValues(); - defaultColor = vp->LineColor.getValue(); + if (iShapeVec.empty()) { + return {}; } - if ( static_cast(colors.size()) != anIndices.Extent() ) - { - colors.resize(anIndices.Extent(), defaultColor); + TopoDS_Compound aCompound; + TopoDS_Builder aBuilder; + aBuilder.MakeCompound(aCompound); + + for (const auto& it : iShapeVec) { + if (m_currentShowType == "edges") { + for (const auto& it2 : it.aProjectedEdgeVec) { + aBuilder.Add(aCompound, it2); + } + for (const auto& it2 : it.aProjectedWireVec) { + aBuilder.Add(aCompound, it2); + } + } + else if (m_currentShowType == "faces") { + if (it.aProjectedFace.IsNull()) { + for (const auto& it2 : it.aProjectedWireVec) { + if (!it2.IsNull()) { + aBuilder.Add(aCompound, it2); + } + } + } + else { + aBuilder.Add(aCompound, it.aProjectedFace); + } + } + else if (m_currentShowType == "all") { + if (!it.aProjectedSolid.IsNull()) { + aBuilder.Add(aCompound, it.aProjectedSolid); + } + else if (!it.aProjectedFace.IsNull()) { + aBuilder.Add(aCompound, it.aProjectedFace); + } + else if (!it.aProjectedWireVec.empty()) { + for (const auto& itWire : it.aProjectedWireVec) { + if (itWire.IsNull()) { + continue; + } + aBuilder.Add(aCompound, itWire); + } + } + else if (!it.aProjectedEdgeVec.empty()) { + for (const auto& itEdge : it.aProjectedEdgeVec) { + if (itEdge.IsNull()) { + continue; + } + aBuilder.Add(aCompound, itEdge); + } + } + } + } + return {std::move(aCompound)}; +} + +void PartGui::DlgProjectionOnSurface::show_projected_shapes( + const std::vector& iShapeStoreVec) +{ + if (!m_projectionObject) { + return; + } + auto aCompound = create_compound(iShapeStoreVec); + if (aCompound.IsNull()) { + if (!m_partDocument) { + return; + } + m_projectionObject->Shape.setValue(TopoDS_Shape()); + return; + } + auto currentPlacement = m_projectionObject->Placement.getValue(); + m_projectionObject->Shape.setValue(aCompound); + m_projectionObject->Placement.setValue(currentPlacement); + + // set color + auto vp = dynamic_cast( + Gui::Application::Instance->getViewProvider(m_projectionObject)); + if (vp) { + const unsigned int color = 0x8ae23400; + vp->LineColor.setValue(color); + vp->ShapeColor.setValue(color); + vp->PointColor.setValue(color); + vp->Transparency.setValue(0); + } +} + +void PartGui::DlgProjectionOnSurface::disable_ui_elements(const std::vector& iObjectVec, + QWidget* iExceptThis) +{ + for (auto it : iObjectVec) { + if (!it) { + continue; + } + if (it == iExceptThis) { + continue; + } + it->setDisabled(true); + } +} + +void PartGui::DlgProjectionOnSurface::enable_ui_elements(const std::vector& iObjectVec, + QWidget* iExceptThis) +{ + for (auto it : iObjectVec) { + if (!it) { + continue; + } + if (it == iExceptThis) { + continue; + } + it->setEnabled(true); + } +} + +void PartGui::DlgProjectionOnSurface::higlight_object(Part::Feature* iCurrentObject, + const std::string& iShapeName, + bool iHighlight, + unsigned int iColor) +{ + if (!iCurrentObject) { + return; + } + auto partenShape = iCurrentObject->Shape.getShape().getShape(); + auto subShape = iCurrentObject->Shape.getShape().getSubShape(iShapeName.c_str(), true); + + TopoDS_Shape currentShape = subShape; + if (subShape.IsNull()) { + currentShape = partenShape; } - if ( iHighlight ) - { - App::Color aColor; - aColor.setPackedValue(iColor); - colors.at(index - 1) = aColor; + auto currentShapeType = currentShape.ShapeType(); + TopTools_IndexedMapOfShape anIndices; + TopExp::MapShapes(partenShape, currentShapeType, anIndices); + if (anIndices.IsEmpty()) { + return; } - else - { - colors.at(index - 1) = defaultColor; + if (!anIndices.Contains(currentShape)) { + return; } - if (currentShapeType == TopAbs_FACE) - { - vp->DiffuseColor.setValues(colors); + auto index = anIndices.FindIndex(currentShape); + + // set color + auto vp = dynamic_cast( + Gui::Application::Instance->getViewProvider(iCurrentObject)); + if (vp) { + std::vector colors; + App::Color defaultColor; + if (currentShapeType == TopAbs_FACE) { + colors = vp->DiffuseColor.getValues(); + defaultColor = vp->ShapeColor.getValue(); + } + else if (currentShapeType == TopAbs_EDGE) { + colors = vp->LineColorArray.getValues(); + defaultColor = vp->LineColor.getValue(); + } + + if (static_cast(colors.size()) != anIndices.Extent()) { + colors.resize(anIndices.Extent(), defaultColor); + } + + if (iHighlight) { + App::Color aColor; + aColor.setPackedValue(iColor); + colors.at(index - 1) = aColor; + } + else { + colors.at(index - 1) = defaultColor; + } + if (currentShapeType == TopAbs_FACE) { + vp->DiffuseColor.setValues(colors); + } + else if (currentShapeType == TopAbs_EDGE) { + vp->LineColorArray.setValues(colors); + } } - else if (currentShapeType == TopAbs_EDGE) - { - vp->LineColorArray.setValues(colors); - } - } } void PartGui::DlgProjectionOnSurface::get_all_wire_from_face(SShapeStore& ioCurrentSahpe) { - auto outerWire = ShapeAnalysis::OuterWire(ioCurrentSahpe.aFace); - ioCurrentSahpe.aWireVec.push_back(outerWire); - for (TopExp_Explorer aExplorer(ioCurrentSahpe.aFace, TopAbs_WIRE); aExplorer.More(); aExplorer.Next()) - { - auto currentWire = TopoDS::Wire(aExplorer.Current()); - if (currentWire.IsSame(outerWire)) continue; - ioCurrentSahpe.aWireVec.push_back(currentWire); - } -} - -void PartGui::DlgProjectionOnSurface::create_projection_face_from_wire(std::vector& iCurrentShape) -{ - try - { - if (iCurrentShape.empty()) - return; - - for ( auto &itCurrentShape : iCurrentShape ) - { - if (itCurrentShape.aFace.IsNull()) continue;; - if (itCurrentShape.aProjectedWireVec.empty()) continue;; - if (!itCurrentShape.aProjectedFace.IsNull()) continue;; - - auto surface = BRep_Tool::Surface(itCurrentShape.surfaceToProject); - - //create a wire of all edges in parametric space on the surface of the face to projected - // --> otherwise BRepBuilderAPI_MakeFace can not make a face from the wire! - for (const auto& itWireVec : itCurrentShape.aProjectedWireVec) - { - std::vector edgeVec; - for (TopExp_Explorer aExplorer(itWireVec, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) - { - auto currentEdge = TopoDS::Edge(aExplorer.Current()); - edgeVec.push_back(currentEdge); + auto outerWire = ShapeAnalysis::OuterWire(ioCurrentSahpe.aFace); + ioCurrentSahpe.aWireVec.push_back(outerWire); + for (TopExp_Explorer aExplorer(ioCurrentSahpe.aFace, TopAbs_WIRE); aExplorer.More(); + aExplorer.Next()) { + auto currentWire = TopoDS::Wire(aExplorer.Current()); + if (currentWire.IsSame(outerWire)) { + continue; } - if (edgeVec.empty()) continue; - - std::vector edgeInParametricSpaceVec; - for (auto itEdge : edgeVec) - { - Standard_Real first, last; - auto currentCurve = BRep_Tool::CurveOnSurface(TopoDS::Edge(itEdge), itCurrentShape.surfaceToProject, first, last); - if (!currentCurve) continue; - auto edgeInParametricSpace = BRepBuilderAPI_MakeEdge(currentCurve, surface, first,last).Edge(); - edgeInParametricSpaceVec.push_back(edgeInParametricSpace); - } - auto aWire = sort_and_heal_wire(edgeInParametricSpaceVec, itCurrentShape.surfaceToProject); - itCurrentShape.aProjectedWireInParametricSpaceVec.push_back(aWire); - } - - // try to create a face from the wires - // the first wire is the otherwise - // the following wires are the inside wires - BRepBuilderAPI_MakeFace faceMaker; - bool first = true; - for (auto itWireVec : itCurrentShape.aProjectedWireInParametricSpaceVec) - { - if (first) - { - first = false; - // change the wire direction, otherwise no face is created - auto currentWire = TopoDS::Wire(itWireVec.Reversed()); - if (itCurrentShape.surfaceToProject.Orientation() == TopAbs_REVERSED) currentWire = itWireVec; - faceMaker = BRepBuilderAPI_MakeFace(surface, currentWire); - ShapeFix_Face fix(faceMaker.Face()); - fix.Perform(); - auto aFace = fix.Face(); - BRepCheck_Analyzer aChecker(aFace); - if (!aChecker.IsValid()) - { - faceMaker = BRepBuilderAPI_MakeFace(surface, TopoDS::Wire(currentWire.Reversed())); - } - } - else - { - // make a copy of the current face maker - // if the face fails just try again with the copy - TopoDS_Face tempCopy = BRepBuilderAPI_MakeFace(faceMaker.Face()).Face(); - faceMaker.Add(TopoDS::Wire(itWireVec.Reversed())); - ShapeFix_Face fix(faceMaker.Face()); - fix.Perform(); - auto aFace = fix.Face(); - BRepCheck_Analyzer aChecker(aFace); - if (!aChecker.IsValid()) - { - faceMaker = BRepBuilderAPI_MakeFace(tempCopy); - faceMaker.Add(TopoDS::Wire(itWireVec)); - } - } - } - //auto doneFlag = faceMaker.IsDone(); - //auto error = faceMaker.Error(); - itCurrentShape.aProjectedFace = faceMaker.Face(); + ioCurrentSahpe.aWireVec.push_back(currentWire); } - } - catch (const Standard_Failure& error) - { - std::stringstream ssOcc; - error.Print(ssOcc); - throw Base::ValueError(ssOcc.str().c_str()); - } } -TopoDS_Wire PartGui::DlgProjectionOnSurface::sort_and_heal_wire(const TopoDS_Shape& iShape, const TopoDS_Face& iFaceToProject) +void PartGui::DlgProjectionOnSurface::create_projection_face_from_wire( + std::vector& iCurrentShape) { - std::vector aEdgeVec; - for (TopExp_Explorer aExplorer(iShape, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) - { - auto anEdge = TopoDS::Edge(aExplorer.Current()); - aEdgeVec.push_back(anEdge); - } - return sort_and_heal_wire(aEdgeVec, iFaceToProject); + try { + if (iCurrentShape.empty()) { + return; + } + + for (auto& itCurrentShape : iCurrentShape) { + if (itCurrentShape.aFace.IsNull()) { + continue; + }; + if (itCurrentShape.aProjectedWireVec.empty()) { + continue; + }; + if (!itCurrentShape.aProjectedFace.IsNull()) { + continue; + }; + + auto surface = BRep_Tool::Surface(itCurrentShape.surfaceToProject); + + // create a wire of all edges in parametric space on the surface of the face to + // projected + // --> otherwise BRepBuilderAPI_MakeFace can not make a face from the wire! + for (const auto& itWireVec : itCurrentShape.aProjectedWireVec) { + std::vector edgeVec; + for (TopExp_Explorer aExplorer(itWireVec, TopAbs_EDGE); aExplorer.More(); + aExplorer.Next()) { + auto currentEdge = TopoDS::Edge(aExplorer.Current()); + edgeVec.push_back(currentEdge); + } + if (edgeVec.empty()) { + continue; + } + + std::vector edgeInParametricSpaceVec; + for (auto itEdge : edgeVec) { + Standard_Real first {}; + Standard_Real last {}; + auto currentCurve = BRep_Tool::CurveOnSurface(TopoDS::Edge(itEdge), + itCurrentShape.surfaceToProject, + first, + last); + if (!currentCurve) { + continue; + } + auto edgeInParametricSpace = + BRepBuilderAPI_MakeEdge(currentCurve, surface, first, last).Edge(); + edgeInParametricSpaceVec.push_back(edgeInParametricSpace); + } + auto aWire = + sort_and_heal_wire(edgeInParametricSpaceVec, itCurrentShape.surfaceToProject); + itCurrentShape.aProjectedWireInParametricSpaceVec.push_back(aWire); + } + + // try to create a face from the wires + // the first wire is the otherwise + // the following wires are the inside wires + BRepBuilderAPI_MakeFace faceMaker; + bool first = true; + for (auto itWireVec : itCurrentShape.aProjectedWireInParametricSpaceVec) { + if (first) { + first = false; + // change the wire direction, otherwise no face is created + auto currentWire = TopoDS::Wire(itWireVec.Reversed()); + if (itCurrentShape.surfaceToProject.Orientation() == TopAbs_REVERSED) { + currentWire = itWireVec; + } + faceMaker = BRepBuilderAPI_MakeFace(surface, currentWire); + ShapeFix_Face fix(faceMaker.Face()); + fix.Perform(); + auto aFace = fix.Face(); + BRepCheck_Analyzer aChecker(aFace); + if (!aChecker.IsValid()) { + faceMaker = + BRepBuilderAPI_MakeFace(surface, TopoDS::Wire(currentWire.Reversed())); + } + } + else { + // make a copy of the current face maker + // if the face fails just try again with the copy + TopoDS_Face tempCopy = BRepBuilderAPI_MakeFace(faceMaker.Face()).Face(); + faceMaker.Add(TopoDS::Wire(itWireVec.Reversed())); + ShapeFix_Face fix(faceMaker.Face()); + fix.Perform(); + auto aFace = fix.Face(); + BRepCheck_Analyzer aChecker(aFace); + if (!aChecker.IsValid()) { + faceMaker = BRepBuilderAPI_MakeFace(tempCopy); + faceMaker.Add(TopoDS::Wire(itWireVec)); + } + } + } + // auto doneFlag = faceMaker.IsDone(); + // auto error = faceMaker.Error(); + itCurrentShape.aProjectedFace = faceMaker.Face(); + } + } + catch (const Standard_Failure& error) { + std::stringstream ssOcc; + error.Print(ssOcc); + throw Base::ValueError(ssOcc.str().c_str()); + } } -TopoDS_Wire PartGui::DlgProjectionOnSurface::sort_and_heal_wire(const std::vector& iEdgeVec, const TopoDS_Face& iFaceToProject) +TopoDS_Wire PartGui::DlgProjectionOnSurface::sort_and_heal_wire(const TopoDS_Shape& iShape, + const TopoDS_Face& iFaceToProject) { - // try to sort and heal all wires -// if the wires are not clean making a face will fail! - ShapeAnalysis_FreeBounds shapeAnalyzer; - Handle(TopTools_HSequenceOfShape) shapeList = new TopTools_HSequenceOfShape; - Handle(TopTools_HSequenceOfShape) aWireHandle; - Handle(TopTools_HSequenceOfShape) aWireWireHandle; + std::vector aEdgeVec; + for (TopExp_Explorer aExplorer(iShape, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) { + auto anEdge = TopoDS::Edge(aExplorer.Current()); + aEdgeVec.push_back(anEdge); + } + return sort_and_heal_wire(aEdgeVec, iFaceToProject); +} - for (const auto& it : iEdgeVec) - { - shapeList->Append(it); - } +TopoDS_Wire +PartGui::DlgProjectionOnSurface::sort_and_heal_wire(const std::vector& iEdgeVec, + const TopoDS_Face& iFaceToProject) +{ + // try to sort and heal all wires + // if the wires are not clean making a face will fail! + ShapeAnalysis_FreeBounds shapeAnalyzer; + Handle(TopTools_HSequenceOfShape) shapeList = new TopTools_HSequenceOfShape; + Handle(TopTools_HSequenceOfShape) aWireHandle; + Handle(TopTools_HSequenceOfShape) aWireWireHandle; - shapeAnalyzer.ConnectEdgesToWires(shapeList, 0.0001, false, aWireHandle); - shapeAnalyzer.ConnectWiresToWires(aWireHandle, 0.0001, false, aWireWireHandle); - if (!aWireWireHandle) - return {}; - for (auto it = 1; it <= aWireWireHandle->Length(); ++it) - { - auto aShape = TopoDS::Wire(aWireWireHandle->Value(it)); - ShapeFix_Wire aWireRepair(aShape, iFaceToProject, 0.0001); - aWireRepair.FixAddCurve3dMode() = 1; - aWireRepair.FixAddPCurveMode() = 1; - aWireRepair.Perform(); - //return aWireRepair.Wire(); - ShapeFix_Wireframe aWireFramFix(aWireRepair.Wire()); - auto retVal = aWireFramFix.FixWireGaps(); - retVal = aWireFramFix.FixSmallEdges(); - Q_UNUSED(retVal); - return TopoDS::Wire(aWireFramFix.Shape()); - } - return {}; + for (const auto& it : iEdgeVec) { + shapeList->Append(it); + } + + const double tolerance = 0.0001; + ShapeAnalysis_FreeBounds::ConnectEdgesToWires(shapeList, tolerance, false, aWireHandle); + ShapeAnalysis_FreeBounds::ConnectWiresToWires(aWireHandle, tolerance, false, aWireWireHandle); + if (!aWireWireHandle) { + return {}; + } + for (auto it = 1; it <= aWireWireHandle->Length(); ++it) { + auto aShape = TopoDS::Wire(aWireWireHandle->Value(it)); + ShapeFix_Wire aWireRepair(aShape, iFaceToProject, tolerance); + aWireRepair.FixAddCurve3dMode() = 1; + aWireRepair.FixAddPCurveMode() = 1; + aWireRepair.Perform(); + // return aWireRepair.Wire(); + ShapeFix_Wireframe aWireFramFix(aWireRepair.Wire()); + aWireFramFix.FixWireGaps(); + aWireFramFix.FixSmallEdges(); + return TopoDS::Wire(aWireFramFix.Shape()); + } + return {}; } void PartGui::DlgProjectionOnSurface::create_face_extrude(std::vector& iCurrentShape) { - try - { - if (iCurrentShape.empty()) - return; + try { + if (iCurrentShape.empty()) { + return; + } - auto height = ui->doubleSpinBoxExtrudeHeight->value(); + auto height = ui->doubleSpinBoxExtrudeHeight->value(); - for ( auto &itCurrentShape : iCurrentShape ) - { - if (itCurrentShape.aProjectedFace.IsNull()) continue;; - if (itCurrentShape.extrudeValue == height) continue;; + for (auto& itCurrentShape : iCurrentShape) { + if (itCurrentShape.aProjectedFace.IsNull()) { + continue; + } + if (itCurrentShape.extrudeValue == height) { + continue; + } - itCurrentShape.extrudeValue = height; - if (height == 0) - { - itCurrentShape.aProjectedSolid.Nullify(); - } - else - { - gp_Vec directionToExtrude(itCurrentShape.aProjectionDir.XYZ()); - directionToExtrude.Reverse(); - directionToExtrude.Multiply(height); - BRepPrimAPI_MakePrism extrude(itCurrentShape.aProjectedFace, directionToExtrude); - itCurrentShape.aProjectedSolid = extrude.Shape(); - } + itCurrentShape.extrudeValue = height; + if (height == 0) { + itCurrentShape.aProjectedSolid.Nullify(); + } + else { + gp_Vec directionToExtrude(itCurrentShape.aProjectionDir.XYZ()); + directionToExtrude.Reverse(); + directionToExtrude.Multiply(height); + BRepPrimAPI_MakePrism extrude(itCurrentShape.aProjectedFace, directionToExtrude); + itCurrentShape.aProjectedSolid = extrude.Shape(); + } + } + } + catch (const Standard_Failure& error) { + std::stringstream ssOcc; + error.Print(ssOcc); + throw Base::ValueError(ssOcc.str().c_str()); } - } - catch (const Standard_Failure& error) - { - std::stringstream ssOcc; - error.Print(ssOcc); - throw Base::ValueError(ssOcc.str().c_str()); - } } -void PartGui::DlgProjectionOnSurface::store_wire_in_vector(const SShapeStore& iCurrentShape, const TopoDS_Shape& iParentShape, std::vector& iStoreVec, const unsigned int iColor) +void PartGui::DlgProjectionOnSurface::store_wire_in_vector(const SShapeStore& iCurrentShape, + const TopoDS_Shape& iParentShape, + std::vector& iStoreVec, + unsigned int iColor) { - if (m_currentSelection != "add_wire") - return; - if (iParentShape.IsNull()) - return; - if (iCurrentShape.inputShape.IsNull()) - return; - auto currentType = iCurrentShape.inputShape.ShapeType(); - if (currentType != TopAbs_EDGE) - return; - - std::vector aWireVec; - for (TopExp_Explorer aExplorer(iParentShape, TopAbs_WIRE); aExplorer.More(); aExplorer.Next()) - { - aWireVec.push_back(TopoDS::Wire(aExplorer.Current())); - } - - std::vector edgeVec; - for (const auto& it : aWireVec ) - { - bool edgeExists = false; - for (TopExp_Explorer aExplorer(it, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) - { - auto currentEdge = TopoDS::Edge(aExplorer.Current()); - edgeVec.push_back(currentEdge); - if (currentEdge.IsSame(iCurrentShape.inputShape)) edgeExists = true; - } - if (edgeExists) break; - edgeVec.clear(); - } - - if (edgeVec.empty()) - return; - TopTools_IndexedMapOfShape indexMap; - TopExp::MapShapes(iParentShape, TopAbs_EDGE, indexMap); - if (indexMap.IsEmpty()) - return; - - for (const auto& it : edgeVec ) - { - if ( it.IsSame(iCurrentShape.inputShape)) continue; - if (!indexMap.Contains(it)) + if (m_currentSelection != "add_wire") { return; - auto index = indexMap.FindIndex(it); - auto newEdgeObject = iCurrentShape; - newEdgeObject.inputShape = it; - newEdgeObject.partName = "Edge" + std::to_string(index); + } + if (iParentShape.IsNull()) { + return; + } + if (iCurrentShape.inputShape.IsNull()) { + return; + } + auto currentType = iCurrentShape.inputShape.ShapeType(); + if (currentType != TopAbs_EDGE) { + return; + } - auto store = store_part_in_vector(newEdgeObject, iStoreVec); - higlight_object(newEdgeObject.partFeature, newEdgeObject.partName, store, iColor); - } + std::vector aWireVec; + for (TopExp_Explorer aExplorer(iParentShape, TopAbs_WIRE); aExplorer.More(); aExplorer.Next()) { + aWireVec.push_back(TopoDS::Wire(aExplorer.Current())); + } + + std::vector edgeVec; + for (const auto& it : aWireVec) { + bool edgeExists = false; + for (TopExp_Explorer aExplorer(it, TopAbs_EDGE); aExplorer.More(); aExplorer.Next()) { + auto currentEdge = TopoDS::Edge(aExplorer.Current()); + edgeVec.push_back(currentEdge); + if (currentEdge.IsSame(iCurrentShape.inputShape)) { + edgeExists = true; + } + } + if (edgeExists) { + break; + } + edgeVec.clear(); + } + + if (edgeVec.empty()) { + return; + } + TopTools_IndexedMapOfShape indexMap; + TopExp::MapShapes(iParentShape, TopAbs_EDGE, indexMap); + if (indexMap.IsEmpty()) { + return; + } + + for (const auto& it : edgeVec) { + if (it.IsSame(iCurrentShape.inputShape)) { + continue; + } + if (!indexMap.Contains(it)) { + return; + } + auto index = indexMap.FindIndex(it); + auto newEdgeObject = iCurrentShape; + newEdgeObject.inputShape = it; + newEdgeObject.partName = "Edge" + std::to_string(index); + + auto store = store_part_in_vector(newEdgeObject, iStoreVec); + higlight_object(newEdgeObject.partFeature, newEdgeObject.partName, store, iColor); + } } void PartGui::DlgProjectionOnSurface::set_xyz_dir_spinbox(QDoubleSpinBox* icurrentSpinBox) { - auto currentVal = icurrentSpinBox->value(); - auto newVal = 0.0; - if (currentVal != 1.0 && currentVal != -1.0) - { - newVal = -1; - } - else if (currentVal == 1.0) - { - newVal = -1; - } - else if (currentVal == -1.0) - { - newVal = 1; - } - ui->doubleSpinBoxDirX->setValue(0); - ui->doubleSpinBoxDirY->setValue(0); - ui->doubleSpinBoxDirZ->setValue(0); - icurrentSpinBox->setValue(newVal); + auto currentVal = icurrentSpinBox->value(); + auto newVal = 0.0; + if (currentVal != 1.0 && currentVal != -1.0) { + newVal = -1; + } + else if (currentVal == 1.0) { + newVal = -1; + } + else if (currentVal == -1.0) { + newVal = 1; + } + ui->doubleSpinBoxDirX->setValue(0); + ui->doubleSpinBoxDirY->setValue(0); + ui->doubleSpinBoxDirZ->setValue(0); + icurrentSpinBox->setValue(newVal); } -void PartGui::DlgProjectionOnSurface::transform_shape_to_global_position(TopoDS_Shape& ioShape, Part::Feature* iPart) +void PartGui::DlgProjectionOnSurface::transform_shape_to_global_position(TopoDS_Shape& ioShape, + Part::Feature* iPart) { - auto currentPos = iPart->Placement.getValue().getPosition(); - auto currentRotation = iPart->Placement.getValue().getRotation(); - auto globalPlacement = iPart->globalPlacement(); - auto globalPosition = globalPlacement.getPosition(); - auto globalRotation = globalPlacement.getRotation(); + auto currentPos = iPart->Placement.getValue().getPosition(); + auto currentRotation = iPart->Placement.getValue().getRotation(); + auto globalPlacement = iPart->globalPlacement(); + auto globalPosition = globalPlacement.getPosition(); + auto globalRotation = globalPlacement.getRotation(); - if (currentRotation != globalRotation) - { - auto newRotation = globalRotation; - newRotation *= currentRotation.invert(); + if (currentRotation != globalRotation) { + auto newRotation = globalRotation; + newRotation *= currentRotation.invert(); - gp_Trsf aAngleTransform; - Base::Vector3d rotationAxes; - double rotationAngle; - newRotation.getRawValue(rotationAxes, rotationAngle); - aAngleTransform.SetRotation(gp_Ax1(gp_Pnt(currentPos.x, currentPos.y, currentPos.z), gp_Dir(rotationAxes.x, rotationAxes.y, rotationAxes.z)), rotationAngle); - ioShape = BRepBuilderAPI_Transform(ioShape, aAngleTransform, true).Shape(); - } + gp_Trsf aAngleTransform; + Base::Vector3d rotationAxes; + double rotationAngle {}; + newRotation.getRawValue(rotationAxes, rotationAngle); + aAngleTransform.SetRotation(gp_Ax1(gp_Pnt(currentPos.x, currentPos.y, currentPos.z), + gp_Dir(rotationAxes.x, rotationAxes.y, rotationAxes.z)), + rotationAngle); + ioShape = BRepBuilderAPI_Transform(ioShape, aAngleTransform, true).Shape(); + } - if (currentPos != globalPosition) - { - gp_Trsf aPosTransform; - aPosTransform.SetTranslation(gp_Pnt(currentPos.x, currentPos.y, currentPos.z), gp_Pnt(globalPosition.x, globalPosition.y, globalPosition.z)); - ioShape = BRepBuilderAPI_Transform(ioShape, aPosTransform, true).Shape(); - } + if (currentPos != globalPosition) { + gp_Trsf aPosTransform; + aPosTransform.SetTranslation(gp_Pnt(currentPos.x, currentPos.y, currentPos.z), + gp_Pnt(globalPosition.x, globalPosition.y, globalPosition.z)); + ioShape = BRepBuilderAPI_Transform(ioShape, aPosTransform, true).Shape(); + } } void PartGui::DlgProjectionOnSurface::onPushButtonAddProjFaceClicked() { - if (ui->pushButtonAddProjFace->isChecked()) - { - m_currentSelection = "add_projection_surface"; - disable_ui_elements(m_guiObjectVec, ui->pushButtonAddProjFace); - if (!filterFace) - { - filterFace = new FaceSelection(); - Gui::Selection().addSelectionGate(filterFace); + if (ui->pushButtonAddProjFace->isChecked()) { + m_currentSelection = "add_projection_surface"; + disable_ui_elements(m_guiObjectVec, ui->pushButtonAddProjFace); + if (!filterFace) { + filterFace = new FaceSelection(); + Gui::Selection().addSelectionGate(filterFace); + } + } + else { + m_currentSelection = ""; + enable_ui_elements(m_guiObjectVec, nullptr); + Gui::Selection().rmvSelectionGate(); + filterFace = nullptr; } - } - else - { - m_currentSelection = ""; - enable_ui_elements(m_guiObjectVec, nullptr); - Gui::Selection().rmvSelectionGate(); - filterFace = nullptr; - } } void PartGui::DlgProjectionOnSurface::onRadioButtonShowAllClicked() { - m_currentShowType = "all"; - show_projected_shapes(m_shapeVec); + m_currentShowType = "all"; + show_projected_shapes(m_shapeVec); } void PartGui::DlgProjectionOnSurface::onRadioButtonFacesClicked() { - m_currentShowType = "faces"; - show_projected_shapes(m_shapeVec); + m_currentShowType = "faces"; + show_projected_shapes(m_shapeVec); } void PartGui::DlgProjectionOnSurface::onRadioButtonEdgesClicked() { - m_currentShowType = "edges"; - show_projected_shapes(m_shapeVec); + m_currentShowType = "edges"; + show_projected_shapes(m_shapeVec); } void PartGui::DlgProjectionOnSurface::onDoubleSpinBoxExtrudeHeightValueChanged(double arg1) { - Q_UNUSED(arg1); - create_face_extrude(m_shapeVec); - show_projected_shapes(m_shapeVec); + Q_UNUSED(arg1); + create_face_extrude(m_shapeVec); + show_projected_shapes(m_shapeVec); } void PartGui::DlgProjectionOnSurface::onPushButtonAddWireClicked() { - if (ui->pushButtonAddWire->isChecked()) - { - m_currentSelection = "add_wire"; - disable_ui_elements(m_guiObjectVec, ui->pushButtonAddWire); - if (!filterEdge) - { - filterEdge = new EdgeSelection(); - Gui::Selection().addSelectionGate(filterEdge); + if (ui->pushButtonAddWire->isChecked()) { + m_currentSelection = "add_wire"; + disable_ui_elements(m_guiObjectVec, ui->pushButtonAddWire); + if (!filterEdge) { + filterEdge = new EdgeSelection(); + Gui::Selection().addSelectionGate(filterEdge); + } + ui->radioButtonEdges->setChecked(true); + onRadioButtonEdgesClicked(); + } + else { + m_currentSelection = ""; + enable_ui_elements(m_guiObjectVec, nullptr); + Gui::Selection().rmvSelectionGate(); + filterEdge = nullptr; } - ui->radioButtonEdges->setChecked(true); - onRadioButtonEdgesClicked(); - } - else - { - m_currentSelection = ""; - enable_ui_elements(m_guiObjectVec, nullptr); - Gui::Selection().rmvSelectionGate(); - filterEdge = nullptr; - } } void PartGui::DlgProjectionOnSurface::onDoubleSpinBoxSolidDepthValueChanged(double arg1) { - auto valX = ui->doubleSpinBoxDirX->value(); - auto valY = ui->doubleSpinBoxDirY->value(); - auto valZ = ui->doubleSpinBoxDirZ->value(); + auto valX = ui->doubleSpinBoxDirX->value(); + auto valY = ui->doubleSpinBoxDirY->value(); + auto valZ = ui->doubleSpinBoxDirZ->value(); - auto valueToMove = arg1 - m_lastDepthVal; - Base::Vector3d vectorToMove(valX, valY, valZ); - vectorToMove *= valueToMove; + auto valueToMove = arg1 - m_lastDepthVal; + Base::Vector3d vectorToMove(valX, valY, valZ); + vectorToMove *= valueToMove; - auto placment = m_projectionObject->Placement.getValue(); - placment.move(vectorToMove); - m_projectionObject->Placement.setValue(placment); + auto placment = m_projectionObject->Placement.getValue(); + placment.move(vectorToMove); + m_projectionObject->Placement.setValue(placment); - m_lastDepthVal = ui->doubleSpinBoxSolidDepth->value(); + m_lastDepthVal = ui->doubleSpinBoxSolidDepth->value(); } // --------------------------------------- TaskProjectionOnSurface::TaskProjectionOnSurface() + : widget(new DlgProjectionOnSurface()) + , taskbox(new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("Part_ProjectionOnSurface"), + widget->windowTitle(), + true, + nullptr)) { - widget = new DlgProjectionOnSurface(); - taskbox = new Gui::TaskView::TaskBox( - Gui::BitmapFactory().pixmap("Part_ProjectionOnSurface"), - widget->windowTitle(), true, nullptr); - taskbox->groupLayout()->addWidget(widget); - Content.push_back(taskbox); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); } bool TaskProjectionOnSurface::accept() { - widget->apply(); - return true; - //return (widget->result() == QDialog::Accepted); + widget->apply(); + return true; } bool TaskProjectionOnSurface::reject() { - widget->reject(); - return true; + widget->reject(); + return true; } void TaskProjectionOnSurface::clicked(int id) { - if (id == QDialogButtonBox::Apply) { - try { - widget->apply(); + if (id == QDialogButtonBox::Apply) { + try { + widget->apply(); + } + catch (Base::AbortException&) { + } } - catch (Base::AbortException&) { - - }; - } } #include "moc_DlgProjectionOnSurface.cpp" diff --git a/src/Mod/Part/Gui/DlgProjectionOnSurface.h b/src/Mod/Part/Gui/DlgProjectionOnSurface.h index ec1cd285b0..4a4f6c10bc 100644 --- a/src/Mod/Part/Gui/DlgProjectionOnSurface.h +++ b/src/Mod/Part/Gui/DlgProjectionOnSurface.h @@ -36,22 +36,24 @@ #include -namespace PartGui { +namespace PartGui +{ - class Ui_DlgProjectionOnSurface; +class Ui_DlgProjectionOnSurface; - namespace Ui { - class DlgProjectionOnSurface; - } +namespace Ui +{ +class DlgProjectionOnSurface; +} -class DlgProjectionOnSurface : public QWidget, - public Gui::SelectionObserver, - public App::DocumentObserver +class DlgProjectionOnSurface: public QWidget, + public Gui::SelectionObserver, + public App::DocumentObserver { Q_OBJECT public: - explicit DlgProjectionOnSurface(QWidget *parent = nullptr); + explicit DlgProjectionOnSurface(QWidget* parent = nullptr); ~DlgProjectionOnSurface() override; void apply(); @@ -74,57 +76,64 @@ private: void onDoubleSpinBoxSolidDepthValueChanged(double arg1); private: + struct SShapeStore + { + TopoDS_Shape inputShape; + TopoDS_Face surfaceToProject; + gp_Dir aProjectionDir; + TopoDS_Face aFace; + TopoDS_Edge aEdge; + std::vector aWireVec; + std::vector aProjectedWireVec; + std::vector aProjectedEdgeVec; + std::vector aProjectedWireInParametricSpaceVec; + TopoDS_Face aProjectedFace; + TopoDS_Shape aProjectedSolid; + Part::Feature* partFeature = nullptr; + std::string partName; + bool is_selectable = false; + long transparency = 0; + double extrudeValue = 0.0; + }; - struct SShapeStore - { - TopoDS_Shape inputShape; - TopoDS_Face surfaceToProject; - gp_Dir aProjectionDir; - TopoDS_Face aFace; - TopoDS_Edge aEdge; - std::vector aWireVec; - std::vector aProjectedWireVec; - std::vector aProjectedEdgeVec; - std::vector aProjectedWireInParametricSpaceVec; - TopoDS_Face aProjectedFace; - TopoDS_Shape aProjectedSolid; - Part::Feature* partFeature = nullptr; - std::string partName; - bool is_selectable = false; - long transparency = 0; - float extrudeValue = 0.0f; - }; - - //from Gui::SelectionObserver - void onSelectionChanged(const Gui::SelectionChanges& msg) override; + // from Gui::SelectionObserver + void onSelectionChanged(const Gui::SelectionChanges& msg) override; - void get_camera_direction(); - void store_current_selected_parts(std::vector& iStoreVec, const unsigned int iColor); - bool store_part_in_vector(SShapeStore& iCurrentShape, std::vector& iStoreVec); - void create_projection_wire(std::vector& iCurrentShape); - TopoDS_Shape create_compound(const std::vector& iShapeVec); - void show_projected_shapes(const std::vector& iShapeStoreVec); - void disable_ui_elements(const std::vector& iObjectVec, QWidget* iExceptThis); - void enable_ui_elements(const std::vector& iObjectVec, QWidget* iExceptThis); - void higlight_object(Part::Feature* iCurrentObject, const std::string& iShapeName, bool iHighlight, const unsigned int iColor); - void get_all_wire_from_face(SShapeStore& ioCurrentSahpe); - void create_projection_face_from_wire(std::vector& iCurrentShape); - TopoDS_Wire sort_and_heal_wire(const TopoDS_Shape& iShape, const TopoDS_Face& iFaceToProject); - TopoDS_Wire sort_and_heal_wire(const std::vector& iEdgeVec, const TopoDS_Face& iFaceToProject); - void create_face_extrude(std::vector& iCurrentShape); - void store_wire_in_vector(const SShapeStore& iCurrentShape, const TopoDS_Shape& iParentShape, std::vector& iStoreVec, const unsigned int iColor); - void set_xyz_dir_spinbox(QDoubleSpinBox* icurrentSpinBox); - void transform_shape_to_global_position(TopoDS_Shape& ioShape, Part::Feature* iPart); + void get_camera_direction(); + void store_current_selected_parts(std::vector& iStoreVec, + unsigned int iColor); + bool store_part_in_vector(SShapeStore& iCurrentShape, std::vector& iStoreVec); + void create_projection_wire(std::vector& iCurrentShape); + TopoDS_Shape create_compound(const std::vector& iShapeVec); + void show_projected_shapes(const std::vector& iShapeStoreVec); + void disable_ui_elements(const std::vector& iObjectVec, QWidget* iExceptThis); + void enable_ui_elements(const std::vector& iObjectVec, QWidget* iExceptThis); + void higlight_object(Part::Feature* iCurrentObject, + const std::string& iShapeName, + bool iHighlight, + unsigned int iColor); + void get_all_wire_from_face(SShapeStore& ioCurrentSahpe); + void create_projection_face_from_wire(std::vector& iCurrentShape); + TopoDS_Wire sort_and_heal_wire(const TopoDS_Shape& iShape, const TopoDS_Face& iFaceToProject); + TopoDS_Wire sort_and_heal_wire(const std::vector& iEdgeVec, + const TopoDS_Face& iFaceToProject); + void create_face_extrude(std::vector& iCurrentShape); + void store_wire_in_vector(const SShapeStore& iCurrentShape, + const TopoDS_Shape& iParentShape, + std::vector& iStoreVec, + unsigned int iColor); + void set_xyz_dir_spinbox(QDoubleSpinBox* icurrentSpinBox); + void transform_shape_to_global_position(TopoDS_Shape& ioShape, Part::Feature* iPart); private: - /** Checks if the given document is about to be closed */ - void slotDeletedDocument(const App::Document& Doc) override; - /** Checks if the given object is about to be removed. */ - void slotDeletedObject(const App::DocumentObject& Obj) override; + /** Checks if the given document is about to be closed */ + void slotDeletedDocument(const App::Document& Doc) override; + /** Checks if the given object is about to be removed. */ + void slotDeletedObject(const App::DocumentObject& Obj) override; private: - Ui::DlgProjectionOnSurface *ui; + Ui::DlgProjectionOnSurface* ui; std::vector m_shapeVec; std::vector m_projectionSurfaceVec; @@ -134,9 +143,9 @@ private: std::vector m_guiObjectVec; const QString m_projectionObjectName; - Part::Feature* m_projectionObject; - App::Document* m_partDocument; - float m_lastDepthVal; + Part::Feature* m_projectionObject = nullptr; + App::Document* m_partDocument = nullptr; + double m_lastDepthVal; class EdgeSelection; EdgeSelection* filterEdge; @@ -145,28 +154,28 @@ private: FaceSelection* filterFace; }; -class TaskProjectionOnSurface : public Gui::TaskView::TaskDialog +class TaskProjectionOnSurface: public Gui::TaskView::TaskDialog { - Q_OBJECT + Q_OBJECT public: - TaskProjectionOnSurface(); + TaskProjectionOnSurface(); public: - bool accept() override; - bool reject() override; - void clicked(int) override; + bool accept() override; + bool reject() override; + void clicked(int id) override; - QDialogButtonBox::StandardButtons getStandardButtons() const override - { - return QDialogButtonBox::Ok | QDialogButtonBox::Cancel; - } + QDialogButtonBox::StandardButtons getStandardButtons() const override + { + return QDialogButtonBox::Ok | QDialogButtonBox::Cancel; + } private: - DlgProjectionOnSurface* widget; - Gui::TaskView::TaskBox* taskbox; + DlgProjectionOnSurface* widget = nullptr; + Gui::TaskView::TaskBox* taskbox = nullptr; }; -} // namespace PartGui -#endif // PARTGUI_DLGPROJECTIONONSURFACE_H +} // namespace PartGui +#endif // PARTGUI_DLGPROJECTIONONSURFACE_H From c667dd2d456ae0ab6d04092b82241bbce4acb961 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Sun, 24 Mar 2024 19:10:00 +0100 Subject: [PATCH 49/59] FEM: Update DlgSettingsFemCcx.ui --- src/Mod/Fem/Gui/DlgSettingsFemCcx.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui b/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui index 7865805b31..45ee0c7964 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui +++ b/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui @@ -425,6 +425,9 @@
+ + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 1 From 58757a312948fd8ec2d14f3326f2701074d0d382 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Sun, 24 Mar 2024 19:12:30 +0100 Subject: [PATCH 50/59] FEM: Update DlgSettingsFemCcx.ui --- src/Mod/Fem/Gui/DlgSettingsFemCcx.ui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui b/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui index 45ee0c7964..ad991d117c 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui +++ b/src/Mod/Fem/Gui/DlgSettingsFemCcx.ui @@ -497,7 +497,7 @@ - + s @@ -536,7 +536,7 @@ - + s @@ -646,7 +646,7 @@ - + s @@ -808,7 +808,7 @@ - + Hz From e5d4f0e38eb3a0c867d5ef383db081285ffeeba1 Mon Sep 17 00:00:00 2001 From: Jacob Oursland Date: Sun, 24 Mar 2024 15:53:42 -0600 Subject: [PATCH 51/59] Conda: Unpin packages post miniforge upgrade. Miniforge has upgraded from 23.x.y to 24.x.y, permitting the unpinning of dependencies. Furthermore, these pinned packages cause build issues with the latest version of Miniforge. --- conda/conda-env.yaml | 5 ++--- conda/environment.devenv.yml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/conda/conda-env.yaml b/conda/conda-env.yaml index 1bd63b1952..85f441dae3 100644 --- a/conda/conda-env.yaml +++ b/conda/conda-env.yaml @@ -3,6 +3,5 @@ channels: - conda-forge dependencies: - conda-devenv -- mamba==1.4.9 # NOTE: Pin to highest version supported by the devenv -- python==3.11.* # dependencies. If a higher version is installed, a crash - # occurs during the downgrade process. +- mamba +- python==3.11.* diff --git a/conda/environment.devenv.yml b/conda/environment.devenv.yml index fa434281b4..a2504b69e6 100644 --- a/conda/environment.devenv.yml +++ b/conda/environment.devenv.yml @@ -73,7 +73,7 @@ dependencies: - graphviz - hdf5 - libcxx -- mamba==1.4.9 +- mamba - matplotlib - ninja - numpy From 0434bc1197a0e09fd3e082419996d3139013c590 Mon Sep 17 00:00:00 2001 From: FEA-eng <59876896+FEA-eng@users.noreply.github.com> Date: Mon, 25 Mar 2024 10:44:29 +0100 Subject: [PATCH 52/59] FEM: Update DlgSettingsFemCcxImp.cpp --- src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp b/src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp index a5030ffd0a..bea9df709f 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp +++ b/src/Mod/Fem/Gui/DlgSettingsFemCcxImp.cpp @@ -73,6 +73,8 @@ void DlgSettingsFemCcxImp::saveSettings() ui->sb_ccx_max_iterations->onSave(); // Max number of iterations ui->dsb_ccx_initial_time_step->onSave(); // Initial time step ui->dsb_ccx_analysis_time->onSave(); // Analysis time + ui->dsb_ccx_minimum_time_step->onSave(); // Minimum time step + ui->dsb_ccx_maximum_time_step->onSave(); // Maximum time step ui->cb_analysis_type->onSave(); ui->cb_BeamShellOutput->onSave(); // Beam shell output 3d or 2d @@ -98,6 +100,8 @@ void DlgSettingsFemCcxImp::loadSettings() ui->sb_ccx_max_iterations->onRestore(); // Max number of iterations ui->dsb_ccx_initial_time_step->onRestore(); // Initial time step ui->dsb_ccx_analysis_time->onRestore(); // Analysis time + ui->dsb_ccx_minimum_time_step->onRestore(); // Minimum time step + ui->dsb_ccx_maximum_time_step->onRestore(); // Maximum time step ui->cb_analysis_type->onRestore(); ui->cb_BeamShellOutput->onRestore(); // Beam shell output 3d or 2d From 3defef03c6fa696fadd6f5ea37072ae98e53b74c Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 25 Mar 2024 17:29:33 +0100 Subject: [PATCH 53/59] Arch: Allow to write IFC objects without owner history (#13076) * Arch: Allow to write IFC objects without owner history * Arch: Fixed context detection in IFC exporter --- src/Mod/Arch/exportIFC.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Mod/Arch/exportIFC.py b/src/Mod/Arch/exportIFC.py index d940b7c249..e168c3bf93 100644 --- a/src/Mod/Arch/exportIFC.py +++ b/src/Mod/Arch/exportIFC.py @@ -265,7 +265,12 @@ def export(exportList, filename, colors=None, preferences=None): ifcfile = ifcopenshell.open(templatefile) ifcfile = exportIFCHelper.writeUnits(ifcfile,preferences["IFC_UNIT"]) - history = ifcfile.by_type("IfcOwnerHistory")[0] + history = ifcfile.by_type("IfcOwnerHistory") + if history: + history = history[0] + else: + # IFC4 allows to not write any history + history = None objectslist = Draft.get_group_contents(exportList, walls=True, addgroups=True) @@ -296,7 +301,9 @@ def export(exportList, filename, colors=None, preferences=None): if existing_file: project = ifcfile.by_type("IfcProject")[0] - context = ifcfile.by_type("IFcGeometricRepresentationContext")[-1] + body_contexts = [c for c in ifcfile.by_type("IfcGeometricRepresentationSubContext") if c.ContextIdentifier in ["Body", "Facetation"]] + body_contexts.extend([c for c in ifcfile.by_type("IfcGeometricRepresentationContext", include_subtypes=False) if c.ContextType == "Model"]) + context = body_contexts[0] # we take the first one (subcontext if existing, or context if not) else: contextCreator = exportIFCHelper.ContextCreator(ifcfile, objectslist) context = contextCreator.model_view_subcontext From e4213fc10f4149e7debf3fffc9dfbc7a4c9c7e11 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Fri, 16 Feb 2024 15:28:30 +0100 Subject: [PATCH 54/59] Sketcher: Symmetry tool rework. --- src/Mod/Sketcher/App/SketchObject.cpp | 660 ++++++++---------- src/Mod/Sketcher/App/SketchObject.h | 11 +- src/Mod/Sketcher/Gui/CMakeLists.txt | 1 + src/Mod/Sketcher/Gui/CommandSketcherTools.cpp | 187 +---- .../Sketcher/Gui/DrawSketchHandlerSymmetry.h | 291 ++++++++ src/Mod/Sketcher/Gui/Resources/Sketcher.qrc | 1 + .../Sketcher_Pointer_Create_Symmetry.svg | 67 ++ 7 files changed, 676 insertions(+), 542 deletions(-) create mode 100644 src/Mod/Sketcher/Gui/DrawSketchHandlerSymmetry.h create mode 100644 src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_Symmetry.svg diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index ecb6c9abe6..6452b8607f 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -4227,105 +4227,298 @@ bool SketchObject::isCarbonCopyAllowed(App::Document* pDoc, App::DocumentObject* } int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, - Sketcher::PointPos refPosId /*=Sketcher::PointPos::none*/) + Sketcher::PointPos refPosId /*=Sketcher::PointPos::none*/, + bool addSymmetryConstraints /*= false*/) { // no need to check input data validity as this is an sketchobject managed operation. Base::StateLocker lock(managedoperation, true); - const std::vector& geovals = getInternalGeometry(); - std::vector newgeoVals(geovals); - const std::vector& constrvals = this->Constraints.getValues(); std::vector newconstrVals(constrvals); - newgeoVals.reserve(geovals.size() + geoIdList.size()); - - int cgeoid = getHighestCurveIndex() + 1; - - std::map geoIdMap; - std::map isStartEndInverted; - // Find out if reference is aligned with V or H axis, // if so we can keep Vertical and Horizontal constraints in the mirrored geometry. + bool refIsLine = refPosId == Sketcher::PointPos::none; bool refIsAxisAligned = false; - if (refGeoId == Sketcher::GeoEnum::VAxis || refGeoId == Sketcher::GeoEnum::HAxis) { + if (refGeoId == Sketcher::GeoEnum::VAxis || refGeoId == Sketcher::GeoEnum::HAxis || !refIsLine) { refIsAxisAligned = true; } else { - for (std::vector::const_iterator it = constrvals.begin(); - it != constrvals.end(); - ++it) { - Constraint* constr = *(it); + for (auto* constr : constrvals) { if (constr->First == refGeoId - && (constr->Type == Sketcher::Vertical || constr->Type == Sketcher::Horizontal)) + && (constr->Type == Sketcher::Vertical || constr->Type == Sketcher::Horizontal)){ refIsAxisAligned = true; + } } } - // reference is a line - if (refPosId == Sketcher::PointPos::none) { - const Part::Geometry* georef = getGeometry(refGeoId); - if (georef->getTypeId() != Part::GeomLineSegment::getClassTypeId()) { - Base::Console().Error("Reference for symmetric is neither a point nor a line.\n"); - return -1; + // add the geometry + std::map geoIdMap; + std::map isStartEndInverted; + std::vector newgeoVals(getInternalGeometry()); + std::vector symmetricVals = getSymmetric(geoIdList, geoIdMap, isStartEndInverted, refGeoId, refPosId); + newgeoVals.insert(newgeoVals.end(), symmetricVals.begin(), symmetricVals.end()); + + // Block acceptGeometry in OnChanged to avoid unnecessary checks and updates + { + Base::StateLocker lock(internaltransaction, true); + Geometry.setValues(std::move(newgeoVals)); + + + for (auto* constr : constrvals) { + // we look in the map, because we might have skipped internal alignment geometry + auto fit = geoIdMap.find(constr->First); + + if (fit != geoIdMap.end()) {// if First of constraint is in geoIdList + if (addSymmetryConstraints && constr->Type != Sketcher::InternalAlignment) { + // if we are making symmetric constraints, then we don't want to copy all constraints + continue; + } + + if (constr->Second == GeoEnum::GeoUndef /*&& constr->Third == GeoEnum::GeoUndef*/) { + if (refIsAxisAligned) { + // in this case we want to keep the Vertical, Horizontal constraints + // DistanceX ,and DistanceY constraints should also be possible to keep in + // this case, but keeping them causes segfault, not sure why. + + if (constr->Type != Sketcher::DistanceX + && constr->Type != Sketcher::DistanceY) { + Constraint* constNew = constr->copy(); + constNew->First = fit->second; + newconstrVals.push_back(constNew); + } + } + else if (constr->Type != Sketcher::DistanceX + && constr->Type != Sketcher::DistanceY + && constr->Type != Sketcher::Vertical + && constr->Type != Sketcher::Horizontal) { + // this includes all non-directional single GeoId constraints, as radius, + // diameter, weight,... + + Constraint* constNew = constr->copy(); + constNew->First = fit->second; + newconstrVals.push_back(constNew); + } + } + else {// other geoids intervene in this constraint + + auto sit = geoIdMap.find(constr->Second); + + if (sit != geoIdMap.end()) {// Second is also in the list + + if (constr->Third == GeoEnum::GeoUndef) { + if (constr->Type == Sketcher::Coincident + || constr->Type == Sketcher::Perpendicular + || constr->Type == Sketcher::Parallel + || constr->Type == Sketcher::Tangent + || constr->Type == Sketcher::Distance + || constr->Type == Sketcher::Equal || constr->Type == Sketcher::Angle + || constr->Type == Sketcher::PointOnObject + || constr->Type == Sketcher::InternalAlignment) { + Constraint* constNew = constr->copy(); + + constNew->First = fit->second; + constNew->Second = sit->second; + if (isStartEndInverted[constr->First]) { + if (constr->FirstPos == Sketcher::PointPos::start) + constNew->FirstPos = Sketcher::PointPos::end; + else if (constr->FirstPos == Sketcher::PointPos::end) + constNew->FirstPos = Sketcher::PointPos::start; + } + if (isStartEndInverted[constr->Second]) { + if (constr->SecondPos == Sketcher::PointPos::start) + constNew->SecondPos = Sketcher::PointPos::end; + else if (constr->SecondPos == Sketcher::PointPos::end) + constNew->SecondPos = Sketcher::PointPos::start; + } + + if (constNew->Type == Tangent || constNew->Type == Perpendicular) + AutoLockTangencyAndPerpty(constNew, true); + + if ((constr->Type == Sketcher::Angle) + && (refPosId == Sketcher::PointPos::none)) { + constNew->setValue(-constr->getValue()); + } + + newconstrVals.push_back(constNew); + } + } + else {// three GeoIds intervene in constraint + auto tit = geoIdMap.find(constr->Third); + + if (tit != geoIdMap.end()) {// Third is also in the list + Constraint* constNew = constr->copy(); + constNew->First = fit->second; + constNew->Second = sit->second; + constNew->Third = tit->second; + if (isStartEndInverted[constr->First]) { + if (constr->FirstPos == Sketcher::PointPos::start) + constNew->FirstPos = Sketcher::PointPos::end; + else if (constr->FirstPos == Sketcher::PointPos::end) + constNew->FirstPos = Sketcher::PointPos::start; + } + if (isStartEndInverted[constr->Second]) { + if (constr->SecondPos == Sketcher::PointPos::start) + constNew->SecondPos = Sketcher::PointPos::end; + else if (constr->SecondPos == Sketcher::PointPos::end) + constNew->SecondPos = Sketcher::PointPos::start; + } + if (isStartEndInverted[constr->Third]) { + if (constr->ThirdPos == Sketcher::PointPos::start) + constNew->ThirdPos = Sketcher::PointPos::end; + else if (constr->ThirdPos == Sketcher::PointPos::end) + constNew->ThirdPos = Sketcher::PointPos::start; + } + newconstrVals.push_back(constNew); + } + } + } + } + } } - const Part::GeomLineSegment* refGeoLine = static_cast(georef); + if (addSymmetryConstraints) { + auto createSymConstr = [&] + (int first, int second, Sketcher::PointPos firstPos, Sketcher::PointPos secondPos) { + auto symConstr = new Constraint(); + symConstr->Type = Symmetric; + symConstr->First = first; + symConstr->Second = second; + symConstr->Third = refGeoId; + symConstr->FirstPos = firstPos; + symConstr->SecondPos = secondPos; + symConstr->ThirdPos = refPosId; + newconstrVals.push_back(symConstr); + }; + auto createEqualityConstr = [&] + (int first, int second) { + auto symConstr = new Constraint(); + symConstr->Type = Equal; + symConstr->First = first; + symConstr->Second = second; + newconstrVals.push_back(symConstr); + }; + + for (auto geoIdPair : geoIdMap) { + int geoId1 = geoIdPair.first; + int geoId2 = geoIdPair.second; + const Part::Geometry* geo = getGeometry(geoId1); + + if (geo->is()) { + auto gf = GeometryFacade::getFacade(geo); + if (!gf->isInternalAligned()) { + // Note internal aligned lines (ellipse, parabola, hyperbola) are causing redundant constraint. + createSymConstr(geoId1, geoId2, PointPos::start, isStartEndInverted[geoId1] ? PointPos::end : PointPos::start); + createSymConstr(geoId1, geoId2, PointPos::end, isStartEndInverted[geoId1] ? PointPos::start : PointPos::end); + } + } + else if (geo->is() || geo->is()) { + createEqualityConstr(geoId1, geoId2); + createSymConstr(geoId1, geoId2, PointPos::mid, PointPos::mid); + } + else if (geo->is() + || geo->is() + || geo->is() + || geo->is()) { + createEqualityConstr(geoId1, geoId2); + createSymConstr(geoId1, geoId2, PointPos::start, isStartEndInverted[geoId1] ? PointPos::end : PointPos::start); + createSymConstr(geoId1, geoId2, PointPos::end, isStartEndInverted[geoId1] ? PointPos::start : PointPos::end); + } + else if (geo->is()) { + auto gf = GeometryFacade::getFacade(geo); + if (!gf->isInternalAligned()) { + createSymConstr(geoId1, geoId2, PointPos::start, PointPos::start); + } + } + // Note bspline has symmetric by the internal aligned circles. + } + } + + if (newconstrVals.size() > constrvals.size()){ + Constraints.setValues(std::move(newconstrVals)); + } + } + + // we delayed update, so trigger it now. + // Update geometry indices and rebuild vertexindex now via onChanged, so that + // ViewProvider::UpdateData is triggered. + Geometry.touch(); + + return Geometry.getSize() - 1; +} + + +std::vector SketchObject::getSymmetric(const std::vector& geoIdList, + std::map& geoIdMap, + std::map& isStartEndInverted, + int refGeoId, + Sketcher::PointPos refPosId) +{ + std::vector symmetricVals; + bool refIsLine = refPosId == Sketcher::PointPos::none; + int cgeoid = getHighestCurveIndex() + 1; + + auto shouldCopyGeometry = [&](auto* geo, int geoId) -> bool { + auto gf = GeometryFacade::getFacade(geo); + if (gf->isInternalAligned()) { + // only add if the corresponding geometry it defines is also in the list. + int definedGeo = GeoEnum::GeoUndef; + for (auto c : Constraints.getValues()) { + if (c->Type == Sketcher::InternalAlignment && c->First == geoId) { + definedGeo = c->Second; + break; + } + } + // Return true if definedGeo is in geoIdList, false otherwise + return std::find(geoIdList.begin(), geoIdList.end(), definedGeo) != geoIdList.end(); + } + // Return true if not internal aligned, indicating it should always be copied + return true; + }; + + if (refIsLine) { + const Part::Geometry* georef = getGeometry(refGeoId); + if (!georef->is()) { + Base::Console().Error("Reference for symmetric is neither a point nor a line.\n"); + return {}; + } + + auto* refGeoLine = static_cast(georef); // line Base::Vector3d refstart = refGeoLine->getStartPoint(); Base::Vector3d vectline = refGeoLine->getEndPoint() - refstart; - for (std::vector::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) { - const Part::Geometry* geo = getGeometry(*it); + for (auto geoId : geoIdList) { + const Part::Geometry* geo = getGeometry(geoId); Part::Geometry* geosym; - auto gf = GeometryFacade::getFacade(geo); - - if (gf->isInternalAligned()) { - // only add this geometry if the corresponding geometry it defines is also in the - // list. - int definedGeo = GeoEnum::GeoUndef; - - for (auto c : Constraints.getValues()) { - if (c->Type == Sketcher::InternalAlignment && c->First == *it) { - definedGeo = c->Second; - break; - } - } - - if (std::find(geoIdList.begin(), geoIdList.end(), definedGeo) != geoIdList.end()) - geosym = geo->copy(); - else { - // we should not mirror internal alignment geometry, unless the element they - // define is also mirrored - continue; - } - } - else { - geosym = geo->copy(); + if (!shouldCopyGeometry(geo, geoId)) { + continue; } + geosym = geo->copy(); + // Handle Geometry if (geosym->is()) { - Part::GeomLineSegment* geosymline = static_cast(geosym); + auto* geosymline = static_cast(geosym); Base::Vector3d sp = geosymline->getStartPoint(); Base::Vector3d ep = geosymline->getEndPoint(); geosymline->setPoints( sp + 2.0 * (sp.Perpendicular(refGeoLine->getStartPoint(), vectline) - sp), ep + 2.0 * (ep.Perpendicular(refGeoLine->getStartPoint(), vectline) - ep)); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomCircle* geosymcircle = static_cast(geosym); + auto* geosymcircle = static_cast(geosym); Base::Vector3d cp = geosymcircle->getCenter(); geosymcircle->setCenter( cp + 2.0 * (cp.Perpendicular(refGeoLine->getStartPoint(), vectline) - cp)); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomArcOfCircle* geoaoc = static_cast(geosym); + auto* geoaoc = static_cast(geosym); Base::Vector3d sp = geoaoc->getStartPoint(true); Base::Vector3d ep = geoaoc->getEndPoint(true); Base::Vector3d cp = geoaoc->getCenter(); @@ -4342,10 +4535,10 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, geoaoc->setCenter(scp); geoaoc->setRange(theta1, theta2, true); - isStartEndInverted.insert(std::make_pair(*it, true)); + isStartEndInverted.insert(std::make_pair(geoId, true)); } else if (geosym->is()) { - Part::GeomEllipse* geosymellipse = static_cast(geosym); + auto* geosymellipse = static_cast(geosym); Base::Vector3d cp = geosymellipse->getCenter(); Base::Vector3d majdir = geosymellipse->getMajorAxisDir(); @@ -4362,10 +4555,10 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, geosymellipse->setMajorAxisDir(sf1 - scp); geosymellipse->setCenter(scp); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomArcOfEllipse* geosymaoe = static_cast(geosym); + auto* geosymaoe = static_cast(geosym); Base::Vector3d cp = geosymaoe->getCenter(); Base::Vector3d majdir = geosymaoe->getMajorAxisDir(); @@ -4394,11 +4587,10 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, } geosymaoe->setRange(theta1, theta2, true); - isStartEndInverted.insert(std::make_pair(*it, true)); + isStartEndInverted.insert(std::make_pair(geoId, true)); } else if (geosym->is()) { - Part::GeomArcOfHyperbola* geosymaoe = - static_cast(geosym); + auto* geosymaoe = static_cast(geosym); Base::Vector3d cp = geosymaoe->getCenter(); Base::Vector3d majdir = geosymaoe->getMajorAxisDir(); @@ -4423,10 +4615,10 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, std::swap(theta1, theta2); geosymaoe->setRange(theta1, theta2, true); - isStartEndInverted.insert(std::make_pair(*it, true)); + isStartEndInverted.insert(std::make_pair(geoId, true)); } else if (geosym->is()) { - Part::GeomArcOfParabola* geosymaoe = static_cast(geosym); + auto* geosymaoe = static_cast(geosym); Base::Vector3d cp = geosymaoe->getCenter(); // double df= geosymaoe->getFocal(); @@ -4447,45 +4639,41 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, std::swap(theta1, theta2); geosymaoe->setRange(theta1, theta2, true); - isStartEndInverted.insert(std::make_pair(*it, true)); + isStartEndInverted.insert(std::make_pair(geoId, true)); } else if (geosym->is()) { - Part::GeomBSplineCurve* geosymbsp = static_cast(geosym); + auto* geosymbsp = static_cast(geosym); std::vector poles = geosymbsp->getPoles(); - for (std::vector::iterator jt = poles.begin(); jt != poles.end(); - ++jt) { - - (*jt) = (*jt) - + 2.0 - * ((*jt).Perpendicular(refGeoLine->getStartPoint(), vectline) - (*jt)); + for (auto& pole : poles) { + pole = pole + + 2.0 * (pole.Perpendicular(refGeoLine->getStartPoint(), vectline) - pole); } geosymbsp->setPoles(poles); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomPoint* geosympoint = static_cast(geosym); + auto* geosympoint = static_cast(geosym); Base::Vector3d cp = geosympoint->getPoint(); geosympoint->setPoint( cp + 2.0 * (cp.Perpendicular(refGeoLine->getStartPoint(), vectline) - cp)); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else { Base::Console().Error("Unsupported Geometry!! Just copying it.\n"); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } - newgeoVals.push_back(geosym); - geoIdMap.insert(std::make_pair(*it, cgeoid)); + symmetricVals.push_back(geosym); + geoIdMap.insert(std::make_pair(geoId, cgeoid)); cgeoid++; } } else {// reference is a point - refIsAxisAligned = true; Vector3d refpoint; const Part::Geometry* georef = getGeometry(refGeoId); @@ -4496,160 +4684,43 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, refpoint = Vector3d(0, 0, 0); } else { - switch (refPosId) { - case Sketcher::PointPos::start: - if (georef->is()) { - const Part::GeomLineSegment* geosymline = - static_cast(georef); - refpoint = geosymline->getStartPoint(); - } - else if (georef->is()) { - const Part::GeomArcOfCircle* geoaoc = - static_cast(georef); - refpoint = geoaoc->getStartPoint(true); - } - else if (georef->is()) { - const Part::GeomArcOfEllipse* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getStartPoint(true); - } - else if (georef->is()) { - const Part::GeomArcOfHyperbola* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getStartPoint(true); - } - else if (georef->is()) { - const Part::GeomArcOfParabola* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getStartPoint(true); - } - else if (georef->is()) { - const Part::GeomBSplineCurve* geosymbsp = - static_cast(georef); - refpoint = geosymbsp->getStartPoint(); - } - break; - case Sketcher::PointPos::end: - if (georef->is()) { - const Part::GeomLineSegment* geosymline = - static_cast(georef); - refpoint = geosymline->getEndPoint(); - } - else if (georef->is()) { - const Part::GeomArcOfCircle* geoaoc = - static_cast(georef); - refpoint = geoaoc->getEndPoint(true); - } - else if (georef->is()) { - const Part::GeomArcOfEllipse* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getEndPoint(true); - } - else if (georef->is()) { - const Part::GeomArcOfHyperbola* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getEndPoint(true); - } - else if (georef->is()) { - const Part::GeomArcOfParabola* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getEndPoint(true); - } - else if (georef->is()) { - const Part::GeomBSplineCurve* geosymbsp = - static_cast(georef); - refpoint = geosymbsp->getEndPoint(); - } - break; - case Sketcher::PointPos::mid: - if (georef->is()) { - const Part::GeomCircle* geosymcircle = - static_cast(georef); - refpoint = geosymcircle->getCenter(); - } - else if (georef->is()) { - const Part::GeomArcOfCircle* geoaoc = - static_cast(georef); - refpoint = geoaoc->getCenter(); - } - else if (georef->is()) { - const Part::GeomEllipse* geosymellipse = - static_cast(georef); - refpoint = geosymellipse->getCenter(); - } - else if (georef->is()) { - const Part::GeomArcOfEllipse* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getCenter(); - } - else if (georef->is()) { - const Part::GeomArcOfHyperbola* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getCenter(); - } - else if (georef->is()) { - const Part::GeomArcOfParabola* geosymaoe = - static_cast(georef); - refpoint = geosymaoe->getCenter(); - } - break; - default: - Base::Console().Error("Wrong PointPosId.\n"); - return -1; + if (refPosId == Sketcher::PointPos::none) { + Base::Console().Error("Wrong PointPosId.\n"); + return {}; } + refpoint = getPoint(georef, refPosId); } - for (std::vector::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) { - const Part::Geometry* geo = getGeometry(*it); - + for (auto geoId : geoIdList) { + const Part::Geometry* geo = getGeometry(geoId); Part::Geometry* geosym; - auto gf = GeometryFacade::getFacade(geo); - - if (gf->isInternalAligned()) { - // only add this geometry if the corresponding geometry it defines is also in the - // list. - int definedGeo = GeoEnum::GeoUndef; - - for (auto c : Constraints.getValues()) { - if (c->Type == Sketcher::InternalAlignment && c->First == *it) { - definedGeo = c->Second; - break; - } - } - - if (std::find(geoIdList.begin(), geoIdList.end(), definedGeo) != geoIdList.end()) - geosym = geo->copy(); - else { - // we should not mirror internal alignment geometry, unless the element they - // define is also mirrored - continue; - } - } - else { - geosym = geo->copy(); + if (!shouldCopyGeometry(geo, geoId)) { + continue; } + geosym = geo->copy(); + // Handle Geometry if (geosym->is()) { - Part::GeomLineSegment* geosymline = static_cast(geosym); + auto* geosymline = static_cast(geosym); Base::Vector3d sp = geosymline->getStartPoint(); Base::Vector3d ep = geosymline->getEndPoint(); Base::Vector3d ssp = sp + 2.0 * (refpoint - sp); Base::Vector3d sep = ep + 2.0 * (refpoint - ep); geosymline->setPoints(ssp, sep); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomCircle* geosymcircle = static_cast(geosym); + auto* geosymcircle = static_cast(geosym); Base::Vector3d cp = geosymcircle->getCenter(); geosymcircle->setCenter(cp + 2.0 * (refpoint - cp)); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomArcOfCircle* geoaoc = static_cast(geosym); + auto* geoaoc = static_cast(geosym); Base::Vector3d sp = geoaoc->getStartPoint(true); Base::Vector3d ep = geoaoc->getEndPoint(true); Base::Vector3d cp = geoaoc->getCenter(); @@ -4663,10 +4734,10 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, geoaoc->setCenter(scp); geoaoc->setRange(theta1, theta2, true); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomEllipse* geosymellipse = static_cast(geosym); + auto* geosymellipse = static_cast(geosym); Base::Vector3d cp = geosymellipse->getCenter(); Base::Vector3d majdir = geosymellipse->getMajorAxisDir(); @@ -4681,10 +4752,10 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, geosymellipse->setMajorAxisDir(sf1 - scp); geosymellipse->setCenter(scp); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomArcOfEllipse* geosymaoe = static_cast(geosym); + auto* geosymaoe = static_cast(geosym); Base::Vector3d cp = geosymaoe->getCenter(); Base::Vector3d majdir = geosymaoe->getMajorAxisDir(); @@ -4699,11 +4770,10 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, geosymaoe->setMajorAxisDir(sf1 - scp); geosymaoe->setCenter(scp); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomArcOfHyperbola* geosymaoe = - static_cast(geosym); + auto* geosymaoe = static_cast(geosym); Base::Vector3d cp = geosymaoe->getCenter(); Base::Vector3d majdir = geosymaoe->getMajorAxisDir(); @@ -4718,10 +4788,10 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, geosymaoe->setMajorAxisDir(sf1 - scp); geosymaoe->setCenter(scp); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomArcOfParabola* geosymaoe = static_cast(geosym); + auto* geosymaoe = static_cast(geosym); Base::Vector3d cp = geosymaoe->getCenter(); /*double df= geosymaoe->getFocal();*/ @@ -4733,167 +4803,39 @@ int SketchObject::addSymmetric(const std::vector& geoIdList, int refGeoId, geosymaoe->setXAxisDir(sf1 - scp); geosymaoe->setCenter(scp); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomBSplineCurve* geosymbsp = static_cast(geosym); + auto* geosymbsp = static_cast(geosym); std::vector poles = geosymbsp->getPoles(); - for (std::vector::iterator it = poles.begin(); it != poles.end(); - ++it) { - (*it) = (*it) + 2.0 * (refpoint - (*it)); + for (auto& pole : poles) { + pole = pole + 2.0 * (refpoint - pole); } geosymbsp->setPoles(poles); - // isStartEndInverted.insert(std::make_pair(*it, false)); + // isStartEndInverted.insert(std::make_pair(geoId, false)); } else if (geosym->is()) { - Part::GeomPoint* geosympoint = static_cast(geosym); + auto* geosympoint = static_cast(geosym); Base::Vector3d cp = geosympoint->getPoint(); geosympoint->setPoint(cp + 2.0 * (refpoint - cp)); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } else { Base::Console().Error("Unsupported Geometry!! Just copying it.\n"); - isStartEndInverted.insert(std::make_pair(*it, false)); + isStartEndInverted.insert(std::make_pair(geoId, false)); } - newgeoVals.push_back(geosym); - geoIdMap.insert(std::make_pair(*it, cgeoid)); + symmetricVals.push_back(geosym); + geoIdMap.insert(std::make_pair(geoId, cgeoid)); cgeoid++; } } - - // add the geometry - - // Block acceptGeometry in OnChanged to avoid unnecessary checks and updates - { - Base::StateLocker lock(internaltransaction, true); - Geometry.setValues(std::move(newgeoVals)); - - for (std::vector::const_iterator it = constrvals.begin(); - it != constrvals.end(); - ++it) { - // we look in the map, because we might have skipped internal alignment geometry - auto fit = geoIdMap.find((*it)->First); - - if (fit != geoIdMap.end()) {// if First of constraint is in geoIdList - - if ((*it)->Second == GeoEnum::GeoUndef /*&& (*it)->Third == GeoEnum::GeoUndef*/) { - if (refIsAxisAligned) { - // in this case we want to keep the Vertical, Horizontal constraints - // DistanceX ,and DistanceY constraints should also be possible to keep in - // this case, but keeping them causes segfault, not sure why. - - if ((*it)->Type != Sketcher::DistanceX - && (*it)->Type != Sketcher::DistanceY) { - Constraint* constNew = (*it)->copy(); - constNew->First = fit->second; - newconstrVals.push_back(constNew); - } - } - else if ((*it)->Type != Sketcher::DistanceX - && (*it)->Type != Sketcher::DistanceY - && (*it)->Type != Sketcher::Vertical - && (*it)->Type != Sketcher::Horizontal) { - // this includes all non-directional single GeoId constraints, as radius, - // diameter, weight,... - - Constraint* constNew = (*it)->copy(); - constNew->First = fit->second; - newconstrVals.push_back(constNew); - } - } - else {// other geoids intervene in this constraint - - auto sit = geoIdMap.find((*it)->Second); - - if (sit != geoIdMap.end()) {// Second is also in the list - - if ((*it)->Third == GeoEnum::GeoUndef) { - if ((*it)->Type == Sketcher::Coincident - || (*it)->Type == Sketcher::Perpendicular - || (*it)->Type == Sketcher::Parallel - || (*it)->Type == Sketcher::Tangent - || (*it)->Type == Sketcher::Distance - || (*it)->Type == Sketcher::Equal || (*it)->Type == Sketcher::Angle - || (*it)->Type == Sketcher::PointOnObject - || (*it)->Type == Sketcher::InternalAlignment) { - Constraint* constNew = (*it)->copy(); - - constNew->First = fit->second; - constNew->Second = sit->second; - if (isStartEndInverted[(*it)->First]) { - if ((*it)->FirstPos == Sketcher::PointPos::start) - constNew->FirstPos = Sketcher::PointPos::end; - else if ((*it)->FirstPos == Sketcher::PointPos::end) - constNew->FirstPos = Sketcher::PointPos::start; - } - if (isStartEndInverted[(*it)->Second]) { - if ((*it)->SecondPos == Sketcher::PointPos::start) - constNew->SecondPos = Sketcher::PointPos::end; - else if ((*it)->SecondPos == Sketcher::PointPos::end) - constNew->SecondPos = Sketcher::PointPos::start; - } - - if (constNew->Type == Tangent || constNew->Type == Perpendicular) - AutoLockTangencyAndPerpty(constNew, true); - - if (((*it)->Type == Sketcher::Angle) - && (refPosId == Sketcher::PointPos::none)) { - constNew->setValue(-(*it)->getValue()); - } - - newconstrVals.push_back(constNew); - } - } - else {// three GeoIds intervene in constraint - auto tit = geoIdMap.find((*it)->Third); - - if (tit != geoIdMap.end()) {// Third is also in the list - Constraint* constNew = (*it)->copy(); - constNew->First = fit->second; - constNew->Second = sit->second; - constNew->Third = tit->second; - if (isStartEndInverted[(*it)->First]) { - if ((*it)->FirstPos == Sketcher::PointPos::start) - constNew->FirstPos = Sketcher::PointPos::end; - else if ((*it)->FirstPos == Sketcher::PointPos::end) - constNew->FirstPos = Sketcher::PointPos::start; - } - if (isStartEndInverted[(*it)->Second]) { - if ((*it)->SecondPos == Sketcher::PointPos::start) - constNew->SecondPos = Sketcher::PointPos::end; - else if ((*it)->SecondPos == Sketcher::PointPos::end) - constNew->SecondPos = Sketcher::PointPos::start; - } - if (isStartEndInverted[(*it)->Third]) { - if ((*it)->ThirdPos == Sketcher::PointPos::start) - constNew->ThirdPos = Sketcher::PointPos::end; - else if ((*it)->ThirdPos == Sketcher::PointPos::end) - constNew->ThirdPos = Sketcher::PointPos::start; - } - newconstrVals.push_back(constNew); - } - } - } - } - } - } - - if (newconstrVals.size() > constrvals.size()) - Constraints.setValues(std::move(newconstrVals)); - } - - // we delayed update, so trigger it now. - // Update geometry indices and rebuild vertexindex now via onChanged, so that - // ViewProvider::UpdateData is triggered. - Geometry.touch(); - - return Geometry.getSize() - 1; + return symmetricVals; } int SketchObject::addCopy(const std::vector& geoIdList, const Base::Vector3d& displacement, diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 5d748bdadb..1d64977edc 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -368,7 +368,16 @@ public: /// adds symmetric geometric elements with respect to the refGeoId (line or point) int addSymmetric(const std::vector& geoIdList, int refGeoId, - Sketcher::PointPos refPosId = Sketcher::PointPos::none); + Sketcher::PointPos refPosId = Sketcher::PointPos::none, + bool addSymmetryConstraints = false); + // get the symmetric geometries of the geoIdList + std::vector + getSymmetric(const std::vector& geoIdList, + std::map& geoIdMap, + std::map& isStartEndInverted, + int refGeoId, + Sketcher::PointPos refPosId = Sketcher::PointPos::none); + /// with default parameters adds a copy of the geometric elements displaced by the displacement /// vector. It creates an array of csize elements in the direction of the displacement vector by /// rsize elements in the direction perpendicular to the displacement vector, wherein the diff --git a/src/Mod/Sketcher/Gui/CMakeLists.txt b/src/Mod/Sketcher/Gui/CMakeLists.txt index 1b1c42cd93..1c500f3b9a 100644 --- a/src/Mod/Sketcher/Gui/CMakeLists.txt +++ b/src/Mod/Sketcher/Gui/CMakeLists.txt @@ -80,6 +80,7 @@ SET(SketcherGui_SRCS DrawSketchHandlerOffset.h DrawSketchHandlerRotate.h DrawSketchHandlerScale.h + DrawSketchHandlerSymmetry.h CommandCreateGeo.cpp CommandConstraints.h CommandConstraints.cpp diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index b3d56aa851..93de8d79d8 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -58,6 +58,7 @@ #include "DrawSketchHandlerOffset.h" #include "DrawSketchHandlerRotate.h" #include "DrawSketchHandlerScale.h" +#include "DrawSketchHandlerSymmetry.h" // Hint: this is to prevent to re-format big parts of the file. Remove it later again. // clang-format off @@ -1092,7 +1093,7 @@ CmdSketcherSymmetry::CmdSketcherSymmetry() sGroup = "Sketcher"; sMenuText = QT_TR_NOOP("Symmetry"); sToolTipText = - QT_TR_NOOP("Creates symmetric geometry with respect to the last selected line or point"); + QT_TR_NOOP("Creates symmetric of selected geometry. After starting the tool select the reference line or point."); sWhatsThis = "Sketcher_Symmetry"; sStatusTip = sToolTipText; sPixmap = "Sketcher_Symmetry"; @@ -1103,190 +1104,12 @@ CmdSketcherSymmetry::CmdSketcherSymmetry() void CmdSketcherSymmetry::activated(int iMsg) { Q_UNUSED(iMsg); + std::vector listOfGeoIds = getListOfSelectedGeoIds(true); - // Cancel any in-progress operation - Gui::Document* doc = Gui::Application::Instance->activeDocument(); - SketcherGui::ReleaseHandler(doc); - - // get the selection - std::vector selection; - selection = getSelection().getSelectionEx(nullptr, Sketcher::SketchObject::getClassTypeId()); - - // only one sketch with its subelements are allowed to be selected - if (selection.size() != 1) { - Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(), - QObject::tr("Wrong selection"), - QObject::tr("Select elements from a single sketch.")); - return; + if (!listOfGeoIds.empty()) { + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerSymmetry(listOfGeoIds)); } - - // get the needed lists and objects - const std::vector& SubNames = selection[0].getSubNames(); - if (SubNames.empty()) { - Gui::TranslatedUserWarning(getActiveGuiDocument()->getDocument(), - QObject::tr("Wrong selection"), - QObject::tr("Select elements from a single sketch.")); - - return; - } - - Sketcher::SketchObject* Obj = static_cast(selection[0].getObject()); getSelection().clearSelection(); - - int LastGeoId = 0; - Sketcher::PointPos LastPointPos = Sketcher::PointPos::none; - const Part::Geometry* LastGeo; - using GeoType = enum { invalid = -1, line = 0, point = 1 }; - - GeoType lastgeotype = invalid; - - // create python command with list of elements - std::stringstream stream; - int geoids = 0; - - for (std::vector::const_iterator it = SubNames.begin(); it != SubNames.end(); - ++it) { - // only handle non-external edges - if ((it->size() > 4 && it->substr(0, 4) == "Edge") - || (it->size() > 12 && it->substr(0, 12) == "ExternalEdge")) { - - if (it->substr(0, 4) == "Edge") { - LastGeoId = std::atoi(it->substr(4, 4000).c_str()) - 1; - LastPointPos = Sketcher::PointPos::none; - } - else { - LastGeoId = -std::atoi(it->substr(12, 4000).c_str()) - 2; - LastPointPos = Sketcher::PointPos::none; - } - - // reference can be external or non-external - LastGeo = Obj->getGeometry(LastGeoId); - // Only for supported types - if (LastGeo->is()) - lastgeotype = line; - else - lastgeotype = invalid; - - // lines to make symmetric (only non-external) - if (LastGeoId >= 0) { - geoids++; - stream << LastGeoId << ","; - } - } - else if (it->size() > 6 && it->substr(0, 6) == "Vertex") { - // only if it is a GeomPoint - int VtId = std::atoi(it->substr(6, 4000).c_str()) - 1; - int GeoId; - Sketcher::PointPos PosId; - Obj->getGeoVertexIndex(VtId, GeoId, PosId); - - if (Obj->getGeometry(GeoId)->is()) { - LastGeoId = GeoId; - LastPointPos = Sketcher::PointPos::start; - lastgeotype = point; - - // points to make symmetric - if (LastGeoId >= 0) { - geoids++; - stream << LastGeoId << ","; - } - } - } - } - - bool lastvertexoraxis = false; - // check if last selected element is a Vertex, not being a GeomPoint - if (SubNames.rbegin()->size() > 6 && SubNames.rbegin()->substr(0, 6) == "Vertex") { - int VtId = std::atoi(SubNames.rbegin()->substr(6, 4000).c_str()) - 1; - int GeoId; - Sketcher::PointPos PosId; - Obj->getGeoVertexIndex(VtId, GeoId, PosId); - if (Obj->getGeometry(GeoId)->getTypeId() != Part::GeomPoint::getClassTypeId()) { - LastGeoId = GeoId; - LastPointPos = PosId; - lastgeotype = point; - lastvertexoraxis = true; - } - } - // check if last selected element is horizontal axis - else if (SubNames.rbegin()->size() == 6 && SubNames.rbegin()->substr(0, 6) == "H_Axis") { - LastGeoId = Sketcher::GeoEnum::HAxis; - LastPointPos = Sketcher::PointPos::none; - lastgeotype = line; - lastvertexoraxis = true; - } - // check if last selected element is vertical axis - else if (SubNames.rbegin()->size() == 6 && SubNames.rbegin()->substr(0, 6) == "V_Axis") { - LastGeoId = Sketcher::GeoEnum::VAxis; - LastPointPos = Sketcher::PointPos::none; - lastgeotype = line; - lastvertexoraxis = true; - } - // check if last selected element is the root point - else if (SubNames.rbegin()->size() == 9 && SubNames.rbegin()->substr(0, 9) == "RootPoint") { - LastGeoId = Sketcher::GeoEnum::RtPnt; - LastPointPos = Sketcher::PointPos::start; - lastgeotype = point; - lastvertexoraxis = true; - } - - if (geoids == 0 || (geoids == 1 && LastGeoId >= 0 && !lastvertexoraxis)) { - Gui::TranslatedUserWarning(Obj, - QObject::tr("Wrong selection"), - QObject::tr("A symmetric construction requires " - "at least two geometric elements, " - "the last geometric element being the reference " - "for the symmetry construction.")); - return; - } - - if (lastgeotype == invalid) { - Gui::TranslatedUserWarning(Obj, - QObject::tr("Wrong selection"), - QObject::tr("The last element must be a point " - "or a line serving as reference " - "for the symmetry construction.")); - - return; - } - - std::string geoIdList = stream.str(); - - // missing cases: - // 1- Last element is an edge, and is V or H axis - // 2- Last element is a point GeomPoint - // 3- Last element is a point (Vertex) - - if (LastGeoId >= 0 && !lastvertexoraxis) { - // if LastGeoId was added remove the last element - int index = geoIdList.rfind(','); - index = geoIdList.rfind(',', index - 1); - geoIdList.resize(index); - } - else { - int index = geoIdList.rfind(','); - geoIdList.resize(index); - } - - geoIdList.insert(0, 1, '['); - geoIdList.append(1, ']'); - - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create symmetric geometry")); - - try { - Gui::cmdAppObjectArgs(Obj, - "addSymmetric(%s, %d, %d)", - geoIdList.c_str(), - LastGeoId, - static_cast(LastPointPos)); - Gui::Command::commitCommand(); - } - catch (const Base::Exception& e) { - Gui::NotifyUserError( - Obj, QT_TRANSLATE_NOOP("Notifications", "Invalid Constraint"), e.what()); - Gui::Command::abortCommand(); - } - tryAutoRecomputeIfNotSolve(Obj); } bool CmdSketcherSymmetry::isActive() diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerSymmetry.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerSymmetry.h new file mode 100644 index 0000000000..b151a77056 --- /dev/null +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerSymmetry.h @@ -0,0 +1,291 @@ +/*************************************************************************** + * Copyright (c) 2022 Boyer Pierre-Louis * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_DrawSketchHandlerSymmetry_H +#define SKETCHERGUI_DrawSketchHandlerSymmetry_H + +#include + +#include +#include +#include +#include + +#include +#include + +#include "DrawSketchDefaultWidgetController.h" +#include "DrawSketchControllableHandler.h" + +#include "GeometryCreationMode.h" +#include "Utils.h" + +using namespace Sketcher; + +namespace SketcherGui +{ + +extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp + +class DrawSketchHandlerSymmetry; + +using DSHSymmetryController = + DrawSketchDefaultWidgetController, + /*WidgetParametersT =*/WidgetParameters<0>, + /*WidgetCheckboxesT =*/WidgetCheckboxes<2>, + /*WidgetComboboxesT =*/WidgetComboboxes<0>>; + +using DSHSymmetryControllerBase = DSHSymmetryController::ControllerBase; + +using DrawSketchHandlerSymmetryBase = DrawSketchControllableHandler; + +class DrawSketchHandlerSymmetry: public DrawSketchHandlerSymmetryBase +{ + friend DSHSymmetryController; + friend DSHSymmetryControllerBase; + +public: + explicit DrawSketchHandlerSymmetry(std::vector listOfGeoIds) + : listOfGeoIds(listOfGeoIds) + , refGeoId(Sketcher::GeoEnum::GeoUndef) + , refPosId(Sketcher::PointPos::none) + , deleteOriginal(false) + , createSymConstraints(false) + {} + + DrawSketchHandlerSymmetry(const DrawSketchHandlerSymmetry&) = delete; + DrawSketchHandlerSymmetry(DrawSketchHandlerSymmetry&&) = delete; + DrawSketchHandlerSymmetry& operator=(const DrawSketchHandlerSymmetry&) = delete; + DrawSketchHandlerSymmetry& operator=(DrawSketchHandlerSymmetry&&) = delete; + + ~DrawSketchHandlerSymmetry() override = default; + +private: + void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override + { + switch (state()) { + case SelectMode::SeekFirst: { + int VtId = getPreselectPoint(); + int CrvId = getPreselectCurve(); + int CrsId = getPreselectCross(); + + if (VtId >= 0) { // Vertex + SketchObject* Obj = sketchgui->getSketchObject(); + Obj->getGeoVertexIndex(VtId, refGeoId, refPosId); + } + else if (CrsId == 0) { // RootPoint + refGeoId = Sketcher::GeoEnum::RtPnt; + refPosId = Sketcher::PointPos::start; + } + else if (CrsId == 1) { // H_Axis + refGeoId = Sketcher::GeoEnum::HAxis; + refPosId = Sketcher::PointPos::none; + } + else if (CrsId == 2) { // V_Axis + refGeoId = Sketcher::GeoEnum::VAxis; + refPosId = Sketcher::PointPos::none; + } + else if (CrvId >= 0 || CrvId <= Sketcher::GeoEnum::RefExt) { // Curves + refGeoId = CrvId; + refPosId = Sketcher::PointPos::none; + } + else { + refGeoId = Sketcher::GeoEnum::GeoUndef; + refPosId = Sketcher::PointPos::none; + } + + + CreateAndDrawShapeGeometry(); + } break; + default: + break; + } + } + + void executeCommands() override + { + try { + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Symmetry geometries")); + + SketchObject* Obj = sketchgui->getSketchObject(); + createSymConstraints = !deleteOriginal && createSymConstraints; + Obj->addSymmetric(listOfGeoIds, refGeoId, refPosId, createSymConstraints); + + if (deleteOriginal) { + deleteOriginalGeos(); + } + tryAutoRecomputeIfNotSolve(Obj); + + Gui::Command::commitCommand(); + } + catch (const Base::Exception& e) { + e.ReportException(); + Gui::NotifyError(sketchgui, + QT_TRANSLATE_NOOP("Notifications", "Error"), + QT_TRANSLATE_NOOP("Notifications", "Failed to create symmetry")); + + Gui::Command::abortCommand(); + THROWM(Base::RuntimeError, + QT_TRANSLATE_NOOP( + "Notifications", + "Tool execution aborted") "\n") // This prevents constraints from being + // applied on non existing geometry + } + } + + void createAutoConstraints() override + { + // none + } + + std::string getToolName() const override + { + return "DSH_Symmetry"; + } + + QString getCrosshairCursorSVGName() const override + { + return QString::fromLatin1("Sketcher_Pointer_Create_Symmetry"); + } + + std::unique_ptr createWidget() const override + { + return std::make_unique(); + } + + bool isWidgetVisible() const override + { + return true; + }; + + QPixmap getToolIcon() const override + { + return Gui::BitmapFactory().pixmap("Sketcher_Symmetry"); + } + + QString getToolWidgetText() const override + { + return QString(QObject::tr("Symmetry parameters")); + } + + void activated() override + { + DrawSketchDefaultHandler::activated(); + continuousMode = false; + } + + bool canGoToNextMode() override + { + if (state() == SelectMode::SeekFirst && refGeoId == Sketcher::GeoEnum::GeoUndef) { + // Prevent validation if no reference selected. + return false; + } + return true; + } + +private: + std::vector listOfGeoIds; + int refGeoId; + Sketcher::PointPos refPosId; + bool deleteOriginal, createSymConstraints; + + void deleteOriginalGeos() + { + std::stringstream stream; + for (size_t j = 0; j < listOfGeoIds.size() - 1; j++) { + stream << listOfGeoIds[j] << ","; + } + stream << listOfGeoIds[listOfGeoIds.size() - 1]; + try { + Gui::cmdAppObjectArgs(sketchgui->getObject(), + "delGeometries([%s])", + stream.str().c_str()); + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + } + } + + void createShape(bool onlyeditoutline) override + { + SketchObject* Obj = sketchgui->getSketchObject(); + + ShapeGeometry.clear(); + + if (refGeoId == Sketcher::GeoEnum::GeoUndef) { + return; + } + + if (onlyeditoutline) { + std::map dummy1; + std::map dummy2; + std::vector symGeos = + Obj->getSymmetric(listOfGeoIds, dummy1, dummy2, refGeoId, refPosId); + + for (auto* geo : symGeos) { + ShapeGeometry.emplace_back(std::move(std::unique_ptr(geo))); + } + } + } +}; + +template<> +void DSHSymmetryController::configureToolWidget() +{ + if (!init) { // Code to be executed only upon initialisation + toolWidget->setCheckboxLabel(WCheckbox::FirstBox, + QApplication::translate("TaskSketcherTool_c1_symmetry", + "Delete original geometries (U)")); + toolWidget->setCheckboxLabel(WCheckbox::SecondBox, + QApplication::translate("TaskSketcherTool_c2_symmetry", + "Create Symmetry Constraints (J)")); + } +} + +template<> +void DSHSymmetryController::adaptDrawingToCheckboxChange(int checkboxindex, bool value) +{ + switch (checkboxindex) { + case WCheckbox::FirstBox: { + handler->deleteOriginal = value; + if (value && toolWidget->getCheckboxChecked(WCheckbox::SecondBox)) { + toolWidget->setCheckboxChecked(WCheckbox::SecondBox, false); + } + } break; + case WCheckbox::SecondBox: { + handler->createSymConstraints = value; + if (value && toolWidget->getCheckboxChecked(WCheckbox::FirstBox)) { + toolWidget->setCheckboxChecked(WCheckbox::FirstBox, false); + } + } break; + } +} + + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_DrawSketchHandlerSymmetry_H diff --git a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc index 6572d92280..621488dec2 100644 --- a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc +++ b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc @@ -246,6 +246,7 @@ icons/pointers/Sketcher_Pointer_Create_Offset.svg icons/pointers/Sketcher_Pointer_Create_Rotate.svg icons/pointers/Sketcher_Pointer_Create_Scale.svg + icons/pointers/Sketcher_Pointer_Create_Symmetry.svg icons/pointers/Sketcher_Pointer_Extension.svg icons/pointers/Sketcher_Pointer_External.svg icons/pointers/Sketcher_Pointer_Heptagon.svg diff --git a/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_Symmetry.svg b/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_Symmetry.svg new file mode 100644 index 0000000000..6b8026d8d4 --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_Symmetry.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + From 843dd55dd4c2fed3c3bf352c946b2273320d81e7 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 20 Mar 2024 17:37:17 +0100 Subject: [PATCH 55/59] TechDraw: Enable dragging and dropping of views between pages. --- .../TechDraw/Gui/ViewProviderPageExtension.cpp | 16 ++++++++++++++++ src/Mod/TechDraw/Gui/ViewProviderPageExtension.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/Mod/TechDraw/Gui/ViewProviderPageExtension.cpp b/src/Mod/TechDraw/Gui/ViewProviderPageExtension.cpp index 5cf0e88bbb..48c56d1e78 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderPageExtension.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderPageExtension.cpp @@ -71,6 +71,22 @@ bool ViewProviderPageExtension::extensionCanDropObject(App::DocumentObject* obj) return false; } +bool ViewProviderPageExtension::extensionCanDropObjectEx(App::DocumentObject* obj, App::DocumentObject* owner, + const char* subname, + const std::vector& elements) const +{ + //only DrawView objects can live on pages (except special case Template) + if (obj->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) { + return true; + } + if (obj->isDerivedFrom(TechDraw::DrawTemplate::getClassTypeId())) { + //don't let another extension try to drop templates + return true; + } + + return false; +} + void ViewProviderPageExtension::extensionDropObject(App::DocumentObject* obj) { if (obj->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) { diff --git a/src/Mod/TechDraw/Gui/ViewProviderPageExtension.h b/src/Mod/TechDraw/Gui/ViewProviderPageExtension.h index 755e986a1c..0971827105 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderPageExtension.h +++ b/src/Mod/TechDraw/Gui/ViewProviderPageExtension.h @@ -46,6 +46,9 @@ public: void extensionDragObject(App::DocumentObject*) override; bool extensionCanDropObjects() const override; bool extensionCanDropObject(App::DocumentObject*) const override; + bool extensionCanDropObjectEx(App::DocumentObject* obj, App::DocumentObject* owner, + const char* subname, + const std::vector& elements) const override; void extensionDropObject(App::DocumentObject*) override; void dropObject(App::DocumentObject* docObj); From fdfa5de192b412a1527d2b58a67ebc907b5857a6 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 20 Mar 2024 17:38:38 +0100 Subject: [PATCH 56/59] TechDraw: Remove "TechDraw_MoveView" as it is now handled by drag and drop. --- src/Mod/TechDraw/Gui/Workbench.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index 9cc7c249c2..1fdfeda451 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -221,7 +221,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const *views << "TechDraw_Symbol"; *views << "TechDraw_Image"; *views << "Separator"; - *views << "TechDraw_MoveView"; *views << "TechDraw_ShareView"; *views << "Separator"; *views << "TechDraw_ToggleFrame"; @@ -308,7 +307,6 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *views << "TechDraw_DraftView"; *views << "TechDraw_ArchView"; *views << "TechDraw_SpreadsheetView"; - *views << "TechDraw_MoveView"; *views << "TechDraw_ShareView"; *views << "TechDraw_ProjectShape"; @@ -422,7 +420,6 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const *views << "TechDraw_DetailView"; *views << "TechDraw_DraftView"; *views << "TechDraw_SpreadsheetView"; - *views << "TechDraw_MoveView"; *views << "TechDraw_ShareView"; *views << "TechDraw_ProjectShape"; From fae245be0e679a70a6f9a9ac2224bc6d258b4e77 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 20 Mar 2024 18:02:49 +0100 Subject: [PATCH 57/59] TechDraw: double clicking page switch to techdraw wb. Fixes #13061 --- src/Mod/TechDraw/Gui/ViewProviderPage.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Mod/TechDraw/Gui/ViewProviderPage.cpp b/src/Mod/TechDraw/Gui/ViewProviderPage.cpp index eeb751330c..ddd5c3dd07 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderPage.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderPage.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -262,6 +263,16 @@ void ViewProviderPage::unsetEdit(int ModNum) bool ViewProviderPage::doubleClicked(void) { + // assure the TechDraw workbench + if (App::GetApplication() + .GetUserParameter() + .GetGroup("BaseApp") + ->GetGroup("Preferences") + ->GetGroup("Mod/TechDraw") + ->GetBool("SwitchToWB", true)) { + Gui::Command::assureWorkbench("TechDrawWorkbench"); + } + show(); if (m_mdiView) { Gui::getMainWindow()->setActiveWindow(m_mdiView); From dfb4afb3af0cacd2ad427955f5174f6c33ce386b Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 20 Mar 2024 20:37:09 +0100 Subject: [PATCH 58/59] TechDraw: Enable drag and drop to and from clip groups. Remove TechDraw_ClipGroupAdd and TechDraw_ClipGroupRemove from the UI. --- src/Mod/TechDraw/Gui/ViewProviderViewClip.cpp | 40 +++++++++++++++++++ src/Mod/TechDraw/Gui/ViewProviderViewClip.h | 2 + src/Mod/TechDraw/Gui/Workbench.cpp | 25 ++---------- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/Mod/TechDraw/Gui/ViewProviderViewClip.cpp b/src/Mod/TechDraw/Gui/ViewProviderViewClip.cpp index af7b8513b0..d653b9bb25 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderViewClip.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderViewClip.cpp @@ -31,6 +31,9 @@ #endif #include +#include +#include + #include "ViewProviderViewClip.h" using namespace TechDrawGui; @@ -104,3 +107,40 @@ TechDraw::DrawViewClip* ViewProviderViewClip::getObject() const { return getViewObject(); } + + +void ViewProviderViewClip::dragObject(App::DocumentObject* docObj) +{ + if (!docObj->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) { + return; + } + + auto dv = static_cast(docObj); + + getObject()->removeView(dv); +} + +void ViewProviderViewClip::dropObject(App::DocumentObject* docObj) +{ + if (docObj->isDerivedFrom(TechDraw::DrawProjGroupItem::getClassTypeId())) { + //DPGI can not be dropped onto the Page as it belongs to DPG, not Page + return; + } + if (!docObj->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) { + return; + } + + auto dv = static_cast(docObj); + TechDraw::DrawPage* pageClip = getObject()->findParentPage(); + TechDraw::DrawPage* pageView = dv->findParentPage(); + if (!pageClip || !pageView) { + return; + } + + if (pageClip != pageView) { + pageView->removeView(dv); + pageClip->addView(dv); + } + + getObject()->addView(dv); +} \ No newline at end of file diff --git a/src/Mod/TechDraw/Gui/ViewProviderViewClip.h b/src/Mod/TechDraw/Gui/ViewProviderViewClip.h index 123b33bafd..a33f61bbe4 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderViewClip.h +++ b/src/Mod/TechDraw/Gui/ViewProviderViewClip.h @@ -56,6 +56,8 @@ public: bool canDelete(App::DocumentObject* obj) const override; + void dragObject(App::DocumentObject* docObj) override; + void dropObject(App::DocumentObject* docObj) override; }; } // namespace TechDrawGui diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index 1fdfeda451..0dbd7f64a7 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -49,7 +49,6 @@ using namespace TechDrawGui; qApp->translate("Workbench", "TechDraw Annotation"); qApp->translate("Workbench", "TechDraw Attributes"); qApp->translate("Workbench", "TechDraw Centerlines"); - qApp->translate("Workbench", "TechDraw Clips"); qApp->translate("Workbench", "TechDraw Decoration"); qApp->translate("Workbench", "TechDraw Dimensions"); qApp->translate("Workbench", "TechDraw Extend Dimensions"); @@ -217,6 +216,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *views << "TechDraw_ComplexSection"; *views << "TechDraw_DetailView"; *views << "TechDraw_ProjectionGroup"; + *views << "TechDraw_ClipGroup"; *views << "Separator"; *views << "TechDraw_Symbol"; *views << "TechDraw_Image"; @@ -235,13 +235,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const *other << "TechDraw_ArchView"; *other << "TechDraw_SpreadsheetView"; - // clip groups - Gui::MenuItem* clips = new Gui::MenuItem; - clips->setCommand("Clipped Views"); - *clips << "TechDraw_ClipGroup"; - *clips << "TechDraw_ClipGroupAdd"; - *clips << "TechDraw_ClipGroupRemove"; - // hatching Gui::MenuItem* hatch = new Gui::MenuItem; hatch->setCommand("Hatching"); @@ -263,8 +256,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const *draw << "Separator"; *draw << other; *draw << "Separator"; - *draw << clips; - *draw << "Separator"; *draw << dimensions; *draw << "Separator"; *draw << hatch; @@ -307,15 +298,10 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *views << "TechDraw_DraftView"; *views << "TechDraw_ArchView"; *views << "TechDraw_SpreadsheetView"; + *views << "TechDraw_ClipGroup"; *views << "TechDraw_ShareView"; *views << "TechDraw_ProjectShape"; - Gui::ToolBarItem* clips = new Gui::ToolBarItem(root); - clips->setCommand("TechDraw Clips"); - *clips << "TechDraw_ClipGroup"; - *clips << "TechDraw_ClipGroupAdd"; - *clips << "TechDraw_ClipGroupRemove"; - Gui::ToolBarItem* stacking = new Gui::ToolBarItem(root); stacking->setCommand("TechDraw Stacking"); *stacking << "TechDraw_StackGroup"; @@ -420,15 +406,10 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const *views << "TechDraw_DetailView"; *views << "TechDraw_DraftView"; *views << "TechDraw_SpreadsheetView"; + *views << "TechDraw_ClipGroup"; *views << "TechDraw_ShareView"; *views << "TechDraw_ProjectShape"; - Gui::ToolBarItem* clips = new Gui::ToolBarItem(root); - clips->setCommand("TechDraw Clips"); - *clips << "TechDraw_ClipGroup"; - *clips << "TechDraw_ClipGroupAdd"; - *clips << "TechDraw_ClipGroupRemove"; - Gui::ToolBarItem* stacking = new Gui::ToolBarItem(root); stacking->setCommand("TechDraw Stacking"); *stacking << "TechDraw_StackGroup"; From c7d85ff9d93ad0f1c70fe745ca5838517218642e Mon Sep 17 00:00:00 2001 From: Florian Foinant-Willig Date: Mon, 25 Mar 2024 18:31:20 +0100 Subject: [PATCH 59/59] [PartDesign] Still a helix fix (#12977) * [PartDesign] Still a helix fix If we don't break the helix path at each turns we get a vaild path for MakePipe (solid) even with an angle. * Decrease helix tests requirements --- src/Mod/PartDesign/App/FeatureHelix.cpp | 84 ++----------------- src/Mod/PartDesign/App/FeatureHelix.h | 2 +- .../PartDesign/PartDesignTests/TestHelix.py | 8 +- 3 files changed, 13 insertions(+), 81 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureHelix.cpp b/src/Mod/PartDesign/App/FeatureHelix.cpp index e9730b1a79..97c52a42fb 100644 --- a/src/Mod/PartDesign/App/FeatureHelix.cpp +++ b/src/Mod/PartDesign/App/FeatureHelix.cpp @@ -232,79 +232,12 @@ App::DocumentObjectExecReturn* Helix::execute() // generate the helix path TopoDS_Shape path = generateHelixPath(); - TopoDS_Shape auxpath = generateHelixPath(1.0); - // Use MakePipe for frenet ( Angle is 0 ) calculations, faster than MakePipeShell - if ( Angle.getValue() == 0 ) { - TopoDS_Shape face = Part::FaceMakerCheese::makeFace(wires); - face.Move(invObjLoc); - BRepOffsetAPI_MakePipe mkPS(TopoDS::Wire(path), face, GeomFill_Trihedron::GeomFill_IsFrenet, Standard_False); - mkPS.Build(); - result = mkPS.Shape(); - } else { - std::vector> wiresections; - for (TopoDS_Wire& wire : wires) - wiresections.emplace_back(1, wire); - - //build all shells - std::vector shells; - std::vector frontwires, backwires; - for (std::vector& wires : wiresections) { - - BRepOffsetAPI_MakePipeShell mkPS(TopoDS::Wire(path)); - - // Frenet mode doesn't place the face quite right on an angled helix, so - // use the auxiliary spine to force that. - mkPS.SetMode(TopoDS::Wire(auxpath), true); // this is for auxiliary - - for (TopoDS_Wire& wire : wires) { - wire.Move(invObjLoc); - mkPS.Add(wire); - } - - if (!mkPS.IsReady()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Could not build")); - mkPS.Build(); - - shells.push_back(mkPS.Shape()); - - if (!mkPS.Shape().Closed()) { - // // shell is not closed - use simulate to get the end wires - TopTools_ListOfShape sim; - mkPS.Simulate(2, sim); - - frontwires.push_back(TopoDS::Wire(sim.First())); - backwires.push_back(TopoDS::Wire(sim.Last())); - } - BRepBuilderAPI_MakeSolid mkSolid; - - if (!frontwires.empty()) { - // build the end faces, sew the shell and build the final solid - TopoDS_Shape front = Part::FaceMakerCheese::makeFace(frontwires); - TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires); - - BRepBuilderAPI_Sewing sewer; - sewer.SetTolerance(Precision::Confusion()); - sewer.Add(front); - sewer.Add(back); - - for (TopoDS_Shape& s : shells) - sewer.Add(s); - sewer.Perform(); - mkSolid.Add(TopoDS::Shell(sewer.SewedShape())); - } - else { - // shells are already closed - add them directly - for (TopoDS_Shape& s : shells) { - mkSolid.Add(TopoDS::Shell(s)); - } - } - if (!mkSolid.IsDone()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Error: Result is not a solid")); - - result = mkSolid.Shape(); - } - } + TopoDS_Shape face = Part::FaceMakerCheese::makeFace(wires); + face.Move(invObjLoc); + BRepOffsetAPI_MakePipe mkPS(TopoDS::Wire(path), face, GeomFill_Trihedron::GeomFill_IsFrenet, Standard_False); + mkPS.Build(); + result = mkPS.Shape(); BRepClass3d_SolidClassifier SC(result); SC.PerformInfinitePoint(Precision::Confusion()); @@ -403,7 +336,7 @@ void Helix::updateAxis() Axis.setValue(dir.x, dir.y, dir.z); } -TopoDS_Shape Helix::generateHelixPath(double startOffset0) +TopoDS_Shape Helix::generateHelixPath() { double turns = Turns.getValue(); double height = Height.getValue(); @@ -449,7 +382,7 @@ TopoDS_Shape Helix::generateHelixPath(double startOffset0) bool turned = axisOffset < 0; // since the factor does not only change the radius but also the path position, we must shift its offset back // using the square of the factor - double startOffset = 10000.0 * std::fabs(startOffset0 + profileCenter * axisVector - baseVector * axisVector); + double startOffset = 10000.0 * std::fabs(baseVector * axisVector); if (radius < Precision::Confusion()) { // in this case ensure that axis is not in the sketch plane @@ -466,8 +399,7 @@ TopoDS_Shape Helix::generateHelixPath(double startOffset0) radiusTop = radius + height * tan(Base::toRadians(angle)); //build the helix path - //TopoShape helix = TopoShape().makeLongHelix(pitch, height, radius, angle, leftHanded); - TopoDS_Shape path = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, 1, leftHanded); + TopoDS_Shape path = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, 1000, leftHanded); /* * The helix wire is created with the axis coinciding with z-axis and the start point at (radius, 0, 0) diff --git a/src/Mod/PartDesign/App/FeatureHelix.h b/src/Mod/PartDesign/App/FeatureHelix.h index 531ded92d6..3a4a3a7cb7 100644 --- a/src/Mod/PartDesign/App/FeatureHelix.h +++ b/src/Mod/PartDesign/App/FeatureHelix.h @@ -80,7 +80,7 @@ protected: void updateAxis(); /// generate helix and move it to the right location. - TopoDS_Shape generateHelixPath(double startOffset0 = 0.0); + TopoDS_Shape generateHelixPath(); // project shape on plane. Used for detecting self intersection. TopoDS_Shape projectShape(const TopoDS_Shape& input, const gp_Ax2& plane); diff --git a/src/Mod/PartDesign/PartDesignTests/TestHelix.py b/src/Mod/PartDesign/PartDesignTests/TestHelix.py index ea938b5a3e..3be150af22 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestHelix.py +++ b/src/Mod/PartDesign/PartDesignTests/TestHelix.py @@ -87,15 +87,15 @@ class TestHelix(unittest.TestCase): helix.Angle = 0 helix.Mode = 1 self.Doc.recompute() - self.assertAlmostEqual(helix.Shape.Volume, 78.95687956849457,places=5) + self.assertAlmostEqual(helix.Shape.Volume, 78.957,places=3) helix.Angle = 25 self.Doc.recompute() - self.assertAlmostEqual(helix.Shape.Volume, 134.17450779511307,places=5) + self.assertAlmostEqual(helix.Shape.Volume, 134.17,places=2) profileSketch.addGeometry(Part.Circle(FreeCAD.Vector(2, 0, 0), FreeCAD.Vector(0,0,1), 0.5) ) self.Doc.recompute() - self.assertAlmostEqual(helix.Shape.Volume, 100.63088079046352,places=5) + self.assertAlmostEqual(helix.Shape.Volume, 100.63,places=2) def testRectangle(self): @@ -174,7 +174,7 @@ class TestHelix(unittest.TestCase): helix.Mode = 0 helix.Reversed = True self.Doc.recompute() - self.assertAlmostEqual(helix.Shape.Volume, 388285.4117047924,places=5) + self.assertAlmostEqual(helix.Shape.Volume/1e5, 3.8828,places=4) def tearDown(self): FreeCAD.closeDocument("PartDesignTestHelix")