/*************************************************************************** * Copyright (c) 2011 Juergen Riegel * * * * 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 * * * ***************************************************************************/ #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 #endif #include #include #include #include #include #include #include #include #include "json.hpp" #include "FeatureHole.h" namespace PartDesign { /* TRANSLATOR PartDesign::Hole */ const char* Hole::DepthTypeEnums[] = { "Dimension", "ThroughAll", /*, "UpToFirst", */ NULL }; const char* Hole::ThreadTypeEnums[] = { "None", "ISOMetricProfile", "ISOMetricFineProfile", "UNC", "UNF", "UNEF", NULL}; const char* Hole::ThreadFitEnums[] = { "Standard", "Close", NULL}; const char* Hole::DrillPointEnums[] = { "Flat", "Angled", NULL}; /* "None" profile */ const char* Hole::HoleCutType_None_Enums[] = { "None", "Counterbore", "Countersink", NULL }; const char* Hole::ThreadSize_None_Enums[] = { "None", NULL }; const char* Hole::ThreadClass_None_Enums[] = { "None", NULL }; /* Sources: http://www.engineeringtoolbox.com/metric-threads-d_777.html http://www.metalmart.com/tools/miscellaneous-guides/standard-drill-size/ */ const Hole::ThreadDescription Hole::threadDescription[][171] = { /* None */ { { "---", 6.0, 0.0 }, }, /* ISO metric coarse */ { { "M1.6", 1.60, 0.35 }, { "M2", 2.00, 0.40 }, { "M2.5", 2.50, 0.45 }, { "M3", 3.00, 0.50 }, { "M3.5", 3.50, 0.60 }, { "M4", 4.00, 0.70 }, { "M5", 5.00, 0.80 }, { "M6", 6.00, 1.00 }, { "M8", 8.00, 1.25 }, { "M10", 10.00, 1.50 }, { "M12", 12.00, 1.75 }, { "M14", 14.00, 2.00 }, { "M16", 16.00, 2.00 }, { "M20", 20.00, 2.50 }, { "M22", 22.00, 2.50 }, { "M24", 24.00, 3.00 }, { "M27", 27.00, 3.00 }, { "M30", 30.00, 3.50 }, { "M36", 36.00, 4.00 }, { "M42", 42.00, 4.50 }, { "M48", 48.00, 5.00 }, { "M56", 56.00, 5.50 }, { "M64", 64.00, 6.00 }, { "M68", 68.00, 6.00 }, }, /* ISO metric fine */ { { "M1.0x0.2", 1.0, 0.20 }, { "M1.1x0.2", 1.1, 0.20 }, { "M1.2x0.2", 1.2, 0.20 }, { "M1.4x0.2", 1.4, 0.20 }, { "M1.6x0.2", 1.6, 0.20 }, { "M1.8x0.2", 1.8, 0.20 }, { "M2x0.25", 2.0, 0.25 }, { "M2.2x0.25", 2.2, 0.25 }, { "M2.5x0.35", 2.5, 0.35 }, { "M3x0.35", 3.0, 0.35 }, { "M3.5x0.35", 3.5, 0.35 }, { "M4x0.5", 4.0, 0.50 }, { "M4.5x0.5", 4.5, 0.50 }, { "M5x0.5", 5.0, 0.50 }, { "M5.5x0.5", 5.5, 0.50 }, { "M6x0.75", 6.0, 0.75 }, { "M7x0.75", 7.0, 0.75 }, { "M8x0.75", 8.0, 0.75 }, { "M8x1.0", 8.0, 1.00 }, { "M9x0.75", 9.0, 0.75 }, { "M9x1", 9.0, 1.00 }, { "M10x0.75", 10.0, 0.75 }, { "M10x1", 10.0, 1.00 }, { "M10x1.25", 10.0, 1.25 }, { "M11x0.75", 11.0, 0.75 }, { "M11x1", 11.0, 1.00 }, { "M12x1", 12.0, 1.00 }, { "M12x1.25", 12.0, 1.25 }, { "M12x1.5", 12.0, 1.50 }, { "M14x1.0", 14.0, 1.00 }, { "M14x1.25", 14.0, 1.25 }, { "M14x1.5", 14.0, 1.50 }, { "M15x1", 15.0, 1.00 }, { "M15x1.5", 15.0, 1.50 }, { "M16x1", 16.0, 1.00 }, { "M16x1.5", 16.0, 1.50 }, { "M17x1.0", 17.0, 1.00 }, { "M17x1.5", 17.0, 1.50 }, { "M18x1.0", 18.0, 1.00 }, { "M18x1.5", 18.0, 1.50 }, { "M18x2.0", 18.0, 2.00 }, { "M20x1.0", 20.0, 1.00 }, { "M20x1.5", 20.0, 1.50 }, { "M20x2.0", 20.0, 2.00 }, { "M22x1.0", 22.0, 1.00 }, { "M22x1.5", 22.0, 1.50 }, { "M22x2.0", 22.0, 2.00 }, { "M24x1.0", 24.0, 1.00 }, { "M24x1.5", 24.0, 1.50 }, { "M24x2.0", 24.0, 2.00 }, { "M25x1.0", 25.0, 1.00 }, { "M25x1.5", 25.0, 1.50 }, { "M25x2.0", 25.0, 2.00 }, { "M27x1.0", 27.0, 1.00 }, { "M27x1.5", 27.0, 1.50 }, { "M27x2.0", 27.0, 2.00 }, { "M28x1.0", 28.0, 1.00 }, { "M28x1.5", 28.0, 1.50 }, { "M28x2.0", 28.0, 2.00 }, { "M30x1.0", 30.0, 1.00 }, { "M30x1.5", 30.0, 1.50 }, { "M30x2.0", 30.0, 2.00 }, { "M30x3.0", 30.0, 3.00 }, { "M32x1.5", 32.0, 1.50 }, { "M32x2.0", 32.0, 2.00 }, { "M33x1.5", 33.0, 1.50 }, { "M33x2.0", 33.0, 2.00 }, { "M33x3.0", 33.0, 3.00 }, { "M35x1.5", 35.0, 1.50 }, { "M35x2.0", 35.0, 2.00 }, { "M36x1.5", 36.0, 1.50 }, { "M36x2.0", 36.0, 2.00 }, { "M36x3.0", 36.0, 3.00 }, { "M39x1.5", 39.0, 1.50 }, { "M39x2.0", 39.0, 2.00 }, { "M39x3.0", 39.0, 3.00 }, { "M40x1.5", 40.0, 1.50 }, { "M40x2.0", 40.0, 2.00 }, { "M40x3.0", 40.0, 3.00 }, { "M42x1.5", 42.0, 1.50 }, { "M42x2.0", 42.0, 2.00 }, { "M42x3.0", 42.0, 3.00 }, { "M42x4.0", 42.0, 4.00 }, { "M45x1.5", 45.0, 1.50 }, { "M45x2.0", 45.0, 2.00 }, { "M45x3.0", 45.0, 3.00 }, { "M45x4.0", 45.0, 4.00 }, { "M48x1.5", 48.0, 1.50 }, { "M48x2.0", 48.0, 2.00 }, { "M48x3.0", 48.0, 3.00 }, { "M48x4.0", 48.0, 4.00 }, { "M50x1.5", 50.0, 1.50 }, { "M50x2.0", 50.0, 2.00 }, { "M50x3.0", 50.0, 3.00 }, { "M52x1.5", 52.0, 1.50 }, { "M52x2.0", 52.0, 2.00 }, { "M52x3.0", 52.0, 3.00 }, { "M52x4.0", 52.0, 4.00 }, { "M55x1.5", 55.0, 1.50 }, { "M55x2.0", 55.0, 2.00 }, { "M55x3.0", 55.0, 3.00 }, { "M55x4.0", 55.0, 4.00 }, { "M56x1.5", 56.0, 1.50 }, { "M56x2.0", 56.0, 2.00 }, { "M56x3.0", 56.0, 3.00 }, { "M56x4.0", 56.0, 4.00 }, { "M58x1.5", 58.0, 1.50 }, { "M58x2.0", 58.0, 2.00 }, { "M58x3.0", 58.0, 3.00 }, { "M58x4.0", 58.0, 4.00 }, { "M60x1.5", 60.0, 1.50 }, { "M60x2.0", 60.0, 2.00 }, { "M60x3.0", 60.0, 3.00 }, { "M60x4.0", 60.0, 4.00 }, { "M62x1.5", 62.0, 1.50 }, { "M62x2.0", 62.0, 2.00 }, { "M62x3.0", 62.0, 3.00 }, { "M62x4.0", 62.0, 4.00 }, { "M64x1.5", 64.0, 1.50 }, { "M64x2.0", 64.0, 2.00 }, { "M64x3.0", 64.0, 3.00 }, { "M64x4.0", 64.0, 4.00 }, { "M65x1.5", 65.0, 1.50 }, { "M65x2.0", 65.0, 2.00 }, { "M65x3.0", 65.0, 3.00 }, { "M65x4.0", 65.0, 4.00 }, { "M68x1.5", 68.0, 1.50 }, { "M68x2.0", 68.0, 2.00 }, { "M68x3.0", 68.0, 3.00 }, { "M68x4.0", 68.0, 4.00 }, { "M70x1.5", 70.0, 1.50 }, { "M70x2.0", 70.0, 2.00 }, { "M70x3.0", 70.0, 3.00 }, { "M70x4.0", 70.0, 4.00 }, { "M70x6.0", 70.0, 6.00 }, { "M72x1.5", 72.0, 1.50 }, { "M72x2.0", 72.0, 2.00 }, { "M72x3.0", 72.0, 3.00 }, { "M72x4.0", 72.0, 4.00 }, { "M72x6.0", 72.0, 6.00 }, { "M75x1.5", 75.0, 1.50 }, { "M75x2.0", 75.0, 2.00 }, { "M75x3.0", 75.0, 3.00 }, { "M75x4.0", 75.0, 4.00 }, { "M75x6.0", 75.0, 6.00 }, { "M76x1.5", 76.0, 1.50 }, { "M76x2.0", 76.0, 2.00 }, { "M76x3.0", 76.0, 3.00 }, { "M76x4.0", 76.0, 4.00 }, { "M76x6.0", 76.0, 6.00 }, { "M80x1.5", 80.0, 1.50 }, { "M80x2.0", 80.0, 2.00 }, { "M80x3.0", 80.0, 3.00 }, { "M80x4.0", 80.0, 4.00 }, { "M80x6.0", 80.0, 6.00 }, { "M85x2.0", 85.0, 2.00 }, { "M85x3.0", 85.0, 3.00 }, { "M85x4.0", 85.0, 4.00 }, { "M85x6.0", 85.0, 6.00 }, { "M90x2.0", 90.0, 2.00 }, { "M90x3.0", 90.0, 3.00 }, { "M90x4.0", 90.0, 4.00 }, { "M90x6.0", 90.0, 6.00 }, { "M95x2.0", 95.0, 2.00 }, { "M95x3.0", 95.0, 3.00 }, { "M95x4.0", 95.0, 4.00 }, { "M95x6.0", 95.0, 6.00 }, { "M100x2.0", 100.0, 2.00 }, { "M100x3.0", 100.0, 3.00 }, { "M100x4.0", 100.0, 4.00 }, { "M100x6.0", 100.0, 6.00 } }, /* UNC */ { { "#1", 1.8542, 0.3969 }, { "#2", 2.1844, 0.4536 }, { "#3", 2.5146, 0.5292 }, { "#4", 2.8448, 0.6350 }, { "#5", 3.1750, 0.6350 }, { "#6", 3.5052, 0.7938 }, { "#8", 4.1656, 0.7938 }, { "#10", 4.8260, 1.0583 }, { "#12", 5.4864, 1.0583 }, { "1/4", 6.3500, 1.2700 }, { "5/16", 7.9375, 1.4111 }, { "3/8", 9.5250, 1.5875 }, { "7/16", 11.1125, 1.8143 }, { "1/2", 12.7000, 1.9538 }, { "9/16", 14.2875, 2.1167 }, { "5/8", 15.8750, 2.3091 }, { "3/4", 19.0500, 2.5400 }, { "7/8", 22.2250, 2.8222 }, { "1", 25.4000, 3.1750 }, }, /* UNF */ { { "#0", 1.5240, 0.3175 }, { "#1", 1.8542, 0.3528 }, { "#2", 2.1844, 0.3969 }, { "#3", 2.5146, 0.4536 }, { "#4", 2.8448, 0.5292 }, { "#5", 3.1750, 0.5773 }, { "#6", 3.5052, 0.6350 }, { "#8", 4.1656, 0.7056 }, { "#10", 4.8260, 0.7938 }, { "#12", 5.4864, 0.9071 }, { "1/4", 6.3500, 0.9071 }, { "5/16", 7.9375, 1.0583 }, { "3/8", 9.5250, 1.0583 }, { "7/16", 11.1125, 1.2700 }, { "1/2", 12.7000, 1.2700 }, { "9/16", 14.2875, 1.4111 }, { "5/8", 15.8750, 1.4111 }, { "3/4", 19.0500, 1.5875 }, { "7/8", 22.2250, 1.8143 }, { "1", 25.4000, 2.1167 }, } , /* UNEF */ { { "#12", 5.4864, 0.7938 }, { "1/4", 6.3500, 0.7938 }, { "5/16", 7.9375, 0.7938 }, { "3/8", 9.5250, 0.7938 }, { "7/16", 11.1125, 0.9071 }, { "1/2", 12.7000, 0.9071 }, { "9/16", 14.2875, 1.0583 }, { "5/8", 15.8750, 1.0583 }, { "3/4", 19.0500, 1.2700 }, { "7/8", 22.2250, 1.2700 }, { "1", 25.4000, 1.2700 }, } }; /* ISO coarse metric enums */ std::vector Hole::HoleCutType_ISOmetric_Enums = { "None", "Counterbore", "Countersink", "Cheesehead (deprecated)", "Countersink socket screw (deprecated)", "Cap screw (deprecated)" }; const char* Hole::ThreadSize_ISOmetric_Enums[] = { "M1.6", "M2", "M2.5", "M3", "M3.5", "M4", "M5", "M6", "M8", "M10", "M12", "M14", "M16", "M20", "M22", "M24", "M27", "M30", "M36", "M42", "M48", "M56", "M64", "M68", NULL }; const char* Hole::ThreadClass_ISOmetric_Enums[] = { "4G", "4H", "5G", "5H", "6G", "6H", "7G", "7H","8G", "8H", NULL }; std::vector Hole::HoleCutType_ISOmetricfine_Enums = { "None", "Counterbore", "Countersink", "Cheesehead (deprecated)", "Countersink socket screw (deprecated)", "Cap screw (deprecated)" }; const char* Hole::ThreadSize_ISOmetricfine_Enums[] = { "M1.0x0.2", "M1.1x0.2", "M1.2x0.2", "M1.4x0.2", "M1.6x0.2", "M1.8x0.2", "M2x0.25", "M2.2x0.25", "M2.5x0.35", "M3x0.35", "M3.5x0.35", "M4x0.5", "M4.5x0.5", "M5x0.5", "M5.5x0.5", "M6x0.75", "M7x0.75", "M8x0.75", "M8x1.0", "M9x0.75", "M9x 1", "M10x0.75", "M10x1", "M10x1.25", "M11x0.75", "M11x1", "M12x1", "M12x1.25", "M12x1.5", "M14x1.0", "M14x1.25", "M14x1.5", "M15x1", "M15x1.5", "M16x1", "M16x1.5", "M17x1.0", "M17x1.5", "M18x1.0", "M18x1.5", "M18x2.0", "M20x1.0", "M20x1.5", "M20x2.0", "M22x1.0", "M22x1.5", "M22x2.0", "M24x1.0", "M24x1.5", "M24x2.0", "M25x1.0", "M25x1.5", "M25x2.0", "M27x1.0", "M27x1.5", "M27x2.0", "M28x1.0", "M28x1.5", "M28x2.0", "M30x1.0", "M30x1.5", "M30x2.0", "M30x3.0", "M32x1.5", "M32x2.0", "M33x1.5", "M33x2.0", "M33x3.0", "M35x1.5", "M35x2.0", "M36x1.5", "M36x2.0", "M36x3.0", "M39x1.5", "M39x2.0", "M39x3.0", "M40x1.5", "M40x2.0", "M40x3.0", "M42x1.5", "M42x2.0", "M42x3.0", "M42x4.0", "M45x1.5", "M45x2.0", "M45x3.0", "M45x4.0", "M48x1.5", "M48x2.0", "M48x3.0", "M48x4.0", "M50x1.5", "M50x2.0", "M50x3.0", "M52x1.5", "M52x2.0", "M52x3.0", "M52x4.0", "M55x1.5", "M55x2.0", "M55x3.0", "M55x4.0", "M56x1.5", "M56x2.0", "M56x3.0", "M56x4.0", "M58x1.5", "M58x2.0", "M58x3.0", "M58x4.0", "M60x1.5", "M60x2.0", "M60x3.0", "M60x4.0", "M62x1.5", "M62x2.0", "M62x3.0", "M62x4.0", "M64x1.5", "M64x2.0", "M64x3.0", "M64x4.0", "M65x1.5", "M65x2.0", "M65x3.0", "M65x4.0", "M68x1.5", "M68x2.0", "M68x3.0", "M68x4.0", "M70x1.5", "M70x2.0", "M70x3.0", "M70x4.0", "M70x6.0", "M72x1.5", "M72x2.0", "M72x3.0", "M72x4.0", "M72x6.0", "M75x1.5", "M75x2.0", "M75x3.0", "M75x4.0", "M75x6.0", "M76x1.5", "M76x2.0", "M76x3.0", "M76x4.0", "M76x6.0", "M80x1.5", "M80x2.0", "M80x3.0", "M80x4.0", "M80x6.0", "M85x2.0", "M85x3.0", "M85x4.0", "M85x6.0", "M90x2.0", "M90x3.0", "M90x4.0", "M90x6.0", "M95x2.0", "M95x3.0", "M95x4.0", "M95x6.0", "M100x2.0", "M100x3.0", "M100x4.0", "M100x6.0", NULL }; const char* Hole::ThreadClass_ISOmetricfine_Enums[] = { "4G", "4H", "5G", "5H", "6G", "6H", "7G", "7H","8G", "8H", NULL }; /* Details from https://en.wikipedia.org/wiki/Unified_Thread_Standard */ /* UTS coarse */ const char* Hole::HoleCutType_UNC_Enums[] = { "None", "Counterbore", "Countersink", NULL}; const char* Hole::ThreadSize_UNC_Enums[] = { "#1", "#2", "#3", "#4", "#5", "#6", "#8", "#10", "#12", "1/4", "5/16", "3/8", "7/16", "1/2", "9/16", "5/8", "3/4", "7/8", "1", NULL }; const char* Hole::ThreadClass_UNC_Enums[] = { "1B", "2B", "3B", NULL }; /* UTS fine */ const char* Hole::HoleCutType_UNF_Enums[] = { "None", "Counterbore", "Countersink", NULL}; const char* Hole::ThreadSize_UNF_Enums[] = { "#1", "#2", "#3", "#4", "#5", "#6", "#8", "#10", "#12", "1/4", "5/16", "3/8", "7/16", "1/2", "9/16", "5/8", "3/4", "7/8", "1", NULL }; const char* Hole::ThreadClass_UNF_Enums[] = { "1B", "2B", "3B", NULL }; /* UTS extrafine */ const char* Hole::HoleCutType_UNEF_Enums[] = { "None", "Counterbore", "Countersink", NULL}; const char* Hole::ThreadSize_UNEF_Enums[] = { "#12", "1/4", "5/16", "3/8", "7/16", "1/2", "9/16", "5/8", "3/4", "7/8", "1", NULL }; const char* Hole::ThreadClass_UNEF_Enums[] = { "1B", "2B", "3B", NULL }; const char* Hole::ThreadDirectionEnums[] = { "Right", "Left", NULL}; PROPERTY_SOURCE(PartDesign::Hole, PartDesign::ProfileBased) Hole::Hole() { addSubType = FeatureAddSub::Subtractive; readCutDefinitions(); ADD_PROPERTY_TYPE(Threaded, ((long)0), "Hole", App::Prop_None, "Threaded"); ADD_PROPERTY_TYPE(ModelActualThread, ((long)0), "Hole", App::Prop_None, "Model actual thread"); ADD_PROPERTY_TYPE(ThreadPitch, ((long)0), "Hole", App::Prop_None, "Thread pitch"); ADD_PROPERTY_TYPE(ThreadAngle, ((long)0), "Hole", App::Prop_None, "Thread angle"); ADD_PROPERTY_TYPE(ThreadCutOffInner, ((long)0), "Hole", App::Prop_None, "Thread CutOff Inner"); ADD_PROPERTY_TYPE(ThreadCutOffOuter, ((long)0), "Hole", App::Prop_None, "Thread CutOff Outer"); ADD_PROPERTY_TYPE(ThreadType, ((long)0), "Hole", App::Prop_None, "Thread type"); ThreadType.setEnums(ThreadTypeEnums); ADD_PROPERTY_TYPE(ThreadSize, ((long)0), "Hole", App::Prop_None, "Thread size"); ThreadSize.setEnums(ThreadSize_None_Enums); ADD_PROPERTY_TYPE(ThreadClass, ((long)0), "Hole", App::Prop_None, "Thread class"); ThreadClass.setEnums(ThreadClass_None_Enums); ADD_PROPERTY_TYPE(ThreadFit, ((long)0), "Hole", App::Prop_None, "Thread fit"); ThreadFit.setEnums(ThreadFitEnums); ADD_PROPERTY_TYPE(Diameter, (6.0), "Hole", App::Prop_None, "Diameter"); ADD_PROPERTY_TYPE(ThreadDirection, ((long)0), "Hole", App::Prop_None, "Thread direction"); ThreadDirection.setEnums(ThreadDirectionEnums); ADD_PROPERTY_TYPE(HoleCutType, ((long)0), "Hole", App::Prop_None, "Head cut type"); HoleCutType.setEnums(HoleCutType_None_Enums); ADD_PROPERTY_TYPE(HoleCutDiameter, (0.0), "Hole", App::Prop_None, "Head cut diameter"); ADD_PROPERTY_TYPE(HoleCutDepth, (0.0), "Hole", App::Prop_None, "Head cut deth"); ADD_PROPERTY_TYPE(HoleCutCountersinkAngle, (90.0), "Hole", App::Prop_None, "Head cut countersink angle"); ADD_PROPERTY_TYPE(DepthType, ((long)0), "Hole", App::Prop_None, "Type"); DepthType.setEnums(DepthTypeEnums); ADD_PROPERTY_TYPE(Depth, (25.0), "Hole", App::Prop_None, "Length"); ADD_PROPERTY_TYPE(DrillPoint, ((long)1), "Hole", App::Prop_None, "Drill point type"); DrillPoint.setEnums(DrillPointEnums); ADD_PROPERTY_TYPE(DrillPointAngle, (118.0), "Hole", App::Prop_None, "Drill point angle"); ADD_PROPERTY_TYPE(Tapered, ((bool)false),"Hole", App::Prop_None, "Tapered"); ADD_PROPERTY_TYPE(TaperedAngle, (90.0), "Hole", App::Prop_None, "Tapered angle"); } void Hole::updateHoleCutParams() { std::string threadType = ThreadType.getValueAsString(); if (threadType == "ISOMetricProfile" || threadType == "ISOMetricFineProfile") { std::string holeCutType = HoleCutType.getValueAsString(); if (ThreadType.getValue() < 0) throw Base::IndexError("Thread type out of range"); if (ThreadSize.getValue() < 0) throw Base::IndexError("Thread size out of range"); if (holeCutType == "None") return; // get diameter and size double diameter = threadDescription[ThreadType.getValue()][ThreadSize.getValue()].diameter; std::string threadSize{ ThreadSize.getValueAsString() }; // we don't update for these settings but we need to set a value for new holes // if we have a cut but the values are zero, we assume it is a new hole // we take in this case the values from the norm ISO 4762 or ISO 10642 if (holeCutType == "Counterbore") { //read ISO 4762 values const CutDimensionSet& counter = find_cutDimensionSet(threadType, "ISO 4762"); const CounterBoreDimension& dimen = counter.get_bore(threadSize); if (HoleCutDiameter.getValue() == 0.0) { HoleCutDiameter.setValue(dimen.diameter); HoleCutDepth.setValue(dimen.depth); } if (HoleCutDepth.getValue() == 0.0) HoleCutDepth.setValue(dimen.depth); } else if (holeCutType == "Countersink") { if (HoleCutDiameter.getValue() == 0.0) { //read ISO 10642 values const CutDimensionSet& counter = find_cutDimensionSet(threadType, "ISO 10642"); const CounterSinkDimension& dimen = counter.get_sink(threadSize); HoleCutDiameter.setValue(dimen.diameter); HoleCutCountersinkAngle.setValue(counter.angle); } } // cut definition CutDimensionKey key { threadType, holeCutType }; if (HoleCutTypeMap.count(key)) { const CutDimensionSet &counter = find_cutDimensionSet(key); if (counter.cut_type == CutDimensionSet::Counterbore) { const CounterBoreDimension &dimen = counter.get_bore(threadSize); if (dimen.thread == "None") { // valid values for visual feedback HoleCutDiameter.setValue(Diameter.getValue() + 0.1); HoleCutDepth.setValue(0.1); } else { HoleCutDiameter.setValue(dimen.diameter); HoleCutDepth.setValue(dimen.depth); } } else if (counter.cut_type == CutDimensionSet::Countersink) { const CounterSinkDimension &dimen = counter.get_sink(threadSize); if (dimen.thread == "None") { // valid values for visual feedback HoleCutDiameter.setValue(Diameter.getValue() + 0.1); } else { HoleCutDiameter.setValue(dimen.diameter); HoleCutCountersinkAngle.setValue(counter.angle); } } } // handle legacy types but don’t change user settings for // user defined None, Counterbore and Countersink // handle legacy types but don’t change user settings for // user defined None, Counterbore and Countersink else if (holeCutType == "Cheesehead (deprecated)") { HoleCutDiameter.setValue(diameter * 1.6); HoleCutDepth.setValue(diameter * 0.6); } else if (holeCutType == "Countersink socket screw (deprecated)") { HoleCutDiameter.setValue(diameter * 2.0); HoleCutDepth.setValue(diameter * 0.0); } else if (holeCutType == "Cap screw (deprecated)") { HoleCutDiameter.setValue(diameter * 1.5); HoleCutDepth.setValue(diameter * 1.25); } } } void Hole::updateDiameterParam() { // Diameter parameter depends on Threaded, ThreadType, ThreadSize, and ThreadFit int threadType = ThreadType.getValue(); int threadSize = ThreadSize.getValue(); if (threadType < 0) { // When restoring the feature it might be in an inconsistent state. // So, just silently ignore it instead of throwing an exception. if (isRestoring()) return; throw Base::IndexError("Thread type out of range"); } if (threadSize < 0) { // When restoring the feature it might be in an inconsistent state. // So, just silently ignore it instead of throwing an exception. if (isRestoring()) return; throw Base::IndexError("Thread size out of range"); } double diameter = threadDescription[threadType][threadSize].diameter; double pitch = threadDescription[threadType][threadSize].pitch; if (threadType == 0) return; if (Threaded.getValue()) { if (std::string(ThreadType.getValueAsString()) != "None") { double h = pitch * sqrt(3) / 2; // Basic profile for ISO and UTS threads ThreadPitch.setValue(pitch); ThreadAngle.setValue(60); ThreadCutOffInner.setValue(h/8); ThreadCutOffOuter.setValue(h/4); } if (ModelActualThread.getValue()) { pitch = ThreadPitch.getValue(); } /* Use thread tap diameter, normal D - pitch */ diameter = diameter - pitch; } else { switch ( ThreadFit.getValue() ) { case 0: /* standard */ diameter = ( 5 * ( (int)( ( diameter * 110 ) / 5 ) ) ) / 100.0; break; case 1: /* close */ diameter = ( 5 * ( (int)( ( diameter * 105 ) / 5 ) ) ) / 100.0; break; default: assert( 0 ); } } Diameter.setValue(diameter); } void Hole::onChanged(const App::Property *prop) { if (prop == &ThreadType) { std::string type, holeCutType; if (ThreadType.isValid()) type = ThreadType.getValueAsString(); if (HoleCutType.isValid()) holeCutType = HoleCutType.getValueAsString(); if (type == "None" ) { ThreadSize.setEnums(ThreadSize_None_Enums); ThreadClass.setEnums(ThreadClass_None_Enums); HoleCutType.setEnums(HoleCutType_None_Enums); Threaded.setReadOnly(true); ThreadSize.setReadOnly(true); ThreadFit.setReadOnly(true); ThreadClass.setReadOnly(true); Diameter.setReadOnly(false); Threaded.setValue(0); } else if ( type == "ISOMetricProfile" ) { ThreadSize.setEnums(ThreadSize_ISOmetric_Enums); ThreadClass.setEnums(ThreadClass_ISOmetric_Enums); HoleCutType.setEnums(HoleCutType_ISOmetric_Enums); Threaded.setReadOnly(false); ThreadSize.setReadOnly(false); // thread class and direction are only sensible if threaded // fit only sensible if not threaded ThreadFit.setReadOnly(Threaded.getValue()); ThreadClass.setReadOnly(!Threaded.getValue()); Diameter.setReadOnly(true); } else if ( type == "ISOMetricFineProfile" ) { ThreadSize.setEnums(ThreadSize_ISOmetricfine_Enums); ThreadClass.setEnums(ThreadClass_ISOmetricfine_Enums); HoleCutType.setEnums(HoleCutType_ISOmetricfine_Enums); Threaded.setReadOnly(false); ThreadSize.setReadOnly(false); // thread class and direction are only sensible if threaded // fit only sensible if not threaded ThreadFit.setReadOnly(Threaded.getValue()); ThreadClass.setReadOnly(!Threaded.getValue()); Diameter.setReadOnly(true); } else if ( type == "UNC" ) { ThreadSize.setEnums(ThreadSize_UNC_Enums); ThreadClass.setEnums(ThreadClass_UNC_Enums); HoleCutType.setEnums(HoleCutType_UNC_Enums); Threaded.setReadOnly(false); ThreadSize.setReadOnly(false); // thread class and direction are only sensible if threaded // fit only sensible if not threaded ThreadFit.setReadOnly(Threaded.getValue()); ThreadClass.setReadOnly(!Threaded.getValue()); Diameter.setReadOnly(true); } else if ( type == "UNF" ) { ThreadSize.setEnums(ThreadSize_UNF_Enums); ThreadClass.setEnums(ThreadClass_UNF_Enums); HoleCutType.setEnums(HoleCutType_UNF_Enums); Threaded.setReadOnly(false); ThreadSize.setReadOnly(false); // thread class and direction are only sensible if threaded // fit only sensible if not threaded ThreadFit.setReadOnly(Threaded.getValue()); ThreadClass.setReadOnly(!Threaded.getValue()); Diameter.setReadOnly(true); } else if ( type == "UNEF" ) { ThreadSize.setEnums(ThreadSize_UNEF_Enums); ThreadClass.setEnums(ThreadClass_UNEF_Enums); HoleCutType.setEnums(HoleCutType_UNEF_Enums); Threaded.setReadOnly(false); ThreadSize.setReadOnly(false); // thread class and direction are only sensible if threaded // fit only sensible if not threaded ThreadFit.setReadOnly(Threaded.getValue()); ThreadClass.setReadOnly(!Threaded.getValue());; Diameter.setReadOnly(true); } if (holeCutType == "None") { HoleCutDiameter.setReadOnly(true); HoleCutDepth.setReadOnly(true); HoleCutCountersinkAngle.setReadOnly(true); } else if (holeCutType == "Counterbore") { HoleCutDiameter.setReadOnly(false); HoleCutDepth.setReadOnly(false); HoleCutCountersinkAngle.setReadOnly(true); } else if (holeCutType == "Countersink") { HoleCutDiameter.setReadOnly(false); HoleCutDepth.setReadOnly(true); HoleCutCountersinkAngle.setReadOnly(false); } else { HoleCutDiameter.setReadOnly(false); HoleCutDepth.setReadOnly(false); HoleCutCountersinkAngle.setReadOnly(true); } if (type == "ISOMetricProfile" || type == "ISOMetricFineProfile") HoleCutCountersinkAngle.setValue(90.0); else if (type == "UNC" || type == "UNF" || type == "UNEF") HoleCutCountersinkAngle.setValue(82.0); // Signal changes to these ProfileBased::onChanged(&ThreadSize); ProfileBased::onChanged(&ThreadClass); ProfileBased::onChanged(&HoleCutType); ProfileBased::onChanged(&Threaded); bool v = (type != "None") || !Threaded.getValue() || !ModelActualThread.getValue(); ThreadPitch.setReadOnly(v); ThreadAngle.setReadOnly(v); ThreadCutOffInner.setReadOnly(v); ThreadCutOffOuter.setReadOnly(v); // Diameter parameter depends on this if (type != "None" ) updateDiameterParam(); } else if (prop == &Threaded) { std::string type(ThreadType.getValueAsString()); // thread class and direction are only sensible if threaded // fit only sensible if not threaded if (Threaded.getValue()) { ThreadClass.setReadOnly(false); ThreadDirection.setReadOnly(false); ThreadFit.setReadOnly(true); ModelActualThread.setReadOnly(true); // For now set this one to read only } else { ThreadClass.setReadOnly(true); ThreadDirection.setReadOnly(true); if (type == "None") ThreadFit.setReadOnly(true); else ThreadFit.setReadOnly(false); ModelActualThread.setReadOnly(true); } // Diameter parameter depends on this updateDiameterParam(); } else if (prop == &ModelActualThread) { bool v =(!ModelActualThread.getValue()) || (Threaded.isReadOnly()) || (std::string(ThreadType.getValueAsString()) != "None"); ThreadPitch.setReadOnly(v); ThreadAngle.setReadOnly(v); ThreadCutOffInner.setReadOnly(v); ThreadCutOffOuter.setReadOnly(v); } else if (prop == &DrillPoint) { if (DrillPoint.getValue() == 1) DrillPointAngle.setReadOnly(false); else DrillPointAngle.setReadOnly(true); } else if (prop == &Tapered) { if (Tapered.getValue()) TaperedAngle.setReadOnly(false); else TaperedAngle.setReadOnly(true); } else if (prop == &ThreadSize) { updateDiameterParam(); updateHoleCutParams(); } else if (prop == &ThreadFit) { updateDiameterParam(); } else if (prop == &HoleCutType) { std::string holeCutType; if (HoleCutType.isValid()) holeCutType = HoleCutType.getValueAsString(); if (holeCutType == "None") { HoleCutDiameter.setReadOnly(true); HoleCutDepth.setReadOnly(true); HoleCutCountersinkAngle.setReadOnly(true); } else if (holeCutType == "Counterbore") { HoleCutDiameter.setReadOnly(false); HoleCutDepth.setReadOnly(false); HoleCutCountersinkAngle.setReadOnly(true); } else if (holeCutType == "Countersink") { HoleCutDiameter.setReadOnly(false); HoleCutDepth.setReadOnly(true); HoleCutCountersinkAngle.setReadOnly(false); } else { // screw definition HoleCutDiameter.setReadOnly(false); HoleCutDepth.setReadOnly(false); HoleCutCountersinkAngle.setReadOnly(true); } ProfileBased::onChanged(&HoleCutDiameter); ProfileBased::onChanged(&HoleCutDepth); ProfileBased::onChanged(&HoleCutCountersinkAngle); updateHoleCutParams(); } else if (prop == &DepthType) { Depth.setReadOnly((std::string(DepthType.getValueAsString()) != "Dimension")); } ProfileBased::onChanged(prop); } /** * Compute 2D intersection between the lines (pa1, pa2) and (pb1, pb2). * The lines are assumed to be crossing, and it is an error * to specify parallel lines. * * Only the x and y coordinates are used to compute the 2D intersection. * */ static void computeIntersection(gp_Pnt pa1, gp_Pnt pa2, gp_Pnt pb1, gp_Pnt pb2, double & x, double & y) { double vx1 = pa1.X() - pa2.X(); double vy1 = pa1.Y() - pa2.Y(); double vx2 = pb1.X() - pb2.X(); double vy2 = pb1.Y() - pb2.Y(); double x1 = pa1.X(); double y1 = pa1.Y(); double x2 = pb1.X(); double y2 = pb1.Y(); /* Solve the system x1 + t1 * vx1 = x2 + t2 * vx2 y1 + t1 * vy1 = y2 + t2 * vy2 => [ vx1 -vx2 ] [ t1 ] = [ x2 - x1 ] [ vy1 -vy2 ] [ t2 ] = [ y2 - y1 ] => [ t1 ] = f * [ -vy2 vx2 ] [ x2 - x1 ] [ t2 ] = [ -vy1 vx1 ] [ y2 - y1 ] */ assert( ( ( vx1 * - vy2 ) - ( -vx2 * vy1 ) ) != 0 ); double f = 1 / ( ( vx1 * - vy2 ) - ( -vx2 * vy1 ) ); double t1 = -vy2 * f * ( x2 - x1 ) + vx2 * f * ( y2 - y1 ); #ifdef _DEBUG double t2 = -vy1 * f * ( x2 - x1 ) + vx1 * f * ( y2 - y1 ); assert( ( x1 + t1 * vx1 ) - ( x2 + t2 * vx2 ) < 1e-6 ); assert( ( y1 + t1 * vy1 ) - ( y2 + t2 * vy2 ) < 1e-6 ); #endif x = x1 + t1 * vx1; y = y1 + t1 * vy1; } short Hole::mustExecute() const { if ( ThreadType.isTouched() || Threaded.isTouched() || ModelActualThread.isTouched() || ThreadPitch.isTouched() || ThreadAngle.isTouched() || ThreadCutOffInner.isTouched() || ThreadCutOffOuter.isTouched() || ThreadSize.isTouched() || ThreadClass.isTouched() || ThreadFit.isTouched() || Diameter.isTouched() || ThreadDirection.isTouched() || HoleCutType.isTouched() || HoleCutDiameter.isTouched() || HoleCutDepth.isTouched() || HoleCutCountersinkAngle.isTouched() || DepthType.isTouched() || Depth.isTouched() || DrillPoint.isTouched() || DrillPointAngle.isTouched() || Tapered.isTouched() || TaperedAngle.isTouched() ) return 1; return ProfileBased::mustExecute(); } void Hole::Restore(Base::XMLReader &reader) { ProfileBased::Restore(reader); updateProps(); } void Hole::updateProps() { onChanged(&Threaded); onChanged(&ModelActualThread); onChanged(&ThreadPitch); onChanged(&ThreadAngle); onChanged(&ThreadCutOffInner); onChanged(&ThreadCutOffOuter); onChanged(&ThreadType); onChanged(&ThreadSize); onChanged(&ThreadClass); onChanged(&ThreadFit); onChanged(&Diameter); onChanged(&ThreadDirection); onChanged(&HoleCutType); onChanged(&HoleCutDiameter); onChanged(&HoleCutDepth); onChanged(&HoleCutCountersinkAngle); onChanged(&DepthType); onChanged(&Depth); onChanged(&DrillPoint); onChanged(&DrillPointAngle); onChanged(&Tapered); onChanged(&TaperedAngle); } static gp_Pnt toPnt(gp_Vec dir) { return gp_Pnt(dir.X(), dir.Y(), dir.Z()); } App::DocumentObjectExecReturn *Hole::execute(void) { Part::Feature* profile = 0; TopoDS_Shape profileshape; try { profile = getVerifiedObject(); profileshape = getVerifiedFace(); } catch (const Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } // Find the base shape TopoDS_Shape base; try { base = getBaseShape(); } catch (const Base::Exception&) { std::string text(QT_TR_NOOP("The requested feature cannot be created. The reason may be that:\n" " - the active Body does not contain a base shape, so there is no\n" " material to be removed;\n" " - the selected sketch does not belong to the active Body.")); return new App::DocumentObjectExecReturn(text); } try { std::string method(DepthType.getValueAsString()); double length = 0.0; this->positionByPrevious(); TopLoc_Location invObjLoc = this->getLocation().Inverted(); base.Move(invObjLoc); if (profileshape.IsNull()) return new App::DocumentObjectExecReturn("Pocket: Creating a face from sketch failed"); profileshape.Move(invObjLoc); /* Build the prototype hole */ // Get vector normal to profile Base::Vector3d SketchVector = getProfileNormal(); if (Reversed.getValue()) SketchVector *= -1.0; // Define this as zDir gp_Vec zDir(SketchVector.x, SketchVector.y, SketchVector.z); zDir.Transform(invObjLoc.Transformation()); // Define xDir gp_Vec xDir; /* Compute xDir normal to zDir */ if (std::abs(zDir.Z() - zDir.X()) > Precision::Confusion()) xDir = gp_Vec(zDir.Z(), 0, -zDir.X()); else if (std::abs(zDir.Z() - zDir.Y()) > Precision::Confusion()) xDir = gp_Vec(zDir.Y(), -zDir.X(), 0); else xDir = gp_Vec(0, -zDir.Z(), zDir.Y()); // Normalize xDir; this is needed as the computation above does not necessarily give a unit-length vector. xDir.Normalize(); if ( method == "Dimension" ) length = Depth.getValue(); else if ( method == "UpToFirst" ) { /* FIXME */ } else if ( method == "ThroughAll" ) { // Use a large (10m), but finite length length = 1e4; } else return new App::DocumentObjectExecReturn("Hole: Unsupported length specification."); if (length <= 0) return new App::DocumentObjectExecReturn("Hole: Invalid hole depth"); BRepBuilderAPI_MakeWire mkWire; const std::string holeCutType = HoleCutType.getValueAsString(); const std::string threadType = ThreadType.getValueAsString(); bool isCountersink = (holeCutType == "Countersink" || holeCutType == "Countersink socket screw (deprecated)" || isDynamicCountersink(threadType, holeCutType)); bool isCounterbore = (holeCutType == "Counterbore" || holeCutType == "Cheesehead (deprecated)" || holeCutType == "Cap screw (deprecated)" || isDynamicCounterbore(threadType, holeCutType)); double hasTaperedAngle = Tapered.getValue() ? Base::toRadians( TaperedAngle.getValue() ) : Base::toRadians(90.0); double radiusBottom = Diameter.getValue() / 2.0 - length * 1.0 / tan( hasTaperedAngle ); double radius = Diameter.getValue() / 2.0; double holeCutRadius = HoleCutDiameter.getValue() / 2.0; gp_Pnt firstPoint(0, 0, 0); gp_Pnt lastPoint(0, 0, 0); double length1 = 0; if ( hasTaperedAngle <= 0 || hasTaperedAngle > Base::toRadians( 180.0 ) ) return new App::DocumentObjectExecReturn("Hole: Invalid taper angle."); if ( isCountersink ) { double x, z; double countersinkAngle = Base::toRadians( HoleCutCountersinkAngle.getValue() / 2.0 ); if ( countersinkAngle <= 0 || countersinkAngle > Base::toRadians( 180.0 ) ) return new App::DocumentObjectExecReturn("Hole: Invalid countersink angle."); if (holeCutRadius < radius) return new App::DocumentObjectExecReturn("Hole: Hole cut diameter too small."); // Top point gp_Pnt newPoint = toPnt(holeCutRadius * xDir); mkWire.Add( BRepBuilderAPI_MakeEdge(lastPoint, newPoint) ); lastPoint = newPoint; computeIntersection(gp_Pnt( holeCutRadius, 0, 0 ), gp_Pnt( holeCutRadius - sin( countersinkAngle ), -cos( countersinkAngle ), 0 ), gp_Pnt( radius, 0, 0 ), gp_Pnt( radiusBottom, -length, 0), x, z ); if (-length > z) return new App::DocumentObjectExecReturn("Hole: Invalid countersink."); length1 = z; newPoint = toPnt(x * xDir + z * zDir); mkWire.Add( BRepBuilderAPI_MakeEdge( lastPoint, newPoint ) ); lastPoint = newPoint; } else if ( isCounterbore ) { double holeCutDepth = HoleCutDepth.getValue(); double x, z; if (holeCutDepth <= 0) return new App::DocumentObjectExecReturn("Hole: Hole cut depth must be greater than zero."); if (holeCutDepth > length) return new App::DocumentObjectExecReturn("Hole: Hole cut depth must be less than hole depth."); if (holeCutRadius < radius) return new App::DocumentObjectExecReturn("Hole: Hole cut diameter too small."); // Top point gp_Pnt newPoint = toPnt(holeCutRadius * xDir); mkWire.Add(BRepBuilderAPI_MakeEdge(lastPoint, newPoint)); lastPoint = newPoint; // Bottom of counterbore newPoint = toPnt(holeCutRadius * xDir -holeCutDepth * zDir); mkWire.Add(BRepBuilderAPI_MakeEdge(lastPoint, newPoint)); lastPoint = newPoint; // Compute intersection of tapered edge and line at bottom of counterbore hole computeIntersection(gp_Pnt( 0, -holeCutDepth, 0 ), gp_Pnt( holeCutRadius, -holeCutDepth, 0 ), gp_Pnt( radius, 0, 0 ), gp_Pnt( radiusBottom, length, 0 ), x, z ); length1 = z; newPoint = toPnt(x * xDir + z * zDir); mkWire.Add(BRepBuilderAPI_MakeEdge(lastPoint, newPoint)); lastPoint = newPoint; } else { gp_Pnt newPoint = toPnt(radius * xDir); mkWire.Add(BRepBuilderAPI_MakeEdge(lastPoint, newPoint)); lastPoint = newPoint; length1 = 0; } std::string drillPoint = DrillPoint.getValueAsString(); if (drillPoint == "Flat") { gp_Pnt newPoint = toPnt(radiusBottom * xDir + -length * zDir); mkWire.Add(BRepBuilderAPI_MakeEdge(lastPoint, newPoint)); lastPoint = newPoint; newPoint = toPnt(-length * zDir); mkWire.Add( BRepBuilderAPI_MakeEdge( lastPoint, newPoint ) ); lastPoint = newPoint; } else if (drillPoint == "Angled") { double drillPointAngle = Base::toRadians( ( 180.0 - DrillPointAngle.getValue() ) / 2.0 ); double x, z; gp_Pnt newPoint; if ( drillPointAngle <= 0 || drillPointAngle > Base::toRadians( 180.0 ) ) return new App::DocumentObjectExecReturn("Hole: Invalid drill point angle."); computeIntersection(gp_Pnt( 0, -length, 0 ), gp_Pnt( cos( drillPointAngle ), -length + sin( drillPointAngle ), 0), gp_Pnt(radius, 0, 0), gp_Pnt(radiusBottom, -length, 0), x, z); if (z > 0 || z >= length1) return new App::DocumentObjectExecReturn("Hole: Invalid drill point."); newPoint = toPnt(x * xDir + z * zDir); mkWire.Add(BRepBuilderAPI_MakeEdge(lastPoint, newPoint)); lastPoint = newPoint; newPoint = toPnt(-length * zDir); mkWire.Add(BRepBuilderAPI_MakeEdge(lastPoint, newPoint)); lastPoint = newPoint; } mkWire.Add( BRepBuilderAPI_MakeEdge(lastPoint, firstPoint) ); TopoDS_Wire wire = mkWire.Wire(); TopoDS_Face face = BRepBuilderAPI_MakeFace(wire); double angle = Base::toRadians(360.0); BRepPrimAPI_MakeRevol RevolMaker(face, gp_Ax1(firstPoint, zDir), angle); TopoDS_Shape protoHole; if (RevolMaker.IsDone()) { protoHole = RevolMaker.Shape(); if (protoHole.IsNull()) return new App::DocumentObjectExecReturn("Hole: Resulting shape is empty"); } else return new App::DocumentObjectExecReturn("Hole: Could not revolve sketch!"); #if 0 // Make thread // This code is work in progress; making threas in OCC is not very easy, so // this work is postponed until later if (ModelActualThread.getValue()) { BRepBuilderAPI_MakeWire mkThreadWire; double z = 0; double d_min = Diameter.getValue() + ThreadCutOffInner.getValue(); double d_maj = Diameter.getValue() - ThreadCutOffInner.getValue(); int i = 0; firstPoint = toPnt(xDir * d_min); mkThreadWire.Add(BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), firstPoint)); while (z < length) { double z1 = i * ThreadPitch.getValue() + ThreadPitch.getValue() * 0.1; double z2 = i * ThreadPitch.getValue() + ThreadPitch.getValue() * 0.45; double z3 = i * ThreadPitch.getValue() + ThreadPitch.getValue() * 0.55; double z4 = i * ThreadPitch.getValue() + ThreadPitch.getValue() * 0.9; gp_Pnt p2 = toPnt(xDir * d_min - zDir * z1); gp_Pnt p3 = toPnt(xDir * d_maj - zDir * z2); gp_Pnt p4 = toPnt(xDir * d_maj - zDir * z3); gp_Pnt p5 = toPnt(xDir * d_min - zDir * z4); mkThreadWire.Add(BRepBuilderAPI_MakeEdge(firstPoint, p2)); mkThreadWire.Add(BRepBuilderAPI_MakeEdge(p2, p3)); mkThreadWire.Add(BRepBuilderAPI_MakeEdge(p3, p4)); mkThreadWire.Add(BRepBuilderAPI_MakeEdge(p4, p5)); firstPoint = p5; ++i; z += ThreadPitch.getValue(); } mkThreadWire.Add(BRepBuilderAPI_MakeEdge(firstPoint, toPnt(-z * zDir))); mkThreadWire.Add(BRepBuilderAPI_MakeEdge(toPnt(-z * zDir), gp_Pnt(0, 0, 0))); TopoDS_Wire threadWire = mkThreadWire.Wire(); TopoDS_Face threadFace = BRepBuilderAPI_MakeFace(threadWire); //TopoDS_Wire helix = TopoShape::makeHelix(ThreadPitch.getValue(), ThreadPitch.getValue(), Diameter.getValue()); double angle = Base::toRadians(360.0); BRepPrimAPI_MakeRevol RevolMaker2(threadFace, gp_Ax1(gp_Pnt(0,0,0), zDir), angle); //TopoDS_Shape protoHole; if (RevolMaker2.IsDone()) { protoHole = RevolMaker2.Shape(); if (protoHole.IsNull()) return new App::DocumentObjectExecReturn("Hole: Resulting shape is empty"); } else return new App::DocumentObjectExecReturn("Hole: Could not revolve sketch!"); } #endif BRep_Builder builder; TopoDS_Compound holes; builder.MakeCompound(holes); TopTools_IndexedMapOfShape edgeMap; TopExp::MapShapes(profileshape, TopAbs_EDGE, edgeMap); for ( int i=1 ; i<=edgeMap.Extent() ; i++ ) { Standard_Real c_start; Standard_Real c_end; TopoDS_Edge edge = TopoDS::Edge(edgeMap(i)); Handle(Geom_Curve) c = BRep_Tool::Curve(edge, c_start, c_end); // Circle? if (c->DynamicType() != STANDARD_TYPE(Geom_Circle)) continue; Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast(c); const gp_Pnt& loc = circle->Axis().Location(); gp_Trsf sketchTransformation; gp_Trsf localSketchTransformation; Base::Placement SketchPos = profile->Placement.getValue(); Base::Matrix4D mat = SketchPos.toMatrix(); sketchTransformation.SetValues( mat[0][0], mat[0][1], mat[0][2], mat[0][3], mat[1][0], mat[1][1], mat[1][2], mat[1][3], mat[2][0], mat[2][1], mat[2][2], mat[2][3] #if OCC_VERSION_HEX < 0x060800 , 0.00001, 0.00001 #endif ); //precision was removed in OCCT CR0025194 localSketchTransformation.SetTranslation( gp_Pnt( 0, 0, 0 ), gp_Pnt(loc.X(), loc.Y(), loc.Z()) ); TopoDS_Shape copy = protoHole; BRepBuilderAPI_Transform transformer(copy, localSketchTransformation ); copy = transformer.Shape(); BRepAlgoAPI_Cut mkCut( base, copy ); if (!mkCut.IsDone()) return new App::DocumentObjectExecReturn("Hole: Cut out of base feature failed"); TopoDS_Shape result = mkCut.Shape(); // We have to get the solids (fuse sometimes creates compounds) base = getSolid(result); if (base.IsNull()) return new App::DocumentObjectExecReturn("Hole: Resulting shape is not a solid"); base = refineShapeIfActive(base); builder.Add(holes, transformer.Shape() ); } // Do not apply a placement to the AddSubShape property (#0003547) //holes.Move( this->getLocation().Inverted() ); // set the subtractive shape property for later usage in e.g. pattern this->AddSubShape.setValue( holes ); remapSupportShape(base); int solidCount = countSolids(base); if (solidCount > 1) { return new App::DocumentObjectExecReturn("Hole: Result has multiple solids. This is not supported at this time."); } this->Shape.setValue(base); return App::DocumentObject::StdReturn; } catch (Standard_Failure& e) { if (std::string(e.GetMessageString()) == "TopoDS::Face" && (std::string(DepthType.getValueAsString()) == "UpToFirst" || std::string(DepthType.getValueAsString()) == "UpToFace")) return new App::DocumentObjectExecReturn("Could not create face from sketch.\n" "Intersecting sketch entities or multiple faces in a sketch are not allowed " "for making a pocket up to a face."); else return new App::DocumentObjectExecReturn(e.GetMessageString()); } catch (Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } } void Hole::addCutType(const CutDimensionSet& dimensions) { const CutDimensionSet::ThreadType thread = dimensions.thread_type; const std::string &name = dimensions.name; std::vector *list; switch(thread) { case CutDimensionSet::Metric: HoleCutTypeMap.emplace(CutDimensionKey("ISOMetricProfile", name), dimensions); list = &HoleCutType_ISOmetric_Enums; break; case CutDimensionSet::MetricFine: HoleCutTypeMap.emplace(CutDimensionKey("ISOMetricFineProfile", name), dimensions); list = &HoleCutType_ISOmetricfine_Enums; break; default: return; } // add the collected lists of JSON definitions to the lists // if a name doesn't already exist in the list if (std::all_of(list->begin(), list->end(), [name](const std::string &x){ return x != name; })) list->push_back(name); } bool Hole::isDynamicCounterbore(const std::string &thread, const std::string &holeCutType) { CutDimensionKey key { thread, holeCutType }; return HoleCutTypeMap.count(key) && HoleCutTypeMap.find(key)->second.cut_type == CutDimensionSet::Counterbore; } bool Hole::isDynamicCountersink(const std::string &thread, const std::string &holeCutType) { CutDimensionKey key { thread, holeCutType }; return HoleCutTypeMap.count(key) && HoleCutTypeMap.find(key)->second.cut_type == CutDimensionSet::Countersink; } /* * Counter Dimensions */ const Hole::CounterBoreDimension Hole::CounterBoreDimension::nothing { "None", 0.0, 0.0 }; const Hole::CounterSinkDimension Hole::CounterSinkDimension::nothing { "None", 0.0 }; Hole::CutDimensionKey::CutDimensionKey(const std::string &t, const std::string &c) : thread_type { t }, cut_name { c } { } bool Hole::CutDimensionKey::operator<(const CutDimensionKey &b) const { return thread_type < b.thread_type || (thread_type == b.thread_type && cut_name < b.cut_name); } const Hole::CutDimensionSet& Hole::find_cutDimensionSet(const std::string &t, const std::string &c) { return HoleCutTypeMap.find(CutDimensionKey(t, c))->second; } const Hole::CutDimensionSet& Hole::find_cutDimensionSet(const CutDimensionKey &k) { return HoleCutTypeMap.find(k)->second; } Hole::CutDimensionSet::CutDimensionSet(const std::string &nme, std::vector &&d, CutType cut, ThreadType thread) : bore_data{ std::move(d) }, cut_type{ cut }, thread_type{thread}, name{nme} { } Hole::CutDimensionSet::CutDimensionSet(const std::string &nme, std::vector &&d, CutType cut, ThreadType thread) : sink_data{ std::move(d) }, cut_type{ cut }, thread_type{thread}, name{nme} { } const Hole::CounterBoreDimension &Hole::CutDimensionSet::get_bore(const std::string &t) const { auto i = std::find_if(bore_data.begin(), bore_data.end(), [t](const Hole::CounterBoreDimension &x) { return x.thread == t; } ); if (i == bore_data.end()) return CounterBoreDimension::nothing; else return *i; } const Hole::CounterSinkDimension &Hole::CutDimensionSet::get_sink(const std::string &t) const { auto i = std::find_if(sink_data.begin(), sink_data.end(), [t](const Hole::CounterSinkDimension &x) { return x.thread == t; } ); if (i == sink_data.end()) return CounterSinkDimension::nothing; else return *i; } void from_json(const nlohmann::json &j, Hole::CounterBoreDimension &t) { t.thread = j["thread"].get(); t.diameter = j["diameter"].get(); t.depth = j["depth"].get(); } void from_json(const nlohmann::json &j, Hole::CounterSinkDimension &t) { t.thread = j["thread"].get(); t.diameter = j["diameter"].get(); } void from_json(const nlohmann::json &j, Hole::CutDimensionSet &t) { t.name = j["name"].get(); std::string thread_type_string = j["thread_type"].get(); if (thread_type_string == "metric") t.thread_type = Hole::CutDimensionSet::Metric; else if (thread_type_string == "metricfine") t.thread_type = Hole::CutDimensionSet::MetricFine; else throw Base::IndexError(std::string("Thread type ‘") + thread_type_string + "’ unsupported"); std::string cut_type_string = j["cut_type"].get(); if (cut_type_string == "counterbore") { t.cut_type = Hole::CutDimensionSet::Counterbore; t.bore_data = j["data"].get >(); t.angle = 0.0; } else if (cut_type_string == "countersink") { t.cut_type = Hole::CutDimensionSet::Countersink; t.sink_data = j["data"].get >(); t.angle = j["angle"].get(); } else throw Base::IndexError(std::string("Cut type ‘") + cut_type_string + "’ unsupported"); t.name = j["name"].get(); } void Hole::readCutDefinitions() { const char subpath[] = "Mod/PartDesign/Resources/Hole"; std::vector dirs { ::App::Application::getResourceDir() + subpath, ::App::Application::getUserAppDataDir() + subpath, }; std::clog << "Looking for thread definitions in: "; for (auto &i : dirs) std::clog << i << " "; std::clog << "\n"; for (auto &dir : dirs) { std::vector<::Base::FileInfo> files { ::Base::FileInfo(dir).getDirectoryContent() }; for (const auto &f : files) { if (f.extension() == "json") { try { std::ifstream input(f.filePath()); nlohmann::json j; input >> j; CutDimensionSet screwtype = j.get(); addCutType(screwtype); } catch(std::exception &e) { std::cerr << "Failed reading ‘" << f.filePath() << "’ with: "<< e.what() << "\n"; } } } } } } // namespace PartDesign