From cb12116e1355d0496a54d821c393b63e41001b0c Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Tue, 27 Jun 2023 11:08:02 +0300 Subject: [PATCH 1/5] internal/stroke: tiny optimization to approxCubeTo Signed-off-by: Egon Elbre --- internal/stroke/stroke.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/stroke/stroke.go b/internal/stroke/stroke.go index daf472d2b..b4325630c 100644 --- a/internal/stroke/stroke.go +++ b/internal/stroke/stroke.go @@ -677,13 +677,14 @@ func SplitCubic(from, ctrl0, ctrl1, to f32.Point, quads []QuadSegment) []QuadSeg if h := hull.Dy(); h > l { l = h } - approxCubeTo(&quads, 0, l*0.001, from, ctrl0, ctrl1, to) + maxDist := l * 0.001 + approxCubeTo(&quads, 0, maxDist*maxDist, from, ctrl0, ctrl1, to) return quads } // approxCubeTo approximates a cubic Bézier by a series of quadratic // curves. -func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0, ctrl1, to f32.Point) int { +func approxCubeTo(quads *[]QuadSegment, splits int, maxDistSq float32, from, ctrl0, ctrl1, to f32.Point) int { // The idea is from // https://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html // where a quadratic approximates a cubic by eliminating its t³ term @@ -722,7 +723,7 @@ func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0 // To save a square root, compare d² with the squared tolerance. v := to.Sub(ctrl1.Mul(3)).Add(ctrl0.Mul(3)).Sub(from) d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36) - if d2 <= maxDist*maxDist { + if d2 <= maxDistSq { *quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to}) return splits } @@ -735,7 +736,7 @@ func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0 c12 := c1.Add(c2.Sub(c1).Mul(t)) c0112 := c01.Add(c12.Sub(c01).Mul(t)) splits++ - splits = approxCubeTo(quads, splits, maxDist, from, c0, c01, c0112) - splits = approxCubeTo(quads, splits, maxDist, c0112, c12, c2, to) + splits = approxCubeTo(quads, splits, maxDistSq, from, c0, c01, c0112) + splits = approxCubeTo(quads, splits, maxDistSq, c0112, c12, c2, to) return splits } From 2176fed2c0477303aab6efdae4c79d09cda785d6 Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Tue, 27 Jun 2023 11:31:54 +0300 Subject: [PATCH 2/5] internal/stroke: add BenchmarkSplitCubic Signed-off-by: Egon Elbre --- internal/stroke/stroke_test.go | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 internal/stroke/stroke_test.go diff --git a/internal/stroke/stroke_test.go b/internal/stroke/stroke_test.go new file mode 100644 index 000000000..b6ffc5479 --- /dev/null +++ b/internal/stroke/stroke_test.go @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package stroke + +import ( + "strconv" + "testing" + + "gioui.org/internal/f32" +) + +func BenchmarkSplitCubic(b *testing.B) { + type scenario struct { + segments int + from, ctrl0, ctrl1, to f32.Point + } + + scenarios := []scenario{ + { + segments: 4, + from: f32.Pt(0, 0), + ctrl0: f32.Pt(10, 10), + ctrl1: f32.Pt(10, 10), + to: f32.Pt(20, 0), + }, + { + segments: 8, + from: f32.Pt(-145.90305, 703.21277), + ctrl0: f32.Pt(-940.20215, 606.05994), + ctrl1: f32.Pt(74.58341, 405.815), + to: f32.Pt(104.35474, -241.543), + }, + { + segments: 16, + from: f32.Pt(770.35626, 639.77765), + ctrl0: f32.Pt(735.57135, 545.07837), + ctrl1: f32.Pt(286.7138, 853.7052), + to: f32.Pt(286.7138, 890.5413), + }, + { + segments: 33, + from: f32.Pt(0, 0), + ctrl0: f32.Pt(0, 0), + ctrl1: f32.Pt(100, 100), + to: f32.Pt(100, 100), + }, + } + + for _, s := range scenarios { + s := s + b.Run(strconv.Itoa(s.segments), func(b *testing.B) { + from, ctrl0, ctrl1, to := s.from, s.ctrl0, s.ctrl1, s.to + quads := make([]QuadSegment, s.segments) + b.ResetTimer() + for i := 0; i < b.N; i++ { + quads = SplitCubic(from, ctrl0, ctrl1, to, quads[:0]) + } + if len(quads) != s.segments { + // this is just for checking that we are benchmarking similar splits + // when splitting algorithm splits differently, then it's fine to adjust the + // parameters to give appropriate number of segments. + b.Fatalf("expected %d but got %d", s.segments, len(quads)) + } + }) + } +} From 47384fddc3657f3c361c804e438a9769f309a522 Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Tue, 27 Jun 2023 11:44:26 +0300 Subject: [PATCH 3/5] internal/stroke: optimize SplitCubic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit │ sec/op │ sec/op vs base │ SplitCubic/4-10 37.36n ± 0% 36.16n ± 0% -3.21% (p=0.000 n=10) SplitCubic/8-10 74.53n ± 0% 72.21n ± 0% -3.12% (p=0.000 n=10) SplitCubic/16-10 149.3n ± 1% 144.5n ± 0% -3.22% (p=0.000 n=10) SplitCubic/33-10 340.1n ± 0% 334.4n ± 0% -1.65% (p=0.000 n=10) Signed-off-by: Egon Elbre --- internal/stroke/stroke.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/stroke/stroke.go b/internal/stroke/stroke.go index b4325630c..437528989 100644 --- a/internal/stroke/stroke.go +++ b/internal/stroke/stroke.go @@ -709,7 +709,11 @@ func approxCubeTo(quads *[]QuadSegment, splits int, maxDistSq float32, from, ctr // and use the midpoint between the two curves Q1 and Q2 as control point: // // C = (3ctrl0 - pen + 3ctrl1 - to)/4 - c := ctrl0.Mul(3).Sub(from).Add(ctrl1.Mul(3)).Sub(to).Mul(1.0 / 4.0) + // using, q0 := 3ctrl0 - pen, q1 := 3ctrl1 - to + // C = (q0 + q1)/4 + q0 := ctrl0.Mul(3).Sub(from) + q1 := ctrl1.Mul(3).Sub(to) + c := q0.Add(q1).Mul(1.0 / 4.0) const maxSplits = 32 if splits >= maxSplits { *quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to}) @@ -718,10 +722,12 @@ func approxCubeTo(quads *[]QuadSegment, splits int, maxDistSq float32, from, ctr // The maximum distance between the cubic P and its approximation Q given t // can be shown to be // - // d = sqrt(3)/36*|to - 3ctrl1 + 3ctrl0 - pen| + // d = sqrt(3)/36 * |to - 3ctrl1 + 3ctrl0 - pen| + // reusing, q0 := 3ctrl0 - pen, q1 := 3ctrl1 - to + // d = sqrt(3)/36 * |-q1 + q0| // // To save a square root, compare d² with the squared tolerance. - v := to.Sub(ctrl1.Mul(3)).Add(ctrl0.Mul(3)).Sub(from) + v := q0.Sub(q1) d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36) if d2 <= maxDistSq { *quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to}) From ef78b707a0dc3ee6ae3e0f8a498fc1dfea6c0d24 Mon Sep 17 00:00:00 2001 From: inkeliz Date: Tue, 4 Jul 2023 16:14:13 +0100 Subject: [PATCH 4/5] app: [js] fix tabbing Signed-off-by: inkeliz --- app/os_js.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/os_js.go b/app/os_js.go index c540b44b2..5f3521cc4 100644 --- a/app/os_js.go +++ b/app/os_js.go @@ -373,6 +373,10 @@ func (w *window) keyEvent(e js.Value, ks key.State) { State: ks, } w.w.Event(cmd) + + if cmd.Name == key.NameTab { + e.Call("preventDefault") + } } } From 1934b4f5f2d16bcd837f05e85133e8aff8b72d23 Mon Sep 17 00:00:00 2001 From: inkeliz Date: Tue, 4 Jul 2023 16:15:12 +0100 Subject: [PATCH 5/5] widget: request software-keyboard on focus Signed-off-by: inkeliz --- widget/editor.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/widget/editor.go b/widget/editor.go index 69820e8d1..0669ff5b3 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -319,6 +319,9 @@ func (e *Editor) processKey(gtx layout.Context) { switch ke := ke.(type) { case key.FocusEvent: e.focused = ke.Focus + if e.focused { + key.SoftKeyboardOp{Show: true}.Add(gtx.Ops) + } // Reset IME state. e.ime.imeState = imeState{} case key.Event: