@@ -965,101 +965,101 @@ impl<S: Clone + Debug + Send + Sync> Sketch<S> {
965965 /// - `backlash`: backlash allowance
966966 /// - `segments_per_flank`: tessellation resolution per tooth flank
967967 /// - `metadata`: optional metadata
968- pub fn involute_gear (
969- module_ : Real ,
970- teeth : usize ,
971- pressure_angle_deg : Real ,
972- clearance : Real ,
973- backlash : Real ,
974- segments_per_flank : usize ,
975- metadata : Option < S > ,
976- ) -> Sketch < S > {
977- assert ! ( teeth >= 4 , "Need at least 4 teeth" ) ;
978- assert ! ( segments_per_flank >= 2 ) ;
979-
980- let m = module_;
981- let z = teeth as Real ;
982- let pressure_angle = pressure_angle_deg. to_radians ( ) ;
983-
984- // Standard gear dimensions
985- let pitch_radius = 0.5 * m * z;
986- let addendum = m;
987- let dedendum = 1.25 * m + clearance;
988- let outer_radius = pitch_radius + addendum;
989- let base_radius = pitch_radius * pressure_angle. cos ( ) ;
990- let _root_radius = ( pitch_radius - dedendum) . max ( base_radius * 0.9 ) ; // avoid < base
991-
992- let angular_pitch = TAU / z;
993- let tooth_thickness_at_pitch = angular_pitch / 2.0 - backlash / pitch_radius;
994- let half_tooth_angle = tooth_thickness_at_pitch / 2.0 ;
995-
996- // Helper: generate one involute flank from r1 to r2
997- let generate_flank = | r_start : Real , r_end : Real , reverse : bool | -> Vec < ( Real , Real ) > {
998- let mut pts = Vec :: with_capacity ( segments_per_flank + 1 ) ;
999- for i in 0 ..= segments_per_flank {
1000- let t = i as Real / segments_per_flank as Real ;
1001- let r = r_start + t * ( r_end - r_start ) ;
1002- let phi = ( ( r / base_radius ) . powi ( 2 ) - 1.0 ) . max ( 0.0 ) . sqrt ( ) ; // involute angle
1003- let ( x , y ) = ( base_radius * ( phi . cos ( ) + phi * phi . sin ( ) ) ,
1004- base_radius * ( phi . sin ( ) - phi * phi . cos ( ) ) ) ;
1005- pts . push ( ( x , y ) ) ;
1006- }
1007- if reverse {
1008- pts. reverse ( ) ;
1009- }
1010- pts
1011- } ;
1012-
1013- // Build one full tooth (right flank + arc at tip + left flank + root arc)
1014- let mut tooth_profile = Vec :: new ( ) ;
1015-
1016- // Right flank: from base to outer
1017- let right_flank = generate_flank ( base_radius , outer_radius , false ) ;
1018- // Left flank: mirror and reverse
1019- let left_flank : Vec < _ > = right_flank . iter ( )
1020- . map ( | & ( x , y ) | ( x , -y ) )
1021- . rev ( )
1022- . collect ( ) ;
1023-
1024- // Rotate flanks to align with tooth center
1025- let rotate = |x : Real , y : Real , angle : Real | -> ( Real , Real ) {
1026- let c = angle. cos ( ) ;
1027- let s = angle. sin ( ) ;
1028- ( x * c - y * s, x * s + y * c)
1029- } ;
1030-
1031- // Angular offset from tooth center to flank start at base circle
1032- let phi_base = ( ( pitch_radius / base_radius) . powi ( 2 ) - 1.0 ) . sqrt ( ) ;
1033- let inv_phi_base = phi_base - pressure_angle; // involute function value
1034- let offset_angle = inv_phi_base + half_tooth_angle;
1035-
1036- // Apply rotation to flanks
1037- for & ( x, y) in & right_flank {
1038- tooth_profile. push ( rotate ( x, y, -offset_angle) ) ;
1039- }
1040- for & ( x, y) in & left_flank {
1041- tooth_profile. push ( rotate ( x, y, offset_angle) ) ;
1042- }
1043-
1044- // Close the tooth at the root with a small arc (optional but improves validity)
1045- // For simplicity, we'll just connect to root circle with straight lines or small arc.
1046- // But for now, connect last point to first via root radius approximation.
1047- // Better: add root fillet, but we'll skip for brevity.
1048-
1049- // Now replicate around the gear
1050- let mut outline = Vec :: with_capacity ( tooth_profile. len ( ) * teeth + 1 ) ;
1051- for i in 0 ..teeth {
1052- let rot = i as Real * angular_pitch;
1053- let c = rot. cos ( ) ;
1054- let s = rot. sin ( ) ;
1055- for & ( x, y) in & tooth_profile {
1056- outline. push ( [ x * c - y * s, x * s + y * c] ) ;
1057- }
1058- }
1059- outline. push ( outline[ 0 ] ) ; // close
1060-
1061- Sketch :: polygon ( & outline, metadata)
1062- }
968+ pub fn involute_gear (
969+ module_ : Real ,
970+ teeth : usize ,
971+ pressure_angle_deg : Real ,
972+ clearance : Real ,
973+ backlash : Real ,
974+ segments_per_flank : usize ,
975+ metadata : Option < S > ,
976+ ) -> Sketch < S > {
977+ assert ! ( teeth >= 4 , "Need at least 4 teeth" ) ;
978+ assert ! ( segments_per_flank >= 2 ) ;
979+
980+ let m = module_;
981+ let z = teeth as Real ;
982+ let pressure_angle = pressure_angle_deg. to_radians ( ) ;
983+
984+ // Standard gear dimensions
985+ let pitch_radius = 0.5 * m * z;
986+ let addendum = m;
987+ let dedendum = 1.25 * m + clearance;
988+ let outer_radius = pitch_radius + addendum;
989+ let base_radius = pitch_radius * pressure_angle. cos ( ) ;
990+ let _root_radius = ( pitch_radius - dedendum) . max ( base_radius * 0.9 ) ; // avoid < base
991+
992+ let angular_pitch = TAU / z;
993+ let tooth_thickness_at_pitch = angular_pitch / 2.0 - backlash / pitch_radius;
994+ let half_tooth_angle = tooth_thickness_at_pitch / 2.0 ;
995+
996+ // Helper: generate one involute flank from r1 to r2
997+ let generate_flank =
998+ | r_start : Real , r_end : Real , reverse : bool | -> Vec < ( Real , Real ) > {
999+ let mut pts = Vec :: with_capacity ( segments_per_flank + 1 ) ;
1000+ for i in 0 ..=segments_per_flank {
1001+ let t = i as Real / segments_per_flank as Real ;
1002+ let r = r_start + t * ( r_end - r_start ) ;
1003+ let phi = ( ( r / base_radius ) . powi ( 2 ) - 1.0 ) . max ( 0.0 ) . sqrt ( ) ; // involute angle
1004+ let ( x , y ) = (
1005+ base_radius * ( phi . cos ( ) + phi * phi . sin ( ) ) ,
1006+ base_radius * ( phi . sin ( ) - phi * phi . cos ( ) ) ,
1007+ ) ;
1008+ pts. push ( ( x , y ) ) ;
1009+ }
1010+ if reverse {
1011+ pts . reverse ( ) ;
1012+ }
1013+ pts
1014+ } ;
1015+
1016+ // Build one full tooth (right flank + arc at tip + left flank + root arc)
1017+ let mut tooth_profile = Vec :: new ( ) ;
1018+
1019+ // Right flank: from base to outer
1020+ let right_flank = generate_flank ( base_radius , outer_radius , false ) ;
1021+ // Left flank: mirror and reverse
1022+ let left_flank : Vec < _ > = right_flank . iter ( ) . map ( | & ( x , y ) | ( x , -y ) ) . rev ( ) . collect ( ) ;
1023+
1024+ // Rotate flanks to align with tooth center
1025+ let rotate = |x : Real , y : Real , angle : Real | -> ( Real , Real ) {
1026+ let c = angle. cos ( ) ;
1027+ let s = angle. sin ( ) ;
1028+ ( x * c - y * s, x * s + y * c)
1029+ } ;
1030+
1031+ // Angular offset from tooth center to flank start at base circle
1032+ let phi_base = ( ( pitch_radius / base_radius) . powi ( 2 ) - 1.0 ) . sqrt ( ) ;
1033+ let inv_phi_base = phi_base - pressure_angle; // involute function value
1034+ let offset_angle = inv_phi_base + half_tooth_angle;
1035+
1036+ // Apply rotation to flanks
1037+ for & ( x, y) in & right_flank {
1038+ tooth_profile. push ( rotate ( x, y, -offset_angle) ) ;
1039+ }
1040+ for & ( x, y) in & left_flank {
1041+ tooth_profile. push ( rotate ( x, y, offset_angle) ) ;
1042+ }
1043+
1044+ // Close the tooth at the root with a small arc (optional but improves validity)
1045+ // For simplicity, we'll just connect to root circle with straight lines or small arc.
1046+ // But for now, connect last point to first via root radius approximation.
1047+ // Better: add root fillet, but we'll skip for brevity.
1048+
1049+ // Now replicate around the gear
1050+ let mut outline = Vec :: with_capacity ( tooth_profile. len ( ) * teeth + 1 ) ;
1051+ for i in 0 ..teeth {
1052+ let rot = i as Real * angular_pitch;
1053+ let c = rot. cos ( ) ;
1054+ let s = rot. sin ( ) ;
1055+ for & ( x, y) in & tooth_profile {
1056+ outline. push ( [ x * c - y * s, x * s + y * c] ) ;
1057+ }
1058+ }
1059+ outline. push ( outline[ 0 ] ) ; // close
1060+
1061+ Sketch :: polygon ( & outline, metadata)
1062+ }
10631063
10641064 /// Generate an (epicyclic) cycloidal gear outline
10651065 ///
0 commit comments