Golang exercise slices how does it deal with big values - go

I'm going through the Golang tutorial and I'm a little bit confused as to what it is doing with some of the values in the slices exercise. https://tour.golang.org/moretypes/18
Here is the code that I am confused with:
A value of 0 is a perfectly blue pixel and a value of 255 is a perfectly white pixel. So what is happening here when the value displayed is some form of x*y (I did /20 to make the image a little bit bigger and easier to see).
If you follow the image horizontally, you will see that at some point in the process, the ever increasing x and y values seem to revert to blue (0 value) If I type a static value like 256 in the return I get a compile error. So it obviously does not allow the numbers to go off the scale and revert to 0 or anything. So how does it get the blue curves in the picture?
imported source here: https://github.com/golang/tour/blob/master/pic/pic.go#L15
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
//First, the array has to be made so we can put some values in it later
//This only makes the second dimension of the array ([[uint8 dy]])?
image := make([][]uint8, dy)
//The inputs into the function are Int's, so it is ok to have a non uint8
//loop initializer
for x := 0; x < dy; x++ {
//once we are in the loop we have to make the first dimension of the array
//based on the dx values
image[x] = make([]uint8, dx)
for y := 0; y < dx; y++ {
//This is a function +to assign the pixel values to the array
image[x][y] = uint8((x * y) /20)
}
}
return image
}
func main() {
pic.Show(Pic)
}

Imagine i is of type int, uint8(i) returns Least Significant Byte (LSB) of i:
When x is in range [0, 255] , meaning: 0 <= x <= 255
and y is in range [0, 255],
then x*y is in range [0, 255*255] = [0, 65025]
so x*y/20 is in range [0, 255*255/20] = [0, 65025/20] = [0, 3251]
and value of uint8(x*y/20) is equal to (x*y/20)%256 meaning exactly LSB byte:
uint8(3251) = uint8(0XCB3) = 0XB3 = 179
3251 = 12*256 + 179
So every time the x*y/20 is bigger than 255 it counts from 0 again: (x*y/20) % 256 this is why your image is repeated circles.
Try this working sample code:
package main
import "fmt"
func main() {
for y := 0; y <= 255; y++ {
for x := 0; x <= 255; x++ {
v := x * y / 20
if int(uint8(v)) != v%256 {
fmt.Println(v, v%256)
}
}
}
fmt.Println("Done.")
}
output:
Done.
Let's simplify you example, see this working sample code:
package main
import (
"bytes"
"image"
"image/png"
"os"
)
func main() {
const dx = 256
const dy = 256
m := image.NewNRGBA(image.Rect(0, 0, dx, dy))
for y := 0; y < dy; y++ {
for x := 0; x < dx; x++ {
v := uint8(x * y / 20)
i := y*m.Stride + x*4
m.Pix[i] = v //R
m.Pix[i+1] = v //G
m.Pix[i+2] = 255 //B
m.Pix[i+3] = 255 //A
}
}
var buf bytes.Buffer
err := png.Encode(&buf, m)
if err != nil {
panic(err)
}
os.Stdout.Write(buf.Bytes())
}
And redirect the output to a file like main > b.png or, go run main.go > b.png
see output file b.png:

uint8(anotherIntValue) conversion will take the last byte of anotherIntValue. That is why your code can produce many blue (0). For example, following code would print 'val = 0'.
dx, dy := 128, 2
fmt.Println("val =", uint8(dx*dy))
Constant conversion will be checked by compiler for out of range errors.

Related

Splitting big.Int by digit

I'm trying to split a big.Int into a number of int64s such that each is a portion of the larger number, with a standard offset of 18 digits. For example, given the following input value of 1234512351234088800000999, I would expect the following output: [351234088800000999, 1234512]. For negative numbers, I would expect all of the parts to be negative (i.e. -1234512351234088800000999 produces [-351234088800000999, -1234512]).
I already know I can do this to get the result I want:
func Split(input *big.Int) []int64 {
const width = 18
asStr := in.Coefficient().Text(10)
strLen := len(asStr)
offset := 0
if in.IsNegative() {
offset = 1
}
length := int(math.Ceil(float64(strLen-offset) / width))
ints := make([]int64, length)
for i := 1; i <= length; i++ {
start := strLen - (i * width)
end := start + width
if start < 0 || (start == 1 && asStr[0] == '-') {
start = 0
}
ints[i-1], _ = strconv.ParseInt(asStr[start:end], 10, 64)
if offset == 1 && ints[i-1] > 0 {
ints[i-1] = 0 - ints[i-1]
}
}
return ints
}
However, I don't like the idea of using string-parsing nor do I like the use of strconv. Is there a way I can do this utilizing the big.Int directly?
You can use the DivMod function to do what you need here, with some special care to handle negative numbers:
var offset = big.NewInt(1e18)
func Split(input *big.Int) []int64 {
rest := new(big.Int)
rest.Abs(input)
var ints []int64
r := new(big.Int)
for {
rest.DivMod(rest, offset, r)
ints = append(ints, r.Int64() * int64(input.Sign()))
if rest.BitLen() == 0 {
break
}
}
return ints
}
Multiplying each output by input.Sign() ensures that each output will be negative if the input is negative. The sum of the output values multiplied by 1e18 times their position in the output should equal the input.

Coloring for 3D Isometric projection

The ask is, base on the following program
https://github.com/adonovan/gopl.io/blob/master/ch3/surface/main.go
Turn it to a web server and render the SVG as web page
Color the SVG so that the peak is red and valley is blue
I've got the 1st part right for sure, and I think I got the 2nd part right but apparently not, yet I have no idea where I'm wrong. Please help.
package main
import (
"fmt"
"math"
"net/http"
"strconv"
)
const (
cells = 100 // number of grid cells
xyrange = 30.0 // axis ranges (-xyrange..+xyrange)
angle = math.Pi / 6 // angle of x, y axes (=30°)
)
var height, width = 300, 600 // canvas size in pixels
var xyscale = width / 2 / xyrange // pixels per x or y unit
var zscale = float64(height) * 0.4 // pixels per z unit
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
func main() {
addr := ":8000"
fmt.Printf("Visit\n http://localhost%s/\n http://localhost%[1]s/?height=600&width=1200\n", addr)
//http server
http.HandleFunc("/", handle)
http.ListenAndServe(addr, nil)
}
func handle(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
if err := r.ParseForm(); err != nil {
return
}
for k, v := range r.Form {
if k == "height" {
h, _ := strconv.Atoi(v[0])
if h > 0 {
height = h
}
}
if k == "width" {
w, _ := strconv.Atoi(v[0])
if w > 0 {
width = w
}
}
}
xyscale = width / 2 / xyrange
zscale = float64(height) * 0.4
fmt.Fprintf(w, "<svg xmlns='http://www.w3.org/2000/svg' "+
"style='stroke: grey; stroke-width: 0.7' "+
"width='%d' height='%d'>", width, height)
for i := 0; i < cells; i++ {
for j := 0; j < cells; j++ {
ax, ay := corner(i+1, j)
bx, by := corner(i, j)
cx, cy := corner(i, j+1)
dx, dy := corner(i+1, j+1)
r, g, b := getColor(i, j)
fmt.Fprintf(w, "<polygon points='%g,%g %g,%g %g,%g %g,%g' fill='#%x%x%x'/>\n",
ax, ay, bx, by, cx, cy, dx, dy, r, g, b)
}
}
fmt.Fprintf(w, "</svg>")
}
func corner(i, j int) (float64, float64) {
// Find point (x,y) at corner of cell (i,j).
x := xyrange * (float64(i)/cells - 0.5)
y := xyrange * (float64(j)/cells - 0.5)
// Compute surface height z.
z := f(x, y)
// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).
sx := float64(width/2) + (x-y)*cos30*float64(xyscale)
sy := float64(height/2) + (x+y)*sin30*float64(xyscale) - z*zscale
return sx, sy
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
return math.Sin(r) / r
}
func getColor(i, j int) (int, int, int) {
// Find point (x,y) at middle of corner of cell (i,j) to cell (i+1,j+1).
x := xyrange * (float64(i)/cells + 0.5/cells - 0.5)
y := xyrange * (float64(j)/cells + 0.5/cells - 0.5)
// Compute surface height z.
z := math.Hypot(x, y) // distance from (0,0)
v := int(math.Sin(z)*127) + 128
r := v
g := 0
b := 255 - v
return r, g, b
}
Here is the result that I got:
NB, although the question seems to be for Go, but it is actually the
getColor() algorithm that I'm asking about. You can understand/answer even if you don't write in Go.
Your code uses the format verb %x to print the hex values to the SVG's fill attribute:
fmt.Fprintf(w, "<polygon points='%g,%g %g,%g %g,%g %g,%g' fill='#%x%x%x'/>\n",
ax, ay, bx, by, cx, cy, dx, dy, r, g, b)
This causes some numbers like 0 and 1 to be formatted with one hex digit. For example RGB (254, 0, 1) would be formatted as fe01. The browser then render colors incorrectly.
Change the format verbs to %02x to ensure the RGB is always printed with two hex digits.
Now RGB (254, 0, 1) is printed as fe0001, which is the correct hex color.
Output:

How to improve my function that rounds floats to nearest 10 if 2 digit number, 100 if 3 digit number etc

I am drawing bar charts and i've come across a tricky problem. How to programmatically set the max value for the y axis label depending on the max value for a given series. So if you had a bar with a value of 7, you might want the y axis to go up to 10
My approach is not ideal but works like this:
Get a number to round, like 829
Count the number of digits (3)
Use a loop to convert to a string of 0s ("000")
Add a 1 to the start of the string then convert to a float (1000)
Find the difference (1000 - 829 = 171)
Get the first digit of the difference (1) and then add that to the first digit of the float, with the remaining set to zero ("900"), then convert to a number (900)
This means that 725 will see a y axis max label number of 800, and 829 of 900
My code works, but I feel like it's a piece of crap with a hacky approach
I have to code for big numbers. For example, if the float I want to find the max value for is >10000 then take the first two digits, and add 1000 to it. If >100,000 add 10,000
How can I improve here? I'm a little stuck, is my idea of converting to strings even right?!
Full code here:
package main
import (
"fmt"
"strconv"
)
func main() {
myFloat := 899175.0
x := getMaxYAxisValueForChart(myFloat)
fmt.Println("The number to find the maximum value for is: ", myFloat)
fmt.Println("This should be the max value for the y axis: ", x)
}
func getMaxYAxisValueForChart(float float64) (YAxisMaximum float64) {
//Convert to string with no decimals
floatAsString := fmt.Sprintf("%.f", float)
//Get length of the string float
floatAsStringLength := len(floatAsString)
//For each digit in the string, make a zero-string
stringPowerTen := "0"
for i := 1; i < floatAsStringLength; i++ {
stringPowerTen += "0"
}
//Add a 1 to the 0 string to get the difference from the float
stringPowerTenWithOne := "1" + stringPowerTen
//Convert the number string to a float
convertStringPowerTenToFloat := ConvertStringsToFloat(stringPowerTenWithOne)
//Get the difference from the denominator from the numerator
difference := convertStringPowerTenToFloat - float
//We want to isolate the first digit to check how far the float is (100 is far from 1000) and then correct if so
floatAsStringDifference := fmt.Sprintf("%.f", difference)
runes := []rune(floatAsStringDifference)
floatAsStringDifferenceFirstDigit := string(runes[0])
//For the denominator we want to take away the difference that is rounded to the nearest ten, hundred etc
runes = []rune(stringPowerTen)
differenceLastDigitsAsString := ""
if difference < 10 {
differenceLastDigitsAsString = "1"
} else if difference < 30 && difference < 100 {
differenceLastDigitsAsString = "0"
} else {
differenceLastDigitsAsString = floatAsStringDifferenceFirstDigit + string(runes[1:])
}
//Convert the number difference string from total to a float
convertDifferenceStringPowerTenToFloat := ConvertStringsToFloat(differenceLastDigitsAsString)
YAxisMaximum = convertStringPowerTenToFloat - convertDifferenceStringPowerTenToFloat
//If float is less than 10,0000
if float < 10000 && (YAxisMaximum-float >= 500) {
YAxisMaximum = YAxisMaximum - 500
}
if float < 10000 && (YAxisMaximum-float < 500) {
YAxisMaximum = YAxisMaximum
}
//If number bigger than 10,000 then get the nearest 1,000
if float > 10000 {
runes = []rune(floatAsString)
floatAsString = string(runes[0:2])
runes = []rune(stringPowerTen)
stringPowerTen = string(runes[2:])
runes = []rune(stringPowerTenWithOne)
stringPowerTenWithOne = string(runes[0:(len(stringPowerTenWithOne) - 2)])
YAxisMaximum = ConvertStringsToFloat(floatAsString+stringPowerTen) + ConvertStringsToFloat(stringPowerTenWithOne)
}
if float > 10000 {
runes = []rune(floatAsString)
floatAsString = string(runes[0:2])
runes = []rune(stringPowerTen)
stringPowerTen = string(runes[:])
runes = []rune(stringPowerTenWithOne)
stringPowerTenWithOne = string(runes[0:(len(stringPowerTenWithOne))])
YAxisMaximum = ConvertStringsToFloat(floatAsString+stringPowerTen) + ConvertStringsToFloat(stringPowerTenWithOne)
}
return YAxisMaximum
}
func ConvertStringsToFloat(stringToConvert string) (floatOutput float64) {
floatOutput, Error := strconv.ParseFloat(stringToConvert, 64)
if Error != nil {
fmt.Println(Error)
}
return floatOutput
}
Here is the solution based off of Matt Timmermans answer, but converted to work in Go:
func testing(float float64) (YAxisMaximum float64) {
place := 1.0
for float >= place*10.0 {
place *= 10.0
}
return math.Ceil(float/place) * place
}
Wow, that's a pretty complicated procedure you have. This is how I would do it if the numbers aren't enormous. I don't know go, so I'm going to guess about how to write it in that language:
func getMaxYAxisValueForChart(float float64) {
place := 1.0;
while float >= place*10.0 {
place *= 10.0;
}
return math.Ceil(float/place) * place;
}
You can get the magnitude of a number using Math.Log10
int magnitude = (int)Math.Pow(10, (int)Math.Log10(value));
Use that to divide the number down, calculate ceiling and then scale it back up.
No strings, no while loops.
Take the length of the string and calculate that 10 to the power of that length
Or...better take the Log base 10, get the integer part, add 1 and then return that to the power of 10 :)
import (
"fmt"
"math"
)
//func PowerScale(x int) int64{
// return int64(math.Pow(10,float64(len((fmt.Sprintf("%d",x))))))
//}
func PowerScale(x int) int64 {
return int64(math.Pow(10,float64(int(math.Log10(float64(x))+1))))
}
func main() {
fmt.Println(PowerScale(829))
fmt.Println(PowerScale(7))
}
Since 829 is an int, or can be cast to, a pure integer solution :
func getMaxYAxisValueForChart(int int64) {
base := 10;
while int > base*10 {
base := 10 * base;
}
return int + (base - int) % base;
}

Go tour #18. How do I pass in integers to Pic?

The following code errors with index out of range. I tried modifying main to
pic.Show(Pic(500, 500)) but that changes the argument from function to the return type and it fails to compile. How do I pass in integers if the pic.Show is expecting a function as an argument.
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
mypic := [][]uint8{}
for y := 0; y < dy; y++ {
mypic[y] = []uint8{}
for x := 0; x < dx; x++ {
mypic[y][x] = uint8((x + y) / 2)
}
}
return mypic
}
func main() {
pic.Show(Pic)
}
You don't. The Go Tour program will pass Pic test values to your program. Your problem is in your code: panic: runtime error: index out of range. [][]uint8{} and []uint8{} allocate zero y and zero x slice elements. Use make to allocate your y and x slices. For example,
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
pixels := make([][]uint8, dy)
for y := 0; y < dy; y++ {
pixels[y] = make([]uint8, dx)
for x := 0; x < dx; x++ {
pixels[y][x] = uint8((x + y) / 2)
}
}
return pixels
}
func main() {
pic.Show(Pic)
}
Reference: Making slices, maps and channels, The Go Programming Language Specification

Processing: Image with rounded corners

I'm drawing a section of an image, however I'd like to apply rounded corners to it. I can't find any way of doing this.
In the draw() method:
img_section = img.get(gaze_x, gaze_y, gaze_size_x, gaze_size_y);
image(img_section, gaze_x, gaze_y);
You could copy the image and then manually set the corner pixels using the set() function.
You could just draw a rounded rectangle around the image- if the image will be placed on a background with a single color, just draw a rounded rectangle with the same color as the image.
Or you could come up with an image mask and draw that on top of your image.
package utils
import (
"ddkt365-poster/library/log"
"image"
"image/color"
"math"
)
// Settable Settable
type Settable interface {
Set(x, y int, c color.Color)
}
var empty = color.RGBA{255, 255, 255, 0}
// Convert Convert
func Convert(m *image.Image, rate float64) {
b := (*m).Bounds()
w, h := b.Dx(), b.Dy()
r := (float64(min(w, h)) / 2) * rate
log.Error("bounds:%v", r)
sm, ok := (*m).(Settable)
if !ok {
// Check if image is YCbCr format.
ym, ok := (*m).(*image.YCbCr)
if !ok {
log.Error("errInvalidFormat")
return
}
*m = yCbCrToRGBA(ym)
sm = (*m).(Settable)
}
// Parallelize?
for y := 0.0; y <= r; y++ {
l := math.Round(r - math.Sqrt(2*y*r-y*y))
for x := 0; x <= int(l); x++ {
sm.Set(x-1, int(y)-1, empty)
}
for x := 0; x <= int(l); x++ {
sm.Set(w-x, int(y)-1, empty)
}
for x := 0; x <= int(l); x++ {
sm.Set(x-1, h-int(y), empty)
}
for x := 0; x <= int(l); x++ {
sm.Set(w-x, h-int(y), empty)
}
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func yCbCrToRGBA(m image.Image) image.Image {
b := m.Bounds()
nm := image.NewRGBA(b)
for y := 0; y < b.Dy(); y++ {
for x := 0; x < b.Dx(); x++ {
nm.Set(x, y, m.At(x, y))
}
}
return nm
}
// Image with rounded corners (Go image/draw package)
if i.BorderRadius > 0 {
utils.Convert(&img, (float64(i.BorderRadius) / 100))
}
draw.Draw(canvs, img.Bounds().Add(image.Pt(i.X, i.Y)), img, image.ZP, draw.Over)

Resources