| 
/* 
 | 
* Licensed to the Apache Software Foundation (ASF) under one 
 | 
* or more contributor license agreements.  See the NOTICE file 
 | 
* distributed with this work for additional information 
 | 
* regarding copyright ownership.  The ASF licenses this file 
 | 
* to you under the Apache License, Version 2.0 (the 
 | 
* "License"); you may not use this file except in compliance 
 | 
* with the License.  You may obtain a copy of the License at 
 | 
* 
 | 
*   http://www.apache.org/licenses/LICENSE-2.0 
 | 
* 
 | 
* Unless required by applicable law or agreed to in writing, 
 | 
* software distributed under the License is distributed on an 
 | 
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 | 
* KIND, either express or implied.  See the License for the 
 | 
* specific language governing permissions and limitations 
 | 
* under the License. 
 | 
*/ 
 | 
  
 | 
  
 | 
/** 
 | 
 * AUTO-GENERATED FILE. DO NOT MODIFY. 
 | 
 */ 
 | 
  
 | 
/* 
 | 
* Licensed to the Apache Software Foundation (ASF) under one 
 | 
* or more contributor license agreements.  See the NOTICE file 
 | 
* distributed with this work for additional information 
 | 
* regarding copyright ownership.  The ASF licenses this file 
 | 
* to you under the Apache License, Version 2.0 (the 
 | 
* "License"); you may not use this file except in compliance 
 | 
* with the License.  You may obtain a copy of the License at 
 | 
* 
 | 
*   http://www.apache.org/licenses/LICENSE-2.0 
 | 
* 
 | 
* Unless required by applicable law or agreed to in writing, 
 | 
* software distributed under the License is distributed on an 
 | 
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 | 
* KIND, either express or implied.  See the License for the 
 | 
* specific language governing permissions and limitations 
 | 
* under the License. 
 | 
*/ 
 | 
import { BoundingRect, OrientedBoundingRect } from '../util/graphic.js'; 
 | 
export function prepareLayoutList(input) { 
 | 
  var list = []; 
 | 
  for (var i = 0; i < input.length; i++) { 
 | 
    var rawItem = input[i]; 
 | 
    if (rawItem.defaultAttr.ignore) { 
 | 
      continue; 
 | 
    } 
 | 
    var label = rawItem.label; 
 | 
    var transform = label.getComputedTransform(); 
 | 
    // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el. 
 | 
    var localRect = label.getBoundingRect(); 
 | 
    var isAxisAligned = !transform || transform[1] < 1e-5 && transform[2] < 1e-5; 
 | 
    var minMargin = label.style.margin || 0; 
 | 
    var globalRect = localRect.clone(); 
 | 
    globalRect.applyTransform(transform); 
 | 
    globalRect.x -= minMargin / 2; 
 | 
    globalRect.y -= minMargin / 2; 
 | 
    globalRect.width += minMargin; 
 | 
    globalRect.height += minMargin; 
 | 
    var obb = isAxisAligned ? new OrientedBoundingRect(localRect, transform) : null; 
 | 
    list.push({ 
 | 
      label: label, 
 | 
      labelLine: rawItem.labelLine, 
 | 
      rect: globalRect, 
 | 
      localRect: localRect, 
 | 
      obb: obb, 
 | 
      priority: rawItem.priority, 
 | 
      defaultAttr: rawItem.defaultAttr, 
 | 
      layoutOption: rawItem.computedLayoutOption, 
 | 
      axisAligned: isAxisAligned, 
 | 
      transform: transform 
 | 
    }); 
 | 
  } 
 | 
  return list; 
 | 
} 
 | 
function shiftLayout(list, xyDim, sizeDim, minBound, maxBound, balanceShift) { 
 | 
  var len = list.length; 
 | 
  if (len < 2) { 
 | 
    return; 
 | 
  } 
 | 
  list.sort(function (a, b) { 
 | 
    return a.rect[xyDim] - b.rect[xyDim]; 
 | 
  }); 
 | 
  var lastPos = 0; 
 | 
  var delta; 
 | 
  var adjusted = false; 
 | 
  var shifts = []; 
 | 
  var totalShifts = 0; 
 | 
  for (var i = 0; i < len; i++) { 
 | 
    var item = list[i]; 
 | 
    var rect = item.rect; 
 | 
    delta = rect[xyDim] - lastPos; 
 | 
    if (delta < 0) { 
 | 
      // shiftForward(i, len, -delta); 
 | 
      rect[xyDim] -= delta; 
 | 
      item.label[xyDim] -= delta; 
 | 
      adjusted = true; 
 | 
    } 
 | 
    var shift = Math.max(-delta, 0); 
 | 
    shifts.push(shift); 
 | 
    totalShifts += shift; 
 | 
    lastPos = rect[xyDim] + rect[sizeDim]; 
 | 
  } 
 | 
  if (totalShifts > 0 && balanceShift) { 
 | 
    // Shift back to make the distribution more equally. 
 | 
    shiftList(-totalShifts / len, 0, len); 
 | 
  } 
 | 
  // TODO bleedMargin? 
 | 
  var first = list[0]; 
 | 
  var last = list[len - 1]; 
 | 
  var minGap; 
 | 
  var maxGap; 
 | 
  updateMinMaxGap(); 
 | 
  // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds. 
 | 
  minGap < 0 && squeezeGaps(-minGap, 0.8); 
 | 
  maxGap < 0 && squeezeGaps(maxGap, 0.8); 
 | 
  updateMinMaxGap(); 
 | 
  takeBoundsGap(minGap, maxGap, 1); 
 | 
  takeBoundsGap(maxGap, minGap, -1); 
 | 
  // Handle bailout when there is not enough space. 
 | 
  updateMinMaxGap(); 
 | 
  if (minGap < 0) { 
 | 
    squeezeWhenBailout(-minGap); 
 | 
  } 
 | 
  if (maxGap < 0) { 
 | 
    squeezeWhenBailout(maxGap); 
 | 
  } 
 | 
  function updateMinMaxGap() { 
 | 
    minGap = first.rect[xyDim] - minBound; 
 | 
    maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim]; 
 | 
  } 
 | 
  function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) { 
 | 
    if (gapThisBound < 0) { 
 | 
      // Move from other gap if can. 
 | 
      var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound); 
 | 
      if (moveFromMaxGap > 0) { 
 | 
        shiftList(moveFromMaxGap * moveDir, 0, len); 
 | 
        var remained = moveFromMaxGap + gapThisBound; 
 | 
        if (remained < 0) { 
 | 
          squeezeGaps(-remained * moveDir, 1); 
 | 
        } 
 | 
      } else { 
 | 
        squeezeGaps(-gapThisBound * moveDir, 1); 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
  function shiftList(delta, start, end) { 
 | 
    if (delta !== 0) { 
 | 
      adjusted = true; 
 | 
    } 
 | 
    for (var i = start; i < end; i++) { 
 | 
      var item = list[i]; 
 | 
      var rect = item.rect; 
 | 
      rect[xyDim] += delta; 
 | 
      item.label[xyDim] += delta; 
 | 
    } 
 | 
  } 
 | 
  // Squeeze gaps if the labels exceed margin. 
 | 
  function squeezeGaps(delta, maxSqeezePercent) { 
 | 
    var gaps = []; 
 | 
    var totalGaps = 0; 
 | 
    for (var i = 1; i < len; i++) { 
 | 
      var prevItemRect = list[i - 1].rect; 
 | 
      var gap = Math.max(list[i].rect[xyDim] - prevItemRect[xyDim] - prevItemRect[sizeDim], 0); 
 | 
      gaps.push(gap); 
 | 
      totalGaps += gap; 
 | 
    } 
 | 
    if (!totalGaps) { 
 | 
      return; 
 | 
    } 
 | 
    var squeezePercent = Math.min(Math.abs(delta) / totalGaps, maxSqeezePercent); 
 | 
    if (delta > 0) { 
 | 
      for (var i = 0; i < len - 1; i++) { 
 | 
        // Distribute the shift delta to all gaps. 
 | 
        var movement = gaps[i] * squeezePercent; 
 | 
        // Forward 
 | 
        shiftList(movement, 0, i + 1); 
 | 
      } 
 | 
    } else { 
 | 
      // Backward 
 | 
      for (var i = len - 1; i > 0; i--) { 
 | 
        // Distribute the shift delta to all gaps. 
 | 
        var movement = gaps[i - 1] * squeezePercent; 
 | 
        shiftList(-movement, i, len); 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
  /** 
 | 
   * Squeeze to allow overlap if there is no more space available. 
 | 
   * Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds. 
 | 
   */ 
 | 
  function squeezeWhenBailout(delta) { 
 | 
    var dir = delta < 0 ? -1 : 1; 
 | 
    delta = Math.abs(delta); 
 | 
    var moveForEachLabel = Math.ceil(delta / (len - 1)); 
 | 
    for (var i = 0; i < len - 1; i++) { 
 | 
      if (dir > 0) { 
 | 
        // Forward 
 | 
        shiftList(moveForEachLabel, 0, i + 1); 
 | 
      } else { 
 | 
        // Backward 
 | 
        shiftList(-moveForEachLabel, len - i - 1, len); 
 | 
      } 
 | 
      delta -= moveForEachLabel; 
 | 
      if (delta <= 0) { 
 | 
        return; 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
  return adjusted; 
 | 
} 
 | 
/** 
 | 
 * Adjust labels on x direction to avoid overlap. 
 | 
 */ 
 | 
export function shiftLayoutOnX(list, leftBound, rightBound, 
 | 
// If average the shifts on all labels and add them to 0 
 | 
// TODO: Not sure if should enable it. 
 | 
// Pros: The angle of lines will distribute more equally 
 | 
// Cons: In some layout. It may not what user wanted. like in pie. the label of last sector is usually changed unexpectedly. 
 | 
balanceShift) { 
 | 
  return shiftLayout(list, 'x', 'width', leftBound, rightBound, balanceShift); 
 | 
} 
 | 
/** 
 | 
 * Adjust labels on y direction to avoid overlap. 
 | 
 */ 
 | 
export function shiftLayoutOnY(list, topBound, bottomBound, 
 | 
// If average the shifts on all labels and add them to 0 
 | 
balanceShift) { 
 | 
  return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift); 
 | 
} 
 | 
export function hideOverlap(labelList) { 
 | 
  var displayedLabels = []; 
 | 
  // TODO, render overflow visible first, put in the displayedLabels. 
 | 
  labelList.sort(function (a, b) { 
 | 
    return b.priority - a.priority; 
 | 
  }); 
 | 
  var globalRect = new BoundingRect(0, 0, 0, 0); 
 | 
  function hideEl(el) { 
 | 
    if (!el.ignore) { 
 | 
      // Show on emphasis. 
 | 
      var emphasisState = el.ensureState('emphasis'); 
 | 
      if (emphasisState.ignore == null) { 
 | 
        emphasisState.ignore = false; 
 | 
      } 
 | 
    } 
 | 
    el.ignore = true; 
 | 
  } 
 | 
  for (var i = 0; i < labelList.length; i++) { 
 | 
    var labelItem = labelList[i]; 
 | 
    var isAxisAligned = labelItem.axisAligned; 
 | 
    var localRect = labelItem.localRect; 
 | 
    var transform = labelItem.transform; 
 | 
    var label = labelItem.label; 
 | 
    var labelLine = labelItem.labelLine; 
 | 
    globalRect.copy(labelItem.rect); 
 | 
    // Add a threshold because layout may be aligned precisely. 
 | 
    globalRect.width -= 0.1; 
 | 
    globalRect.height -= 0.1; 
 | 
    globalRect.x += 0.05; 
 | 
    globalRect.y += 0.05; 
 | 
    var obb = labelItem.obb; 
 | 
    var overlapped = false; 
 | 
    for (var j = 0; j < displayedLabels.length; j++) { 
 | 
      var existsTextCfg = displayedLabels[j]; 
 | 
      // Fast rejection. 
 | 
      if (!globalRect.intersect(existsTextCfg.rect)) { 
 | 
        continue; 
 | 
      } 
 | 
      if (isAxisAligned && existsTextCfg.axisAligned) { 
 | 
        // Is overlapped 
 | 
        overlapped = true; 
 | 
        break; 
 | 
      } 
 | 
      if (!existsTextCfg.obb) { 
 | 
        // If self is not axis aligned. But other is. 
 | 
        existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform); 
 | 
      } 
 | 
      if (!obb) { 
 | 
        // If self is axis aligned. But other is not. 
 | 
        obb = new OrientedBoundingRect(localRect, transform); 
 | 
      } 
 | 
      if (obb.intersect(existsTextCfg.obb)) { 
 | 
        overlapped = true; 
 | 
        break; 
 | 
      } 
 | 
    } 
 | 
    // TODO Callback to determine if this overlap should be handled? 
 | 
    if (overlapped) { 
 | 
      hideEl(label); 
 | 
      labelLine && hideEl(labelLine); 
 | 
    } else { 
 | 
      label.attr('ignore', labelItem.defaultAttr.ignore); 
 | 
      labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore); 
 | 
      displayedLabels.push(labelItem); 
 | 
    } 
 | 
  } 
 | 
} 
 |