/*========================================================================= Program: Visualization Toolkit Module: TestSVGContextExport.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkSVGExporter.h" #include "vtkAbstractMapper.h" #include "vtkBrush.h" #include "vtkCellArray.h" #include "vtkContext2D.h" #include "vtkContextItem.h" #include "vtkContextScene.h" #include "vtkContextView.h" #include "vtkFloatArray.h" #include "vtkImageData.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkOpenGLContextDevice2D.h" #include "vtkPen.h" #include "vtkPointData.h" #include "vtkPoints.h" #include "vtkPoints2D.h" #include "vtkPolyData.h" #include "vtkPolyLine.h" #include "vtkRTAnalyticSource.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkSmartPointer.h" #include "vtkTestingInteractor.h" #include "vtkTextProperty.h" #include "vtkTransform2D.h" #include "vtkTriangle.h" #include "vtkUnsignedCharArray.h" #include //------------------------------------------------------------------------------ class ContextSVGTest : public vtkContextItem { void SetSpritePoint(int x, int y, vtkImageData* sprite); public: static ContextSVGTest* New(); vtkTypeMacro(ContextSVGTest, vtkContextItem); // Paint event for the chart, called whenever the chart needs to be drawn bool Paint(vtkContext2D* painter) override; }; void drawPolyLinePolyData(vtkContext2D* painter) { // Setup points vtkSmartPointer points = vtkSmartPointer::New(); points->InsertNextPoint(50.0, 0.0, 0.0); points->InsertNextPoint(0.0, 0.0, 0.0); points->InsertNextPoint(0.0, 50.0, 0.0); points->InsertNextPoint(50.0, 0.0, 0.0); // Define some colors unsigned char black[4] = { 0, 0, 0, 255 }; // Setup the colors array vtkSmartPointer colors = vtkSmartPointer::New(); colors->SetNumberOfComponents(4); colors->SetName("Colors"); // Add the three colors we have created to the array colors->InsertNextTypedTuple(black); colors->InsertNextTypedTuple(black); colors->InsertNextTypedTuple(black); colors->InsertNextTypedTuple(black); vtkNew polylines; vtkNew polyline; polyline->GetPointIds()->SetNumberOfIds(4); polyline->GetPointIds()->SetId(0, 0); polyline->GetPointIds()->SetId(1, 1); polyline->GetPointIds()->SetId(2, 2); polyline->GetPointIds()->SetId(3, 3); polylines->InsertNextCell(polyline); // Create a polydata object and add everything to it vtkSmartPointer polydata = vtkSmartPointer::New(); polydata->SetPoints(points); polydata->SetLines(polylines); painter->GetPen()->SetWidth(2.0); painter->DrawPolyData(475, 200, polydata, colors, VTK_SCALAR_MODE_USE_POINT_DATA); } int TestSVGContextExport(int, char*[]) { // Set up a 2D context view, context test object and add it to the scene vtkNew view; view->GetRenderer()->SetBackground(1.0, 1.0, 0.5); view->GetRenderer()->SetBackground2(1.0, 0.5, 1.0); view->GetRenderer()->SetBackgroundAlpha(0.5); view->GetRenderer()->GradientBackgroundOn(); view->GetRenderWindow()->SetSize(800, 600); vtkNew test; view->GetScene()->AddItem(test); // Force the use of the freetype based rendering strategy vtkOpenGLContextDevice2D::SafeDownCast(view->GetContext()->GetDevice()) ->SetStringRendererToFreeType(); view->GetRenderWindow()->SetMultiSamples(0); view->GetRenderWindow()->Render(); std::string filename = vtkTestingInteractor::TempDirectory + std::string("/TestSVGContextExport.svg"); vtkNew exp; exp->SetRenderWindow(view->GetRenderWindow()); exp->SetFileName(filename.c_str()); exp->Write(); #if 0 vtkNew iren; iren->SetRenderWindow(view->GetRenderWindow()); view->GetRenderWindow()->GetInteractor()->Initialize(); view->GetRenderWindow()->GetInteractor()->Start(); #endif return EXIT_SUCCESS; } // Make our new derived class to draw a diagram vtkStandardNewMacro(ContextSVGTest); // This function aims to test the primitives provided by the 2D API. bool ContextSVGTest::Paint(vtkContext2D* painter) { // Reset painter state that we care about: painter->GetBrush()->SetTexture(nullptr); painter->GetBrush()->SetColor(0, 0, 0, 255); painter->GetPen()->SetColor(0, 0, 0, 255); painter->GetPen()->SetWidth(1.f); painter->GetTextProp()->SetUseTightBoundingBox(1); painter->GetTextProp()->SetOrientation(0.); painter->GetTextProp()->SetVerticalJustificationToCentered(); painter->GetTextProp()->SetJustificationToCentered(); painter->GetTextProp()->SetColor(0.0, 0.0, 0.0); painter->GetTextProp()->SetOpacity(1.); painter->GetTextProp()->SetFontSize(24); painter->GetTextProp()->SetBold(0); painter->GetTextProp()->SetItalic(0); painter->GetTextProp()->SetFontFamilyToArial(); // Now to draw some points painter->GetPen()->SetColor(0, 0, 255); painter->GetPen()->SetWidth(5.0); painter->DrawPoint(10, 10); painter->DrawPoint(790, 10); painter->DrawPoint(10, 590); painter->DrawPoint(790, 590); // Test the string drawing functionality of the context painter->DrawString(400, 25, "SVG is used as a backend to the context."); painter->GetTextProp()->SetFontFamilyToTimes(); painter->GetTextProp()->SetColor(1, 0.2, 0.1); painter->GetTextProp()->SetOpacity(0.5); painter->GetTextProp()->SetOrientation(-38.); painter->GetTextProp()->SetJustificationToRight(); painter->GetTextProp()->SetVerticalJustificationToCentered(); painter->DrawString(475, 250, "Testing multi-\nline justified\nand rotated text."); drawPolyLinePolyData(painter); // Draw some individual lines of different thicknesses. for (int i = 0; i < 10; ++i) { painter->GetPen()->SetColor(255, i * 25, 0); painter->GetPen()->SetWidth(1.f + static_cast(i)); painter->DrawLine(10, 50 + float(i) * 10, 60, 50 + float(i) * 10); } // Draw some individual lines of different thicknesses. painter->GetPen()->SetWidth(10); for (int i = 0; i < 10; ++i) { painter->GetPen()->SetLineType(i % (vtkPen::DENSE_DOT_LINE + 1)); painter->GetPen()->SetColor(static_cast(float(i) * 25.0), 255, 128); painter->DrawLine(10, 250 + float(i) * 10, 60, 250 + float(i) * 10); } painter->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Use the draw lines function now to draw a shape. vtkSmartPointer points = vtkSmartPointer::New(); points->SetNumberOfPoints(30); for (int i = 0; i < 30; ++i) { double point[2] = { float(i) * 25.0 + 10.0, sin(float(i) / 5.0) * 100.0 + 200.0 }; points->SetPoint(i, point); } painter->GetPen()->SetColor(0, 255, 0); painter->GetPen()->SetWidth(5.0); painter->DrawPoly(points); // Test the markers float markerPoints[10 * 2]; unsigned char markerColors[10 * 4]; for (int i = 0; i < 10; ++i) { markerPoints[2 * i] = 500.0 + i * 30.0; markerPoints[2 * i + 1] = 20 * sin(markerPoints[2 * i]) + 375.0; markerColors[4 * i] = static_cast(255 * i / 10.0); markerColors[4 * i + 1] = static_cast(255 * (1.0 - i / 10.0)); markerColors[4 * i + 2] = static_cast(255 * (0.3)); markerColors[4 * i + 3] = static_cast(255 * (1.0 - ((i / 10.0) * 0.25))); } for (int style = VTK_MARKER_NONE + 1; style < VTK_MARKER_UNKNOWN; ++style) { // Increment the y values: for (int i = 0; i < 10; ++i) { markerPoints[2 * i + 1] += 35.0; } painter->GetPen()->SetWidth(style * 5 + 5); // Not highlighted: painter->DrawMarkers(style, false, markerPoints, 10, markerColors, 4); // Highlight the middle 4 points. painter->GetPen()->SetColorF(0.9, 0.8, 0.1, 0.5); painter->DrawMarkers(style, true, markerPoints + 3 * 2, 4); } // Draw some points of different widths. for (int i = 0; i < 10; ++i) { painter->GetPen()->SetColor(0, static_cast(float(i) * 25.0), 255, 255); painter->GetPen()->SetWidth(1.0 + float(i)); painter->DrawPoint(75, 50 + float(i) * 10); } painter->GetPen()->SetColor(0, 0, 255); painter->GetPen()->SetWidth(3.0); painter->DrawPoints(points); painter->GetPen()->SetColor(100, 200, 255); painter->GetPen()->SetWidth(3.0); painter->GetBrush()->SetColor(100, 255, 100); painter->DrawRect(100, 50, 200, 100); // Add in an arbitrary quad painter->GetPen()->SetColor(159, 0, 255); painter->GetPen()->SetWidth(1.0); painter->GetBrush()->SetColor(100, 55, 0, 200); painter->DrawQuad(350, 50, 375, 150, 525, 199, 666, 45); // Now to test out the transform... vtkNew transform; transform->Translate(145, 385); transform->Scale(0.25, 0.25); transform->Rotate(-45.); painter->PushMatrix(); painter->SetTransform(transform); painter->GetPen()->SetColor(255, 0, 0); painter->GetPen()->SetWidth(6.0); painter->DrawPoly(points); transform->Identity(); transform->Translate(0, 10); painter->AppendTransform(transform); painter->GetPen()->SetColor(0, 0, 200); painter->GetPen()->SetWidth(2.0); painter->DrawPoints(points); transform->Identity(); transform->Translate(0, -20); painter->AppendTransform(transform); painter->GetPen()->SetColor(200, 0, 100); painter->GetPen()->SetWidth(5.0); painter->DrawPoints(points); transform->Identity(); transform->Translate(20, 200); painter->SetTransform(transform); painter->GetPen()->SetColor(0, 0, 0); painter->GetPen()->SetWidth(1.0); painter->GetBrush()->SetColor(0, 0, 100, 69); // Draws smooth path (Full circle, testing oddball angles): painter->DrawEllipseWedge(100.0, 89.0, 20, 100, 15, 75, -26.23, 333.77); painter->DrawEllipseWedge(100.0, 89.0, 15, 15, 0, 0, -26.23, 333.77); painter->DrawEllipseWedge(125.0, 89.0, 20, 100, 0, 0, -26.23, 333.77); // Parital circle, more odd angles: painter->DrawEllipseWedge(150.0, 89.0, 20, 100, 15, 75, 403.f, 541.f); painter->DrawEllipseWedge(150.0, 89.0, 15, 75, 0, 0, 181.f, 403.f); // Smooth path: painter->DrawEllipticArc(100.0, 89.0, 20, 100, 0, 360); painter->DrawEllipticArc(100.0, 89.0, 15, 15, 0, 360); // Partial path: painter->DrawEllipticArc(150.0, 89.0, 20, 100, 43, 181); // Remove the transform: painter->PopMatrix(); // Toss some images in: vtkNew imageSrc; imageSrc->SetWholeExtent(0, 49, 0, 49, 0, 0); imageSrc->SetMaximum(1.0); imageSrc->Update(); vtkImageData* image = imageSrc->GetOutput(); // convert to RGB bytes: vtkFloatArray* vals = static_cast(image->GetPointData()->GetScalars()); float imgRange[2]; vals->GetValueRange(imgRange); float invRange = 1.f / (imgRange[1] - imgRange[0]); vtkUnsignedCharArray* scalars = vtkUnsignedCharArray::New(); scalars->SetNumberOfComponents(3); scalars->SetNumberOfTuples(vals->GetNumberOfTuples()); for (vtkIdType i = 0; i < vals->GetNumberOfTuples(); ++i) { float val = vals->GetValue(i); val = (val - imgRange[0]) * invRange; // normalize to (0, 1) scalars->SetComponent(i, 0, val * 255); scalars->SetComponent(i, 1, (1.f - val) * 255); scalars->SetComponent(i, 2, (val * val) * 255); } image->GetPointData()->SetScalars(scalars); scalars->Delete(); painter->DrawImage(10, 525, image); painter->DrawImage(65, 500, 2.f, image); painter->DrawImage(vtkRectf(170, 537.5f, 25, 25), image); // Test transparent text over geometry: painter->GetTextProp()->SetOrientation(0.); painter->GetTextProp()->SetFontSize(175); painter->GetTextProp()->SetColor(1., 0., 0.); painter->GetTextProp()->SetOpacity(0.25); painter->GetTextProp()->SetBold(1); painter->GetTextProp()->SetJustificationToCentered(); painter->GetTextProp()->SetVerticalJustificationToCentered(); painter->DrawString(600, 450, "T"); // Test text alignment: float alignX = 600.f; float alignY = 250.f; float alignW = 100.f * 0.5f; float alignH = 50.f * 0.5f; painter->GetPen()->SetWidth(1.f); painter->GetPen()->SetColor(0, 0, 0, 255); painter->DrawLine(alignX, alignY - alignH, alignX, alignY + alignH); painter->DrawLine(alignX - alignW, alignY, alignX + alignW, alignY); painter->GetTextProp()->SetFontSize(32); painter->GetTextProp()->SetJustificationToRight(); painter->GetTextProp()->SetVerticalJustificationToBottom(); painter->DrawString(alignX, alignY, "dag"); painter->GetTextProp()->SetJustificationToLeft(); painter->GetTextProp()->SetVerticalJustificationToBottom(); painter->DrawString(alignX, alignY, "dig"); painter->GetTextProp()->SetJustificationToRight(); painter->GetTextProp()->SetVerticalJustificationToTop(); painter->DrawString(alignX, alignY, "dog"); painter->GetTextProp()->SetJustificationToLeft(); painter->GetTextProp()->SetVerticalJustificationToTop(); painter->DrawString(alignX, alignY, "dug"); // Centering: float rect[4]; const char* centerString = "Center"; painter->ComputeStringBounds(centerString, rect); rect[0] += 350.f; rect[1] += 550.f; painter->GetBrush()->SetColor(0, 0, 0, 0); painter->DrawRect(rect[0], rect[1], rect[2], rect[3]); painter->GetTextProp()->SetJustificationToCentered(); painter->GetTextProp()->SetVerticalJustificationToCentered(); painter->DrawString(rect[0] + rect[2] * 0.5f, rect[1] + rect[3] * 0.5f, centerString); // Texturing: vtkNew pattern; pattern->SetDimensions(6, 6, 1); vtkNew patternScalars; patternScalars->SetNumberOfComponents(3); patternScalars->SetNumberOfTuples(36); patternScalars->FillValue(0); for (vtkIdType row = 0; row < 6; ++row) { for (vtkIdType col = 0; col < 6; ++col) { // Alternate red/blue: vtkIdType i = row * 6 + col; vtkIdType test = row + col; patternScalars->SetTypedComponent(i, (test % 2 == 0) ? 0 : 2, 255); } } pattern->GetPointData()->SetScalars(patternScalars); painter->GetBrush()->SetTexture(pattern); painter->GetBrush()->SetOpacity(0); // Stretching: painter->GetBrush()->SetTextureProperties(vtkBrush::Nearest | vtkBrush::Stretch); painter->DrawQuad(200, 485, 300, 400, 190, 420, 125, 390); // Tiling: painter->GetBrush()->SetTextureProperties(vtkBrush::Linear | vtkBrush::Repeat); painter->DrawQuad(300, 585, 400, 500, 290, 520, 230, 560); painter->GetBrush()->SetTexture(nullptr); // Some point sprites: vtkNew sprite; sprite->SetDimensions(25, 25, 1); vtkNew spriteScalars; spriteScalars->SetNumberOfComponents(3); spriteScalars->SetNumberOfTuples(25 * 25); spriteScalars->FillValue(0); sprite->GetPointData()->SetScalars(spriteScalars); std::vector spritePoints; spritePoints.reserve(50); std::vector spriteColors; spriteColors.reserve(100); for (int i = 0; i < 25; ++i) { this->SetSpritePoint(i, 0, sprite); this->SetSpritePoint(0, i, sprite); this->SetSpritePoint(i, i, sprite); this->SetSpritePoint(10, i, sprite); this->SetSpritePoint(i, 10, sprite); spritePoints.push_back(790.f); // x spritePoints.push_back(50.f + i * 20); // y spriteColors.push_back(static_cast(127 + 128 / (i + 1))); // r spriteColors.push_back(static_cast(255 - 128 / (i + 1))); // g spriteColors.push_back(static_cast(64 + 128 / (i + 1))); // b spriteColors.push_back(static_cast(64 + 191 / (i + 1))); // a } for (int i = 0; i < 10; ++i) { this->SetSpritePoint(24 - i, i, sprite); } painter->GetPen()->SetWidth(18); painter->DrawPointSprites(sprite, spritePoints.data(), 25, spriteColors.data(), 4); painter->GetPen()->SetColor(0, 255, 0, 64); painter->GetPen()->SetWidth(1); painter->DrawLine(781, 0, 781, 600); painter->DrawLine(799, 0, 799, 600); // Leaving this untested, since I can't find a viewer that seems to implement // clipPaths...webkit seems to not support userSpaceOnUse clipPath coords, // and forces objectBoundBox coords. #if 0 // Test clipping (why does Context2D not expose these directly??) vtkContextDevice2D *dev = painter->GetDevice(); std::array clipRect = { 325, 385, 150, 100 }; // xywh painter->GetPen()->SetColor(0, 128, 255, 255); painter->GetBrush()->SetColor(0, 128, 255, 64); painter->DrawQuad(clipRect[0], clipRect[1], clipRect[0] + clipRect[2], clipRect[1], clipRect[0] + clipRect[2], clipRect[1] + clipRect[3], clipRect[0], clipRect[1] + clipRect[3]); dev->SetClipping(clipRect.data()); dev->EnableClipping(true); painter->GetBrush()->SetColor(0, 255, 128, 128); painter->DrawWedge(clipRect[0] + clipRect[2] / 2.f, clipRect[1] + clipRect[3] / 2.f, 75.f, 50.f, 0.f, 360.f); dev->EnableClipping(false); #endif return true; } //------------------------------------------------------------------------------ void ContextSVGTest::SetSpritePoint(int x, int y, vtkImageData* sprite) { unsigned char* ptr = static_cast(sprite->GetScalarPointer(x, y, 0)); std::fill(ptr, ptr + 3, 255); }