| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- /*
- * 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.
- */
- // Layout helpers for each component positioning
- import * as zrUtil from 'zrender/lib/core/util.js';
- import BoundingRect from 'zrender/lib/core/BoundingRect.js';
- import { parsePercent } from './number.js';
- import * as formatUtil from './format.js';
- import { error } from './log.js';
- import { BoxCoordinateSystemCoordFrom, getCoordForBoxCoordSys } from '../core/CoordinateSystem.js';
- var each = zrUtil.each;
- /**
- * @public
- */
- export var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height'];
- /**
- * @public
- */
- export var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']];
- function boxLayout(orient, group, gap, maxWidth, maxHeight) {
- var x = 0;
- var y = 0;
- if (maxWidth == null) {
- maxWidth = Infinity;
- }
- if (maxHeight == null) {
- maxHeight = Infinity;
- }
- var currentLineMaxSize = 0;
- group.eachChild(function (child, idx) {
- var rect = child.getBoundingRect();
- var nextChild = group.childAt(idx + 1);
- var nextChildRect = nextChild && nextChild.getBoundingRect();
- var nextX;
- var nextY;
- if (orient === 'horizontal') {
- var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0);
- nextX = x + moveX;
- // Wrap when width exceeds maxWidth or meet a `newline` group
- // FIXME compare before adding gap?
- if (nextX > maxWidth || child.newline) {
- x = 0;
- nextX = moveX;
- y += currentLineMaxSize + gap;
- currentLineMaxSize = rect.height;
- } else {
- // FIXME: consider rect.y is not `0`?
- currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
- }
- } else {
- var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0);
- nextY = y + moveY;
- // Wrap when width exceeds maxHeight or meet a `newline` group
- if (nextY > maxHeight || child.newline) {
- x += currentLineMaxSize + gap;
- y = 0;
- nextY = moveY;
- currentLineMaxSize = rect.width;
- } else {
- currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);
- }
- }
- if (child.newline) {
- return;
- }
- child.x = x;
- child.y = y;
- child.markRedraw();
- orient === 'horizontal' ? x = nextX + gap : y = nextY + gap;
- });
- }
- /**
- * VBox or HBox layouting
- * @param {string} orient
- * @param {module:zrender/graphic/Group} group
- * @param {number} gap
- * @param {number} [width=Infinity]
- * @param {number} [height=Infinity]
- */
- export var box = boxLayout;
- /**
- * VBox layouting
- * @param {module:zrender/graphic/Group} group
- * @param {number} gap
- * @param {number} [width=Infinity]
- * @param {number} [height=Infinity]
- */
- export var vbox = zrUtil.curry(boxLayout, 'vertical');
- /**
- * HBox layouting
- * @param {module:zrender/graphic/Group} group
- * @param {number} gap
- * @param {number} [width=Infinity]
- * @param {number} [height=Infinity]
- */
- export var hbox = zrUtil.curry(boxLayout, 'horizontal');
- export function getBoxLayoutParams(boxLayoutModel, ignoreParent) {
- return {
- left: boxLayoutModel.getShallow('left', ignoreParent),
- top: boxLayoutModel.getShallow('top', ignoreParent),
- right: boxLayoutModel.getShallow('right', ignoreParent),
- bottom: boxLayoutModel.getShallow('bottom', ignoreParent),
- width: boxLayoutModel.getShallow('width', ignoreParent),
- height: boxLayoutModel.getShallow('height', ignoreParent)
- };
- }
- function getViewRectAndCenterForCircleLayout(seriesModel, api) {
- var layoutRef = createBoxLayoutReference(seriesModel, api, {
- enableLayoutOnlyByCenter: true
- });
- var boxLayoutParams = seriesModel.getBoxLayoutParams();
- var viewRect;
- var center;
- if (layoutRef.type === BoxLayoutReferenceType.point) {
- center = layoutRef.refPoint;
- // `viewRect` is required in `pie/labelLayout.ts`.
- viewRect = getLayoutRect(boxLayoutParams, {
- width: api.getWidth(),
- height: api.getHeight()
- });
- } else {
- // layoutRef.type === layout.BoxLayoutReferenceType.rect
- var centerOption = seriesModel.get('center');
- var centerOptionArr = zrUtil.isArray(centerOption) ? centerOption : [centerOption, centerOption];
- viewRect = getLayoutRect(boxLayoutParams, layoutRef.refContainer);
- center = layoutRef.boxCoordFrom === BoxCoordinateSystemCoordFrom.coord2 ? layoutRef.refPoint // option `series.center` has been used as coord.
- : [parsePercent(centerOptionArr[0], viewRect.width) + viewRect.x, parsePercent(centerOptionArr[1], viewRect.height) + viewRect.y];
- }
- return {
- viewRect: viewRect,
- center: center
- };
- }
- export function getCircleLayout(seriesModel, api) {
- // center can be string or number when coordinateSystem is specified
- var _a = getViewRectAndCenterForCircleLayout(seriesModel, api),
- viewRect = _a.viewRect,
- center = _a.center;
- var radius = seriesModel.get('radius');
- if (!zrUtil.isArray(radius)) {
- radius = [0, radius];
- }
- var width = parsePercent(viewRect.width, api.getWidth());
- var height = parsePercent(viewRect.height, api.getHeight());
- var size = Math.min(width, height);
- var r0 = parsePercent(radius[0], size / 2);
- var r = parsePercent(radius[1], size / 2);
- return {
- cx: center[0],
- cy: center[1],
- r0: r0,
- r: r,
- viewRect: viewRect
- };
- }
- /**
- * Parse position info.
- */
- export function getLayoutRect(positionInfo, containerRect,
- // This is the space from the `containerRect` to the returned bounding rect.
- // Commonly used in option `legend.padding`, `timeline.padding`, `title.padding`,
- // `visualMap.padding`, ...
- // [NOTICE]:
- // It's named `margin`, because it's the space that outside the bounding rect. But from
- // the perspective of the the caller, it's commonly used as the `padding` of a component,
- // because conventionally background color covers this space.
- // [BEHAVIOR]:
- // - If width/height is specified, `margin` does not effect them.
- // - Otherwise, they are calculated based on the rect that `containerRect` shrinked by `margin`.
- // - left/right/top/bottom are based on the rect that `containerRect` shrinked by `margin`.
- margin) {
- margin = formatUtil.normalizeCssArray(margin || 0);
- var containerWidth = containerRect.width;
- var containerHeight = containerRect.height;
- var left = parsePercent(positionInfo.left, containerWidth);
- var top = parsePercent(positionInfo.top, containerHeight);
- var right = parsePercent(positionInfo.right, containerWidth);
- var bottom = parsePercent(positionInfo.bottom, containerHeight);
- var width = parsePercent(positionInfo.width, containerWidth);
- var height = parsePercent(positionInfo.height, containerHeight);
- var verticalMargin = margin[2] + margin[0];
- var horizontalMargin = margin[1] + margin[3];
- var aspect = positionInfo.aspect;
- // If width is not specified, calculate width from left and right
- if (isNaN(width)) {
- width = containerWidth - right - horizontalMargin - left;
- }
- if (isNaN(height)) {
- height = containerHeight - bottom - verticalMargin - top;
- }
- if (aspect != null) {
- // If width and height are not given
- // 1. Graph should not exceeds the container
- // 2. Aspect must be keeped
- // 3. Graph should take the space as more as possible
- // FIXME
- // Margin is not considered, because there is no case that both
- // using margin and aspect so far.
- if (isNaN(width) && isNaN(height)) {
- // PENDING: if only `left` or `right` is defined, perhaps it's more preferable to
- // calculate size based on `containerWidth - left` or `containerWidth - left` here,
- // but for backward compatibility we do not change it.
- if (aspect > containerWidth / containerHeight) {
- width = containerWidth * 0.8;
- } else {
- height = containerHeight * 0.8;
- }
- }
- // Calculate width or height with given aspect
- if (isNaN(width)) {
- width = aspect * height;
- }
- if (isNaN(height)) {
- height = width / aspect;
- }
- }
- // If left is not specified, calculate left from right and width
- if (isNaN(left)) {
- left = containerWidth - right - width - horizontalMargin;
- }
- if (isNaN(top)) {
- top = containerHeight - bottom - height - verticalMargin;
- }
- // Align left and top
- switch (positionInfo.left || positionInfo.right) {
- case 'center':
- left = containerWidth / 2 - width / 2 - margin[3];
- break;
- case 'right':
- left = containerWidth - width - horizontalMargin;
- break;
- }
- switch (positionInfo.top || positionInfo.bottom) {
- case 'middle':
- case 'center':
- top = containerHeight / 2 - height / 2 - margin[0];
- break;
- case 'bottom':
- top = containerHeight - height - verticalMargin;
- break;
- }
- // If something is wrong and left, top, width, height are calculated as NaN
- left = left || 0;
- top = top || 0;
- if (isNaN(width)) {
- // Width may be NaN if only one value is given except width
- width = containerWidth - horizontalMargin - left - (right || 0);
- }
- if (isNaN(height)) {
- // Height may be NaN if only one value is given except height
- height = containerHeight - verticalMargin - top - (bottom || 0);
- }
- var rect = new BoundingRect((containerRect.x || 0) + left + margin[3], (containerRect.y || 0) + top + margin[0], width, height);
- rect.margin = margin;
- return rect;
- }
- /**
- * PENDING:
- * when preserveAspect: 'cover' and aspect is near Infinity
- * or when preserveAspect: 'contain' and aspect is near 0,
- * the result width or height is near Inifity. It's logically correct,
- * Therefore currently we do not handle it, until bad cases arise.
- */
- export function applyPreserveAspect(component, layoutRect,
- // That is, `width / height`.
- // Assume `aspect` is positive.
- aspect) {
- var preserveAspect = component.getShallow('preserveAspect', true);
- if (!preserveAspect) {
- return layoutRect;
- }
- var actualAspect = layoutRect.width / layoutRect.height;
- if (Math.abs(Math.atan(aspect) - Math.atan(actualAspect)) < 1e-9) {
- return layoutRect;
- }
- var preserveAspectAlign = component.getShallow('preserveAspectAlign', true);
- var preserveAspectVerticalAlign = component.getShallow('preserveAspectVerticalAlign', true);
- var layoutOptInner = {
- width: layoutRect.width,
- height: layoutRect.height
- };
- var isCover = preserveAspect === 'cover';
- if (actualAspect > aspect && !isCover || actualAspect < aspect && isCover) {
- layoutOptInner.width = layoutRect.height * aspect;
- preserveAspectAlign === 'left' ? layoutOptInner.left = 0 : preserveAspectAlign === 'right' ? layoutOptInner.right = 0 : layoutOptInner.left = 'center';
- } else {
- layoutOptInner.height = layoutRect.width / aspect;
- preserveAspectVerticalAlign === 'top' ? layoutOptInner.top = 0 : preserveAspectVerticalAlign === 'bottom' ? layoutOptInner.bottom = 0 : layoutOptInner.top = 'middle';
- }
- return getLayoutRect(layoutOptInner, layoutRect);
- }
- export var BoxLayoutReferenceType = {
- rect: 1,
- point: 2
- };
- /**
- * Uniformly calculate layout reference (rect or center) based on either:
- * - viewport:
- * - Get `refContainer` as `{x: 0, y: 0, width: api.getWidth(), height: api.getHeight()}`
- * - coordinate system, which can serve in several ways:
- * - Use `dataToPoint` to get the `refPoint`, such as, in cartesian2d coord sys.
- * - Use `dataToLayout` to get the `refContainer`, such as, in matrix coord sys.
- */
- export function createBoxLayoutReference(model, api, opt) {
- var refContainer;
- var refPoint;
- var layoutRefType;
- var boxCoordSys = model.boxCoordinateSystem;
- var boxCoordFrom;
- if (boxCoordSys) {
- var _a = getCoordForBoxCoordSys(model),
- coord = _a.coord,
- from = _a.from;
- // Do not use `clamp` in `dataToLayout` and `dataToPoint`, because:
- // 1. Should support overflow (such as, by dataZoom), where NaN should be in the result.
- // 2. Be consistent with the way used in `series.data`
- if (boxCoordSys.dataToLayout) {
- layoutRefType = BoxLayoutReferenceType.rect;
- boxCoordFrom = from;
- var result = boxCoordSys.dataToLayout(coord);
- refContainer = result.contentRect || result.rect;
- } else if (opt && opt.enableLayoutOnlyByCenter && boxCoordSys.dataToPoint) {
- layoutRefType = BoxLayoutReferenceType.point;
- boxCoordFrom = from;
- refPoint = boxCoordSys.dataToPoint(coord);
- } else {
- if (process.env.NODE_ENV !== 'production') {
- error(model.type + "[" + model.componentIndex + "]" + (" layout based on " + boxCoordSys.type + " is not supported."));
- }
- }
- }
- if (layoutRefType == null) {
- layoutRefType = BoxLayoutReferenceType.rect;
- }
- if (layoutRefType === BoxLayoutReferenceType.rect) {
- if (!refContainer) {
- refContainer = {
- x: 0,
- y: 0,
- width: api.getWidth(),
- height: api.getHeight()
- };
- }
- refPoint = [refContainer.x + refContainer.width / 2, refContainer.y + refContainer.height / 2];
- }
- return {
- type: layoutRefType,
- refContainer: refContainer,
- refPoint: refPoint,
- boxCoordFrom: boxCoordFrom
- };
- }
- /**
- * Position a zr element in viewport
- * Group position is specified by either
- * {left, top}, {right, bottom}
- * If all properties exists, right and bottom will be igonred.
- *
- * Logic:
- * 1. Scale (against origin point in parent coord)
- * 2. Rotate (against origin point in parent coord)
- * 3. Translate (with el.position by this method)
- * So this method only fixes the last step 'Translate', which does not affect
- * scaling and rotating.
- *
- * If be called repeatedly with the same input el, the same result will be gotten.
- *
- * Return true if the layout happened.
- *
- * @param el Should have `getBoundingRect` method.
- * @param positionInfo
- * @param positionInfo.left
- * @param positionInfo.top
- * @param positionInfo.right
- * @param positionInfo.bottom
- * @param positionInfo.width Only for opt.boundingModel: 'raw'
- * @param positionInfo.height Only for opt.boundingModel: 'raw'
- * @param containerRect
- * @param margin
- * @param opt
- * @param opt.hv Only horizontal or only vertical. Default to be [1, 1]
- * @param opt.boundingMode
- * Specify how to calculate boundingRect when locating.
- * 'all': Position the boundingRect that is transformed and uioned
- * both itself and its descendants.
- * This mode simplies confine the elements in the bounding
- * of their container (e.g., using 'right: 0').
- * 'raw': Position the boundingRect that is not transformed and only itself.
- * This mode is useful when you want a element can overflow its
- * container. (Consider a rotated circle needs to be located in a corner.)
- * In this mode positionInfo.width/height can only be number.
- */
- export function positionElement(el, positionInfo, containerRect, margin, opt, out) {
- var h = !opt || !opt.hv || opt.hv[0];
- var v = !opt || !opt.hv || opt.hv[1];
- var boundingMode = opt && opt.boundingMode || 'all';
- out = out || el;
- out.x = el.x;
- out.y = el.y;
- if (!h && !v) {
- return false;
- }
- var rect;
- if (boundingMode === 'raw') {
- rect = el.type === 'group' ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) : el.getBoundingRect();
- } else {
- rect = el.getBoundingRect();
- if (el.needLocalTransform()) {
- var transform = el.getLocalTransform();
- // Notice: raw rect may be inner object of el,
- // which should not be modified.
- rect = rect.clone();
- rect.applyTransform(transform);
- }
- }
- // The real width and height can not be specified but calculated by the given el.
- var layoutRect = getLayoutRect(zrUtil.defaults({
- width: rect.width,
- height: rect.height
- }, positionInfo), containerRect, margin);
- // Because 'tranlate' is the last step in transform
- // (see zrender/core/Transformable#getLocalTransform),
- // we can just only modify el.position to get final result.
- var dx = h ? layoutRect.x - rect.x : 0;
- var dy = v ? layoutRect.y - rect.y : 0;
- if (boundingMode === 'raw') {
- out.x = dx;
- out.y = dy;
- } else {
- out.x += dx;
- out.y += dy;
- }
- if (out === el) {
- el.markRedraw();
- }
- return true;
- }
- /**
- * @param option Contains some of the properties in HV_NAMES.
- * @param hvIdx 0: horizontal; 1: vertical.
- */
- export function sizeCalculable(option, hvIdx) {
- return option[HV_NAMES[hvIdx][0]] != null || option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null;
- }
- export function fetchLayoutMode(ins) {
- var layoutMode = ins.layoutMode || ins.constructor.layoutMode;
- return zrUtil.isObject(layoutMode) ? layoutMode : layoutMode ? {
- type: layoutMode
- } : null;
- }
- /**
- * Consider Case:
- * When default option has {left: 0, width: 100}, and we set {right: 0}
- * through setOption or media query, using normal zrUtil.merge will cause
- * {right: 0} does not take effect.
- *
- * @example
- * ComponentModel.extend({
- * init: function () {
- * ...
- * let inputPositionParams = layout.getLayoutParams(option);
- * this.mergeOption(inputPositionParams);
- * },
- * mergeOption: function (newOption) {
- * newOption && zrUtil.merge(thisOption, newOption, true);
- * layout.mergeLayoutParam(thisOption, newOption);
- * }
- * });
- *
- * @param targetOption
- * @param newOption
- * @param opt
- */
- export function mergeLayoutParam(targetOption, newOption, opt) {
- var ignoreSize = opt && opt.ignoreSize;
- !zrUtil.isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
- var hResult = merge(HV_NAMES[0], 0);
- var vResult = merge(HV_NAMES[1], 1);
- copy(HV_NAMES[0], targetOption, hResult);
- copy(HV_NAMES[1], targetOption, vResult);
- function merge(names, hvIdx) {
- var newParams = {};
- var newValueCount = 0;
- var merged = {};
- var mergedValueCount = 0;
- var enoughParamNumber = 2;
- each(names, function (name) {
- merged[name] = targetOption[name];
- });
- each(names, function (name) {
- // Consider case: newOption.width is null, which is
- // set by user for removing width setting.
- zrUtil.hasOwn(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
- hasValue(newParams, name) && newValueCount++;
- hasValue(merged, name) && mergedValueCount++;
- });
- if (ignoreSize[hvIdx]) {
- // Only one of left/right is premitted to exist.
- if (hasValue(newOption, names[1])) {
- merged[names[2]] = null;
- } else if (hasValue(newOption, names[2])) {
- merged[names[1]] = null;
- }
- return merged;
- }
- // Case: newOption: {width: ..., right: ...},
- // or targetOption: {right: ...} and newOption: {width: ...},
- // There is no conflict when merged only has params count
- // little than enoughParamNumber.
- if (mergedValueCount === enoughParamNumber || !newValueCount) {
- return merged;
- }
- // Case: newOption: {width: ..., right: ...},
- // Than we can make sure user only want those two, and ignore
- // all origin params in targetOption.
- else if (newValueCount >= enoughParamNumber) {
- return newParams;
- } else {
- // Chose another param from targetOption by priority.
- for (var i = 0; i < names.length; i++) {
- var name_1 = names[i];
- if (!zrUtil.hasOwn(newParams, name_1) && zrUtil.hasOwn(targetOption, name_1)) {
- newParams[name_1] = targetOption[name_1];
- break;
- }
- }
- return newParams;
- }
- }
- function hasValue(obj, name) {
- return obj[name] != null && obj[name] !== 'auto';
- }
- function copy(names, target, source) {
- each(names, function (name) {
- target[name] = source[name];
- });
- }
- }
- /**
- * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
- */
- export function getLayoutParams(source) {
- return copyLayoutParams({}, source);
- }
- /**
- * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
- * @param {Object} source
- * @return {Object} Result contains those props.
- */
- export function copyLayoutParams(target, source) {
- source && target && each(LOCATION_PARAMS, function (name) {
- zrUtil.hasOwn(source, name) && (target[name] = source[name]);
- });
- return target;
- }
|