| <!DOCTYPE html> | |
| <!-- | |
| 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. | |
| --> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <script src="lib/simpleRequire.js"></script> | |
| <script src="lib/config.js"></script> | |
| <script src="../dist/echarts.js"></script> | |
| <script src="lib/jquery.min.js"></script> | |
| <script src="lib/facePrint.js"></script> | |
| <script src="lib/testHelper.js"></script> | |
| <link rel="stylesheet" href="lib/reset.css" /> | |
| </head> | |
| <body> | |
| <style> | |
| </style> | |
| <div id="main-cluster-step"></div> | |
| <script> | |
| require(['echarts', 'ecStat'], function (echarts, ecStat) { | |
| var originalData = [[3.275154, 2.957587], [-3.344465, 2.603513], [0.355083, -3.376585], [1.852435, 3.547351], [-2.078973, 2.552013], [-0.993756, -0.884433], [2.682252, 4.007573], [-3.087776, 2.878713], [-1.565978, -1.256985], [2.441611, 0.444826], [-0.659487, 3.111284], [-0.459601, -2.618005], [2.17768, 2.387793], [-2.920969, 2.917485], [-0.028814, -4.168078], [3.625746, 2.119041], [-3.912363, 1.325108], [-0.551694, -2.814223], [2.855808, 3.483301], [-3.594448, 2.856651], [0.421993, -2.372646], [1.650821, 3.407572], [-2.082902, 3.384412], [-0.718809, -2.492514], [4.513623, 3.841029], [-4.822011, 4.607049], [-0.656297, -1.449872], [1.919901, 4.439368], [-3.287749, 3.918836], [-1.576936, -2.977622], [3.598143, 1.97597], [-3.977329, 4.900932], [-1.79108, -2.184517], [3.914654, 3.559303], [-1.910108, 4.166946], [-1.226597, -3.317889], [1.148946, 3.345138], [-2.113864, 3.548172], [0.845762, -3.589788], [2.629062, 3.535831], [-1.640717, 2.990517], [-1.881012, -2.485405], [4.606999, 3.510312], [-4.366462, 4.023316], [0.765015, -3.00127], [3.121904, 2.173988], [-4.025139, 4.65231], [-0.559558, -3.840539], [4.376754, 4.863579], [-1.874308, 4.032237], [-0.089337, -3.026809], [3.997787, 2.518662], [-3.082978, 2.884822], [0.845235, -3.454465], [1.327224, 3.358778], [-2.889949, 3.596178], [-0.966018, -2.839827], [2.960769, 3.079555], [-3.275518, 1.577068], [0.639276, -3.41284]]; | |
| var DIM_CLUSTER_INDEX = 2; | |
| var DATA_DIM_IDX = [0, 1]; | |
| var CENTER_DIM_IDX = [3, 4]; | |
| var step = ecStat.clustering.hierarchicalKMeans(originalData, { | |
| clusterCount: 6, | |
| outputType: 'single', | |
| outputClusterIndexDimension: DIM_CLUSTER_INDEX, | |
| outputCentroidDimensions: CENTER_DIM_IDX, | |
| stepByStep: true | |
| }); | |
| var colorAll = [ | |
| '#bbb', '#37A2DA', '#e06343', '#37a354', '#b55dba', '#b5bd48', '#8378EA', '#96BFFF' | |
| ]; | |
| var ANIMATION_DURATION_UPDATE = 1500; | |
| function renderItemPoint(params, api) { | |
| var coord = api.coord([api.value(0), api.value(1)]); | |
| var clusterIdx = api.value(2); | |
| if (clusterIdx == null || isNaN(clusterIdx)) { | |
| clusterIdx = 0; | |
| } | |
| var isNewCluster = clusterIdx === api.value(3); | |
| var extra = { | |
| transition: [] | |
| }; | |
| var contentColor = colorAll[clusterIdx]; | |
| // addColorTransition(extra, contentColor, 'content'); | |
| return { | |
| type: 'circle', | |
| x: coord[0], | |
| y: coord[1], | |
| shape: { | |
| cx: 0, | |
| cy: 0, | |
| r: 10 | |
| }, | |
| extra: extra, | |
| style: { | |
| fill: contentColor, | |
| stroke: '#333', | |
| lineWidth: 1, | |
| shadowColor: contentColor, | |
| shadowBlur: isNewCluster ? 12 : 0, | |
| transition: ['shadowBlur', 'fill'] | |
| } | |
| }; | |
| } | |
| function renderBoundary(params, api) { | |
| var xVal = api.value(0); | |
| var yVal = api.value(1); | |
| var maxDist = api.value(2); | |
| var center = api.coord([xVal, yVal]); | |
| var size = api.size([maxDist, maxDist]); | |
| var renderProgressStart; | |
| return { | |
| type: 'ellipse', | |
| shape: { | |
| cx: isNaN(center[0]) ? 0 : center[0], | |
| cy: isNaN(center[1]) ? 0 : center[1], | |
| rx: isNaN(size[0]) ? 0 : size[0] + 15, | |
| ry: isNaN(size[1]) ? 0 : size[1] + 15 | |
| }, | |
| extra: { | |
| renderProgress: ++targetRenderProgress, | |
| enterFrom: { | |
| renderProgress: 0 | |
| }, | |
| transition: 'renderProgress' | |
| }, | |
| style: { | |
| fill: null, | |
| stroke: 'rgba(0,0,0,0.2)', | |
| lineDash: [4, 4], | |
| lineWidth: 4 | |
| }, | |
| during: function (apiDuring) { | |
| var currProgress = apiDuring.getExtra('renderProgress'); | |
| renderProgressStart == null && (renderProgressStart = currProgress); | |
| var opacity = (currProgress - renderProgressStart) | |
| / (targetRenderProgress - renderProgressStart); | |
| apiDuring.setStyle('opacity', opacity); | |
| } | |
| }; | |
| } | |
| function makeStepOption(option, data, centroids) { | |
| var newCluIdx = centroids ? centroids.length - 1 : -1; | |
| var maxDist = 0; | |
| for (var i = 0; i < data.length; i++) { | |
| var line = data[i]; | |
| if (line[DIM_CLUSTER_INDEX] === newCluIdx) { | |
| var dist0 = Math.pow(line[DATA_DIM_IDX[0]] - line[CENTER_DIM_IDX[0]], 2); | |
| var dist1 = Math.pow(line[DATA_DIM_IDX[1]] - line[CENTER_DIM_IDX[1]], 2); | |
| maxDist = Math.max(maxDist, dist0 + dist1); | |
| } | |
| } | |
| var boundaryData = centroids | |
| ? [ | |
| [ | |
| centroids[newCluIdx][0], | |
| centroids[newCluIdx][1], | |
| Math.sqrt(maxDist) | |
| ] | |
| ] | |
| : []; | |
| option.options.push({ | |
| series: [{ | |
| type: 'custom', | |
| encode: { | |
| tooltip: [0, 1] | |
| }, | |
| renderItem: renderItemPoint, | |
| data: data | |
| }, { | |
| type: 'custom', | |
| renderItem: renderBoundary, | |
| animationDuration: 3000, | |
| silent: true, | |
| data: boundaryData | |
| }] | |
| }); | |
| } | |
| var targetRenderProgress = 0; | |
| var option = { | |
| timeline: { | |
| top: 'center', | |
| right: 50, | |
| height: 300, | |
| width: 10, | |
| inverse: true, | |
| autoPlay: false, | |
| playInterval: 2500, | |
| symbol: 'none', | |
| orient: 'vertical', | |
| axisType: 'category', | |
| label: { | |
| formatter: 'step {value}', | |
| position: 10 | |
| }, | |
| checkpointStyle: { | |
| color: '#555', | |
| borderWidth: 0, | |
| animationDuration: ANIMATION_DURATION_UPDATE | |
| }, | |
| data: [] | |
| }, | |
| baseOption: { | |
| animationDurationUpdate: ANIMATION_DURATION_UPDATE, | |
| tooltip: { | |
| }, | |
| xAxis: { | |
| type: 'value' | |
| }, | |
| yAxis: { | |
| type: 'value' | |
| }, | |
| series: [{ | |
| type: 'scatter' | |
| }] | |
| }, | |
| options: [] | |
| }; | |
| makeStepOption(option, originalData); | |
| option.timeline.data.push('0'); | |
| for (var i = 1, stepResult; !(stepResult = step.next()).isEnd; i++) { | |
| makeStepOption( | |
| option, | |
| echarts.util.clone(stepResult.data), | |
| echarts.util.clone(stepResult.centroids) | |
| ); | |
| option.timeline.data.push(i + ''); | |
| } | |
| var chart = testHelper.create(echarts, 'main-cluster-step', { | |
| title: [ | |
| 'Cluster algorithm visualization' | |
| ], | |
| height: 600, | |
| option: option | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |