1// ===== stl2.go =====
   2// Package main stl2.go
   3package main
   4
   5import (
   6	"bytes"
   7	"crypto/rand"
   8	"encoding/base64"
   9	"encoding/binary"
  10	"log"
  11	m "math"
  12	r "reflect"
  13	"runtime"
  14	"strconv"
  15	"strings"
  16	"syscall/js"
  17	u "unsafe"
  18
  19	"github.com/go-gl/mathgl/mgl32"
  20	"gitlab.com/russoj88/stl/stl"
  21)
  22
  23var (
  24	wasmName                                                     string
  25	running                                                      = true
  26	done                                                         chan struct{}
  27	stlFileName, originalHTML                                    string
  28	rr                                                           Renderer
  29	existingFooter, body, footer, sXV, sYV, sZV, sZoomV, cEl, gl js.Value
  30	currentZoom                                                  float32 = 3
  31)
  32
  33const gebi = "getElementById"
  34const ael = "addEventListener"
  35const ih = "innerHTML"
  36
  37func main() {
  38	ready := make(chan struct{})
  39
  40	document := js.Global().Get("document")
  41	readyState := document.Get("readyState").String()
  42	if readyState == "interactive" || readyState == "complete" {
  43		log.Println(wasmName+":", "WASM: DOM already fully loaded")
  44		close(ready)
  45	} else {
  46		cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  47			log.Println(wasmName+":", "WASM: DOM fully loaded and parsed")
  48			close(ready)
  49			return nil
  50		})
  51		defer cb.Release()
  52
  53		document.Call(ael, "DOMContentLoaded", cb)
  54		log.Println(wasmName+":", "WASM: waiting for DOM to load")
  55	}
  56
  57	// Wait until the DOM is ready
  58	<-ready
  59
  60	cEl = document.Call(gebi, "gocanvas")
  61	if cEl.IsUndefined() || cEl.IsNull() {
  62		return
  63	}
  64
  65	window := js.Global().Get("window")
  66	location := window.Get("location")
  67	pathname := location.Get("pathname").String()
  68	parts := strings.SplitN(pathname, "/", 3)
  69	if len(parts) > 1 {
  70		parts[1] = "/" + parts[1]
  71	} else {
  72		parts = append(parts, "/")
  73	}
  74	switch parts[1] {
  75	case "/cat":
  76		log.Println(wasmName+":", "nothing to animate ; return")
  77		return
  78	case "/p":
  79		log.Println(wasmName+":", "fetching stereolithograph")
  80		preElement := js.Global().Get("document").Call("getElementById", "package-type")
  81		if preElement.IsUndefined() || preElement.IsNull() {
  82			log.Println(wasmName+":", "Element with ID 'package-type' not found. Exiting.")
  83			return // Exit the WebAssembly here
  84		}
  85
  86		packageText := preElement.Get("textContent").String()
  87		packageType := packageText[len("Package Type: "):]
  88		stlFileName = packageType + ".stl"
  89
  90		// Fetch the STL file
  91		response := js.Global().Call("fetch", "/i/stl/base64/"+stlFileName)
  92		promise := response.Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  93			resp := args[0]
  94			if !resp.Get("ok").Bool() {
  95				log.Printf("HTTP Error: %s (status: %d)", resp.Get("statusText").String(), resp.Get("status").Int())
  96				log.Println(wasmName+":", "Failed to fetch the STL file "+stlFileName+". Exiting.")
  97				return nil // Exit the WebAssembly here
  98			}
  99			return resp.Call("text")
 100		}))
 101
 102		// Process the fetched data
 103		promise.Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
 104			if len(args) > 0 {
 105				result := args[0].String()
 106				uploadedFile, err1 := parseBase64File(result)
 107				if !err1.IsNull() {
 108					log.Println(wasmName+":", "Error parsing the base64 file:", err1)
 109					return nil
 110				}
 111
 112				stlSolid, err2 := NewSTL(uploadedFile)
 113				if err2 != nil {
 114					log.Println(wasmName+":", "Error creating STL object:", err2)
 115					return nil
 116				}
 117
 118				vert, colors, indices := stlSolid.GetModel()
 119				modelSize := getMaxScalar(vert)
 120				currentZoom := modelSize * 3
 121				rr.SetZoom(currentZoom)
 122				rr.SetModel(colors, vert, indices)
 123			}
 124			return nil
 125		}))
 126
 127		// Ensure this function does not execute until the fetch of the STL file succeeds
 128		promise.Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
 129			log.Println(wasmName+":", "An error occurred during the fetch or processing. Exiting.")
 130			return nil
 131		}))
 132		niam()
 133	default:
 134		niam()
 135	}
 136}
 137func niam() {
 138	mgl32.DisableMemoryPooling()
 139
 140	//stlFileName = "TO-247.stl"
 141	tdata := struct {
 142		XRange, ZMin, ZMax string
 143		XStep, ZoomStep    string
 144	}{
 145		XRange: "1", ZMin: "0", ZMax: "50",
 146		XStep: "0.01", ZoomStep: "0.1",
 147	}
 148	if stlFileName != ".stl" && stlFileName != "" {
 149		tdata.ZMin = "10"
 150		tdata.ZMax = "1000"
 151	}
 152
 153	var controlsHTML = `
 154	<datalist id="speeds">
 155	<option>-` + tdata.XRange + `</option><option>0</option><option>` + tdata.XRange + `</option></datalist>
 156	<table class="🌐">
 157	<tr><td><p><button type="button" id="stop">Stop Rendering</button></p></td><td><p>X<input id="X" type="range" min="-` + tdata.XRange + `" max="` + tdata.XRange + `" step="` + tdata.XStep + `" list="speeds"><text id="XV">00.00</text></p></td>
 158	<td><p>Y<input id="Y" type="range" min="-` + tdata.XRange + `" max="` + tdata.XRange + `" step="` + tdata.XStep + `" list="speeds"><text id="YV">00.00</text></p></td>
 159	<td><p>Z<input id="Z" type="range" min="-` + tdata.XRange + `" max="` + tdata.XRange + `" step="` + tdata.XStep + `" list="speeds"><text id="ZV">00.00</text></p></td>
 160	<td><p>Zoom<input id="Zoom" type="range" min="` + tdata.ZMin + `" max="` + tdata.ZMax + `" step="` + tdata.ZoomStep + `" list="speeds"><text id="ZoomV">0000.00</text></p></td>
 161	</table>
 162	`
 163	doc := js.Global().Get("document")
 164	body = doc.Get("body")
 165	existingFooter = doc.Call("getElementsByTagName", "footer").Index(0)
 166	if existingFooter.Truthy() {
 167		originalHTML = existingFooter.Get(ih).String()
 168		footer = doc.Call("createElement", "footer")
 169		footer.Set(ih, originalHTML+controlsHTML)
 170		body.Call("replaceChild", footer, existingFooter)
 171	} else {
 172		footer = doc.Call("createElement", "footer")
 173		footer.Set(ih, controlsHTML)
 174		body.Call("appendChild", footer)
 175	}
 176
 177	cEl = doc.Call(gebi, "gocanvas")
 178	width := doc.Get("body").Get("clientWidth").Int()
 179	height := doc.Get("body").Get("clientHeight").Int()
 180
 181	cEl.Set("width", width)
 182	cEl.Set("height", height)
 183	sXc := js.FuncOf(sCX)
 184	sX := doc.Call(gebi, "X")
 185	sX.Call(ael, "input", sXc)
 186	sXV = doc.Call(gebi, "XV")
 187
 188	sYc := js.FuncOf(sCY)
 189	sY := doc.Call(gebi, "Y")
 190	sY.Call(ael, "input", sYc)
 191	sYV = doc.Call(gebi, "YV")
 192
 193	sZc := js.FuncOf(sCZ)
 194	sZ := doc.Call(gebi, "Z")
 195	sZ.Call(ael, "input", sZc)
 196	sZV = doc.Call(gebi, "ZV")
 197
 198	sZoomc := js.FuncOf(sCZoom)
 199	sZoom := doc.Call(gebi, "Zoom")
 200	sZoom.Call(ael, "input", sZoomc)
 201	sZoomV = doc.Call(gebi, "ZoomV")
 202
 203	sBc := js.FuncOf(stopApplication)
 204	sB := doc.Call(gebi, "stop")
 205	sB.Call(ael, "click", sBc)
 206	defer sBc.Release()
 207
 208	gl = cEl.Call("getContext", "webgl")
 209	if gl.IsUndefined() {
 210		gl = cEl.Call("getContext", "experimental-webgl")
 211	}
 212	if gl.IsUndefined() {
 213		js.Global().Call("alert", "WASM:  browser might not support webgl")
 214		return
 215	}
 216
 217	config := InitialConfig{
 218		W:        width,
 219		H:        height,
 220		X:        0,
 221		Y:        0,
 222		Z:        0,
 223		Vertices: verticesNative,
 224		Indices:  indicesNative,
 225		Colors:   colorsNative,
 226		FSC:      fragShaderCode,
 227		VSC:      vertShaderCode,
 228	}
 229
 230	config.X = cryptoRandFloat32() / 20
 231	config.Y = cryptoRandFloat32() / 20
 232	config.Z = cryptoRandFloat32() / 20
 233	config.Vertices, config.Indices = generateSphereVertices(float32(1.0), 30, 30)
 234	if stlFileName == ".stl" || stlFileName == "" {
 235		config.FSC, config.VSC = fragShaderCode1, vertShaderCode1
 236	}
 237	var jsErr js.Value
 238	rr, jsErr = NewRenderer(gl, config)
 239	if !jsErr.IsNull() {
 240		js.Global().Call("alert", "WASM: Cannot load webgl ")
 241		return
 242	}
 243	rr.SetZoom(currentZoom)
 244	defer rr.Release()
 245
 246	x, y, z := rr.GetSpeed()
 247	sX.Set("value", f32(x, 'f', -1, 64))
 248	if x > 0 {
 249		sXV.Set(ih, "+"+f32(x, 'f', 2, 64))
 250	}
 251	if x == 0 {
 252		sXV.Set(ih, " "+f32(x, 'f', 2, 64))
 253	}
 254	if x < 0 {
 255		sXV.Set(ih, f32(x, 'f', 2, 64))
 256	}
 257	sY.Set("value", f32(y, 'f', -1, 64))
 258	if y > 0 {
 259		sYV.Set(ih, "+"+f32(y, 'f', 2, 64))
 260	}
 261	if y == 0 {
 262		sYV.Set(ih, "0"+f32(y, 'f', 2, 64))
 263	}
 264	if y < 0 {
 265		sYV.Set(ih, f32(y, 'f', 2, 64))
 266	}
 267	sZ.Set("value", f32(z, 'f', -1, 64))
 268	if z > 0 {
 269		sZV.Set(ih, "+"+f32(z, 'f', 2, 64))
 270	}
 271	if z == 0 {
 272		sZV.Set(ih, "0"+f32(z, 'f', 2, 64))
 273	}
 274	if z < 0 {
 275		sZV.Set(ih, f32(z, 'f', 2, 64))
 276	}
 277
 278	var renderFrame js.Func
 279	renderFrame = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
 280		rr.Render(this, args)
 281		js.Global().Call("requestAnimationFrame", renderFrame)
 282		return nil
 283	})
 284	js.Global().Call("requestAnimationFrame", renderFrame)
 285
 286	done = make(chan struct{})
 287
 288	<-done
 289}
 290
 291func parseBase64File(input string) (output []byte, err js.Value) {
 292	searchString := "base64,"
 293	searchLength := len(searchString)
 294	var index = -1
 295	for i := 0; i <= len(input)-searchLength; i++ {
 296		if input[i:i+searchLength] == searchString {
 297			index = i
 298			break
 299		}
 300	}
 301	if index < 0 {
 302		err = js.Global().Get("Error").New("Error opening file")
 303		return
 304	}
 305	sBuffer := input[index+searchLength:]
 306	output, decodeErr := base64.StdEncoding.DecodeString(sBuffer)
 307	if decodeErr != nil {
 308		err = js.Global().Get("Error").New(decodeErr.Error())
 309		return
 310	}
 311	return output, js.Null()
 312}
 313
 314func getMaxScalar(vertices []float32) float32 {
 315	var max float32
 316	for baseIndex := 0; baseIndex < len(vertices); baseIndex += 3 {
 317		testScale := scalar(vertices[baseIndex], vertices[baseIndex], vertices[baseIndex])
 318		if testScale > max {
 319			max = testScale
 320		}
 321	}
 322	return max
 323}
 324
 325func scalar(x float32, y float32, z float32) float32 {
 326	xy := m.Sqrt(float64(x*x + y*y))
 327	return float32(m.Sqrt(xy*xy + float64(z*z)))
 328}
 329
 330func cryptoRandFloat32() float32 {
 331	b := make([]byte, 4)
 332	_, err := rand.Read(b)
 333	if err != nil {
 334		panic("crypto/rand read failed: " + err.Error())
 335	}
 336	u := uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
 337	return float32(u) / float32(m.MaxUint32)
 338}
 339
 340func stopApplication(_ js.Value, _ []js.Value) interface{} {
 341	running = false
 342	sZoomV.Set(ih, float32(0))
 343	currentZoom = float32(0)
 344	rr.SetZoom(float32(0))
 345	footer.Set(ih, originalHTML)
 346
 347	if callUpdateCartDisplay := js.Global().Get("callUpdateCartDisplay"); !callUpdateCartDisplay.IsUndefined() && !callUpdateCartDisplay.IsNull() {
 348		callUpdateCartDisplay.Invoke()
 349	} else {
 350		js.Global().Call("console.warn", "callUpdateCartDisplay is undefined or null")
 351	}
 352
 353	js.Global().Call("setTimeout", js.FuncOf(func(this js.Value, p []js.Value) interface{} {
 354		close(done)
 355		//		done <- struct{}{}
 356		return nil
 357	}), 5000)
 358	return nil
 359}
 360
 361func sCX(this js.Value, _ []js.Value) interface{} {
 362	sSpeed := this.Get("value").String()
 363	s, _ := strconv.ParseFloat(sSpeed, 64)
 364	rr.SetX(float32(s))
 365	if s > 0 {
 366		sXV.Set(ih, "+"+f64(s, 'f', 2, 32))
 367	}
 368	if s == 0 {
 369		sXV.Set(ih, "0"+f64(s, 'f', 2, 32))
 370	}
 371	if s < 0 {
 372		sXV.Set(ih, f64(s, 'f', 2, 32))
 373	}
 374	return nil
 375}
 376
 377func sCY(this js.Value, _ []js.Value) interface{} {
 378	sS := this.Get("value").String()
 379	s, _ := strconv.ParseFloat(sS, 64)
 380	rr.SetY(float32(s))
 381	if s > 0 {
 382		sYV.Set(ih, "+"+f64(s, 'f', 2, 32))
 383	}
 384	if s == 0 {
 385		sYV.Set(ih, "0"+f64(s, 'f', 2, 32))
 386	}
 387	if s < 0 {
 388		sYV.Set(ih, f64(s, 'f', 2, 32))
 389	}
 390	return nil
 391}
 392
 393func sCZ(this js.Value, _ []js.Value) interface{} {
 394	sS := this.Get("value").String()
 395	s, _ := strconv.ParseFloat(sS, 64)
 396	rr.SetZ(float32(s))
 397	if s > 0 {
 398		sZV.Set(ih, "+"+f64(s, 'f', 2, 32))
 399	}
 400	if s == 0 {
 401		sZV.Set(ih, "0"+f64(s, 'f', 2, 32))
 402	}
 403	if s < 0 {
 404		sZV.Set(ih, f64(s, 'f', 2, 32))
 405	}
 406	return nil
 407}
 408
 409func sCZoom(this js.Value, _ []js.Value) interface{} {
 410	sS := this.Get("value").String()
 411	s, _ := strconv.ParseFloat(sS, 64)
 412	if s < 10 {
 413		sZoomV.Set(ih, "000"+f64(s, 'f', 2, 32))
 414	} else if s < 100 {
 415		sZoomV.Set(ih, "00"+f64(s, 'f', 2, 32))
 416	} else if s < 1000 {
 417		sZoomV.Set(ih, "0"+f64(s, 'f', 2, 32))
 418	} else {
 419		sZoomV.Set(ih, f64(s, 'f', 2, 32))
 420	}
 421	currentZoom = float32(s)
 422	rr.SetZoom(currentZoom)
 423	return nil
 424}
 425
 426// Model is an interface for a model
 427type Model interface {
 428	GetModel() ([]float32, []float32, []uint16)
 429}
 430
 431func cryptoRandIntn(max int) (int, js.Value) {
 432	if max <= 0 {
 433		return 0, js.Global().Get("Error").New("max must be a positive integer")
 434	}
 435	numBytes := (max + 7) / 8
 436	maxBytes := 1 << (numBytes * 8)
 437	randBytes := make([]byte, numBytes)
 438	randNum := 0
 439	for {
 440		_, err := rand.Read(randBytes)
 441		if err != nil {
 442			return 0, js.Global().Get("Error").New("error generating random number")
 443		}
 444		for _, b := range randBytes {
 445			randNum = (randNum << 8) | int(b)
 446		}
 447		if randNum < maxBytes-maxBytes%max {
 448			break
 449		}
 450	}
 451	return randNum % max, js.Null()
 452}
 453
 454// NewSTL returns a new STL & error
 455func NewSTL(buffer []byte) (o STL, err error) {
 456	bufferReader := bytes.NewReader(buffer)
 457	solid, err := stl.From(bufferReader)
 458	if err != nil {
 459		return
 460	}
 461
 462	// Generate random rotation matrix
 463	rotationMatrix := randomRotationMatrix()
 464
 465	// Generate colors
 466	numColors, _ := cryptoRandIntn(5)
 467	numColors += 2 // Random number between 2 and 6
 468	colors := GenerateGradient(numColors, int(solid.TriangleCount))
 469
 470	var index uint32
 471	for i, triangle := range solid.Triangles {
 472		colorR := colors[i].Red
 473		colorG := colors[i].Green
 474		colorB := colors[i].Blue
 475
 476		// Convert each triangle's vertices to custom Vertex type and apply rotation
 477		v0 := Vertex{X: float32(triangle.Vertices[0].X), Y: float32(triangle.Vertices[0].Y), Z: float32(triangle.Vertices[0].Z)}
 478		v1 := Vertex{X: float32(triangle.Vertices[1].X), Y: float32(triangle.Vertices[1].Y), Z: float32(triangle.Vertices[1].Z)}
 479		v2 := Vertex{X: float32(triangle.Vertices[2].X), Y: float32(triangle.Vertices[2].Y), Z: float32(triangle.Vertices[2].Z)}
 480
 481		// Rotate and add vertices
 482		o.addRotatedVertex(&index, v0, rotationMatrix, colorR, colorG, colorB)
 483		o.addRotatedVertex(&index, v1, rotationMatrix, colorR, colorG, colorB)
 484		o.addRotatedVertex(&index, v2, rotationMatrix, colorR, colorG, colorB)
 485	}
 486
 487	return o, err
 488}
 489
 490// Add a rotated vertex to the STL structure
 491func (s *STL) addRotatedVertex(index *uint32, vertex Vertex, rotation mgl32.Mat4, r, g, b float32) {
 492	// Apply rotation
 493	rotatedVertex := rotation.Mul4x1(mgl32.Vec3{vertex.X, vertex.Y, vertex.Z}.Vec4(1.0))
 494
 495	// Add rotated vertex to the STL struct
 496	s.v = append(s.v, rotatedVertex[0], rotatedVertex[1], rotatedVertex[2])
 497	s.i = append(s.i, *index)
 498	s.c = append(s.c, r, g, b)
 499	(*index)++
 500}
 501
 502// STL is a stereolithograph
 503type STL struct {
 504	v []float32
 505	c []float32
 506	i []uint32
 507}
 508
 509// Define a custom Vertex type for storing the vertex data.
 510type Vertex struct {
 511	X, Y, Z float32
 512}
 513
 514// GetModel gets the model
 515func (s STL) GetModel() ([]float32, []float32, []uint32) {
 516	return s.v, s.c, s.i
 517}
 518
 519// InitialConfig is the initial config
 520type InitialConfig struct {
 521	W        int
 522	H        int
 523	X        float32
 524	Y        float32
 525	Z        float32
 526	Colors   []float32
 527	Vertices []float32
 528	Indices  []uint32
 529	FSC      string
 530	VSC      string
 531}
 532
 533// Renderer is the renderer
 534type Renderer struct {
 535	glContext      js.Value
 536	glTypes        GLTypes
 537	colors         js.Value
 538	v              js.Value
 539	i              js.Value
 540	colorBuffer    js.Value
 541	vertexBuffer   js.Value
 542	indexBuffer    js.Value
 543	numIndices     int
 544	numVertices    int
 545	fragShader     js.Value
 546	vertShader     js.Value
 547	shaderProgram  js.Value
 548	tmark          float32
 549	rX             float32 //rotation X
 550	rY             float32
 551	rZ             float32
 552	movMatrix      mgl32.Mat4
 553	PositionMatrix js.Value
 554	ViewMatrix     js.Value
 555	ModelMatrix    js.Value
 556	height         int
 557	width          int
 558	sX             float32
 559	sY             float32
 560	sZ             float32
 561}
 562
 563// NewRenderer returns a new renderer & error
 564func NewRenderer(gl js.Value, config InitialConfig) (r Renderer, err js.Value) {
 565	// Get some WebGL bindings
 566	r.glContext = gl
 567	err = r.glTypes.New(r.glContext)
 568	r.numIndices = len(config.Indices)
 569	r.numVertices = len(config.Vertices)
 570	r.movMatrix = mgl32.Ident4()
 571	r.width = config.W
 572	r.height = config.H
 573
 574	r.sX = config.X
 575	r.sY = config.Y
 576	r.sZ = config.Z
 577
 578	// Convert buffers to JS TypedArrays
 579	r.UpdateColorBuffer(config.Colors)
 580	r.UpdateVerticesBuffer(config.Vertices)
 581	r.UpdateIndicesBuffer(config.Indices)
 582
 583	r.UpdateFragmentShader(config.FSC)
 584	r.UpdateVertexShader(config.VSC)
 585	r.updateShaderProgram()
 586	r.attachShaderProgram()
 587
 588	r.setContextFlags()
 589
 590	r.createMatrixes()
 591	r.EnableObject()
 592	return
 593}
 594
 595// SetModel sets a new model
 596func (r *Renderer) SetModel(Colors []float32, Vertices []float32, Indices []uint32) {
 597	r.numIndices = len(Indices)
 598	r.UpdateColorBuffer(Colors)
 599	r.UpdateVerticesBuffer(Vertices)
 600	r.UpdateIndicesBuffer(Indices)
 601	r.EnableObject()
 602}
 603
 604// Release releases the renderer
 605func (r *Renderer) Release() {
 606	return
 607}
 608
 609// EnableObject enables the object
 610func (r *Renderer) EnableObject() {
 611	r.glContext.Call("bindBuffer", r.glTypes.ElementArrayBuffer, r.indexBuffer)
 612}
 613
 614// SetX set rotation x axis speed
 615func (r *Renderer) SetX(x float32) {
 616	r.sX = x
 617}
 618
 619// SetY set rotation y axis speed
 620func (r *Renderer) SetY(y float32) {
 621	r.sY = y
 622}
 623
 624// SetZ set rotation z axis speed
 625func (r *Renderer) SetZ(z float32) {
 626	r.sZ = z
 627}
 628
 629// GetSpeed returns the rotation speeds
 630func (r *Renderer) GetSpeed() (x, y, z float32) {
 631	return r.sX, r.sY, r.sZ
 632}
 633
 634// SetSize sets the size of the rendering
 635func (r *Renderer) SetSize(height, width int) {
 636	r.height = height
 637	r.width = width
 638}
 639
 640func (r *Renderer) createMatrixes() {
 641	ratio := float32(r.width) / float32(r.height)
 642	//	fmt.Println("Renderer.createMatrixes")
 643	projMatrix := mgl32.Perspective(mgl32.DegToRad(45.0), ratio, 1, 100000.0)
 644	projMatrixBuffer := (*[16]float32)(u.Pointer(&projMatrix)) // nolint
 645	typedProjMatrixBuffer := S2TA([]float32((*projMatrixBuffer)[:]))
 646	r.glContext.Call("uniformMatrix4fv", r.PositionMatrix, false, typedProjMatrixBuffer)
 647
 648	viewMatrix := mgl32.LookAtV(mgl32.Vec3{3.0, 3.0, 3.0}, mgl32.Vec3{0.0, 0.0, 0.0}, mgl32.Vec3{0.0, 1.0, 0.0})
 649	viewMatrixBuffer := (*[16]float32)(u.Pointer(&viewMatrix)) // nolint
 650	typedViewMatrixBuffer := S2TA([]float32((*viewMatrixBuffer)[:]))
 651	r.glContext.Call("uniformMatrix4fv", r.ViewMatrix, false, typedViewMatrixBuffer)
 652}
 653
 654func (r *Renderer) setContextFlags() {
 655	r.glContext.Call("clearColor", 0.0, 0.0, 0.0, 0.0)    // Color the screen is cleared to
 656	r.glContext.Call("viewport", 0, 0, r.width, r.height) // Viewport size
 657	r.glContext.Call("depthFunc", r.glTypes.LEqual)
 658}
 659
 660// UpdateFragmentShader Updates the Fragment Shader
 661func (r *Renderer) UpdateFragmentShader(shaderCode string) {
 662	r.fragShader = r.glContext.Call("createShader", r.glTypes.FragmentShader)
 663	r.glContext.Call("shaderSource", r.fragShader, shaderCode)
 664	r.glContext.Call("compileShader", r.fragShader)
 665}
 666
 667// UpdateVertexShader updates the vertex shader
 668func (r *Renderer) UpdateVertexShader(shaderCode string) {
 669	r.vertShader = r.glContext.Call("createShader", r.glTypes.VertexShader)
 670	r.glContext.Call("shaderSource", r.vertShader, shaderCode)
 671	r.glContext.Call("compileShader", r.vertShader)
 672}
 673
 674func (r *Renderer) updateShaderProgram() {
 675	if r.fragShader.IsUndefined() || r.vertShader.IsUndefined() {
 676		return
 677	}
 678	r.shaderProgram = r.glContext.Call("createProgram")
 679	r.glContext.Call("attachShader", r.shaderProgram, r.vertShader)
 680	r.glContext.Call("attachShader", r.shaderProgram, r.fragShader)
 681	r.glContext.Call("linkProgram", r.shaderProgram)
 682}
 683
 684const gul = "getUniformLocation"
 685
 686func (r *Renderer) attachShaderProgram() {
 687	r.PositionMatrix = r.glContext.Call(gul, r.shaderProgram, "Pmatrix")
 688	r.ViewMatrix = r.glContext.Call(gul, r.shaderProgram, "Vmatrix")
 689	r.ModelMatrix = r.glContext.Call(gul, r.shaderProgram, "Mmatrix")
 690
 691	r.glContext.Call("bindBuffer", r.glTypes.ArrayBuffer, r.vertexBuffer)
 692	position := r.glContext.Call("getAttribLocation", r.shaderProgram, "position")
 693	r.glContext.Call("vertexAttribPointer", position, 3, r.glTypes.Float, false, 0, 0)
 694	r.glContext.Call("enableVertexAttribArray", position)
 695
 696	r.glContext.Call("bindBuffer", r.glTypes.ArrayBuffer, r.colorBuffer)
 697	color := r.glContext.Call("getAttribLocation", r.shaderProgram, "color")
 698	r.glContext.Call("vertexAttribPointer", color, 3, r.glTypes.Float, false, 0, 0)
 699	r.glContext.Call("enableVertexAttribArray", color)
 700
 701	r.glContext.Call("useProgram", r.shaderProgram)
 702	if stlFileName == ".stl" || stlFileName == "" {
 703
 704		uBaseColor := r.glContext.Call(gul, r.shaderProgram, "uBaseColor")
 705		uTopColor := r.glContext.Call(gul, r.shaderProgram, "uTopColor")
 706		uColor := r.glContext.Call(gul, r.shaderProgram, "uColor")
 707		r.glContext.Call("uniform3f", uBaseColor, 1.0, 0.0, 0.0)
 708		r.glContext.Call("uniform3f", uTopColor, 0.0, 0.0, 1.0)
 709		r.glContext.Call("uniform3f", uColor, 1.0, 1.0, 1.0)
 710	}
 711}
 712
 713// UpdateColorBuffer Updates the ColorBuffer
 714func (r *Renderer) UpdateColorBuffer(buffer []float32) {
 715	r.colors = S2TA(buffer)
 716	if r.colorBuffer.IsUndefined() {
 717		r.colorBuffer = r.glContext.Call("createBuffer")
 718	}
 719	r.glContext.Call("bindBuffer", r.glTypes.ArrayBuffer, r.colorBuffer)
 720	r.glContext.Call("bufferData", r.glTypes.ArrayBuffer, r.colors, r.glTypes.StaticDraw)
 721}
 722
 723// UpdateVerticesBuffer Updates the VerticesBuffer
 724func (r *Renderer) UpdateVerticesBuffer(buffer []float32) {
 725	r.v = S2TA(buffer)
 726	if r.vertexBuffer.IsUndefined() {
 727		r.vertexBuffer = r.glContext.Call("createBuffer")
 728	}
 729	r.glContext.Call("bindBuffer", r.glTypes.ArrayBuffer, r.vertexBuffer)
 730	r.glContext.Call("bufferData", r.glTypes.ArrayBuffer, r.v, r.glTypes.StaticDraw)
 731}
 732
 733// UpdateIndicesBuffer Updates the IndicesBuffer
 734func (r *Renderer) UpdateIndicesBuffer(buffer []uint32) {
 735	r.i = S2TA(buffer)
 736	if r.indexBuffer.IsUndefined() {
 737		r.indexBuffer = r.glContext.Call("createBuffer")
 738	}
 739	r.glContext.Call("bindBuffer", r.glTypes.ElementArrayBuffer, r.indexBuffer)
 740	r.glContext.Call("bufferData", r.glTypes.ElementArrayBuffer, r.i, r.glTypes.StaticDraw)
 741}
 742
 743// Render renders
 744func (r *Renderer) Render(_ js.Value, args []js.Value) interface{} { // nolint
 745	now := float32(args[0].Float())
 746	tdiff := now - r.tmark
 747	r.tmark = now
 748	r.rX = r.rX + r.sX*float32(tdiff)/500
 749	r.rY = r.rY + r.sY*float32(tdiff)/500
 750	r.rZ = r.rZ + r.sZ*float32(tdiff)/500
 751
 752	r.movMatrix = mgl32.HomogRotate3DX(r.rX)
 753	r.movMatrix = r.movMatrix.Mul4(mgl32.HomogRotate3DY(r.rY))
 754	r.movMatrix = r.movMatrix.Mul4(mgl32.HomogRotate3DZ(r.rZ))
 755
 756	modelMatrixBuffer := (*[16]float32)(u.Pointer(&r.movMatrix)) // nolint
 757	typedModelMatrixBuffer := S2TA([]float32((*modelMatrixBuffer)[:]))
 758
 759	r.glContext.Call("uniformMatrix4fv", r.ModelMatrix, false, typedModelMatrixBuffer)
 760
 761	r.glContext.Call("enable", r.glTypes.DepthTest)
 762	r.glContext.Call("clear", r.glTypes.ColorBufferBit)
 763	r.glContext.Call("clear", r.glTypes.DepthBufferBit)
 764	usegltype := r.glTypes.Triangles
 765	if stlFileName == ".stl" || stlFileName == "" {
 766		usegltype = r.glTypes.Line
 767		r.glContext.Call("drawArrays", r.glTypes.LineLoop, 0, r.numVertices/3)
 768	}
 769	r.glContext.Call("drawElements", usegltype, r.numIndices, r.glTypes.UnsignedInt, 0)
 770
 771	return nil
 772}
 773
 774// SetZoom Sets the Zoom
 775func (r *Renderer) SetZoom(currentZoom float32) {
 776	viewMatrix := mgl32.LookAtV(mgl32.Vec3{currentZoom, currentZoom, currentZoom}, mgl32.Vec3{0.0, 0.0, 0.0}, mgl32.Vec3{0.0, 1.0, 0.0})
 777	viewMatrixBuffer := (*[16]float32)(u.Pointer(&viewMatrix)) // nolint
 778	typedViewMatrixBuffer := S2TA([]float32((*viewMatrixBuffer)[:]))
 779	r.glContext.Call("uniformMatrix4fv", r.ViewMatrix, false, typedViewMatrixBuffer)
 780}
 781
 782// NewColorInterpolation generates color interpolation
 783func NewColorInterpolation(a Color, b Color) ColorInterpolation {
 784	return ColorInterpolation{
 785		a,
 786		b,
 787		a.Subtract(b),
 788	}
 789}
 790
 791// ColorInterpolation is interpolated color
 792type ColorInterpolation struct {
 793	startColor Color
 794	endColor   Color
 795	deltaColor Color
 796}
 797
 798// Interpolate interpolates
 799func (c ColorInterpolation) Interpolate(percent float32) Color {
 800	scaled := c.deltaColor.MultiplyFloat(percent)
 801	return c.startColor.Add(scaled)
 802}
 803
 804// Color represents a color
 805type Color struct {
 806	Red   float32
 807	Green float32
 808	Blue  float32
 809}
 810
 811// NewRandomColor returns a New RandomColor
 812func NewRandomColor() Color {
 813	const maxRGB = 255
 814	var r, g, b float64
 815	buf := make([]byte, 3)
 816	rand.Read(buf)
 817	r = float64(buf[0]) / 256
 818	g = float64(buf[1]) / 256
 819	b = float64(buf[2]) / 256
 820	r = r * maxRGB
 821	g = g * maxRGB
 822	b = b * maxRGB
 823	return Color{float32(r), float32(g), float32(b)}
 824}
 825
 826// Subtract Subtracts color
 827func (c Color) Subtract(d Color) Color {
 828	return Color{
 829		c.Red - d.Red,
 830		c.Green - d.Green,
 831		c.Blue - d.Blue,
 832	}
 833}
 834
 835// Add Adds color
 836func (c Color) Add(d Color) Color {
 837	return Color{
 838		c.Red + d.Red,
 839		c.Green + d.Green,
 840		c.Blue + d.Blue,
 841	}
 842}
 843
 844// MultiplyFloat Multiplies Float
 845func (c Color) MultiplyFloat(x float32) Color {
 846	return Color{
 847		c.Red * x,
 848		c.Green * x,
 849		c.Blue * x,
 850	}
 851}
 852
 853// GenerateGradient Generates Gradient
 854func GenerateGradient(numColors int, steps int) []Color {
 855	distribution := distributeColors(numColors, steps)
 856	colors := make([]Color, numColors)
 857	for i := 0; i < numColors; i++ {
 858		colors[i] = NewRandomColor()
 859	}
 860	outputBuffer := make([]Color, 0, steps)
 861	for index := 0; index < numColors; index++ {
 862		if index >= numColors-1 {
 863			size := steps - distribution[index]
 864			interpolation := NewColorInterpolation(colors[index-1], colors[index])
 865			buffer := generateSingleGradient(interpolation, size)
 866			outputBuffer = append(outputBuffer, buffer...)
 867			break
 868		}
 869		currentStep := distribution[index]
 870		nextStep := distribution[index+1]
 871		size := nextStep - currentStep
 872		interpolation := NewColorInterpolation(colors[index], colors[index+1])
 873		buffer := generateSingleGradient(interpolation, size)
 874		outputBuffer = append(outputBuffer, buffer...)
 875	}
 876	return outputBuffer
 877}
 878
 879func distributeColors(numColors int, steps int) []int {
 880	diff := int(m.Ceil(float64(steps) / float64(numColors)))
 881	output := make([]int, numColors)
 882	for i := 0; i < numColors; i++ {
 883		output[i] = diff * i
 884	}
 885	return output
 886}
 887
 888func generateSingleGradient(c ColorInterpolation, numSteps int) []Color {
 889	output := make([]Color, numSteps)
 890	for i := 0; i < numSteps; i++ {
 891		percent := float32(i) / float32(numSteps)
 892		output[i] = c.Interpolate(percent)
 893	}
 894	return output
 895}
 896
 897func f32(f float32, g byte, prec, bitSize int) string {
 898	return strconv.FormatFloat(float64(f), g, prec, bitSize)
 899}
 900func f64(f float64, g byte, prec, bitSize int) string {
 901	return strconv.FormatFloat(f, g, prec, bitSize)
 902}
 903
 904func generateSphereVertices(radius float32, stacks, slices int) ([]float32, []uint32) {
 905	var vertices []float32
 906	var indices []uint32
 907
 908	// Generate random initial rotation
 909	rotationMatrix := randomRotationMatrix()
 910
 911	// Generate sphere vertices
 912	for i := 0; i <= stacks; i++ {
 913		phi := float32(i) * float32(m.Pi) / float32(stacks)
 914		for j := 0; j <= slices; j++ {
 915			theta := float32(j) * 2.0 * float32(m.Pi) / float32(slices)
 916			x := radius * float32(m.Sin(float64(phi))) * float32(m.Cos(float64(theta)))
 917			y := radius * float32(m.Sin(float64(phi))) * float32(m.Sin(float64(theta)))
 918			z := radius * float32(m.Cos(float64(phi)))
 919
 920			// Apply rotation to the vertex
 921			vertex := mgl32.Vec3{z, y, x}
 922			rotatedVertex := rotationMatrix.Mul4x1(vertex.Vec4(1.0))
 923
 924			// Append rotated vertex
 925			vertices = append(vertices, rotatedVertex[0], rotatedVertex[1], rotatedVertex[2])
 926		}
 927	}
 928
 929	// Generate sphere indices
 930	for i := 0; i < stacks; i++ {
 931		for j := 0; j <= slices; j++ {
 932			indices = append(indices, uint32(i*(slices+1)+j), uint32((i+1)*(slices+1)+j))
 933		}
 934	}
 935
 936	return vertices, indices
 937}
 938
 939func randomRotationMatrix() mgl32.Mat4 {
 940	// Generate random rotation angles (in radians) using crypto/rand
 941	rotX := randomFloat32() * 2 * float32(m.Pi)
 942	rotY := randomFloat32() * 2 * float32(m.Pi)
 943	rotZ := randomFloat32() * 2 * float32(m.Pi)
 944
 945	// Create rotation matrices for each axis
 946	rotMatrixX := mgl32.HomogRotate3DX(rotX)
 947	rotMatrixY := mgl32.HomogRotate3DY(rotY)
 948	rotMatrixZ := mgl32.HomogRotate3DZ(rotZ)
 949
 950	// Combine rotations (Z * Y * X)
 951	return rotMatrixZ.Mul4(rotMatrixY).Mul4(rotMatrixX)
 952}
 953
 954func randomFloat32() float32 {
 955	var randomValue uint32
 956	err := binary.Read(rand.Reader, binary.BigEndian, &randomValue)
 957	if err != nil {
 958		log.Fatalf("Failed to read random value: %v", err)
 959	}
 960	return float32(randomValue) / float32(0xFFFFFFFF)
 961}
 962
 963var verticesNative = []float32{
 964	-1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,
 965	-1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1,
 966	-1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1,
 967	1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1,
 968	-1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1,
 969	-1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1,
 970}
 971var colorsNative = []float32{
 972	5, 3, 7, 5, 3, 7, 5, 3, 7, 5, 3, 7,
 973	1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3,
 974	0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
 975	1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
 976	1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
 977	0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
 978}
 979
 980var indicesNative = []uint32{
 981	0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7,
 982	8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15,
 983	16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
 984}
 985
 986const vertShaderCode = `
 987attribute vec3 position;
 988uniform mat4 Pmatrix;
 989uniform mat4 Vmatrix;
 990uniform mat4 Mmatrix;
 991attribute vec3 color;
 992varying vec3 vColor;
 993
 994void main(void) {
 995	gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);
 996	vColor = color;
 997}
 998`
 999const fragShaderCode = `
1000precision mediump float;
1001varying vec3 vColor;
1002void main(void) {
1003	gl_FragColor = vec4(vColor, 1.);
1004}
1005`
1006const fragShaderCode1 = `
1007precision mediump float;
1008uniform vec3 uBaseColor; // Color value at the base
1009uniform vec3 uTopColor;  // Color value at the top
1010varying vec3 vPosition;  // Interpolated vertex position
1011void main(void) {
1012	float t = (vPosition.y + 1.0) * 0.5; // Normalize the y-coordinate to [0, 1]
1013	vec3 rainbowColor = mix(uBaseColor, uTopColor, t);
1014	gl_FragColor = vec4(rainbowColor, 1.0);
1015}
1016`
1017const vertShaderCode1 = `
1018	attribute vec3 position;
1019	uniform mat4 Pmatrix;
1020	uniform mat4 Vmatrix;
1021	uniform mat4 Mmatrix;
1022	varying vec3 vPosition;  // Pass vertex position to fragment shader
1023	void main(void) {
1024		gl_Position = Pmatrix * Vmatrix * Mmatrix * vec4(position, 1.0);
1025		vPosition = position;  // Pass vertex position to fragment shader
1026	}
1027	`
1028
1029// GLTypes provides WebGL bindings.
1030type GLTypes struct {
1031	StaticDraw         js.Value
1032	ArrayBuffer        js.Value
1033	ElementArrayBuffer js.Value
1034	VertexShader       js.Value
1035	FragmentShader     js.Value
1036	Float              js.Value
1037	DepthTest          js.Value
1038	ColorBufferBit     js.Value
1039	DepthBufferBit     js.Value
1040	Triangles          js.Value
1041	UnsignedShort      js.Value
1042	UnsignedInt        js.Value
1043	LEqual             js.Value
1044	LineLoop           js.Value
1045	Line               js.Value
1046}
1047
1048// New grabs the WebGL bindings from a GL context.
1049func (types *GLTypes) New(gl js.Value) js.Value {
1050	types.StaticDraw = gl.Get("STATIC_DRAW")
1051	types.ArrayBuffer = gl.Get("ARRAY_BUFFER")
1052	types.ElementArrayBuffer = gl.Get("ELEMENT_ARRAY_BUFFER")
1053	types.VertexShader = gl.Get("VERTEX_SHADER")
1054	types.FragmentShader = gl.Get("FRAGMENT_SHADER")
1055	types.Float = gl.Get("FLOAT")
1056	types.DepthTest = gl.Get("DEPTH_TEST")
1057	types.ColorBufferBit = gl.Get("COLOR_BUFFER_BIT")
1058	types.Triangles = gl.Get("TRIANGLES")
1059	types.UnsignedShort = gl.Get("UNSIGNED_SHORT")
1060	types.LEqual = gl.Get("LEQUAL")
1061	types.DepthBufferBit = gl.Get("DEPTH_BUFFER_BIT")
1062	types.LineLoop = gl.Get("LINE_LOOP")
1063	types.Line = gl.Get("LINES")
1064	enabled := gl.Call("getExtension", "OES_element_index_uint")
1065	if !enabled.Truthy() {
1066		return js.Global().Get("Error").New("missing extension: OES_element_index_uint")
1067	}
1068	types.UnsignedInt = gl.Get("UNSIGNED_INT")
1069	return js.Null()
1070}
1071
1072func sliceToByteSlice(s interface{}) []byte {
1073	switch s := s.(type) {
1074	case []int8:
1075		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1076		return *(*[]byte)(u.Pointer(h))      // nolint
1077	case []int16:
1078		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1079		h.Len *= 2
1080		h.Cap *= 2
1081		return *(*[]byte)(u.Pointer(h)) // nolint
1082	case []int32:
1083		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1084		h.Len *= 4
1085		h.Cap *= 4
1086		return *(*[]byte)(u.Pointer(h)) // nolint
1087	case []int64:
1088		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1089		h.Len *= 8
1090		h.Cap *= 8
1091		return *(*[]byte)(u.Pointer(h)) // nolint
1092	case []uint8:
1093		return s
1094	case []uint16:
1095		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1096		h.Len *= 2
1097		h.Cap *= 2
1098		return *(*[]byte)(u.Pointer(h)) // nolint
1099	case []uint32:
1100		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1101		h.Len *= 4
1102		h.Cap *= 4
1103		return *(*[]byte)(u.Pointer(h)) // nolint
1104	case []uint64:
1105		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1106		h.Len *= 8
1107		h.Cap *= 8
1108		return *(*[]byte)(u.Pointer(h)) // nolint
1109	case []float32:
1110		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1111		h.Len *= 4
1112		h.Cap *= 4
1113		return *(*[]byte)(u.Pointer(h)) // nolint
1114	case []float64:
1115		h := (*r.SliceHeader)(u.Pointer(&s)) // nolint
1116		h.Len *= 8
1117		h.Cap *= 8
1118		return *(*[]byte)(u.Pointer(h)) // nolint
1119	default:
1120		//		panic("jsutil: unexpected value at sliceToBytesSlice: " + r.TypeOf(s).String())
1121		panic("jsutil: unexpected value at sliceToBytesSlice: ")
1122	}
1123}
1124
1125const bo = "byteOffset"
1126const bl = "byteLength"
1127const b = "buffer"
1128const u8a = "Uint8Array"
1129
1130// S2TA converts Slice To TypedArray
1131func S2TA(s interface{}) js.Value {
1132	switch s := s.(type) {
1133	case []int8:
1134		a := js.Global().Get(u8a).New(len(s))
1135		js.CopyBytesToJS(a, sliceToByteSlice(s))
1136		runtime.KeepAlive(s)
1137		buf := a.Get(b)
1138		return js.Global().Get("Int8Array").New(buf, a.Get(bo), a.Get(bl))
1139	case []int16:
1140		a := js.Global().Get(u8a).New(len(s) * 2)
1141		js.CopyBytesToJS(a, sliceToByteSlice(s))
1142		runtime.KeepAlive(s)
1143		buf := a.Get(b)
1144		return js.Global().Get("Int16Array").New(buf, a.Get(bo), a.Get(bl).Int()/2)
1145	case []int32:
1146		a := js.Global().Get(u8a).New(len(s) * 4)
1147		js.CopyBytesToJS(a, sliceToByteSlice(s))
1148		runtime.KeepAlive(s)
1149		buf := a.Get(b)
1150		return js.Global().Get("Int32Array").New(buf, a.Get(bo), a.Get(bl).Int()/4)
1151	case []uint8:
1152		a := js.Global().Get(u8a).New(len(s))
1153		js.CopyBytesToJS(a, s)
1154		runtime.KeepAlive(s)
1155		return a
1156	case []uint16:
1157		a := js.Global().Get(u8a).New(len(s) * 2)
1158		js.CopyBytesToJS(a, sliceToByteSlice(s))
1159		runtime.KeepAlive(s)
1160		buf := a.Get(b)
1161		return js.Global().Get("Uint16Array").New(buf, a.Get(bo), a.Get(bl).Int()/2)
1162	case []uint32:
1163		a := js.Global().Get(u8a).New(len(s) * 4)
1164		js.CopyBytesToJS(a, sliceToByteSlice(s))
1165		runtime.KeepAlive(s)
1166		buf := a.Get(b)
1167		return js.Global().Get("Uint32Array").New(buf, a.Get(bo), a.Get(bl).Int()/4)
1168	case []float32:
1169		a := js.Global().Get(u8a).New(len(s) * 4)
1170		js.CopyBytesToJS(a, sliceToByteSlice(s))
1171		runtime.KeepAlive(s)
1172		buf := a.Get(b)
1173		return js.Global().Get("Float32Array").New(buf, a.Get(bo), a.Get(bl).Int()/4)
1174	case []float64:
1175		a := js.Global().Get(u8a).New(len(s) * 8)
1176		js.CopyBytesToJS(a, sliceToByteSlice(s))
1177		runtime.KeepAlive(s)
1178		buf := a.Get(b)
1179		return js.Global().Get("Float64Array").New(buf, a.Get(bo), a.Get(bl).Int()/8)
1180	default:
1181		//		panic("jsutil: unexpected value at S2TA: " + r.TypeOf(s).String())
1182		panic("jsutil: unexpected value at S2TA: ")
1183	}
1184}
1185
1186