00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 namespace ::math::geometry {
00013 }
00014
00015 package require math
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073 ret ::math::geometry::calculateDistanceToLine (type P , type line) {
00074 # solution based on FAQ 1.02 on comp.graphics.algorithms
00075 # L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 )
00076 # (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
00077 # s = -----------------------------
00078 # L^2
00079 # dist = |s|*L
00080 #
00081 # =>
00082 #
00083 # | (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) |
00084 # dist = ---------------------------------
00085 # L
00086 set Ax [lindex $line 0]
00087 set Ay [lindex $line 1]
00088 set Bx [lindex $line 2]
00089 set By [lindex $line 3]
00090 set Cx [lindex $P 0]
00091 set Cy [lindex $P 1]
00092 if {$Ax==$Bx && $Ay==$By} {
00093 return [lengthOfPolyline [concat $P [lrange $line 0 1]]]
00094 } else {
00095 set L [expr {sqrt(pow($Bx-$Ax,2) + pow($By-$Ay,2))}]
00096 return [expr {abs(($Ay-$Cy)*($Bx-$Ax)-($Ax-$Cx)*($By-$Ay)) / $L}]
00097 }
00098 }
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118 ret ::math::geometry::findClosestPointOnLine (type P , type line) {
00119 return [lindex [findClosestPointOnLineImpl $P $line] 0]
00120 }
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141 ret ::math::geometry::findClosestPointOnLineImpl (type P , type line) {
00142 # solution based on FAQ 1.02 on comp.graphics.algorithms
00143 # L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 )
00144 # (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay)
00145 # r = -------------------------------
00146 # L^2
00147 # Px = Ax + r(Bx-Ax)
00148 # Py = Ay + r(By-Ay)
00149 set Ax [lindex $line 0]
00150 set Ay [lindex $line 1]
00151 set Bx [lindex $line 2]
00152 set By [lindex $line 3]
00153 set Cx [lindex $P 0]
00154 set Cy [lindex $P 1]
00155 if {$Ax==$Bx && $Ay==$By} {
00156 return [list [list $Ax $Ay] 0]
00157 } else {
00158 set L [expr {sqrt(pow($Bx-$Ax,2) + pow($By-$Ay,2))}]
00159 set r [expr {(($Cx-$Ax)*($Bx-$Ax) + ($Cy-$Ay)*($By-$Ay))/pow($L,2)}]
00160 set Px [expr {$Ax + $r*($Bx-$Ax)}]
00161 set Py [expr {$Ay + $r*($By-$Ay)}]
00162 return [list [list $Px $Py] $r]
00163 }
00164 }
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184 ret ::math::geometry::calculateDistanceToLineSegment (type P , type linesegment) {
00185 set result [calculateDistanceToLineSegmentImpl $P $linesegment]
00186 set distToLine [lindex $result 0]
00187 set r [lindex $result 1]
00188 if {$r<0} {
00189 return [lengthOfPolyline [concat $P [lrange $linesegment 0 1]]]
00190 } elseif {$r>1} {
00191 return [lengthOfPolyline [concat $P [lrange $linesegment 2 3]]]
00192 } else {
00193 return $distToLine
00194 }
00195 }
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215 ret ::math::geometry::calculateDistanceToLineSegmentImpl (type P , type linesegment) {
00216 # solution based on FAQ 1.02 on comp.graphics.algorithms
00217 # L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 )
00218 # (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
00219 # s = -----------------------------
00220 # L^2
00221 # (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay)
00222 # r = -------------------------------
00223 # L^2
00224 # dist = |s|*L
00225 #
00226 # =>
00227 #
00228 # | (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) |
00229 # dist = ---------------------------------
00230 # L
00231 set Ax [lindex $linesegment 0]
00232 set Ay [lindex $linesegment 1]
00233 set Bx [lindex $linesegment 2]
00234 set By [lindex $linesegment 3]
00235 set Cx [lindex $P 0]
00236 set Cy [lindex $P 1]
00237 if {$Ax==$Bx && $Ay==$By} {
00238 return [list [lengthOfPolyline [concat $P [lrange $linesegment 0 1]]] 0]
00239 } else {
00240 set L [expr {sqrt(pow($Bx-$Ax,2) + pow($By-$Ay,2))}]
00241 set r [expr {(($Cx-$Ax)*($Bx-$Ax) + ($Cy-$Ay)*($By-$Ay))/pow($L,2)}]
00242 return [list [expr {abs(($Ay-$Cy)*($Bx-$Ax)-($Ax-$Cx)*($By-$Ay)) / $L}] $r]
00243 }
00244 }
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264 ret ::math::geometry::findClosestPointOnLineSegment (type P , type linesegment) {
00265 set result [findClosestPointOnLineImpl $P $linesegment]
00266 set Q [lindex $result 0]
00267 set r [lindex $result 1]
00268 if {$r<0} {
00269 return [lrange $linesegment 0 1]
00270 } elseif {$r>1} {
00271 return [lrange $linesegment 2 3]
00272 } else {
00273 return $Q
00274 }
00275
00276 }
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 ret ::math::geometry::calculateDistanceToPolyline (type P , type polyline) {
00297 set minDist "none"
00298 foreach {Ax Ay} [lrange $polyline 0 end-2] {Bx By} [lrange $polyline 2 end] {
00299 set dist [calculateDistanceToLineSegment $P [list $Ax $Ay $Bx $By]]
00300 if {$minDist=="none" || $dist < $minDist} {
00301 set minDist $dist
00302 }
00303 }
00304 return $minDist
00305 }
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325 ret ::math::geometry::findClosestPointOnPolyline (type P , type polyline) {
00326 set closestPoint "none"
00327 foreach {Ax Ay} [lrange $polyline 0 end-2] {Bx By} [lrange $polyline 2 end] {
00328 set Q [findClosestPointOnLineSegment $P [list $Ax $Ay $Bx $By]]
00329 set dist [lengthOfPolyline [concat $P $Q]]
00330 if {$closestPoint=="none" || $dist<$closestDistance} {
00331 set closestPoint $Q
00332 set closestDistance $dist
00333 }
00334 }
00335 return $closestPoint
00336 }
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358 ret ::math::geometry::lengthOfPolyline (type polyline) {
00359 set length 0
00360 foreach {x1 y1} [lrange $polyline 0 end-2] {x2 y2} [lrange $polyline 2 end] {
00361 set length [expr {$length + sqrt(pow($x1-$x2,2) + pow($y1-$y2,2))}]
00362 #set length [expr {$length + sqrt(($x1-$x2)*($x1-$x2) + ($y1-$y2)*($y1-$y2))}]
00363 }
00364 return $length
00365 }
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390 ret ::math::geometry::movePointInDirection (type P , type direction , type dist) {
00391 set x [lindex $P 0]
00392 set y [lindex $P 1]
00393 set pi [expr {4*atan(1)}]
00394 set xt [expr {$x + $dist*cos(($direction*$pi)/180)}]
00395 set yt [expr {$y + $dist*sin(($direction*$pi)/180)}]
00396 return [list $xt $yt]
00397 }
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415 ret ::math::geometry::angle (type line) {
00416 set x1 [lindex $line 0]
00417 set y1 [lindex $line 1]
00418 set x2 [lindex $line 2]
00419 set y2 [lindex $line 3]
00420 # - handle vertical lines
00421 if {$x1==$x2} {if {$y1<$y2} {return 90} else {return 270}}
00422 # - handle other lines
00423 set a [expr {atan(abs((1.0*$y1-$y2)/(1.0*$x1-$x2)))}] ; # a is between 0 and pi/2
00424 set pi [expr {4*atan(1)}]
00425 if {$y1<=$y2} {
00426 # line is going upwards
00427 if {$x1<$x2} {set b $a} else {set b [expr {$pi-$a}]}
00428 } else {
00429 # line is going downwards
00430 if {$x1<$x2} {set b [expr {2*$pi-$a}]} else {set b [expr {$pi+$a}]}
00431 }
00432 return [expr {$b/$pi*180}] ; # convert b to degrees
00433 }
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464 ret ::math::geometry::lineSegmentsIntersect (type linesegment1 , type linesegment2) {
00465 # Algorithm based on Sedgewick.
00466 set l1x1 [lindex $linesegment1 0]
00467 set l1y1 [lindex $linesegment1 1]
00468 set l1x2 [lindex $linesegment1 2]
00469 set l1y2 [lindex $linesegment1 3]
00470 set l2x1 [lindex $linesegment2 0]
00471 set l2y1 [lindex $linesegment2 1]
00472 set l2x2 [lindex $linesegment2 2]
00473 set l2y2 [lindex $linesegment2 3]
00474 return [expr {([ccw [list $l1x1 $l1y1] [list $l1x2 $l1y2] [list $l2x1 $l2y1]]\
00475 *[ccw [list $l1x1 $l1y1] [list $l1x2 $l1y2] [list $l2x2 $l2y2]] <= 0) \
00476 && ([ccw [list $l2x1 $l2y1] [list $l2x2 $l2y2] [list $l1x1 $l1y1]]\
00477 *[ccw [list $l2x1 $l2y1] [list $l2x2 $l2y2] [list $l1x2 $l1y2]] <= 0)}]
00478 }
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506 ret ::math::geometry::findLineSegmentIntersection (type linesegment1 , type linesegment2) {
00507 if {[lineSegmentsIntersect $linesegment1 $linesegment2]} {
00508 set lineintersect [findLineIntersection $linesegment1 $linesegment2]
00509 switch -- $lineintersect {
00510
00511 "coincident" {
00512 # lines are coincident
00513 set l1x1 [lindex $linesegment1 0]
00514 set l1y1 [lindex $linesegment1 1]
00515 set l1x2 [lindex $linesegment1 2]
00516 set l1y2 [lindex $linesegment1 3]
00517 set l2x1 [lindex $linesegment2 0]
00518 set l2y1 [lindex $linesegment2 1]
00519 set l2x2 [lindex $linesegment2 2]
00520 set l2y2 [lindex $linesegment2 3]
00521 # check if the line SEGMENTS overlap
00522 # (NOT enough to check if the x-intervals overlap (vertical lines!))
00523 set overlapx [intervalsOverlap $l1x1 $l1x2 $l2x1 $l2x2 0]
00524 set overlapy [intervalsOverlap $l1y1 $l1y2 $l2y1 $l2y2 0]
00525 if {$overlapx && $overlapy} {
00526 return "coincident"
00527 } else {
00528 return "none"
00529 }
00530 }
00531
00532 "none" {
00533 # should never happen, because we call "lineSegmentsIntersect" first
00534 puts stderr "::math::geometry::findLineSegmentIntersection: suddenly no intersection?"
00535 return "none"
00536 }
00537
00538 default {
00539 # lineintersect = the intersection point
00540 return $lineintersect
00541 }
00542 }
00543 } else {
00544 return "none"
00545 }
00546 }
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576 ret ::math::geometry::findLineIntersection (type line1 , type line2) {
00577 set l1x1 [lindex $line1 0]
00578 set l1y1 [lindex $line1 1]
00579 set l1x2 [lindex $line1 2]
00580 set l1y2 [lindex $line1 3]
00581 set l2x1 [lindex $line2 0]
00582 set l2y1 [lindex $line2 1]
00583 set l2x2 [lindex $line2 2]
00584 set l2y2 [lindex $line2 3]
00585
00586 # Is one of the lines vertical?
00587 if {$l1x1==$l1x2 || $l2x1==$l2x2} {
00588 # One of the lines is vertical
00589 if {$l1x1==$l1x2 && $l2x1==$l2x2} {
00590 # both lines are vertical
00591 if {$l1x1==$l2x1} {
00592 return "coincident"
00593 } else {
00594 return "none"
00595 }
00596 }
00597
00598 # make sure line1 is a vertical line
00599 if {$l1x1!=$l1x2} {
00600 # interchange line 1 and 2
00601 set l1x1 [lindex $line2 0]
00602 set l1y1 [lindex $line2 1]
00603 set l1x2 [lindex $line2 2]
00604 set l1y2 [lindex $line2 3]
00605 set l2x1 [lindex $line1 0]
00606 set l2y1 [lindex $line1 1]
00607 set l2x2 [lindex $line1 2]
00608 set l2y2 [lindex $line1 3]
00609 }
00610
00611 # get equation of line 2 (y=a*x+b)
00612 set a [expr {1.0*($l2y2-$l2y1)/($l2x2-$l2x1)}]
00613 set b [expr {$l2y1-$a*$l2x1}]
00614
00615 # Calculate intersection
00616 set y [expr {$a*$l1x1+$b}]
00617 return [list $l1x1 $y]
00618 } else {
00619 # None of the lines are vertical
00620 # - get equation of line 1 (y=a1*x+b1)
00621 set a1 [expr {(1.0*$l1y2-$l1y1)/($l1x2-$l1x1)}]
00622 set b1 [expr {$l1y1-$a1*$l1x1}]
00623 # - get equation of line 2 (y=a2*x+b2)
00624 set a2 [expr {(1.0*$l2y2-$l2y1)/($l2x2-$l2x1)}]
00625 set b2 [expr {$l2y1-$a2*$l2x1}]
00626
00627 if {abs($a2-$a1) > 0.0001} {
00628 # the lines are not parallel
00629 set x [expr {($b2-$b1)/($a1-$a2)}]
00630 set y [expr {$a1*$x+$b1}]
00631 return [list $x $y]
00632 } else {
00633 # the lines are parallel
00634 if {abs($b1-$b2) < 0.00001} {
00635 return "coincident"
00636 } else {
00637 return "none"
00638 }
00639 }
00640 }
00641 }
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661 ret ::math::geometry::polylinesIntersect (type polyline1 , type polyline2) {
00662 return [polylinesBoundingIntersect $polyline1 $polyline2 0]
00663 }
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692 ret ::math::geometry::polylinesBoundingIntersect (type polyline1 , type polyline2 , type granularity) {
00693 if {$granularity<=1} {
00694 # Use perfect intersect
00695 # => first pin down where an intersection point may be, and then
00696 # call MultilinesIntersectPerfect on those parts
00697 set granularity 10 ; # optimal search granularity?
00698 set perfectmatch 1
00699 } else {
00700 set perfectmatch 0
00701 }
00702
00703 # split the lines into parts consisting of $granularity points
00704 set polyline1parts {}
00705 for {set i 0} {$i<[llength $polyline1]} {incr i [expr {2*$granularity-2}]} {
00706 lappend polyline1parts [lrange $polyline1 $i [expr {$i+2*$granularity-1}]]
00707 }
00708 set polyline2parts {}
00709 for {set i 0} {$i<[llength $polyline2]} {incr i [expr {2*$granularity-2}]} {
00710 lappend polyline2parts [lrange $polyline2 $i [expr {$i+2*$granularity-1}]]
00711 }
00712
00713 # do any of the parts overlap?
00714 foreach part1 $polyline1parts {
00715 foreach part2 $polyline2parts {
00716 set part1bbox [bbox $part1]
00717 set part2bbox [bbox $part2]
00718 if {[rectanglesOverlap [lrange $part1bbox 0 1] [lrange $part1bbox 2 3] \
00719 [lrange $part2bbox 0 1] [lrange $part2bbox 2 3] 0]} {
00720 # the lines' bounding boxes intersect
00721 if {$perfectmatch} {
00722 foreach {l1x1 l1y1} [lrange $part1 0 end-2] {l1x2 l1y2} [lrange $part1 2 end] {
00723 foreach {l2x1 l2y1} [lrange $part2 0 end-2] {l2x2 l2y2} [lrange $part2 2 end] {
00724 if {[lineSegmentsIntersect [list $l1x1 $l1y1 $l1x2 $l1y2] \
00725 [list $l2x1 $l2y1 $l2x2 $l2y2]]} {
00726 # two line segments overlap
00727 return 1
00728 }
00729 }
00730 }
00731 return 0
00732 } else {
00733 return 1
00734 }
00735 }
00736 }
00737 }
00738 return 0
00739 }
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756 ret ::math::geometry::ccw (type A , type B , type C) {
00757 set Ax [lindex $A 0]
00758 set Ay [lindex $A 1]
00759 set Bx [lindex $B 0]
00760 set By [lindex $B 1]
00761 set Cx [lindex $C 0]
00762 set Cy [lindex $C 1]
00763 set dx1 [expr {$Bx - $Ax}]
00764 set dy1 [expr {$By - $Ay}]
00765 set dx2 [expr {$Cx - $Ax}]
00766 set dy2 [expr {$Cy - $Ay}]
00767 if {$dx1*$dy2 > $dy1*$dx2} {return 1}
00768 if {$dx1*$dy2 < $dy1*$dx2} {return -1}
00769 if {($dx1*$dx2 < 0) || ($dy1*$dy2 < 0)} {return -1}
00770 if {($dx1*$dx1 + $dy1*$dy1) < ($dx2*$dx2+$dy2*$dy2)} {return 1}
00771 return 0
00772 }
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809 ret ::math::geometry::intervalsOverlap (type y1 , type y2 , type y3 , type y4 , type strict) {
00810 if {$y1>$y2} {
00811 set temp $y1
00812 set y1 $y2
00813 set y2 $temp
00814 }
00815 if {$y3>$y4} {
00816 set temp $y3
00817 set y3 $y4
00818 set y4 $temp
00819 }
00820 if {$strict} {
00821 return [expr {$y2>$y3 && $y4>$y1}]
00822 } else {
00823 return [expr {$y2>=$y3 && $y4>=$y1}]
00824 }
00825 }
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847 ret ::math::geometry::rectanglesOverlap (type P1 , type P2 , type Q1 , type Q2 , type strict) {
00848 set b1x1 [lindex $P1 0]
00849 set b1y1 [lindex $P1 1]
00850 set b1x2 [lindex $P2 0]
00851 set b1y2 [lindex $P2 1]
00852 set b2x1 [lindex $Q1 0]
00853 set b2y1 [lindex $Q1 1]
00854 set b2x2 [lindex $Q2 0]
00855 set b2y2 [lindex $Q2 1]
00856 # ensure b1x1<=b1x2 etc.
00857 if {$b1x1 > $b1x2} {
00858 set temp $b1x1
00859 set b1x1 $b1x2
00860 set b1x2 $temp
00861 }
00862 if {$b1y1 > $b1y2} {
00863 set temp $b1y1
00864 set b1y1 $b1y2
00865 set b1y2 $temp
00866 }
00867 if {$b2x1 > $b2x2} {
00868 set temp $b2x1
00869 set b2x1 $b2x2
00870 set b2x2 $temp
00871 }
00872 if {$b2y1 > $b2y2} {
00873 set temp $b2y1
00874 set b2y1 $b2y2
00875 set b2y2 $temp
00876 }
00877 # Check if the boxes intersect
00878 # (From: Cormen, Leiserson, and Rivests' "Algorithms", page 889)
00879 if {$strict} {
00880 return [expr {($b1x2>$b2x1) && ($b2x2>$b1x1) \
00881 && ($b1y2>$b2y1) && ($b2y2>$b1y1)}]
00882 } else {
00883 return [expr {($b1x2>=$b2x1) && ($b2x2>=$b1x1) \
00884 && ($b1y2>=$b2y1) && ($b2y2>=$b1y1)}]
00885 }
00886 }
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905 ret ::math::geometry::bbox (type polyline) {
00906 set minX [lindex $polyline 0]
00907 set maxX $minX
00908 set minY [lindex $polyline 1]
00909 set maxY $minY
00910 foreach {x y} $polyline {
00911 if {$x < $minX} {set minX $x}
00912 if {$x > $maxX} {set maxX $x}
00913 if {$y < $minY} {set minY $y}
00914 if {$y > $maxY} {set maxY $y}
00915 }
00916 return [list $minX $minY $maxX $maxY]
00917 }
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944 ret ::math::geometry::pointInsidePolygon (type P , type polygon) {
00945 # check if P is on one of the polygon's sides (if so, P is not
00946 # inside the polygon)
00947 set closedPolygon [concat $polygon [lrange $polygon 0 1]]
00948 foreach {x1 y1} [lrange $closedPolygon 0 end-2] {x2 y2} [lrange $closedPolygon 2 end] {
00949 if {[calculateDistanceToLineSegment $P [list $x1 $y1 $x2 $y2]]<0.0000001} {
00950 return 0
00951 }
00952 }
00953
00954 # Algorithm
00955 #
00956 # Consider a straight line going from P to a point far away from both
00957 # P and the polygon (in particular outside the polygon).
00958 # - If the line intersects with 0 of the polygon's sides, then
00959 # P must be outside the polygon.
00960 # - If the line intersects with 1 of the polygon's sides, then
00961 # P must be inside the polygon (since the other end of the line
00962 # is outside the polygon).
00963 # - If the line intersects with 2 of the polygon's sides, then
00964 # the line must pass into one polygon area and out of it again,
00965 # and hence P is outside the polygon.
00966 # - In general: if the line intersects with the polygon's sides an odd
00967 # number of times, then P is inside the polygon. Note: we also have
00968 # to check whether the line crosses one of the polygon's
00969 # bend points for the same reason.
00970
00971 # get point far away and define the line
00972 set polygonBbox [bbox $polygon]
00973 set pointFarAway [list [expr {[lindex $polygonBbox 0]-1}] [expr {[lindex $polygonBbox 1]-1}]]
00974 set infinityLine [concat $pointFarAway $P]
00975 # calculate number of intersections
00976 set noOfIntersections 0
00977 # 1. count intersections between the line and the polygon's sides
00978 set closedPolygon [concat $polygon [lrange $polygon 0 1]]
00979 foreach {x1 y1} [lrange $closedPolygon 0 end-2] {x2 y2} [lrange $closedPolygon 2 end] {
00980 if {[lineSegmentsIntersect $infinityLine [list $x1 $y1 $x2 $y2]]} {
00981 incr noOfIntersections
00982 }
00983 }
00984 # 2. count intersections between the line and the polygon's points
00985 foreach {x1 y1} $polygon {
00986 if {[calculateDistanceToLineSegment [list $x1 $y1] $infinityLine]<0.0000001} {
00987 incr noOfIntersections
00988 }
00989 }
00990 return [expr {$noOfIntersections % 2}]
00991 }
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017 ret ::math::geometry::rectangleInsidePolygon (type P1 , type P2 , type polygon) {
01018 # get coordinates of rectangle
01019 set bx1 [lindex $P1 0]
01020 set by1 [lindex $P1 1]
01021 set bx2 [lindex $P2 0]
01022 set by2 [lindex $P2 1]
01023
01024 # if rectangle does not overlap with the bbox of polygon, then the
01025 # rectangle cannot be inside the polygon (this is a quick way to
01026 # get an answer in many cases)
01027 set polygonBbox [bbox $polygon]
01028 set polygonP1x [lindex $polygonBbox 0]
01029 set polygonP1y [lindex $polygonBbox 1]
01030 set polygonP2x [lindex $polygonBbox 2]
01031 set polygonP2y [lindex $polygonBbox 3]
01032 if {![rectanglesOverlap [list $bx1 $by1] [list $bx2 $by2] \
01033 [list $polygonP1x $polygonP1y] [list $polygonP2x $polygonP2y] 0]} {
01034 return 0
01035 }
01036
01037 # 1. if one of the points of the polygon is inside the rectangle,
01038 # then the rectangle cannot be inside the polygon
01039 foreach {x y} $polygon {
01040 if {$bx1<$x && $x<$bx2 && $by1<$y && $y<$by2} {
01041 return 0
01042 }
01043 }
01044
01045 # 2. if one of the line segments of the polygon intersect with the
01046 # rectangle, then the rectangle cannot be inside the polygon
01047 set rectanglePolyline [list $bx1 $by1 $bx2 $by1 $bx2 $by2 $bx1 $by2 $bx1 $by1]
01048 set closedPolygon [concat $polygon [lrange $polygon 0 1]]
01049 if {[polylinesIntersect $closedPolygon $rectanglePolyline]} {
01050 return 0
01051 }
01052
01053 # at this point we know that:
01054 # 1. the polygon has no points inside the rectangle
01055 # 2. the polygon's sides don't intersect with the rectangle
01056 # therefore:
01057 # either the rectangle is (completely) inside the polygon, or
01058 # the rectangle is (completely) outside the polygon
01059
01060 # final test: if one of the points on the rectangle is inside the
01061 # polygon, then the whole rectangle must be inside the rectangle
01062 return [pointInsidePolygon [list $bx1 $by1] $polygon]
01063 }
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080 ret ::math::geometry::areaPolygon (type polygon) {
01081
01082 foreach {a1 a2 b1 b2} $polygon {break}
01083
01084 set area 0.0
01085 foreach {c1 c2} [lrange $polygon 4 end] {
01086 set area [expr {$area + $b1*$c2 - $b2*$c1}]
01087 set b1 $c1
01088 set b2 $c2
01089 }
01090 expr {0.5*abs($area)}
01091 }
01092
01093 package provide math::geometry 1.0.3
01094