github.com/pavlo67/common@v0.5.3/common/mathlib/plane/chains_test.go (about) 1 package plane 2 3 import ( 4 "math" 5 "reflect" 6 "testing" 7 8 "github.com/pavlo67/common/common/mathlib" 9 ) 10 11 func TestPolyChainAveragedProbe(t *testing.T) { 12 pCh0 := PolyChain{ 13 {342, 162.5}, {364, 207.5}, 14 } 15 pCh1 := PolyChain{ 16 {335, 151}, {406.5, 302}, {403.7629562043795, 375.0206204379562}, 17 } 18 19 //[{341.3650461698297 162.800656946074} {363.29489523856535 207.82993665827638}] 20 //[{334.4224510771934 151.46712409917882} {337.37437836467245 156.29806754937331}] 21 //[{341.3650461698297 162.800656946074} {406.5 302} {403.7629562043795 375.0206204379562}] 22 23 ok, pCh0Averaged, pCh1Averaged := AveragePolyChains(pCh0, pCh1, 9.870530984139577, false) 24 25 t.Log(ok, "\n", pCh0Averaged, "\n", pCh1Averaged) 26 } 27 28 func TestAveragePolyChains(t *testing.T) { 29 tests := []struct { 30 name string 31 polyChain0 PolyChain 32 polyChain1 PolyChain 33 minDistance float64 34 wantOk bool 35 wantPolyChain0Averaged PolyChain 36 wantPolyChain1Rest []PolyChain 37 connectEnds bool 38 }{ 39 { 40 name: "", 41 polyChain0: PolyChain{{472, 513}, {648, 197}}, 42 polyChain1: PolyChain{{673, 13}, {648, 197}, {472, 513}}, 43 minDistance: 11, 44 wantOk: true, 45 wantPolyChain0Averaged: nil, 46 wantPolyChain1Rest: []PolyChain{{{472, 513}, {648, 197}, {673, 13}}}, 47 connectEnds: true, 48 }, 49 50 { 51 name: "", 52 polyChain0: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 53 polyChain1: PolyChain{{0, 4}, {0, 6}}, 54 minDistance: 0, 55 wantOk: true, 56 wantPolyChain0Averaged: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 57 wantPolyChain1Rest: []PolyChain{{{0, 4}, {0, 6}}}, 58 connectEnds: false, 59 }, 60 { 61 name: "", 62 polyChain0: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 63 polyChain1: PolyChain{{0, 3}, {0, 4}, {0, 6}}, 64 minDistance: 0, 65 wantOk: true, 66 wantPolyChain0Averaged: PolyChain{{0, 0}, {0, 2}, {0, 3}, {0, 4}}, 67 wantPolyChain1Rest: []PolyChain{{{0, 4}, {0, 6}}}, 68 connectEnds: false, 69 }, 70 { 71 name: "", 72 polyChain0: PolyChain{{0, 0}, {0.05, 2}, {0, 4}}, 73 polyChain1: PolyChain{{0.1, 3}, {0.1, 4}, {0.1, 6}}, 74 minDistance: 0.1, 75 wantOk: true, 76 wantPolyChain0Averaged: PolyChain{{0, 0}, {0.05, 2}, {0.07500000000000001, 3}, {0.05, 4}}, 77 wantPolyChain1Rest: []PolyChain{{{0.05, 4}, {0.1, 6}}}, 78 connectEnds: false, 79 }, 80 81 { 82 name: "", 83 polyChain0: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 84 polyChain1: PolyChain{{0, 4}, {0, 6}}, 85 minDistance: 0, 86 wantOk: true, 87 wantPolyChain0Averaged: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 88 wantPolyChain1Rest: []PolyChain{{{0, 4}, {0, 6}}}, 89 }, 90 { 91 name: "", 92 polyChain0: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 93 polyChain1: PolyChain{{0, 3}, {0, 4}, {0, 6}}, 94 minDistance: 0, 95 wantOk: true, 96 wantPolyChain0Averaged: PolyChain{{0, 0}, {0, 2}, {0, 3}, {0, 4}}, 97 wantPolyChain1Rest: []PolyChain{{{0, 4}, {0, 6}}}, 98 }, 99 { 100 name: "", 101 polyChain0: PolyChain{{0, 0}, {0.05, 2}, {0, 4}}, 102 polyChain1: PolyChain{{0.1, 3}, {0.1, 4}, {0.1, 6}}, 103 minDistance: 0.1, 104 wantOk: true, 105 wantPolyChain0Averaged: PolyChain{{0, 0}, {0.05, 2}, {0.07500000000000001, 3}, {0.05, 4}}, 106 wantPolyChain1Rest: []PolyChain{{{0.05, 4}, {0.1, 6}}}, 107 }, 108 } 109 for _, tt := range tests { 110 t.Run(tt.name, func(t *testing.T) { 111 gotOk, gotAveraged, gotRest := AveragePolyChains(tt.polyChain0, tt.polyChain1, tt.minDistance, tt.connectEnds) 112 113 if gotOk != tt.wantOk { 114 t.Errorf("AveragePolyChains() gotOk = %t, wantOk %t", gotOk, tt.wantOk) 115 } 116 if !reflect.DeepEqual(gotAveraged, tt.wantPolyChain0Averaged) { 117 t.Errorf("AveragePolyChains() gotAveraged = %v, wantAveraged %v", gotAveraged, tt.wantPolyChain0Averaged) 118 } 119 if !reflect.DeepEqual(gotRest, tt.wantPolyChain1Rest) { 120 t.Errorf("AveragePolyChains() gotRest = %v, wantRest %v", gotRest, tt.wantPolyChain1Rest) 121 } 122 }) 123 } 124 } 125 126 func ComparePolyChains(pCh0, pCh1 PolyChain) bool { 127 if len(pCh0) != len(pCh1) { 128 return false 129 } 130 for i, p := range pCh0 { 131 if p != pCh1[i] { 132 return false 133 } 134 } 135 136 return true 137 } 138 func TestCutPolyChain(t *testing.T) { 139 type args struct { 140 polyChain PolyChain 141 endI int 142 axis Segment 143 } 144 145 tests := []struct { 146 name string 147 args args 148 want []Point2 149 }{ 150 { 151 name: "", 152 args: args{ 153 polyChain: []Point2{{-1, -1}, {1, 1}, {1, -1}}, 154 endI: 0, 155 axis: Segment{Point2{X: 0, Y: 2}, Point2{X: 1, Y: 2}}, 156 }, 157 want: []Point2{{-1, -1}, {1, 1}, {1, -1}}, 158 }, 159 160 { 161 name: "", 162 args: args{ 163 polyChain: []Point2{{-1, -1}, {1, 1}, {1, -1}}, 164 endI: 0, 165 axis: Segment{Point2{X: 0, Y: 1}, Point2{X: 1, Y: 1}}, 166 }, 167 want: []Point2{{-1, -1}, {1, 1}, {1, -1}}, 168 }, 169 170 { 171 name: "", 172 args: args{ 173 polyChain: []Point2{{-1, -1}, {1, 1}, {1, -1}}, 174 endI: 0, 175 axis: Segment{Point2{X: 0, Y: 0}, Point2{X: 1, Y: 0}}, 176 }, 177 178 want: []Point2{{-1, -1}, {0, 0}, {1, 0}, {1, -1}}, 179 }, 180 181 { 182 name: "", 183 args: args{ 184 polyChain: PolyChain{{-1, -1}, {1, 1}, {1, -1}}, 185 endI: 1, 186 axis: Segment{Point2{X: 0, Y: 0}, Point2{X: 1, Y: 0}}, 187 }, 188 189 want: []Point2{{1, 1}, {1, 0}, {0, 0}}, 190 }, 191 } 192 for _, tt := range tests { 193 t.Run(tt.name, func(t *testing.T) { 194 if got := tt.args.polyChain.Cut(tt.args.endI, tt.args.axis); !ComparePolyChains(got, tt.want) { 195 t.Errorf("CutPolyChain() = %v, wantPolyChain %v", got, tt.want) 196 } 197 }) 198 } 199 } 200 201 func TestApproximatePolyChain(t *testing.T) { 202 tests := []struct { 203 name string 204 pts2 PolyChain 205 maxDeviation float64 206 want PolyChain 207 }{ 208 { 209 name: "0", 210 pts2: nil, 211 maxDeviation: 1, 212 want: nil, 213 }, 214 { 215 name: "1", pts2: PolyChain{{}}, maxDeviation: 1, want: PolyChain{{}}, 216 }, 217 { 218 name: "2", pts2: PolyChain{{}, {1, 1}}, maxDeviation: 1, want: PolyChain{{}, {1, 1}}, 219 }, 220 { 221 name: "3", pts2: PolyChain{{}, {0, 1}, {2, 2}}, maxDeviation: 0.8, 222 want: PolyChain{{}, {2, 2}}, 223 }, 224 { 225 name: "4", pts2: PolyChain{{}, {0, 1}, {2, 2}}, maxDeviation: 0.5, 226 want: PolyChain{{}, {0, 1}, {2, 2}}, 227 }, 228 { 229 name: "5", pts2: PolyChain{{}, {1, 1}, {2, 1}, {3, 1}, {4, 0}}, maxDeviation: 0.9, 230 want: PolyChain{{}, {2, 1}, {4, 0}}, 231 }, 232 { 233 name: "5", pts2: PolyChain{{}, {-1, 0}, {4, 0}}, maxDeviation: 0.9, 234 want: PolyChain{{}, {-1, 0}, {4, 0}}, 235 }, 236 } 237 for _, tt := range tests { 238 t.Run(tt.name, func(t *testing.T) { 239 if got := tt.pts2.Approximate(tt.maxDeviation); !reflect.DeepEqual(got, tt.want) { 240 t.Errorf("ApproximatePolyChain() = %v, wantDistance %v", got, tt.want) 241 } 242 }) 243 } 244 } 245 246 //func TestAveragePolyChains(t *testing.T) { 247 // tests := []struct { 248 // name string 249 // polyChain0 PolyChain 250 // polyChain1 PolyChain 251 // minDistance float64 252 // wantOk bool 253 // wantPolyChain0Averaged PolyChain 254 // wantPolyChain1Rest []PolyChain 255 // }{ 256 // { 257 // name: "", 258 // polyChain0: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 259 // polyChain1: PolyChain{{0, 4}, {0, 6}}, 260 // minDistance: 0, 261 // wantOk: true, 262 // wantPolyChain0Averaged: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 263 // wantPolyChain1Rest: []PolyChain{{{0, 4}, {0, 6}}}, 264 // }, 265 // { 266 // name: "", 267 // polyChain0: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 268 // polyChain1: PolyChain{{0, 3}, {0, 4}, {0, 6}}, 269 // minDistance: 0, 270 // wantOk: true, 271 // wantPolyChain0Averaged: PolyChain{{0, 0}, {0, 2}, {0, 3}, {0, 4}}, 272 // wantPolyChain1Rest: []PolyChain{{{0, 4}, {0, 6}}}, 273 // }, 274 // { 275 // name: "", 276 // polyChain0: PolyChain{{0, 0}, {0.05, 2}, {0, 4}}, 277 // polyChain1: PolyChain{{0.1, 3}, {0.1, 4}, {0.1, 6}}, 278 // minDistance: 0.1, 279 // wantOk: true, 280 // wantPolyChain0Averaged: PolyChain{{0, 0}, {0.05, 2}, {0.07500000000000001, 3}, {0.05, 4}}, 281 // wantPolyChain1Rest: []PolyChain{{{0.05, 4}, {0.1, 6}}}, 282 // }, 283 // } 284 // for _, tt := range tests { 285 // t.Run(tt.name, func(t *testing.T) { 286 // gotOk, gotAveraged, gotRest := AveragePolyChains(tt.polyChain0, tt.polyChain1, tt.minDistance) 287 // 288 // if gotOk != tt.wantOk { 289 // t.Errorf("AveragePolyChains() gotOk = %t, wantOk %t", gotOk, tt.wantOk) 290 // } 291 // if !reflect.DeepEqual(gotAveraged, tt.wantPolyChain0Averaged) { 292 // t.Errorf("AveragePolyChains() gotAveraged = %v, wantAveraged %v", gotAveraged, tt.wantPolyChain0Averaged) 293 // } 294 // if !reflect.DeepEqual(gotRest, tt.wantPolyChain1Rest) { 295 // t.Errorf("AveragePolyChains() gotRest = %v, wantRest %v", gotRest, tt.wantPolyChain1Rest) 296 // } 297 // }) 298 // } 299 //} 300 301 func TestDistanceToPolyChain(t *testing.T) { 302 tests := []struct { 303 name string 304 polyChain PolyChain 305 p Point2 306 wantDistance float64 307 wantProjection ProjectionOnPolyChain 308 }{ 309 { 310 name: "", 311 polyChain: PolyChain{{0.1, 3}, {0.1, 4}, {0.1, 6}}, 312 p: Point2{0, 4}, 313 wantDistance: 0.1, 314 wantProjection: ProjectionOnPolyChain{N: 1, Position: 0, Point2: Point2{0.1, 4}}, 315 }, 316 { 317 name: "", 318 polyChain: PolyChain{{0, 4}, {0, 6}}, 319 p: Point2{0, 0}, 320 wantDistance: 4, 321 wantProjection: ProjectionOnPolyChain{N: 0, Position: 0, Point2: Point2{0, 4}}, 322 }, 323 { 324 name: "", 325 polyChain: PolyChain{{0, 4}, {0, 6}}, 326 p: Point2{0, 2}, 327 wantDistance: 2, 328 wantProjection: ProjectionOnPolyChain{N: 0, Position: 0, Point2: Point2{0, 4}}, 329 }, 330 { 331 name: "", 332 polyChain: PolyChain{{0, 4}, {0, 6}}, 333 p: Point2{0, 4}, 334 wantDistance: 0, 335 wantProjection: ProjectionOnPolyChain{N: 0, Position: 0, Point2: Point2{0, 4}}, 336 }, 337 { 338 name: "", 339 polyChain: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 340 p: Point2{0, 4}, 341 wantDistance: 0, 342 wantProjection: ProjectionOnPolyChain{N: 2, Position: 0, Point2: Point2{0, 4}}, 343 }, 344 { 345 name: "", 346 polyChain: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 347 p: Point2{0, 6}, 348 wantDistance: 2, 349 wantProjection: ProjectionOnPolyChain{N: 2, Position: 0, Point2: Point2{0, 4}}, 350 }, 351 { 352 name: "", 353 polyChain: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 354 p: Point2{1, 3}, 355 wantDistance: 1, 356 wantProjection: ProjectionOnPolyChain{N: 1, Position: 1, Point2: Point2{0, 3}}, 357 }, 358 { 359 name: "", 360 polyChain: PolyChain{{0, 0}, {0, 2}, {0, 4}}, 361 p: Point2{1, 2}, 362 wantDistance: 1, 363 wantProjection: ProjectionOnPolyChain{N: 1, Position: 0, Point2: Point2{0, 2}}, 364 }, 365 { 366 name: "", 367 polyChain: PolyChain{{0, 0}, {0, 2}, {2, 2}}, 368 p: Point2{1, 1}, 369 wantDistance: 1, 370 wantProjection: ProjectionOnPolyChain{N: 0, Position: 1, Point2: Point2{0, 1}}, // , {N: 1, Position: 1, Point2: Point2{1, 2}}}, 371 }, 372 { 373 name: "", 374 polyChain: PolyChain{{0, 0}, {0, 2}, {2, 2}}, 375 p: Point2{1, 1.5}, 376 wantDistance: 0.5, 377 wantProjection: ProjectionOnPolyChain{N: 1, Position: 1, Point2: Point2{1, 2}}, 378 }, 379 { 380 name: "", 381 polyChain: PolyChain{{0, 0}, {0, 2}, {2, 2}}, 382 p: Point2{0.5, 1}, 383 wantDistance: 0.5, 384 wantProjection: ProjectionOnPolyChain{N: 0, Position: 1, Point2: Point2{0, 1}}, 385 }, 386 { 387 name: "", 388 polyChain: PolyChain{{0, 0}, {0, 2}, {2, 2}}, 389 p: Point2{2, 0}, 390 wantDistance: 2, 391 wantProjection: ProjectionOnPolyChain{N: 0, Position: 0, Point2: Point2{0, 0}}, // {N: 2, Position: 0, Point2: Point2{2, 2}}}, 392 }, 393 } 394 for _, tt := range tests { 395 t.Run(tt.name, func(t *testing.T) { 396 gotDistance, gotProjections := tt.p.DistanceToPolyChain(tt.polyChain) 397 398 if math.Abs(gotDistance-tt.wantDistance) > mathlib.Eps { 399 t.Errorf("TestDistanceToPolyChain() gotDistance = %f, wantDistance %f", gotDistance, tt.wantDistance) 400 } 401 if !reflect.DeepEqual(gotProjections, tt.wantProjection) { 402 t.Errorf("TestDistanceToPolyChain() gotProjections = %v, wantProjection %v", gotProjections, tt.wantProjection) 403 } 404 }) 405 } 406 } 407 408 func TestDistanceToLineSegment(t *testing.T) { 409 tests := []struct { 410 name string 411 p Point2 412 ls Segment 413 wantDistance float64 414 wantPosition float64 415 }{ 416 { 417 name: "", ls: Segment{{0, 0}, {4, 0}}, p: Point2{-5, 0}, 418 wantDistance: 5, wantPosition: 0, 419 }, 420 { 421 name: "", ls: Segment{{0, 0}, {4, 0}}, p: Point2{-3, -4}, 422 wantDistance: 5, wantPosition: 0, 423 }, 424 { 425 name: "", ls: Segment{{0, 0}, {4, 0}}, p: Point2{0, 4}, 426 wantDistance: 4, wantPosition: 0, 427 }, 428 { 429 name: "", ls: Segment{{0, 0}, {4, 0}}, p: Point2{1, 4}, 430 wantDistance: 4, wantPosition: 1, 431 }, 432 { 433 name: "", ls: Segment{{0, 0}, {4, 0}}, p: Point2{2, 4}, 434 wantDistance: 4, wantPosition: 2, 435 }, 436 { 437 name: "", ls: Segment{{0, 0}, {4, 0}}, p: Point2{3, 4}, 438 wantDistance: 4, wantPosition: 3, 439 }, 440 { 441 name: "", ls: Segment{{0, 0}, {4, 0}}, p: Point2{4, 0}, 442 wantDistance: 0, wantPosition: 4, 443 }, 444 { 445 name: "", ls: Segment{{0, 0}, {4, 0}}, p: Point2{2, 0}, 446 wantDistance: 0, wantPosition: 2, 447 }, 448 { 449 name: "", 450 p: Point2{0, 4}, ls: Segment{{0, 0}, {0, 2}}, 451 wantDistance: 2, wantPosition: 2, 452 }, 453 { 454 name: "", 455 p: Point2{0, 6}, ls: Segment{{0, 0}, {0, 2}}, 456 wantDistance: 4, wantPosition: 2, 457 }, 458 { 459 name: "", 460 p: Point2{0, 4}, ls: Segment{{0, 2}, {0, 4}}, 461 wantDistance: 0, wantPosition: 2, 462 }, 463 { 464 name: "", 465 p: Point2{0, 6}, ls: Segment{{0, 2}, {0, 4}}, 466 wantDistance: 2, wantPosition: 2, 467 }, 468 { 469 name: "", 470 p: Point2{1, 3}, ls: Segment{{0, 2}, {0, 4}}, 471 wantDistance: 1, wantPosition: 1, 472 }, 473 { 474 name: "", 475 p: Point2{1, 3.5}, ls: Segment{{0, 2}, {0, 4}}, 476 wantDistance: 1, wantPosition: 1.5, 477 }, 478 { 479 name: "", 480 p: Point2{1, 2.5}, ls: Segment{{0, 2}, {0, 4}}, 481 wantDistance: 1, wantPosition: 0.5, 482 }, 483 { 484 name: "", 485 p: Point2{0, 3}, ls: Segment{{0, 2}, {0, 4}}, 486 wantDistance: 0, wantPosition: 1, 487 }, 488 { 489 name: "", 490 p: Point2{0, 2}, ls: Segment{{0, 2}, {0, 4}}, 491 wantDistance: 0, wantPosition: 0, 492 }, 493 } 494 for _, tt := range tests { 495 t.Run(tt.name, func(t *testing.T) { 496 gotDistance, gotPosition := tt.p.DistanceToSegment(tt.ls) 497 if math.Abs(gotDistance-tt.wantDistance) > mathlib.Eps { 498 t.Errorf("DistanceToSegment() gotDistance = %v, wantDistance %v", gotDistance, tt.wantDistance) 499 } 500 if math.Abs(gotPosition-tt.wantPosition) > mathlib.Eps { 501 t.Errorf("DistanceToSegment() gotPosition = %v, wantDistance %v", gotPosition, tt.wantPosition) 502 } 503 }) 504 } 505 } 506 507 func TestFilterPolyChain(t *testing.T) { 508 tests := []struct { 509 name string 510 pCh PolyChain 511 pChWanted PolyChain 512 }{ 513 { 514 name: "", 515 pCh: PolyChain{{1, 1}, {1, 1}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}}, 516 pChWanted: PolyChain{{1, 1}, {1, 0}}, 517 }, 518 { 519 name: "", 520 pCh: PolyChain{{1, 2}, {1, 1}, {1, 0}, {1, 0}, {1, 0}, {3, 1}, {1, 0}, {1, 0}}, 521 pChWanted: PolyChain{{1, 2}, {1, 1}, {1, 0}, {3, 1}, {1, 0}}, 522 }, 523 { 524 name: "", 525 pCh: PolyChain{{1, 2}, {1, 1}, {1, 0}, {3, 1}, {1, 0}}, 526 pChWanted: PolyChain{{1, 2}, {1, 1}, {1, 0}, {3, 1}, {1, 0}}, 527 }, 528 } 529 for _, tt := range tests { 530 t.Run(tt.name, func(t *testing.T) { 531 if got := tt.pCh.Filter(); !reflect.DeepEqual(got, tt.pChWanted) { 532 t.Errorf("FilterPolyChain() = %v, pChWanted %v", got, tt.pChWanted) 533 } 534 }) 535 } 536 }