// DrawLine only writes to the slot label. // The pointer to the amorph remains unchanged // Empty() still reports true for the slot func (m *TransposableMatrix) DrawLine(line []Point, suffix string, pivotPointsOnly bool) { for i := 0; i < len(line); i++ { s := spf("%v%v", i%10, suffix) if suffix == "" { s = "" } if pivotPointsOnly || i == len(line)-1 { m.SetLabel(line[i].x, line[i].y, Slot{Label: s}) } else { x := util.Min(line[i+1].x, line[i].x) y := util.Min(line[i+1].y, line[i].y) dx := util.Abs(line[i+1].x - line[i].x) dy := util.Abs(line[i+1].y - line[i].y) // pf("sect :%v %v %v %v \n", x, x+dx, y, y+dy) for j := x; j <= x+dx; j++ { for k := y; k <= y+dy; k++ { m.SetLabel(j, k, Slot{Label: s}) } } } } }
// // exactStairyEdge returns amorphs // with exactly the desired edges. // The edge is also attached to the amorph. // Param limit restricts the amount of amorphs returned. func (ar *Reservoir) exactStairyEdge(x1, y, x2 int, limit int) (amorphs []Amorph) { enc := Enc(x1, y, x2) if _, ok := ar.Edge3[enc]; ok { mp := ar.Edge3[enc] for amIdx, _ := range mp { // pf("found idx %v of %v \n", amIdx, len(ar.Amorphs)) lp := ar.Amorphs[amIdx] // effects a copying of the amorph lp.Cols = x1 + x2 lp.Rows, lp.Slack = OtherSide(lp.NElements, lp.Cols) // Increase rows, // if the requested edge is a superedge. cntr := 0 for lp.Rows <= util.Abs(y) || lp.Slack < util.Abs(x2*y) { lp.Rows++ lp.Slack = (lp.Cols * lp.Rows) - lp.NElements if cntr++; cntr > 100 { panic("superedge blowup logic faulty") } } // Attach the edge lp.Edge = []int{x1, y, x2} amorphs = append(amorphs, lp) if len(amorphs) >= limit { return } } } return }
// returns a format string with as few as needed // post-decimal digits ; 1000 => 1000 , but 0.0400 => 0.04 func practicalFormat(mv float64) (floatFormat string, exponent int) { //Log x = Ln x / Ln 10 fExponent := math.Log(mv) / math.Log(10) //exponent = int(fExponent) exponent = util.Round(fExponent) sExponent := fmt.Sprint(util.Abs(exponent) + 2) floatFormat = "%12.0f" if mv < 10 { floatFormat = "%12.1f" } if mv < 1 { floatFormat = "%12." + sExponent + "f" } return }
func (m *TransposableMatrix) renderHeaderTB(xscale, yscale int) { xo1 := RectOutp2.X1 xo2 := RectOutp2.X2 yo1 := RectOutp2.Y1 yo2 := RectOutp2.Y2 xoo := xo1 + margL yoo := yo1 + margT // row1 tboxc.PriLineAt(xo1, yo1+0, " x|") tboxc.PriLineAt(xo1, yo1+1, " |") for x := xoo; x <= xo2-2; x += 2 { if xscale%10 == 0 { colHead1 := spf("%2d", (xscale-xscale%10)/10) tboxc.PriLineAt(x, yo1+0, colHead1) } else { tboxc.PriLineAt(x, yo1+0, " ") } colHead2 := spf("%2d", util.Abs(xscale%10)) tboxc.PriLineAt(x, yo1+1, colHead2) xscale += 1 } // row3 tboxc.PriLineAt(xo1, yo1+2, " y|") tboxc.PriLineAt(xoo, yo1+2, spf(strings.Repeat("=", xo2-xo1-margL+1))) for y := yoo; y <= yo2; y++ { tboxc.PriLineAt(xo1, y, spf("%3d|", yscale)) yscale++ } }
// Distance returns levenshtein edit distance for the two slices of tokens of m. func (m *Matrix) Distance() (int, float64) { dist := m.mx[len(m.mx)-1][len(m.mx[0])-1] relDist := 0.0 ls1, ls2 := len(m.mx), len(m.mx[0]) // First relDist computation: // 1.) compensated for the size // 2.) related to the smaller slice // Can lead to Zero, when diff == dist diff := util.Abs(ls1 - ls2) if ls1 >= ls2 { // row > col relDist = float64(dist-diff) / float64(ls2) } else { relDist = float64(dist-diff) / float64(ls1) } // Second relDist: Simply related to the larger slice. // Also account for ls1 and ls2 being one larger than the practical number of tokens. divisor := float64(ls1) if ls2 > ls1 { // row > col divisor = float64(ls2) } divisor-- if divisor == 0.0 { divisor = 1.0 } relDist = float64(dist) / divisor if relDist == 0.25 { fmt.Printf("dist %v - ls1 %v - relDist %5.2v\n", dist, divisor, relDist) } return dist, relDist }
// https://courses.engr.illinois.edu/ece390/archive/archive-f2000/mp/mp4/anti.html func FuncDrawLiner(lCol color.RGBA, img *image.RGBA) func(P_next image.Point, lCol color.RGBA, img *image.RGBA) { var P_last image.Point = image.Point{-1111, -1111} r := img.Rect p0 := r.Min p1 := r.Max imgWidth := p1.X - p0.X return func(P_next image.Point, lCol color.RGBA, img *image.RGBA) { var P0, P1 image.Point if P_last.X == -1111 && P_last.Y == -1111 { P_last = P_next return } else { P0 = P_last P1 = P_next P_last = P_next } log.Printf("draw_line_start---------------------------------") x0, y0 := P0.X, P0.Y x1, y1 := P1.X, P1.Y bpp := 4 // bytes per pixel addr := (y0*imgWidth + x0) * bpp dx := x1 - x0 dy := y1 - y0 var du, dv, u, v int var uincr int = bpp var vincr int = imgWidth * bpp // switching to (u,v) to combine all eight octants if util.Abs(dx) > util.Abs(dy) { du = util.Abs(dx) dv = util.Abs(dy) u = x1 v = y1 uincr = bpp vincr = imgWidth * bpp if dx < 0 { uincr = -uincr } if dy < 0 { vincr = -vincr } } else { du = util.Abs(dy) dv = util.Abs(dx) u = y1 v = x1 uincr = imgWidth * bpp vincr = bpp if dy < 0 { uincr = -uincr } if dx < 0 { vincr = -vincr } } log.Printf("draw_line\tu %v - v %v - du %v - dv %v - uinc %v - vinc %v ", u, v, du, dv, uincr, vincr) // uend := u + 2 * du // d := (2 * dv) - du // Initial value as in Bresenham's // incrS := 2 * dv // Δd for straight increments // incrD := 2 * (dv - du) // Δd for diagonal increments // twovdu := 0 // Numerator of distance starts at 0 // I have NO idea why - but unless I use -1- // instead of the orginal -2- as factor, // all lines are drawn DOUBLE the intended size // THIS is how it works for me: uend := u + 1*du d := (1 * dv) - du // Initial value as in Bresenham's incrS := 1 * dv // Δd for straight increments incrD := 1 * (dv - du) // Δd for diagonal increments twovdu := 0 // Numerator of distance starts at 0 log.Printf("draw_line\tuend %v - d %v - incrS %v - incrD %v - twovdu %v", uend, d, incrS, incrD, twovdu) tmp := float64(du*du + dv*dv) invD := 1.0 / (2.0 * math.Sqrt(tmp)) /* Precomputed inverse denominator */ invD2du := 2.0 * (float64(du) * invD) /* Precomputed constant */ log.Printf("draw_line\tinvD %v - invD2du %v", invD, invD2du) cntr := -1 setPix := funcSetPixler(lCol, img) for { cntr++ //log.Printf("==lp%v ", cntr ) // Ensure that addr is valid ftwovdu := float64(twovdu) setPix(addr-vincr, invD2du+ftwovdu*invD) setPix(addr, ftwovdu*invD) setPix(addr+vincr, invD2du-ftwovdu*invD) if d < 0 { /* choose straight (u direction) */ twovdu = d + du d = d + incrS } else { /* choose diagonal (u+v direction) */ twovdu = d - du d = d + incrD v = v + 1 addr = addr + vincr } u = u + 1 addr = addr + uincr if u > uend { break } } log.Printf("draw_line_end---------------------------------") } }
// FuseTwoSections takes an outline with all concavest lowest narrowest sections, // and one or two designated section indize out of clns. // So far mostly *one* section is given, while the second is the // computed right neighbor. Rightmost sect is complemented by the left neighbor. // Those designated sections are fused. // Param l => outline // Param clns => concavest, lowest, narrowest sections // sct1 => index to clns for which to find a pair // Returns: Fusion func (ar *Reservoir) FuseTwoSections(l []Point, clns [][]int, sct1 []int) (Fusion, error) { fs := NewFusion() var err error // find the two sections - westward or eastward west, east := findYourNeighbor(clns, sct1) var sct2 []int if sct1[1] == 0 { // only fuse eastw possible if east < 0 { err = epf("NO EASTW NEIGHBOR 1 to %v", sct1) return fs, err } sct2 = clns[east] } else if sct1[1] == len(l)-1 { // only fuse westw possible if west < 0 { err = epf("NO WESTW NEIGHBOR 1 to %v", sct1) return fs, err } sct2 = sct1 sct1 = clns[west] } else { if util.Abs(sct1[3]) > 0 && util.Abs(sct1[3]) < util.Abs(sct1[6]) { // east flank higher than west flank? // => fuse westwards sct2 = sct1 if west < 0 { err = epf("NO WESTW NEIGHBOR 2 to %v", sct1) return fs, err } sct1 = clns[west] } else { // fuse eastwards if east < 0 { err = epf("NO EASTW NEIGHBOR 2 to %v", sct1) return fs, err } sct2 = clns[east] } } // PrintSectOfCLNS(sct1) // PrintSectOfCLNS(sct2) fs.idxL = append(fs.idxL, sct1[0], sct1[1], sct2[0], sct2[1]) fs.xyx = append(fs.xyx, sct1[2], sct1[6], sct2[2]) fs.w = append(fs.w, sct1[3], sct1[4], sct1[5]) fs.e = append(fs.e, sct2[6], sct2[7], sct2[8]) sdgN := ar.SmallestDesirableHeight sdgWE := ar.SmallestDesirableWidth pW, pE, pN := Permissiveness(sdgN, sdgWE, fs.xyx, fs.w, fs.e) fs.pm = []int{pW, pE, pN} // // base point // always bottom left fs.base.x = l[sct1[0]].x // x-coord taken from beginning baseY1 := l[sct1[0]].y // y-coord taken either from beginning baseY2 := l[sct2[1]].y // or taken from end fs.base.y = util.Max(baseY1, baseY2) // take lowest y - meaning Max(), not Min() dyw := sct1[3] // correction for concave angles if dyw > 0 { fs.base.x++ } switch { case fs.pm[0] < 0 && fs.pm[1] < 0: // eastw, westw blocked, concave fs.curveDesc = cncave fs.dirIdx, fs.maxOffs = -1, 0 case fs.pm[0] > 0 && fs.pm[1] < 0: // westwards fs.curveDesc = stairW fs.dirIdx, fs.maxOffs = 0, fs.pm[0] case fs.pm[0] < 0 && fs.pm[1] > 0: // eastwards fs.curveDesc = stairE fs.dirIdx, fs.maxOffs = 1, fs.pm[1] case fs.pm[0] > 0 && fs.pm[1] > 0: // utterly convex fs.curveDesc = convex fs.dirIdx, fs.maxOffs = 1, fs.pm[1] // wanton choice: grow east } lowerX := 0 if fs.xyx[1] < 0 { lowerX = 1 } if lowerX == 0 && fs.w[0] > 0 { fs.concaveCore = true } if lowerX == 1 && fs.e[0] > 0 { fs.concaveCore = true } return fs, nil }
// abundantHeightMatch should replace mostAbundant() // for the selection of the most appropriate amorph. // It returns at least an amorph, complying to max height. // If there are *several* amorphs close to the optimal height, // then we return one of the most abundant in the interval plus-minus 2 func (dummy MostAbundantInProximity) Filter(amorphBlocks [][]Amorph, fs Fusion) (chosen *Amorph) { pfTmp := intermedPf(pf) defer func() { pf = pfTmp }() pf = pfDevNull heightLim := fs.pm[2] heightOpt := fs.FillHeightFloor() pf("lim%v,opt%v ", heightLim, heightOpt) // plus minus 2 // -2=>0 , -1=>1 , 0=>2 , 1=>3 , 2=>4 closest := [][]Amorph{ []Amorph{}, []Amorph{}, []Amorph{}, []Amorph{}, []Amorph{}, } for i := 0; i < len(amorphBlocks); i++ { amorphs := amorphBlocks[i] // Scan all blocks of amorphs. // Extract one, which is closest to desired height. // Also create a histogram of amorphs, // which have a height of plus-minus 2 for j := 0; j < len(amorphs); j++ { var lastDist, lpDist int lp := amorphs[j] if lp.Rows > heightLim { continue } if chosen == nil { chosen = &lp } lastDist = chosen.Rows - heightOpt lpDist = lp.Rows - heightOpt if util.Abs(lpDist) < util.Abs(lastDist) { chosen = &lp } if util.Abs(lpDist) <= 2 { closest[lpDist+2] = append(closest[lpDist+2], lp) } } } // Debug output if false { pf("\n") for i := 0; i < len(closest); i++ { sa := closest[i] pf("h%v fnd%v: ", heightOpt+i-2, len(sa)) for j := 0; j < len(sa); j++ { pf("%2v %vx%v=%2v | ", sa[j].IdxArticle, sa[j].Rows, sa[j].Cols, sa[j].NElements) } pf("\n") } } // How many amorphs were close to desired height maxBatch := 0 for i := 0; i < len(closest); i++ { if len(closest[i]) > maxBatch { maxBatch = len(closest[i]) } } // Return one abundant amorph // from the range +/- 2 if maxBatch > 0 { for { if len(closest[2]) == maxBatch { chosen = &closest[2][0] break } if len(closest[1]) == maxBatch { chosen = &closest[1][0] break } if len(closest[3]) == maxBatch { chosen = &closest[3][0] break } if len(closest[0]) == maxBatch { chosen = &closest[0][0] break } if len(closest[4]) == maxBatch { chosen = &closest[4][0] break } break } } return }