Merge pull request #136 from Abhinandan-Kushwaha/feat/negative-line-chart

Feat/negative line chart
This commit is contained in:
Abhinandan Kushwaha 2022-04-13 03:09:21 +05:30 committed by GitHub
commit cc423c4986
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 203 additions and 146 deletions

133
App.js
View File

@ -1,5 +1,5 @@
import React, {useEffect, useState} from 'react';
import {TouchableOpacity} from 'react-native';
import {ScrollView, TouchableOpacity} from 'react-native';
import {Alert} from 'react-native';
import {View, Text, StyleSheet} from 'react-native';
import {MyPattern} from './src/pattern';
@ -682,34 +682,34 @@ const App = () => {
{value: 0.85, label: '15'},
];
const lineData = [
{value: 8, dataPointText: '8'},
{value: 10, dataPointText: '10'},
{value: 12, dataPointText: '12'},
{value: 8, dataPointText: '8'},
{value: 11, dataPointText: '11'},
{value: 13, dataPointText: '13'},
{value: 19, dataPointText: '19'},
{value: 18, dataPointText: '18'},
{value: 22, dataPointText: '22'},
{value: 20, dataPointText: '20'},
{value: 28, dataPointText: '28'},
{value: 32, dataPointText: '32'},
{value: 36, dataPointText: '36'},
{value: 40, dataPointText: '40'},
{value: 38, dataPointText: '38'},
{value: 40, dataPointText: '40'},
{value: 42, dataPointText: '42'},
{value: 46, dataPointText: '46'},
{value: 44, dataPointText: '44'},
{value: 40, dataPointText: '40'},
{value: 36, dataPointText: '36'},
{value: 32, dataPointText: '32'},
{value: 38, dataPointText: '38'},
{value: 36, dataPointText: '36'},
{value: 32, dataPointText: '32'},
{value: 28, dataPointText: '28'},
{value: 22, dataPointText: '22'},
{value: 20, dataPointText: '20'},
{value: 8,pointerShiftX:10,pointerShiftY:-10},
{value: 10},
{value: 12},
{value: 8},
{value: 11},
{value: 13},
{value: 19},
{value: 18},
{value: 22},
{value: 20},
{value: 28},
{value: 32},
{value: 36},
{value: 40},
{value: 38},
{value: 40},
{value: 42},
{value: 46},
{value: 44},
{value: 40},
{value: 36},
{value: 32},
{value: 38},
{value: 36},
{value: 32},
{value: 28},
{value: 22},
{value: 20},
];
// const lineData = [
@ -735,14 +735,17 @@ const App = () => {
// ];
const lineData1 = [
{value: 20},
{value: 20,pointerShiftX:10},
{value: 40},
{value: 48},
{value: 48, onPress:(item)=>Alert.alert(item.value+'')},
{value: 50},
{value: 56},
{value: 70},
{value: 90},
{value: 95},
{value: 70},
{value: 90},
{value: 95},
];
const lineData2 = [
{value: 0},
@ -812,21 +815,31 @@ const App = () => {
return (
<View
style={{
flex:1,
// flex: 1,
paddingVertical: 100,
backgroundColor:'black'
backgroundColor: 'black',
}}>
{/* <MyPattern/> */}
{/* <LineChart
<LineChart
data={lineData1}
// areaChart
yAxisSide='right'
horizontalRulesStyle={{zIndex:1}}
areaChart
// isAnimated
curved
dataPointsShape='rectangular'
// yAxisSide='right'
// pressEnabled
// showStripOnPress
// pointerConfig={{pointerStripColor: 'blue'}}
initialSpacing={0}
hideRules
// scrollToEnd
// horizontalRulesStyle={{zIndex:1}}
// backgroundColor='rgba(255,0,0,0.8)'
yAxisLabelContainerStyle={{zIndex:100000,elevation:1,textAlign:'left'}}
yAxisTextStyle={{fontWeight:'bold',marginRight:-110,zIndex:10000,textAlign:'left'}}
spacing={30}
/> */}
// yAxisLabelContainerStyle={{zIndex:100000,elevation:1,textAlign:'left'}}
// yAxisTextStyle={{fontWeight:'bold',marginRight:-110,zIndex:10000,textAlign:'left'}}
// spacing={30}
/>
{/* <BarChart
data={ldt}
maxValue={80}
@ -857,25 +870,26 @@ const App = () => {
/> */}
{/* <View style={{backgroundColor: '#1A3461'}}>
<LineChart
initialSpacing={0}
data={lineData}
curved
// isAnimated
spacing={30}
textColor1="yellow"
textShiftY={-8}
textShiftX={-10}
textFontSize={13}
thickness={5}
hideRules
hideYAxisText
yAxisColor="#0BA5A4"
showVerticalLines
verticalLinesColor="rgba(14,164,164,0.5)"
xAxisColor="#0BA5A4"
color="#0BA5A4"
initialSpacing={0}
data={lineData}
curved
// isAnimated
spacing={30}
textColor1="yellow"
textShiftY={-8}
textShiftX={-10}
textFontSize={13}
thickness={5}
hideRules
hideYAxisText
yAxisColor="#0BA5A4"
showVerticalLines
verticalLinesColor="rgba(14,164,164,0.5)"
xAxisColor="#0BA5A4"
color="#0BA5A4"
/>
</View> */}
</View> */}
{/* <PieChart data={[]} isThreeD shadow donut/> */}
<LineChart
data={lineData}
@ -883,6 +897,7 @@ const App = () => {
spacing={12}
areaChart
curved
isAnimated
hideDataPoints
startFillColor1="lightgreen"
endOpacity={0.6}

View File

@ -100,6 +100,7 @@ The properties of this line chart can be controlled using the `lineConfig` prop
| capColor | ColorValue | Color of the bar cap |
| capRadius | number | Border radius of the bar cap |
| barBorderRadius | number | Border radius of the bar |
| barMarginBottom | number | margin at the bottom of the bar (above X axis) |
| spacing | number | Distance of the next Bar from the currennt Bar |
| barBackgroundPattern | Component | A svg component containing the background pattern for bars |
| patternId | String | ID of the pattern component |
@ -192,6 +193,7 @@ type referenceConfigType = {
| activeOpacity | number | activeOpacity on pressing the bar | 0.2 |
| disablePress | Boolean | Prop to disable the bar press action | false |
| barBorderRadius | number | Border radius of the bar | 0 |
| barMarginBottom | number | margin at the bottom of the bar (above X axis) | 0 |
| barBackgroundPattern | Component | A svg component containing the background pattern for bars | \_ |
| patternId | String | ID of the pattern component | \_ |

View File

@ -50,6 +50,7 @@ So, all the three must be used together. Using any 1 or 2 of them may produce ab
| Key | Value type | Description |
| ------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| value | number | Value of the item representing representing its position |
| onPress | function | Function called on pressing the bar |
| label | string | Label text appearing under the X axis |
| labelTextStyle | object | Style object for the label text appearing under the X axis |
| labelComponent | Function | custom label component appearing under the X axis |
@ -66,7 +67,7 @@ So, all the three must be used together. Using any 1 or 2 of them may produce ab
| dataPointShape | string | Shape of the data point (rectangular or circular) defaults to circular |
| hideDataPoint | Boolean | To hide the data point |
| showVerticalLine | Boolean | When set to true, a vertical line will be displayed along that data point |
| verticalLineUptoDataPoint | Boolean | When set to true, it sets the height of the vertical line upto the corresponding data point |
| verticalLineUptoDataPoint | Boolean | When set to true, it sets the height of the vertical line upto the corresponding data point |
| verticalLineColor | ColorValue | Color of the vertical Line displayed along the data point |
| verticalLineThickness | number | Thickness of the vertical Line displayed along the data point |
| dataPointLabelWidth | number | width of the label shown beside a data point |
@ -79,6 +80,8 @@ So, all the three must be used together. Using any 1 or 2 of them may produce ab
| stripWidth | number | Width of the vertical strip that becomes visible on pressing the corresponding area of the chart, or when showStrip is set to true |
| stripColor | ColorValue | Color of the vertical strip that becomes visible on pressing the corresponding area of the chart, or when showStrip is set to true |
| stripOpacity | number | Opacity of the vertical strip that becomes visible on pressing the corresponding area of the chart, or when showStrip is set to true |
| pointerShiftX | number | Shifts the pointer for that item horizontally by given quantity (used only when pointerConfig prop is passed) |
| pointerShiftY | number | Shifts the pointer for that item vertically by given quantity (used only when pointerConfig prop is passed) |
**Alert**\
When you are using the `dataPointLabelComponent`, make sure to provide the `dataPointsHeight` and `dataPointsWidth` values too (either in the corresponding item object, or directly as a props of the <LineChart> component). Otherwise the data points might appear shifted from their intended positions.

View File

@ -1,6 +1,6 @@
{
"name": "react-native-gifted-charts",
"version": "1.0.16",
"version": "1.0.17",
"description": "The most complete library for Bar, Line, Area, Pie, Donut and Stacked Bar charts in React Native. Allows 2D, 3D, gradient, animations and live data updates.",
"main": "src/index.tsx",
"files": [

View File

@ -37,6 +37,7 @@ type propTypes = {
maxValue?: number;
barBackgroundPattern?: Function;
patternId?: String;
barMarginBottom?: number;
};
type itemType = {
value?: number;
@ -58,6 +59,7 @@ type itemType = {
barBorderRadius?: number;
barBackgroundPattern?: Function;
patternId?: String;
barMarginBottom?: number;
};
const Animated2DWithGradient = (props: propTypes) => {
@ -72,6 +74,7 @@ const Animated2DWithGradient = (props: propTypes) => {
noAnimation,
containerHeight,
maxValue,
barMarginBottom,
} = props;
const [height, setHeight] = useState(noAnimation ? props.height : 2);
const [initialRender, setInitialRender] = useState(
@ -114,18 +117,20 @@ const Animated2DWithGradient = (props: propTypes) => {
position: 'absolute',
bottom: 0,
width: '100%',
height: noAnimation
? (Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
: height,
height:
(noAnimation
? (Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
: height) - (barMarginBottom || 0),
}}>
<View
style={{
width: '100%',
height: noAnimation
? (Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
: height,
height:
(noAnimation
? (Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
: height) - (barMarginBottom || 0),
}}>
{noGradient ? (
<View
@ -168,7 +173,7 @@ const Animated2DWithGradient = (props: propTypes) => {
height:
item.capThickness === 0
? 0
: item.capThickness || props.capThickness || 0,
: item.capThickness || props.capThickness || 6,
backgroundColor:
item.capColor || props.capColor || 'black',
borderTopLeftRadius:
@ -223,12 +228,12 @@ const Animated2DWithGradient = (props: propTypes) => {
{props.cappedBars && (
<View
style={{
// position: 'absolute',
// width: '100%',
position: 'absolute',
width: '100%',
height:
item.capThickness === 0
? 0
: item.capThickness || props.capThickness || 0,
: item.capThickness || props.capThickness || 6,
backgroundColor:
item.capColor || props.capColor || 'black',
borderTopLeftRadius:

View File

@ -61,6 +61,7 @@ type Props = {
autoShiftLabels?: Boolean;
barBackgroundPattern?: Function;
patternId?: String;
barMarginBottom?: number;
};
type itemType = {
value?: number;
@ -87,6 +88,7 @@ type itemType = {
labelWidth?: number;
barBackgroundPattern?: Function;
patternId?: String;
barMarginBottom?: number;
};
const RenderBars = (props: Props) => {
const {
@ -108,6 +110,9 @@ const RenderBars = (props: Props) => {
autoShiftLabels,
} = props;
const barMarginBottom =
item.barMarginBottom === 0 ? 0 : props.barMarginBottom || 0;
const renderLabel = (label: String, labelTextStyle: any, value: number) => {
return (
<View
@ -313,17 +318,17 @@ const RenderBars = (props: Props) => {
style={[
{
// overflow: 'visible',
marginBottom: 60,
marginBottom: 60 + barMarginBottom,
width: item.barWidth || props.barWidth || 30,
height:
item.value >= 0 &&
(item.value >= 0 &&
(!isThreeD || isAnimated) &&
item.topLabelComponent
? (item.topLabelComponentHeight || 30) +
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
: (Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200),
(maxValue || 200)) - barMarginBottom,
marginRight: spacing,
},
item.value < 0 && {
@ -391,7 +396,8 @@ const RenderBars = (props: Props) => {
animationDuration={animationDuration || 800}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
(maxValue || 200) -
barMarginBottom
}
intactTopLabel={props.intactTopLabel}
horizontal={props.horizontal}
@ -423,7 +429,8 @@ const RenderBars = (props: Props) => {
intactTopLabel={props.intactTopLabel}
height={
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
(maxValue || 200) -
barMarginBottom
}
value={item.value}
/>
@ -447,6 +454,7 @@ const RenderBars = (props: Props) => {
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
}
barMarginBottom={barMarginBottom}
cappedBars={props.cappedBars}
capThickness={props.capThickness}
capColor={props.capColor}
@ -477,6 +485,7 @@ const RenderBars = (props: Props) => {
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
}
barMarginBottom={barMarginBottom}
cappedBars={props.cappedBars}
capThickness={props.capThickness}
capColor={props.capColor}
@ -505,6 +514,7 @@ const RenderBars = (props: Props) => {
(Math.abs(item.value) * (containerHeight || 200)) /
(maxValue || 200)
}
barMarginBottom={barMarginBottom}
cappedBars={props.cappedBars}
capThickness={props.capThickness}
capColor={props.capColor}

View File

@ -127,6 +127,7 @@ type PropTypes = {
labelsExtraHeight?: number;
barBackgroundPattern?: Function;
patternId?: String;
barMarginBottom?: number;
};
type lineConfigType = {
initialSpacing?: number;
@ -1269,6 +1270,7 @@ export const BarChart = (props: PropTypes) => {
autoShiftLabels={autoShiftLabels}
barBackgroundPattern={props.barBackgroundPattern}
patternId={props.patternId}
barMarginBottom={props.barMarginBottom}
/>
))}
</Fragment>

View File

@ -304,6 +304,9 @@ type itemType = {
verticalLineUptoDataPoint?: Boolean;
verticalLineColor?: string;
verticalLineThickness?: number;
pointerShiftX?: number;
pointerShiftY?: number;
onPress?: Function;
};
type sectionType = {
@ -332,7 +335,10 @@ export const LineChart = (props: propTypes) => {
const scrollRef = useRef();
const [pointerX, setPointerX] = useState(0);
const [pointerY, setPointerY] = useState(0);
const [pointerItem, setPointerItem] = useState({});
const [pointerItem, setPointerItem] = useState({
pointerShiftX: 0,
pointerShiftY: 0,
});
const [points, setPoints] = useState('');
const [points2, setPoints2] = useState('');
const [points3, setPoints3] = useState('');
@ -408,6 +414,63 @@ export const LineChart = (props: propTypes) => {
newFillPoints = '';
let counter = 0;
const initialSpacing =
props.initialSpacing === 0 ? 0 : props.initialSpacing || 40;
const thickness = props.thickness || 2;
const spacing = props.spacing === 0 ? 0 : props.spacing || 60;
const xAxisThickness = props.xAxisThickness || 1;
const dataPointsHeight1 =
props.dataPointsHeight1 || props.dataPointsHeight || 2;
const dataPointsWidth1 = props.dataPointsWidth1 || props.dataPointsWidth || 2;
const dataPointsRadius1 =
props.dataPointsRadius1 || props.dataPointsRadius || 3;
const dataPointsColor1 =
props.dataPointsColor1 || props.dataPointsColor || 'black';
const dataPointsShape1 =
props.dataPointsShape1 || props.dataPointsShape || 'circular';
const dataPointsHeight2 =
props.dataPointsHeight2 || props.dataPointsHeight || 2;
const dataPointsWidth2 = props.dataPointsWidth2 || props.dataPointsWidth || 2;
const dataPointsRadius2 =
props.dataPointsRadius2 || props.dataPointsRadius || 3;
const dataPointsColor2 =
props.dataPointsColor2 || props.dataPointsColor || 'blue';
const dataPointsShape2 =
props.dataPointsShape2 || props.dataPointsShape || 'circular';
const dataPointsHeight3 =
props.dataPointsHeight3 || props.dataPointsHeight || 2;
const dataPointsWidth3 = props.dataPointsWidth3 || props.dataPointsWidth || 2;
const dataPointsRadius3 =
props.dataPointsRadius3 || props.dataPointsRadius || 3;
const dataPointsColor3 =
props.dataPointsColor3 || props.dataPointsColor || 'red';
const dataPointsShape3 =
props.dataPointsShape3 || props.dataPointsShape || 'circular';
const dataPointsHeight4 =
props.dataPointsHeight4 || props.dataPointsHeight || 2;
const dataPointsWidth4 = props.dataPointsWidth4 || props.dataPointsWidth || 2;
const dataPointsRadius4 =
props.dataPointsRadius4 || props.dataPointsRadius || 3;
const dataPointsColor4 =
props.dataPointsColor4 || props.dataPointsColor || 'red';
const dataPointsShape4 =
props.dataPointsShape4 || props.dataPointsShape || 'circular';
const dataPointsHeight5 =
props.dataPointsHeight5 || props.dataPointsHeight || 2;
const dataPointsWidth5 = props.dataPointsWidth5 || props.dataPointsWidth || 2;
const dataPointsRadius5 =
props.dataPointsRadius5 || props.dataPointsRadius || 3;
const dataPointsColor5 =
props.dataPointsColor5 || props.dataPointsColor || 'red';
const dataPointsShape5 =
props.dataPointsShape5 || props.dataPointsShape || 'circular';
if (animateOnDataChange) {
animations.forEach((item, index) => {
item.addListener(val => {
@ -548,56 +611,6 @@ export const LineChart = (props: propTypes) => {
}, [animationDuration, widthValue5]);
const areaChart = props.areaChart || false;
const dataPointsHeight1 =
props.dataPointsHeight1 || props.dataPointsHeight || 2;
const dataPointsWidth1 = props.dataPointsWidth1 || props.dataPointsWidth || 2;
const dataPointsRadius1 =
props.dataPointsRadius1 || props.dataPointsRadius || 3;
const dataPointsColor1 =
props.dataPointsColor1 || props.dataPointsColor || 'black';
const dataPointsShape1 =
props.dataPointsShape1 || props.dataPointsShape || 'circular';
const dataPointsHeight2 =
props.dataPointsHeight2 || props.dataPointsHeight || 2;
const dataPointsWidth2 = props.dataPointsWidth2 || props.dataPointsWidth || 2;
const dataPointsRadius2 =
props.dataPointsRadius2 || props.dataPointsRadius || 3;
const dataPointsColor2 =
props.dataPointsColor2 || props.dataPointsColor || 'blue';
const dataPointsShape2 =
props.dataPointsShape2 || props.dataPointsShape || 'circular';
const dataPointsHeight3 =
props.dataPointsHeight3 || props.dataPointsHeight || 2;
const dataPointsWidth3 = props.dataPointsWidth3 || props.dataPointsWidth || 2;
const dataPointsRadius3 =
props.dataPointsRadius3 || props.dataPointsRadius || 3;
const dataPointsColor3 =
props.dataPointsColor3 || props.dataPointsColor || 'red';
const dataPointsShape3 =
props.dataPointsShape3 || props.dataPointsShape || 'circular';
const dataPointsHeight4 =
props.dataPointsHeight4 || props.dataPointsHeight || 2;
const dataPointsWidth4 = props.dataPointsWidth4 || props.dataPointsWidth || 2;
const dataPointsRadius4 =
props.dataPointsRadius4 || props.dataPointsRadius || 3;
const dataPointsColor4 =
props.dataPointsColor4 || props.dataPointsColor || 'red';
const dataPointsShape4 =
props.dataPointsShape4 || props.dataPointsShape || 'circular';
const dataPointsHeight5 =
props.dataPointsHeight5 || props.dataPointsHeight || 2;
const dataPointsWidth5 = props.dataPointsWidth5 || props.dataPointsWidth || 2;
const dataPointsRadius5 =
props.dataPointsRadius5 || props.dataPointsRadius || 3;
const dataPointsColor5 =
props.dataPointsColor5 || props.dataPointsColor || 'red';
const dataPointsShape5 =
props.dataPointsShape5 || props.dataPointsShape || 'circular';
const textFontSize1 = props.textFontSize1 || props.textFontSize || 10;
const textFontSize2 = props.textFontSize2 || props.textFontSize || 10;
const textFontSize3 = props.textFontSize3 || props.textFontSize || 10;
@ -608,13 +621,6 @@ export const LineChart = (props: propTypes) => {
const textColor3 = props.textColor3 || props.textColor || 'gray';
const textColor4 = props.textColor4 || props.textColor || 'gray';
const textColor5 = props.textColor5 || props.textColor || 'gray';
const initialSpacing =
props.initialSpacing === 0 ? 0 : props.initialSpacing || 40;
const thickness = props.thickness || 2;
const spacing = props.spacing === 0 ? 0 : props.spacing || 60;
const xAxisThickness = props.xAxisThickness || 1;
const xAxisColor = props.xAxisColor || 'black';
let totalWidth = initialSpacing;
@ -1936,6 +1942,9 @@ export const LineChart = (props: propTypes) => {
: 'none'
: dataPointsColor
}
onPress={() => {
item.onPress ? item.onPress(item, index) : null;
}}
/>
)}
</Fragment>
@ -1957,6 +1966,9 @@ export const LineChart = (props: propTypes) => {
: 'none'
: dataPointsColor
}
onPress={() => {
item.onPress ? item.onPress(item, index) : null;
}}
/>
)}
</Fragment>
@ -2054,14 +2066,22 @@ export const LineChart = (props: propTypes) => {
<View
style={{
position: 'absolute',
height: pointerHeight || pointerRadius * 2,
width: pointerWidth || pointerRadius * 2,
backgroundColor: pointerColor,
borderRadius: pointerRadius || 0,
left: pointerX,
left: pointerX + (pointerItem.pointerShiftX || 0),
top: pointerY,
}}>
{pointerComponent ? pointerComponent() : null}
{pointerComponent ? (
pointerComponent()
) : (
<View
style={{
height: pointerHeight || pointerRadius * 2,
width: pointerWidth || pointerRadius * 2,
marginTop: pointerItem.pointerShiftY || 0,
backgroundColor: pointerColor,
borderRadius: pointerRadius || 0,
}}
/>
)}
{showPointerStrip && (
<View
style={{
@ -2124,7 +2144,7 @@ export const LineChart = (props: propTypes) => {
onResponderGrant={evt => {
if (!pointerConfig) return;
let x = evt.nativeEvent.locationX;
let factor = x / (initialSpacing + spacing);
let factor = (x - initialSpacing) / spacing;
factor = Math.round(factor);
factor = Math.min(factor, data.length - 1);
factor = Math.max(factor, 0);
@ -2146,7 +2166,7 @@ export const LineChart = (props: propTypes) => {
onResponderMove={evt => {
if (!pointerConfig) return;
let x = evt.nativeEvent.locationX;
let factor = x / (initialSpacing + spacing);
let factor = (x - initialSpacing) / spacing;
factor = Math.round(factor);
factor = Math.min(factor, data.length - 1);
factor = Math.max(factor, 0);
@ -2331,7 +2351,7 @@ export const LineChart = (props: propTypes) => {
onResponderGrant={evt => {
if (!pointerConfig) return;
let x = evt.nativeEvent.locationX;
let factor = x / (initialSpacing + spacing);
let factor = (x - initialSpacing) / spacing;
factor = Math.round(factor);
factor = Math.min(factor, data.length - 1);
factor = Math.max(factor, 0);
@ -2353,7 +2373,7 @@ export const LineChart = (props: propTypes) => {
onResponderMove={evt => {
if (!pointerConfig) return;
let x = evt.nativeEvent.locationX;
let factor = x / (initialSpacing + spacing);
let factor = (x - initialSpacing) / spacing;
factor = Math.round(factor);
factor = Math.min(factor, data.length - 1);
factor = Math.max(factor, 0);