|   | 
| /* | 
| * 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 { Point, Path, Polyline } from '../util/graphic.js'; | 
| import PathProxy from 'zrender/lib/core/PathProxy.js'; | 
| import { normalizeRadian } from 'zrender/lib/contain/util.js'; | 
| import { cubicProjectPoint, quadraticProjectPoint } from 'zrender/lib/core/curve.js'; | 
| import { defaults, retrieve2 } from 'zrender/lib/core/util.js'; | 
| import { invert } from 'zrender/lib/core/matrix.js'; | 
| import * as vector from 'zrender/lib/core/vector.js'; | 
| import { DISPLAY_STATES, SPECIAL_STATES } from '../util/states.js'; | 
| var PI2 = Math.PI * 2; | 
| var CMD = PathProxy.CMD; | 
| var DEFAULT_SEARCH_SPACE = ['top', 'right', 'bottom', 'left']; | 
| function getCandidateAnchor(pos, distance, rect, outPt, outDir) { | 
|   var width = rect.width; | 
|   var height = rect.height; | 
|   switch (pos) { | 
|     case 'top': | 
|       outPt.set(rect.x + width / 2, rect.y - distance); | 
|       outDir.set(0, -1); | 
|       break; | 
|     case 'bottom': | 
|       outPt.set(rect.x + width / 2, rect.y + height + distance); | 
|       outDir.set(0, 1); | 
|       break; | 
|     case 'left': | 
|       outPt.set(rect.x - distance, rect.y + height / 2); | 
|       outDir.set(-1, 0); | 
|       break; | 
|     case 'right': | 
|       outPt.set(rect.x + width + distance, rect.y + height / 2); | 
|       outDir.set(1, 0); | 
|       break; | 
|   } | 
| } | 
| function projectPointToArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y, out) { | 
|   x -= cx; | 
|   y -= cy; | 
|   var d = Math.sqrt(x * x + y * y); | 
|   x /= d; | 
|   y /= d; | 
|   // Intersect point. | 
|   var ox = x * r + cx; | 
|   var oy = y * r + cy; | 
|   if (Math.abs(startAngle - endAngle) % PI2 < 1e-4) { | 
|     // Is a circle | 
|     out[0] = ox; | 
|     out[1] = oy; | 
|     return d - r; | 
|   } | 
|   if (anticlockwise) { | 
|     var tmp = startAngle; | 
|     startAngle = normalizeRadian(endAngle); | 
|     endAngle = normalizeRadian(tmp); | 
|   } else { | 
|     startAngle = normalizeRadian(startAngle); | 
|     endAngle = normalizeRadian(endAngle); | 
|   } | 
|   if (startAngle > endAngle) { | 
|     endAngle += PI2; | 
|   } | 
|   var angle = Math.atan2(y, x); | 
|   if (angle < 0) { | 
|     angle += PI2; | 
|   } | 
|   if (angle >= startAngle && angle <= endAngle || angle + PI2 >= startAngle && angle + PI2 <= endAngle) { | 
|     // Project point is on the arc. | 
|     out[0] = ox; | 
|     out[1] = oy; | 
|     return d - r; | 
|   } | 
|   var x1 = r * Math.cos(startAngle) + cx; | 
|   var y1 = r * Math.sin(startAngle) + cy; | 
|   var x2 = r * Math.cos(endAngle) + cx; | 
|   var y2 = r * Math.sin(endAngle) + cy; | 
|   var d1 = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y); | 
|   var d2 = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y); | 
|   if (d1 < d2) { | 
|     out[0] = x1; | 
|     out[1] = y1; | 
|     return Math.sqrt(d1); | 
|   } else { | 
|     out[0] = x2; | 
|     out[1] = y2; | 
|     return Math.sqrt(d2); | 
|   } | 
| } | 
| function projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) { | 
|   var dx = x - x1; | 
|   var dy = y - y1; | 
|   var dx1 = x2 - x1; | 
|   var dy1 = y2 - y1; | 
|   var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1); | 
|   dx1 /= lineLen; | 
|   dy1 /= lineLen; | 
|   // dot product | 
|   var projectedLen = dx * dx1 + dy * dy1; | 
|   var t = projectedLen / lineLen; | 
|   if (limitToEnds) { | 
|     t = Math.min(Math.max(t, 0), 1); | 
|   } | 
|   t *= lineLen; | 
|   var ox = out[0] = x1 + t * dx1; | 
|   var oy = out[1] = y1 + t * dy1; | 
|   return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y)); | 
| } | 
| function projectPointToRect(x1, y1, width, height, x, y, out) { | 
|   if (width < 0) { | 
|     x1 = x1 + width; | 
|     width = -width; | 
|   } | 
|   if (height < 0) { | 
|     y1 = y1 + height; | 
|     height = -height; | 
|   } | 
|   var x2 = x1 + width; | 
|   var y2 = y1 + height; | 
|   var ox = out[0] = Math.min(Math.max(x, x1), x2); | 
|   var oy = out[1] = Math.min(Math.max(y, y1), y2); | 
|   return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y)); | 
| } | 
| var tmpPt = []; | 
| function nearestPointOnRect(pt, rect, out) { | 
|   var dist = projectPointToRect(rect.x, rect.y, rect.width, rect.height, pt.x, pt.y, tmpPt); | 
|   out.set(tmpPt[0], tmpPt[1]); | 
|   return dist; | 
| } | 
| /** | 
|  * Calculate min distance corresponding point. | 
|  * This method won't evaluate if point is in the path. | 
|  */ | 
| function nearestPointOnPath(pt, path, out) { | 
|   var xi = 0; | 
|   var yi = 0; | 
|   var x0 = 0; | 
|   var y0 = 0; | 
|   var x1; | 
|   var y1; | 
|   var minDist = Infinity; | 
|   var data = path.data; | 
|   var x = pt.x; | 
|   var y = pt.y; | 
|   for (var i = 0; i < data.length;) { | 
|     var cmd = data[i++]; | 
|     if (i === 1) { | 
|       xi = data[i]; | 
|       yi = data[i + 1]; | 
|       x0 = xi; | 
|       y0 = yi; | 
|     } | 
|     var d = minDist; | 
|     switch (cmd) { | 
|       case CMD.M: | 
|         // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点 | 
|         // 在 closePath 的时候使用 | 
|         x0 = data[i++]; | 
|         y0 = data[i++]; | 
|         xi = x0; | 
|         yi = y0; | 
|         break; | 
|       case CMD.L: | 
|         d = projectPointToLine(xi, yi, data[i], data[i + 1], x, y, tmpPt, true); | 
|         xi = data[i++]; | 
|         yi = data[i++]; | 
|         break; | 
|       case CMD.C: | 
|         d = cubicProjectPoint(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt); | 
|         xi = data[i++]; | 
|         yi = data[i++]; | 
|         break; | 
|       case CMD.Q: | 
|         d = quadraticProjectPoint(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt); | 
|         xi = data[i++]; | 
|         yi = data[i++]; | 
|         break; | 
|       case CMD.A: | 
|         // TODO Arc 判断的开销比较大 | 
|         var cx = data[i++]; | 
|         var cy = data[i++]; | 
|         var rx = data[i++]; | 
|         var ry = data[i++]; | 
|         var theta = data[i++]; | 
|         var dTheta = data[i++]; | 
|         // TODO Arc 旋转 | 
|         i += 1; | 
|         var anticlockwise = !!(1 - data[i++]); | 
|         x1 = Math.cos(theta) * rx + cx; | 
|         y1 = Math.sin(theta) * ry + cy; | 
|         // 不是直接使用 arc 命令 | 
|         if (i <= 1) { | 
|           // 第一个命令起点还未定义 | 
|           x0 = x1; | 
|           y0 = y1; | 
|         } | 
|         // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放 | 
|         var _x = (x - cx) * ry / rx + cx; | 
|         d = projectPointToArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y, tmpPt); | 
|         xi = Math.cos(theta + dTheta) * rx + cx; | 
|         yi = Math.sin(theta + dTheta) * ry + cy; | 
|         break; | 
|       case CMD.R: | 
|         x0 = xi = data[i++]; | 
|         y0 = yi = data[i++]; | 
|         var width = data[i++]; | 
|         var height = data[i++]; | 
|         d = projectPointToRect(x0, y0, width, height, x, y, tmpPt); | 
|         break; | 
|       case CMD.Z: | 
|         d = projectPointToLine(xi, yi, x0, y0, x, y, tmpPt, true); | 
|         xi = x0; | 
|         yi = y0; | 
|         break; | 
|     } | 
|     if (d < minDist) { | 
|       minDist = d; | 
|       out.set(tmpPt[0], tmpPt[1]); | 
|     } | 
|   } | 
|   return minDist; | 
| } | 
| // Temporal variable for intermediate usage. | 
| var pt0 = new Point(); | 
| var pt1 = new Point(); | 
| var pt2 = new Point(); | 
| var dir = new Point(); | 
| var dir2 = new Point(); | 
| /** | 
|  * Calculate a proper guide line based on the label position and graphic element definition | 
|  * @param label | 
|  * @param labelRect | 
|  * @param target | 
|  * @param targetRect | 
|  */ | 
| export function updateLabelLinePoints(target, labelLineModel) { | 
|   if (!target) { | 
|     return; | 
|   } | 
|   var labelLine = target.getTextGuideLine(); | 
|   var label = target.getTextContent(); | 
|   // Needs to create text guide in each charts. | 
|   if (!(label && labelLine)) { | 
|     return; | 
|   } | 
|   var labelGuideConfig = target.textGuideLineConfig || {}; | 
|   var points = [[0, 0], [0, 0], [0, 0]]; | 
|   var searchSpace = labelGuideConfig.candidates || DEFAULT_SEARCH_SPACE; | 
|   var labelRect = label.getBoundingRect().clone(); | 
|   labelRect.applyTransform(label.getComputedTransform()); | 
|   var minDist = Infinity; | 
|   var anchorPoint = labelGuideConfig.anchor; | 
|   var targetTransform = target.getComputedTransform(); | 
|   var targetInversedTransform = targetTransform && invert([], targetTransform); | 
|   var len = labelLineModel.get('length2') || 0; | 
|   if (anchorPoint) { | 
|     pt2.copy(anchorPoint); | 
|   } | 
|   for (var i = 0; i < searchSpace.length; i++) { | 
|     var candidate = searchSpace[i]; | 
|     getCandidateAnchor(candidate, 0, labelRect, pt0, dir); | 
|     Point.scaleAndAdd(pt1, pt0, dir, len); | 
|     // Transform to target coord space. | 
|     pt1.transform(targetInversedTransform); | 
|     // Note: getBoundingRect will ensure the `path` being created. | 
|     var boundingRect = target.getBoundingRect(); | 
|     var dist = anchorPoint ? anchorPoint.distance(pt1) : target instanceof Path ? nearestPointOnPath(pt1, target.path, pt2) : nearestPointOnRect(pt1, boundingRect, pt2); | 
|     // TODO pt2 is in the path | 
|     if (dist < minDist) { | 
|       minDist = dist; | 
|       // Transform back to global space. | 
|       pt1.transform(targetTransform); | 
|       pt2.transform(targetTransform); | 
|       pt2.toArray(points[0]); | 
|       pt1.toArray(points[1]); | 
|       pt0.toArray(points[2]); | 
|     } | 
|   } | 
|   limitTurnAngle(points, labelLineModel.get('minTurnAngle')); | 
|   labelLine.setShape({ | 
|     points: points | 
|   }); | 
| } | 
| // Temporal variable for the limitTurnAngle function | 
| var tmpArr = []; | 
| var tmpProjPoint = new Point(); | 
| /** | 
|  * Reduce the line segment attached to the label to limit the turn angle between two segments. | 
|  * @param linePoints | 
|  * @param minTurnAngle Radian of minimum turn angle. 0 - 180 | 
|  */ | 
| export function limitTurnAngle(linePoints, minTurnAngle) { | 
|   if (!(minTurnAngle <= 180 && minTurnAngle > 0)) { | 
|     return; | 
|   } | 
|   minTurnAngle = minTurnAngle / 180 * Math.PI; | 
|   // The line points can be | 
|   //      /pt1----pt2 (label) | 
|   //     / | 
|   // pt0/ | 
|   pt0.fromArray(linePoints[0]); | 
|   pt1.fromArray(linePoints[1]); | 
|   pt2.fromArray(linePoints[2]); | 
|   Point.sub(dir, pt0, pt1); | 
|   Point.sub(dir2, pt2, pt1); | 
|   var len1 = dir.len(); | 
|   var len2 = dir2.len(); | 
|   if (len1 < 1e-3 || len2 < 1e-3) { | 
|     return; | 
|   } | 
|   dir.scale(1 / len1); | 
|   dir2.scale(1 / len2); | 
|   var angleCos = dir.dot(dir2); | 
|   var minTurnAngleCos = Math.cos(minTurnAngle); | 
|   if (minTurnAngleCos < angleCos) { | 
|     // Smaller than minTurnAngle | 
|     // Calculate project point of pt0 on pt1-pt2 | 
|     var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false); | 
|     tmpProjPoint.fromArray(tmpArr); | 
|     // Calculate new projected length with limited minTurnAngle and get the new connect point | 
|     tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); | 
|     // Limit the new calculated connect point between pt1 and pt2. | 
|     var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y); | 
|     if (isNaN(t)) { | 
|       return; | 
|     } | 
|     if (t < 0) { | 
|       Point.copy(tmpProjPoint, pt1); | 
|     } else if (t > 1) { | 
|       Point.copy(tmpProjPoint, pt2); | 
|     } | 
|     tmpProjPoint.toArray(linePoints[1]); | 
|   } | 
| } | 
| /** | 
|  * Limit the angle of line and the surface | 
|  * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite | 
|  */ | 
| export function limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) { | 
|   if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) { | 
|     return; | 
|   } | 
|   maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI; | 
|   pt0.fromArray(linePoints[0]); | 
|   pt1.fromArray(linePoints[1]); | 
|   pt2.fromArray(linePoints[2]); | 
|   Point.sub(dir, pt1, pt0); | 
|   Point.sub(dir2, pt2, pt1); | 
|   var len1 = dir.len(); | 
|   var len2 = dir2.len(); | 
|   if (len1 < 1e-3 || len2 < 1e-3) { | 
|     return; | 
|   } | 
|   dir.scale(1 / len1); | 
|   dir2.scale(1 / len2); | 
|   var angleCos = dir.dot(surfaceNormal); | 
|   var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle); | 
|   if (angleCos < maxSurfaceAngleCos) { | 
|     // Calculate project point of pt0 on pt1-pt2 | 
|     var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false); | 
|     tmpProjPoint.fromArray(tmpArr); | 
|     var HALF_PI = Math.PI / 2; | 
|     var angle2 = Math.acos(dir2.dot(surfaceNormal)); | 
|     var newAngle = HALF_PI + angle2 - maxSurfaceAngle; | 
|     if (newAngle >= HALF_PI) { | 
|       // parallel | 
|       Point.copy(tmpProjPoint, pt2); | 
|     } else { | 
|       // Calculate new projected length with limited minTurnAngle and get the new connect point | 
|       tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle)); | 
|       // Limit the new calculated connect point between pt1 and pt2. | 
|       var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y); | 
|       if (isNaN(t)) { | 
|         return; | 
|       } | 
|       if (t < 0) { | 
|         Point.copy(tmpProjPoint, pt1); | 
|       } else if (t > 1) { | 
|         Point.copy(tmpProjPoint, pt2); | 
|       } | 
|     } | 
|     tmpProjPoint.toArray(linePoints[1]); | 
|   } | 
| } | 
| function setLabelLineState(labelLine, ignore, stateName, stateModel) { | 
|   var isNormal = stateName === 'normal'; | 
|   var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); | 
|   // Make sure display. | 
|   stateObj.ignore = ignore; | 
|   // Set smooth | 
|   var smooth = stateModel.get('smooth'); | 
|   if (smooth && smooth === true) { | 
|     smooth = 0.3; | 
|   } | 
|   stateObj.shape = stateObj.shape || {}; | 
|   if (smooth > 0) { | 
|     stateObj.shape.smooth = smooth; | 
|   } | 
|   var styleObj = stateModel.getModel('lineStyle').getLineStyle(); | 
|   isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj; | 
| } | 
| function buildLabelLinePath(path, shape) { | 
|   var smooth = shape.smooth; | 
|   var points = shape.points; | 
|   if (!points) { | 
|     return; | 
|   } | 
|   path.moveTo(points[0][0], points[0][1]); | 
|   if (smooth > 0 && points.length >= 3) { | 
|     var len1 = vector.dist(points[0], points[1]); | 
|     var len2 = vector.dist(points[1], points[2]); | 
|     if (!len1 || !len2) { | 
|       path.lineTo(points[1][0], points[1][1]); | 
|       path.lineTo(points[2][0], points[2][1]); | 
|       return; | 
|     } | 
|     var moveLen = Math.min(len1, len2) * smooth; | 
|     var midPoint0 = vector.lerp([], points[1], points[0], moveLen / len1); | 
|     var midPoint2 = vector.lerp([], points[1], points[2], moveLen / len2); | 
|     var midPoint1 = vector.lerp([], midPoint0, midPoint2, 0.5); | 
|     path.bezierCurveTo(midPoint0[0], midPoint0[1], midPoint0[0], midPoint0[1], midPoint1[0], midPoint1[1]); | 
|     path.bezierCurveTo(midPoint2[0], midPoint2[1], midPoint2[0], midPoint2[1], points[2][0], points[2][1]); | 
|   } else { | 
|     for (var i = 1; i < points.length; i++) { | 
|       path.lineTo(points[i][0], points[i][1]); | 
|     } | 
|   } | 
| } | 
| /** | 
|  * Create a label line if necessary and set it's style. | 
|  */ | 
| export function setLabelLineStyle(targetEl, statesModels, defaultStyle) { | 
|   var labelLine = targetEl.getTextGuideLine(); | 
|   var label = targetEl.getTextContent(); | 
|   if (!label) { | 
|     // Not show label line if there is no label. | 
|     if (labelLine) { | 
|       targetEl.removeTextGuideLine(); | 
|     } | 
|     return; | 
|   } | 
|   var normalModel = statesModels.normal; | 
|   var showNormal = normalModel.get('show'); | 
|   var labelIgnoreNormal = label.ignore; | 
|   for (var i = 0; i < DISPLAY_STATES.length; i++) { | 
|     var stateName = DISPLAY_STATES[i]; | 
|     var stateModel = statesModels[stateName]; | 
|     var isNormal = stateName === 'normal'; | 
|     if (stateModel) { | 
|       var stateShow = stateModel.get('show'); | 
|       var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal); | 
|       if (isLabelIgnored // Not show when label is not shown in this state. | 
|       || !retrieve2(stateShow, showNormal) // Use normal state by default if not set. | 
|       ) { | 
|         var stateObj = isNormal ? labelLine : labelLine && labelLine.states[stateName]; | 
|         if (stateObj) { | 
|           stateObj.ignore = true; | 
|         } | 
|         if (!!labelLine) { | 
|           setLabelLineState(labelLine, true, stateName, stateModel); | 
|         } | 
|         continue; | 
|       } | 
|       // Create labelLine if not exists | 
|       if (!labelLine) { | 
|         labelLine = new Polyline(); | 
|         targetEl.setTextGuideLine(labelLine); | 
|         // Reset state of normal because it's new created. | 
|         // NOTE: NORMAL should always been the first! | 
|         if (!isNormal && (labelIgnoreNormal || !showNormal)) { | 
|           setLabelLineState(labelLine, true, 'normal', statesModels.normal); | 
|         } | 
|         // Use same state proxy. | 
|         if (targetEl.stateProxy) { | 
|           labelLine.stateProxy = targetEl.stateProxy; | 
|         } | 
|       } | 
|       setLabelLineState(labelLine, false, stateName, stateModel); | 
|     } | 
|   } | 
|   if (labelLine) { | 
|     defaults(labelLine.style, defaultStyle); | 
|     // Not fill. | 
|     labelLine.style.fill = null; | 
|     var showAbove = normalModel.get('showAbove'); | 
|     var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {}; | 
|     labelLineConfig.showAbove = showAbove || false; | 
|     // Custom the buildPath. | 
|     labelLine.buildPath = buildLabelLinePath; | 
|   } | 
| } | 
| export function getLabelLineStatesModels(itemModel, labelLineName) { | 
|   labelLineName = labelLineName || 'labelLine'; | 
|   var statesModels = { | 
|     normal: itemModel.getModel(labelLineName) | 
|   }; | 
|   for (var i = 0; i < SPECIAL_STATES.length; i++) { | 
|     var stateName = SPECIAL_STATES[i]; | 
|     statesModels[stateName] = itemModel.getModel([stateName, labelLineName]); | 
|   } | 
|   return statesModels; | 
| } |