gitee.com/quant1x/num@v0.3.2/slope.go (about) 1 package num 2 3 import "math" 4 5 const ( 6 // 1度=多少弧度 7 radiansPreDegrees = math.Pi / 180.00 8 // 1弧度=多少度 9 degreesPreRadian = 180.00 / math.Pi 10 ) 11 12 // Slope 计算斜率 13 func Slope(x1 int, y1 float64, x2 int, y2 float64) float64 { 14 return (y2 - y1) / float64(x2-x1) 15 } 16 17 // TriangleBevel 三角形斜边 18 func TriangleBevel(slope float64, x1 int, y1 float64, xn int) float64 { 19 return slope*float64(xn-x1) + y1 20 } 21 22 // degreesToRadian 角度转弧度 23 // 24 // 弧度=角度÷180×π 25 func degreesToRadian(degrees float64) float64 { 26 // degrees * math.Pi / 180 27 return degrees * radiansPreDegrees 28 } 29 30 // radianToDegrees 弧度转角度 31 // 32 // 角度=弧度×180÷π 33 func radianToDegrees(radian float64) float64 { 34 // (radian * 180) / math.Pi 35 return radian * degreesPreRadian 36 } 37 38 // DegreesToSlope 角度转斜率 39 func DegreesToSlope(x float64) float64 { 40 radians := degreesToRadian(x) 41 return math.Tan(radians) 42 } 43 44 // SlopeToDegrees 斜率转角度 45 func SlopeToDegrees(m float64) float64 { 46 // math.Atan(m) * 180 / math.Pi 47 radians := math.Atan(m) 48 degrees := radianToDegrees(radians) 49 return degrees 50 } 51 52 // Point 点 53 type Point struct { 54 X float64 // x 轴 55 Y float64 // y 轴 56 } 57 58 func NewPoint[T1 Number, T2 Number](x T1, y T2) Point { 59 return Point{X: float64(x), Y: float64(y)} 60 } 61 62 func (this Point) Add(p Point) Point { 63 return Point{X: this.X + p.X, Y: this.Y + p.Y} 64 } 65 66 func (this Point) ToDataPoint() DataPoint { 67 return DataPoint{X: int(this.X), Y: this.Y} 68 } 69 70 // DataPoint 数据点, X轴为时间类切片的索引, Y轴为具体数值 71 type DataPoint struct { 72 X int // Y切片的索引 73 Y float64 // 值 74 } 75 76 func (this DataPoint) ToPoint() Point { 77 return Point{X: float64(this.X), Y: this.Y} 78 } 79 80 // Line LineEquation 线性方程式 81 // 82 // ① Ax + By + C = 0 83 // ② 斜率k = -(A/B) 84 // ③ x截距 = -(C/A), y截距 = -(C/B) 85 type Line struct { 86 slope float64 // 斜率, k 87 intercept float64 // 截距, b 88 offset float64 // 偏移量, 左边点的x周期起始点 89 } 90 91 // CalculateLineEquation 已知两个点, 计算线性方程式 92 func CalculateLineEquation(point1, point2 Point) Line { 93 m := (point2.Y - point1.Y) / (point2.X - point1.X) 94 c := point1.Y - m*point1.X 95 96 line := Line{slope: m, intercept: c, offset: point1.X} 97 return line 98 } 99 100 // Radian 弧度 101 func (this Line) Radian() float64 { 102 return math.Atan(this.slope) 103 } 104 105 // Degrees 计算角度 106 func (this Line) Degrees() float64 { 107 return this.Radian() * degreesPreRadian 108 } 109 110 // Angle 计算两条直线之间的角度 111 func (this Line) Angle(other Line) float64 { 112 return this.v3Angle(other) 113 } 114 115 func (this Line) v1Angle(other Line) float64 { 116 theta := math.Atan(other.slope-this.slope) * degreesPreRadian 117 return theta 118 } 119 120 func (this Line) v2Angle(other Line) float64 { 121 m := other.slope * this.slope 122 if m == -1 { 123 if other.slope < 0 { 124 return -90 125 } else { 126 return 90 127 } 128 } 129 theta := math.Atan((other.slope-this.slope)/(1+m)) * degreesPreRadian 130 return theta 131 } 132 133 func (this Line) v3Angle(other Line) float64 { 134 angle1 := other.Degrees() 135 angle2 := this.Degrees() 136 theta := angle1 - angle2 137 return theta 138 } 139 140 // Equation 返回方程式 A, B, C 141 func (this Line) Equation() (A, B, C float64) { 142 A = this.slope 143 B = -1.0 144 C = this.intercept 145 return 146 } 147 148 // Distance 点到线的距离 149 // 150 // ___________ 151 // 公式 |(Ax0 + By0 + C)| / √(A^2 + B^2) 152 func (this Line) Distance(p Point) float64 { 153 A, B, C := this.Equation() 154 numerator := A*p.X + B*p.Y + C 155 //numerator = math.Abs(numerator) 156 denominator := math.Sqrt(A*A + B*B) 157 distance := numerator / denominator 158 return distance 159 } 160 161 // HorizontalDistance 水平方向距离 162 // 163 // p点到线的水平方向上的距离 164 func (this Line) HorizontalDistance(p Point) float64 { 165 // 计算水平方向相交点, Y轴相等 166 x := this.X(p.Y) 167 return x - p.X 168 } 169 170 // VerticalDistance 垂直方向距离 171 // 172 // p点到线的垂直方向上的距离 173 func (this Line) VerticalDistance(p Point) float64 { 174 y := this.Y(p.X) 175 return y - p.Y 176 } 177 178 // ShortestDistance 计算 与另外一条直线的最短距离 179 func (this Line) ShortestDistance(other Line) float64 { 180 distance := (other.intercept - this.intercept) / math.Sqrt(1+this.slope*this.slope) 181 return distance 182 } 183 184 // ParallelLine 通过 一个点的y轴坐标计算一个新的平行线 185 func (this Line) ParallelLine(p Point) Line { 186 newIntercept := p.Y - this.slope*p.X 187 return Line{slope: this.slope, intercept: newIntercept, offset: p.X} 188 } 189 190 // X 通过 y 轴坐标计算 x轴坐标 191 func (this Line) X(y float64) float64 { 192 x := (y - this.intercept) / this.slope 193 return x 194 } 195 196 // Y 通过 x 轴坐标计算 y轴坐标 197 func (this Line) Y(x float64) float64 { 198 y := this.slope*x + this.intercept 199 return y 200 } 201 202 // Incr 增量方式得出line延伸的x轴和y轴 203 func (this Line) Incr(n int) (x, y float64) { 204 xAxis := this.offset + float64(n) 205 yAxis := this.Y(xAxis) 206 return xAxis, yAxis 207 } 208 209 // SymmetricParallelLine 计算 点对称(等距离)的平行线 210 func (this Line) SymmetricParallelLine(p Point) Line { 211 return this.v2SymmetricParallelLine(p) 212 } 213 214 // SymmetricParallelLine 计算 点对称(等距离)的平行线 215 func (this Line) v1SymmetricParallelLine(p Point) Line { 216 // 1. 确定点p到line的距离 217 distance := this.Distance(p) 218 // 2. 规划直角三角形 219 // 2.1 斜边 Hypotenuse 220 hypotenuse := distance 221 // 2.2 计算 斜边于底边的角度 222 lineDegrees := this.Degrees() 223 alpha := 180 - lineDegrees 224 radian := degreesToRadian(alpha) 225 // 2.3 底边 x, Opposite side 226 opposite := hypotenuse * math.Sin(radian) 227 // 2.4 邻边 y, Adjacent side 228 adjacent := hypotenuse * math.Cos(radian) 229 // 3. 计算新的平行线 230 newPoint := p.Add(Point{X: opposite, Y: adjacent}) 231 newLine := this.ParallelLine(newPoint) 232 //// 验证2x距离 233 //d := this.Distance(newPoint) 234 //_ = d 235 return newLine 236 } 237 238 // SymmetricParallelLine 计算 点对称(等距离)的平行线 239 func (this Line) v2SymmetricParallelLine(p Point) Line { 240 // 1. 确定点p到line的垂直距离, 即以p的x轴确定line的y坐标 241 distance := this.VerticalDistance(p) 242 // 2. 确定平行线的点 243 newPoint := p.Add(Point{X: 0, Y: -distance}) 244 // 3. 计算新的平行线 245 newLine := this.ParallelLine(newPoint) 246 //// 验证2x距离 247 //d := this.Distance(newPoint) 248 //_ = d 249 return newLine 250 } 251 252 // 趋势变化 253 const ( 254 TendencyUnchanged = 0 // 趋势不变 255 TendencyBreakThrough = 1 // break through 突破 256 TendencyFallDrastically = -1 // fall drastically 跌破 257 ) 258 259 // Extend 延伸线 260 func (this Line) Extend(data []float64, digits int) (X, Y []float64, tendency int) { 261 offset := int(this.offset) 262 count := len(data) 263 length := count - offset 264 x := make([]float64, length) 265 y := make([]float64, length) 266 for i := 0; i < length; i++ { 267 x[i] = float64(i + offset) 268 y[i] = this.Y(x[i]) 269 y[i] = Decimal(y[i], digits) 270 } 271 X = x 272 Y = y 273 tendency = TendencyUnchanged 274 if length >= 2 { 275 d1 := data[count-1] 276 d2 := data[count-2] 277 y1 := y[length-1] 278 y2 := y[length-2] 279 if d2 < y2 && d1 > y1 { 280 tendency = TendencyBreakThrough 281 } else if d2 > y2 && d1 < y1 { 282 tendency = TendencyFallDrastically 283 } 284 } 285 return 286 } 287 288 // Analyze 分析线性趋势 289 func (this Line) Analyze(data []float64, digits int) (X, Y []float64, tendency []LinerTrend) { 290 offset := int(this.offset) 291 count := len(data) 292 length := count - offset 293 x := make([]float64, length) 294 y := make([]float64, length) 295 for i := 0; i < length; i++ { 296 x[i] = float64(i + offset) 297 y[i] = this.Y(x[i]) 298 y[i] = Decimal(y[i], digits) 299 } 300 X = x 301 Y = y 302 tendency = Cross(data[offset:], y) 303 return 304 }