diff options
Diffstat (limited to 'ui.go')
| -rw-r--r-- | ui.go | 444 |
1 files changed, 95 insertions, 349 deletions
@@ -1,371 +1,117 @@ package main import ( - "fmt" - g "github.com/AllenDang/giu" "image" "image/color" - "image/draw" - "strconv" -) + "sync" -var red = color.RGBA{255, 0, 0, 255} + "volute/gui" + "volute/gui/layout" + "volute/gui/widget" +) -func displacementRow() *g.RowWidget { - s := VolumeUnits[volumeUnitIndex] - unit, err := ParseVolumeUnit(s) - Check(err) - engDisp := float32(displacement / unit) - valWid, _ := g.CalcTextSize("12345.67") - unitWid, _ := g.CalcTextSize(VolumeUnits[volumeUnitIndex]) - return g.Row( - g.Label("Engine Displacement"), - g.InputFloat(&engDisp). - Format("%.2f"). - OnChange(func() { - displacement = Volume(engDisp) * unit - for i := 0; i < numPoints; i++ { - massFlowRateAir[i] = massFlowRateAt(i) - go updateCompImg() - } - }). - Size(valWid), - g.Combo( - "", - VolumeUnits[volumeUnitIndex], - VolumeUnits, - &volumeUnitIndex, - ).Size(unitWid*2), +func spawnWidgets( + displacementChan chan uint, + rpmChan, veChan, imapChan, actChan [POINTS]chan uint, + flowChan [POINTS]chan float64, + focus *widget.Focus, + mux *gui.Mux, + wg *sync.WaitGroup, +) { + bounds := layout.Grid{ + Rows: []int{2, 7, 7, 7, 7, 7}, + Background: color.Gray{255}, + Gap: 1, + Split: split, + SplitRows: splitRows, + Margin: 0, + Border: 0, + BorderColor: color.Gray{16}, + Flip: false, + }.Lay(image.Rect(0, 0, WIDTH, HEIGHT)) + + wg.Add(1) + go widget.Label("displacement (cc)", bounds[0], mux.MakeEnv(), wg) + wg.Add(1) + go widget.Input( + displacementChan, + bounds[1], + focus.Widgets[0][0], + mux.MakeEnv(), + wg, ) -} - -func speedRow() *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(&speed[i]).OnChange(func() { - massFlowRateAir[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() { - massFlowRateAir[i] = massFlowRateAt(i) - go updateCompImg() - }), - ) - } - return g.TableRow(widgets...) -} - -func intakeAirTemperatureRow() *g.TableRowWidget { - wid, _ := g.CalcTextSize(TemperatureUnits[temperatureUnitIndex]) - widgets := []g.Widget{ - g.Label("Intake Air Temperature"), - g.Combo( - "", - TemperatureUnits[temperatureUnitIndex], - TemperatureUnits, - &temperatureUnitIndex, - ).OnChange(func() { - s := TemperatureUnits[temperatureUnitIndex] - u, err := ParseTemperatureUnit(s) - Check(err) - - for i := range intakeAirTemperature { - t, err := intakeAirTemperature[i].AsUnit(u) - Check(err) - intakeAirTemperature[i] = 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() { - massFlowRateAir[i] = massFlowRateAt(i) - go updateCompImg() - }), - ) - } - return g.TableRow(widgets...) -} - -func manifoldPressureRow() *g.TableRowWidget { - s := PressureUnits[pressureUnitIndex] - unit, err := ParsePressureUnit(s) - Check(err) - wid, _ := g.CalcTextSize(PressureUnits[pressureUnitIndex]) - widgets := []g.Widget{ - g.Label("Manifold Absolute Pressure"), - g.Combo( - "", - PressureUnits[pressureUnitIndex], - PressureUnits, - &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(manPres * float32(unit)) - pressureRatio[i] = pressureRatioAt(i) - massFlowRateAir[i] = massFlowRateAt(i) - go updateCompImg() - }), + wg.Add(1) + go widget.Label("speed (rpm)", bounds[2], mux.MakeEnv(), wg) + wg.Add(1) + go widget.Label("VE (%)", bounds[3+POINTS], mux.MakeEnv(), wg) + wg.Add(1) + go widget.Label("IMAP (mbar)", bounds[4+2*POINTS], mux.MakeEnv(), wg) + wg.Add(1) + go widget.Label("ACT (°C)", bounds[5+3*POINTS], mux.MakeEnv(), wg) + wg.Add(1) + go widget.Label("mass flow (kg/min)", bounds[6+4*POINTS], mux.MakeEnv(), wg) + for i := 0; i < POINTS; i++ { + wg.Add(1) + go widget.Input( // speed + rpmChan[i], + bounds[3+i], + focus.Widgets[1][i], + mux.MakeEnv(), + wg, ) - } - 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), + wg.Add(1) + go widget.Input( // VE + veChan[i], + bounds[4+POINTS+i], + focus.Widgets[2][i], + mux.MakeEnv(), + wg, ) - } - return g.TableRow(widgets...) -} - -func massFlowRateRow() *g.TableRowWidget { - s := MassFlowRateUnits[massFlowRateUnitIndex] - mfrUnit, err := ParseMassFlowRateUnit(s) - Check(err) - - wid, _ := g.CalcTextSize(MassFlowRateUnits[massFlowRateUnitIndex]) - widgets := []g.Widget{ - g.Label("Mass Flow Rate"), - g.Combo( - "", - MassFlowRateUnits[massFlowRateUnitIndex], - MassFlowRateUnits, - &massFlowRateUnitIndex, - ).Size(wid * 2), - } - for i := 0; i < numPoints; i++ { - mfr := strconv.FormatFloat( - float64(massFlowRateAir[i]/mfrUnit), - 'f', - 3, - 32, + wg.Add(1) + go widget.Input( // IMAP + imapChan[i], + bounds[5+2*POINTS+i], + focus.Widgets[3][i], + mux.MakeEnv(), + wg, ) - widgets = append( - widgets, - g.Label(mfr), + wg.Add(1) + go widget.Input( // ACT + actChan[i], + bounds[6+3*POINTS+i], + focus.Widgets[4][i], + mux.MakeEnv(), + wg, ) - } - 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++ - speed = Insert( - speed, - speed[i], - i, - ) - volumetricEfficiency = Insert( - volumetricEfficiency, - volumetricEfficiency[i], - i, - ) - intakeAirTemperature = Insert( - intakeAirTemperature, - intakeAirTemperature[i], - i, - ) - manifoldPressure = Insert( - manifoldPressure, - manifoldPressure[i], - i, - ) - pressureRatio = Insert( - pressureRatio, - pressureRatio[i], - i, - ) - massFlowRateAir = Insert( - massFlowRateAir, - massFlowRateAir[i], - i, - ) - go updateCompImg() - }), - g.Button("Delete").OnClick(func() { - if numPoints < 2 { - return - } - numPoints-- - speed = Remove(speed, i) - volumetricEfficiency = Remove(volumetricEfficiency, i) - intakeAirTemperature = Remove(intakeAirTemperature, i) - manifoldPressure = Remove(manifoldPressure, i) - pressureRatio = Remove(pressureRatio, i) - massFlowRateAir = Remove(massFlowRateAir, 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)), + wg.Add(1) + go widget.Output( // mass flow + flowChan[i], + bounds[7+4*POINTS+i], + mux.MakeEnv(), + wg, ) } - return widgets } -var compressorTree []g.Widget - -func init() { - 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 split(elements int, space int) []int { + bounds := make([]int, elements) + widths := []int{ + widget.TextSize(WIDEST_LABEL).X, + widget.TextSize("123456").X, } -} - -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, - ) + for i := 0; i < elements && space > 0; i++ { + bounds[i] = min(widths[min(i, len(widths)-1)], space) + space -= bounds[i] } - updatedCompImg <- img + return bounds } -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 splitRows(elements int, space int) []int { + bounds := make([]int, elements) + height := widget.TextSize("1").Y + for i := 0; i < elements && space > 0; i++ { + bounds[i] = min(height, space) + space -= bounds[i] } -} - -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 = KilogramsPerSecond - mfr := massFlowRateAir[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 + return bounds } |