func GenericXAxis(bg BasicGraphics, rng Range, y, ym int, options PlotOptions) {
	_, fontheight, _ := bg.FontMetrics(elementStyle(options, MajorTicElement).Font)
	var ticLen int = 0
	if !rng.TicSetting.Hide {
		ticLen = imin(12, imax(4, fontheight/2))
	}
	xa, xe := rng.Data2Screen(rng.Min), rng.Data2Screen(rng.Max)
	aly := y + 2*ticLen
	if !rng.TicSetting.Hide {
		aly += (3 * fontheight) / 2
	}
	if rng.ShowLimits {
		font := elementStyle(options, RangeLimitElement).Font
		if rng.Time {
			bg.Text(xa, aly, rng.TMin.Format("2006-01-02 15:04:05"), "tl", 0, font)
			bg.Text(xe, aly, rng.TMax.Format("2006-01-02 15:04:05"), "tr", 0, font)
		} else {
			bg.Text(xa, aly, fmt.Sprintf("%g", rng.Min), "tl", 0, font)
			bg.Text(xe, aly, fmt.Sprintf("%g", rng.Max), "tr", 0, font)
		}
	}
	if rng.Label != "" {  
		font := elementStyle(options, MajorAxisElement).Font
		bg.Text((xa+xe)/2, aly, "  "+rng.Label+"  ", "tc", 0, font)
	}
	if !rng.TicSetting.Hide {
		drawXTics(bg, rng, y, ym, ticLen, options)
	}
	bg.Line(xa, y, xe, y, elementStyle(options, MajorAxisElement))
	if rng.TicSetting.Mirror >= 1 {
		bg.Line(xa, ym, xe, ym, elementStyle(options, MinorAxisElement))
	}
	if rng.ShowZero && rng.Min < 0 && rng.Max > 0 {
		z := rng.Data2Screen(0)
		bg.Line(z, y, z, ym, elementStyle(options, ZeroAxisElement))
	}
}
GenericXAxis draws the x-axis with range rng solely by graphic primitives of bg.
The x-axis is drawn at y on the screen and the mirrored x-axis is drawn at ym.
package chart
import (
	"fmt"
	"image/color"
	"math"
)
type MinimalGraphics interface {
	Background() (r, g, b, a uint8)                          
	FontMetrics(font Font) (fw float32, fh int, mono bool)   
	TextLen(t string, font Font) int                         
	Line(x0, y0, x1, y1 int, style Style)                    
	Text(x, y int, t string, align string, rot int, f Font)  
}
type BasicGraphics interface {
	MinimalGraphics
	Symbol(x, y int, style Style)                           
	Rect(x, y, w, h int, style Style)                       
	Wedge(x, y, ro, ri int, phi, psi float64, style Style)  
	Path(x, y []int, style Style)                           
	Options() PlotOptions                                   
}
type Graphics interface {
	BasicGraphics
	Dimensions() (int, int)  
	Begin()  
	End()    
	XAxis(xr Range, ys, yms int, options PlotOptions)  
	YAxis(yr Range, xs, xms int, options PlotOptions)  
	Scatter(points []EPoint, plotstyle PlotStyle, style Style)  
	Boxes(boxes []Box, width int, style Style)                  
	Bars(bars []Barinfo, style Style)                           
	Rings(wedeges []Wedgeinfo, x, y, ro, ri int)                
	Key(x, y int, key Key, options PlotOptions)  
}
type Barinfo struct {
	x, y  int     
	w, h  int     
	t, tp string  
	f     Font    
}
type Wedgeinfo struct {
	Phi, Psi float64  
	Text, Tp string   
	Style    Style    
	Font     Font     
	Shift    int      
}
func GenericTextLen(mg MinimalGraphics, t string, font Font) (width int) {
	fw, _, mono := mg.FontMetrics(font)
	if mono {
		for _ = range t {
			width++
		}
		width = int(float32(width)*fw + 0.5)
	} else {
		var length float32
		for _, r := range t {
			if w, ok := CharacterWidth[int(r)]; ok {
				length += w
			} else {
				length += 20  
			}
		}
		length /= averageCharacterWidth
		length *= fw
		width = int(length + 0.5)
	}
	return
}
func SanitizeRect(x, y, w, h, r int) (int, int, int, int) {
	if w < 0 {
		x += w
		w = -w
	}
	if h < 0 {
		y += h
		h = -h
	}
	d := (imax(1, r) - 1) / 2
	return x + d, y + d, w - 2*d, h - 2*d
}
func GenericRect(mg MinimalGraphics, x, y, w, h int, style Style) {
	x, y, w, h = SanitizeRect(x, y, w, h, style.LineWidth)
	if style.FillColor != nil {
		fs := Style{LineWidth: 1, LineColor: style.FillColor, LineStyle: SolidLine}
		for i := 1; i < h; i++ {
			mg.Line(x+1, y+i, x+w-1, y+i, fs)
		}
	}
	mg.Line(x, y, x+w, y, style)
	mg.Line(x+w, y, x+w, y+h, style)
	mg.Line(x+w, y+h, x, y+h, style)
	mg.Line(x, y+h, x, y, style)
}
func GenericPath(mg MinimalGraphics, x, y []int, style Style) {
	n := imin(len(x), len(y))
	for i := 1; i < n; i++ {
		mg.Line(x[i-1], y[i-1], x[i], y[i], style)
	}
}
func drawXTics(bg BasicGraphics, rng Range, y, ym, ticLen int, options PlotOptions) {
	xe := rng.Data2Screen(rng.Max)
	if rng.TicSetting.Grid > GridOff {
		for ticcnt, tic := range rng.Tics {
			x := rng.Data2Screen(tic.Pos)
			if ticcnt >= 0 && ticcnt <= len(rng.Tics)-1 && rng.TicSetting.Grid == GridLines {
				bg.Line(x, y-1, x, ym+1, elementStyle(options, GridLineElement))
			} else if rng.TicSetting.Grid == GridBlocks {
				if ticcnt%2 == 1 {
					x0 := rng.Data2Screen(rng.Tics[ticcnt-1].Pos)
					bg.Rect(x0, ym, x-x0, y-ym, elementStyle(options, GridBlockElement))
				} else if ticcnt == len(rng.Tics)-1 && x < xe-1 {
					bg.Rect(x, ym, xe-x, y-ym, elementStyle(options, GridBlockElement))
				}
			}
		}
	}
	ticstyle := elementStyle(options, MajorTicElement)
	ticfont := ticstyle.Font
	for _, tic := range rng.Tics {
		x := rng.Data2Screen(tic.Pos)
		lx := rng.Data2Screen(tic.LabelPos)
		switch rng.TicSetting.Tics {
		case 0:
			bg.Line(x, y-ticLen, x, y+ticLen, ticstyle)
		case 1:
			bg.Line(x, y-ticLen, x, y, ticstyle)
		case 2:
			bg.Line(x, y, x, y+ticLen, ticstyle)
		default:
		}
		if rng.TicSetting.Mirror >= 2 {
			switch rng.TicSetting.Tics {
			case 0:
				bg.Line(x, ym-ticLen, x, ym+ticLen, ticstyle)
			case 1:
				bg.Line(x, ym, x, ym+ticLen, ticstyle)
			case 2:
				bg.Line(x, ym-ticLen, x, ym, ticstyle)
			default:
			}
		}
		if !rng.TicSetting.HideLabels {
			if rng.Time && tic.Align == -1 {
				bg.Line(x, y+ticLen, x, y+2*ticLen, ticstyle)
				bg.Text(lx, y+2*ticLen, tic.Label, "tl", 0, ticfont)
			} else {
				bg.Text(lx, y+2*ticLen, tic.Label, "tc", 0, ticfont)
			}
		}
	}
}