2016-04-17 14 views
7

Robiłem trochę pracy z Androidem Canvas, szczególnie próbując ustalić, jak ustalane są jego wyniki getClipBounds. Rozumiem, że Canvas wewnętrznie przechowuje transformację Matrix, która jest aktualizowana podczas wywoływania translate, scale itd., Ale próba odtworzenia wyników Matrix mnie zdziwiła.Jak Canvas określa granice klipu?

@Override 
public void onDraw(Canvas canvas) { 
    Rect clipBounds; 
    RectF viewport; 

    canvas.save(); 
    canvas.concat(translationMatrix); 
    //viewportMatrix.preConcat(canvas.getMatrix()); 
    viewportMatrix.set(canvas.getMatrix()); 

    clipBounds = canvas.getClipBounds(); 
    viewport = GetViewport(); 

    Log.d("clipBounds", clipBounds.toString() + " (" + clipBounds.width() + ", " + clipBounds.height() + ")"); 
    Log.d("viewport", viewport.toString() + " (" + viewport.width() + ", " + viewport.height() + ")"); 

    //drawing is done here 

    canvas.restore(); 
} 

//viewport code modeled after http://stackoverflow.com/a/17142856/2245528 
private RectF GetViewport() { 
    RectF viewport = new RectF(); 

    viewportMatrix.mapRect(viewport, originalViewport); 

    return viewport; 
} 

private void Translate(float x, float y) { 
    translationMatrix.postTranslate(x, y); 

    invalidate(); 
} 

private void Scale(float scaleFactor, PointF focusPoint) { 
    if (focusPoint == null) { 
     translationMatrix.postScale(scaleFactor, scaleFactor); 
    } 
    //keep the focus point in focus if possible 
    else { 
     translationMatrix.postScale(scaleFactor, scaleFactor, focusPoint.x, focusPoint.y); 
    } 

    invalidate(); 
} 

private final Matrix translationMatrix = new Matrix(); 
private final RectF originalViewport = new RectF(); 
private final Matrix viewportMatrix = new Matrix(); 

originalViewport jest ustawiony na 0, 0, canvas.getWidth(), canvas.getHeight(). Translate i Scale są wywoływane z procedur obsługi zdarzeń gestów, które działają poprawnie.

Częścią, która mnie myli, jest viewportMatrix. Nie ma to znaczenia, czy robię

viewportMatrix.set(canvas.getMatrix()); 

lub

viewportMatrix.preConcat(canvas.getMatrix()); 

lub nawet wezwanie jednorazowa

viewportMatrix.set(canvas.getMatrix()); 

na początku następnie side-by-side Translate/Scale połączenia z dwiema macierzami. Ja nawet nie próbował całkowicie ignorując „s Canvas wbudowane Matrix i przepisywanie GetViewport jak

//viewport code modeled after http://stackoverflow.com/a/17142856/2245528 
private RectF GetViewport() { 
    RectF viewport = new RectF(); 

    translationMatrix.mapRect(viewport, originalViewport); 

    return viewport; 
} 

nigdy nie wydają się dopasować getClipBounds(), a rozbieżności są dość poważne:

z viewportMatrix.set(canvas.getMatrix):

clipBounds: Rect (-97, -97 - 602, 452) (699, 549)
rzutni: RectF (97,04178, 97,06036, 797,04175 647. 06036) (700,0, 550,0)

z :

clipBounds: Rect (-97, -96 - 602, 453) (699, 549)
wziernika: RectF (2708,9663, 2722.2754, 3408.9663, 3272.2754) (700,0, 550,0)

z translationMatrix.mapRect:

clipBounds: Rect (-96, -96 - 603, 453) (699, 549)
wziernika: RectF (96,73213, 96,85794, 796,7321, 646,8579) (700.0, 550.0)

z jednego -shot wywołanie viewportMatrix.preConcat(canvas.getMatrix()) następnie side-by-side Translate/Scale połączeń:

clipBounds: rect (-96, -97 - 603, 452) (699, 549)
wziernika: RectF (96,57738, 97,78168, 796,5774, 647,7817) (700,0, 550.0)

z rozmowy jeden strzał viewportMatrix.set(canvas.getMatrix()) następnie side-by-side Translate/Scale połączeń:

clipBounds: Rect (-96, -96 - 603, 453) (699 , 549)
rzutnia: RectF (96,40051, 96,88153, 796,4005, 646,88153) (700,0, 550,0)

nie mogę nawet sprawdzić the Canvas source, jak cały kod Matrix znika w prywatnej nat ive połączeń, których kod nie jest wyświetlany.

Dlaczego moje wywołania GetViewport są tak brutalne, a co dzieje się za kulisami z getClipBounds?

Odpowiedz

1

czytam przez this answer on GameDev SE, który wykorzystuje inwersji macierzy można przełączać się między ekranem a światem układów współrzędnych:

Aby przejść z ekranu na przestrzeni światowej prostu użyć Vector2.Transform. Jest to powszechnie używane, aby uzyskać lokalizację myszy na świecie do wybierania obiektów.

Vector2.Transform(mouseLocation, Matrix.Invert(Camera.TransformMatrix)); 

Aby przejść od świata do ekranu, po prostu odwrotnie.

Vector2.Transform(mouseLocation, Camera.TransformMatrix); 

Zainspirowany tym podejściem, próbowałem odwracanie macierzy transformacji tak:

private RectF GetViewport() { 
    RectF viewport = new RectF(); 
    Matrix viewportMatrix = new Matrix(); 

    translationMatrix.invert(viewportMatrix); 

    viewportMatrix.mapRect(viewport, originalViewport); 

    return viewport; 
} 

Ten rzutnia poprawnie dopasowuje wyniki zwracane przez getClipBounds.

This matrix tutorial wyjaśnia, co bym zauważył z wynikami transform połączeń, ale nie stosuje się do mojej matrycy:

w programie komputerowym kamera nie porusza w ogóle, aw rzeczywistości, Świat po prostu porusza się w przeciwnym kierunku: kierunek i orientacja, jak chcesz, aby kamera poruszała się w rzeczywistości.

Aby zrozumieć poprawnie, musimy myśleć w kategoriach dwóch różnych rzeczy:

  1. Transformacja Camera Matrix: Transformacja że umieszcza aparat w prawidłowej pozycji i orientacji w przestrzeni światowej (to jest transformacja, którą można zastosować do modelu 3D kamery, jeśli chcesz ją reprezentować w scenie).
  2. Macierz widokowa: Ta macierz przekształci wierzchołki z przestrzeni kosmicznej na widokową. Ta macierz jest odwrotnością opisanej powyżej macierzy transformacji kamery.