| OLD | NEW |
| 1 // | 1 // |
| 2 // Copyright 2014 Google Inc. All rights reserved. | 2 // Copyright 2014 Google Inc. All rights reserved. |
| 3 // | 3 // |
| 4 // Use of this source code is governed by a BSD-style | 4 // Use of this source code is governed by a BSD-style |
| 5 // license that can be found in the LICENSE file or at | 5 // license that can be found in the LICENSE file or at |
| 6 // https://developers.google.com/open-source/licenses/bsd | 6 // https://developers.google.com/open-source/licenses/bsd |
| 7 // | 7 // |
| 8 | 8 |
| 9 part of charted.charts; | 9 part of charted.charts; |
| 10 | 10 |
| 11 class DefaultChartAxisImpl { | 11 class DefaultChartAxisImpl { |
| 12 static const int _AXIS_TITLE_HEIGHT = 20; |
| 13 |
| 12 CartesianArea _area; | 14 CartesianArea _area; |
| 13 ChartAxisConfig _config; | 15 ChartAxisConfig config; |
| 14 ChartAxisTheme _theme; | 16 ChartAxisTheme _theme; |
| 15 SvgAxisTicks _axisTicksPlacement; | 17 SvgAxisTicks _axisTicksPlacement; |
| 16 | 18 |
| 17 int _column; | 19 int _column; |
| 18 bool _isDimension; | 20 bool _isDimension; |
| 19 ChartColumnSpec _columnSpec; | 21 ChartColumnSpec _columnSpec; |
| 20 | 22 |
| 21 bool _isVertical; | 23 bool _isVertical; |
| 22 String _orientation; | 24 String _orientation; |
| 23 Scale _scale; | 25 Scale _scale; |
| 24 SelectionScope _scope; | 26 SelectionScope _scope; |
| 27 String _title; |
| 25 | 28 |
| 26 MutableRect size; | 29 MutableRect size; |
| 27 | 30 |
| 28 DefaultChartAxisImpl.withAxisConfig(this._area, this._config); | 31 DefaultChartAxisImpl.withAxisConfig(this._area, this.config); |
| 29 DefaultChartAxisImpl(this._area); | 32 DefaultChartAxisImpl(this._area); |
| 30 | 33 |
| 31 void initAxisDomain(int column, bool isDimension, Iterable domain) { | 34 void initAxisDomain(int column, bool isDimension, Iterable domain) { |
| 32 _columnSpec = _area.data.columns.elementAt(column); | 35 _columnSpec = _area.data.columns.elementAt(column); |
| 33 _column = column; | 36 _column = column; |
| 34 _isDimension = isDimension; | 37 _isDimension = isDimension; |
| 35 | 38 |
| 36 // If we don't have a scale yet, create one. | 39 // If we don't have a scale yet, create one. |
| 37 if (scale == null) { | 40 if (scale == null) { |
| 38 _scale = _columnSpec.createDefaultScale(); | 41 _scale = _columnSpec.createDefaultScale(); |
| 39 } | 42 } |
| 40 | 43 |
| 41 // We have the scale, get theme. | 44 // We have the scale, get theme. |
| 42 _theme = isDimension | 45 _theme = isDimension |
| 43 ? _area.theme.getDimensionAxisTheme(scale) | 46 ? _area.theme.getDimensionAxisTheme(scale) |
| 44 : _area.theme.getMeasureAxisTheme(scale); | 47 : _area.theme.getMeasureAxisTheme(scale); |
| 45 | 48 |
| 46 // Sets the domain if not using a custom scale. | 49 // Sets the domain if not using a custom scale. |
| 47 if (_config == null || (_config != null && _config.scale == null)) { | 50 if (config == null || (config != null && config.scale == null)) { |
| 48 scale.domain = domain; | 51 scale.domain = domain; |
| 49 scale.nice = !_isDimension; | 52 scale.nice = !_isDimension && |
| 53 !(config?.forcedTicksCount != null && config.forcedTicksCount > 0); |
| 50 } | 54 } |
| 55 |
| 56 _title = config?.title; |
| 51 } | 57 } |
| 52 | 58 |
| 53 void initAxisScale(Iterable range) { | 59 void initAxisScale(Iterable range) { |
| 54 assert(scale != null); | 60 assert(scale != null); |
| 55 if (scale is OrdinalScale) { | 61 if (scale is OrdinalScale) { |
| 56 var usingBands = _area.dimensionsUsingBands.contains(_column), | 62 var usingBands = _area.dimensionsUsingBands.contains(_column), |
| 57 innerPadding = usingBands ? _theme.axisBandInnerPadding : 1.0, | 63 innerPadding = usingBands ? _theme.axisBandInnerPadding : 1.0, |
| 58 outerPadding = | 64 outerPadding = |
| 59 usingBands ? _theme.axisBandOuterPadding : _theme.axisOuterPadding; | 65 usingBands ? _theme.axisBandOuterPadding : _theme.axisOuterPadding; |
| 60 | 66 |
| 61 // This is because when left axis is primary the first data row should | 67 // This is because when left axis is primary the first data row should |
| 62 // appear on top of the y-axis instead of on bottom. | 68 // appear on top of the y-axis instead of on bottom. |
| 63 if (_area.config.isLeftAxisPrimary) { | 69 if (_area.config.isLeftAxisPrimary) { |
| 64 range = range.toList().reversed; | 70 range = range.toList().reversed; |
| 65 } | 71 } |
| 66 (scale as OrdinalScale) | 72 if (usingBands) { |
| 67 .rangeRoundBands(range, innerPadding, outerPadding); | 73 (scale as OrdinalScale) |
| 74 .rangeRoundBands(range, innerPadding, outerPadding); |
| 75 } else { |
| 76 (scale as OrdinalScale).rangePoints(range, outerPadding); |
| 77 } |
| 68 } else { | 78 } else { |
| 69 scale.range = range; | 79 if (_title != null) { |
| 80 var modifiedRange = range.take(range.length - 1).toList(); |
| 81 modifiedRange.add(range.last + _AXIS_TITLE_HEIGHT); |
| 82 scale.range = modifiedRange; |
| 83 } else { |
| 84 scale.range = range; |
| 85 } |
| 70 scale.ticksCount = _theme.axisTickCount; | 86 scale.ticksCount = _theme.axisTickCount; |
| 71 } | 87 } |
| 72 } | 88 } |
| 73 | 89 |
| 74 void prepareToDraw(String orientation) { | 90 void prepareToDraw(String orientation) { |
| 75 if (orientation == null) orientation = ORIENTATION_BOTTOM; | 91 if (orientation == null) orientation = ORIENTATION_BOTTOM; |
| 76 _orientation = orientation; | 92 _orientation = orientation; |
| 77 _isVertical = | 93 _isVertical = |
| 78 _orientation == ORIENTATION_LEFT || _orientation == ORIENTATION_RIGHT; | 94 _orientation == ORIENTATION_LEFT || _orientation == ORIENTATION_RIGHT; |
| 79 | 95 |
| 80 var layout = _area.layout.chartArea; | 96 var layout = _area.layout.chartArea; |
| 81 size = _isVertical | 97 size = _isVertical |
| 82 ? new MutableRect.size(_theme.verticalAxisWidth, layout.width) | 98 ? new MutableRect.size(_theme.verticalAxisWidth, layout.width) |
| 83 : new MutableRect.size(layout.height, _theme.horizontalAxisHeight); | 99 : new MutableRect.size(layout.height, _theme.horizontalAxisHeight); |
| 84 | 100 |
| 101 if (config?.forcedTicksCount != null && config.forcedTicksCount > 0) { |
| 102 scale.forcedTicksCount = config.forcedTicksCount; |
| 103 } |
| 104 |
| 85 // Handle auto re-sizing of horizontal axis. | 105 // Handle auto re-sizing of horizontal axis. |
| 86 var ticks = (_config != null && !isNullOrEmpty(_config.tickValues)) | 106 var ticks = (config != null && !isNullOrEmpty(config.tickValues)) |
| 87 ? _config.tickValues | 107 ? config.tickValues |
| 88 : scale.ticks, | 108 : scale.ticks, |
| 109 |
| 89 formatter = _columnSpec.formatter == null | 110 formatter = _columnSpec.formatter == null |
| 90 ? scale.createTickFormatter() | 111 ? scale.createTickFormatter() |
| 91 : _columnSpec.formatter, | 112 : _columnSpec.formatter, |
| 92 textMetrics = new TextMetrics(fontStyle: _theme.ticksFont), | 113 textMetrics = new TextMetrics(fontStyle: _theme.ticksFont), |
| 93 formattedTicks = ticks.map((x) => formatter(x)).toList(), | 114 formattedTicks = ticks.map((x) => formatter(x)).toList(), |
| 94 shortenedTicks = formattedTicks; | 115 shortenedTicks = formattedTicks; |
| 95 if (_isVertical) { | 116 if (_isVertical) { |
| 96 var width = textMetrics.getLongestTextWidth(formattedTicks).ceil(); | 117 var width = textMetrics.getLongestTextWidth(formattedTicks).ceil(); |
| 97 if (width > _theme.verticalAxisWidth) { | 118 if (width > _theme.verticalAxisWidth) { |
| 98 width = _theme.verticalAxisWidth; | 119 width = _theme.verticalAxisWidth; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 124 void draw(Element element, SelectionScope scope, {bool preRender: false}) { | 145 void draw(Element element, SelectionScope scope, {bool preRender: false}) { |
| 125 assert(element != null && element is GElement); | 146 assert(element != null && element is GElement); |
| 126 assert(scale != null); | 147 assert(scale != null); |
| 127 | 148 |
| 128 var rect = _area.layout.axes[_orientation], | 149 var rect = _area.layout.axes[_orientation], |
| 129 renderAreaRect = _area.layout.renderArea, | 150 renderAreaRect = _area.layout.renderArea, |
| 130 range = _isVertical ? [rect.height, 0] : [0, rect.width], | 151 range = _isVertical ? [rect.height, 0] : [0, rect.width], |
| 131 innerTickSize = _theme.axisTickSize <= ChartAxisTheme.FILL_RENDER_AREA | 152 innerTickSize = _theme.axisTickSize <= ChartAxisTheme.FILL_RENDER_AREA |
| 132 ? 0 - (_isVertical ? renderAreaRect.width : renderAreaRect.height) | 153 ? 0 - (_isVertical ? renderAreaRect.width : renderAreaRect.height) |
| 133 : _theme.axisTickSize, | 154 : _theme.axisTickSize, |
| 134 tickValues = _config != null && !isNullOrEmpty(_config.tickValues) | 155 tickValues = config != null && !isNullOrEmpty(config.tickValues) |
| 135 ? _config.tickValues | 156 ? config.tickValues |
| 136 : null; | 157 : null; |
| 137 | 158 |
| 138 element.attributes['transform'] = 'translate(${rect.x}, ${rect.y})'; | 159 element.attributes['transform'] = 'translate(${rect.x}, ${rect.y})'; |
| 139 | 160 |
| 140 if (!_isVertical) { | 161 if (!_isVertical) { |
| 141 _axisTicksPlacement = new RotateHorizontalAxisTicks( | 162 _axisTicksPlacement = new RotateHorizontalAxisTicks( |
| 142 rect, _theme.ticksFont, _theme.axisTickSize + _theme.axisTickPadding); | 163 rect, _theme.ticksFont, _theme.axisTickSize + _theme.axisTickPadding); |
| 143 } | 164 } |
| 165 initAxisScale(range); |
| 144 | 166 |
| 145 initAxisScale(range); | 167 if (_title != null) { |
| 168 var label = element.querySelector('.chart-axis-label'); |
| 169 if (label != null) { |
| 170 label.text = _title; |
| 171 } else { |
| 172 var title = Namespace.createChildElement('text', element); |
| 173 title.attributes['text-anchor'] = 'middle'; |
| 174 title.text = _title; |
| 175 title.classes.add('chart-axis-label'); |
| 176 element.append(title); |
| 177 } |
| 178 } |
| 179 |
| 146 var axis = new SvgAxis( | 180 var axis = new SvgAxis( |
| 147 orientation: _orientation, | 181 orientation: _orientation, |
| 148 innerTickSize: innerTickSize, | 182 innerTickSize: innerTickSize, |
| 149 outerTickSize: 0, | 183 outerTickSize: 0, |
| 150 tickPadding: _theme.axisTickPadding, | 184 tickPadding: _theme.axisTickPadding, |
| 151 tickFormat: _columnSpec.formatter, | 185 tickFormat: _columnSpec.formatter, |
| 152 tickValues: tickValues, | 186 tickValues: tickValues, |
| 153 scale: scale); | 187 scale: scale); |
| 154 | 188 |
| 155 axis.create(element, scope, | 189 axis.create(element, scope, |
| 156 axisTicksBuilder: _axisTicksPlacement, isRTL: _area.config.isRTL); | 190 axisTicksBuilder: _axisTicksPlacement, isRTL: _area.config.isRTL); |
| 157 } | 191 } |
| 158 | 192 |
| 159 void clear() {} | 193 void clear() {} |
| 160 | 194 |
| 161 // Scale passed through configuration takes precedence | 195 // Scale passed through configuration takes precedence |
| 162 Scale get scale => | 196 Scale get scale => |
| 163 (_config != null && _config.scale != null) ? _config.scale : _scale; | 197 (config != null && config.scale != null) ? config.scale : _scale; |
| 164 | 198 |
| 165 set scale(Scale value) { | 199 set scale(Scale value) { |
| 166 _scale = value; | 200 _scale = value; |
| 167 } | 201 } |
| 202 |
| 168 } | 203 } |
| 169 | 204 |
| 170 class PrecomputedAxisTicks implements SvgAxisTicks { | 205 class PrecomputedAxisTicks implements SvgAxisTicks { |
| 171 final int rotation = 0; | 206 final int rotation = 0; |
| 172 final Iterable ticks; | 207 final Iterable ticks; |
| 173 final Iterable formattedTicks; | 208 final Iterable formattedTicks; |
| 174 final Iterable shortenedTicks; | 209 final Iterable shortenedTicks; |
| 175 const PrecomputedAxisTicks( | 210 const PrecomputedAxisTicks( |
| 176 this.ticks, this.formattedTicks, this.shortenedTicks); | 211 this.ticks, this.formattedTicks, this.shortenedTicks); |
| 177 void init(SvgAxis axis) {} | 212 void init(SvgAxis axis) {} |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 // Check if we have enough space to render full chart | 249 // Check if we have enough space to render full chart |
| 215 allowedWidth = (1.4142 * (rectHeight)) - (textMetrics.fontSize / 1.4142); | 250 allowedWidth = (1.4142 * (rectHeight)) - (textMetrics.fontSize / 1.4142); |
| 216 if (maxLabelWidth > allowedWidth) { | 251 if (maxLabelWidth > allowedWidth) { |
| 217 shortenedTicks = formattedTicks | 252 shortenedTicks = formattedTicks |
| 218 .map((x) => textMetrics.ellipsizeText(x, allowedWidth)) | 253 .map((x) => textMetrics.ellipsizeText(x, allowedWidth)) |
| 219 .toList(); | 254 .toList(); |
| 220 } | 255 } |
| 221 } | 256 } |
| 222 } | 257 } |
| 223 } | 258 } |
| OLD | NEW |