6

私は現在NSView、ユーザーがグリッドの間隔とグリッドの色を変更できるという考えで、グリッドパターン(基本的には水平線と垂直線のガイド)を描画するを持っています。

グリッドの目的は、オブジェクトを並べるときにユーザーのガイドラインとして機能することです。1つの例外を除いて、すべてが正常に機能します。NSWindowグリッド間隔が特に小さい場合(たとえば10ピクセル)、サイズ変更ハンドルをドラッグしてサイズを変更します。ドラッグのサイズ変更は本質的に無気力になります。

グリッドの私のdrawRectコードは次のとおりです。

-(void)drawRect:(NSRect)dirtyRect {

    NSRect thisViewSize = [self bounds];

    // Set the line color

    [[NSColor colorWithDeviceRed:0 
                           green:(255/255.0) 
                            blue:(255/255.0) 
                           alpha:1] set];

    // Draw the vertical lines first

    NSBezierPath * verticalLinePath = [NSBezierPath bezierPath];

    int gridWidth = thisViewSize.size.width;
    int gridHeight = thisViewSize.size.height;

    int i;

    while (i < gridWidth)
    {
        i = i + [self currentSpacing];

        NSPoint startPoint = {i,0};
        NSPoint endPoint = {i, gridHeight};

        [verticalLinePath setLineWidth:1];
        [verticalLinePath moveToPoint:startPoint];
        [verticalLinePath lineToPoint:endPoint];
        [verticalLinePath stroke];
    }

    // Draw the horizontal lines

    NSBezierPath * horizontalLinePath = [NSBezierPath bezierPath];

    i = 0;

    while (i < gridHeight)
    {
        i = i + [self currentSpacing];

        NSPoint startPoint = {0,i};
        NSPoint endPoint = {gridWidth, i};

        [horizontalLinePath setLineWidth:1];
        [horizontalLinePath moveToPoint:startPoint];
        [horizontalLinePath lineToPoint:endPoint];

        [horizontalLinePath stroke];
    }
}

これは、グリッドを描画する方法と完全に関係していると思います。また、グリッドをどのように処理するかについての提案を受け入れています。

非効率性がどこにあるのかがわかります。ドラッグサイズ変更は、サイズ変更時にこのビューでNSWindow常に呼び出してdrawRectいます。グリッドが近いほど、親ウィンドウのピクセルあたりのドラッグ数が多くなります。

ウィンドウのサイズ変更時にビューを非表示にすることを考えていましたが、動的ではありません。遅延やちらつきが感じられないように、ユーザーエクスペリエンスを非常にスムーズにしたいと考えています。

グリッドを描画するためのより良いまたはより効率的な方法について誰かが何かアイデアを持っていますか?

いつものように、すべての助けは非常にありがたいです。

4

3 に答える 3

13

誤ってSchlemielをアルゴリズムに導入しました。呼び出すたびmoveToPointlineToPoint、ループ内で、実際には同じパスにさらに線を追加します。これらの線はすべて、そのパスを呼び出すたびに描画されstrokeます。

これは、最初に1本の線を、2回目に2本の線を、3回目に3本の線を描画することを意味します。

簡単な解決策は、ループを通過するたびに新しいパスを使用して、ループのstroke に実行することです(Jason Cocoのアイデアに感謝します)。

path = [NSBezierPath path];
while (...)
{
    ...

    [path setLineWidth:1];
    [path moveToPoint:startPoint];  
    [path lineToPoint:endPoint];
}
[path stroke];

更新:NSBezierPath別のアプローチは、それを完全に作成することを避け、 strokeLineFromPoint:toPoint:クラスメソッドを使用することです:

[NSBezierPath setDefaultLineWidth:1];
while (...)
{
    ...
    [NSBezierPath strokeLineFromPoint:startPoint toPoint:endPoint];
}

更新#2:これまでのアプローチでいくつかの基本的なベンチマークを行いました。私は800x600ピクセルのサイズのウィンドウ、10ピクセルのグリッド間隔を使用しており、ココアにウィンドウを1000回再描画させ、800x600から900x700にスケーリングし、また元に戻しています。2GHz Core Duo Intel MacBookで実行していると、次のように表示されます。

Original method posted in question:  206.53 seconds  
Calling stroke after the loops:       16.68 seconds  
New path each time through the loop:  16.68 seconds  
Using strokeLineFromPoint:toPoint:    16.68 seconds  

これは、速度低下が完全に繰り返しによって引き起こされたこと、およびいくつかのマイクロ改善のいずれも実際に物事をスピードアップするためにほとんど何もしないことを意味します。画面上のピクセルの実際の描画は(ほとんどの場合)単純なループや数学演算よりもはるかにプロセッサを集中的に使用するため、これはそれほど驚くべきことではありません。

学ぶべき教訓:

  1. 隠されたシュレミエルは本当に物事を遅くすることができます。
  2. 不要な最適化を行う前に、常にコードのプロファイルを作成してください
于 2010-04-26T22:38:52.180 に答える
0

Instruments Cpu Samplerを実行して、ほとんどの時間が費やされている場所を特定し、その情報に基づいて最適化する必要があります。ストロークの場合は、ループの外側に配置します。パスを描画している場合は、レンダリングをGPUにオフロードしてみてください。CALayerが役立つかどうかを確認してください。

于 2010-04-26T23:45:56.533 に答える
0

パーティーに遅れるかもしれませんが、誰かがこれが役立つと思うかもしれません。最近、グリッドのサイズ変更可能なオーバーレイUIViewを再作成するために、顧客用のカスタムコンポーネントが必要でした。以下は、ごくわずかな寸法でも問題なく動作するはずです。

コードはiPhone(UIView)用ですが、NSViewに非常にすばやく移植できます。

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, rect);
    CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);

    //corners
    CGContextSetLineWidth(context, 5.0);
    CGContextMoveToPoint(context, 0, 0);
    CGContextAddLineToPoint(context, 15, 0);
    CGContextMoveToPoint(context, 0, 0);
    CGContextAddLineToPoint(context, 0, 15);
    CGContextMoveToPoint(context, rect.size.width, 0);
    CGContextAddLineToPoint(context, rect.size.width-15, 0);
    CGContextMoveToPoint(context, rect.size.width, 0);
    CGContextAddLineToPoint(context, rect.size.width, 15);
    CGContextMoveToPoint(context, 0, rect.size.height);
    CGContextAddLineToPoint(context, 15, rect.size.height);
    CGContextMoveToPoint(context, 0, rect.size.height);
    CGContextAddLineToPoint(context, 0, rect.size.height-15);
    CGContextMoveToPoint(context, rect.size.width, rect.size.height);
    CGContextAddLineToPoint(context, rect.size.width-15, rect.size.height);
    CGContextMoveToPoint(context, rect.size.width, rect.size.height);
    CGContextAddLineToPoint(context, rect.size.width, rect.size.height-15);
    CGContextStrokePath(context);


    //border
    CGFloat correctRatio = 2.0;
    CGContextSetLineWidth(context, correctRatio);
    CGContextAddRect(context, rect);
    CGContextStrokePath(context);

    //grid
    CGContextSetLineWidth(context, 0.5);
    for (int i=0; i<4; i++) {
        //vertical
        CGPoint aPoint = CGPointMake(i*(rect.size.width/4), 0.0);
        CGContextMoveToPoint(context, aPoint.x, aPoint.y);
        CGContextAddLineToPoint(context,aPoint.x, rect.size.height);
        CGContextStrokePath(context);

        //horizontal
        aPoint = CGPointMake(0.0, i*(rect.size.height/4));
        CGContextMoveToPoint(context, aPoint.x, aPoint.y);
        CGContextAddLineToPoint(context,rect.size.width, aPoint.y);
        CGContextStrokePath(context);
    }

}
于 2016-01-07T18:56:47.780 に答える