aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2024-01-17 15:38:33 -0500
committerSam Anthony <sam@samanthony.xyz>2024-01-17 15:38:33 -0500
commit29e2be59cc3d0f31241884087624ece7d35115e4 (patch)
tree07486ce8cebf9d7f9901f015c6ccd7803b8baa59
parent6a2e268df7e008579d1b6a0f2ef47597fcbe4862 (diff)
downloadvolute-29e2be59cc3d0f31241884087624ece7d35115e4.zip
add user input widget
-rw-r--r--go.mod18
-rw-r--r--go.sum43
-rw-r--r--gui/widget/concurrent_face.go51
-rw-r--r--gui/widget/text.go74
-rw-r--r--gui/widget/widget.go65
-rw-r--r--main.go172
-rw-r--r--ui.go381
7 files changed, 235 insertions, 569 deletions
diff --git a/go.mod b/go.mod
index 3eb3590..8a14268 100644
--- a/go.mod
+++ b/go.mod
@@ -3,19 +3,11 @@ module volute
go 1.18
require (
- github.com/AllenDang/giu v0.6.2
github.com/BurntSushi/toml v1.1.0
+ github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3
+ github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71
+ github.com/go-gl/glfw v0.0.0-20240108052320-294b0144ba39
+ golang.org/x/image v0.15.0
)
-require (
- github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8 // indirect
- github.com/AllenDang/imgui-go v1.12.1-0.20220322114136-499bbf6a42ad // indirect
- github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 // indirect
- github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
- github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958 // indirect
- github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
- github.com/sahilm/fuzzy v0.1.0 // indirect
- golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
- golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
- gopkg.in/eapache/queue.v1 v1.1.0 // indirect
-)
+require golang.org/x/text v0.14.0 // indirect
diff --git a/go.sum b/go.sum
index 074e02e..523443f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,39 +1,12 @@
-github.com/AllenDang/giu v0.6.2 h1:CFIHSQxDqEFNsNnTO9LXBVZ8zlInV71H3M6V3BNagmI=
-github.com/AllenDang/giu v0.6.2/go.mod h1:9hCQh0l0wbBzOqe9cr02EB9EsNOy9AwFIjG4xVsR6TI=
-github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8 h1:dKZMqib/yUDoCFigmz2agG8geZ/e3iRq304/KJXqKyw=
-github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8/go.mod h1:b4uuDd0s6KRIPa84cEEchdQ9ICh7K0OryZHbSzMca9k=
-github.com/AllenDang/imgui-go v1.12.1-0.20220322114136-499bbf6a42ad h1:Kr961C2uEEAklK+jBRiZVnQH0AgS7o6pXrIgUTUUGiM=
-github.com/AllenDang/imgui-go v1.12.1-0.20220322114136-499bbf6a42ad/go.mod h1:kuPs9RWleaUuK7D49bE6HPxyRA36Lp4ICKGp+5OnnbY=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 h1:baVdMKlASEHrj19iqjARrPbaRisD7EuZEVJj6ZMLl1Q=
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M=
-github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
-github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958 h1:TL70PMkdPCt9cRhKTqsm+giRpgrd0IGEj763nNr2VFY=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220320163800-277f93cfa958/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
-github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
-golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGbhG5+9fMuvOmUYwNEF4q4=
-golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
-golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/eapache/queue.v1 v1.1.0 h1:EldqoJEGtXYiVCMRo2C9mePO2UUGnYn2+qLmlQSqPdc=
-gopkg.in/eapache/queue.v1 v1.1.0/go.mod h1:wNtmx1/O7kZSR9zNT1TTOJ7GLpm3Vn7srzlfylFbQwU=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
+github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
+github.com/go-gl/glfw v0.0.0-20240108052320-294b0144ba39 h1:NQ/PsAvvBcCUiFODaU3tmnKesYruVJL+Dx5hTRrr+DA=
+github.com/go-gl/glfw v0.0.0-20240108052320-294b0144ba39/go.mod h1:wyvWpaEu9B/VQiV1jsPs7Mha9I7yto/HqIBw197ZAzk=
+golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
+golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
diff --git a/gui/widget/concurrent_face.go b/gui/widget/concurrent_face.go
new file mode 100644
index 0000000..98db572
--- /dev/null
+++ b/gui/widget/concurrent_face.go
@@ -0,0 +1,51 @@
+package widget
+
+import (
+ "image"
+ "sync"
+
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+type concurrentFace struct {
+ mu sync.Mutex
+ face font.Face
+}
+
+func (cf *concurrentFace) Close() error {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.Close()
+}
+
+func (cf *concurrentFace) Glyph(dot fixed.Point26_6, r rune) (
+ dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.Glyph(dot, r)
+}
+
+func (cf *concurrentFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.GlyphBounds(r)
+}
+
+func (cf *concurrentFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.GlyphAdvance(r)
+}
+
+func (cf *concurrentFace) Kern(r0, r1 rune) fixed.Int26_6 {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.Kern(r0, r1)
+}
+
+func (cf *concurrentFace) Metrics() font.Metrics {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.Metrics()
+}
diff --git a/gui/widget/text.go b/gui/widget/text.go
new file mode 100644
index 0000000..cb66a0a
--- /dev/null
+++ b/gui/widget/text.go
@@ -0,0 +1,74 @@
+package widget
+
+import (
+ "log"
+ "sync"
+
+ "image"
+ "image/draw"
+
+ "golang.org/x/image/font"
+ "golang.org/x/image/font/gofont/goregular"
+ "golang.org/x/image/font/opentype"
+ "golang.org/x/image/math/fixed"
+)
+
+var (
+ FONT = goregular.TTF
+ FONT_SIZE float64 = 15
+ DPI float64 = 72
+ BG_COLOR = image.White
+ TEXT_COLOR = image.Black
+)
+
+var face *concurrentFace
+
+func init() {
+ fnt, err := opentype.Parse(FONT)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fce, err := opentype.NewFace(fnt, &opentype.FaceOptions{
+ Size: FONT_SIZE,
+ DPI: DPI,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ face = &concurrentFace{sync.Mutex{}, fce}
+}
+
+func drawText(text []byte, dst draw.Image, r image.Rectangle) {
+ drawer := font.Drawer{
+ Src: TEXT_COLOR,
+ Face: face,
+ Dot: fixed.P(0, 0),
+ }
+
+ bounds := textBounds(text, drawer)
+
+ // background
+ draw.Draw(dst, r, BG_COLOR, image.ZP, draw.Src)
+
+ // text image
+ textImg := image.NewRGBA(bounds)
+ draw.Draw(textImg, bounds, BG_COLOR, image.ZP, draw.Src)
+ drawer.Dst = textImg
+ drawer.DrawBytes(text)
+
+ // draw text image over background
+ left := image.Pt(bounds.Min.X, (bounds.Min.Y+bounds.Max.Y)/2)
+ target := image.Pt(r.Min.X, (r.Min.Y+r.Max.Y)/2)
+ delta := target.Sub(left)
+ draw.Draw(dst, bounds.Add(delta).Intersect(r), drawer.Dst, bounds.Min, draw.Src)
+}
+
+func textBounds(text []byte, drawer font.Drawer) image.Rectangle {
+ b, _ := drawer.BoundBytes(text)
+ return image.Rect(
+ b.Min.X.Floor(),
+ b.Min.Y.Floor(),
+ b.Max.X.Ceil(),
+ b.Max.Y.Ceil(),
+ )
+}
diff --git a/gui/widget/widget.go b/gui/widget/widget.go
new file mode 100644
index 0000000..913809b
--- /dev/null
+++ b/gui/widget/widget.go
@@ -0,0 +1,65 @@
+package widget
+
+import (
+ "cmp"
+ "fmt"
+
+ "image"
+ "image/draw"
+
+ "volute/gui"
+ "volute/gui/win"
+)
+
+func Input(env gui.Env, r image.Rectangle, val chan<- float64) {
+ redraw := func(text []byte) func(draw.Image) image.Rectangle {
+ return func(drw draw.Image) image.Rectangle {
+ drawText(text, drw, r)
+ return r
+ }
+ }
+ text := []byte{'0'}
+ focus := false
+
+ env.Draw() <- redraw(text)
+
+ for event := range env.Events() {
+ switch event := event.(type) {
+ case win.WiFocus:
+ if event.Focused {
+ env.Draw() <- redraw(text)
+ }
+ case win.MoDown:
+ if event.Point.In(r) {
+ focus = true
+ }
+ case win.KbType:
+ if !focus ||
+ (!isDigit(event.Rune) && event.Rune != '.') ||
+ (event.Rune == '.' && contains(text, '.')) {
+ continue
+ }
+ text = fmt.Appendf(text, "%c", event.Rune)
+ env.Draw() <- redraw(text)
+ case win.KbDown:
+ if event.Key == win.KeyBackspace && len(text) > 0 {
+ text = text[:len(text)-1]
+ env.Draw() <- redraw(text)
+ }
+ }
+ }
+ close(env.Draw())
+}
+
+func isDigit(r rune) bool {
+ return '0' <= r && r <= '9'
+}
+
+func contains[T cmp.Ordered](slc []T, v T) bool {
+ for i := range slc {
+ if slc[i] == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/main.go b/main.go
index 491aa80..c6c522f 100644
--- a/main.go
+++ b/main.go
@@ -1,155 +1,47 @@
package main
import (
- "fmt"
- g "github.com/AllenDang/giu"
"image"
- "image/draw"
- _ "image/jpeg"
- "os"
- "volute/compressor"
- "volute/mass"
- "volute/pressure"
- "volute/temperature"
- "volute/util"
- "volute/volume"
+ "github.com/faiface/mainthread"
+ "volute/gui"
+ "volute/gui/widget"
+ "volute/gui/win"
)
-const (
- gasConstant = 8.314472
- airMolarMass = 0.0289647 // kg/mol
-)
-
-// numPoints is the number of datapoints on the compressor map.
-var numPoints = 1
-
-var (
- displacement = 2000 * volume.CubicCentimetre
- // volumeUnitIndex is used to index volume.UnitStrings().
- volumeUnitIndex = volume.DefaultUnitIndex
-
- engineSpeed = []int32{2000}
-
- volumetricEfficiency = []int32{80}
-
- intakeAirTemperature = []temperature.Temperature{{25, temperature.Celcius}}
- // temperatureUnitIndex is used to index temperature.UnitStrings().
- temperatureUnitIndex = temperature.DefaultUnitIndex
-
- manifoldPressure = []pressure.Pressure{pressure.Atmospheric()}
- // pressureUnitIndex is used to index pressure.UnitStrings().
- pressureUnitIndex = pressure.DefaultUnitIndex
-)
-
-var pressureRatio []float32
-
-func pressureRatioAt(point int) float32 {
- u := pressure.Pascal
- m := manifoldPressure[point] / u
- a := pressure.Atmospheric() / u
- return float32(m / a)
-}
-func init() {
- pressureRatio = append(pressureRatio, pressureRatioAt(0))
-}
-
-var (
- engineMassFlowRate []mass.FlowRate
- // selectedMassFlowRateUnit is used to index mass.FlowRateUnitStrings().
- selectedMassFlowRateUnit = mass.DefaultFlowRateUnitIndex
-)
-
-func massFlowRateAt(point int) mass.FlowRate {
- rpm := float32(engineSpeed[point])
- disp := float32(displacement / volume.CubicMetre)
- ve := float32(volumetricEfficiency[point]) / 100.0
- cubicMetresPerMin := (rpm / 2.0) * disp * ve
-
- iat, err := intakeAirTemperature[point].AsUnit(temperature.Kelvin)
- util.Check(err)
- pres := manifoldPressure[point] / pressure.Pascal
- molsPerMin := (float32(pres) * cubicMetresPerMin) / (gasConstant * iat)
-
- kgPerMin := molsPerMin * airMolarMass
+func run() {
+ w, err := win.New(win.Title("volute"), win.Size(800, 600))
+ if err != nil {
+ panic(err)
+ }
- mfr := mass.FlowRate(kgPerMin/60.0) * mass.KilogramsPerSecond
- return mfr
-}
-func init() {
- engineMassFlowRate = append(engineMassFlowRate, massFlowRateAt(0))
-}
+ mux, env := gui.NewMux(w)
-var (
- compressorImage *image.RGBA
- compressorTexture *g.Texture
- selectedCompressor compressor.Compressor
-)
+ var (
+ displacementChan = make(chan float64)
+ )
-func init() {
- manufacturer := "garrett"
- series := "g"
- model := "25-660"
- c, ok := compressor.Compressors()[manufacturer][series][model]
- if !ok {
- fmt.Printf("compressor.Compressors()[\"%s\"][\"%s\"][\"%s\"] does not exist.\n",
- manufacturer, series, model,
- )
- os.Exit(1)
+ go widget.Input(mux.MakeEnv(), image.Rect(20, 20, 100, 40), displacementChan)
+
+Loop:
+ for event := range env.Events() {
+ switch event := event.(type) {
+ case win.WiClose:
+ break Loop
+ case win.KbType:
+ if event.Rune == 'q' {
+ break Loop
+ }
+ }
+ select {
+ case _ = <-displacementChan:
+ default:
+ }
}
-
- setCompressor(c)
+ close(env.Draw())
+ close(displacementChan)
}
func main() {
- wnd := g.NewMasterWindow("volute", 400, 200, 0)
-
- go updateCompImg()
- m := <-updatedCompImg
- g.EnqueueNewTextureFromRgba(m, func(tex *g.Texture) {
- compressorTexture = tex
- })
-
- wnd.Run(loop)
-}
-
-func setCompressor(c compressor.Compressor) {
- f, err := os.Open(c.FileName)
- util.Check(err)
- defer f.Close()
-
- j, _, err := image.Decode(f)
- util.Check(err)
-
- b := j.Bounds()
- m := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
- draw.Draw(m, m.Bounds(), j, b.Min, draw.Src)
-
- selectedCompressor = c
- compressorImage = m
-
- go updateCompImg()
-}
-
-func loop() {
- g.SingleWindow().Layout(
- engineDisplacementRow(),
- g.Table().
- Size(g.Auto, 190).
- Rows(
- engineSpeedRow(),
- volumetricEfficiencyRow(),
- intakeAirTemperatureRow(),
- manifoldPressureRow(),
- pressureRatioRow(),
- massFlowRateRow(),
- duplicateDeleteRow(),
- ).
- Columns(
- columns()...,
- ).
- Flags(g.TableFlagsSizingFixedFit),
- selectCompressor(),
- g.Custom(compressorWidget),
- )
+ mainthread.Run(run)
}
diff --git a/ui.go b/ui.go
deleted file mode 100644
index 118069d..0000000
--- a/ui.go
+++ /dev/null
@@ -1,381 +0,0 @@
-package main
-
-import (
- "fmt"
- g "github.com/AllenDang/giu"
- "image"
- "image/color"
- "image/draw"
- "strconv"
-
- "volute/compressor"
- "volute/mass"
- "volute/pressure"
- "volute/temperature"
- "volute/util"
- "volute/volume"
-)
-
-func red() color.RGBA {
- return color.RGBA{255, 0, 0, 255}
-}
-
-func engineDisplacementRow() *g.RowWidget {
- s := volume.UnitStrings()[volumeUnitIndex]
- unit, err := volume.UnitFromString(s)
- util.Check(err)
- engDisp := float32(displacement / unit)
- valWid, _ := g.CalcTextSize("12345.67")
- unitWid, _ := g.CalcTextSize(volume.UnitStrings()[volumeUnitIndex])
- return g.Row(
- g.Label("Engine Displacement"),
- g.InputFloat(&engDisp).
- Format("%.2f").
- OnChange(func() {
- displacement = volume.Volume(engDisp) * unit
- for i := 0; i < numPoints; i++ {
- engineMassFlowRate[i] = massFlowRateAt(i)
- go updateCompImg()
- }
- }).
- Size(valWid),
- g.Combo(
- "",
- volume.UnitStrings()[volumeUnitIndex],
- volume.UnitStrings(),
- &volumeUnitIndex,
- ).Size(unitWid*2),
- )
-}
-
-func engineSpeedRow() *g.TableRowWidget {
- widgets := []g.Widget{
- g.Label("Engine Speed"),
- g.Label("rpm"),
- }
- for i := 0; i < numPoints; i++ {
- i := i
- widgets = append(
- widgets,
- g.InputInt(&engineSpeed[i]).OnChange(func() {
- engineMassFlowRate[i] = massFlowRateAt(i)
- go updateCompImg()
- }),
- )
- }
- return g.TableRow(widgets...)
-}
-
-func volumetricEfficiencyRow() *g.TableRowWidget {
- widgets := []g.Widget{
- g.Label("Volumetric Efficiency"),
- g.Label("%"),
- }
- for i := 0; i < numPoints; i++ {
- i := i
- widgets = append(
- widgets,
- g.InputInt(&volumetricEfficiency[i]).OnChange(func() {
- engineMassFlowRate[i] = massFlowRateAt(i)
- go updateCompImg()
- }),
- )
- }
- return g.TableRow(widgets...)
-}
-
-func intakeAirTemperatureRow() *g.TableRowWidget {
- wid, _ := g.CalcTextSize(temperature.UnitStrings()[temperatureUnitIndex])
- widgets := []g.Widget{
- g.Label("Intake Air Temperature"),
- g.Combo(
- "",
- temperature.UnitStrings()[temperatureUnitIndex],
- temperature.UnitStrings(),
- &temperatureUnitIndex,
- ).OnChange(func() {
- s := temperature.UnitStrings()[temperatureUnitIndex]
- u, err := temperature.UnitFromString(s)
- util.Check(err)
-
- for i := range intakeAirTemperature {
- t, err := intakeAirTemperature[i].AsUnit(u)
- util.Check(err)
- intakeAirTemperature[i] = temperature.Temperature{t, u}
- }
- }).Size(wid * 2),
- }
- for i := 0; i < numPoints; i++ {
- i := i
- widgets = append(
- widgets,
- g.InputFloat(&intakeAirTemperature[i].Val).
- Format("%.2f").
- OnChange(func() {
- engineMassFlowRate[i] = massFlowRateAt(i)
- go updateCompImg()
- }),
- )
- }
- return g.TableRow(widgets...)
-}
-
-func manifoldPressureRow() *g.TableRowWidget {
- s := pressure.UnitStrings()[pressureUnitIndex]
- unit, err := pressure.UnitFromString(s)
- util.Check(err)
- wid, _ := g.CalcTextSize(pressure.UnitStrings()[pressureUnitIndex])
- widgets := []g.Widget{
- g.Label("Manifold Absolute Pressure"),
- g.Combo(
- "",
- pressure.UnitStrings()[pressureUnitIndex],
- pressure.UnitStrings(),
- &pressureUnitIndex,
- ).Size(wid * 2),
- }
- for i := 0; i < numPoints; i++ {
- i := i
- manPres := float32(manifoldPressure[i] / unit)
- widgets = append(
- widgets,
- g.InputFloat(&manPres).Format("%.2f").
- OnChange(func() {
- manifoldPressure[i] = pressure.Pressure(manPres * float32(unit))
- pressureRatio[i] = pressureRatioAt(i)
- engineMassFlowRate[i] = massFlowRateAt(i)
- go updateCompImg()
- }),
- )
- }
- return g.TableRow(widgets...)
-}
-
-func pressureRatioRow() *g.TableRowWidget {
- widgets := []g.Widget{
- g.Label("Pressure Ratio"),
- g.Label(""),
- }
- for i := 0; i < numPoints; i++ {
- pr := strconv.FormatFloat(float64(pressureRatio[i]), 'f', 1, 32)
- widgets = append(
- widgets,
- g.Label(pr),
- )
- }
- return g.TableRow(widgets...)
-}
-
-func massFlowRateRow() *g.TableRowWidget {
- s := mass.FlowRateUnitStrings()[selectedMassFlowRateUnit]
- mfrUnit, err := mass.FlowRateUnitFromString(s)
- util.Check(err)
-
- wid, _ := g.CalcTextSize(mass.FlowRateUnitStrings()[selectedMassFlowRateUnit])
- widgets := []g.Widget{
- g.Label("Mass Flow Rate"),
- g.Combo(
- "",
- mass.FlowRateUnitStrings()[selectedMassFlowRateUnit],
- mass.FlowRateUnitStrings(),
- &selectedMassFlowRateUnit,
- ).Size(wid * 2),
- }
- for i := 0; i < numPoints; i++ {
- mfr := strconv.FormatFloat(
- float64(engineMassFlowRate[i]/mfrUnit),
- 'f',
- 3,
- 32,
- )
- widgets = append(
- widgets,
- g.Label(mfr),
- )
- }
- return g.TableRow(widgets...)
-}
-
-func duplicateDeleteRow() *g.TableRowWidget {
- widgets := []g.Widget{g.Label(""), g.Label("")}
- for i := 0; i < numPoints; i++ {
- i := i
- widgets = append(widgets, g.Row(
- g.Button("Duplicate").OnClick(func() {
- numPoints++
- engineSpeed = util.Insert(
- engineSpeed,
- engineSpeed[i],
- i,
- )
- volumetricEfficiency = util.Insert(
- volumetricEfficiency,
- volumetricEfficiency[i],
- i,
- )
- intakeAirTemperature = util.Insert(
- intakeAirTemperature,
- intakeAirTemperature[i],
- i,
- )
- manifoldPressure = util.Insert(
- manifoldPressure,
- manifoldPressure[i],
- i,
- )
- pressureRatio = util.Insert(
- pressureRatio,
- pressureRatio[i],
- i,
- )
- engineMassFlowRate = util.Insert(
- engineMassFlowRate,
- engineMassFlowRate[i],
- i,
- )
- go updateCompImg()
- }),
- g.Button("Delete").OnClick(func() {
- if numPoints < 2 {
- return
- }
- numPoints--
- engineSpeed = util.Remove(engineSpeed, i)
- volumetricEfficiency = util.Remove(volumetricEfficiency, i)
- intakeAirTemperature = util.Remove(intakeAirTemperature, i)
- manifoldPressure = util.Remove(manifoldPressure, i)
- pressureRatio = util.Remove(pressureRatio, i)
- engineMassFlowRate = util.Remove(engineMassFlowRate, i)
- go updateCompImg()
- }),
- ))
- }
- return g.TableRow(widgets...)
-}
-
-func columns() []*g.TableColumnWidget {
- widgets := []*g.TableColumnWidget{
- g.TableColumn("Parameter"),
- g.TableColumn("Unit"),
- }
- for i := 0; i < numPoints; i++ {
- widgets = append(
- widgets,
- g.TableColumn(fmt.Sprintf("Point %d", i+1)),
- )
- }
- return widgets
-}
-
-var compressorTree []g.Widget
-
-func init() {
- compressors := compressor.Compressors()
- for man := range compressors {
- man := man // Manufacturer
- var serNodes []g.Widget
- for ser := range compressors[man] {
- ser := ser // Series
- var modNodes []g.Widget
- for mod, c := range compressors[man][ser] {
- mod := mod // Model
- c := c // Compressor
- modNodes = append(
- modNodes,
- g.Selectable(mod).OnClick(func() {
- go setCompressor(c)
- }),
- )
- }
- serNodes = append(
- serNodes,
- g.TreeNode(ser).Layout(modNodes...),
- )
- }
- manNode := g.TreeNode(man).Layout(serNodes...)
- compressorTree = append(compressorTree, manNode)
- }
-}
-
-func selectCompressor() g.Widget {
- return g.ComboCustom("Compressor", selectedCompressor.Name).
- Layout(compressorTree...)
-}
-
-var updatedCompImg = make(chan image.Image)
-
-func updateCompImg() {
- img := copyImage(compressorImage)
- for i := 0; i < numPoints; i++ {
- pos := pointPos(i)
- ps := img.Bounds().Dx() / 100 // Point size
- draw.Draw(img,
- image.Rect(pos.X-ps/2, pos.Y-ps/2, pos.X+ps/2, pos.Y+ps/2),
- &image.Uniform{red()},
- image.ZP,
- draw.Src,
- )
- }
- updatedCompImg <- img
-}
-
-func compressorWidget() {
- select {
- case m := <-updatedCompImg:
- g.EnqueueNewTextureFromRgba(m, func(tex *g.Texture) {
- compressorTexture = tex
- })
- default:
- }
-
- canvas := g.GetCanvas()
- if compressorTexture != nil {
- winWidth, winHeight := g.GetAvailableRegion()
-
- bounds := compressorImage.Bounds()
- imWidth := float32(bounds.Dx())
- imHeight := float32(bounds.Dy())
-
- var ratio, xratio, yratio float32
- xratio = winWidth / imWidth
- yratio = winHeight / imHeight
- if xratio < yratio {
- ratio = xratio
- } else {
- ratio = yratio
- }
-
- x := int(imWidth * ratio)
- y := int(imHeight * ratio)
-
- canvas.AddImage(
- compressorTexture,
- image.Pt(0, 250),
- image.Pt(x, y),
- )
- }
-}
-
-func copyImage(old *image.RGBA) *image.RGBA {
- b := old.Bounds()
- img := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
- draw.Draw(img, img.Bounds(), old, b.Min, draw.Src)
- return img
-}
-
-// The position on the compressor map of an operating point.
-func pointPos(i int) (pos image.Point) {
- const unit = mass.KilogramsPerSecond
- mfr := engineMassFlowRate[i] / unit
- maxMfr := selectedCompressor.MaxFlow / unit
- min := selectedCompressor.MinX
- max := selectedCompressor.MaxX
- pos.X = min + int(float32(max-min)*float32(mfr/maxMfr))
-
- min = selectedCompressor.MinY
- max = selectedCompressor.MaxY
- pr := pressureRatio[i]
- maxPr := selectedCompressor.MaxPR
- pos.Y = min - int(float32((min-max))*((pr-1.0)/(maxPr-1.0)))
- return pos
-}