Tworzenie siatki w NSView

Obecnie mam NSView, który rysuje wzór siatki (zasadniczo przewodnik linii poziomych i pionowych) z myślą o tym, że użytkownik może zmienić odstępy siatki i kolor siatki.

Celem siatki jest działanie jako wytyczne dla użytkownika podczas ustawiania obiektów w kolejce. Wszystko działa dobrze z jednym wyjątkiem. Kiedy zmieniam rozmiar NSWindow przeciągając uchwyt zmiany rozmiaru, jeśli odstępy między siatkami są szczególnie małe (powiedzmy 10 pikseli). przeciągnij Zmień rozmiar staje się ospały w przyrodzie.

Mój drawRect kod dla siatki jest następujący:

-(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];
    }
}

Podejrzewam, że jest to całkowicie związane ze sposobem, w jaki rysuję siatkę i jestem otwarty na sugestie, jak lepiej to zrobić.

Widzę, gdzie pojawia się nieefektywność, drag-zmiana rozmiaru NSWindow ciągle wywołuje drawRect w tym widoku, gdy zmienia rozmiar, a im bliżej siatki, tym więcej obliczeń na przeciągnięcie piksela okna nadrzędnego.

Myślałem o ukryciu widoku przy zmianie rozmiaru okna, ale nie wydaje się tak dynamiczne. Chcę, aby doświadczenie użytkownika było bardzo płynne, bez zauważalnego opóźnienia lub migotania.

Czy ktoś ma jakieś pomysły na lepszą lub skuteczniejszą metodę rysowania siatki?

Wszelka pomoc, jak zawsze, bardzo mile widziana.
Author: Hooligancat, 2010-04-27

3 answers

Nieumyślnie wprowadziłeś Schlemiela do swojego algorytmu. Za każdym razem, gdy wywołujesz moveToPoint i lineToPoint W pętli, w rzeczywistości dodajesz więcej linii do tej samej ścieżki, wszystkie będą rysowane za każdym razem, gdy wywołasz stroke na tej ścieżce.

Oznacza to, że rysujesz jedną linię za pierwszym razem, dwie linie za drugim razem, trzy linie za trzecim razem itd...

Szybką poprawką byłoby użycie nowej ścieżki za każdym razem przez loop po prostu wykonaj stroke po pętla (z podziękowaniami dla Jasona Coco za pomysł):

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

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

Aktualizacja: innym podejściem byłoby unikanie tworzenia NSBezierPath w ogóle, i po prostu użyj strokeLineFromPoint: toPoint: metoda klasy:

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

Aktualizacja #2: zrobiłem kilka podstawowych benchmarking na podejściach do tej pory. Używam okna o wymiarach 800x600 pikseli, rozstaw siatki 10 pikseli i mam cocoa przerysuj okno tysiąc razy, skalując z 800x600 do 900x700 i z powrotem. Działa na moim MacBooku 2GHz Core Duo Intel, widzę następujące czasy:

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  

Oznacza to, że spowolnienie zostało całkowicie spowodowane powtórzeniem, a każda z kilku mikro-ulepszeń robi bardzo niewiele, aby przyspieszyć działanie. Nie powinno to być zaskoczeniem, ponieważ rzeczywiste rysowanie pikseli na ekranie jest (prawie zawsze) znacznie bardziej wymagające od procesora niż proste pętle i matematyczne szef.

Lekcje do nauczenia się:

  1. Hidden Schlemiels can really spowalniać rzeczy.
  2. zawsze profiluj swój kod przed niepotrzebną optymalizacją
 12
Author: e.James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-27 00:39:26

Należy uruchomić Instruments Cpu Sampler, aby określić, gdzie większość czasu jest spędzany, a następnie zoptymalizować na podstawie tych informacji. Jeśli to pociągnięcie, wyłóż go poza pętlę. Jeśli rysuje ścieżkę, spróbuj przenieść renderowanie do procesora gpu. Sprawdź, czy CALayer może pomóc.

 0
Author: lucius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-26 23:45:56

Może za późno na przyjęcie, jednak ktoś może znaleźć to pomocne. Ostatnio potrzebowałem niestandardowych komponentów dla klienta, aby odtworzyć nakładkę z możliwością zmiany rozmiaru siatki UIView. Poniżej należy do pracy, bez problemów, nawet przy bardzo małych wymiarach.

Kod jest przeznaczony dla iPhone ' a (UIView), ale można go bardzo szybko przenieść do 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);
    }

}
 0
Author: valvoline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-07 18:56:47