Drawing zig zags makes GUI slow?

For me this makes the GUI unusably slow to resize:

(
v = UserView(nil, Rect(0, 0, 1000, 500)).front.drawFunc = { |view|
  var height = view.bounds.height;
  var width = view.bounds.width;
  var n = 200;
  var nratio = width / n;
  Pen.use {
    n.do { |i|
      var y = if (i.even) { 0 } { height };
      Pen.lineTo((i * nratio)@y);
    };
    Pen.stroke;
  };
};
)

also this:

(200.collect{ |i| if (i.even) { 0 } { 1 } }).plot

Using an EnvelopeView is fast but also not accurate, it draws a sawtooth instead of triangle wave:

EnvelopeView().setEnv(Env(200.collect{ |i| if (i.even) { 0 } { 1 } }, 1.dup(199))).thumbSize_(0).front

Is there any better way?

Yes, I can confirm the same behavior on my system - extremely slow. Not that I can offer any solutions…

Yes, they are slow here too. The Pen version is by far the slowest.

Wierdly, the EnvelopeView version seems to work fine if you reduce the number of points to 100. This shows triangles:

EnvelopeView().setEnv(Env(100.collect{ |i| if (i.even) { 0 } { 1 } }, 1.dup(99))).thumbSize_(0).front

However, 150 points looks very wonky - a mix of up/down ramps and triangles:

EnvelopeView().setEnv(Env(150.collect{ |i| if (i.even) { 0 } { 1 } }, 1.dup(149))).thumbSize_(0).front

Interestingly, the performance on my machine (Mac M1) is worse on SC 3.12.2 vs. 3.13/14-dev.
3.12.2: beach ball for 10 seconds
3.13/14-dev: a couple of annoying seconds of pause.

Is this just a limitation of Qt? Or is there a way to fix this? If anyone has ideas to try I’d be happy to give it a go to the best of my abilities

You’d need to run this through a profiler to see where the bottle neck is. Without this, we can only speculate.

That being said, Qt widgets (which is the gui system used in sc) doesn’t support GPU rendering, and is very slow when drawing lots of things. It not really made heavy graphics use so there is a limit you cannot improve upon.

It might be worth trying to improve envelope view instead.

And this one?


(
v = UserView(nil, Rect(0, 0, 1000, 500)).front;
v.drawFunc = { |view|
    var bounds = view.bounds;
    var n = 50; 
    var step = bounds.width / n;
    
    Pen.use {
        Pen.moveTo(0@0);
        n.do { |i|
            var x = i * step;
            var y = if(i.odd, bounds.height, 0);
            Pen.lineTo(x@y);
        };
        Pen.stroke;
    };
};

)

It’s not a complex GUI at all… is that the limit now? There is a bug somewhere. If not, the GUI should not redraw everything until the user reaches the new size.

Just trying out some options,

One possible solution is to use Image

(
// "resolution" = pixels per second
~imageFromEnv = { |env, resolution = 30, height = 500, startTime = 0, duration|
  var width, image;
  duration = duration ?? env.duration;
  width = (duration * resolution).asInteger;
  image = Image(width@height);
  width.asInteger.do { |x|
    var x2time = { |x| x / width * duration + startTime };
    var val2y = { |val| ((1 - val) * (height - 1)).asInteger };
    var time = x2time.(x);
    var nextTime = x2time.(x + 1);
    var val = env[time];
    var nextVal = env[nextTime];
    var y = val2y.(val);
    var nextY = val2y.(nextVal);
    var thisTop = min(y, nextY);
    var thisHeight = max(1, abs(y - nextY));
    image.setPixels(Int32Array.fill(thisHeight, {Image.colorToPixel(Color.black)}), Rect(x, thisTop, 1, thisHeight));
  };
  image;
};
)

~imageFromEnv.(Env(50.collect{ |i| if (i.even) { 0 } { 1 } }, 1.dup(99)), 30).plot(freeOnClose: true);

this is fast, of course not perfect for many reasons including aliasing if envelope changes faster than resolution:

~imageFromEnv.(Env(50.collect{ |i| if (i.even) { 0 } { 1 } }, 0.1.dup(99)), 30).plot(freeOnClose: true);