proc ConGetArcLengthToPt {C P {ArcError 1e-10}} { # Return the arc length locator for point P on con C to within ArcError. # # First find an arc length value that is guaranteed to be very close # to P. The straight line distances between P and either end of C, # normalized by con length, are the minimum change that must be made # in arc length to move towards P. # set LL [gg::conGetLength $C]; # only accurate to 5 or 6 sig figs. set aMin 0.0; # Start set aMax 1.0; # at ends. set Dmin 1.0 set Dmax 1.0 set count 0 set countMax 1000 # # Theoretically aMax will never drop below aMin, but it does when LL # is too small as it usually is. If LL is too big, then the Di go # to zero. # set Err [expr {$ArcError * $LL}] while {$aMax > $aMin} { set Dmin [PtsDistance $P [gg::conGetPt $C -arc $aMin]] if {$Dmin < $Err} {return $aMin} set Dmax [PtsDistance $P [gg::conGetPt $C -arc $aMax]] if {$Dmax < $Err} {return $aMax} set aMin [expr {$aMin + $Dmin/$LL}] set aMax [expr {$aMax - $Dmax/$LL}] # if {[incr count] > $countMax} { error "Preliminary solution not found after $count iterations."} } # The above algorithm often results in aMax and aMin straddling P, but # not necessarily so when P is close to an end. set a [expr {($aMin + $aMax)/2.}] # # However, a should be close enough to P that the following algorithm # will converge quickly. # while {[incr count] < $countMax} { if {$a < 0.5} {set s 1} else {set s -1} set Q [gg::conGetPt $C -arc $a] set Qs [gg::conGetPt $C -arc [expr {$a + $s*1e-5}]] set QQs [ggu::vec3Sub $Qs $Q] set QP [ggu::vec3Sub $P $Q] if {[ggu::vec3Dot $QQs $QP] < 0} {set s [expr {-$s}]} set aLast $a set a [expr {$a + $s*[ggu::vec3Length $QP]/$LL}] if {abs($a - $aLast) < $ArcError} {return $a} } error "No solution found after $count iterations." } proc PtsDistance {p1 p2} { return [ggu::vec3Length [ggu::vec3Sub $p1 $p2]] }