<commit_before>package parse

import (
	"net"
	"os"
	"path/filepath"
	"strings"
)

type parser struct {
	Dispenser
	block           serverBlock // current server block being parsed
	eof             bool        // if we encounter a valid EOF in a hard place
	checkDirectives bool        // if true, directives must be known
}

func (p *parser) parseAll() ([]serverBlock, error) {
	var blocks []serverBlock

	for p.Next() {
		err := p.parseOne()
		if err != nil {
			return blocks, err
		}
		if len(p.block.Addresses) > 0 {
			blocks = append(blocks, p.block)
		}
	}

	return blocks, nil
}

func (p *parser) parseOne() error {
	p.block = serverBlock{Tokens: make(map[string][]token)}

	err := p.begin()
	if err != nil {
		return err
	}

	return nil
}

func (p *parser) begin() error {
	if len(p.tokens) == 0 {
		return nil
	}

	err := p.addresses()
	if err != nil {
		return err
	}

	if p.eof {
		// this happens if the Caddyfile consists of only
		// a line of addresses and nothing else
		return nil
	}

	err = p.blockContents()
	if err != nil {
		return err
	}

	return nil
}

func (p *parser) addresses() error {
	var expectingAnother bool

	for {
		tkn := replaceEnvVars(p.Val())

		// special case: import directive replaces tokens during parse-time
		if tkn == "import" && p.isNewLine() {
			err := p.doImport()
			if err != nil {
				return err
			}
			continue
		}

		// Open brace definitely indicates end of addresses
		if tkn == "{" {
			if expectingAnother {
				return p.Errf("Expected another address but had '%s' - check for extra comma", tkn)
			}
			break
		}

		if tkn != "" { // empty token possible if user typed "" in Caddyfile
			// Trailing comma indicates another address will follow, which
			// may possibly be on the next line
			if tkn[len(tkn)-1] == ',' {
				tkn = tkn[:len(tkn)-1]
				expectingAnother = true
			} else {
				expectingAnother = false // but we may still see another one on this line
			}

			// Parse and save this address
			host, port, err := standardAddress(tkn)
			if err != nil {
				return err
			}
			p.block.Addresses = append(p.block.Addresses, address{host, port})
		}

		// Advance token and possibly break out of loop or return error
		hasNext := p.Next()
		if expectingAnother && !hasNext {
			return p.EOFErr()
		}
		if !hasNext {
			p.eof = true
			break // EOF
		}
		if !expectingAnother && p.isNewLine() {
			break
		}
	}

	return nil
}

func (p *parser) blockContents() error {
	errOpenCurlyBrace := p.openCurlyBrace()
	if errOpenCurlyBrace != nil {
		// single-server configs don't need curly braces
		p.cursor--
	}

	err := p.directives()
	if err != nil {
		return err
	}

	// Only look for close curly brace if there was an opening
	if errOpenCurlyBrace == nil {
		err = p.closeCurlyBrace()
		if err != nil {
			return err
		}
	}

	return nil
}

// directives parses through all the lines for directives
// and it expects the next token to be the first
// directive. It goes until EOF or closing curly brace
// which ends the server block.
func (p *parser) directives() error {
	for p.Next() {
		// end of server block
		if p.Val() == "}" {
			break
		}

		// special case: import directive replaces tokens during parse-time
		if p.Val() == "import" {
			err := p.doImport()
			if err != nil {
				return err
			}
			p.cursor-- // cursor is advanced when we continue, so roll back one more
			continue
		}

		// normal case: parse a directive on this line
		if err := p.directive(); err != nil {
			return err
		}
	}
	return nil
}

// doImport swaps out the import directive and its argument
// (a total of 2 tokens) with the tokens in the specified file
// or globbing pattern. When the function returns, the cursor
// is on the token before where the import directive was. In
// other words, call Next() to access the first token that was
// imported.
func (p *parser) doImport() error {
	if !p.NextArg() {
		return p.ArgErr()
	}
	importPattern := p.Val()
	if p.NextArg() {
		return p.Err("Import allows only one file to import")
	}

	matches, err := filepath.Glob(importPattern)
	if err != nil {
		return p.Errf("Failed to use import pattern %s - %s", importPattern, err.Error())
	}

	if len(matches) == 0 {
		return p.Errf("No files matching the import pattern %s", importPattern)
	}

	// Splice out the import directive and its argument (2 tokens total)
	// and insert the imported tokens in their place.
	tokensBefore := p.tokens[:p.cursor-1]
	tokensAfter := p.tokens[p.cursor+1:]
	// cursor was advanced one position to read filename; rewind it
	p.cursor--

	p.tokens = tokensBefore

	for _, importFile := range matches {
		if err := p.doSingleImport(importFile); err != nil {
			return err
		}
	}

	p.tokens = append(p.tokens, append(tokensAfter)...)

	return nil
}

// doSingleImport lexes the individual files matching the
// globbing pattern from of the import directive.
func (p *parser) doSingleImport(importFile string) error {
	file, err := os.Open(importFile)
	if err != nil {
		return p.Errf("Could not import %s - %v", importFile, err)
	}
	defer file.Close()
	importedTokens := allTokens(file)

	// Tack the filename onto these tokens so any errors show the imported file's name
	for i := 0; i < len(importedTokens); i++ {
		importedTokens[i].file = filepath.Base(importFile)
	}

	// Splice out the import directive and its argument (2 tokens total)
	// and insert the imported tokens in their place.
	p.tokens = append(p.tokens, append(importedTokens)...)

	return nil
}

// directive collects tokens until the directive's scope
// closes (either end of line or end of curly brace block).
// It expects the currently-loaded token to be a directive
// (or } that ends a server block). The collected tokens
// are loaded into the current server block for later use
// by directive setup functions.
func (p *parser) directive() error {
	dir := p.Val()
	nesting := 0

	if p.checkDirectives {
		if _, ok := ValidDirectives[dir]; !ok {
			return p.Errf("Unknown directive '%s'", dir)
		}
	}

	// The directive itself is appended as a relevant token
	p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])

	for p.Next() {
		if p.Val() == "{" {
			nesting++
		} else if p.isNewLine() && nesting == 0 {
			p.cursor-- // read too far
			break
		} else if p.Val() == "}" && nesting > 0 {
			nesting--
		} else if p.Val() == "}" && nesting == 0 {
			return p.Err("Unexpected '}' because no matching opening brace")
		}
		p.tokens[p.cursor].text = replaceEnvVars(p.tokens[p.cursor].text)
		p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
	}

	if nesting > 0 {
		return p.EOFErr()
	}
	return nil
}

// openCurlyBrace expects the current token to be an
// opening curly brace. This acts like an assertion
// because it returns an error if the token is not
// a opening curly brace. It does NOT advance the token.
func (p *parser) openCurlyBrace() error {
	if p.Val() != "{" {
		return p.SyntaxErr("{")
	}
	return nil
}

// closeCurlyBrace expects the current token to be
// a closing curly brace. This acts like an assertion
// because it returns an error if the token is not
// a closing curly brace. It does NOT advance the token.
func (p *parser) closeCurlyBrace() error {
	if p.Val() != "}" {
		return p.SyntaxErr("}")
	}
	return nil
}

// standardAddress turns the accepted host and port patterns
// into a format accepted by net.Dial.
func standardAddress(str string) (host, port string, err error) {
	var schemePort, splitPort string

	if strings.HasPrefix(str, "https://") {
		schemePort = "https"
		str = str[8:]
	} else if strings.HasPrefix(str, "http://") {
		schemePort = "http"
		str = str[7:]
	}

	host, splitPort, err = net.SplitHostPort(str)
	if err != nil {
		host, splitPort, err = net.SplitHostPort(str + ":") // tack on empty port
	}
	if err != nil {
		// ¯\_(ツ)_/¯
		host = str
	}

	if splitPort != "" {
		port = splitPort
	} else {
		port = schemePort
	}

	return
}

// replaceEnvVars replaces environment variables that appear in the token
// and understands both the Unix $SYNTAX and Windows %SYNTAX%.
func replaceEnvVars(s string) string {
	s = replaceEnvReferences(s, "{%", "%}")
	s = replaceEnvReferences(s, "{$", "}")
	return s
}

// replaceEnvReferences performs the actual replacement of env variables
// in s, given the placeholder start and placeholder end strings.
func replaceEnvReferences(s, refStart, refEnd string) string {
	index := strings.Index(s, refStart)
	for index != -1 {
		endIndex := strings.Index(s, refEnd)
		if endIndex != -1 {
			ref := s[index : endIndex+len(refEnd)]
			s = strings.Replace(s, ref, os.Getenv(ref[len(refStart):len(ref)-len(refEnd)]), -1)
		} else {
			return s
		}
		index = strings.Index(s, refStart)
	}
	return s
}

type (
	// serverBlock associates tokens with a list of addresses
	// and groups tokens by directive name.
	serverBlock struct {
		Addresses []address
		Tokens    map[string][]token
	}

	address struct {
		Host, Port string
	}
)

// HostList converts the list of addresses (hosts)
// that are associated with this server block into
// a slice of strings. Each string is a host:port
// combination.
func (sb serverBlock) HostList() []string {
	sbHosts := make([]string, len(sb.Addresses))
	for j, addr := range sb.Addresses {
		sbHosts[j] = net.JoinHostPort(addr.Host, addr.Port)
	}
	return sbHosts
}
<commit_msg>Import allows only one expression<commit_after>package parse

import (
	"net"
	"os"
	"path/filepath"
	"strings"
)

type parser struct {
	Dispenser
	block           serverBlock // current server block being parsed
	eof             bool        // if we encounter a valid EOF in a hard place
	checkDirectives bool        // if true, directives must be known
}

func (p *parser) parseAll() ([]serverBlock, error) {
	var blocks []serverBlock

	for p.Next() {
		err := p.parseOne()
		if err != nil {
			return blocks, err
		}
		if len(p.block.Addresses) > 0 {
			blocks = append(blocks, p.block)
		}
	}

	return blocks, nil
}

func (p *parser) parseOne() error {
	p.block = serverBlock{Tokens: make(map[string][]token)}

	err := p.begin()
	if err != nil {
		return err
	}

	return nil
}

func (p *parser) begin() error {
	if len(p.tokens) == 0 {
		return nil
	}

	err := p.addresses()
	if err != nil {
		return err
	}

	if p.eof {
		// this happens if the Caddyfile consists of only
		// a line of addresses and nothing else
		return nil
	}

	err = p.blockContents()
	if err != nil {
		return err
	}

	return nil
}

func (p *parser) addresses() error {
	var expectingAnother bool

	for {
		tkn := replaceEnvVars(p.Val())

		// special case: import directive replaces tokens during parse-time
		if tkn == "import" && p.isNewLine() {
			err := p.doImport()
			if err != nil {
				return err
			}
			continue
		}

		// Open brace definitely indicates end of addresses
		if tkn == "{" {
			if expectingAnother {
				return p.Errf("Expected another address but had '%s' - check for extra comma", tkn)
			}
			break
		}

		if tkn != "" { // empty token possible if user typed "" in Caddyfile
			// Trailing comma indicates another address will follow, which
			// may possibly be on the next line
			if tkn[len(tkn)-1] == ',' {
				tkn = tkn[:len(tkn)-1]
				expectingAnother = true
			} else {
				expectingAnother = false // but we may still see another one on this line
			}

			// Parse and save this address
			host, port, err := standardAddress(tkn)
			if err != nil {
				return err
			}
			p.block.Addresses = append(p.block.Addresses, address{host, port})
		}

		// Advance token and possibly break out of loop or return error
		hasNext := p.Next()
		if expectingAnother && !hasNext {
			return p.EOFErr()
		}
		if !hasNext {
			p.eof = true
			break // EOF
		}
		if !expectingAnother && p.isNewLine() {
			break
		}
	}

	return nil
}

func (p *parser) blockContents() error {
	errOpenCurlyBrace := p.openCurlyBrace()
	if errOpenCurlyBrace != nil {
		// single-server configs don't need curly braces
		p.cursor--
	}

	err := p.directives()
	if err != nil {
		return err
	}

	// Only look for close curly brace if there was an opening
	if errOpenCurlyBrace == nil {
		err = p.closeCurlyBrace()
		if err != nil {
			return err
		}
	}

	return nil
}

// directives parses through all the lines for directives
// and it expects the next token to be the first
// directive. It goes until EOF or closing curly brace
// which ends the server block.
func (p *parser) directives() error {
	for p.Next() {
		// end of server block
		if p.Val() == "}" {
			break
		}

		// special case: import directive replaces tokens during parse-time
		if p.Val() == "import" {
			err := p.doImport()
			if err != nil {
				return err
			}
			p.cursor-- // cursor is advanced when we continue, so roll back one more
			continue
		}

		// normal case: parse a directive on this line
		if err := p.directive(); err != nil {
			return err
		}
	}
	return nil
}

// doImport swaps out the import directive and its argument
// (a total of 2 tokens) with the tokens in the specified file
// or globbing pattern. When the function returns, the cursor
// is on the token before where the import directive was. In
// other words, call Next() to access the first token that was
// imported.
func (p *parser) doImport() error {
	if !p.NextArg() {
		return p.ArgErr()
	}
	importPattern := p.Val()
	if p.NextArg() {
		return p.Err("Import allows only one expression, either file or glob pattern")
	}

	matches, err := filepath.Glob(importPattern)
	if err != nil {
		return p.Errf("Failed to use import pattern %s - %s", importPattern, err.Error())
	}

	if len(matches) == 0 {
		return p.Errf("No files matching the import pattern %s", importPattern)
	}

	// Splice out the import directive and its argument (2 tokens total)
	// and insert the imported tokens in their place.
	tokensBefore := p.tokens[:p.cursor-1]
	tokensAfter := p.tokens[p.cursor+1:]
	// cursor was advanced one position to read filename; rewind it
	p.cursor--

	p.tokens = tokensBefore

	for _, importFile := range matches {
		if err := p.doSingleImport(importFile); err != nil {
			return err
		}
	}

	p.tokens = append(p.tokens, append(tokensAfter)...)

	return nil
}

// doSingleImport lexes the individual files matching the
// globbing pattern from of the import directive.
func (p *parser) doSingleImport(importFile string) error {
	file, err := os.Open(importFile)
	if err != nil {
		return p.Errf("Could not import %s - %v", importFile, err)
	}
	defer file.Close()
	importedTokens := allTokens(file)

	// Tack the filename onto these tokens so any errors show the imported file's name
	for i := 0; i < len(importedTokens); i++ {
		importedTokens[i].file = filepath.Base(importFile)
	}

	// Splice out the import directive and its argument (2 tokens total)
	// and insert the imported tokens in their place.
	p.tokens = append(p.tokens, append(importedTokens)...)

	return nil
}

// directive collects tokens until the directive's scope
// closes (either end of line or end of curly brace block).
// It expects the currently-loaded token to be a directive
// (or } that ends a server block). The collected tokens
// are loaded into the current server block for later use
// by directive setup functions.
func (p *parser) directive() error {
	dir := p.Val()
	nesting := 0

	if p.checkDirectives {
		if _, ok := ValidDirectives[dir]; !ok {
			return p.Errf("Unknown directive '%s'", dir)
		}
	}

	// The directive itself is appended as a relevant token
	p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])

	for p.Next() {
		if p.Val() == "{" {
			nesting++
		} else if p.isNewLine() && nesting == 0 {
			p.cursor-- // read too far
			break
		} else if p.Val() == "}" && nesting > 0 {
			nesting--
		} else if p.Val() == "}" && nesting == 0 {
			return p.Err("Unexpected '}' because no matching opening brace")
		}
		p.tokens[p.cursor].text = replaceEnvVars(p.tokens[p.cursor].text)
		p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
	}

	if nesting > 0 {
		return p.EOFErr()
	}
	return nil
}

// openCurlyBrace expects the current token to be an
// opening curly brace. This acts like an assertion
// because it returns an error if the token is not
// a opening curly brace. It does NOT advance the token.
func (p *parser) openCurlyBrace() error {
	if p.Val() != "{" {
		return p.SyntaxErr("{")
	}
	return nil
}

// closeCurlyBrace expects the current token to be
// a closing curly brace. This acts like an assertion
// because it returns an error if the token is not
// a closing curly brace. It does NOT advance the token.
func (p *parser) closeCurlyBrace() error {
	if p.Val() != "}" {
		return p.SyntaxErr("}")
	}
	return nil
}

// standardAddress turns the accepted host and port patterns
// into a format accepted by net.Dial.
func standardAddress(str string) (host, port string, err error) {
	var schemePort, splitPort string

	if strings.HasPrefix(str, "https://") {
		schemePort = "https"
		str = str[8:]
	} else if strings.HasPrefix(str, "http://") {
		schemePort = "http"
		str = str[7:]
	}

	host, splitPort, err = net.SplitHostPort(str)
	if err != nil {
		host, splitPort, err = net.SplitHostPort(str + ":") // tack on empty port
	}
	if err != nil {
		// ¯\_(ツ)_/¯
		host = str
	}

	if splitPort != "" {
		port = splitPort
	} else {
		port = schemePort
	}

	return
}

// replaceEnvVars replaces environment variables that appear in the token
// and understands both the Unix $SYNTAX and Windows %SYNTAX%.
func replaceEnvVars(s string) string {
	s = replaceEnvReferences(s, "{%", "%}")
	s = replaceEnvReferences(s, "{$", "}")
	return s
}

// replaceEnvReferences performs the actual replacement of env variables
// in s, given the placeholder start and placeholder end strings.
func replaceEnvReferences(s, refStart, refEnd string) string {
	index := strings.Index(s, refStart)
	for index != -1 {
		endIndex := strings.Index(s, refEnd)
		if endIndex != -1 {
			ref := s[index : endIndex+len(refEnd)]
			s = strings.Replace(s, ref, os.Getenv(ref[len(refStart):len(ref)-len(refEnd)]), -1)
		} else {
			return s
		}
		index = strings.Index(s, refStart)
	}
	return s
}

type (
	// serverBlock associates tokens with a list of addresses
	// and groups tokens by directive name.
	serverBlock struct {
		Addresses []address
		Tokens    map[string][]token
	}

	address struct {
		Host, Port string
	}
)

// HostList converts the list of addresses (hosts)
// that are associated with this server block into
// a slice of strings. Each string is a host:port
// combination.
func (sb serverBlock) HostList() []string {
	sbHosts := make([]string, len(sb.Addresses))
	for j, addr := range sb.Addresses {
		sbHosts[j] = net.JoinHostPort(addr.Host, addr.Port)
	}
	return sbHosts
}
<|endoftext|>


<commit_before>package infermedica

import (
	"encoding/json"
	"net/http"
)

type SymptomRes struct {
	ID             string         `json:"id"`
	Name           string         `json:"name"`
	CommonName     string         `json:"common_name"`
	Category       string         `json:"category"`
	Seriousness    string         `json:"seriousness"`
	Children       []SymptomChild `json:"children"`
	ImageURL       string         `json:"image_url"`
	ImageSource    string         `json:"image_source"`
	ParentID       string         `json:"parent_id"`
	ParentRelation string         `json:"parent_relation"`
}

type SymptomChild struct {
	ID             string `json:"id"`
	ParentRelation string `json:"parent_relation"`
}

func (a *App) Symptoms() (*[]SymptomRes, error) {
	req, err := a.prepareRequest("GET", "symptoms", nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := []SymptomRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

func (a *App) SymptomByID(id string) (*SymptomRes, error) {
	req, err := a.prepareRequest("GET", "symptoms/"+id, nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := SymptomRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}
<commit_msg>added symptom id map func<commit_after>package infermedica

import (
	"encoding/json"
	"net/http"
)

type SymptomRes struct {
	ID             string         `json:"id"`
	Name           string         `json:"name"`
	CommonName     string         `json:"common_name"`
	Category       string         `json:"category"`
	Seriousness    string         `json:"seriousness"`
	Children       []SymptomChild `json:"children"`
	ImageURL       string         `json:"image_url"`
	ImageSource    string         `json:"image_source"`
	ParentID       string         `json:"parent_id"`
	ParentRelation string         `json:"parent_relation"`
}

type SymptomChild struct {
	ID             string `json:"id"`
	ParentRelation string `json:"parent_relation"`
}

func (a *App) Symptoms() (*[]SymptomRes, error) {
	req, err := a.prepareRequest("GET", "symptoms", nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := []SymptomRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

func (a *App) SymptomsIDMap() (*map[string]SymptomRes, error) {
	req, err := a.prepareRequest("GET", "symptoms", nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := []SymptomRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	var rmap map[string]SymptomRes
	for _, sr := range r {
		rmap[sr.ID] = sr
	}
	return &rmap, nil
}

func (a *App) SymptomByID(id string) (*SymptomRes, error) {
	req, err := a.prepareRequest("GET", "symptoms/"+id, nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	r := SymptomRes{}
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package text offers functions to draw texts on an Ebiten's image.
//
// Note: This package is experimental and API might be changed.
package text

import (
	"image"
	"image/color"
	"math"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/internal/graphics" // TODO: Move NextPowerOf2Int to a new different package
	"github.com/hajimehoshi/ebiten/internal/sync"
)

var (
	monotonicClock int64
)

func now() int64 {
	monotonicClock++
	return monotonicClock
}

var (
	charBounds = map[char]fixed.Rectangle26_6{}
)

type char struct {
	face font.Face
	rune rune
}

func (c *char) bounds() fixed.Rectangle26_6 {
	if b, ok := charBounds[*c]; ok {
		return b
	}
	b, _, _ := c.face.GlyphBounds(c.rune)
	charBounds[*c] = b
	return b
}

func (c *char) size() fixed.Point26_6 {
	b := c.bounds()
	return b.Max.Sub(b.Min)
}

func (c *char) empty() bool {
	s := c.size()
	return s.X == 0 || s.Y == 0
}

func (c *char) atlasGroup() int {
	s := c.size()
	w, h := s.X.Ceil(), s.Y.Ceil()
	t := w
	if t < h {
		t = h
	}

	// Different images for small runes are inefficient.
	// Let's use a same texture atlas for typical character sizes.
	if t < 32 {
		return 32
	}
	return graphics.NextPowerOf2Int(t)
}

type glyph struct {
	char  char
	index int
	atime int64
}

func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
	return float64(x) / (1 << 6)
}

func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
	cr, cg, cb, ca := clr.RGBA()
	if ca == 0 {
		return
	}

	b := g.char.bounds()
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(fixed26_6ToFloat64(x), fixed26_6ToFloat64(y))
	op.GeoM.Translate(fixed26_6ToFloat64(b.Min.X), fixed26_6ToFloat64(b.Min.Y))

	rf := float64(cr) / float64(ca)
	gf := float64(cg) / float64(ca)
	bf := float64(cb) / float64(ca)
	af := float64(ca) / 0xffff
	op.ColorM.Scale(rf, gf, bf, af)

	a := atlases[g.char.atlasGroup()]
	sx, sy := a.at(g)
	r := image.Rect(sx, sy, sx+a.size, sy+a.size)
	op.SourceRect = &r

	dst.DrawImage(a.image, op)
}

var (
	glyphs  = map[char]*glyph{}
	atlases = map[int]*atlas{}
)

type atlas struct {
	// image is the back-end image to hold glyph cache.
	image *ebiten.Image

	// tmpImage is the temporary image as a renderer source for glyph.
	tmpImage *ebiten.Image

	// size is the size of one glyph in the cache.
	// This value is always power of 2.
	size int

	// glyphs is the set of glyph information.
	glyphs []*glyph

	// num is the number of glyphs the atlas holds.
	num int
}

func (a *atlas) at(glyph *glyph) (int, int) {
	if a.size != glyph.char.atlasGroup() {
		panic("not reached")
	}
	w, _ := a.image.Size()
	xnum := w / a.size
	x, y := glyph.index%xnum, glyph.index/xnum
	return x * a.size, y * a.size
}

func (a *atlas) append(glyph *glyph) {
	if a.num == len(a.glyphs) {
		idx := -1
		t := int64(math.MaxInt64)
		for i, g := range a.glyphs {
			if g.atime < t {
				t = g.atime
				idx = i
			}
		}
		if idx < 0 {
			panic("not reached")
		}
		oldest := a.glyphs[idx]
		delete(glyphs, oldest.char)

		glyph.index = idx
		a.glyphs[idx] = glyph
		a.draw(glyph)
		return
	}
	idx := -1
	for i, g := range a.glyphs {
		if g == nil {
			idx = i
			break
		}
	}
	if idx < 0 {
		panic("not reached")
	}
	a.num++
	glyph.index = idx
	a.glyphs[idx] = glyph
	a.draw(glyph)
}

func (a *atlas) draw(glyph *glyph) {
	if a.tmpImage == nil {
		a.tmpImage, _ = ebiten.NewImage(a.size, a.size, ebiten.FilterNearest)
	}

	dst := image.NewRGBA(image.Rect(0, 0, a.size, a.size))
	d := font.Drawer{
		Dst:  dst,
		Src:  image.White,
		Face: glyph.char.face,
	}
	b := glyph.char.bounds()
	d.Dot = fixed.Point26_6{-b.Min.X, -b.Min.Y}
	d.DrawString(string(glyph.char.rune))
	a.tmpImage.ReplacePixels(dst.Pix)

	op := &ebiten.DrawImageOptions{}
	x, y := a.at(glyph)
	op.GeoM.Translate(float64(x), float64(y))
	op.CompositeMode = ebiten.CompositeModeCopy
	a.image.DrawImage(a.tmpImage, op)

	a.tmpImage.Clear()
}

func getGlyphFromCache(face font.Face, r rune, now int64) *glyph {
	ch := char{face, r}
	g, ok := glyphs[ch]
	if ok {
		g.atime = now
		return g
	}

	g = &glyph{
		char:  ch,
		atime: now,
	}
	if ch.empty() {
		return g
	}

	a, ok := atlases[g.char.atlasGroup()]
	if !ok {
		// Don't use ebiten.MaxImageSize here.
		// It's because the back-end image pixels will be restored from GPU
		// whenever a new glyph is rendered on the image, and restoring cost is
		// expensive if the image is big.
		// The back-end image is updated a temporary image, and the temporary image is
		// always cleared after used. This means that there is no clue to restore
		// the back-end image without reading from GPU
		// (see the package 'restorable' implementation).
		//
		// TODO: How about making a new function for 'flagile' image?
		const size = 1024
		i, _ := ebiten.NewImage(size, size, ebiten.FilterNearest)
		a = &atlas{
			image: i,
			size:  g.char.atlasGroup(),
		}
		w, h := a.image.Size()
		xnum := w / a.size
		ynum := h / a.size
		a.glyphs = make([]*glyph, xnum*ynum)
		atlases[g.char.atlasGroup()] = a
	}

	a.append(g)
	glyphs[g.char] = g
	return g
}

var textM sync.Mutex

// Draw draws a given text on a given destination image dst.
//
// face is the font for text rendering.
// (x, y) represents a 'dot' position. Be careful that this doesn't represent left-upper corner position.
// clr is the color for text rendering.
//
// Glyphs used for rendering are cached in least-recently-used way.
// It is OK to call this function with a same text and a same face at every frame.
//
// This function is concurrent-safe.
func Draw(dst *ebiten.Image, face font.Face, text string, x, y int, clr color.Color) {
	textM.Lock()

	n := now()
	fx := fixed.I(x)
	prevC := rune(-1)

	runes := []rune(text)
	for _, c := range runes {
		if prevC >= 0 {
			fx += face.Kern(prevC, c)
		}
		if g := getGlyphFromCache(face, c, n); g != nil {
			if !g.char.empty() {
				g.draw(dst, fx, fixed.I(y), clr)
			}
			a, _ := face.GlyphAdvance(c)
			fx += a
		}
		prevC = c
	}

	textM.Unlock()
}
<commit_msg>text: Refactoring: Reduce a global variable<commit_after>// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package text offers functions to draw texts on an Ebiten's image.
//
// Note: This package is experimental and API might be changed.
package text

import (
	"image"
	"image/color"
	"math"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/internal/graphics" // TODO: Move NextPowerOf2Int to a new different package
	"github.com/hajimehoshi/ebiten/internal/sync"
)

var (
	monotonicClock int64
)

func now() int64 {
	monotonicClock++
	return monotonicClock
}

var (
	charBounds = map[char]fixed.Rectangle26_6{}
)

type char struct {
	face font.Face
	rune rune
}

func (c *char) bounds() fixed.Rectangle26_6 {
	if b, ok := charBounds[*c]; ok {
		return b
	}
	b, _, _ := c.face.GlyphBounds(c.rune)
	charBounds[*c] = b
	return b
}

func (c *char) size() fixed.Point26_6 {
	b := c.bounds()
	return b.Max.Sub(b.Min)
}

func (c *char) empty() bool {
	s := c.size()
	return s.X == 0 || s.Y == 0
}

func (c *char) atlasGroup() int {
	s := c.size()
	w, h := s.X.Ceil(), s.Y.Ceil()
	t := w
	if t < h {
		t = h
	}

	// Different images for small runes are inefficient.
	// Let's use a same texture atlas for typical character sizes.
	if t < 32 {
		return 32
	}
	return graphics.NextPowerOf2Int(t)
}

type glyph struct {
	char  char
	index int
	atime int64
}

func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
	return float64(x) / (1 << 6)
}

func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
	cr, cg, cb, ca := clr.RGBA()
	if ca == 0 {
		return
	}

	b := g.char.bounds()
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(fixed26_6ToFloat64(x), fixed26_6ToFloat64(y))
	op.GeoM.Translate(fixed26_6ToFloat64(b.Min.X), fixed26_6ToFloat64(b.Min.Y))

	rf := float64(cr) / float64(ca)
	gf := float64(cg) / float64(ca)
	bf := float64(cb) / float64(ca)
	af := float64(ca) / 0xffff
	op.ColorM.Scale(rf, gf, bf, af)

	a := atlases[g.char.atlasGroup()]
	sx, sy := a.at(g)
	r := image.Rect(sx, sy, sx+a.size, sy+a.size)
	op.SourceRect = &r

	dst.DrawImage(a.image, op)
}

var (
	atlases = map[int]*atlas{}
)

type atlas struct {
	// image is the back-end image to hold glyph cache.
	image *ebiten.Image

	// tmpImage is the temporary image as a renderer source for glyph.
	tmpImage *ebiten.Image

	// size is the size of one glyph in the cache.
	// This value is always power of 2.
	size int

	charToGlyph map[char]*glyph

	// glyphs is the set of glyph information.
	glyphs []*glyph

	// num is the number of glyphs the atlas holds.
	num int
}

func (a *atlas) at(glyph *glyph) (int, int) {
	if a.size != glyph.char.atlasGroup() {
		panic("not reached")
	}
	w, _ := a.image.Size()
	xnum := w / a.size
	x, y := glyph.index%xnum, glyph.index/xnum
	return x * a.size, y * a.size
}

func (a *atlas) append(glyph *glyph) {
	if a.num == len(a.glyphs) {
		idx := -1
		t := int64(math.MaxInt64)
		for i, g := range a.glyphs {
			if g.atime < t {
				t = g.atime
				idx = i
			}
		}
		if idx < 0 {
			panic("not reached")
		}
		oldest := a.glyphs[idx]
		delete(a.charToGlyph, oldest.char)

		glyph.index = idx
		a.glyphs[idx] = glyph
		a.charToGlyph[glyph.char] = glyph
		a.draw(glyph)
		return
	}
	idx := -1
	for i, g := range a.glyphs {
		if g == nil {
			idx = i
			break
		}
	}
	if idx < 0 {
		panic("not reached")
	}
	a.num++
	glyph.index = idx
	a.glyphs[idx] = glyph
	a.charToGlyph[glyph.char] = glyph
	a.draw(glyph)
}

func (a *atlas) draw(glyph *glyph) {
	if a.tmpImage == nil {
		a.tmpImage, _ = ebiten.NewImage(a.size, a.size, ebiten.FilterNearest)
	}

	dst := image.NewRGBA(image.Rect(0, 0, a.size, a.size))
	d := font.Drawer{
		Dst:  dst,
		Src:  image.White,
		Face: glyph.char.face,
	}
	b := glyph.char.bounds()
	d.Dot = fixed.Point26_6{-b.Min.X, -b.Min.Y}
	d.DrawString(string(glyph.char.rune))
	a.tmpImage.ReplacePixels(dst.Pix)

	op := &ebiten.DrawImageOptions{}
	x, y := a.at(glyph)
	op.GeoM.Translate(float64(x), float64(y))
	op.CompositeMode = ebiten.CompositeModeCopy
	a.image.DrawImage(a.tmpImage, op)

	a.tmpImage.Clear()
}

func getGlyphFromCache(face font.Face, r rune, now int64) *glyph {
	ch := char{face, r}
	a, ok := atlases[ch.atlasGroup()]
	if ok {
		g, ok := a.charToGlyph[ch]
		if ok {
			g.atime = now
			return g
		}
	}

	g := &glyph{
		char:  ch,
		atime: now,
	}
	if ch.empty() {
		return g
	}

	if !ok {
		// Don't use ebiten.MaxImageSize here.
		// It's because the back-end image pixels will be restored from GPU
		// whenever a new glyph is rendered on the image, and restoring cost is
		// expensive if the image is big.
		// The back-end image is updated a temporary image, and the temporary image is
		// always cleared after used. This means that there is no clue to restore
		// the back-end image without reading from GPU
		// (see the package 'restorable' implementation).
		//
		// TODO: How about making a new function for 'flagile' image?
		const size = 1024
		i, _ := ebiten.NewImage(size, size, ebiten.FilterNearest)
		a = &atlas{
			image:       i,
			size:        g.char.atlasGroup(),
			charToGlyph: map[char]*glyph{},
		}
		w, h := a.image.Size()
		xnum := w / a.size
		ynum := h / a.size
		a.glyphs = make([]*glyph, xnum*ynum)
		atlases[g.char.atlasGroup()] = a
	}

	a.append(g)
	return g
}

var textM sync.Mutex

// Draw draws a given text on a given destination image dst.
//
// face is the font for text rendering.
// (x, y) represents a 'dot' position. Be careful that this doesn't represent left-upper corner position.
// clr is the color for text rendering.
//
// Glyphs used for rendering are cached in least-recently-used way.
// It is OK to call this function with a same text and a same face at every frame.
//
// This function is concurrent-safe.
func Draw(dst *ebiten.Image, face font.Face, text string, x, y int, clr color.Color) {
	textM.Lock()

	n := now()
	fx := fixed.I(x)
	prevC := rune(-1)

	runes := []rune(text)
	for _, c := range runes {
		if prevC >= 0 {
			fx += face.Kern(prevC, c)
		}
		if g := getGlyphFromCache(face, c, n); g != nil {
			if !g.char.empty() {
				g.draw(dst, fx, fixed.I(y), clr)
			}
			a, _ := face.GlyphAdvance(c)
			fx += a
		}
		prevC = c
	}

	textM.Unlock()
}
<|endoftext|>


<commit_before>// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package text offers functions to draw texts on an Ebiten's image.
//
// For the example using a TTF font, see font package in the examples.
package text

import (
	"image"
	"image/color"
	"math"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

var (
	monotonicClock int64
)

func now() int64 {
	monotonicClock++
	return monotonicClock
}

func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
	return float64(x) / (1 << 6)
}

const (
	cacheLimit = 512 // This is an arbitrary number.
)

type colorMCacheKey uint32

type colorMCacheEntry struct {
	m     ebiten.ColorM
	atime int64
}

func drawGlyph(dst *ebiten.Image, face font.Face, r rune, img *glyphImage, x, y fixed.Int26_6, clr ebiten.ColorM) {
	if img == nil {
		return
	}

	b := getGlyphBounds(face, r)
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(fixed26_6ToFloat64(x+b.Min.X), fixed26_6ToFloat64(y+b.Min.Y))

	op.ColorM = clr
	re := image.Rect(img.x, img.y, img.x+img.width, img.y+img.height)
	op.SourceRect = &re

	_ = dst.DrawImage(img.image, op)
}

var (
	// Use pointers to avoid copying on browsers.
	glyphBoundsCache = map[font.Face]map[rune]*fixed.Rectangle26_6{}
)

func getGlyphBounds(face font.Face, r rune) *fixed.Rectangle26_6 {
	if _, ok := glyphBoundsCache[face]; !ok {
		glyphBoundsCache[face] = map[rune]*fixed.Rectangle26_6{}
	}
	if b, ok := glyphBoundsCache[face][r]; ok {
		return b
	}
	b, _, _ := face.GlyphBounds(r)
	glyphBoundsCache[face][r] = &b
	return &b
}

type glyphImage struct {
	image  *ebiten.Image
	x      int
	y      int
	width  int
	height int
}

type glyphImageCacheEntry struct {
	image *glyphImage
	atime int64
}

var (
	glyphImageCache = map[font.Face]map[rune]*glyphImageCacheEntry{}
	emptyGlyphs     = map[font.Face]map[rune]struct{}{}
)

func getGlyphImages(face font.Face, runes []rune) []*glyphImage {
	if _, ok := emptyGlyphs[face]; !ok {
		emptyGlyphs[face] = map[rune]struct{}{}
	}
	if _, ok := glyphImageCache[face]; !ok {
		glyphImageCache[face] = map[rune]*glyphImageCacheEntry{}
	}

	imgs := make([]*glyphImage, len(runes))
	neededGlyphs := map[int]*fixed.Rectangle26_6{}
	for i, r := range runes {
		if _, ok := emptyGlyphs[face][r]; ok {
			continue
		}

		if e, ok := glyphImageCache[face][r]; ok {
			e.atime = now()
			imgs[i] = e.image
			continue
		}

		b := getGlyphBounds(face, r)
		w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()
		if w == 0 || h == 0 {
			emptyGlyphs[face][r] = struct{}{}
			continue
		}

		// TODO: What if len(runes) > cacheLimit?
		if len(glyphImageCache[face]) > cacheLimit {
			oldest := int64(math.MaxInt64)
			oldestKey := rune(-1)
			for r, e := range glyphImageCache[face] {
				if e.atime < oldest {
					oldestKey = r
					oldest = e.atime
				}
			}
			delete(glyphImageCache[face], oldestKey)
		}

		neededGlyphs[i] = b
	}

	if len(neededGlyphs) > 0 {
		// TODO: What if w2 is too big (e.g. > 4096)?
		w2 := 0
		h2 := 0
		for _, b := range neededGlyphs {
			w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()
			w2 += w
			if h2 < h {
				h2 = h
			}
		}
		rgba := image.NewRGBA(image.Rect(0, 0, w2, h2))

		x := 0
		for i, b := range neededGlyphs {
			w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()

			r := runes[i]
			d := font.Drawer{
				Dst:  rgba,
				Src:  image.White,
				Face: face,
			}
			d.Dot = fixed.Point26_6{fixed.I(x) - b.Min.X, -b.Min.Y}
			d.DrawString(string(r))

			img, _ := ebiten.NewImageFromImage(rgba, ebiten.FilterDefault)
			g := &glyphImage{
				image:  img,
				x:      x,
				y:      0,
				width:  w,
				height: h,
			}
			glyphImageCache[face][r] = &glyphImageCacheEntry{
				image: g,
				atime: now(),
			}
			imgs[i] = g

			x += w
		}
	}
	return imgs
}

var textM sync.Mutex

var (
	colorMCache = map[colorMCacheKey]*colorMCacheEntry{}
	emptyColorM ebiten.ColorM
)

func init() {
	emptyColorM.Scale(0, 0, 0, 0)
}

func colorToColorM(clr color.Color) ebiten.ColorM {
	// RGBA() is in [0 - 0xffff]. Adjust them in [0 - 0xff].
	cr, cg, cb, ca := clr.RGBA()
	cr >>= 8
	cg >>= 8
	cb >>= 8
	ca >>= 8
	if ca == 0 {
		return emptyColorM
	}
	key := colorMCacheKey(uint32(cr) | (uint32(cg) << 8) | (uint32(cb) << 16) | (uint32(ca) << 24))
	e, ok := colorMCache[key]
	if ok {
		e.atime = now()
		return e.m
	}
	if len(colorMCache) > cacheLimit {
		oldest := int64(math.MaxInt64)
		oldestKey := colorMCacheKey(0)
		for key, c := range colorMCache {
			if c.atime < oldest {
				oldestKey = key
				oldest = c.atime
			}
		}
		delete(colorMCache, oldestKey)
	}

	cm := ebiten.ColorM{}
	rf := float64(cr) / float64(ca)
	gf := float64(cg) / float64(ca)
	bf := float64(cb) / float64(ca)
	af := float64(ca) / 0xff
	cm.Scale(rf, gf, bf, af)
	e = &colorMCacheEntry{
		m:     cm,
		atime: now(),
	}
	colorMCache[key] = e

	return e.m
}

// Draw draws a given text on a given destination image dst.
//
// face is the font for text rendering.
// (x, y) represents a 'dot' (period) position.
// Be careful that this doesn't represent left-upper corner position.
// clr is the color for text rendering.
//
// Glyphs used for rendering are cached in least-recently-used way.
// It is OK to call this function with a same text and a same face at every frame in terms of performance.
//
// Be careful that the passed font face is held by this package and is never released.
// This is a known issue (#498).
//
// This function is concurrent-safe.
func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Color) {
	textM.Lock()

	fx := fixed.I(x)
	prevR := rune(-1)

	runes := []rune(text)
	glyphImgs := getGlyphImages(face, runes)
	colorm := colorToColorM(clr)

	for i, r := range runes {
		if prevR >= 0 {
			fx += face.Kern(prevR, r)
		}
		drawGlyph(dst, face, r, glyphImgs[i], fx, fixed.I(y), colorm)
		fx += glyphAdvance(face, r)

		prevR = r
	}

	textM.Unlock()
}
<commit_msg>text: Bug fix: one image should be used for multiple glyphs (#535)<commit_after>// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package text offers functions to draw texts on an Ebiten's image.
//
// For the example using a TTF font, see font package in the examples.
package text

import (
	"image"
	"image/color"
	"math"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

var (
	monotonicClock int64
)

func now() int64 {
	monotonicClock++
	return monotonicClock
}

func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
	return float64(x) / (1 << 6)
}

const (
	cacheLimit = 512 // This is an arbitrary number.
)

type colorMCacheKey uint32

type colorMCacheEntry struct {
	m     ebiten.ColorM
	atime int64
}

func drawGlyph(dst *ebiten.Image, face font.Face, r rune, img *glyphImage, x, y fixed.Int26_6, clr ebiten.ColorM) {
	if img == nil {
		return
	}

	b := getGlyphBounds(face, r)
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(fixed26_6ToFloat64(x+b.Min.X), fixed26_6ToFloat64(y+b.Min.Y))

	op.ColorM = clr
	re := image.Rect(img.x, img.y, img.x+img.width, img.y+img.height)
	op.SourceRect = &re

	_ = dst.DrawImage(img.image, op)
}

var (
	// Use pointers to avoid copying on browsers.
	glyphBoundsCache = map[font.Face]map[rune]*fixed.Rectangle26_6{}
)

func getGlyphBounds(face font.Face, r rune) *fixed.Rectangle26_6 {
	if _, ok := glyphBoundsCache[face]; !ok {
		glyphBoundsCache[face] = map[rune]*fixed.Rectangle26_6{}
	}
	if b, ok := glyphBoundsCache[face][r]; ok {
		return b
	}
	b, _, _ := face.GlyphBounds(r)
	glyphBoundsCache[face][r] = &b
	return &b
}

type glyphImage struct {
	image  *ebiten.Image
	x      int
	y      int
	width  int
	height int
}

type glyphImageCacheEntry struct {
	image *glyphImage
	atime int64
}

var (
	glyphImageCache = map[font.Face]map[rune]*glyphImageCacheEntry{}
	emptyGlyphs     = map[font.Face]map[rune]struct{}{}
)

func getGlyphImages(face font.Face, runes []rune) []*glyphImage {
	if _, ok := emptyGlyphs[face]; !ok {
		emptyGlyphs[face] = map[rune]struct{}{}
	}
	if _, ok := glyphImageCache[face]; !ok {
		glyphImageCache[face] = map[rune]*glyphImageCacheEntry{}
	}

	imgs := make([]*glyphImage, len(runes))
	neededGlyphs := map[int]*fixed.Rectangle26_6{}
	for i, r := range runes {
		if _, ok := emptyGlyphs[face][r]; ok {
			continue
		}

		if e, ok := glyphImageCache[face][r]; ok {
			e.atime = now()
			imgs[i] = e.image
			continue
		}

		b := getGlyphBounds(face, r)
		w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()
		if w == 0 || h == 0 {
			emptyGlyphs[face][r] = struct{}{}
			continue
		}

		// TODO: What if len(runes) > cacheLimit?
		if len(glyphImageCache[face]) > cacheLimit {
			oldest := int64(math.MaxInt64)
			oldestKey := rune(-1)
			for r, e := range glyphImageCache[face] {
				if e.atime < oldest {
					oldestKey = r
					oldest = e.atime
				}
			}
			delete(glyphImageCache[face], oldestKey)
		}

		neededGlyphs[i] = b
	}

	if len(neededGlyphs) > 0 {
		// TODO: What if w2 is too big (e.g. > 4096)?
		w2 := 0
		h2 := 0
		for _, b := range neededGlyphs {
			w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()
			w2 += w
			if h2 < h {
				h2 = h
			}
		}
		rgba := image.NewRGBA(image.Rect(0, 0, w2, h2))

		x := 0
		for i, b := range neededGlyphs {
			w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()

			r := runes[i]
			d := font.Drawer{
				Dst:  rgba,
				Src:  image.White,
				Face: face,
			}
			d.Dot = fixed.Point26_6{fixed.I(x) - b.Min.X, -b.Min.Y}
			d.DrawString(string(r))

			g := &glyphImage{
				image:  nil,
				x:      x,
				y:      0,
				width:  w,
				height: h,
			}
			glyphImageCache[face][r] = &glyphImageCacheEntry{
				image: g,
				atime: now(),
			}
			imgs[i] = g

			x += w
		}

		img, _ := ebiten.NewImageFromImage(rgba, ebiten.FilterDefault)
		for i := range neededGlyphs {
			imgs[i].image = img
		}
	}
	return imgs
}

var textM sync.Mutex

var (
	colorMCache = map[colorMCacheKey]*colorMCacheEntry{}
	emptyColorM ebiten.ColorM
)

func init() {
	emptyColorM.Scale(0, 0, 0, 0)
}

func colorToColorM(clr color.Color) ebiten.ColorM {
	// RGBA() is in [0 - 0xffff]. Adjust them in [0 - 0xff].
	cr, cg, cb, ca := clr.RGBA()
	cr >>= 8
	cg >>= 8
	cb >>= 8
	ca >>= 8
	if ca == 0 {
		return emptyColorM
	}
	key := colorMCacheKey(uint32(cr) | (uint32(cg) << 8) | (uint32(cb) << 16) | (uint32(ca) << 24))
	e, ok := colorMCache[key]
	if ok {
		e.atime = now()
		return e.m
	}
	if len(colorMCache) > cacheLimit {
		oldest := int64(math.MaxInt64)
		oldestKey := colorMCacheKey(0)
		for key, c := range colorMCache {
			if c.atime < oldest {
				oldestKey = key
				oldest = c.atime
			}
		}
		delete(colorMCache, oldestKey)
	}

	cm := ebiten.ColorM{}
	rf := float64(cr) / float64(ca)
	gf := float64(cg) / float64(ca)
	bf := float64(cb) / float64(ca)
	af := float64(ca) / 0xff
	cm.Scale(rf, gf, bf, af)
	e = &colorMCacheEntry{
		m:     cm,
		atime: now(),
	}
	colorMCache[key] = e

	return e.m
}

// Draw draws a given text on a given destination image dst.
//
// face is the font for text rendering.
// (x, y) represents a 'dot' (period) position.
// Be careful that this doesn't represent left-upper corner position.
// clr is the color for text rendering.
//
// Glyphs used for rendering are cached in least-recently-used way.
// It is OK to call this function with a same text and a same face at every frame in terms of performance.
//
// Be careful that the passed font face is held by this package and is never released.
// This is a known issue (#498).
//
// This function is concurrent-safe.
func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Color) {
	textM.Lock()

	fx := fixed.I(x)
	prevR := rune(-1)

	runes := []rune(text)
	glyphImgs := getGlyphImages(face, runes)
	colorm := colorToColorM(clr)

	for i, r := range runes {
		if prevR >= 0 {
			fx += face.Kern(prevR, r)
		}
		drawGlyph(dst, face, r, glyphImgs[i], fx, fixed.I(y), colorm)
		fx += glyphAdvance(face, r)

		prevR = r
	}

	textM.Unlock()
}
<|endoftext|>


<commit_before>package tomb_test

import (
	"errors"
	"launchpad.net/tomb"
	"reflect"
	"testing"
)

func TestNewTomb(t *testing.T) {
	tb := &tomb.Tomb{}
	testState(t, tb, false, false, tomb.ErrStillAlive)

	tb.Done()
	testState(t, tb, true, true, nil)
}

func TestKill(t *testing.T) {
	// a nil reason flags the goroutine as dying
	tb := &tomb.Tomb{}
	tb.Kill(nil)
	testState(t, tb, true, false, nil)

	// a non-nil reason now will override Kill
	err := errors.New("some error")
	tb.Kill(err)
	testState(t, tb, true, false, err)

	// another non-nil reason won't replace the first one
	tb.Kill(errors.New("ignore me"))
	testState(t, tb, true, false, err)

	tb.Done()
	testState(t, tb, true, true, err)
}

func TestKillf(t *testing.T) {
	tb := &tomb.Tomb{}

	err := tb.Killf("BO%s", "OM")
	if s := err.Error(); s != "BOOM" {
		t.Fatalf(`Killf("BO%s", "OM"): want "BOOM", got %q`, s)
	}
	testState(t, tb, true, false, err)

	// another non-nil reason won't replace the first one
	tb.Killf("ignore me")
	testState(t, tb, true, false, err)

	tb.Done()
	testState(t, tb, true, true, err)
}

func TestErrDying(t *testing.T) {
	// ErrDying being used properly, after a clean death.
	tb := &tomb.Tomb{}
	tb.Kill(nil)
	tb.Kill(tomb.ErrDying)
	testState(t, tb, true, false, nil)

	// ErrDying being used properly, after an errorful death.
	err := errors.New("some error")
	tb.Kill(err)
	tb.Kill(tomb.ErrDying)
	testState(t, tb, true, false, err)

	// ErrDying being used badly, with an alive tomb.
	tb = &tomb.Tomb{}
	defer func() {
		err := recover()
		if err != "tomb: Kill with ErrDying while still alive" {
			t.Fatalf("Wrong panic on Kill(ErrDying): %v", err)
		}
		testState(t, tb, false, false, tomb.ErrStillAlive)
	}()
	tb.Kill(tomb.ErrDying)
}

func testState(t *testing.T, tb *tomb.Tomb, wantDying, wantDead bool, wantErr error) {
	select {
	case <-tb.Dying():
		if !wantDying {
			t.Error("<-Dying: should block")
		}
	default:
		if wantDying {
			t.Error("<-Dying: should not block")
		}
	}
	seemsDead := false
	select {
	case <-tb.Dead():
		if !wantDead {
			t.Error("<-Dead: should block")
		}
		seemsDead = true
	default:
		if wantDead {
			t.Error("<-Dead: should not block")
		}
	}
	if err := tb.Err(); err != wantErr {
		t.Errorf("Err: want %#v, got %#v", wantErr, err)
	}
	if wantDead && seemsDead {
		waitErr := tb.Wait()
		switch {
		case waitErr == tomb.ErrStillAlive:
			t.Errorf("Wait should not return ErrStillAlive")
		case !reflect.DeepEqual(waitErr, wantErr):
			t.Errorf("Wait: want %#v, got %#v", wantErr, waitErr)
		}
	}
}
<commit_msg>Import from gopkg.in/tomb.v1 from tests.<commit_after>package tomb_test

import (
	"errors"
	"gopkg.in/tomb.v1"
	"reflect"
	"testing"
)

func TestNewTomb(t *testing.T) {
	tb := &tomb.Tomb{}
	testState(t, tb, false, false, tomb.ErrStillAlive)

	tb.Done()
	testState(t, tb, true, true, nil)
}

func TestKill(t *testing.T) {
	// a nil reason flags the goroutine as dying
	tb := &tomb.Tomb{}
	tb.Kill(nil)
	testState(t, tb, true, false, nil)

	// a non-nil reason now will override Kill
	err := errors.New("some error")
	tb.Kill(err)
	testState(t, tb, true, false, err)

	// another non-nil reason won't replace the first one
	tb.Kill(errors.New("ignore me"))
	testState(t, tb, true, false, err)

	tb.Done()
	testState(t, tb, true, true, err)
}

func TestKillf(t *testing.T) {
	tb := &tomb.Tomb{}

	err := tb.Killf("BO%s", "OM")
	if s := err.Error(); s != "BOOM" {
		t.Fatalf(`Killf("BO%s", "OM"): want "BOOM", got %q`, s)
	}
	testState(t, tb, true, false, err)

	// another non-nil reason won't replace the first one
	tb.Killf("ignore me")
	testState(t, tb, true, false, err)

	tb.Done()
	testState(t, tb, true, true, err)
}

func TestErrDying(t *testing.T) {
	// ErrDying being used properly, after a clean death.
	tb := &tomb.Tomb{}
	tb.Kill(nil)
	tb.Kill(tomb.ErrDying)
	testState(t, tb, true, false, nil)

	// ErrDying being used properly, after an errorful death.
	err := errors.New("some error")
	tb.Kill(err)
	tb.Kill(tomb.ErrDying)
	testState(t, tb, true, false, err)

	// ErrDying being used badly, with an alive tomb.
	tb = &tomb.Tomb{}
	defer func() {
		err := recover()
		if err != "tomb: Kill with ErrDying while still alive" {
			t.Fatalf("Wrong panic on Kill(ErrDying): %v", err)
		}
		testState(t, tb, false, false, tomb.ErrStillAlive)
	}()
	tb.Kill(tomb.ErrDying)
}

func testState(t *testing.T, tb *tomb.Tomb, wantDying, wantDead bool, wantErr error) {
	select {
	case <-tb.Dying():
		if !wantDying {
			t.Error("<-Dying: should block")
		}
	default:
		if wantDying {
			t.Error("<-Dying: should not block")
		}
	}
	seemsDead := false
	select {
	case <-tb.Dead():
		if !wantDead {
			t.Error("<-Dead: should block")
		}
		seemsDead = true
	default:
		if wantDead {
			t.Error("<-Dead: should not block")
		}
	}
	if err := tb.Err(); err != wantErr {
		t.Errorf("Err: want %#v, got %#v", wantErr, err)
	}
	if wantDead && seemsDead {
		waitErr := tb.Wait()
		switch {
		case waitErr == tomb.ErrStillAlive:
			t.Errorf("Wait should not return ErrStillAlive")
		case !reflect.DeepEqual(waitErr, wantErr):
			t.Errorf("Wait: want %#v, got %#v", wantErr, waitErr)
		}
	}
}
<|endoftext|>


<commit_before>package topk

import (
	"bufio"
	"bytes"
	"encoding/gob"
	"log"
	"os"
	"reflect"
	"sort"
	"testing"
)

type freqs struct {
	keys   []string
	counts map[string]int
}

func (f freqs) Len() int { return len(f.keys) }

// Actually 'Greater', since we want decreasing
func (f *freqs) Less(i, j int) bool {
	return f.counts[f.keys[i]] > f.counts[f.keys[j]] || f.counts[f.keys[i]] == f.counts[f.keys[j]] && f.keys[i] < f.keys[j]
}

func (f *freqs) Swap(i, j int) { f.keys[i], f.keys[j] = f.keys[j], f.keys[i] }

func TestTopK(t *testing.T) {

	f, err := os.Open("testdata/domains.txt")

	if err != nil {
		t.Fatal(err)
	}

	scanner := bufio.NewScanner(f)

	tk := New(100)
	exact := make(map[string]int)

	for scanner.Scan() {

		item := scanner.Text()

		exact[item]++
		e := tk.Insert(item, 1)
		if e.Count < exact[item] {
			t.Errorf("estimate lower than exact: key=%v, exact=%v, estimate=%v", e.Key, exact[item], e.Count)
		}
		if e.Count-e.Error > exact[item] {
			t.Errorf("error bounds too large: key=%v, count=%v, error=%v, exact=%v", e.Key, e.Count, e.Error, exact[item])
		}
	}

	if err := scanner.Err(); err != nil {
		log.Println("error during scan: ", err)
	}

	var keys []string

	for k, _ := range exact {
		keys = append(keys, k)
	}

	freq := &freqs{keys: keys, counts: exact}

	sort.Sort(freq)

	top := tk.Keys()

	// at least the top 25 must be in order
	for i := 0; i < 25; i++ {
		if top[i].Key != freq.keys[i] {
			t.Errorf("key mismatch: idx=%d top=%s (%d) exact=%s (%d)", i, top[i].Key, top[i].Count, freq.keys[i], freq.counts[freq.keys[i]])
		}
	}
	for k, v := range exact {
		e := tk.Estimate(k)
		if e.Count < v {
			t.Errorf("estimate lower than exact: key=%v, exact=%v, estimate=%v", e.Key, v, e.Count)
		}
		if e.Count-e.Error > v {
			t.Errorf("error bounds too large: key=%v, count=%v, error=%v, exact=%v", e.Key, e.Count, e.Error, v)
		}
	}
	for _, k := range top {
		e := tk.Estimate(k.Key)
		if e != k {
			t.Errorf("estimate differs from top keys: key=%v, estimate=%v(-%v) top=%v(-v)", e.Key, e.Count, e.Error, k.Count, k.Error)
		}
	}

	// gob
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(tk); err != nil {
		t.Error(err)
	}

	decoded := New(100)
	dec := gob.NewDecoder(&buf)
	if err := dec.Decode(decoded); err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(tk, decoded) {
		t.Error("they are not equal.")
	}
}
<commit_msg>Fix Errorf line in test missing a percent sign (#4)<commit_after>package topk

import (
	"bufio"
	"bytes"
	"encoding/gob"
	"log"
	"os"
	"reflect"
	"sort"
	"testing"
)

type freqs struct {
	keys   []string
	counts map[string]int
}

func (f freqs) Len() int { return len(f.keys) }

// Actually 'Greater', since we want decreasing
func (f *freqs) Less(i, j int) bool {
	return f.counts[f.keys[i]] > f.counts[f.keys[j]] || f.counts[f.keys[i]] == f.counts[f.keys[j]] && f.keys[i] < f.keys[j]
}

func (f *freqs) Swap(i, j int) { f.keys[i], f.keys[j] = f.keys[j], f.keys[i] }

func TestTopK(t *testing.T) {

	f, err := os.Open("testdata/domains.txt")

	if err != nil {
		t.Fatal(err)
	}

	scanner := bufio.NewScanner(f)

	tk := New(100)
	exact := make(map[string]int)

	for scanner.Scan() {

		item := scanner.Text()

		exact[item]++
		e := tk.Insert(item, 1)
		if e.Count < exact[item] {
			t.Errorf("estimate lower than exact: key=%v, exact=%v, estimate=%v", e.Key, exact[item], e.Count)
		}
		if e.Count-e.Error > exact[item] {
			t.Errorf("error bounds too large: key=%v, count=%v, error=%v, exact=%v", e.Key, e.Count, e.Error, exact[item])
		}
	}

	if err := scanner.Err(); err != nil {
		log.Println("error during scan: ", err)
	}

	var keys []string

	for k, _ := range exact {
		keys = append(keys, k)
	}

	freq := &freqs{keys: keys, counts: exact}

	sort.Sort(freq)

	top := tk.Keys()

	// at least the top 25 must be in order
	for i := 0; i < 25; i++ {
		if top[i].Key != freq.keys[i] {
			t.Errorf("key mismatch: idx=%d top=%s (%d) exact=%s (%d)", i, top[i].Key, top[i].Count, freq.keys[i], freq.counts[freq.keys[i]])
		}
	}
	for k, v := range exact {
		e := tk.Estimate(k)
		if e.Count < v {
			t.Errorf("estimate lower than exact: key=%v, exact=%v, estimate=%v", e.Key, v, e.Count)
		}
		if e.Count-e.Error > v {
			t.Errorf("error bounds too large: key=%v, count=%v, error=%v, exact=%v", e.Key, e.Count, e.Error, v)
		}
	}
	for _, k := range top {
		e := tk.Estimate(k.Key)
		if e != k {
			t.Errorf("estimate differs from top keys: key=%v, estimate=%v(-%v) top=%v(-%v)", e.Key, e.Count, e.Error, k.Count, k.Error)
		}
	}

	// gob
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(tk); err != nil {
		t.Error(err)
	}

	decoded := New(100)
	dec := gob.NewDecoder(&buf)
	if err := dec.Decode(decoded); err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(tk, decoded) {
		t.Error("they are not equal.")
	}
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"
	"time"

	"github.com/go-sql-driver/mysql"
)

/*
	These are more functions for querying the "users" table,
	but these functions are only used for the website
*/

/*
	Data structures
*/

type StatsSeeded struct {
	TrueSkill float32
	Sigma     float32
	NumRaces  int
	LastRace  mysql.NullTime
}

type StatsUnseeded struct {
	AdjustedAverage int
	RealAverage     int
	NumRaces        int
	NumForfeits     int
	ForfeitPenalty  int
	LowestTime      int
	LastRace        mysql.NullTime
}

type StatsDiversity struct {
	TrueSkill    float64
	Sigma        float64
	Change       float64
	NumRaces     int
	LastRace     mysql.NullTime
	NewTrueSkill float64 // Only used when doing new TrueSkill calculation
}

// ProfilesRow gets each row for all profiles
type ProfilesRow struct {
	Username        string
	DatetimeCreated time.Time
	StreamURL       string
	NumAchievements int
	TotalRaces      int
}

// ProfileData has all data for each racer
type ProfileData struct {
	Username          string
	DatetimeCreated   time.Time
	DatetimeLastLogin time.Time
	Admin             int
	Verified          bool
	StatsSeeded       StatsSeeded
	StatsUnseeded     StatsUnseeded
	StatsDiversity    StatsDiversity
	StreamURL         string
	Banned            bool
}

/*
type LeaderboardRowMostPlayed struct {
	Name     string
	Total    int
	Verified int
}
*/

/*
	Functions
*/

/*
func (*Users) GetStatsSeeded(username string) (StatsSeeded, error) {
	var stats StatsSeeded
	if err := db.QueryRow(`
		SELECT
			seeded_trueskill,
			seeded_trueskill_sigma,
			seeded_num_races,
			seeded_last_race
		FROM
			users
		WHERE
			username = ?
	`, username).Scan(
		&stats.ELO,
		&stats.NumSeededRaces,
		&stats.LastSeededRace,
	); err != nil {
		return stats, err
	} else {
		return stats, nil
	}
}

func (*Users) GetStatsUnseeded(username string) (StatsUnseeded, error) {
	var stats StatsUnseeded
	if err := db.QueryRow(`
		SELECT
			unseeded_adjusted_average,
			unseeded_real_average,
			num_unseeded_races,
			num_forfeits,
			forfeit_penalty,
			lowest_unseeded_time,
			last_unseeded_race
		FROM
			users
		WHERE
			username = ?
	`, username).Scan(
		&stats.UnseededAdjustedAverage,
		&stats.UnseededRealAverage,
		&stats.NumUnseededRaces,
		&stats.NumForfeits,
		&stats.ForfeitPenalty,
		&stats.LowestUnseededTime,
		&stats.LastUnseededRace,
	); err != nil {
		return stats, err
	} else {
		return stats, nil
	}
}
*/

// GetProfileData gets player data to populate the player's profile page
func (*Users) GetProfileData(username string) (ProfileData, error) {
	var profileData ProfileData
	var rawVerified int
	if err := db.QueryRow(`
		SELECT
			u.username,
			u.datetime_created,
			u.datetime_last_login,
			u.admin,
			u.verified,
			u.seeded_trueskill,
			u.seeded_trueskill_sigma,
			u.seeded_num_races,
			u.seeded_last_race,
			u.unseeded_adjusted_average,
			u.unseeded_real_average,
			u.unseeded_num_races,
			u.unseeded_num_forfeits,
			u.unseeded_forfeit_penalty,
			u.unseeded_lowest_time,
			u.unseeded_last_race,
			u.stream_url,
			CASE WHEN u.id IN (SELECT user_id FROM banned_users) THEN 1 ELSE 0 END AS BIT

		FROM
			users u
		WHERE
			steam_id > 0 and
			username = ?
	`, username).Scan(
		&profileData.Username,
		&profileData.DatetimeCreated,
		&profileData.DatetimeLastLogin,
		&profileData.Admin,
		&rawVerified,
		&profileData.StatsSeeded.TrueSkill,
		&profileData.StatsSeeded.Sigma,
		&profileData.StatsSeeded.NumRaces,
		&profileData.StatsSeeded.LastRace,
		&profileData.StatsUnseeded.AdjustedAverage,
		&profileData.StatsUnseeded.RealAverage,
		&profileData.StatsUnseeded.NumRaces,
		&profileData.StatsUnseeded.NumForfeits,
		&profileData.StatsUnseeded.ForfeitPenalty,
		&profileData.StatsUnseeded.LowestTime,
		&profileData.StatsUnseeded.LastRace,
		&profileData.StreamURL,
		&profileData.Banned,
	); err == sql.ErrNoRows {
		return profileData, nil
	} else if err != nil {
		return profileData, err
	} else {
		// Convert the int to a bool
		if rawVerified == 1 {
			profileData.Verified = true
		}
		return profileData, nil
	}
}

// GetUserProfiles gets players data to populate the profiles page
func (*Users) GetUserProfiles(currentPage int, usersPerPage int) ([]ProfilesRow, int, error) {
	usersOffset := (currentPage - 1) * usersPerPage
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.datetime_created,
			u.stream_url,
			count(ua.achievement_id),
			(
				SELECT COUNT(id)
				FROM race_participants
				WHERE user_id = u.id
			) AS num_total_race
		FROM
			users u
		LEFT JOIN
			user_achievements ua
			ON
				u.id = ua.user_id
		WHERE
			u.steam_id > 0
		GROUP BY
			u.username
		ORDER BY
			u.username ASC
		LIMIT
			?
		OFFSET
			?
	`, usersPerPage, usersOffset); err == sql.ErrNoRows {
		return nil, 0, nil
	} else if err != nil {
		return nil, 0, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the user profile results
	profiles := make([]ProfilesRow, 0)
	for rows.Next() {
		var row ProfilesRow
		if err := rows.Scan(
			&row.Username,
			&row.DatetimeCreated,
			&row.StreamURL,
			&row.NumAchievements,
			&row.TotalRaces,
		); err != nil {
			return nil, 0, err
		}

		profiles = append(profiles, row)
	}

	// Find total amount of users
	var allProfilesCount int
	if err := db.QueryRow(`
		SELECT count(id)
		FROM users
		WHERE steam_id > 0
	`).Scan(&allProfilesCount); err != nil {
		return nil, 0, err
	}

	return profiles, allProfilesCount, nil
}

// Make a leaderboard for the unseeded format based on all of the users
type LeaderboardRowUnseeded struct {
	Name            string
	AdjustedAverage int
	RealAverage     int
	NumRaces        int
	NumForfeits     int
	ForfeitPenalty  int
	LowestTime      int
	LastRace        time.Time
	LastRaceId      int
	Verified        int
	StreamURL       string
}

func (*Users) GetLeaderboardUnseeded(racesNeeded int, racesLimit int) ([]LeaderboardRowUnseeded, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.unseeded_adjusted_average,
			u.unseeded_real_average,
			u.unseeded_num_races,
			u.unseeded_num_forfeits,
			u.unseeded_forfeit_penalty,
			u.unseeded_lowest_time,
			u.unseeded_last_race,
			MAX(rp.race_id),
			u.verified,
			u.stream_url
		FROM
			users u
			LEFT JOIN race_participants rp
				ON rp.user_id = u.id
			LEFT JOIN races r
				ON r.id = rp.race_id
		WHERE
			u.unseeded_num_races >= ?
			AND u.id NOT IN (SELECT user_id FROM banned_users)
		GROUP BY
			u.username
		ORDER BY
			unseeded_adjusted_average ASC
		LIMIT ?
	`, racesNeeded, racesLimit); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowUnseeded, 0)
	for rows.Next() {
		var row LeaderboardRowUnseeded
		if err := rows.Scan(
			&row.Name,
			&row.AdjustedAverage,
			&row.RealAverage,
			&row.NumRaces,
			&row.NumForfeits,
			&row.ForfeitPenalty,
			&row.LowestTime,
			&row.LastRace,
			&row.LastRaceId,
			&row.Verified,
			&row.StreamURL,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}
	return leaderboard, nil
}

// Make a leaderboard for the seeded format based on all of the users
type LeaderboardRowSeeded struct {
	Name      string
	TrueSkill float64
	NumRaces  int
	LastRace  time.Time
	Verified  int
}

func (*Users) GetLeaderboardSeeded(racesNeeded int, racesLimit int) ([]LeaderboardRowSeeded, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.seeded_trueskill,
			u.seeded_trueskill_sigma,
			u.seeded_num_races,
			u.seeded_last_race,
			u.verified,
		FROM
			users u
		WHERE
			u.seeded_num_races > ?
			AND u.id NOT IN (SELECT user_id FROM banned_users)
		GROUP BY u.username
		LIMIT ?
	`, racesNeeded, racesLimit); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowSeeded, 0)
	for rows.Next() {
		var row LeaderboardRowSeeded
		if err := rows.Scan(
			&row.Name,
			&row.TrueSkill,
			&row.NumRaces,
			&row.LastRace,
			&row.Verified,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}

	return leaderboard, nil
}

type LeaderboardRowDiversity struct {
	Name              string
	DivTrueSkill      float64
	DivTrueSkillDelta float64
	DivNumRaces       sql.NullInt64
	DivLowestTime     sql.NullInt64
	DivLastRace       time.Time
	DivLastRaceId     int
	Verified          int
	StreamURL         string
}

func (*Users) GetLeaderboardDiversity(racesNeeded int, racesLimit int) ([]LeaderboardRowDiversity, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.diversity_trueskill,
			ROUND(u.diversity_trueskill_change, 2),
			u.diversity_num_races,
			(SELECT
					MIN(run_time)
				FROM
					race_participants
				LEFT JOIN races
					ON race_participants.race_id = races.id
				WHERE
					place > 0
					AND u.id = user_id
					AND races.format = 'diversity') as r_time,
			u.diversity_last_race,
			MAX(rp.race_id),
			u.verified,
			u.stream_url
		FROM
			users u
			LEFT JOIN
				race_participants rp ON rp.user_id = u.id
			LEFT JOIN
				races r ON r.id = rp.race_id
		WHERE
			diversity_num_races >= ?
				AND r.format = 'diversity'
				AND rp.place > 0
				AND u.id NOT IN (SELECT user_id FROM banned_users)
		GROUP BY u.username
		ORDER BY u.diversity_trueskill DESC
		LIMIT ?
	`, racesNeeded, racesLimit); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowDiversity, 0)
	for rows.Next() {
		var row LeaderboardRowDiversity
		if err := rows.Scan(
			&row.Name,
			&row.DivTrueSkill,
			&row.DivTrueSkillDelta,
			&row.DivNumRaces,
			&row.DivLowestTime,
			&row.DivLastRace,
			&row.DivLastRaceId,
			&row.Verified,
			&row.StreamURL,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}
	return leaderboard, nil
}
<commit_msg>This looks bad<commit_after>package models

import (
	"database/sql"
	"time"

	"github.com/go-sql-driver/mysql"
)

/*
	These are more functions for querying the "users" table,
	but these functions are only used for the website
*/

/*
	Data structures
*/

type StatsSeeded struct {
	TrueSkill float32
	Sigma     float32
	NumRaces  int
	LastRace  mysql.NullTime
}

type StatsUnseeded struct {
	AdjustedAverage int
	RealAverage     int
	NumRaces        int
	NumForfeits     int
	ForfeitPenalty  int
	LowestTime      int
	LastRace        mysql.NullTime
}

type StatsDiversity struct {
	TrueSkill    float64
	Sigma        float64
	Change       float64
	NumRaces     int
	LastRace     mysql.NullTime
	NewTrueSkill float64 // Only used when doing new TrueSkill calculation
}

// ProfilesRow gets each row for all profiles
type ProfilesRow struct {
	Username        string
	DatetimeCreated time.Time
	StreamURL       string
	NumAchievements int
	TotalRaces      int
}

// ProfileData has all data for each racer
type ProfileData struct {
	Username          string
	DatetimeCreated   time.Time
	DatetimeLastLogin time.Time
	Admin             int
	Verified          bool
	StatsSeeded       StatsSeeded
	StatsUnseeded     StatsUnseeded
	StatsDiversity    StatsDiversity
	StreamURL         string
	Banned            bool
}

/*
type LeaderboardRowMostPlayed struct {
	Name     string
	Total    int
	Verified int
}
*/

/*
	Functions
*/

/*
func (*Users) GetStatsSeeded(username string) (StatsSeeded, error) {
	var stats StatsSeeded
	if err := db.QueryRow(`
		SELECT
			seeded_trueskill,
			seeded_trueskill_sigma,
			seeded_num_races,
			seeded_last_race
		FROM
			users
		WHERE
			username = ?
	`, username).Scan(
		&stats.ELO,
		&stats.NumSeededRaces,
		&stats.LastSeededRace,
	); err != nil {
		return stats, err
	} else {
		return stats, nil
	}
}

func (*Users) GetStatsUnseeded(username string) (StatsUnseeded, error) {
	var stats StatsUnseeded
	if err := db.QueryRow(`
		SELECT
			unseeded_adjusted_average,
			unseeded_real_average,
			num_unseeded_races,
			num_forfeits,
			forfeit_penalty,
			lowest_unseeded_time,
			last_unseeded_race
		FROM
			users
		WHERE
			username = ?
	`, username).Scan(
		&stats.UnseededAdjustedAverage,
		&stats.UnseededRealAverage,
		&stats.NumUnseededRaces,
		&stats.NumForfeits,
		&stats.ForfeitPenalty,
		&stats.LowestUnseededTime,
		&stats.LastUnseededRace,
	); err != nil {
		return stats, err
	} else {
		return stats, nil
	}
}
*/

// GetProfileData gets player data to populate the player's profile page
func (*Users) GetProfileData(username string) (ProfileData, error) {
	var profileData ProfileData
	var rawVerified int
	if err := db.QueryRow(`
		SELECT
			u.username,
			u.datetime_created,
			u.datetime_last_login,
			u.admin,
			u.verified,
			u.seeded_trueskill,
			u.seeded_trueskill_sigma,
			u.seeded_num_races,
			u.seeded_last_race,
			u.unseeded_adjusted_average,
			u.unseeded_real_average,
			u.unseeded_num_races,
			u.unseeded_num_forfeits,
			u.unseeded_forfeit_penalty,
			u.unseeded_lowest_time,
			u.unseeded_last_race,
			u.stream_url,
			CASE WHEN u.id IN (SELECT user_id FROM banned_users) THEN 1 ELSE 0 END AS BIT

		FROM
			users u
		WHERE
			steam_id > 0 and
			username = ?
	`, username).Scan(
		&profileData.Username,
		&profileData.DatetimeCreated,
		&profileData.DatetimeLastLogin,
		&profileData.Admin,
		&rawVerified,
		&profileData.StatsSeeded.TrueSkill,
		&profileData.StatsSeeded.Sigma,
		&profileData.StatsSeeded.NumRaces,
		&profileData.StatsSeeded.LastRace,
		&profileData.StatsUnseeded.AdjustedAverage,
		&profileData.StatsUnseeded.RealAverage,
		&profileData.StatsUnseeded.NumRaces,
		&profileData.StatsUnseeded.NumForfeits,
		&profileData.StatsUnseeded.ForfeitPenalty,
		&profileData.StatsUnseeded.LowestTime,
		&profileData.StatsUnseeded.LastRace,
		&profileData.StreamURL,
		&profileData.Banned,
	); err == sql.ErrNoRows {
		return profileData, nil
	} else if err != nil {
		return profileData, err
	} else {
		// Convert the int to a bool
		if rawVerified == 1 {
			profileData.Verified = true
		}
		return profileData, nil
	}
}

// GetUserProfiles gets players data to populate the profiles page
func (*Users) GetUserProfiles(currentPage int, usersPerPage int) ([]ProfilesRow, int, error) {
	usersOffset := (currentPage - 1) * usersPerPage
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.datetime_created,
			u.stream_url,
			count(ua.achievement_id),
			(
				SELECT COUNT(id)
				FROM race_participants
				WHERE user_id = u.id
			) AS num_total_race
		FROM
			users u
		LEFT JOIN
			user_achievements ua
			ON
				u.id = ua.user_id
		WHERE
			u.steam_id > 0
		GROUP BY
			u.username
		ORDER BY
			u.username ASC
		LIMIT
			?
		OFFSET
			?
	`, usersPerPage, usersOffset); err == sql.ErrNoRows {
		return nil, 0, nil
	} else if err != nil {
		return nil, 0, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the user profile results
	profiles := make([]ProfilesRow, 0)
	for rows.Next() {
		var row ProfilesRow
		if err := rows.Scan(
			&row.Username,
			&row.DatetimeCreated,
			&row.StreamURL,
			&row.NumAchievements,
			&row.TotalRaces,
		); err != nil {
			return nil, 0, err
		}

		profiles = append(profiles, row)
	}

	// Find total amount of users
	var allProfilesCount int
	if err := db.QueryRow(`
		SELECT count(id)
		FROM users
		WHERE steam_id > 0
	`).Scan(&allProfilesCount); err != nil {
		return nil, 0, err
	}

	return profiles, allProfilesCount, nil
}

// Make a leaderboard for the unseeded format based on all of the users
type LeaderboardRowUnseeded struct {
	Name            string
	AdjustedAverage int
	RealAverage     int
	NumRaces        int
	NumForfeits     int
	ForfeitPenalty  int
	LowestTime      int
	LastRace        time.Time
	LastRaceId      int
	Verified        int
	StreamURL       string
}

func (*Users) GetLeaderboardUnseeded(racesNeeded int, racesLimit int) ([]LeaderboardRowUnseeded, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.unseeded_adjusted_average,
			u.unseeded_real_average,
			u.unseeded_num_races,
			u.unseeded_num_forfeits,
			u.unseeded_forfeit_penalty,
			u.unseeded_lowest_time,
			u.unseeded_last_race,
			MAX(rp.race_id),
			u.verified,
			u.stream_url
		FROM
			users u
			LEFT JOIN race_participants rp
				ON rp.user_id = u.id
			LEFT JOIN races r
				ON r.id = rp.race_id
		WHERE
			u.unseeded_num_races >= ?
			AND u.id NOT IN (SELECT user_id FROM banned_users)
		GROUP BY
			u.username
		ORDER BY
			unseeded_adjusted_average ASC
		LIMIT ?
	`, racesNeeded, racesLimit); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowUnseeded, 0)
	for rows.Next() {
		var row LeaderboardRowUnseeded
		if err := rows.Scan(
			&row.Name,
			&row.AdjustedAverage,
			&row.RealAverage,
			&row.NumRaces,
			&row.NumForfeits,
			&row.ForfeitPenalty,
			&row.LowestTime,
			&row.LastRace,
			&row.LastRaceId,
			&row.Verified,
			&row.StreamURL,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}
	return leaderboard, nil
}

// Make a leaderboard for the seeded format based on all of the users
type LeaderboardRowSeeded struct {
	Name      string
	TrueSkill float64
	NumRaces  int
	LastRace  time.Time
	Verified  int
}

func (*Users) GetLeaderboardSeeded(racesNeeded int, racesLimit int) ([]LeaderboardRowSeeded, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			u.seeded_trueskill,
			u.seeded_trueskill_sigma,
			u.seeded_num_races,
			u.seeded_last_race,
			u.verified,
		FROM
			users u
		WHERE
			u.seeded_num_races > ?
			AND u.id NOT IN (SELECT user_id FROM banned_users)
		GROUP BY u.username
		LIMIT ?
	`, racesNeeded, racesLimit); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowSeeded, 0)
	for rows.Next() {
		var row LeaderboardRowSeeded
		if err := rows.Scan(
			&row.Name,
			&row.TrueSkill,
			&row.NumRaces,
			&row.LastRace,
			&row.Verified,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}

	return leaderboard, nil
}

type LeaderboardRowDiversity struct {
	Name              string
	DivTrueSkill      float64
	DivTrueSkillDelta float64
	DivNumRaces       sql.NullInt64
	DivLowestTime     sql.NullInt64
	DivLastRace       time.Time
	DivLastRaceId     int
	Verified          int
	StreamURL         string
}

func (*Users) GetLeaderboardDiversity(racesNeeded int, racesLimit int) ([]LeaderboardRowDiversity, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
			u.username,
			ROUND(u.diversity_trueskill, 2),
			ROUND(u.diversity_trueskill_change, 2),
			u.diversity_num_races,
			(SELECT
					MIN(run_time)
				FROM
					race_participants
				LEFT JOIN races
					ON race_participants.race_id = races.id
				WHERE
					place > 0
					AND u.id = user_id
					AND races.format = 'diversity') as r_time,
			u.diversity_last_race,
			MAX(rp.race_id),
			u.verified,
			u.stream_url
		FROM
			users u
			LEFT JOIN
				race_participants rp ON rp.user_id = u.id
			LEFT JOIN
				races r ON r.id = rp.race_id
		WHERE
			diversity_num_races >= ?
				AND r.format = 'diversity'
				AND rp.place > 0
				AND u.id NOT IN (SELECT user_id FROM banned_users)
		GROUP BY u.username
		ORDER BY u.diversity_trueskill DESC
		LIMIT ?
	`, racesNeeded, racesLimit); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	// Iterate over the users
	leaderboard := make([]LeaderboardRowDiversity, 0)
	for rows.Next() {
		var row LeaderboardRowDiversity
		if err := rows.Scan(
			&row.Name,
			&row.DivTrueSkill,
			&row.DivTrueSkillDelta,
			&row.DivNumRaces,
			&row.DivLowestTime,
			&row.DivLastRace,
			&row.DivLastRaceId,
			&row.Verified,
			&row.StreamURL,
		); err != nil {
			return nil, err
		}

		// Append this row to the leaderboard
		leaderboard = append(leaderboard, row)
	}
	return leaderboard, nil
}
<|endoftext|>


<commit_before>package three

import (
	"github.com/go-gl/mathgl/mgl32"
)

// Transform stores information about the position, rotation and scale
// of an 3D object.
type Transform struct {
	position   mgl32.Vec3
	rotation   mgl32.Vec3
	quaternion mgl32.Quat
	scale      mgl32.Vec3

	multiplier float32

	Up      mgl32.Vec3
	Right   mgl32.Vec3
	Forward mgl32.Vec3

	matrix mgl32.Mat4
}

// NewTransform creates a new Transform struct with defaults.
// The given multiplier can be used to invert the matrix, e.g. camera matrix
// This value should be 1 or -1 (inverted).
//
// Position: 0,0,0
// Rotation: 0,0,0
// Scale:    1,1,1
//
// Up:       0,1,0
// Right:    1,0,0
// Forward:  0,0,-1
func NewTransform(multiplier float32) Transform {
	return Transform{
		position:   mgl32.Vec3{0, 0, 0},
		rotation:   mgl32.Vec3{0, 0, 0},
		quaternion: mgl32.QuatIdent(),
		scale:      mgl32.Vec3{1, 1, 1},

		multiplier: multiplier,

		Up:      mgl32.Vec3{0, 1, 0},
		Right:   mgl32.Vec3{1, 0, 0},
		Forward: mgl32.Vec3{0, 0, -1},

		matrix: mgl32.Ident4(),
	}
}

// SetPosition sets the position of the 3D object
// and updates it's matrix accordingly.
func (t *Transform) SetPosition(x, y, z float32) {
	t.position = mgl32.Vec3{x, y, z}

	t.matrix[12] = x * t.multiplier
	t.matrix[13] = y * t.multiplier
	t.matrix[14] = z * t.multiplier
}

// TranslateX moves the object along the x axis by the given units.
// The model matrix is updated accordingly.
func (t *Transform) TranslateX(x float32) {
	t.position[0] += x

	t.matrix[12] = t.position[0]
}

// TranslateY moves the object along the y axis by the given units.
// The model matrix is updated accordingly.
func (t *Transform) TranslateY(y float32) {
	t.position[1] += y

	t.matrix[13] = t.position[1]
}

// TranslateZ moves the object along the z axis by the given units.
// The model matrix is updated accordingly.
func (t *Transform) TranslateZ(z float32) {
	t.position[2] += z

	t.matrix[14] = t.position[2]
}

// Translate moves the object by the given vector.
// The model matrix is updated accordingly.
func (t *Transform) Translate(v mgl32.Vec3) {
	t.position = t.position.Add(v)

	t.matrix[12] = t.position[0]
	t.matrix[13] = t.position[1]
	t.matrix[14] = t.position[2]
}

// Scale sets the scale factor of the 3D object to the given values
// and updates it's matrix accordingly.
func (t *Transform) Scale(x, y, z float32) {
	t.scale = mgl32.Vec3{x, y, z}

	t.matrix[0] = x * t.multiplier
	t.matrix[5] = y * t.multiplier
	t.matrix[10] = z * t.multiplier
}

// RotateX rotates the 3D object by the given angle (in radians) around the x axis.
// The model matrix is updated accordingly.
func (t *Transform) RotateX(angle float32) {
	t.rotation[0] += angle

	v1 := mgl32.Vec3{1, 0, 0}
	t.rotateOnAxis(v1, angle)
}

// RotateY rotates the 3D object by the given angle (in radians) around the x axis.
// The model matrix is updated accordingly.
func (t *Transform) RotateY(angle float32) {
	t.rotation[1] += angle

	v1 := mgl32.Vec3{0, 1, 0}
	t.rotateOnAxis(v1, angle)
}

// RotateZ rotates the 3D object by the given angle (in radians) around the x axis.
// The model matrix is updated accordingly.
func (t *Transform) RotateZ(angle float32) {
	t.rotation[2] += angle

	v1 := mgl32.Vec3{0, 0, 1}
	t.rotateOnAxis(v1, angle)
}

func (t *Transform) rotateOnAxis(axis mgl32.Vec3, angle float32) {
	q1 := mgl32.QuatRotate(angle*t.multiplier, axis)
	t.quaternion = t.quaternion.Mul(q1)

	t.matrix = t.matrix.Mul4(q1.Mat4())
}

// LookAt changes the transformation of the 3D object
// to face the target's position. The model matrix
// will be updated accordingly.
//
// Note: This transformation makes use of the up vector.
func (t *Transform) LookAt(x, y, z float32) {
	target := mgl32.Vec3{x, y, z}

	t.matrix = mgl32.LookAtV(
		t.position,
		target,
		t.Up,
	)
}

func (t *Transform) modelMatrix() mgl32.Mat4 {
	return t.matrix
}
<commit_msg>fixed translation to use multiplier for correct camera translation<commit_after>package three

import (
	"github.com/go-gl/mathgl/mgl32"
)

// Transform stores information about the position, rotation and scale
// of an 3D object.
type Transform struct {
	position   mgl32.Vec3
	rotation   mgl32.Vec3
	quaternion mgl32.Quat
	scale      mgl32.Vec3

	multiplier float32

	Up      mgl32.Vec3
	Right   mgl32.Vec3
	Forward mgl32.Vec3

	matrix mgl32.Mat4
}

// NewTransform creates a new Transform struct with defaults.
// The given multiplier can be used to invert the matrix, e.g. camera matrix
// This value should be 1 or -1 (inverted).
//
// Position: 0,0,0
// Rotation: 0,0,0
// Scale:    1,1,1
//
// Up:       0,1,0
// Right:    1,0,0
// Forward:  0,0,-1
func NewTransform(multiplier float32) Transform {
	return Transform{
		position:   mgl32.Vec3{0, 0, 0},
		rotation:   mgl32.Vec3{0, 0, 0},
		quaternion: mgl32.QuatIdent(),
		scale:      mgl32.Vec3{1, 1, 1},

		multiplier: multiplier,

		Up:      mgl32.Vec3{0, 1, 0},
		Right:   mgl32.Vec3{1, 0, 0},
		Forward: mgl32.Vec3{0, 0, -1},

		matrix: mgl32.Ident4(),
	}
}

// SetPosition sets the position of the 3D object
// and updates it's matrix accordingly.
func (t *Transform) SetPosition(x, y, z float32) {
	t.position = mgl32.Vec3{x, y, z}

	t.matrix[12] = x * t.multiplier
	t.matrix[13] = y * t.multiplier
	t.matrix[14] = z * t.multiplier
}

// TranslateX moves the object along the x axis by the given units.
// The model matrix is updated accordingly.
func (t *Transform) TranslateX(x float32) {
	t.position[0] += x

	t.matrix[12] = t.position[0] * t.multiplier
}

// TranslateY moves the object along the y axis by the given units.
// The model matrix is updated accordingly.
func (t *Transform) TranslateY(y float32) {
	t.position[1] += y

	t.matrix[13] = t.position[1] * t.multiplier
}

// TranslateZ moves the object along the z axis by the given units.
// The model matrix is updated accordingly.
func (t *Transform) TranslateZ(z float32) {
	t.position[2] += z

	t.matrix[14] = t.position[2] * t.multiplier
}

// Translate moves the object by the given vector.
// The model matrix is updated accordingly.
func (t *Transform) Translate(v mgl32.Vec3) {
	t.position = t.position.Add(v)

	t.matrix[12] = t.position[0] * t.multiplier
	t.matrix[13] = t.position[1] * t.multiplier
	t.matrix[14] = t.position[2] * t.multiplier
}

// Scale sets the scale factor of the 3D object to the given values
// and updates it's matrix accordingly.
func (t *Transform) Scale(x, y, z float32) {
	t.scale = mgl32.Vec3{x, y, z}

	t.matrix[0] = x * t.multiplier
	t.matrix[5] = y * t.multiplier
	t.matrix[10] = z * t.multiplier
}

// RotateX rotates the 3D object by the given angle (in radians) around the x axis.
// The model matrix is updated accordingly.
func (t *Transform) RotateX(angle float32) {
	t.rotation[0] += angle

	v1 := mgl32.Vec3{1, 0, 0}
	t.rotateOnAxis(v1, angle)
}

// RotateY rotates the 3D object by the given angle (in radians) around the x axis.
// The model matrix is updated accordingly.
func (t *Transform) RotateY(angle float32) {
	t.rotation[1] += angle

	v1 := mgl32.Vec3{0, 1, 0}
	t.rotateOnAxis(v1, angle)
}

// RotateZ rotates the 3D object by the given angle (in radians) around the x axis.
// The model matrix is updated accordingly.
func (t *Transform) RotateZ(angle float32) {
	t.rotation[2] += angle

	v1 := mgl32.Vec3{0, 0, 1}
	t.rotateOnAxis(v1, angle)
}

func (t *Transform) rotateOnAxis(axis mgl32.Vec3, angle float32) {
	q1 := mgl32.QuatRotate(angle*t.multiplier, axis)
	t.quaternion = t.quaternion.Mul(q1)

	t.matrix = t.matrix.Mul4(q1.Mat4())
}

// LookAt changes the transformation of the 3D object
// to face the target's position. The model matrix
// will be updated accordingly.
//
// Note: This transformation makes use of the up vector.
func (t *Transform) LookAt(x, y, z float32) {
	target := mgl32.Vec3{x, y, z}

	t.matrix = mgl32.LookAtV(
		t.position,
		target,
		t.Up,
	)
}

func (t *Transform) modelMatrix() mgl32.Mat4 {
	return t.matrix
}
<|endoftext|>


<commit_before>package httpclient

import (
	"crypto/tls"
	"net/http"
)

var HttpTransport = &http.Transport{
	DisableCompression: true,
}

var HttpClient = &http.Client{
	Transport: HttpTransport,
}

var InsecureTlsConfig = &tls.Config{
	InsecureSkipVerify: true,
}

var InsecureHttpTransport = &http.Transport{
	TLSClientConfig:    InsecureTlsConfig,
	DisableCompression: true,
}

var InsecureHttpClient = &http.Client{
	Transport: InsecureHttpTransport,
}
<commit_msg>Enable option to use proxy<commit_after>package httpclient

import (
	"crypto/tls"
	"net/http"
)

var HttpTransport = &http.Transport{
	DisableCompression: true,
	Proxy:              http.ProxyFromEnvironment,
}

var HttpClient = &http.Client{
	Transport: HttpTransport,
}

var InsecureTlsConfig = &tls.Config{
	InsecureSkipVerify: true,
}

var InsecureHttpTransport = &http.Transport{
	TLSClientConfig:    InsecureTlsConfig,
	DisableCompression: true,
	Proxy:              http.ProxyFromEnvironment,
}

var InsecureHttpClient = &http.Client{
	Transport: InsecureHttpTransport,
}
<|endoftext|>


<commit_before>package tunnel

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/ondevice/ondevice/api"
)

// WSListener -- WebSocket listener
type WSListener interface {
	OnMessage(msgType int, data []byte)
}

// Connection -- WebSocket connection
type Connection struct {
	ws        *websocket.Conn
	onMessage func(int, []byte)
	done      chan struct{}
}

// OpenWebsocket -- Open a websocket connection
func OpenWebsocket(c *Connection, endpoint string, params map[string]string, onMessage func(int, []byte), auths ...api.Authentication) error {
	hdr := http.Header{}

	var auth api.Authentication
	if len(auths) == 0 {
		var err error
		if auth, err = api.CreateClientAuth(); err != nil {
			return err
		}
	} else {
		auth = auths[0]
	}

	hdr.Add("Authorization", auth.GetAuthHeader())

	url := auth.GetURL(endpoint+"/websocket", params, "wss")
	//log.Printf("Opening websocket connection to '%s' (auth: '%s')", url, auth.GetAuthHeader())

	ws, resp, err := websocket.DefaultDialer.Dial(url, hdr)
	if err != nil {
		if resp != nil {
			if resp.StatusCode == 401 {
				return fmt.Errorf("API server authentication failed")
			}
			return fmt.Errorf("Error opening websocket (response code: %s): %s", resp.Status, err)
		}
		return fmt.Errorf("Error opening websocket: %s", err)
	}

	c.ws = ws
	c.onMessage = onMessage
	c.done = make(chan struct{})

	go c.receive()

	return nil
}

// Close -- Close the underlying WebSocket connection
func (c Connection) Close() {
	c.ws.Close()
}

// OnMessage -- pass incoming WebSocket messages on to the listener function
func (c Connection) OnMessage(msgType int, msg []byte) {
	c.onMessage(msgType, msg)
}

func (c Connection) receive() {
	defer c.ws.Close()
	defer close(c.done)

	for {
		msgType, msg, err := c.ws.ReadMessage()
		if err != nil {
			log.Println("read error:", err)
			return
		}
		c.onMessage(msgType, msg)
	}
}

// SendBinary -- Send binary WebSocket message
func (c Connection) SendBinary(data []byte) {
	c.ws.WriteMessage(websocket.BinaryMessage, data)
}

// SendJSON -- Send a JSON text message to the WebSocket
func (c Connection) SendJSON(value interface{}) {
	c.ws.WriteJSON(value)
}

// SendText -- send a raw text websocket messge (use SendJson instead where possible)
func (c Connection) SendText(msg string) {
	c.ws.WriteMessage(websocket.TextMessage, []byte(msg))
}

// Wait -- Wait for the connection to close
func (c Connection) Wait() {
	<-c.done
}
<commit_msg>tunnel/ws.go: Using call-by-reference ‘this’ pointers instead of call-by-value ones<commit_after>package tunnel

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/ondevice/ondevice/api"
)

// WSListener -- WebSocket listener
type WSListener interface {
	OnMessage(msgType int, data []byte)
}

// Connection -- WebSocket connection
type Connection struct {
	ws        *websocket.Conn
	onMessage func(int, []byte)
	done      chan struct{}
}

// OpenWebsocket -- Open a websocket connection
func OpenWebsocket(c *Connection, endpoint string, params map[string]string, onMessage func(int, []byte), auths ...api.Authentication) error {
	hdr := http.Header{}

	var auth api.Authentication
	if len(auths) == 0 {
		var err error
		if auth, err = api.CreateClientAuth(); err != nil {
			return err
		}
	} else {
		auth = auths[0]
	}

	hdr.Add("Authorization", auth.GetAuthHeader())

	url := auth.GetURL(endpoint+"/websocket", params, "wss")
	//log.Printf("Opening websocket connection to '%s' (auth: '%s')", url, auth.GetAuthHeader())

	ws, resp, err := websocket.DefaultDialer.Dial(url, hdr)
	if err != nil {
		if resp != nil {
			if resp.StatusCode == 401 {
				return fmt.Errorf("API server authentication failed")
			}
			return fmt.Errorf("Error opening websocket (response code: %s): %s", resp.Status, err)
		}
		return fmt.Errorf("Error opening websocket: %s", err)
	}

	c.ws = ws
	c.onMessage = onMessage
	c.done = make(chan struct{})

	go c.receive()

	return nil
}

// Close -- Close the underlying WebSocket connection
func (c *Connection) Close() {
	c.ws.Close()
}

// OnMessage -- pass incoming WebSocket messages on to the listener function
func (c *Connection) OnMessage(msgType int, msg []byte) {
	c.onMessage(msgType, msg)
}

func (c *Connection) receive() {
	defer c.ws.Close()
	defer close(c.done)

	for {
		msgType, msg, err := c.ws.ReadMessage()
		if err != nil {
			log.Println("read error:", err)
			return
		}
		c.onMessage(msgType, msg)
	}
}

// SendBinary -- Send binary WebSocket message
func (c *Connection) SendBinary(data []byte) {
	c.ws.WriteMessage(websocket.BinaryMessage, data)
}

// SendJSON -- Send a JSON text message to the WebSocket
func (c *Connection) SendJSON(value interface{}) {
	c.ws.WriteJSON(value)
}

// SendText -- send a raw text websocket messge (use SendJson instead where possible)
func (c *Connection) SendText(msg string) {
	c.ws.WriteMessage(websocket.TextMessage, []byte(msg))
}

// Wait -- Wait for the connection to close
func (c *Connection) Wait() {
	<-c.done
}
<|endoftext|>