go-hep.org/x/hep@v0.38.1/hbook/binning2d.go (about) 1 // Copyright ©2016 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package hbook 6 7 import "sort" 8 9 // indices for the 2D-binning overflows 10 const ( 11 BngNW int = 1 + iota 12 BngN 13 BngNE 14 BngE 15 BngSE 16 BngS 17 BngSW 18 BngW 19 ) 20 21 type Binning2D struct { 22 Bins []Bin2D 23 Dist Dist2D 24 Outflows [8]Dist2D 25 XRange Range 26 YRange Range 27 Nx int 28 Ny int 29 XEdges []Bin1D 30 YEdges []Bin1D 31 } 32 33 func newBinning2D(nx int, xlow, xhigh float64, ny int, ylow, yhigh float64) Binning2D { 34 if xlow >= xhigh { 35 panic(errInvalidXAxis) 36 } 37 if ylow >= yhigh { 38 panic(errInvalidYAxis) 39 } 40 if nx <= 0 { 41 panic(errEmptyXAxis) 42 } 43 if ny <= 0 { 44 panic(errEmptyYAxis) 45 } 46 bng := Binning2D{ 47 Bins: make([]Bin2D, nx*ny), 48 XRange: Range{Min: xlow, Max: xhigh}, 49 YRange: Range{Min: ylow, Max: yhigh}, 50 Nx: nx, 51 Ny: ny, 52 XEdges: make([]Bin1D, nx), 53 YEdges: make([]Bin1D, ny), 54 } 55 xwidth := bng.XRange.Width() / float64(bng.Nx) 56 ywidth := bng.YRange.Width() / float64(bng.Ny) 57 xmin := bng.XRange.Min 58 ymin := bng.YRange.Min 59 for ix := range bng.XEdges { 60 xbin := &bng.XEdges[ix] 61 xbin.Range.Min = xmin + float64(ix)*xwidth 62 xbin.Range.Max = xmin + float64(ix+1)*xwidth 63 for iy := range bng.YEdges { 64 ybin := &bng.YEdges[iy] 65 ybin.Range.Min = ymin + float64(iy)*ywidth 66 ybin.Range.Max = ymin + float64(iy+1)*ywidth 67 i := iy*nx + ix 68 bin := &bng.Bins[i] 69 bin.XRange.Min = xbin.Range.Min 70 bin.XRange.Max = xbin.Range.Max 71 bin.YRange.Min = ybin.Range.Min 72 bin.YRange.Max = ybin.Range.Max 73 } 74 } 75 return bng 76 } 77 78 func newBinning2DFromEdges(xedges, yedges []float64) Binning2D { 79 if len(xedges) <= 1 { 80 panic(errShortXAxis) 81 } 82 if !sort.IsSorted(sort.Float64Slice(xedges)) { 83 panic(errNotSortedXAxis) 84 } 85 if len(yedges) <= 1 { 86 panic(errShortYAxis) 87 } 88 if !sort.IsSorted(sort.Float64Slice(yedges)) { 89 panic(errNotSortedYAxis) 90 } 91 var ( 92 nx = len(xedges) - 1 93 ny = len(yedges) - 1 94 xlow = xedges[0] 95 xhigh = xedges[nx] 96 ylow = yedges[0] 97 yhigh = yedges[ny] 98 ) 99 bng := Binning2D{ 100 Bins: make([]Bin2D, nx*ny), 101 XRange: Range{Min: xlow, Max: xhigh}, 102 YRange: Range{Min: ylow, Max: yhigh}, 103 Nx: nx, 104 Ny: ny, 105 XEdges: make([]Bin1D, nx), 106 YEdges: make([]Bin1D, ny), 107 } 108 for ix, xmin := range xedges[:nx] { 109 xmax := xedges[ix+1] 110 if xmin == xmax { 111 panic(errDupEdgesXAxis) 112 } 113 bng.XEdges[ix].Range.Min = xmin 114 bng.XEdges[ix].Range.Max = xmax 115 for iy, ymin := range yedges[:ny] { 116 ymax := yedges[iy+1] 117 if ymin == ymax { 118 panic(errDupEdgesYAxis) 119 } 120 i := iy*nx + ix 121 bin := &bng.Bins[i] 122 bin.XRange.Min = xmin 123 bin.XRange.Max = xmax 124 bin.YRange.Min = ymin 125 bin.YRange.Max = ymax 126 } 127 } 128 for iy, ymin := range yedges[:ny] { 129 ymax := yedges[iy+1] 130 bng.YEdges[iy].Range.Min = ymin 131 bng.YEdges[iy].Range.Max = ymax 132 } 133 return bng 134 } 135 136 func (bng *Binning2D) entries() int64 { 137 return bng.Dist.Entries() 138 } 139 140 func (bng *Binning2D) effEntries() float64 { 141 return bng.Dist.EffEntries() 142 } 143 144 // xMin returns the low edge of the X-axis 145 func (bng *Binning2D) xMin() float64 { 146 return bng.XRange.Min 147 } 148 149 // xMax returns the high edge of the X-axis 150 func (bng *Binning2D) xMax() float64 { 151 return bng.XRange.Max 152 } 153 154 // yMin returns the low edge of the Y-axis 155 func (bng *Binning2D) yMin() float64 { 156 return bng.YRange.Min 157 } 158 159 // yMax returns the high edge of the Y-axis 160 func (bng *Binning2D) yMax() float64 { 161 return bng.YRange.Max 162 } 163 164 func (bng *Binning2D) fill(x, y, w float64) { 165 idx := bng.coordToIndex(x, y) 166 bng.Dist.fill(x, y, w) 167 if idx == len(bng.Bins) { 168 // GAP bin 169 return 170 } 171 if idx < 0 { 172 bng.Outflows[-idx-1].fill(x, y, w) 173 return 174 } 175 bng.Bins[idx].fill(x, y, w) 176 } 177 178 func (bng *Binning2D) coordToIndex(x, y float64) int { 179 ix := Bin1Ds(bng.XEdges).IndexOf(x) 180 iy := Bin1Ds(bng.YEdges).IndexOf(y) 181 182 switch { 183 case ix == bng.Nx && iy == bng.Ny: // GAP 184 return len(bng.Bins) 185 case ix == OverflowBin1D && iy == OverflowBin1D: 186 return -BngNE 187 case ix == OverflowBin1D && iy == UnderflowBin1D: 188 return -BngSE 189 case ix == UnderflowBin1D && iy == UnderflowBin1D: 190 return -BngSW 191 case ix == UnderflowBin1D && iy == OverflowBin1D: 192 return -BngNW 193 case ix == OverflowBin1D: 194 return -BngE 195 case ix == UnderflowBin1D: 196 return -BngW 197 case iy == OverflowBin1D: 198 return -BngN 199 case iy == UnderflowBin1D: 200 return -BngS 201 } 202 return iy*bng.Nx + ix 203 }