1if (!$) throw new Error('InteractiveDeliveryMap: jQuery not found'); 2if (!ymaps) throw new Error('InteractiveDeliveryMap: Yandex.Maps not found'); 3 4///////////////////////////////////////////////////////////////////////////////////////////// 5// ÐонÑÑÑÑкÑÐ¾Ñ ÑкземплÑÑа плагина 6///////////////////////////////////////////////////////////////////////////////////////////// 7function InteractiveDeliveryMap(options) { 8 var defaults = { 9 hash: '#map', 10 clear_hash_on_startup: true, 11 pickup_button: true, 12 cargo: [], 13 onPickupsReady: undefined, 14 onCalcDeliveryFinish: undefined, 15 onCalcDeliveryError: undefined, 16 onCalcDeliveryAddressFound: undefined, 17 onCalcPickupFinish: undefined, 18 onCalcPickupError: undefined, 19 onPickupSelect: undefined, 20 onPickupCity: undefined, 21 onPickupCityError: undefined, 22 }; 23 this.options = $.extend({}, defaults, options); 24 this.mapDelivery = undefined; 25 this.address = undefined; 26 this.placemark = undefined; 27 this.delivery_areas = []; 28 this.loadDeliveryJSON(); 29 this.lastAreaSign = undefined; 30 this.cityPolygon = undefined; 31 this.suburb_path = undefined; 32 this.pickup_first_run = true; 33 this.need_set_bounds = true; 34 35 if (this.options.clear_hash_on_startup && location.hash == this.options.hash) 36 history.replaceState(undefined, document.title, ' '); 37 38 // Создаем DOM 39 var template = 40 '<div class="idm-modal-background">' + 41 ' <div class="container idm-modal-container">' + 42 ' <div class="idm-modal idm-spinner">' + 43 ' <div id="pickupMapContainer"></div>' + 44 ' <div id="deliveryMapContainer"></div>' + 45 ' </div>' + 46 ' <div class="idm-modal-close idm-modal-close-desktop">×</div>' + 47 ' </div>' + 48 '</div>' + 49 '<div class="idm-modal-close idm-modal-close-mobile">×</div>'; 50 this.element = $(template).appendTo('body'); 51 52 53 $(window).on('hashchange', $.proxy(this.onHashChange, this)); 54 $('.idm-modal-close').on('click', $.proxy(this.close, this)); 55 $('.idm-modal-background').on('mousedown', $.proxy(this.onBackgroundClick, this)); 56 $(document).on('keyup', $.proxy(this.onKeyPress, this)); 57 58 $(this.element).find('.idm-modal > #pickupMapContainer').yadc({ 59 site_id: 2, 60 height: '100%', 61 //map_Ñenter: [lat, lon], 62 auto_geolocation: true, 63 mark_center: true, 64 smart_zoom: false, 65 manual_pvz_adding: true, 66 geolocation_provider: 'auto', 67 auto_open_custom_pickup: false, 68 map_scale: 18, 69 pickup_button: this.options.pickup_button, 70 onReady: $.proxy(this.onYADCReady, this), 71 onPickupSelect: $.proxy(this.onPickupSelectClick, this), 72 cargo: this.options.cargo, 73 }); 74 75} 76 77///////////////////////////////////////////////////////////////////////////////////////////// 78InteractiveDeliveryMap.prototype.onHashChange = function() { 79 if (location.hash == this.options.hash) { 80 $('.idm-modal-background').addClass('active'); 81 } else if ($('.idm-modal-background').hasClass('active')) { 82 $('.idm-modal-background').removeClass('active'); 83 } 84} 85 86///////////////////////////////////////////////////////////////////////////////////////////// 87InteractiveDeliveryMap.prototype.show = function() { 88 if (location.hash != this.options.hash) { 89 window.history.pushState({}, '', this.options.hash); 90 this.onHashChange(); 91 } 92} 93 94///////////////////////////////////////////////////////////////////////////////////////////// 95InteractiveDeliveryMap.prototype.showDelivery = function(address, needCalc = true) { 96 var instance = this; 97 $(this.element).find('.idm-modal > div').hide(); 98 this.show(); 99 if (needCalc) 100 instance.calcDelivery(address); 101 else 102 instance.displayDeliveryMap(); 103} 104 105///////////////////////////////////////////////////////////////////////////////////////////// 106InteractiveDeliveryMap.prototype.showPickup = function(pvz_id) { 107 var instance = this; 108 $(instance.element).find('.idm-modal > div').hide(0); 109 instance.show(); 110 var el = $(instance.element).find('.idm-modal > #pickupMapContainer'); 111 setTimeout(function() { 112 // ÐÑли пеÑвое оÑкÑÑÑие - добавлÑем ÐÐРв ObjectManager (Ð´Ð¾Ð»Ð³Ð°Ñ Ð¾Ð¿ÐµÑаÑиÑ) пока в окне кÑÑÑиÑÑÑ ÑÐ¿Ð¸Ð½Ð½ÐµÑ 113 if (instance.pickup_first_run) { 114 el.yadc('addPVZToObjectManager'); 115 instance.pickup_first_run = false;
116 } 117 // ÐÑобÑажаем yadc 118 el.show(0, function(){ 119 el.yadc('onMapSizeChange'); 120 if (instance.need_set_bounds) { 121 setTimeout(function(){ 122 el.yadc('smartBounds'); 123 instance.need_set_bounds = false; 124 }, 100); 125 } else 126 el.yadc('updateMap'); 127 if (pvz_id) 128 el.yadc('openPVZBaloon', pvz_id); 129 el.yadc('updateBalloon'); 130 el.yadc('updateList'); 131 }); 132 }, 100); 133} 134 135///////////////////////////////////////////////////////////////////////////////////////////// 136InteractiveDeliveryMap.prototype.displayDeliveryMap = function() { 137 $(this.element).find('.idm-modal > #deliveryMapContainer').show(); 138 if (this.mapDelivery !== undefined) 139 this.mapDelivery.container.fitToViewport(); 140} 141 142///////////////////////////////////////////////////////////////////////////////////////////// 143InteractiveDeliveryMap.prototype.close = function() { 144 if (location.hash == this.options.hash) { 145 window.history.go(-1); 146 } 147} 148 149///////////////////////////////////////////////////////////////////////////////////////////// 150InteractiveDeliveryMap.prototype.onBackgroundClick = function(e) { 151 if ($(e.target).hasClass('idm-modal-background') || $(e.target).hasClass('idm-modal-container') ) { 152 this.close(); 153 e.stopPropagation(); 154 e.preventDefault(); 155 } 156} 157 158///////////////////////////////////////////////////////////////////////////////////////////// 159InteractiveDeliveryMap.prototype.onKeyPress = function(e) { 160 if ((e.key === "Escape") && ($('.idm-modal-background').hasClass('active'))) 161 this.close(); 162} 163 164///////////////////////////////////////////////////////////////////////////////////////////// 165InteractiveDeliveryMap.prototype.loadDeliveryJSON = function() { 166 var instance = this; 167 $.ajax({ 168 url: '/products/delivery.json', 169 dataType: 'json', 170 }).done(function(data) { 171 instance.delivery_areas = data; 172 }).fail(function(jqXHR, textStatus, errorThrown){ 173 console.log(errorThrown); 174 }); 175} 176 177///////////////////////////////////////////////////////////////////////////////////////////// 178InteractiveDeliveryMap.prototype.setDeliveryMapCenter = function(coord) { 179 if (this.mapDelivery === undefined) { 180 var el = $(this.element).find('#deliveryMapContainer')[0]; 181 this.mapDelivery = new ymaps.Map(el, {center: coord, zoom: 17}, {suppressMapOpenBlock: true}); 182 } else 183 this.mapDelivery.setCenter(coord); 184} 185 186///////////////////////////////////////////////////////////////////////////////////////////// 187InteractiveDeliveryMap.prototype.setDeliveryPlacemark = function(coord, loc) { 188 var preset = (loc !== undefined) ? 'islands#blueGovernmentIcon' : 'islands#redStretchyIcon'; 189 if (!this.placemark) { 190 191 this.placemark = new ymaps.Placemark(coord, { 192 hintContent: "ÐеÑеÑаÑиÑе меÑÐºÑ Ð´Ð»Ñ ÑÑоÑÐ½ÐµÐ½Ð¸Ñ Ð°Ð´ÑеÑа доÑÑавки", 193 iconContent: "ÐÐµÑ Ð´Ð¾ÑÑавки", 194 }, { 195 preset: preset, 196 draggable: false 197 }); 198 199 this.mapDelivery.geoObjects.add(this.placemark); 200 201 } else { 202 this.placemark.geometry.setCoordinates(coord); 203 this.placemark.options.set('preset', preset); 204 } 205} 206 207///////////////////////////////////////////////////////////////////////////////////////////// 208InteractiveDeliveryMap.prototype.getDeliveryGeoLocator = function(address) { 209 // COMMENT LINE BELOW!!!! 210 //return ymaps.geocode([55.76, 37.64], {}); // ÐоÑква 211 //return ymaps.geocode([55.91842865097746, 37.67366708755087], {}); // ÐÑÑиÑи 212 //return ymaps.geocode([54.0503060853841, 58.02375854489996], {}); //ÐелоÑеÑк 213 //return ymaps.geocode([47.228639, 39.715958], {}); // РоÑÑов-на-ÐÐ¾Ð½Ñ 214 //return ymaps.geocode([54.0503060853841, 58.02375854489996], {}); // ÐелоÑеÑк 215 //return ymaps.geocode([62.912986115138374, 73.85093749999923], {}); // ХанÑÑ-манÑÑ 216 //return ymaps.geocode([63.80332624792426, 85.8040624999952], {}); // ÐÑаÑноÑÑÑкий кÑай 217 //return ymaps.geocode([47.122093, 39.729562], {}); // РоÑÑовÑÐºÐ°Ñ Ð¾Ð±Ð». 218 //return ymaps.geocode([59.797622, 30.639642], {}); // ÐенингÑадÑÐºÐ°Ñ Ð¾Ð±Ð». 219 // 220 if (address === undefined || address === '') 221 return ymaps.geolocation.get({provider: 'auto',}); 222 else 223 return ymaps.geocode(address); 224} 225
226///////////////////////////////////////////////////////////////////////////////////////////// 227InteractiveDeliveryMap.prototype.findDeliveryLocation = function(addr, coord) { 228 var instance = this; 229 for (var i = 0; i < instance.delivery_areas.length; i++) { 230 if (addr.toLowerCase().indexOf(instance.delivery_areas[i].sign.toLowerCase()) >= 0) { 231 return i; 232 } 233 }; 234 return undefined; 235} 236 237///////////////////////////////////////////////////////////////////////////////////////////// 238// РаÑÑÑÑÑ ÑÑоимоÑÑи доÑÑавки до адÑеÑа 239///////////////////////////////////////////////////////////////////////////////////////////// 240InteractiveDeliveryMap.prototype.calcDelivery = function(address, force = false) { 241 242 // ÐÑли адÑÐµÑ Ð½Ðµ поменÑлÑÑ, неÑего делаÑÑ 243 if (this.address !== undefined && address == this.address && !force) { 244 this.displayDeliveryMap(); 245 this.calcDeliveryFinish(); 246 return; 247 } 248 249 var instance = this; 250 $(instance.element).find('.idm-modal > #deliveryMapContainer').hide(); 251 252 // УниÑÑожаем Ñанее пÑолаженнÑй маÑÑÑÑÑ 253 if (instance.suburb_path) { 254 instance.mapDelivery.geoObjects.remove(instance.suburb_path); 255 instance.suburb_path = undefined; 256 } 257 258 instance.getDeliveryGeoLocator(address).then(function(result) { 259 260 if (result.geoObjects.getLength() == 0) 261 instance.calcDeliveryError("Could not geolocate '" + address + "'"); 262 263 var coord = result.geoObjects.get(0).geometry.getCoordinates(); 264 var addr = result.geoObjects.get(0).properties.get('text'); 265 latitude = coord[0]; 266 longitude = coord[1]; 267 //getCity(coord[0], coord[1]); 268 //getDelivery(coord[0], coord[1], addr); 269 270 // ÐповеÑаем о Ð½Ð°Ñ Ð¾Ð¶Ð´ÐµÐ½Ð¸Ð¸ адÑеÑа, еÑли геолоÑиÑовали без адÑеÑа 271 if (address === undefined || address == '') { 272 if (instance.options.onCalcDeliveryAddressFound) { 273 res = instance.options.onCalcDeliveryAddressFound(addr, coord); 274 // Ðам веÑнÑли обÑабоÑаннÑй адÑеÑ? 275 if (res !== undefined && typeof res === 'string') 276 address = res; 277 else 278 address = addr; 279 } else { 280 address = addr; 281 } 282 } 283 284 instance.setDeliveryMapCenter(coord); 285 instance.displayDeliveryMap(); 286 287 // ÐÑем по JSON-ÑÐ°Ð¹Ð»Ñ Ñегион доÑÑавки 288 var loc = instance.findDeliveryLocation(addr, coord); 289 if (loc === undefined) { 290 // ÐÐµÑ Ð´Ð¾ÑÑавки 291 instance.calcDeliveryFinish(-1); 292 } else if (instance.delivery_areas[loc].city) { 293 // ÐоÑÑавка по гоÑÐ¾Ð´Ñ 294 instance.calcDeliveryFinish(0); 295 instance.removeCityPolygon(); 296 } else { 297 // ÐоÑÑавка за гоÑод 298 instance.calcSuburbDistance(coord, instance.delivery_areas[loc].city_center, instance.delivery_areas[loc].city_area, instance.delivery_areas[loc].sign); 299 } 300 301 instance.setDeliveryPlacemark(coord, loc); 302 303 instance.address = address; 304 305 }, function(err) { 306 // ÐÑибка геолокаÑии 307 instance.calcDeliveryError(err.message); 308 }); 309} 310 311///////////////////////////////////////////////////////////////////////////////////////////// 312InteractiveDeliveryMap.prototype.calcSuburbDistance = function(userCoord, cityCenterCoord, cityArea, areaSign, callback) { 313 var instance = this; 314 // Создаем полигон Ñ Ð³ÑаниÑами гоÑода и наноÑим на каÑÑÑ. 315 // Ðез нанеÑÐµÐ½Ð¸Ñ Ð½Ð° каÑÑÑ Ð¼Ð½Ð¾Ð³Ð¸Ðµ ÑÑнкÑии ÑабоÑаÑÑ Ð½Ðµ бÑдÑÑ 316 if (instance.lastAreaSign !== areaSign) { 317 instance.removeCityPolygon(); 318 instance.cityPolygon = new ymaps.Polygon([cityArea]); 319 instance.lastAreaSign = areaSign; 320 //cityPolygon.options.set('visible', false); 321 instance.mapDelivery.geoObjects.add(instance.cityPolygon); 322 } 323 324 // СÑÑоим маÑÑÑÑÑ Ð¾Ñ ÑенÑÑа гоÑода до полÑзоваÑÐµÐ»Ñ 325 ymaps.route([cityCenterCoord, userCoord], {avoidTrafficJams: false, multiRoute: true, results: 10, reverseGeocoding: false}).then(function (multiroute){ 326 327 var dist, min_dist, route; 328 multiroute.getRoutes().each(function(r) { 329 dist = r.getPaths().get(0).properties.get("distance").value; 330 if (min_dist === undefined || min_dist > dist) { 331 min_dist = dist; 332 route = r; 333 } 334 }); 335 336 //var coodinates = route.getPaths().get(0).geometry.getCoordinates(); 337 var coodinates = route.getPaths().get(0).properties.get("coordinates"); 338
339 var points = coodinates.filter(function (el, i, arr) { 340 return robustPointInPolygon(cityArea, el) > 0; 341 }); 342 343 instance.suburb_path = new ymaps.Polyline(points, {}, {strokeColor: '#7ed83f', strokeWidth: 5}); 344 instance.mapDelivery.geoObjects.add(instance.suburb_path); 345 346 dist = instance.suburb_path.geometry.getDistance(); 347 instance.calcDeliveryFinish(1, dist); 348 349 }, function (error){ 350 // ÐÑибка пÑи поÑÑÑоении маÑÑÑÑÑа 351 instance.calcDeliveryError(error.message); 352 }); 353} 354 355///////////////////////////////////////////////////////////////////////////////////////////// 356InteractiveDeliveryMap.prototype.removeCityPolygon = function() { 357 if (this.cityPolygon !== undefined) { 358 this.mapDelivery.geoObjects.remove(this.cityPolygon); 359 this.cityPolygon = undefined; 360 this.lastAreaSign = undefined; 361 } 362} 363 364///////////////////////////////////////////////////////////////////////////////////////////// 365InteractiveDeliveryMap.prototype.calcDeliveryError = function(err) { 366 if (this.options.onCalcDeliveryError) { 367 this.options.onCalcDeliveryError(err); 368 } 369} 370 371///////////////////////////////////////////////////////////////////////////////////////////// 372InteractiveDeliveryMap.prototype.calcDeliveryFinish = function(delivery_location, distance) { 373 if (this.options.onCalcDeliveryFinish) { 374 this.options.onCalcDeliveryFinish(delivery_location, distance); 375 } 376} 377 378///////////////////////////////////////////////////////////////////////////////////////////// 379InteractiveDeliveryMap.prototype.onPickupSelectClick = function(pvz) { 380 if (this.options.onPickupSelect) { 381 this.options.onPickupSelect(pvz); 382 } 383} 384 385///////////////////////////////////////////////////////////////////////////////////////////// 386InteractiveDeliveryMap.prototype.setCargo = function(cargo) { 387 var instance = this; 388 389 instance.options.cargo = cargo; 390 391 el = $(instance.element).find('.idm-modal > #pickupMapContainer'); 392 if ($.data(el[0], 'yadc')) 393 el.yadc('setCargo', cargo); 394} 395 396///////////////////////////////////////////////////////////////////////////////////////////// 397InteractiveDeliveryMap.prototype.calculatePickupPrice = function(pvz_id, cargo) { 398 var instance = this; 399 $.ajax({ 400 url: '//api.yadc-js.ru/calculate.json', 401 type: 'POST', 402 cache: false, 403 data: { 404 'site_id': 2, 405 'pickup_id': pvz_id, 406 'cargo': JSON.stringify(cargo || instance.cargo), 407 }, 408 dataType: 'json', 409 }).done(function(data) { 410 if (instance.options.onCalcPickupFinish) { 411 instance.options.onCalcPickupFinish(data.price); 412 } 413 }).fail(function (jqXHR, textStatus) { 414 console.log('Error calculatePickupPrice() - ' + textStatus); 415 if (instance.options.onCalcPickupError) { 416 instance.options.onCalcPickupError(textStatus); 417 } 418 }); 419} 420 421///////////////////////////////////////////////////////////////////////////////////////////// 422InteractiveDeliveryMap.prototype.onYADCReady = function() { 423 if (this.options.onPickupsReady) { 424 this.options.onPickupsReady(); 425 } 426} 427 428///////////////////////////////////////////////////////////////////////////////////////////// 429InteractiveDeliveryMap.prototype.getPickupCityByCoord = function(coord, cargo) { 430 var instance = this; 431 $.ajax({ 432 url: '//api.yadc-js.ru/city.json', 433 type: 'POST', 434 cache: false, 435 dataType: 'json', 436 data: { 437 'site_id': 2, 438 'latitude': coord[0], 439 'longitude': coord[1], 440 'cargo': JSON.stringify([cargo || {}]), 441 }, 442 }).done(function(data) { 443 if (instance.options.onPickupCity) 444 instance.options.onPickupCity(data); 445 }).fail(function(){ 446 if (instance.options.onPickupCityError) 447 instance.options.onPickupCityError(); 448 }); 449} 450 451///////////////////////////////////////////////////////////////////////////////////////////// 452InteractiveDeliveryMap.prototype.getCityPickups = function(city, cargo) { 453 var instance = this; 454 455 // ÐпÑеделÑем кооÑдинаÑÑ Ð³Ð¾Ñода 456 instance.getDeliveryGeoLocator(city).then(function(result) { 457 458 if (result.geoObjects.getLength() == 0)
459 instance.calcDeliveryError("Could not geolocate '" + address + "'"); 460 461 var coord = result.geoObjects.get(0).geometry.getCoordinates(); 462 var el = $(instance.element).find('.idm-modal > #pickupMapContainer'); 463 el.yadc('setMapCenter', coord, 18); 464 instance.need_set_bounds = true; 465 }, function(err) { 466 // ÐÑибка геолокаÑии 467 }); 468 469 // ÐапÑÑиваем ÑколÑко ÐÐРв гоÑоде и ÑÑоимоÑÑÑ Ð²ÑдаÑи 470 $.ajax({ 471 url: '//api.yadc-js.ru/city.json', 472 type: 'POST', 473 cache: false, 474 dataType: 'json', 475 data: { 476 'site_id': 2, 477 'city': city, 478 'cargo': JSON.stringify([cargo || {}]), 479 }, 480 }).done(function(data) { 481 if (instance.options.onPickupCity) 482 instance.options.onPickupCity(data); 483 }).fail(function(){ 484 if (instance.options.onPickupCityError) 485 instance.options.onPickupCityError(); 486 }); 487} 488 489///////////////////////////////////////////////////////////////////////////////////////////// 490InteractiveDeliveryMap.prototype.getPickupCitiesList = function() { 491 var instance = this; 492 $.ajax({ 493 url: '//api.yadc-js.ru/cities.json', 494 type: 'POST', 495 cache: false, 496 dataType: 'json', 497 data: { 498 site_id: 2, 499 }, 500 }).done(function(data) { 501 if (instance.options.onPickupCitiesList) 502 instance.options.onPickupCitiesList(data); 503 }).fail(function(){ 504 if (instance.options.onPickupCitiesListError) 505 instance.options.onPickupCitiesListtError(); 506 }); 507} 508 509///////////////////////////////////////////////////////////////////////// 510// Taken from https://github.com/mikolalysenko/robust-point-in-polygon 511///////////////////////////////////////////////////////////////////////// 512function robustPointInPolygon(vs, point) { 513 var x = point[0] 514 var y = point[1] 515 var n = vs.length 516 var inside = 1 517 var lim = n 518 for(var i = 0, j = n-1; i<lim; j=i++) { 519 var a = vs[i] 520 var b = vs[j] 521 var yi = a[1] 522 var yj = b[1] 523 if(yj < yi) { 524 if(yj < y && y < yi) { 525 var s = orient(a, b, point) 526 if(s === 0) { 527 return 0 528 } else { 529 inside ^= (0 < s)|0 530 } 531 } else if(y === yi) { 532 var c = vs[(i+1)%n] 533 var yk = c[1] 534 if(yi < yk) { 535 var s = orient(a, b, point) 536 if(s === 0) { 537 return 0 538 } else { 539 inside ^= (0 < s)|0 540 } 541 } 542 } 543 } else if(yi < yj) { 544 if(yi < y && y < yj) { 545 var s = orient(a, b, point) 546 if(s === 0) { 547 return 0 548 } else { 549 inside ^= (s < 0)|0 550 } 551 } else if(y === yi) { 552 var c = vs[(i+1)%n] 553 var yk = c[1] 554 if(yk < yi) { 555 var s = orient(a, b, point) 556 if(s === 0) { 557 return 0 558 } else { 559 inside ^= (s < 0)|0 560 } 561 } 562 } 563 } else if(y === yi) { 564 var x0 = Math.min(a[0], b[0]) 565 var x1 = Math.max(a[0], b[0]) 566 if(i === 0) { 567 while(j>0) { 568 var k = (j+n-1)%n 569 var p = vs[k] 570 if(p[1] !== y) { 571 break 572 } 573 var px = p[0] 574 x0 = Math.min(x0, px) 575 x1 = Math.max(x1, px) 576 j = k 577 } 578 if(j === 0) { 579 if(x0 <= x && x <= x1) { 580 return 0 581 } 582 return 1 583 } 584 lim = j+1 585 } 586 var y0 = vs[(j+n-1)%n][1] 587 while(i+1<lim) { 588 var p = vs[i+1] 589 if(p[1] !== y) { 590 break 591 } 592 var px = p[0] 593 x0 = Math.min(x0, px) 594 x1 = Math.max(x1, px) 595 i += 1 596 } 597 if(x0 <= x && x <= x1) { 598 return 0 599 } 600 var y1 = vs[(i+1)%n][1] 601 if(x < x0 && (y0 < y !== y1 < y)) { 602 inside ^= 1 603 } 604 } 605 } 606 return 2 * inside - 1 607} 608 609///////////////////////////////////////////////////////////////////////// 610var EPSILON = 1.1102230246251565e-16 611var ERRBOUND3 = (3.0 + 16.0 * EPSILON) * EPSILON 612 613function orient(a, b, c) { 614 var l = (a[1] - c[1]) * (b[0] - c[0]) 615 var r = (a[0] - c[0]) * (b[1] - c[1]) 616 var det = l - r 617 var s 618 if(l > 0) { 619 if(r <= 0) { 620 return det 621 } else { 622 s = l + r 623 } 624 } else if(l < 0) { 625 if(r >= 0) { 626 return det 627 } else { 628 s = -(l + r) 629 } 630 } else { 631 return det 632 } 633 var tol = ERRBOUND3 * s 634 if(det >= tol || det <= -tol) { 635 return det 636 } 637 return orientation3Exact(a, b, c) 638} 639
640///////////////////////////////////////////////////////////////////////// 641function orientation3Exact(m0, m1, m2) { 642 var p = linearExpansionSum(linearExpansionSum(twoProduct(m1[1], m2[0]), twoProduct(-m2[1], m1[0])), linearExpansionSum(twoProduct(m0[1], m1[0]), twoProduct(-m1[1], m0[0]))) 643 var n = linearExpansionSum(twoProduct(m0[1], m2[0]), twoProduct(-m2[1], m0[0])) 644 var d = robustSubtract(p, n) 645 return d[d.length - 1] 646 } 647 648//Easy case: Add two scalars 649function scalarScalar(a, b) { 650 var x = a + b 651 var bv = x - a 652 var av = x - bv 653 var br = b - bv 654 var ar = a - av 655 var y = ar + br 656 if(y) { 657 return [y, x] 658 } 659 return [x] 660} 661 662function linearExpansionSum(e, f) { 663 var ne = e.length|0 664 var nf = f.length|0 665 if(ne === 1 && nf === 1) { 666 return scalarScalar(e[0], f[0]) 667 } 668 var n = ne + nf 669 var g = new Array(n) 670 var count = 0 671 var eptr = 0 672 var fptr = 0 673 var abs = Math.abs 674 var ei = e[eptr] 675 var ea = abs(ei) 676 var fi = f[fptr] 677 var fa = abs(fi) 678 var a, b 679 if(ea < fa) { 680 b = ei 681 eptr += 1 682 if(eptr < ne) { 683 ei = e[eptr] 684 ea = abs(ei) 685 } 686 } else { 687 b = fi 688 fptr += 1 689 if(fptr < nf) { 690 fi = f[fptr] 691 fa = abs(fi) 692 } 693 } 694 if((eptr < ne && ea < fa) || (fptr >= nf)) { 695 a = ei 696 eptr += 1 697 if(eptr < ne) { 698 ei = e[eptr] 699 ea = abs(ei) 700 } 701 } else { 702 a = fi 703 fptr += 1 704 if(fptr < nf) { 705 fi = f[fptr] 706 fa = abs(fi) 707 } 708 } 709 var x = a + b 710 var bv = x - a 711 var y = b - bv 712 var q0 = y 713 var q1 = x 714 var _x, _bv, _av, _br, _ar 715 while(eptr < ne && fptr < nf) { 716 if(ea < fa) { 717 a = ei 718 eptr += 1 719 if(eptr < ne) { 720 ei = e[eptr] 721 ea = abs(ei) 722 } 723 } else { 724 a = fi 725 fptr += 1 726 if(fptr < nf) { 727 fi = f[fptr] 728 fa = abs(fi) 729 } 730 } 731 b = q0 732 x = a + b 733 bv = x - a 734 y = b - bv 735 if(y) { 736 g[count++] = y 737 } 738 _x = q1 + x 739 _bv = _x - q1 740 _av = _x - _bv 741 _br = x - _bv 742 _ar = q1 - _av 743 q0 = _ar + _br 744 q1 = _x 745 } 746 while(eptr < ne) { 747 a = ei 748 b = q0 749 x = a + b 750 bv = x - a 751 y = b - bv 752 if(y) { 753 g[count++] = y 754 } 755 _x = q1 + x 756 _bv = _x - q1 757 _av = _x - _bv 758 _br = x - _bv 759 _ar = q1 - _av 760 q0 = _ar + _br 761 q1 = _x 762 eptr += 1 763 if(eptr < ne) { 764 ei = e[eptr] 765 } 766 } 767 while(fptr < nf) { 768 a = fi 769 b = q0 770 x = a + b 771 bv = x - a 772 y = b - bv 773 if(y) { 774 g[count++] = y 775 } 776 _x = q1 + x 777 _bv = _x - q1 778 _av = _x - _bv 779 _br = x - _bv 780 _ar = q1 - _av 781 q0 = _ar + _br 782 q1 = _x 783 fptr += 1 784 if(fptr < nf) { 785 fi = f[fptr] 786 } 787 } 788 if(q0) { 789 g[count++] = q0 790 } 791 if(q1) { 792 g[count++] = q1 793 } 794 if(!count) { 795 g[count++] = 0.0 796 } 797 g.length = count 798 return g 799} 800 801var SPLITTER = +(Math.pow(2, 27) + 1.0) 802 803function twoProduct(a, b, result) { 804 var x = a * b 805 806 var c = SPLITTER * a 807 var abig = c - a 808 var ahi = c - abig 809 var alo = a - ahi 810 811 var d = SPLITTER * b 812 var bbig = d - b 813 var bhi = d - bbig 814 var blo = b - bhi 815 816 var err1 = x - (ahi * bhi) 817 var err2 = err1 - (alo * bhi) 818 var err3 = err2 - (ahi * blo) 819 820 var y = alo * blo - err3 821 822 if(result) { 823 result[0] = y 824 result[1] = x 825 return result 826 } 827 828 return [ y, x ] 829} 830 831//Easy case: Add two scalars 832function scalarScalar(a, b) { 833 var x = a + b 834 var bv = x - a 835 var av = x - bv 836 var br = b - bv 837 var ar = a - av 838 var y = ar + br 839 if(y) { 840 return [y, x] 841 } 842 return [x] 843} 844 845function robustSubtract(e, f) { 846 var ne = e.length|0 847 var nf = f.length|0 848 if(ne === 1 && nf === 1) { 849 return scalarScalar(e[0], -f[0]) 850 } 851 var n = ne + nf 852 var g = new Array(n) 853 var count = 0 854 var eptr = 0 855 var fptr = 0 856 var abs = Math.abs 857 var ei = e[eptr] 858 var ea = abs(ei) 859 var fi = -f[fptr] 860 var fa = abs(fi) 861 var a, b 862 if(ea < fa) { 863 b = ei 864 eptr += 1 865 if(eptr < ne) { 866 ei = e[eptr] 867 ea = abs(ei) 868 } 869 } else { 870 b = fi 871 fptr += 1 872 if(fptr < nf) { 873 fi = -f[fptr] 874 fa = abs(fi) 875 } 876 } 877 if((eptr < ne && ea < fa) || (fptr >= nf)) { 878 a = ei 879 eptr += 1 880 if(eptr < ne) { 881 ei = e[eptr] 882 ea = abs(ei) 883 } 884 } else { 885 a = fi 886 fptr += 1 887 if(fptr < nf) { 888 fi = -f[fptr] 889 fa = abs(fi) 890 } 891 } 892 var x = a + b 893 var bv = x - a 894 var y = b - bv 895 var q0 = y 896 var q1 = x 897 var _x, _bv, _av, _br, _ar 898 while(eptr < ne && fptr < nf) { 899 if(ea < fa) { 900 a = ei 901 eptr += 1 902 if(eptr < ne) { 903 ei = e[eptr] 904 ea = abs(ei) 905 } 906 } else { 907 a = fi 908 fptr += 1 909 if(fptr < nf) { 910 fi = -f[fptr] 911 fa = abs(fi) 912 } 913 } 914 b = q0 915 x = a + b 916 bv = x - a 917 y = b - bv 918 if(y) { 919 g[count++] = y 920 } 921 _x = q1 + x 922 _bv = _x - q1 923 _av = _x - _bv 924 _br = x - _bv 925 _ar = q1 - _av 926 q0 = _ar + _br 927 q1 = _x 928 } 929 while(eptr < ne) { 930 a = ei 931 b = q0 932 x = a + b 933 bv = x - a 934 y = b - bv 935 if(y) { 936 g[count++] = y 937 } 938 _x = q1 + x 939 _bv = _x - q1 940 _av = _x - _bv 941 _br = x - _bv 942 _ar = q1 - _av 943 q0 = _ar + _br 944 q1 = _x 945 eptr += 1 946 if(eptr < ne) { 947 ei = e[eptr] 948 } 949 } 950 while(fptr < nf) { 951 a = fi 952 b = q0 953 x = a + b 954 bv = x - a 955 y = b - bv 956 if(y) { 957 g[count++] = y 958 } 959 _x = q1 + x 960 _bv = _x - q1 961 _av = _x - _bv 962 _br = x - _bv 963 _ar = q1 - _av 964 q0 = _ar + _br 965 q1 = _x 966 fptr += 1 967 if(fptr < nf) { 968 fi = -f[fptr] 969 } 970 } 971 if(q0) { 972 g[count++] = q0 973 } 974 if(q1) { 975 g[count++] = q1 976 } 977 if(!count) { 978 g[count++] = 0.0 979 } 980 g.length = count 981 return g 982}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.