wtorek, 31 marca 2009

Napisy płaskie (WPF 2D) (przykłady w języku C#)

To podejście jest bardziej skomplikowane od poprzedniego, dlatego trudno przygotować funkcję, która jak to miało miejsce w poprzednim przypadku wykonywała wszystkie czynności.

Pierwszym krokiem jest stworzenie dwóch nakładających się warstw:

  • Viewport3D jako warstwy spodniej
  • Canvas jako przeźroczystej warstwy nałożonej
Kod XAML realizujący dwie nakładające się warstwy może wyglądać następująco:

<Grid ClipToBounds="True">

<Viewport3D Name="mainViewport" ClipToBounds="True" Grid.Column="0" Grid.Row="0">

<Viewport3D.Camera>

<PerspectiveCamera

FarPlaneDistance="100"

LookDirection="-11,-10,-9"

UpDirection="0,1,0"

NearPlaneDistance="1"

Position="11,10,9"

FieldOfView="70" />

Viewport3D.Camera>

<ModelVisual3D>

<ModelVisual3D.Content>

<DirectionalLight

Color="White"

Direction="-2,-3,-1" />

</ModelVisual3D.Content>

</ModelVisual3D>

</Viewport3D>

<Canvas Name="mainViewportCanvas" ClipToBounds="True" Grid.Column="0" Grid.Row="0"></Canvas>

</Grid>

Kolejnym zadaniem jest lokalizacja punktu ze sceny 3D na nałożonej warstwie typu Canvas. Do transformacji lokalizacji można użyć funkcję podobną do poniższej:

public static Point Get2DPoint(Point3D p3d, Viewport3D vp)

{

bool TransformationResultOK;

Viewport3DVisual vp3Dv = VisualTreeHelper.GetParent(

vp.Children[0]) as Viewport3DVisual;

Matrix3D m = MathUtils.TryWorldToViewportTransform(vp3Dv, out TransformationResultOK);

if (!TransformationResultOK) return new Point(0, 0);

Point3D pb = m.Transform(p3d);

Point p2d = new Point(pb.X, pb.Y);

return p2d;

}

Zauważmy, że ta funkcja używa funkcji TryWorldToViewportTransform z pakietu 3D Tools (dostępnego tutaj): _3DTools.MathUtils.TryWorldToViewportTransform.

Kolejnym krokiem jest utworzenie i lokalizacja obiektu TextBlock:

UIElement IModelVisual3D.GetUIElement(ModelVisual3DFilter FilterSettings, Viewport3D DestinationViewport3D)

{

if (FilterSettings.Texts2D)

{

TextBlock tb = new TextBlock();

tb.Text = Description;

Point p2d = Panel3DMath.Get2DPoint(this.Point3D, DestinationViewport3D);

Canvas.SetTop(tb, p2d.Y);

Canvas.SetLeft(tb, p2d.X);

return tb;

}

else

return new UIElement();

}

Ostatecznie dodajemy TextBlock (utworzonego w poprzednim kroku) do naszego obiektu canvas.

this.mainViewportCanvas.Children.Add(element.GetUIElement(filter,mainViewport));

(To tylko jedna z części artykułu, przeczytaj pozostałe: 1, 2, 3, 4, 5, 6, 7)

poniedziałek, 30 marca 2009

Zapraszam na AUTOMATICON 2009

Już niedługo 01 kwietnia 2009 (w Prima aprilis ;) ) kolejne, XV Międzynarodowe Targi Automatyki i Pomiarów AUTOMATICON®09. Chciałbym wszystkich moich czytelników zaprosić na prezentację (którą współtworzyłem), która będzie miała miejsce w godz. 11.00 - 11.45, sala B-1.

Prezentacja poświęcona jest rodzinie oprogramowania CommServer, która pozwala na realizację komunikacji między różnorodnymi systemami przy kosztach nieporównywalnie niższych od potencjalnych zysków. Minimalizacja kosztów jest wynikiem zastosowania międzynarodowych standardów komunikacyjnych i rozwiązań niewymagających programowania. Oprogramowanie posiada certyfikaty zgodności dla standardu OPC i było testowane pod nadzorem organizacji OPC Foundation z kilkudziesięcioma produktami najbardziej liczących się w branży producentów. Pozwala na realizację integracji we własnym zakresie i - w efekcie - na zbudowanie systemu otwartego. Jako przykład użycia pokazano zastosowanie integracji systemów telemetrii do prezentacji aktualnej produkcji w systemie GIS i ERP. Innym przykładem jest zastosowanie oprogramowania do integracji systemów automatyki w celu uzyskania dodatkowych korzyści w produkcji z efektu synergii w ciepłownictwie, gdzie grupa elektrociepłowni uzyskuje dodatkowe korzyści w procesie produkcji energii elektrycznej dzięki zsynchronizowanemu prowadzeniu procesu dystrybucji ciepła.

Praktyka pokazuje, że dzięki integracji systemów zarządzania przedsiębiorstwem (MES, SAP, ERP, GIS, itd.) z systemami sterowania i monitorowania produkcji (PLC, SCADA, HMI) można uzyskać dodatkowe zyski dzięki optymalizacji i uzyskanemu efektowi synergii. Warunkiem integracji jest umożliwienie wprowadzenia danych czasu rzeczywistego do systemów zarządzania i na odwrót.

1 kwietnia 2009, godz. 11.00 - 11.45, sala B-1, Warszawskie Centrum EXPO XXI

Szczegóły dotyczące targów http://www.automaticon.pl/

Lokalizacja http://www.automaticon.pl/lokalizacja.php

Zaproszenie

Napisy przestrzenne (WPF 3D), a ich wektory położenia (przykłady w języku C#)

Chciałbym zwrócić uwagę na jeszcze jedną sprawę. Istotne są też wektory które wyznaczają płaszczyznę na której umieszczany jest napis (wektory vectorUp i vectorOver w przedstawionym wcześniej przykładzie).Jeżeli napis umieszczany jest na pewnym obiekcie należącym do sceny, wtedy wyznaczenie odpowiednich wektorów jest proste (ew. może się okazać konieczność ich normalizacji). Trudniejsze jest natomiast dobranie odpowiednich wektorów, gdy napisy mają służyć do opisu sceny i mają się pojawiać na powierzchni wyznaczanej przez płaszczyznę która jest prostopadła do kierunku patrzenia. W tym przypadku należy wybrać takie wektory, które będą wzajemnie do siebie prostopadłe oraz prostopadłe do kierunku patrzenia. Do wyznaczenia takich wektorów należy użyć własności iloczynu skalarnego, którego wartość wynosi 0 jeżeli wektory są prostopadłe.

Funkcja wyznaczająca takie wektory może mieć postać:

void UpdateViewToLookDirection(Vector3D LookDirection)

{

Vector3D look = LookDirection;

look.Normalize();

TextVectorOver = new Vector3D(-look.Y, look.X, 0);

TextVectorUp = new Vector3D(-look.Z * look.X, -look.Z * look.Y, look.Y * look.Y + look.X * look.X);

TextVectorOver.Normalize();

TextVectorUp.Normalize();

}

(przedstawione tutaj powyżej rozwiązanie nie jest może optymalne, ale na pewno poprawia czytelność napisów, nie będąc również skomplikowane w implementacji - jeśli ktoś ma lepszy pomysł chętnie poznam)

Funkcję tą należy wywołać każdorazowo przy zmianie kierunku patrzenia. Kierunek patrzenia można odczytać z używanego Viewport'u 3D:

PerspectiveCamera camera = (PerspectiveCamera)mainViewport.Camera;

Vector3D newlookdirection = Panel3DMath.Transform3DVector(camera.Transform,camera.LookDirection);

W przykładowym kodzie wyżej pokazany jest jeszcze sposób transformowania wektora patrzenia, w oparciu o transformatę wykorzystaną aktualnie do obracania kamery. Funkcja Transform3DVector powinna mieć natomiast postać:

public static Vector3D Transform3DVector(Transform3D transform, Vector3D vector)

{

Point3D input = new Point3D(vector.X, vector.Y, vector.Z);

Point3D output;

if (transform.TryTransform(input, out output))

{

return new Vector3D(output.X, output.Y, output.Z);

}

return vector;

}

(To tylko jedna z części artykułu, przeczytaj pozostałe: 1, 2, 3, 4, 5, 6, 7)

piątek, 27 marca 2009

Napisy przestrzenne (WPF 3D) (przykłady w języku C#)

Aby zobaczyć jak tworzyć etykiety 3D, zobaczymy jak działa funkcja, która to realizuje: CreateTextLabel3D. Ta funkcja jest odpowiedzialna za wytworzenie obiektu typu ModelVisual3D , zawierającego etykiety 3D. Funkcja ta będzie miała następującą deklarację:

public static ModelVisual3D CreateTextLabel3D( string text, Brush textColor, bool isDoubleSided, double height, Point3D basePoint, bool isBasePointCenterPoint, Vector3D vectorOver, Vector3D vectorUp);

Parametry wywołania:

  • text - tekst który chcemy narysować
  • textColor - kolor tekstu
  • isDoubleSided - czy ma być widoczny z dwóch stron?
  • height - wysokość znaków
  • basePoint - punkt bazowy etykiety
  • isBasePointCenterPoint - jeśli ustawione na true punkt bazowy jest punktem centralnym etykiety
  • vectorOver - wektor poziomy napisu
  • vectorUp - wektor pionowy napisu

Funkcja zwraca: element dodawany do Viewport3D
Uwaga: Dwa wektory: vectorOver i vectorUp tworzą powierzchnię, na której rysowany jest tekst. obydwa wektory używane są do wyliczeń wielkości etykiety więc najlepiej, by każda ich współrzędna była 0 lub 1. np. [1,1,0] lub [1,0,1], etc...

Jak ona działa?

  1. W pierwszym kroku tworzymy TextBlock , który zawiera naszą etykietę.

    TextBlock textblock = new TextBlock(new Run(text));

    textblock.Foreground = textColor; // ustawianie koloru

    textblock.FontFamily = new FontFamily("Arial"); // ustawianie czcionki

  2. W drugim kroku, tworzymy pędzel i materiał TextBlock , który zawiera stworzony w poprzednim kroku.

    DiffuseMaterial mataterialWithLabel = new DiffuseMaterial();

    // Allows the application of a 2-D brush,

    // like a SolidColorBrush or TileBrush, to a diffusely-lit 3-D model.

    // we are creating the brush from the TextBlock

    mataterialWithLabel.Brush = new VisualBrush(textblock);

  3. Teraz czas jest na przygotowanie objektu 3D, który zostanie pokryty tekstem

    //calculation of text width (assumming that characters are square):

    double width = text.Length * height;

    // we need to find the four corners

    // p0: the lower left corner; p1: the upper left

    // p2: the lower right; p3: the upper right

    Point3D p0 = basePoint;

    // when the base point is the center point we have to set it up in different way

    if(isBasePointCenterPoint)

    p0 = basePoint - width / 2 * vectorOver - height / 2 * vectorUp;

    Point3D p1 = p0 + vectorUp * 1 * height;

    Point3D p2 = p0 + vectorOver * width;

    Point3D p3 = p0 + vectorUp * 1 * height + vectorOver * width;

    // we are going to create object in 3D now:

    // this object will be painted using the (text) brush created before

    // the object is rectangle made of two triangles (on each side).

    MeshGeometry3D my3D_Restangle = new MeshGeometry3D();

    my3D_Restangle.Positions = new Point3DCollection();

    my3D_Restangle.Positions.Add(p0); // 0

    my3D_Restangle.Positions.Add(p1); // 1

    my3D_Restangle.Positions.Add(p2); // 2

    my3D_Restangle.Positions.Add(p3); // 3

    // when we want to see the text on both sides:

    if (isDoubleSided)

    {

    my3D_Restangle.Positions.Add(p0); // 4

    my3D_Restangle.Positions.Add(p1); // 5

    my3D_Restangle.Positions.Add(p2); // 6

    my3D_Restangle.Positions.Add(p3); // 7

    }

    my3D_Restangle.TriangleIndices.Add(0);

    my3D_Restangle.TriangleIndices.Add(3);

    my3D_Restangle.TriangleIndices.Add(1);

    my3D_Restangle.TriangleIndices.Add(0);

    my3D_Restangle.TriangleIndices.Add(2);

    my3D_Restangle.TriangleIndices.Add(3);

    // when we want to see the text on both sides:

    if (isDoubleSided)

    {

    my3D_Restangle.TriangleIndices.Add(4);

    my3D_Restangle.TriangleIndices.Add(5);

    my3D_Restangle.TriangleIndices.Add(7);

    my3D_Restangle.TriangleIndices.Add(4);

    my3D_Restangle.TriangleIndices.Add(7);

    my3D_Restangle.TriangleIndices.Add(6);

    }

  4. W kolejnym kroku pokrywamy stworzony przed chwilą obiekt 3D używając materiału stworzonego na początku. // texture coordinates must be set to // stretch the TextBox brush to cover // the full side of the 3D label. my3D_Restangle.TextureCoordinates.Add(new Point(0, 1)); my3D_Restangle.TextureCoordinates.Add(new Point(0, 0)); my3D_Restangle.TextureCoordinates.Add(new Point(1, 1)); my3D_Restangle.TextureCoordinates.Add(new Point(1, 0)); // when the label is double sided: if (isDoubleSided) { my3D_Restangle.TextureCoordinates.Add(new Point(1, 1)); my3D_Restangle.TextureCoordinates.Add(new Point(1, 0)); my3D_Restangle.TextureCoordinates.Add(new Point(0, 1)); my3D_Restangle.TextureCoordinates.Add(new Point(0, 0)); }
  5. Teraz jest czas na stworznie obiektu ModelVisual3D z napisem

    ModelVisual3D result = new ModelVisual3D();

    // we are setting the content:

    // our 3D rectangle object covered with materila that is made of label

    // (TextBox with text)

    result.Content = new GeometryModel3D(my3D_Restangle, mataterialWithLabel);

    return result;

(To tylko jedna z części artykułu, przeczytaj pozostałe: 1, 2, 3, 4, 5, 6, 7)

Posty powiązane / Related posts