diff options
Diffstat (limited to 'examples/datavisualization/graphgallery')
44 files changed, 9713 insertions, 0 deletions
diff --git a/examples/datavisualization/graphgallery/CMakeLists.txt b/examples/datavisualization/graphgallery/CMakeLists.txt new file mode 100644 index 00000000..c66fee25 --- /dev/null +++ b/examples/datavisualization/graphgallery/CMakeLists.txt @@ -0,0 +1,75 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(graphgallery LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}") + +find_package(Qt6 COMPONENTS Core) +find_package(Qt6 COMPONENTS Gui) +find_package(Qt6 COMPONENTS Widgets) +find_package(Qt6 COMPONENTS DataVisualization) + +qt_add_executable(graphgallery + main.cpp + bargraph.cpp bargraph.h + graphmodifier.cpp graphmodifier.h + rainfalldata.cpp rainfalldata.h + variantbardatamapping.cpp variantbardatamapping.h + variantbardataproxy.cpp variantbardataproxy.h + variantdataset.cpp variantdataset.h + scattergraph.cpp scattergraph.h + scatterdatamodifier.cpp scatterdatamodifier.h + axesinputhandler.cpp axesinputhandler.h + surfacegraph.cpp surfacegraph.h + surfacegraphmodifier.cpp surfacegraphmodifier.h + custominputhandler.cpp custominputhandler.h + highlightseries.cpp highlightseries.h + topographicseries.cpp topographicseries.h +) +set_target_properties(graphgallery PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) +target_link_libraries(graphgallery PUBLIC + Qt::Core + Qt::Gui + Qt::Widgets + Qt::DataVisualization +) + +set(graphgallery_resource_files + "data/raindata.txt" + "data/layer_1.png" + "data/layer_2.png" + "data/layer_3.png" + "data/oilrig.obj" + "data/pipe.obj" + "data/refinery.obj" + "data/maptexture.jpg" + "data/topography.png" +) + +qt6_add_resources(graphgallery "graphgallery" + PREFIX + "/" + FILES + ${graphgallery_resource_files} +) + +install(TARGETS graphgallery + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/datavisualization/graphgallery/axesinputhandler.cpp b/examples/datavisualization/graphgallery/axesinputhandler.cpp new file mode 100644 index 00000000..3292650e --- /dev/null +++ b/examples/datavisualization/graphgallery/axesinputhandler.cpp @@ -0,0 +1,116 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "axesinputhandler.h" +#include <QtCore/qmath.h> + +AxesInputHandler::AxesInputHandler(QAbstract3DGraph *graph, QObject *parent) : + Q3DInputHandler(parent) +{ + //! [3] + // Connect to the item selection signal from graph + connect(graph, &QAbstract3DGraph::selectedElementChanged, this, + &AxesInputHandler::handleElementSelected); + //! [3] +} + +//! [0] +void AxesInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos) +{ + Q3DInputHandler::mousePressEvent(event, mousePos); + if (Qt::LeftButton == event->button()) + m_mousePressed = true; +} +//! [0] + +//! [2] +void AxesInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) +{ + //! [5] + // Check if we're trying to drag axis label + if (m_mousePressed && m_state != StateNormal) { + //! [5] + setPreviousInputPos(inputPosition()); + setInputPosition(mousePos); + handleAxisDragging(); + } else { + Q3DInputHandler::mouseMoveEvent(event, mousePos); + } +} +//! [2] + +//! [1] +void AxesInputHandler::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos) +{ + Q3DInputHandler::mouseReleaseEvent(event, mousePos); + m_mousePressed = false; + m_state = StateNormal; +} +//! [1] + +void AxesInputHandler::handleElementSelected(QAbstract3DGraph::ElementType type) +{ + //! [4] + switch (type) { + case QAbstract3DGraph::ElementAxisXLabel: + m_state = StateDraggingX; + break; + case QAbstract3DGraph::ElementAxisYLabel: + m_state = StateDraggingY; + break; + case QAbstract3DGraph::ElementAxisZLabel: + m_state = StateDraggingZ; + break; + default: + m_state = StateNormal; + break; + } + //! [4] +} + +void AxesInputHandler::handleAxisDragging() +{ + float distance = 0.0f; + + //! [6] + // Get scene orientation from active camera + float xRotation = scene()->activeCamera()->xRotation(); + float yRotation = scene()->activeCamera()->yRotation(); + //! [6] + + //! [7] + // Calculate directional drag multipliers based on rotation + float xMulX = qCos(qDegreesToRadians(xRotation)); + float xMulY = qSin(qDegreesToRadians(xRotation)); + float zMulX = qSin(qDegreesToRadians(xRotation)); + float zMulY = qCos(qDegreesToRadians(xRotation)); + //! [7] + + //! [8] + // Get the drag amount + QPoint move = inputPosition() - previousInputPos(); + + // Flip the effect of y movement if we're viewing from below + float yMove = (yRotation < 0) ? -move.y() : move.y(); + //! [8] + + //! [9] + // Adjust axes + switch (m_state) { + case StateDraggingX: + distance = (move.x() * xMulX - yMove * xMulY) / m_speedModifier; + m_axisX->setRange(m_axisX->min() - distance, m_axisX->max() - distance); + break; + case StateDraggingZ: + distance = (move.x() * zMulX + yMove * zMulY) / m_speedModifier; + m_axisZ->setRange(m_axisZ->min() + distance, m_axisZ->max() + distance); + break; + case StateDraggingY: + distance = move.y() / m_speedModifier; // No need to use adjusted y move here + m_axisY->setRange(m_axisY->min() + distance, m_axisY->max() + distance); + break; + default: + break; + } + //! [9] +} diff --git a/examples/datavisualization/graphgallery/axesinputhandler.h b/examples/datavisualization/graphgallery/axesinputhandler.h new file mode 100644 index 00000000..bb8432ac --- /dev/null +++ b/examples/datavisualization/graphgallery/axesinputhandler.h @@ -0,0 +1,54 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef AXESINPUTHANDLER_H +#define AXESINPUTHANDLER_H + +#include <QtDataVisualization/q3dinputhandler.h> +#include <QtDataVisualization/qabstract3dgraph.h> +#include <QtDataVisualization/qvalue3daxis.h> + +//! [0] +class AxesInputHandler : public Q3DInputHandler +//! [0] +{ + Q_OBJECT + + enum InputState { + StateNormal = 0, + StateDraggingX, + StateDraggingZ, + StateDraggingY + }; + +public: + explicit AxesInputHandler(QAbstract3DGraph *graph, QObject *parent = 0); + + inline void setAxes(QValue3DAxis *axisX, QValue3DAxis *axisZ, QValue3DAxis *axisY) { + m_axisX = axisX; + m_axisZ = axisZ; + m_axisY = axisY; + } + + //! [1] + inline void setDragSpeedModifier(float modifier) { m_speedModifier = modifier; } + //! [1] + + virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos); + virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos); + virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos); + +private: + void handleElementSelected(QAbstract3DGraph::ElementType type); + void handleAxisDragging(); + +private: + bool m_mousePressed = false; + InputState m_state = StateNormal; + QValue3DAxis *m_axisX = nullptr; + QValue3DAxis *m_axisZ = nullptr; + QValue3DAxis *m_axisY = nullptr; + float m_speedModifier = 15.f; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/bargraph.cpp b/examples/datavisualization/graphgallery/bargraph.cpp new file mode 100644 index 00000000..5873c757 --- /dev/null +++ b/examples/datavisualization/graphgallery/bargraph.cpp @@ -0,0 +1,329 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "bargraph.h" +#include "graphmodifier.h" + +#include <QtWidgets/qboxlayout.h> +#include <QtWidgets/qpushbutton.h> +#include <QtWidgets/qcheckbox.h> +#include <QtWidgets/qslider.h> +#include <QtWidgets/qfontcombobox.h> +#include <QtWidgets/qlabel.h> +#include <QtWidgets/qradiobutton.h> +#include <QtWidgets/qbuttongroup.h> +#include <QtGui/qfontdatabase.h> + +using namespace Qt::StringLiterals; + +BarGraph::BarGraph() +{ + //! [0] + m_barsGraph = new Q3DBars(); + //! [0] +} + +BarGraph::~BarGraph() = default; + +bool BarGraph::initialize(const QSize &minimumGraphSize, const QSize &maximumGraphSize) +{ + if (!m_barsGraph->hasContext()) + return false; + + //! [1] + m_barsWidget = new QWidget; + auto *hLayout = new QHBoxLayout(m_barsWidget); + m_container = QWidget::createWindowContainer(m_barsGraph, m_barsWidget); + m_barsGraph->resize(minimumGraphSize); + m_container->setMinimumSize(minimumGraphSize); + m_container->setMaximumSize(maximumGraphSize); + m_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_container->setFocusPolicy(Qt::StrongFocus); + hLayout->addWidget(m_container, 1); + + auto *vLayout = new QVBoxLayout(); + hLayout->addLayout(vLayout); + //! [1] + + auto *themeList = new QComboBox(m_barsWidget); + themeList->addItem(u"Qt"_s); + themeList->addItem(u"Primary Colors"_s); + themeList->addItem(u"Digia"_s); + themeList->addItem(u"Stone Moss"_s); + themeList->addItem(u"Army Blue"_s); + themeList->addItem(u"Retro"_s); + themeList->addItem(u"Ebony"_s); + themeList->addItem(u"Isabelle"_s); + themeList->setCurrentIndex(0); + + auto *labelButton = new QPushButton(m_barsWidget); + labelButton->setText(u"Change label style"_s); + + auto *smoothCheckBox = new QCheckBox(m_barsWidget); + smoothCheckBox->setText(u"Smooth bars"_s); + smoothCheckBox->setChecked(false); + + auto *barStyleList = new QComboBox(m_barsWidget); + barStyleList->addItem(u"Bar"_s, QAbstract3DSeries::MeshBar); + barStyleList->addItem(u"Pyramid"_s, QAbstract3DSeries::MeshPyramid); + barStyleList->addItem(u"Cone"_s, QAbstract3DSeries::MeshCone); + barStyleList->addItem(u"Cylinder"_s, QAbstract3DSeries::MeshCylinder); + barStyleList->addItem(u"Bevel bar"_s, QAbstract3DSeries::MeshBevelBar); + barStyleList->addItem(u"Sphere"_s, QAbstract3DSeries::MeshSphere); + barStyleList->setCurrentIndex(4); + + auto *cameraButton = new QPushButton(m_barsWidget); + cameraButton->setText(u"Change camera preset"_s); + + auto *zoomToSelectedButton = new QPushButton(m_barsWidget); + zoomToSelectedButton->setText(u"Zoom to selected bar"_s); + + auto *selectionModeList = new QComboBox(m_barsWidget); + selectionModeList->addItem(u"None"_s, + int(QAbstract3DGraph::SelectionNone)); + selectionModeList->addItem(u"Bar"_s, + int(QAbstract3DGraph::SelectionItem)); + selectionModeList->addItem(u"Row"_s, + int(QAbstract3DGraph::SelectionRow)); + selectionModeList->addItem(u"Bar and Row"_s, + int(QAbstract3DGraph::SelectionItemAndRow)); + selectionModeList->addItem(u"Column"_s, + int(QAbstract3DGraph::SelectionColumn)); + selectionModeList->addItem(u"Bar and Column"_s, + int(QAbstract3DGraph::SelectionItemAndColumn)); + selectionModeList->addItem(u"Row and Column"_s, + int(QAbstract3DGraph::SelectionRowAndColumn)); + selectionModeList->addItem(u"Bar, Row and Column"_s, + int(QAbstract3DGraph::SelectionItemRowAndColumn)); + selectionModeList->addItem(u"Slice into Row"_s, + int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionRow)); + selectionModeList->addItem(u"Slice into Row and Item"_s, + int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndRow)); + selectionModeList->addItem(u"Slice into Column"_s, + int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionColumn)); + selectionModeList->addItem(u"Slice into Column and Item"_s, + int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndColumn)); + selectionModeList->addItem(u"Multi: Bar, Row, Col"_s, + int(QAbstract3DGraph::SelectionItemRowAndColumn + | QAbstract3DGraph::SelectionMultiSeries)); + selectionModeList->addItem(u"Multi, Slice: Row, Item"_s, + int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndRow + | QAbstract3DGraph::SelectionMultiSeries)); + selectionModeList->addItem(u"Multi, Slice: Col, Item"_s, + int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndColumn + | QAbstract3DGraph::SelectionMultiSeries)); + selectionModeList->setCurrentIndex(1); + + auto *backgroundCheckBox = new QCheckBox(m_barsWidget); + backgroundCheckBox->setText(u"Show background"_s); + backgroundCheckBox->setChecked(false); + + auto *gridCheckBox = new QCheckBox(m_barsWidget); + gridCheckBox->setText(u"Show grid"_s); + gridCheckBox->setChecked(true); + + auto *seriesCheckBox = new QCheckBox(m_barsWidget); + seriesCheckBox->setText(u"Show second series"_s); + seriesCheckBox->setChecked(false); + + auto *reverseValueAxisCheckBox = new QCheckBox(m_barsWidget); + reverseValueAxisCheckBox->setText(u"Reverse value axis"_s); + reverseValueAxisCheckBox->setChecked(false); + + auto *reflectionCheckBox = new QCheckBox(m_barsWidget); + reflectionCheckBox->setText(u"Show reflections"_s); + reflectionCheckBox->setChecked(false); + + //! [3] + auto *rotationSliderX = new QSlider(Qt::Horizontal, m_barsWidget); + rotationSliderX->setTickInterval(30); + rotationSliderX->setTickPosition(QSlider::TicksBelow); + rotationSliderX->setMinimum(-180); + rotationSliderX->setValue(0); + rotationSliderX->setMaximum(180); + //! [3] + auto *rotationSliderY = new QSlider(Qt::Horizontal, m_barsWidget); + rotationSliderY->setTickInterval(15); + rotationSliderY->setTickPosition(QSlider::TicksAbove); + rotationSliderY->setMinimum(-90); + rotationSliderY->setValue(0); + rotationSliderY->setMaximum(90); + + auto *fontSizeSlider = new QSlider(Qt::Horizontal, m_barsWidget); + fontSizeSlider->setTickInterval(10); + fontSizeSlider->setTickPosition(QSlider::TicksBelow); + fontSizeSlider->setMinimum(1); + fontSizeSlider->setValue(30); + fontSizeSlider->setMaximum(100); + + auto *fontList = new QFontComboBox(m_barsWidget); + fontList->setCurrentFont(QFont("Times New Roman")); + + auto *shadowQuality = new QComboBox(m_barsWidget); + shadowQuality->addItem(u"None"_s); + shadowQuality->addItem(u"Low"_s); + shadowQuality->addItem(u"Medium"_s); + shadowQuality->addItem(u"High"_s); + shadowQuality->addItem(u"Low Soft"_s); + shadowQuality->addItem(u"Medium Soft"_s); + shadowQuality->addItem(u"High Soft"_s); + shadowQuality->setCurrentIndex(5); + + auto *rangeList = new QComboBox(m_barsWidget); + rangeList->addItem(u"2015"_s); + rangeList->addItem(u"2016"_s); + rangeList->addItem(u"2017"_s); + rangeList->addItem(u"2018"_s); + rangeList->addItem(u"2019"_s); + rangeList->addItem(u"2020"_s); + rangeList->addItem(u"2021"_s); + rangeList->addItem(u"2022"_s); + rangeList->addItem(u"All"_s); + rangeList->setCurrentIndex(8); + + auto *axisTitlesVisibleCB = new QCheckBox(m_barsWidget); + axisTitlesVisibleCB->setText(u"Axis titles visible"_s); + axisTitlesVisibleCB->setChecked(true); + + auto *axisTitlesFixedCB = new QCheckBox(m_barsWidget); + axisTitlesFixedCB->setText(u"Axis titles fixed"_s); + axisTitlesFixedCB->setChecked(true); + + auto *axisLabelRotationSlider = new QSlider(Qt::Horizontal, m_barsWidget); + axisLabelRotationSlider->setTickInterval(10); + axisLabelRotationSlider->setTickPosition(QSlider::TicksBelow); + axisLabelRotationSlider->setMinimum(0); + axisLabelRotationSlider->setValue(30); + axisLabelRotationSlider->setMaximum(90); + + auto *modeGroup = new QButtonGroup(m_barsWidget); + auto *modeWeather = new QRadioButton(u"Temperature Data"_s, m_barsWidget); + modeWeather->setChecked(true); + auto *modeCustomProxy = new QRadioButton(u"Custom Proxy Data"_s, m_barsWidget); + modeGroup->addButton(modeWeather); + modeGroup->addButton(modeCustomProxy); + + //! [4] + vLayout->addWidget(new QLabel(u"Rotate horizontally"_s)); + vLayout->addWidget(rotationSliderX, 0, Qt::AlignTop); + //! [4] + vLayout->addWidget(new QLabel(u"Rotate vertically"_s)); + vLayout->addWidget(rotationSliderY, 0, Qt::AlignTop); + vLayout->addWidget(labelButton, 0, Qt::AlignTop); + vLayout->addWidget(cameraButton, 0, Qt::AlignTop); + vLayout->addWidget(zoomToSelectedButton, 0, Qt::AlignTop); + vLayout->addWidget(backgroundCheckBox); + vLayout->addWidget(gridCheckBox); + vLayout->addWidget(smoothCheckBox); + vLayout->addWidget(reflectionCheckBox); + vLayout->addWidget(seriesCheckBox); + vLayout->addWidget(reverseValueAxisCheckBox); + vLayout->addWidget(axisTitlesVisibleCB); + vLayout->addWidget(axisTitlesFixedCB); + vLayout->addWidget(new QLabel(u"Show year"_s)); + vLayout->addWidget(rangeList); + vLayout->addWidget(new QLabel(u"Change bar style"_s)); + vLayout->addWidget(barStyleList); + vLayout->addWidget(new QLabel(u"Change selection mode"_s)); + vLayout->addWidget(selectionModeList); + vLayout->addWidget(new QLabel(u"Change theme"_s)); + vLayout->addWidget(themeList); + vLayout->addWidget(new QLabel(u"Adjust shadow quality"_s)); + vLayout->addWidget(shadowQuality); + vLayout->addWidget(new QLabel(u"Change font"_s)); + vLayout->addWidget(fontList); + vLayout->addWidget(new QLabel(u"Adjust font size"_s)); + vLayout->addWidget(fontSizeSlider); + vLayout->addWidget(new QLabel(u"Axis label rotation"_s)); + vLayout->addWidget(axisLabelRotationSlider, 0, Qt::AlignTop); + vLayout->addWidget(modeWeather, 0, Qt::AlignTop); + vLayout->addWidget(modeCustomProxy, 1, Qt::AlignTop); + + //! [2] + auto *modifier = new GraphModifier(m_barsGraph, this); + //! [2] + + //! [5] + QObject::connect(rotationSliderX, &QSlider::valueChanged, modifier, &GraphModifier::rotateX); + //! [5] + QObject::connect(rotationSliderY, &QSlider::valueChanged, modifier, &GraphModifier::rotateY); + + QObject::connect(labelButton, &QPushButton::clicked, modifier, + &GraphModifier::changeLabelBackground); + QObject::connect(cameraButton, &QPushButton::clicked, modifier, + &GraphModifier::changePresetCamera); + QObject::connect(zoomToSelectedButton, &QPushButton::clicked, modifier, + &GraphModifier::zoomToSelectedBar); + + QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setBackgroundEnabled); + QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setGridEnabled); + QObject::connect(smoothCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setSmoothBars); + QObject::connect(seriesCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setSeriesVisibility); + QObject::connect(reverseValueAxisCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setReverseValueAxis); + QObject::connect(reflectionCheckBox, &QCheckBox::stateChanged, modifier, + &GraphModifier::setReflection); + + QObject::connect(modifier, &GraphModifier::backgroundEnabledChanged, + backgroundCheckBox, &QCheckBox::setChecked); + QObject::connect(modifier, &GraphModifier::gridEnabledChanged, + gridCheckBox, &QCheckBox::setChecked); + + QObject::connect(rangeList, &QComboBox::currentIndexChanged, modifier, + &GraphModifier::changeRange); + + QObject::connect(barStyleList, &QComboBox::currentIndexChanged, modifier, + &GraphModifier::changeStyle); + + QObject::connect(selectionModeList, &QComboBox::currentIndexChanged, modifier, + &GraphModifier::changeSelectionMode); + + QObject::connect(themeList, &QComboBox::currentIndexChanged, modifier, + &GraphModifier::changeTheme); + + QObject::connect(shadowQuality, &QComboBox::currentIndexChanged, modifier, + &GraphModifier::changeShadowQuality); + + QObject::connect(modifier, &GraphModifier::shadowQualityChanged, shadowQuality, + &QComboBox::setCurrentIndex); + QObject::connect(m_barsGraph, &Q3DBars::shadowQualityChanged, modifier, + &GraphModifier::shadowQualityUpdatedByVisual); + + QObject::connect(fontSizeSlider, &QSlider::valueChanged, modifier, + &GraphModifier::changeFontSize); + QObject::connect(fontList, &QFontComboBox::currentFontChanged, modifier, + &GraphModifier::changeFont); + + QObject::connect(modifier, &GraphModifier::fontSizeChanged, fontSizeSlider, + &QSlider::setValue); + QObject::connect(modifier, &GraphModifier::fontChanged, fontList, + &QFontComboBox::setCurrentFont); + + QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged, modifier, + &GraphModifier::setAxisTitleVisibility); + QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged, modifier, + &GraphModifier::setAxisTitleFixed); + QObject::connect(axisLabelRotationSlider, &QSlider::valueChanged, modifier, + &GraphModifier::changeLabelRotation); + + QObject::connect(modeWeather, &QRadioButton::toggled, modifier, + &GraphModifier::setDataModeToWeather); + QObject::connect(modeCustomProxy, &QRadioButton::toggled, modifier, + &GraphModifier::setDataModeToCustom); + QObject::connect(modeWeather, &QRadioButton::toggled, seriesCheckBox, + &QCheckBox::setEnabled); + QObject::connect(modeWeather, &QRadioButton::toggled, rangeList, + &QComboBox::setEnabled); + QObject::connect(modeWeather, &QRadioButton::toggled, axisTitlesVisibleCB, + &QCheckBox::setEnabled); + QObject::connect(modeWeather, &QRadioButton::toggled, axisTitlesFixedCB, + &QCheckBox::setEnabled); + QObject::connect(modeWeather, &QRadioButton::toggled, axisLabelRotationSlider, + &QSlider::setEnabled); + + return true; +} diff --git a/examples/datavisualization/graphgallery/bargraph.h b/examples/datavisualization/graphgallery/bargraph.h new file mode 100644 index 00000000..004c14eb --- /dev/null +++ b/examples/datavisualization/graphgallery/bargraph.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef BARGRAPH_H +#define BARGRAPH_H + +#include <QtDataVisualization/q3dbars.h> +#include <QtCore/qobject.h> + +class BarGraph : public QObject +{ + Q_OBJECT +public: + BarGraph(); + ~BarGraph(); + + bool initialize(const QSize &minimumGraphSize, const QSize &maximumGraphSize); + QWidget *barsWidget() { return m_barsWidget; } + +private: + Q3DBars *m_barsGraph = nullptr; + QWidget *m_container = nullptr; + QWidget *m_barsWidget = nullptr; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/custominputhandler.cpp b/examples/datavisualization/graphgallery/custominputhandler.cpp new file mode 100644 index 00000000..f9f06871 --- /dev/null +++ b/examples/datavisualization/graphgallery/custominputhandler.cpp @@ -0,0 +1,161 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "custominputhandler.h" + +#include <QtDataVisualization/q3dcamera.h> +#include <QtCore/qmath.h> + +CustomInputHandler::CustomInputHandler(QAbstract3DGraph *graph, QObject *parent) : + Q3DInputHandler(parent) +{ + // Connect to the item selection signal from graph + connect(graph, &QAbstract3DGraph::selectedElementChanged, this, + &CustomInputHandler::handleElementSelected); +} + +void CustomInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos) +{ + if (Qt::LeftButton == event->button()) { + m_highlight->setVisible(false); + m_mousePressed = true; + } + Q3DInputHandler::mousePressEvent(event, mousePos); +} + +//! [1] +void CustomInputHandler::wheelEvent(QWheelEvent *event) +{ + float delta = float(event->angleDelta().y()); + + m_axisXMinValue += delta; + m_axisXMaxValue -= delta; + m_axisZMinValue += delta; + m_axisZMaxValue -= delta; + checkConstraints(); + + float y = (m_axisXMaxValue - m_axisXMinValue) * m_aspectRatio; + + m_axisX->setRange(m_axisXMinValue, m_axisXMaxValue); + m_axisY->setRange(100.f, y); + m_axisZ->setRange(m_axisZMinValue, m_axisZMaxValue); +} +//! [1] + +void CustomInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos) +{ + // Check if we're trying to drag axis label + if (m_mousePressed && m_state != StateNormal) { + setPreviousInputPos(inputPosition()); + setInputPosition(mousePos); + handleAxisDragging(); + } else { + Q3DInputHandler::mouseMoveEvent(event, mousePos); + } +} + +void CustomInputHandler::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos) +{ + Q3DInputHandler::mouseReleaseEvent(event, mousePos); + m_mousePressed = false; + m_state = StateNormal; +} + +void CustomInputHandler::handleElementSelected(QAbstract3DGraph::ElementType type) +{ + switch (type) { + case QAbstract3DGraph::ElementAxisXLabel: + m_state = StateDraggingX; + break; + case QAbstract3DGraph::ElementAxisZLabel: + m_state = StateDraggingZ; + break; + default: + m_state = StateNormal; + break; + } +} + +void CustomInputHandler::handleAxisDragging() +{ + float distance = 0.f; + + // Get scene orientation from active camera + float xRotation = scene()->activeCamera()->xRotation(); + + // Calculate directional drag multipliers based on rotation + float xMulX = qCos(qDegreesToRadians(xRotation)); + float xMulY = qSin(qDegreesToRadians(xRotation)); + float zMulX = qSin(qDegreesToRadians(xRotation)); + float zMulY = qCos(qDegreesToRadians(xRotation)); + + // Get the drag amount + QPoint move = inputPosition() - previousInputPos(); + + // Adjust axes + switch (m_state) { + //! [0] + case StateDraggingX: + distance = (move.x() * xMulX - move.y() * xMulY) * m_speedModifier; + m_axisXMinValue -= distance; + m_axisXMaxValue -= distance; + if (m_axisXMinValue < m_areaMinValue) { + float dist = m_axisXMaxValue - m_axisXMinValue; + m_axisXMinValue = m_areaMinValue; + m_axisXMaxValue = m_axisXMinValue + dist; + } + if (m_axisXMaxValue > m_areaMaxValue) { + float dist = m_axisXMaxValue - m_axisXMinValue; + m_axisXMaxValue = m_areaMaxValue; + m_axisXMinValue = m_axisXMaxValue - dist; + } + m_axisX->setRange(m_axisXMinValue, m_axisXMaxValue); + break; + //! [0] + case StateDraggingZ: + distance = (move.x() * zMulX + move.y() * zMulY) * m_speedModifier; + m_axisZMinValue += distance; + m_axisZMaxValue += distance; + if (m_axisZMinValue < m_areaMinValue) { + float dist = m_axisZMaxValue - m_axisZMinValue; + m_axisZMinValue = m_areaMinValue; + m_axisZMaxValue = m_axisZMinValue + dist; + } + if (m_axisZMaxValue > m_areaMaxValue) { + float dist = m_axisZMaxValue - m_axisZMinValue; + m_axisZMaxValue = m_areaMaxValue; + m_axisZMinValue = m_axisZMaxValue - dist; + } + m_axisZ->setRange(m_axisZMinValue, m_axisZMaxValue); + break; + default: + break; + } +} + +void CustomInputHandler::checkConstraints() +{ + //! [2] + if (m_axisXMinValue < m_areaMinValue) + m_axisXMinValue = m_areaMinValue; + if (m_axisXMaxValue > m_areaMaxValue) + m_axisXMaxValue = m_areaMaxValue; + // Don't allow too much zoom in + if ((m_axisXMaxValue - m_axisXMinValue) < m_axisXMinRange) { + float adjust = (m_axisXMinRange - (m_axisXMaxValue - m_axisXMinValue)) / 2.f; + m_axisXMinValue -= adjust; + m_axisXMaxValue += adjust; + } + //! [2] + + if (m_axisZMinValue < m_areaMinValue) + m_axisZMinValue = m_areaMinValue; + if (m_axisZMaxValue > m_areaMaxValue) + m_axisZMaxValue = m_areaMaxValue; + // Don't allow too much zoom in + if ((m_axisZMaxValue - m_axisZMinValue) < m_axisZMinRange) { + float adjust = (m_axisZMinRange - (m_axisZMaxValue - m_axisZMinValue)) / 2.f; + m_axisZMinValue -= adjust; + m_axisZMaxValue += adjust; + } +} diff --git a/examples/datavisualization/graphgallery/custominputhandler.h b/examples/datavisualization/graphgallery/custominputhandler.h new file mode 100644 index 00000000..a28b5851 --- /dev/null +++ b/examples/datavisualization/graphgallery/custominputhandler.h @@ -0,0 +1,76 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef CUSTOMINPUTHANDLER_H +#define CUSTOMINPUTHANDLER_H + +#include "highlightseries.h" + +#include <QtDataVisualization/q3dinputhandler.h> +#include <QtDataVisualization/qabstract3dgraph.h> +#include <QtDataVisualization/qvalue3daxis.h> + +class CustomInputHandler : public Q3DInputHandler +{ + Q_OBJECT + + enum InputState { + StateNormal = 0, + StateDraggingX, + StateDraggingZ, + StateDraggingY + }; + +public: + explicit CustomInputHandler(QAbstract3DGraph *graph, QObject *parent = 0); + + inline void setLimits(float min, float max, float minRange) { + m_areaMinValue = min; + m_areaMaxValue = max; + m_axisXMinValue = m_areaMinValue; + m_axisXMaxValue = m_areaMaxValue; + m_axisZMinValue = m_areaMinValue; + m_axisZMaxValue = m_areaMaxValue; + m_axisXMinRange = minRange; + m_axisZMinRange = minRange; + } + inline void setAxes(QValue3DAxis *axisX, QValue3DAxis *axisY, QValue3DAxis *axisZ) { + m_axisX = axisX; + m_axisY = axisY; + m_axisZ = axisZ; + } + inline void setAspectRatio(float ratio) { m_aspectRatio = ratio; } + inline void setHighlightSeries(HighlightSeries *series) { m_highlight = series; } + inline void setDragSpeedModifier(float modifier) { m_speedModifier = modifier; } + +protected: + virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos); + virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos); + virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos); + virtual void wheelEvent(QWheelEvent *event); + +private: + void handleElementSelected(QAbstract3DGraph::ElementType type); + void handleAxisDragging(); + void checkConstraints(); + +private: + HighlightSeries *m_highlight = nullptr; + bool m_mousePressed = false; + InputState m_state = StateNormal; + QValue3DAxis *m_axisX = nullptr; + QValue3DAxis *m_axisY = nullptr; + QValue3DAxis *m_axisZ = nullptr; + float m_speedModifier = 20.f; + float m_aspectRatio = 0.f; + float m_axisXMinValue = 0.f; + float m_axisXMaxValue = 0.f; + float m_axisXMinRange = 0.f; + float m_axisZMinValue = 0.f; + float m_axisZMaxValue = 0.f; + float m_axisZMinRange = 0.f; + float m_areaMinValue = 0.f; + float m_areaMaxValue = 0.f; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/data/layer_1.png b/examples/datavisualization/graphgallery/data/layer_1.png Binary files differnew file mode 100644 index 00000000..9138c710 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/layer_1.png diff --git a/examples/datavisualization/graphgallery/data/layer_2.png b/examples/datavisualization/graphgallery/data/layer_2.png Binary files differnew file mode 100644 index 00000000..61631ae8 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/layer_2.png diff --git a/examples/datavisualization/graphgallery/data/layer_3.png b/examples/datavisualization/graphgallery/data/layer_3.png Binary files differnew file mode 100644 index 00000000..066ffbe7 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/layer_3.png diff --git a/examples/datavisualization/graphgallery/data/license.txt b/examples/datavisualization/graphgallery/data/license.txt new file mode 100644 index 00000000..749daf31 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/license.txt @@ -0,0 +1,77 @@ +License information regarding the data obtained from National Land Survey of +Finland http://www.maanmittauslaitos.fi/en +- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and + U4422C) 08/2014 +- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014 + +National Land Survey open data licence - version 1.0 - 1 May 2012 + +1. General information + +The National Land Survey of Finland (hereinafter the Licensor), as the holder +of the immaterial rights to the data, has granted on the terms mentioned below +the right to use a copy (hereinafter data or dataset(s)) of the data (or a part +of it). + +The Licensee is a natural or legal person who makes use of the data covered by +this licence. The Licensee accepts the terms of this licence by receiving the +dataset(s) covered by the licence. + +This Licence agreement does not create a co-operation or business relationship +between the Licensee and the Licensor. + +2. Terms of the licence + +2.1. Right of use + +This licence grants a worldwide, free of charge and irrevocable parallel right +of use to open data. According to the terms of the licence, data received by +the Licensee can be freely: + - copied, distributed and published, + - modified and utilised commercially and non-commercially, + - inserted into other products and + - used as a part of a software application or service. + +2.2. Duties and responsibilities of the Licensee + +Through reasonable means suitable to the distribution medium or method which is +used in conjunction with a product containing data or a service utilising data +covered by this licence or while distributing data, the Licensee shall: + - mention the name of the Licensor, the name of the dataset(s) and the time + when the National Land Survey has delivered the dataset(s) (e.g.: contains + data from the National Land Survey of Finland Topographic Database 06/2012) + - provide a copy of this licence or a link to it, as well as + - require third parties to provide the same information when granting rights + to copies of dataset(s) or products and services containing such data and + - remove the name of the Licensor from the product or service, if required to + do so by the Licensor. + +The terms of this licence do not allow the Licensee to state in conjunction +with the use of dataset(s) that the Licensor supports or recommends such use. + +2.3. Duties and responsibilities of the Licensor + +The Licensor shall ensure that + - the Licensor has the right to grant rights to the dataset(s) in accordance + with this licence. + +The data has been licensed "as is" and the Licensor + - shall not be held responsible for any errors or omissions in the data, + disclaims any warranty for the validity or up to date status of the data and + shall be free from liability for direct or consequential damages arising + from the use of data provided by the Licensor, + - and is not obligated to ensure the continuous availability of the data, nor + to announce in advance the interruption or cessation of availability, and + the Licensor shall be free from liability for direct or consequential + damages arising from any such interruption or cessation. + +3. Jurisdiction + +Finnish law shall apply to this licence. + +4. Changes to this licence + +The Licensor may at any time change the terms of the licence or apply a +different licence to the data. The terms of this licence shall, however, still +apply to such data that has been received prior to the change of the terms of +the licence or the licence itself. diff --git a/examples/datavisualization/graphgallery/data/maptexture.jpg b/examples/datavisualization/graphgallery/data/maptexture.jpg Binary files differnew file mode 100644 index 00000000..ae5d66eb --- /dev/null +++ b/examples/datavisualization/graphgallery/data/maptexture.jpg diff --git a/examples/datavisualization/graphgallery/data/oilrig.obj b/examples/datavisualization/graphgallery/data/oilrig.obj new file mode 100644 index 00000000..c3b6ea57 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/oilrig.obj @@ -0,0 +1,2322 @@ +# Blender v2.66 (sub 0) OBJ File: 'oilrig.blend' +# www.blender.org +v 0.057462 2.272318 -1.170324 +v 0.057461 8.181165 -0.128434 +v 0.055540 2.268930 -1.151111 +v 0.055539 8.177776 -0.109221 +v 0.049849 2.265673 -1.132637 +v 0.049849 8.174520 -0.090747 +v 0.040608 2.262671 -1.115611 +v 0.040608 8.171517 -0.073721 +v 0.028172 2.260039 -1.100687 +v 0.028172 8.168886 -0.058798 +v 0.013019 2.257880 -1.088440 +v 0.013018 8.166726 -0.046550 +v -0.004270 2.256275 -1.079339 +v -0.004271 8.165121 -0.037450 +v -0.023029 2.255287 -1.073735 +v -0.023030 8.164133 -0.031846 +v -0.042539 2.254953 -1.071843 +v -0.042539 8.163799 -0.029953 +v -0.062048 2.255287 -1.073735 +v -0.062048 8.164133 -0.031846 +v -0.080807 2.256275 -1.079339 +v -0.080808 8.165121 -0.037450 +v -0.098096 2.257880 -1.088440 +v -0.098096 8.166726 -0.046550 +v -0.113249 2.260039 -1.100687 +v -0.113250 8.168886 -0.058798 +v -0.125685 2.262671 -1.115611 +v -0.125686 8.171517 -0.073721 +v -0.134926 2.265673 -1.132637 +v -0.134927 8.174520 -0.090747 +v -0.140617 2.268930 -1.151111 +v -0.140618 8.177776 -0.109222 +v -0.142538 2.272318 -1.170324 +v -0.142539 8.181165 -0.128434 +v -0.140617 2.275706 -1.189536 +v -0.140618 8.184552 -0.147647 +v -0.134926 2.278963 -1.208011 +v -0.134927 8.187810 -0.166121 +v -0.125685 2.281965 -1.225037 +v -0.125686 8.190812 -0.183147 +v -0.113249 2.284597 -1.239960 +v -0.113250 8.193443 -0.198071 +v -0.098095 2.286757 -1.252208 +v -0.098096 8.195602 -0.210318 +v -0.080807 2.288361 -1.261308 +v -0.080807 8.197207 -0.219419 +v -0.062047 2.289349 -1.266912 +v -0.062048 8.198195 -0.225023 +v -0.042538 2.289683 -1.268804 +v -0.042539 8.198529 -0.226915 +v -0.023029 2.289349 -1.266912 +v -0.023030 8.198195 -0.225023 +v -0.004270 2.288361 -1.261308 +v -0.004271 8.197207 -0.219418 +v 0.013019 2.286757 -1.252207 +v 0.013018 8.195602 -0.210318 +v 0.028172 2.284597 -1.239960 +v 0.028172 8.193443 -0.198070 +v 0.040609 2.281965 -1.225036 +v 0.040608 8.190812 -0.183147 +v 0.049850 2.278963 -1.208010 +v 0.049849 8.187810 -0.166121 +v 0.055540 2.275706 -1.189536 +v 0.055539 8.184552 -0.147646 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.961940 0.308658 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.990393 0.597545 +vt 0.915735 0.222215 +vt 0.961940 0.691342 +vt 0.853553 0.146447 +vt 0.915735 0.777785 +vt 0.777785 0.084265 +vt 0.853553 0.853553 +vt 0.691342 0.038060 +vt 0.777785 0.915735 +vt 0.597545 0.009607 +vt 0.691342 0.961940 +vt 0.000000 0.500000 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.038060 0.691342 +vt 0.009607 0.597546 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.308659 0.961940 +vt 0.222215 0.915735 +vt 0.084265 0.222215 +vt 0.146447 0.853554 +vt 0.084266 0.777786 +vn 0.995185 -0.017020 0.096528 +vn 0.956940 -0.050408 0.285877 +vn 0.881921 -0.081857 0.464235 +vn 0.773009 -0.110162 0.624758 +vn 0.634397 -0.134231 0.761264 +vn 0.471397 -0.153144 0.868523 +vn 0.290285 -0.166171 0.942402 +vn 0.098018 -0.172812 0.980066 +vn -0.098022 -0.172812 0.980065 +vn -0.290285 -0.166171 0.942402 +vn -0.471392 -0.153145 0.868526 +vn -0.634399 -0.134231 0.761262 +vn -0.773009 -0.110162 0.624757 +vn -0.881923 -0.081857 0.464231 +vn -0.956941 -0.050407 0.285873 +vn -0.995185 -0.017021 0.096528 +vn -0.995185 0.017021 -0.096529 +vn -0.956940 0.050407 -0.285875 +vn -0.881920 0.081858 -0.464238 +vn -0.773015 0.110160 -0.624751 +vn -0.634391 0.134232 -0.761268 +vn -0.471394 0.153144 -0.868524 +vn -0.290288 0.166171 -0.942401 +vn -0.098006 0.172812 -0.980067 +vn 0.098019 0.172812 -0.980065 +vn 0.290285 0.166171 -0.942402 +vn 0.471401 0.153144 -0.868521 +vn 0.634393 0.134232 -0.761267 +vn 0.773011 0.110161 -0.624755 +vn 0.881922 0.081857 -0.464233 +vn -0.000000 0.984796 0.173713 +vn 0.995185 0.017020 -0.096527 +vn 0.956941 0.050407 -0.285872 +vn -0.000011 -0.984808 -0.173648 +vn 0.995185 -0.017020 0.096529 +vn 0.956940 -0.050407 0.285875 +vn 0.881920 -0.081858 0.464237 +vn 0.773011 -0.110161 0.624755 +vn 0.634396 -0.134231 0.761265 +vn 0.471389 -0.153145 0.868527 +vn 0.290287 -0.166171 0.942402 +vn 0.098019 -0.172812 0.980065 +vn -0.098021 -0.172812 0.980065 +vn -0.290282 -0.166171 0.942403 +vn -0.471402 -0.153144 0.868520 +vn -0.634395 -0.134232 0.761265 +vn -0.773010 -0.110161 0.624756 +vn -0.881922 -0.081857 0.464234 +vn -0.956940 -0.050408 0.285875 +vn -0.995185 -0.017020 0.096527 +vn -0.956940 0.050408 -0.285876 +vn -0.881921 0.081857 -0.464236 +vn -0.773009 0.110162 -0.624757 +vn -0.634391 0.134232 -0.761269 +vn -0.471398 0.153144 -0.868522 +vn -0.290282 0.166171 -0.942403 +vn -0.098016 0.172812 -0.980066 +vn 0.098018 0.172812 -0.980065 +vn 0.290287 0.166171 -0.942402 +vn 0.471399 0.153144 -0.868522 +vn 0.773014 0.110161 -0.624751 +vn 0.881921 0.081857 -0.464236 +vn -0.000000 0.984812 0.173622 +vn -0.000000 0.984812 0.173623 +vn -0.000000 0.984801 0.173685 +vn -0.000000 0.984814 0.173615 +vn -0.000000 0.984810 0.173634 +vn -0.000000 0.984808 0.173649 +vn -0.000000 0.984806 0.173660 +vn -0.000043 0.984788 0.173763 +vn -0.000000 0.984797 0.173710 +vn -0.000000 0.984805 0.173661 +vn -0.000000 0.984810 0.173635 +vn -0.000000 0.984810 0.173637 +vn -0.000000 0.984802 0.173683 +vn -0.000000 0.984814 0.173611 +vn -0.000000 0.984800 0.173689 +vn -0.000000 0.984800 0.173690 +vn -0.000000 0.984801 0.173686 +vn 0.000005 0.984810 0.173637 +vn 0.956941 0.050407 -0.285873 +vn -0.000000 -0.984818 -0.173587 +vn -0.000007 -0.984807 -0.173654 +vn -0.000000 -0.984808 -0.173648 +vn -0.000027 -0.984801 -0.173685 +vn -0.000004 -0.984807 -0.173652 +vn -0.000010 -0.984800 -0.173693 +vn -0.000020 -0.984817 -0.173596 +vn -0.000013 -0.984810 -0.173638 +vn -0.000001 -0.984807 -0.173650 +vn -0.000005 -0.984808 -0.173646 +vn -0.000002 -0.984808 -0.173648 +vn 0.000002 -0.984808 -0.173649 +vn 0.000001 -0.984808 -0.173649 +vn -0.000011 -0.984809 -0.173642 +vn -0.000004 -0.984808 -0.173646 +vn -0.000001 -0.984808 -0.173648 +vn -0.000002 -0.984808 -0.173649 +vn -0.000006 -0.984808 -0.173649 +vn 0.000004 -0.984808 -0.173648 +vn 0.000000 -0.984808 -0.173649 +vn -0.000004 -0.984807 -0.173650 +vn 0.000005 -0.984808 -0.173646 +vn 0.000003 -0.984808 -0.173647 +vn 0.000008 -0.984805 -0.173663 +s off +f 1/1/1 2/2/1 4/3/1 +f 3/1/2 4/2/2 6/3/2 +f 5/1/3 6/2/3 8/3/3 +f 7/1/4 8/2/4 10/3/4 +f 9/1/5 10/2/5 12/3/5 +f 11/1/6 12/2/6 14/3/6 +f 13/1/7 14/2/7 16/3/7 +f 15/1/8 16/2/8 18/3/8 +f 17/1/9 18/2/9 19/4/9 +f 19/1/10 20/2/10 21/4/10 +f 21/1/11 22/2/11 23/4/11 +f 23/1/12 24/2/12 25/4/12 +f 25/1/13 26/2/13 27/4/13 +f 27/1/14 28/2/14 29/4/14 +f 29/1/15 30/2/15 31/4/15 +f 31/1/16 32/2/16 33/4/16 +f 33/1/17 34/2/17 35/4/17 +f 35/1/18 36/2/18 37/4/18 +f 37/1/19 38/2/19 39/4/19 +f 39/1/20 40/2/20 41/4/20 +f 41/1/21 42/2/21 43/4/21 +f 43/1/22 44/2/22 45/4/22 +f 45/1/23 46/2/23 47/4/23 +f 47/1/24 48/2/24 49/4/24 +f 49/1/25 50/2/25 52/3/25 +f 51/1/26 52/2/26 54/3/26 +f 53/1/27 54/2/27 56/3/27 +f 55/1/28 56/2/28 58/3/28 +f 57/1/29 58/2/29 60/3/29 +f 59/1/30 60/2/30 62/3/30 +f 48/5/31 52/6/31 50/7/31 +f 63/1/32 64/2/32 2/3/32 +f 61/1/33 62/2/33 64/3/33 +f 1/8/34 3/9/34 63/10/34 +f 3/4/35 1/1/35 4/3/35 +f 5/4/36 3/1/36 6/3/36 +f 7/4/37 5/1/37 8/3/37 +f 9/4/38 7/1/38 10/3/38 +f 11/4/39 9/1/39 12/3/39 +f 13/4/40 11/1/40 14/3/40 +f 15/4/41 13/1/41 16/3/41 +f 17/4/42 15/1/42 18/3/42 +f 18/2/43 20/3/43 19/4/43 +f 20/2/44 22/3/44 21/4/44 +f 22/2/45 24/3/45 23/4/45 +f 24/2/46 26/3/46 25/4/46 +f 26/2/47 28/3/47 27/4/47 +f 28/2/48 30/3/48 29/4/48 +f 30/2/49 32/3/49 31/4/49 +f 32/2/50 34/3/50 33/4/50 +f 34/2/17 36/3/17 35/4/17 +f 36/2/51 38/3/51 37/4/51 +f 38/2/52 40/3/52 39/4/52 +f 40/2/53 42/3/53 41/4/53 +f 42/2/54 44/3/54 43/4/54 +f 44/2/55 46/3/55 45/4/55 +f 46/2/56 48/3/56 47/4/56 +f 48/2/57 50/3/57 49/4/57 +f 51/4/58 49/1/58 52/3/58 +f 53/4/59 51/1/59 54/3/59 +f 55/4/60 53/1/60 56/3/60 +f 57/4/28 55/1/28 58/3/28 +f 59/4/61 57/1/61 60/3/61 +f 61/4/62 59/1/62 62/3/62 +f 48/5/63 54/11/63 52/6/63 +f 46/12/64 54/11/64 48/5/64 +f 46/12/65 56/13/65 54/11/65 +f 44/14/65 56/13/65 46/12/65 +f 44/14/66 58/15/66 56/13/66 +f 42/16/66 58/15/66 44/14/66 +f 42/16/67 60/17/67 58/15/67 +f 40/18/67 60/17/67 42/16/67 +f 40/18/68 62/19/68 60/17/68 +f 38/20/68 62/19/68 40/18/68 +f 38/20/69 64/21/69 62/19/69 +f 20/22/70 24/23/70 22/24/70 +f 16/25/71 20/22/71 18/26/71 +f 16/25/72 24/23/72 20/22/72 +f 36/27/69 64/21/69 38/20/69 +f 34/28/73 64/21/73 36/27/73 +f 2/9/74 64/21/74 34/28/74 +f 4/8/75 2/9/75 34/28/75 +f 4/8/75 34/28/75 32/29/75 +f 6/10/76 4/8/76 32/29/76 +f 6/10/76 32/29/76 30/30/76 +f 6/10/68 30/30/68 28/31/68 +f 8/32/68 6/10/68 28/31/68 +f 10/33/73 8/32/73 28/31/73 +f 10/33/67 28/31/67 26/34/67 +f 12/35/77 10/33/77 26/34/77 +f 12/35/78 26/34/78 24/23/78 +f 14/36/79 12/35/79 24/23/79 +f 16/25/80 14/36/80 24/23/80 +f 1/4/32 63/1/32 2/3/32 +f 63/4/81 61/1/81 64/3/81 +f 19/7/82 15/11/82 17/6/82 +f 41/31/83 37/29/83 39/30/83 +f 3/9/84 5/21/84 63/10/84 +f 45/23/85 41/31/85 43/34/85 +f 45/23/86 37/29/86 41/31/86 +f 49/22/87 45/23/87 47/24/87 +f 49/22/84 37/29/84 45/23/84 +f 49/22/84 35/28/84 37/29/84 +f 49/22/84 33/27/84 35/28/84 +f 49/22/84 31/20/84 33/27/84 +f 55/36/88 51/26/88 53/25/88 +f 59/33/89 55/36/89 57/35/89 +f 61/32/90 55/36/90 59/33/90 +f 63/10/91 55/36/91 61/32/91 +f 63/10/92 5/21/92 55/36/92 +f 5/21/93 7/19/93 55/36/93 +f 7/19/94 9/17/94 55/36/94 +f 29/18/95 25/14/95 27/16/95 +f 31/20/96 25/14/96 29/18/96 +f 49/22/97 25/14/97 31/20/97 +f 49/22/98 23/12/98 25/14/98 +f 9/17/93 11/15/93 55/36/93 +f 11/15/99 13/13/99 55/36/99 +f 13/13/100 15/11/100 55/36/100 +f 15/11/101 19/7/101 55/36/101 +f 19/7/102 21/5/102 55/36/102 +f 21/5/103 23/12/103 55/36/103 +f 23/12/104 49/22/104 55/36/104 +f 49/22/105 51/26/105 55/36/105 +v 0.053672 2.252534 1.125439 +v 0.053673 8.161380 0.083549 +v 0.051751 2.255921 1.144652 +v 0.051751 8.164768 0.102762 +v 0.046060 2.259179 1.163126 +v 0.046061 8.168025 0.121236 +v 0.036819 2.262181 1.180152 +v 0.036820 8.171027 0.138262 +v 0.024383 2.264812 1.195075 +v 0.024384 8.173658 0.153186 +v 0.009229 2.266972 1.207323 +v 0.009230 8.175818 0.165433 +v -0.008059 2.268577 1.216423 +v -0.008059 8.177423 0.174534 +v -0.026819 2.269565 1.222028 +v -0.026818 8.178411 0.180138 +v -0.046328 2.269898 1.223920 +v -0.046327 8.178745 0.182030 +v -0.065837 2.269565 1.222027 +v -0.065836 8.178411 0.180138 +v -0.084596 2.268577 1.216423 +v -0.084595 8.177423 0.174534 +v -0.101885 2.266972 1.207323 +v -0.101884 8.175818 0.165433 +v -0.117038 2.264812 1.195075 +v -0.117038 8.173658 0.153186 +v -0.129475 2.262181 1.180152 +v -0.129474 8.171027 0.138262 +v -0.138716 2.259179 1.163126 +v -0.138715 8.168025 0.121236 +v -0.144406 2.255921 1.144651 +v -0.144406 8.164768 0.102762 +v -0.146328 2.252534 1.125439 +v -0.146327 8.161380 0.083549 +v -0.144406 2.249146 1.106226 +v -0.144406 8.157992 0.064337 +v -0.138716 2.245888 1.087752 +v -0.138715 8.154735 0.045862 +v -0.129475 2.242886 1.070726 +v -0.129474 8.151732 0.028836 +v -0.117038 2.240255 1.055802 +v -0.117038 8.149101 0.013913 +v -0.101885 2.238095 1.043555 +v -0.101884 8.146942 0.001666 +v -0.084596 2.236491 1.034454 +v -0.084595 8.145337 -0.007435 +v -0.065837 2.235502 1.028850 +v -0.065836 8.144349 -0.013039 +v -0.046328 2.235169 1.026958 +v -0.046327 8.144015 -0.014931 +v -0.026819 2.235502 1.028850 +v -0.026818 8.144349 -0.013039 +v -0.008059 2.236491 1.034455 +v -0.008059 8.145337 -0.007435 +v 0.009229 2.238095 1.043555 +v 0.009230 8.146942 0.001666 +v 0.024383 2.240255 1.055803 +v 0.024384 8.149101 0.013913 +v 0.036819 2.242886 1.070726 +v 0.036820 8.151732 0.028837 +v 0.046060 2.245888 1.087752 +v 0.046061 8.154735 0.045863 +v 0.051751 2.249146 1.106226 +v 0.051752 8.157992 0.064337 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.308659 0.961940 +vt 0.222215 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 0.990393 0.402455 +vt 1.000000 0.500000 +vt 0.853553 0.146447 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.691342 0.038060 +vt 0.777785 0.084265 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.146447 0.853554 +vt 0.084266 0.777786 +vt 0.038060 0.691342 +vt 0.308658 0.038060 +vt 0.402455 0.009607 +vt 0.146446 0.146447 +vt 0.222215 0.084265 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.009607 0.597546 +vt 0.000000 0.500000 +vn 0.995185 0.017020 0.096528 +vn 0.956940 0.050408 0.285877 +vn 0.881921 0.081857 0.464236 +vn 0.773010 0.110161 0.624756 +vn 0.634396 0.134232 0.761265 +vn 0.471397 0.153144 0.868523 +vn 0.290283 0.166171 0.942403 +vn 0.098018 0.172812 0.980066 +vn -0.098018 0.172812 0.980066 +vn -0.290285 0.166171 0.942402 +vn -0.471398 0.153144 0.868522 +vn -0.634392 0.134232 0.761268 +vn -0.773007 0.110162 0.624759 +vn -0.881923 0.081857 0.464231 +vn -0.956941 0.050407 0.285874 +vn -0.995185 0.017021 0.096528 +vn -0.995185 -0.017021 -0.096529 +vn -0.956940 -0.050408 -0.285877 +vn -0.881921 -0.081857 -0.464236 +vn -0.773008 -0.110162 -0.624758 +vn -0.634391 -0.134232 -0.761269 +vn -0.471388 -0.153145 -0.868528 +vn -0.290288 -0.166171 -0.942401 +vn -0.098006 -0.172812 -0.980067 +vn 0.098021 -0.172812 -0.980065 +vn 0.290285 -0.166171 -0.942402 +vn 0.471402 -0.153144 -0.868520 +vn 0.634392 -0.134232 -0.761267 +vn 0.773010 -0.110161 -0.624755 +vn 0.881922 -0.081857 -0.464233 +vn -0.000356 0.984796 -0.173717 +vn 0.995185 -0.017020 -0.096527 +vn 0.956941 -0.050407 -0.285872 +vn -0.000005 -0.984808 0.173646 +vn 0.995185 0.017020 0.096529 +vn 0.956940 0.050407 0.285875 +vn 0.881920 0.081858 0.464237 +vn 0.773011 0.110161 0.624755 +vn 0.634396 0.134231 0.761264 +vn 0.471389 0.153145 0.868527 +vn 0.290292 0.166171 0.942400 +vn 0.098013 0.172812 0.980066 +vn -0.098019 0.172812 0.980065 +vn -0.290283 0.166171 0.942403 +vn -0.471400 0.153144 0.868521 +vn -0.634396 0.134232 0.761264 +vn -0.773010 0.110161 0.624756 +vn -0.881922 0.081857 0.464234 +vn -0.956940 0.050408 0.285876 +vn -0.995185 0.017020 0.096526 +vn -0.995185 -0.017021 -0.096530 +vn -0.956940 -0.050407 -0.285875 +vn -0.773009 -0.110161 -0.624757 +vn -0.634390 -0.134232 -0.761269 +vn -0.471399 -0.153144 -0.868522 +vn -0.290282 -0.166171 -0.942403 +vn -0.098018 -0.172812 -0.980066 +vn 0.098018 -0.172812 -0.980065 +vn 0.290287 -0.166171 -0.942402 +vn 0.471399 -0.153144 -0.868522 +vn 0.634393 -0.134232 -0.761267 +vn 0.773014 -0.110161 -0.624751 +vn 0.881921 -0.081857 -0.464236 +vn -0.000114 0.984812 -0.173626 +vn -0.000002 0.984808 -0.173648 +vn -0.000054 0.984802 -0.173678 +vn -0.000008 0.984808 -0.173648 +vn -0.000004 0.984808 -0.173647 +vn -0.000107 0.984836 -0.173487 +vn -0.000015 0.984812 -0.173622 +vn -0.000013 0.984820 -0.173580 +vn -0.000001 0.984809 -0.173639 +vn -0.000044 0.984787 -0.173766 +vn -0.000047 0.984804 -0.173672 +vn -0.000051 0.984803 -0.173676 +vn -0.000050 0.984803 -0.173675 +vn -0.000006 0.984799 -0.173697 +vn -0.000037 0.984791 -0.173743 +vn -0.000244 0.984806 -0.173659 +vn -0.000025 0.984806 -0.173659 +vn -0.000168 0.984823 -0.173560 +vn -0.000013 0.984812 -0.173624 +vn -0.000088 0.984819 -0.173584 +vn -0.000036 0.984813 -0.173618 +vn 0.000016 0.984808 -0.173645 +vn 0.000005 0.984808 -0.173650 +vn 0.000006 0.984807 -0.173650 +vn 0.000005 0.984808 -0.173649 +vn 0.000002 0.984808 -0.173648 +vn 0.000003 0.984807 -0.173650 +vn 0.000005 0.984804 -0.173667 +vn 0.956941 -0.050407 -0.285873 +vn -0.000005 -0.984808 0.173649 +vn -0.000001 -0.984818 0.173592 +vn -0.000000 -0.984808 0.173648 +vn -0.000006 -0.984808 0.173646 +vn -0.000001 -0.984808 0.173648 +vn -0.000002 -0.984808 0.173648 +vn -0.000001 -0.984806 0.173661 +vn -0.000000 -0.984805 0.173663 +vn -0.000000 -0.984810 0.173635 +vn -0.000000 -0.984810 0.173634 +vn -0.000000 -0.984807 0.173653 +vn -0.000000 -0.984807 0.173650 +vn -0.000000 -0.984808 0.173647 +vn 0.000000 -0.984807 0.173651 +vn -0.000000 -0.984807 0.173649 +vn -0.000000 -0.984810 0.173637 +vn 0.000000 -0.984806 0.173659 +vn -0.000001 -0.984805 0.173664 +s off +f 65/37/106 66/38/106 68/39/106 +f 67/37/107 68/38/107 70/39/107 +f 69/37/108 70/38/108 72/39/108 +f 71/37/109 72/38/109 74/39/109 +f 73/37/110 74/38/110 76/39/110 +f 75/37/111 76/38/111 78/39/111 +f 77/37/112 78/38/112 80/39/112 +f 79/37/113 80/38/113 82/39/113 +f 81/37/114 82/38/114 83/40/114 +f 83/37/115 84/38/115 85/40/115 +f 85/37/116 86/38/116 87/40/116 +f 87/37/117 88/38/117 89/40/117 +f 89/37/118 90/38/118 91/40/118 +f 91/37/119 92/38/119 93/40/119 +f 93/37/120 94/38/120 95/40/120 +f 95/37/121 96/38/121 97/40/121 +f 97/37/122 98/38/122 99/40/122 +f 99/37/123 100/38/123 101/40/123 +f 101/37/124 102/38/124 103/40/124 +f 103/37/125 104/38/125 105/40/125 +f 105/37/126 106/38/126 107/40/126 +f 107/37/127 108/38/127 109/40/127 +f 109/37/128 110/38/128 111/40/128 +f 111/37/129 112/38/129 113/40/129 +f 113/37/130 114/38/130 116/39/130 +f 115/37/131 116/38/131 118/39/131 +f 117/37/132 118/38/132 120/39/132 +f 119/37/133 120/38/133 122/39/133 +f 121/37/134 122/38/134 124/39/134 +f 123/37/135 124/38/135 126/39/135 +f 68/41/136 66/42/136 70/43/136 +f 127/37/137 128/38/137 66/39/137 +f 125/37/138 126/38/138 128/39/138 +f 65/41/139 67/42/139 69/44/139 +f 67/40/140 65/37/140 68/39/140 +f 69/40/141 67/37/141 70/39/141 +f 71/40/142 69/37/142 72/39/142 +f 73/40/143 71/37/143 74/39/143 +f 75/40/144 73/37/144 76/39/144 +f 77/40/145 75/37/145 78/39/145 +f 79/40/146 77/37/146 80/39/146 +f 81/40/147 79/37/147 82/39/147 +f 82/38/148 84/39/148 83/40/148 +f 84/38/149 86/39/149 85/40/149 +f 86/38/150 88/39/150 87/40/150 +f 88/38/151 90/39/151 89/40/151 +f 90/38/152 92/39/152 91/40/152 +f 92/38/153 94/39/153 93/40/153 +f 94/38/154 96/39/154 95/40/154 +f 96/38/155 98/39/155 97/40/155 +f 98/38/156 100/39/156 99/40/156 +f 100/38/157 102/39/157 101/40/157 +f 102/38/124 104/39/124 103/40/124 +f 104/38/158 106/39/158 105/40/158 +f 106/38/159 108/39/159 107/40/159 +f 108/38/160 110/39/160 109/40/160 +f 110/38/161 112/39/161 111/40/161 +f 112/38/162 114/39/162 113/40/162 +f 115/40/163 113/37/163 116/39/163 +f 117/40/164 115/37/164 118/39/164 +f 119/40/165 117/37/165 120/39/165 +f 121/40/166 119/37/166 122/39/166 +f 123/40/167 121/37/167 124/39/167 +f 125/40/168 123/37/168 126/39/168 +f 66/42/169 128/44/169 126/45/169 +f 70/43/170 66/42/170 126/45/170 +f 72/46/171 70/43/171 74/47/171 +f 70/43/172 126/45/172 74/47/172 +f 126/45/173 124/48/173 74/47/173 +f 122/49/174 120/50/174 118/51/174 +f 124/48/175 122/49/175 118/51/175 +f 114/52/176 118/51/176 116/53/176 +f 124/48/177 118/51/177 114/52/177 +f 108/54/178 112/55/178 110/56/178 +f 104/57/179 108/54/179 106/58/179 +f 102/59/180 108/54/180 104/57/180 +f 100/60/181 108/54/181 102/59/181 +f 76/61/182 74/47/182 78/62/182 +f 78/62/183 74/47/183 80/63/183 +f 96/64/184 100/60/184 98/65/184 +f 96/64/185 108/54/185 100/60/185 +f 92/66/186 96/64/186 94/67/186 +f 90/68/187 96/64/187 92/66/187 +f 88/69/188 96/64/188 90/68/188 +f 86/70/189 96/64/189 88/69/189 +f 82/71/176 86/70/176 84/72/176 +f 74/47/190 124/48/190 80/63/190 +f 124/48/191 114/52/191 80/63/191 +f 114/52/192 112/55/192 80/63/192 +f 112/55/193 108/54/193 80/63/193 +f 108/54/194 96/64/194 80/63/194 +f 96/64/195 86/70/195 80/63/195 +f 86/70/196 82/71/196 80/63/196 +f 65/40/137 127/37/137 66/39/137 +f 127/40/197 125/37/197 128/39/197 +f 127/43/198 65/41/198 125/46/198 +f 83/52/199 79/51/199 81/53/199 +f 97/60/200 93/57/200 95/59/200 +f 99/65/200 93/57/200 97/60/200 +f 103/67/201 99/65/201 101/64/201 +f 103/67/202 93/57/202 99/65/202 +f 105/66/203 93/57/203 103/67/203 +f 115/71/199 111/70/199 113/72/199 +f 115/71/204 109/69/204 111/70/204 +f 117/63/205 109/69/205 115/71/205 +f 117/63/206 107/68/206 109/69/206 +f 119/62/207 107/68/207 117/63/207 +f 119/62/208 105/66/208 107/68/208 +f 121/61/208 105/66/208 119/62/208 +f 123/47/200 105/66/200 121/61/200 +f 125/46/209 105/66/209 123/47/209 +f 125/46/200 65/41/200 105/66/200 +f 105/66/200 65/41/200 93/57/200 +f 65/41/210 69/44/210 93/57/210 +f 69/44/209 71/45/209 93/57/209 +f 93/57/200 71/45/200 91/58/200 +f 91/58/211 71/45/211 89/54/211 +f 71/45/212 73/48/212 89/54/212 +f 73/48/211 75/49/211 89/54/211 +f 89/54/208 75/49/208 87/56/208 +f 75/49/213 77/50/213 87/56/213 +f 87/56/207 77/50/207 85/55/207 +f 77/50/214 79/51/214 85/55/214 +f 79/51/215 83/52/215 85/55/215 +v 1.116865 2.257815 -0.125221 +v 0.074976 8.166661 -0.125221 +v 1.136078 2.261203 -0.123300 +v 0.094188 8.170050 -0.123300 +v 1.154552 2.264460 -0.117609 +v 0.112663 8.173306 -0.117609 +v 1.171578 2.267462 -0.108368 +v 0.129689 8.176309 -0.108368 +v 1.186502 2.270094 -0.095932 +v 0.144612 8.178940 -0.095932 +v 1.198749 2.272254 -0.080778 +v 0.156859 8.181100 -0.080778 +v 1.207850 2.273858 -0.063490 +v 0.165960 8.182705 -0.063490 +v 1.213454 2.274846 -0.044730 +v 0.171564 8.183693 -0.044730 +v 1.215346 2.275180 -0.025221 +v 0.173456 8.184027 -0.025221 +v 1.213454 2.274846 -0.005712 +v 0.171564 8.183693 -0.005712 +v 1.207850 2.273858 0.013047 +v 0.165960 8.182705 0.013047 +v 1.198749 2.272254 0.030336 +v 0.156859 8.181100 0.030336 +v 1.186502 2.270094 0.045489 +v 0.144612 8.178940 0.045489 +v 1.171578 2.267462 0.057926 +v 0.129689 8.176309 0.057926 +v 1.154552 2.264460 0.067167 +v 0.112663 8.173306 0.067167 +v 1.136078 2.261203 0.072857 +v 0.094188 8.170050 0.072857 +v 1.116865 2.257815 0.074779 +v 0.074976 8.166661 0.074779 +v 1.097653 2.254427 0.072857 +v 0.055763 8.163274 0.072857 +v 1.079178 2.251170 0.067167 +v 0.037289 8.160016 0.067167 +v 1.062152 2.248168 0.057926 +v 0.020263 8.157014 0.057926 +v 1.047229 2.245536 0.045489 +v 0.005339 8.154383 0.045489 +v 1.034981 2.243377 0.030336 +v -0.006908 8.152224 0.030336 +v 1.025881 2.241772 0.013047 +v -0.016009 8.150619 0.013047 +v 1.020277 2.240784 -0.005712 +v -0.021613 8.149631 -0.005712 +v 1.018384 2.240450 -0.025221 +v -0.023505 8.149297 -0.025221 +v 1.020277 2.240784 -0.044730 +v -0.021613 8.149631 -0.044730 +v 1.025881 2.241772 -0.063490 +v -0.016009 8.150619 -0.063490 +v 1.034981 2.243377 -0.080778 +v -0.006908 8.152224 -0.080778 +v 1.047229 2.245536 -0.095932 +v 0.005339 8.154383 -0.095932 +v 1.062152 2.248168 -0.108368 +v 0.020263 8.157014 -0.108368 +v 1.079178 2.251170 -0.117609 +v 0.037289 8.160016 -0.117609 +v 1.097653 2.254427 -0.123300 +v 0.055763 8.163274 -0.123300 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.000000 0.500000 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.146447 0.853554 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.308659 0.961940 +vt 0.222215 0.915735 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.009607 0.597546 +vt 0.084265 0.222215 +vn 0.096528 0.017020 -0.995185 +vn 0.285876 0.050408 -0.956940 +vn 0.464235 0.081857 -0.881921 +vn 0.624757 0.110162 -0.773009 +vn 0.761264 0.134232 -0.634396 +vn 0.868521 0.153144 -0.471400 +vn 0.942403 0.166171 -0.290283 +vn 0.980066 0.172812 -0.098018 +vn 0.980066 0.172812 0.098018 +vn 0.942403 0.166171 0.290283 +vn 0.868521 0.153144 0.471399 +vn 0.761264 0.134232 0.634396 +vn 0.624757 0.110162 0.773009 +vn 0.464235 0.081857 0.881921 +vn 0.285876 0.050408 0.956940 +vn 0.096527 0.017020 0.995185 +vn -0.096528 -0.017021 0.995185 +vn -0.285875 -0.050407 0.956940 +vn -0.464235 -0.081857 0.881921 +vn -0.624754 -0.110161 0.773012 +vn -0.761268 -0.134232 0.634391 +vn -0.868524 -0.153144 0.471394 +vn -0.942401 -0.166171 0.290288 +vn -0.980066 -0.172812 0.098010 +vn -0.980065 -0.172812 -0.098018 +vn -0.942402 -0.166171 -0.290285 +vn -0.868521 -0.153144 -0.471401 +vn -0.761266 -0.134232 -0.634394 +vn -0.624751 -0.110161 -0.773014 +vn -0.464236 -0.081857 -0.881921 +vn -0.173717 0.984796 0.000356 +vn -0.096527 -0.017020 -0.995185 +vn -0.285873 -0.050407 -0.956941 +vn 0.173648 -0.984808 -0.000011 +vn 0.285874 0.050407 -0.956940 +vn 0.464237 0.081858 -0.881920 +vn 0.624752 0.110161 -0.773013 +vn 0.761267 0.134232 -0.634393 +vn 0.868524 0.153144 -0.471394 +vn 0.942402 0.166171 -0.290287 +vn 0.980065 0.172812 -0.098019 +vn 0.980065 0.172812 0.098019 +vn 0.942402 0.166171 0.290287 +vn 0.868524 0.153144 0.471394 +vn 0.761267 0.134232 0.634393 +vn 0.624752 0.110161 0.773013 +vn 0.464237 0.081858 0.881920 +vn 0.285874 0.050407 0.956940 +vn -0.096529 -0.017021 0.995185 +vn -0.285874 -0.050407 0.956940 +vn -0.624758 -0.110162 0.773008 +vn -0.761268 -0.134232 0.634392 +vn -0.868523 -0.153144 0.471397 +vn -0.942402 -0.166171 0.290284 +vn -0.980066 -0.172812 0.098018 +vn -0.980065 -0.172812 -0.098021 +vn -0.868521 -0.153144 -0.471400 +vn -0.761267 -0.134232 -0.634392 +vn -0.624756 -0.110161 -0.773010 +vn -0.464234 -0.081857 -0.881922 +vn -0.173640 0.984809 -0.000036 +vn -0.173648 0.984808 0.000040 +vn -0.173648 0.984808 0.000002 +vn -0.173650 0.984807 -0.000020 +vn -0.173652 0.984807 -0.000031 +vn -0.173635 0.984810 0.000027 +vn -0.173649 0.984808 -0.000009 +vn -0.173641 0.984809 0.000007 +vn -0.173650 0.984807 -0.000006 +vn -0.173640 0.984809 0.000006 +vn -0.173660 0.984806 -0.000014 +vn -0.173675 0.984803 0.000050 +vn -0.173675 0.984803 0.000049 +vn -0.173767 0.984787 -0.000044 +vn -0.173767 0.984787 0.000044 +vn -0.173642 0.984809 0.000000 +vn -0.173677 0.984803 0.000052 +vn -0.173717 0.984796 -0.000357 +vn -0.173646 0.984808 0.000005 +vn -0.173645 0.984808 0.000007 +vn -0.173703 0.984798 -0.000011 +vn -0.173684 0.984802 -0.000003 +vn -0.173667 0.984805 0.000002 +vn -0.173671 0.984804 -0.000015 +vn -0.173656 0.984806 -0.000026 +vn -0.173637 0.984810 0.000008 +vn -0.173643 0.984809 0.000005 +vn -0.096528 -0.017020 -0.995185 +vn -0.285872 -0.050407 -0.956941 +vn 0.173648 -0.984808 -0.000000 +vn 0.173647 -0.984808 0.000006 +vn 0.173647 -0.984808 0.000005 +vn 0.173652 -0.984807 -0.000009 +vn 0.173645 -0.984808 0.000005 +vn 0.173653 -0.984807 -0.000007 +vn 0.173643 -0.984809 0.000006 +vn 0.173652 -0.984807 -0.000003 +vn 0.173643 -0.984809 0.000004 +vn 0.173651 -0.984807 -0.000002 +vn 0.173650 -0.984807 -0.000001 +vn 0.173647 -0.984808 0.000000 +vn 0.173649 -0.984808 0.000000 +vn 0.173652 -0.984807 0.000001 +vn 0.173642 -0.984809 -0.000003 +vn 0.173653 -0.984807 0.000003 +vn 0.173642 -0.984809 -0.000004 +vn 0.173655 -0.984806 0.000007 +vn 0.173641 -0.984809 -0.000008 +vn 0.173658 -0.984806 0.000013 +vn 0.173645 -0.984808 -0.000007 +vn 0.173654 -0.984807 0.000007 +vn 0.173646 -0.984808 -0.000004 +s off +f 129/73/216 130/74/216 132/75/216 +f 131/73/217 132/74/217 134/75/217 +f 133/73/218 134/74/218 136/75/218 +f 135/73/219 136/74/219 138/75/219 +f 137/73/220 138/74/220 140/75/220 +f 139/73/221 140/74/221 142/75/221 +f 141/73/222 142/74/222 144/75/222 +f 143/73/223 144/74/223 146/75/223 +f 145/73/224 146/74/224 148/75/224 +f 147/73/225 148/74/225 150/75/225 +f 149/73/226 150/74/226 152/75/226 +f 151/73/227 152/74/227 154/75/227 +f 153/73/228 154/74/228 156/75/228 +f 155/73/229 156/74/229 158/75/229 +f 157/73/230 158/74/230 160/75/230 +f 159/73/231 160/74/231 162/75/231 +f 161/73/232 162/74/232 163/76/232 +f 163/73/233 164/74/233 165/76/233 +f 165/73/234 166/74/234 167/76/234 +f 167/73/235 168/74/235 169/76/235 +f 169/73/236 170/74/236 171/76/236 +f 171/73/237 172/74/237 173/76/237 +f 173/73/238 174/74/238 175/76/238 +f 175/73/239 176/74/239 177/76/239 +f 177/73/240 178/74/240 179/76/240 +f 179/73/241 180/74/241 181/76/241 +f 181/73/242 182/74/242 183/76/242 +f 183/73/243 184/74/243 185/76/243 +f 185/73/244 186/74/244 187/76/244 +f 187/73/245 188/74/245 189/76/245 +f 132/77/246 130/78/246 134/79/246 +f 191/73/247 192/74/247 129/76/247 +f 189/73/248 190/74/248 191/76/248 +f 129/77/249 131/78/249 191/79/249 +f 131/76/216 129/73/216 132/75/216 +f 133/76/250 131/73/250 134/75/250 +f 135/76/251 133/73/251 136/75/251 +f 137/76/252 135/73/252 138/75/252 +f 139/76/253 137/73/253 140/75/253 +f 141/76/254 139/73/254 142/75/254 +f 143/76/255 141/73/255 144/75/255 +f 145/76/256 143/73/256 146/75/256 +f 147/76/257 145/73/257 148/75/257 +f 149/76/258 147/73/258 150/75/258 +f 151/76/259 149/73/259 152/75/259 +f 153/76/260 151/73/260 154/75/260 +f 155/76/261 153/73/261 156/75/261 +f 157/76/262 155/73/262 158/75/262 +f 159/76/263 157/73/263 160/75/263 +f 161/76/231 159/73/231 162/75/231 +f 162/74/264 164/75/264 163/76/264 +f 164/74/265 166/75/265 165/76/265 +f 166/74/234 168/75/234 167/76/234 +f 168/74/266 170/75/266 169/76/266 +f 170/74/267 172/75/267 171/76/267 +f 172/74/268 174/75/268 173/76/268 +f 174/74/269 176/75/269 175/76/269 +f 176/74/270 178/75/270 177/76/270 +f 178/74/271 180/75/271 179/76/271 +f 180/74/241 182/75/241 181/76/241 +f 182/74/272 184/75/272 183/76/272 +f 184/74/273 186/75/273 185/76/273 +f 186/74/274 188/75/274 187/76/274 +f 188/74/275 190/75/275 189/76/275 +f 130/78/276 192/80/276 134/79/276 +f 192/80/277 190/81/277 134/79/277 +f 190/81/278 188/82/278 134/79/278 +f 188/82/279 186/83/279 134/79/279 +f 186/83/280 184/84/280 134/79/280 +f 184/84/281 182/85/281 134/79/281 +f 182/85/282 180/86/282 134/79/282 +f 180/86/283 178/87/283 134/79/283 +f 178/87/284 176/88/284 134/79/284 +f 176/88/285 174/89/285 134/79/285 +f 174/89/286 172/90/286 134/79/286 +f 170/91/287 168/92/287 166/93/287 +f 170/91/288 166/93/288 164/94/288 +f 148/95/289 152/96/289 150/97/289 +f 140/98/290 144/99/290 142/100/290 +f 172/90/287 170/91/287 164/94/287 +f 134/79/291 172/90/291 164/94/291 +f 136/101/292 134/79/292 138/102/292 +f 134/79/291 164/94/291 138/102/291 +f 162/103/293 160/104/293 158/105/293 +f 162/103/294 158/105/294 156/106/294 +f 146/107/295 152/96/295 148/95/295 +f 146/107/296 154/108/296 152/96/296 +f 144/99/297 154/108/297 146/107/297 +f 140/98/298 154/108/298 144/99/298 +f 138/102/299 164/94/299 140/98/299 +f 162/103/300 156/106/300 154/108/300 +f 164/94/301 162/103/301 140/98/301 +f 162/103/302 154/108/302 140/98/302 +f 192/74/303 130/75/303 129/76/303 +f 190/74/304 192/75/304 191/76/304 +f 131/78/305 133/80/305 191/79/305 +f 133/80/306 135/81/306 191/79/306 +f 135/81/305 137/82/305 191/79/305 +f 137/82/307 139/83/307 191/79/307 +f 139/83/308 141/84/308 191/79/308 +f 141/84/309 143/85/309 191/79/309 +f 143/85/310 145/86/310 191/79/310 +f 145/86/311 147/87/311 191/79/311 +f 147/87/312 149/88/312 191/79/312 +f 149/88/313 151/89/313 191/79/313 +f 151/89/314 153/90/314 191/79/314 +f 153/90/305 155/91/305 191/79/305 +f 155/91/315 157/92/315 191/79/315 +f 157/92/305 159/93/305 191/79/305 +f 159/93/316 161/94/316 191/79/316 +f 161/94/305 163/103/305 191/79/305 +f 163/103/305 165/104/305 191/79/305 +f 165/104/317 167/105/317 191/79/317 +f 167/105/317 169/106/317 191/79/317 +f 169/106/318 171/108/318 191/79/318 +f 171/108/319 173/96/319 191/79/319 +f 173/96/320 175/97/320 191/79/320 +f 175/97/321 177/95/321 191/79/321 +f 177/95/322 179/107/322 191/79/322 +f 179/107/323 181/99/323 191/79/323 +f 181/99/324 183/100/324 191/79/324 +f 183/100/325 185/98/325 191/79/325 +f 185/98/326 187/102/326 189/101/326 +f 191/79/327 185/98/327 189/101/327 +v -1.178897 2.277600 -0.129009 +v -0.137008 8.186446 -0.129009 +v -1.159685 2.274212 -0.127088 +v -0.117795 8.183058 -0.127088 +v -1.141210 2.270954 -0.121397 +v -0.099321 8.179801 -0.121397 +v -1.124184 2.267952 -0.112156 +v -0.082295 8.176799 -0.112156 +v -1.109261 2.265321 -0.099720 +v -0.067371 8.174168 -0.099720 +v -1.097013 2.263161 -0.084566 +v -0.055124 8.172008 -0.084566 +v -1.087913 2.261557 -0.067277 +v -0.046023 8.170403 -0.067277 +v -1.082309 2.260568 -0.048518 +v -0.040419 8.169415 -0.048518 +v -1.080417 2.260235 -0.029009 +v -0.038527 8.169081 -0.029009 +v -1.082309 2.260568 -0.009500 +v -0.040419 8.169415 -0.009500 +v -1.087913 2.261557 0.009259 +v -0.046023 8.170403 0.009259 +v -1.097013 2.263161 0.026548 +v -0.055124 8.172008 0.026548 +v -1.109261 2.265321 0.041702 +v -0.067371 8.174168 0.041702 +v -1.124184 2.267952 0.054138 +v -0.082295 8.176799 0.054138 +v -1.141210 2.270954 0.063379 +v -0.099321 8.179801 0.063379 +v -1.159685 2.274212 0.069069 +v -0.117795 8.183058 0.069069 +v -1.178897 2.277600 0.070991 +v -0.137008 8.186446 0.070991 +v -1.198110 2.280987 0.069069 +v -0.156220 8.189834 0.069069 +v -1.216584 2.284245 0.063379 +v -0.174695 8.193091 0.063379 +v -1.233610 2.287247 0.054138 +v -0.191721 8.196094 0.054138 +v -1.248534 2.289878 0.041702 +v -0.206644 8.198725 0.041702 +v -1.260781 2.292038 0.026548 +v -0.218892 8.200884 0.026548 +v -1.269882 2.293643 0.009259 +v -0.227992 8.202489 0.009259 +v -1.275486 2.294631 -0.009500 +v -0.233596 8.203477 -0.009500 +v -1.277378 2.294964 -0.029009 +v -0.235489 8.203811 -0.029009 +v -1.275486 2.294631 -0.048518 +v -0.233596 8.203477 -0.048518 +v -1.269882 2.293643 -0.067278 +v -0.227992 8.202489 -0.067278 +v -1.260781 2.292038 -0.084566 +v -0.218892 8.200884 -0.084566 +v -1.248534 2.289878 -0.099720 +v -0.206644 8.198725 -0.099720 +v -1.233610 2.287247 -0.112156 +v -0.191721 8.196094 -0.112156 +v -1.216584 2.284245 -0.121397 +v -0.174695 8.193091 -0.121397 +v -1.198110 2.280987 -0.127088 +v -0.156220 8.189834 -0.127088 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.000000 0.500000 +vt 0.009607 0.597546 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.146447 0.853554 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +vn 0.096528 -0.017020 -0.995185 +vn 0.285876 -0.050408 -0.956940 +vn 0.464236 -0.081857 -0.881921 +vn 0.624756 -0.110161 -0.773010 +vn 0.761265 -0.134232 -0.634395 +vn 0.868521 -0.153144 -0.471400 +vn 0.942403 -0.166171 -0.290282 +vn 0.980065 -0.172812 -0.098021 +vn 0.980066 -0.172812 0.098018 +vn 0.942402 -0.166171 0.290285 +vn 0.868522 -0.153144 0.471399 +vn 0.761265 -0.134232 0.634395 +vn 0.624756 -0.110161 0.773010 +vn 0.464235 -0.081857 0.881922 +vn 0.285876 -0.050408 0.956940 +vn 0.096527 -0.017020 0.995185 +vn -0.096528 0.017020 0.995185 +vn -0.285876 0.050408 0.956940 +vn -0.464234 0.081857 0.881922 +vn -0.624758 0.110162 0.773008 +vn -0.761264 0.134231 0.634396 +vn -0.868524 0.153144 0.471394 +vn -0.942401 0.166171 0.290288 +vn -0.980066 0.172812 0.098014 +vn -0.980066 0.172812 -0.098014 +vn -0.942403 0.166171 -0.290281 +vn -0.868522 0.153144 -0.471398 +vn -0.761268 0.134232 -0.634392 +vn -0.624751 0.110161 -0.773014 +vn -0.464236 0.081857 -0.881921 +vn 0.173717 0.984795 -0.000357 +vn -0.096527 0.017020 -0.995185 +vn -0.285874 0.050407 -0.956940 +vn -0.173648 -0.984808 0.000011 +vn 0.285874 -0.050407 -0.956940 +vn 0.464237 -0.081858 -0.881920 +vn 0.624755 -0.110161 -0.773011 +vn 0.761264 -0.134231 -0.634396 +vn 0.868524 -0.153144 -0.471394 +vn 0.942402 -0.166171 -0.290287 +vn 0.980066 -0.172812 -0.098013 +vn 0.980066 -0.172812 0.098013 +vn 0.942402 -0.166171 0.290287 +vn 0.868524 -0.153144 0.471394 +vn 0.761264 -0.134231 0.634396 +vn 0.624755 -0.110161 0.773011 +vn 0.464237 -0.081858 0.881920 +vn 0.285874 -0.050407 0.956940 +vn -0.096529 0.017021 0.995185 +vn -0.285874 0.050407 0.956940 +vn -0.464235 0.081857 0.881921 +vn -0.624756 0.110161 0.773010 +vn -0.761269 0.134232 0.634391 +vn -0.868522 0.153144 0.471398 +vn -0.942403 0.166171 0.290282 +vn -0.980066 0.172812 0.098018 +vn -0.980065 0.172812 -0.098019 +vn -0.942402 0.166171 -0.290285 +vn -0.868521 0.153144 -0.471400 +vn -0.761267 0.134232 -0.634392 +vn -0.624756 0.110161 -0.773010 +vn -0.464234 0.081857 -0.881922 +vn 0.173640 0.984809 0.000036 +vn 0.173648 0.984808 -0.000041 +vn 0.173648 0.984808 -0.000002 +vn 0.173650 0.984807 0.000019 +vn 0.173652 0.984807 0.000031 +vn 0.173635 0.984810 -0.000027 +vn 0.173649 0.984808 0.000009 +vn 0.173641 0.984809 -0.000007 +vn 0.173650 0.984807 0.000006 +vn 0.173641 0.984809 -0.000005 +vn 0.173660 0.984806 0.000014 +vn 0.173629 0.984811 -0.000011 +vn 0.173638 0.984810 -0.000005 +vn 0.173649 0.984808 0.000001 +vn 0.173659 0.984806 0.000005 +vn 0.173636 0.984810 -0.000002 +vn 0.173683 0.984802 0.000007 +vn 0.173611 0.984814 0.000000 +vn 0.173649 0.984808 0.000000 +vn 0.173635 0.984810 0.000001 +vn 0.173681 0.984802 -0.000008 +vn 0.173674 0.984803 -0.000006 +vn 0.173644 0.984808 0.000007 +vn 0.173666 0.984805 -0.000005 +vn 0.173677 0.984803 -0.000016 +vn 0.173633 0.984810 0.000028 +vn 0.173518 0.984831 0.000143 +vn 0.173638 0.984810 0.000022 +vn -0.096528 0.017020 -0.995185 +vn -0.285872 0.050407 -0.956941 +vn -0.173648 -0.984808 0.000000 +vn -0.173647 -0.984808 -0.000006 +vn -0.173648 -0.984808 -0.000002 +vn -0.173647 -0.984808 -0.000003 +vn -0.173652 -0.984807 0.000009 +vn -0.173645 -0.984808 -0.000005 +vn -0.173652 -0.984807 0.000005 +vn -0.173644 -0.984809 -0.000005 +vn -0.173652 -0.984807 0.000003 +vn -0.173643 -0.984809 -0.000004 +vn -0.173650 -0.984807 0.000001 +vn -0.173680 -0.984802 0.000024 +vn -0.173592 -0.984818 0.000023 +vn -0.173693 -0.984800 0.000010 +vn -0.173644 -0.984808 -0.000000 +vn -0.173638 -0.984810 0.000013 +vn -0.173653 -0.984807 -0.000003 +vn -0.173644 -0.984808 0.000011 +vn -0.173646 -0.984808 0.000007 +vn -0.173645 -0.984808 -0.000007 +vn -0.173651 -0.984807 0.000003 +vn -0.173649 -0.984808 0.000000 +vn -0.173648 -0.984808 0.000001 +vn -0.173647 -0.984808 0.000002 +vn -0.173640 -0.984809 -0.000005 +vn -0.173647 -0.984808 -0.000001 +s off +f 193/109/328 194/110/328 196/111/328 +f 195/109/329 196/110/329 198/111/329 +f 197/109/330 198/110/330 200/111/330 +f 199/109/331 200/110/331 202/111/331 +f 201/109/332 202/110/332 204/111/332 +f 203/109/333 204/110/333 206/111/333 +f 205/109/334 206/110/334 208/111/334 +f 207/109/335 208/110/335 210/111/335 +f 209/109/336 210/110/336 212/111/336 +f 211/109/337 212/110/337 214/111/337 +f 213/109/338 214/110/338 216/111/338 +f 215/109/339 216/110/339 218/111/339 +f 217/109/340 218/110/340 220/111/340 +f 219/109/341 220/110/341 222/111/341 +f 221/109/342 222/110/342 224/111/342 +f 223/109/343 224/110/343 226/111/343 +f 225/109/344 226/110/344 227/112/344 +f 227/109/345 228/110/345 229/112/345 +f 229/109/346 230/110/346 231/112/346 +f 231/109/347 232/110/347 233/112/347 +f 233/109/348 234/110/348 235/112/348 +f 235/109/349 236/110/349 237/112/349 +f 237/109/350 238/110/350 239/112/350 +f 239/109/351 240/110/351 241/112/351 +f 241/109/352 242/110/352 243/112/352 +f 243/109/353 244/110/353 245/112/353 +f 245/109/354 246/110/354 247/112/354 +f 247/109/355 248/110/355 249/112/355 +f 249/109/356 250/110/356 251/112/356 +f 251/109/357 252/110/357 253/112/357 +f 196/113/358 194/114/358 198/115/358 +f 255/109/359 256/110/359 193/112/359 +f 253/109/360 254/110/360 255/112/360 +f 193/113/361 195/114/361 255/115/361 +f 195/112/328 193/109/328 196/111/328 +f 197/112/362 195/109/362 198/111/362 +f 199/112/363 197/109/363 200/111/363 +f 201/112/364 199/109/364 202/111/364 +f 203/112/365 201/109/365 204/111/365 +f 205/112/366 203/109/366 206/111/366 +f 207/112/367 205/109/367 208/111/367 +f 209/112/368 207/109/368 210/111/368 +f 211/112/369 209/109/369 212/111/369 +f 213/112/370 211/109/370 214/111/370 +f 215/112/371 213/109/371 216/111/371 +f 217/112/372 215/109/372 218/111/372 +f 219/112/373 217/109/373 220/111/373 +f 221/112/374 219/109/374 222/111/374 +f 223/112/375 221/109/375 224/111/375 +f 225/112/343 223/109/343 226/111/343 +f 226/110/376 228/111/376 227/112/376 +f 228/110/377 230/111/377 229/112/377 +f 230/110/378 232/111/378 231/112/378 +f 232/110/379 234/111/379 233/112/379 +f 234/110/380 236/111/380 235/112/380 +f 236/110/381 238/111/381 237/112/381 +f 238/110/382 240/111/382 239/112/382 +f 240/110/383 242/111/383 241/112/383 +f 242/110/384 244/111/384 243/112/384 +f 244/110/385 246/111/385 245/112/385 +f 246/110/386 248/111/386 247/112/386 +f 248/110/387 250/111/387 249/112/387 +f 250/110/388 252/111/388 251/112/388 +f 252/110/389 254/111/389 253/112/389 +f 194/114/390 256/116/390 198/115/390 +f 256/116/391 254/117/391 198/115/391 +f 254/117/392 252/118/392 198/115/392 +f 252/118/393 250/119/393 198/115/393 +f 250/119/394 248/120/394 198/115/394 +f 248/120/395 246/121/395 198/115/395 +f 246/121/396 244/122/396 198/115/396 +f 244/122/397 242/123/397 198/115/397 +f 242/123/398 240/124/398 198/115/398 +f 240/124/399 238/125/399 198/115/399 +f 238/125/400 236/126/400 198/115/400 +f 236/126/401 234/127/401 198/115/401 +f 234/127/402 232/128/402 198/115/402 +f 232/128/403 230/129/403 198/115/403 +f 230/129/404 228/130/404 198/115/404 +f 228/130/405 226/131/405 198/115/405 +f 226/131/406 224/132/406 198/115/406 +f 224/132/407 222/133/407 198/115/407 +f 222/133/408 220/134/408 198/115/408 +f 220/134/409 218/135/409 198/115/409 +f 218/135/410 216/136/410 198/115/410 +f 216/136/411 214/137/411 198/115/411 +f 214/137/412 212/138/412 198/115/412 +f 212/138/413 210/139/413 198/115/413 +f 210/139/398 208/140/398 198/115/398 +f 208/140/414 206/141/414 198/115/414 +f 206/141/415 204/142/415 198/115/415 +f 204/142/416 202/143/416 200/144/416 +f 198/115/417 204/142/417 200/144/417 +f 256/110/418 194/111/418 193/112/418 +f 254/110/419 256/111/419 255/112/419 +f 195/114/420 197/116/420 255/115/420 +f 197/116/421 199/117/421 255/115/421 +f 199/117/422 201/118/422 255/115/422 +f 201/118/423 203/119/423 255/115/423 +f 203/119/424 205/120/424 255/115/424 +f 205/120/425 207/121/425 255/115/425 +f 207/121/426 209/122/426 255/115/426 +f 209/122/427 211/123/427 255/115/427 +f 211/123/428 213/124/428 255/115/428 +f 213/124/429 215/125/429 255/115/429 +f 215/125/430 217/126/430 255/115/430 +f 237/136/431 233/134/431 235/135/431 +f 247/141/432 243/139/432 245/140/432 +f 241/138/433 237/136/433 239/137/433 +f 241/138/434 233/134/434 237/136/434 +f 251/143/435 247/141/435 249/142/435 +f 251/143/436 243/139/436 247/141/436 +f 219/127/437 221/128/437 223/129/437 +f 219/127/438 223/129/438 225/130/438 +f 233/134/439 229/132/439 231/133/439 +f 241/138/440 229/132/440 233/134/440 +f 255/115/437 251/143/437 253/144/437 +f 255/115/441 217/126/441 251/143/441 +f 251/143/441 217/126/441 243/139/441 +f 219/127/442 225/130/442 227/131/442 +f 217/126/443 219/127/443 227/131/443 +f 243/139/420 217/126/420 227/131/420 +f 241/138/444 243/139/444 229/132/444 +f 243/139/445 227/131/445 229/132/445 +v -3.858562 2.027707 3.871576 +v -3.858562 2.027707 -3.907549 +v 3.920563 2.027707 -3.907549 +v 3.920563 2.027707 3.871576 +v -3.858562 2.306528 3.871576 +v -3.858562 2.306528 -3.907549 +v 3.920563 2.306528 -3.907549 +v 3.920563 2.306528 3.871576 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vn -1.000000 0.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 1.000000 -0.000000 0.000000 +vn 0.000000 0.000000 1.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +s off +f 261/145/446 262/146/446 257/147/446 +f 262/145/447 263/146/447 258/147/447 +f 263/145/448 264/146/448 260/148/448 +f 264/145/449 261/146/449 257/148/449 +f 257/145/450 258/146/450 259/148/450 +f 264/145/451 263/146/451 262/148/451 +f 262/146/446 258/148/446 257/147/446 +f 263/146/447 259/148/447 258/147/447 +f 259/147/448 263/145/448 260/148/448 +f 260/147/449 264/145/449 257/148/449 +f 260/147/450 257/145/450 259/148/450 +f 261/147/451 264/145/451 262/148/451 +v 2.043798 0.024218 -3.001008 +v 2.043798 2.024218 -3.001008 +v 2.238889 0.024218 -2.981793 +v 2.238889 2.024218 -2.981793 +v 2.426482 0.024218 -2.924888 +v 2.426482 2.024218 -2.924888 +v 2.599369 0.024218 -2.832478 +v 2.599369 2.024218 -2.832478 +v 2.750905 0.024218 -2.708115 +v 2.750905 2.024218 -2.708115 +v 2.875268 0.024218 -2.556578 +v 2.875268 2.024218 -2.556578 +v 2.967678 0.024218 -2.383692 +v 2.967678 2.024218 -2.383692 +v 3.024584 0.024218 -2.196099 +v 3.024584 2.024218 -2.196099 +v 3.043798 0.024218 -2.001008 +v 3.043798 2.024218 -2.001008 +v 3.024584 0.024218 -1.805918 +v 3.024584 2.024218 -1.805918 +v 2.967678 0.024218 -1.618325 +v 2.967678 2.024218 -1.618325 +v 2.875268 0.024218 -1.445438 +v 2.875268 2.024218 -1.445438 +v 2.750905 0.024218 -1.293901 +v 2.750905 2.024218 -1.293901 +v 2.599369 0.024218 -1.169539 +v 2.599369 2.024218 -1.169539 +v 2.426482 0.024218 -1.077129 +v 2.426482 2.024218 -1.077129 +v 2.238889 0.024218 -1.020223 +v 2.238889 2.024218 -1.020223 +v 2.043798 0.024218 -1.001008 +v 2.043798 2.024218 -1.001008 +v 1.848708 0.024218 -1.020223 +v 1.848708 2.024218 -1.020223 +v 1.661115 0.024218 -1.077129 +v 1.661115 2.024218 -1.077129 +v 1.488228 0.024218 -1.169539 +v 1.488228 2.024218 -1.169539 +v 1.336691 0.024218 -1.293902 +v 1.336691 2.024218 -1.293902 +v 1.212328 0.024218 -1.445439 +v 1.212328 2.024218 -1.445439 +v 1.119919 0.024218 -1.618326 +v 1.119919 2.024218 -1.618326 +v 1.063013 0.024218 -1.805919 +v 1.063013 2.024218 -1.805919 +v 1.043798 0.024218 -2.001009 +v 1.043798 2.024218 -2.001009 +v 1.063013 0.024218 -2.196100 +v 1.063013 2.024218 -2.196100 +v 1.119919 0.024218 -2.383693 +v 1.119919 2.024218 -2.383693 +v 1.212330 0.024218 -2.556580 +v 1.212330 2.024218 -2.556580 +v 1.336693 0.024218 -2.708116 +v 1.336693 2.024218 -2.708116 +v 1.488229 0.024218 -2.832479 +v 1.488229 2.024218 -2.832479 +v 1.661116 0.024218 -2.924888 +v 1.661116 2.024218 -2.924888 +v 1.848710 0.024218 -2.981794 +v 1.848710 2.024218 -2.981794 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.000000 0.500000 +vt 0.009607 0.597546 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.146447 0.853554 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +vn 0.098018 0.000000 -0.995185 +vn 0.290285 0.000000 -0.956940 +vn 0.471397 0.000000 -0.881921 +vn 0.634393 0.000000 -0.773011 +vn 0.773011 0.000000 -0.634393 +vn 0.881921 0.000000 -0.471398 +vn 0.956940 0.000000 -0.290285 +vn 0.995185 0.000000 -0.098017 +vn 0.995185 0.000000 0.098017 +vn 0.956940 0.000000 0.290285 +vn 0.881921 0.000000 0.471397 +vn 0.773011 0.000000 0.634393 +vn 0.634393 0.000000 0.773011 +vn 0.471397 0.000000 0.881921 +vn 0.290285 0.000000 0.956940 +vn 0.098017 0.000000 0.995185 +vn -0.098018 0.000000 0.995185 +vn -0.290285 0.000000 0.956940 +vn -0.471397 0.000000 0.881921 +vn -0.634394 0.000000 0.773010 +vn -0.773011 0.000000 0.634393 +vn -0.881922 0.000000 0.471396 +vn -0.956941 0.000000 0.290283 +vn -0.995185 0.000000 0.098017 +vn -0.995185 -0.000000 -0.098018 +vn -0.956940 -0.000000 -0.290286 +vn -0.881921 -0.000000 -0.471398 +vn -0.773010 -0.000000 -0.634394 +vn -0.634392 -0.000000 -0.773012 +vn -0.471396 -0.000000 -0.881922 +vn -0.098016 -0.000000 -0.995185 +vn -0.290283 -0.000000 -0.956941 +s off +f 265/149/452 266/150/452 268/151/452 +f 267/149/453 268/150/453 270/151/453 +f 269/149/454 270/150/454 272/151/454 +f 271/149/455 272/150/455 274/151/455 +f 273/149/456 274/150/456 276/151/456 +f 275/149/457 276/150/457 278/151/457 +f 277/149/458 278/150/458 280/151/458 +f 279/149/459 280/150/459 282/151/459 +f 281/149/460 282/150/460 284/151/460 +f 283/149/461 284/150/461 286/151/461 +f 285/149/462 286/150/462 288/151/462 +f 287/149/463 288/150/463 290/151/463 +f 289/149/464 290/150/464 292/151/464 +f 291/149/465 292/150/465 294/151/465 +f 293/149/466 294/150/466 296/151/466 +f 295/149/467 296/150/467 298/151/467 +f 297/149/468 298/150/468 299/152/468 +f 299/149/469 300/150/469 301/152/469 +f 301/149/470 302/150/470 303/152/470 +f 303/149/471 304/150/471 305/152/471 +f 305/149/472 306/150/472 307/152/472 +f 307/149/473 308/150/473 309/152/473 +f 309/149/474 310/150/474 311/152/474 +f 311/149/475 312/150/475 313/152/475 +f 313/149/476 314/150/476 315/152/476 +f 315/149/477 316/150/477 317/152/477 +f 317/149/478 318/150/478 319/152/478 +f 319/149/479 320/150/479 321/152/479 +f 321/149/480 322/150/480 323/152/480 +f 323/149/481 324/150/481 325/152/481 +f 268/153/451 266/154/451 270/155/451 +f 327/149/482 328/150/482 265/152/482 +f 325/149/483 326/150/483 327/152/483 +f 265/153/450 267/154/450 327/155/450 +f 267/152/452 265/149/452 268/151/452 +f 269/152/453 267/149/453 270/151/453 +f 271/152/454 269/149/454 272/151/454 +f 273/152/455 271/149/455 274/151/455 +f 275/152/456 273/149/456 276/151/456 +f 277/152/457 275/149/457 278/151/457 +f 279/152/458 277/149/458 280/151/458 +f 281/152/459 279/149/459 282/151/459 +f 283/152/460 281/149/460 284/151/460 +f 285/152/461 283/149/461 286/151/461 +f 287/152/462 285/149/462 288/151/462 +f 289/152/463 287/149/463 290/151/463 +f 291/152/464 289/149/464 292/151/464 +f 293/152/465 291/149/465 294/151/465 +f 295/152/466 293/149/466 296/151/466 +f 297/152/467 295/149/467 298/151/467 +f 298/150/468 300/151/468 299/152/468 +f 300/150/469 302/151/469 301/152/469 +f 302/150/470 304/151/470 303/152/470 +f 304/150/471 306/151/471 305/152/471 +f 306/150/472 308/151/472 307/152/472 +f 308/150/473 310/151/473 309/152/473 +f 310/150/474 312/151/474 311/152/474 +f 312/150/475 314/151/475 313/152/475 +f 314/150/476 316/151/476 315/152/476 +f 316/150/477 318/151/477 317/152/477 +f 318/150/478 320/151/478 319/152/478 +f 320/150/479 322/151/479 321/152/479 +f 322/150/480 324/151/480 323/152/480 +f 324/150/481 326/151/481 325/152/481 +f 266/154/451 328/156/451 270/155/451 +f 328/156/451 326/157/451 270/155/451 +f 326/157/451 324/158/451 270/155/451 +f 324/158/451 322/159/451 270/155/451 +f 322/159/451 320/160/451 270/155/451 +f 320/160/451 318/161/451 270/155/451 +f 318/161/451 316/162/451 270/155/451 +f 316/162/451 314/163/451 270/155/451 +f 314/163/451 312/164/451 270/155/451 +f 312/164/451 310/165/451 270/155/451 +f 310/165/451 308/166/451 270/155/451 +f 308/166/451 306/167/451 270/155/451 +f 306/167/451 304/168/451 270/155/451 +f 304/168/451 302/169/451 270/155/451 +f 302/169/451 300/170/451 270/155/451 +f 300/170/451 298/171/451 270/155/451 +f 298/171/451 296/172/451 270/155/451 +f 296/172/451 294/173/451 270/155/451 +f 294/173/451 292/174/451 270/155/451 +f 292/174/451 290/175/451 270/155/451 +f 290/175/451 288/176/451 270/155/451 +f 288/176/451 286/177/451 270/155/451 +f 286/177/451 284/178/451 270/155/451 +f 284/178/451 282/179/451 270/155/451 +f 282/179/451 280/180/451 270/155/451 +f 280/180/451 278/181/451 270/155/451 +f 278/181/451 276/182/451 270/155/451 +f 276/182/451 274/183/451 270/155/451 +f 274/183/451 272/184/451 270/155/451 +f 328/150/482 266/151/482 265/152/482 +f 326/150/483 328/151/483 327/152/483 +f 267/154/450 269/156/450 327/155/450 +f 269/156/450 271/157/450 327/155/450 +f 271/157/450 273/158/450 327/155/450 +f 273/158/450 275/159/450 327/155/450 +f 275/159/450 277/160/450 327/155/450 +f 277/160/450 279/161/450 327/155/450 +f 279/161/450 281/162/450 327/155/450 +f 281/162/450 283/163/450 327/155/450 +f 283/163/450 285/164/450 327/155/450 +f 285/164/450 287/165/450 327/155/450 +f 287/165/450 289/166/450 327/155/450 +f 289/166/450 291/167/450 327/155/450 +f 291/167/450 293/168/450 327/155/450 +f 293/168/450 295/169/450 327/155/450 +f 295/169/450 297/170/450 327/155/450 +f 297/170/450 299/171/450 327/155/450 +f 299/171/450 301/172/450 327/155/450 +f 301/172/450 303/173/450 327/155/450 +f 303/173/450 305/174/450 327/155/450 +f 305/174/450 307/175/450 327/155/450 +f 307/175/450 309/176/450 327/155/450 +f 309/176/450 311/177/450 327/155/450 +f 311/177/450 313/178/450 327/155/450 +f 313/178/450 315/179/450 327/155/450 +f 315/179/450 317/180/450 327/155/450 +f 317/180/450 319/181/450 327/155/450 +f 319/181/450 321/182/450 327/155/450 +f 321/182/450 323/183/450 325/184/450 +f 327/155/450 321/182/450 325/184/450 +v -2.014818 0.007922 0.998641 +v -2.014818 2.007922 0.998641 +v -1.819728 0.007922 1.017856 +v -1.819728 2.007922 1.017856 +v -1.632135 0.007922 1.074762 +v -1.632135 2.007922 1.074762 +v -1.459248 0.007922 1.167172 +v -1.459248 2.007922 1.167172 +v -1.307712 0.007922 1.291534 +v -1.307712 2.007922 1.291534 +v -1.183349 0.007922 1.443071 +v -1.183349 2.007922 1.443071 +v -1.090939 0.007922 1.615958 +v -1.090939 2.007922 1.615958 +v -1.034033 0.007922 1.803551 +v -1.034033 2.007922 1.803551 +v -1.014818 0.007922 1.998641 +v -1.014818 2.007922 1.998641 +v -1.034033 0.007922 2.193732 +v -1.034033 2.007922 2.193732 +v -1.090939 0.007922 2.381325 +v -1.090939 2.007922 2.381325 +v -1.183349 0.007922 2.554211 +v -1.183349 2.007922 2.554211 +v -1.307712 0.007922 2.705748 +v -1.307712 2.007922 2.705748 +v -1.459248 0.007922 2.830111 +v -1.459248 2.007922 2.830111 +v -1.632135 0.007922 2.922521 +v -1.632135 2.007922 2.922521 +v -1.819728 0.007922 2.979427 +v -1.819728 2.007922 2.979427 +v -2.014819 0.007922 2.998641 +v -2.014819 2.007922 2.998641 +v -2.209909 0.007922 2.979426 +v -2.209909 2.007922 2.979426 +v -2.397502 0.007922 2.922521 +v -2.397502 2.007922 2.922521 +v -2.570389 0.007922 2.830111 +v -2.570389 2.007922 2.830111 +v -2.721926 0.007922 2.705748 +v -2.721926 2.007922 2.705748 +v -2.846288 0.007922 2.554211 +v -2.846288 2.007922 2.554211 +v -2.938698 0.007922 2.381324 +v -2.938698 2.007922 2.381324 +v -2.995604 0.007922 2.193731 +v -2.995604 2.007922 2.193731 +v -3.014818 0.007922 1.998640 +v -3.014818 2.007922 1.998640 +v -2.995604 0.007922 1.803550 +v -2.995604 2.007922 1.803550 +v -2.938698 0.007922 1.615957 +v -2.938698 2.007922 1.615957 +v -2.846287 0.007922 1.443070 +v -2.846287 2.007922 1.443070 +v -2.721924 0.007922 1.291534 +v -2.721924 2.007922 1.291534 +v -2.570388 0.007922 1.167171 +v -2.570388 2.007922 1.167171 +v -2.397501 0.007922 1.074761 +v -2.397501 2.007922 1.074761 +v -2.209907 0.007922 1.017856 +v -2.209907 2.007922 1.017856 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.000000 0.500000 +vt 0.009607 0.597546 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.146447 0.853554 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +vn 0.098017 0.000000 -0.995185 +vn 0.881921 0.000000 -0.471397 +vn 0.634393 0.000000 0.773010 +vn 0.471396 0.000000 0.881922 +vn -0.881920 -0.000000 -0.471398 +vn -0.634393 -0.000000 -0.773011 +vn -0.471395 -0.000000 -0.881922 +s off +f 329/185/484 330/186/484 332/187/484 +f 331/185/453 332/186/453 334/187/453 +f 333/185/454 334/186/454 336/187/454 +f 335/185/455 336/186/455 338/187/455 +f 337/185/456 338/186/456 340/187/456 +f 339/185/485 340/186/485 342/187/485 +f 341/185/458 342/186/458 344/187/458 +f 343/185/459 344/186/459 346/187/459 +f 345/185/460 346/186/460 348/187/460 +f 347/185/461 348/186/461 350/187/461 +f 349/185/462 350/186/462 352/187/462 +f 351/185/463 352/186/463 354/187/463 +f 353/185/486 354/186/486 356/187/486 +f 355/185/487 356/186/487 358/187/487 +f 357/185/466 358/186/466 360/187/466 +f 359/185/467 360/186/467 362/187/467 +f 361/185/468 362/186/468 363/188/468 +f 363/185/469 364/186/469 365/188/469 +f 365/185/470 366/186/470 367/188/470 +f 367/185/471 368/186/471 369/188/471 +f 369/185/472 370/186/472 371/188/472 +f 371/185/473 372/186/473 373/188/473 +f 373/185/474 374/186/474 375/188/474 +f 375/185/475 376/186/475 377/188/475 +f 377/185/476 378/186/476 379/188/476 +f 379/185/477 380/186/477 381/188/477 +f 381/185/488 382/186/488 383/188/488 +f 383/185/479 384/186/479 385/188/479 +f 385/185/489 386/186/489 387/188/489 +f 387/185/490 388/186/490 389/188/490 +f 332/189/451 330/190/451 334/191/451 +f 391/185/482 392/186/482 329/188/482 +f 389/185/483 390/186/483 391/188/483 +f 329/189/450 331/190/450 391/191/450 +f 331/188/484 329/185/484 332/187/484 +f 333/188/453 331/185/453 334/187/453 +f 335/188/454 333/185/454 336/187/454 +f 337/188/455 335/185/455 338/187/455 +f 339/188/456 337/185/456 340/187/456 +f 341/188/485 339/185/485 342/187/485 +f 343/188/458 341/185/458 344/187/458 +f 345/188/459 343/185/459 346/187/459 +f 347/188/460 345/185/460 348/187/460 +f 349/188/461 347/185/461 350/187/461 +f 351/188/462 349/185/462 352/187/462 +f 353/188/463 351/185/463 354/187/463 +f 355/188/486 353/185/486 356/187/486 +f 357/188/487 355/185/487 358/187/487 +f 359/188/466 357/185/466 360/187/466 +f 361/188/467 359/185/467 362/187/467 +f 362/186/468 364/187/468 363/188/468 +f 364/186/469 366/187/469 365/188/469 +f 366/186/470 368/187/470 367/188/470 +f 368/186/471 370/187/471 369/188/471 +f 370/186/472 372/187/472 371/188/472 +f 372/186/473 374/187/473 373/188/473 +f 374/186/474 376/187/474 375/188/474 +f 376/186/475 378/187/475 377/188/475 +f 378/186/476 380/187/476 379/188/476 +f 380/186/477 382/187/477 381/188/477 +f 382/186/488 384/187/488 383/188/488 +f 384/186/479 386/187/479 385/188/479 +f 386/186/489 388/187/489 387/188/489 +f 388/186/490 390/187/490 389/188/490 +f 330/190/451 392/192/451 334/191/451 +f 392/192/451 390/193/451 334/191/451 +f 390/193/451 388/194/451 334/191/451 +f 388/194/451 386/195/451 334/191/451 +f 386/195/451 384/196/451 334/191/451 +f 384/196/451 382/197/451 334/191/451 +f 382/197/451 380/198/451 334/191/451 +f 380/198/451 378/199/451 334/191/451 +f 378/199/451 376/200/451 334/191/451 +f 376/200/451 374/201/451 334/191/451 +f 374/201/451 372/202/451 334/191/451 +f 372/202/451 370/203/451 334/191/451 +f 370/203/451 368/204/451 334/191/451 +f 368/204/451 366/205/451 334/191/451 +f 366/205/451 364/206/451 334/191/451 +f 364/206/451 362/207/451 334/191/451 +f 362/207/451 360/208/451 334/191/451 +f 360/208/451 358/209/451 334/191/451 +f 358/209/451 356/210/451 334/191/451 +f 356/210/451 354/211/451 334/191/451 +f 354/211/451 352/212/451 334/191/451 +f 352/212/451 350/213/451 334/191/451 +f 350/213/451 348/214/451 334/191/451 +f 348/214/451 346/215/451 334/191/451 +f 346/215/451 344/216/451 334/191/451 +f 344/216/451 342/217/451 334/191/451 +f 342/217/451 340/218/451 334/191/451 +f 340/218/451 338/219/451 334/191/451 +f 338/219/451 336/220/451 334/191/451 +f 392/186/482 330/187/482 329/188/482 +f 390/186/483 392/187/483 391/188/483 +f 331/190/450 333/192/450 391/191/450 +f 333/192/450 335/193/450 391/191/450 +f 335/193/450 337/194/450 391/191/450 +f 337/194/450 339/195/450 391/191/450 +f 339/195/450 341/196/450 391/191/450 +f 341/196/450 343/197/450 391/191/450 +f 343/197/450 345/198/450 391/191/450 +f 345/198/450 347/199/450 391/191/450 +f 347/199/450 349/200/450 391/191/450 +f 349/200/450 351/201/450 391/191/450 +f 351/201/450 353/202/450 391/191/450 +f 353/202/450 355/203/450 391/191/450 +f 355/203/450 357/204/450 391/191/450 +f 357/204/450 359/205/450 391/191/450 +f 359/205/450 361/206/450 391/191/450 +f 361/206/450 363/207/450 391/191/450 +f 363/207/450 365/208/450 391/191/450 +f 365/208/450 367/209/450 391/191/450 +f 367/209/450 369/210/450 391/191/450 +f 369/210/450 371/211/450 391/191/450 +f 371/211/450 373/212/450 391/191/450 +f 373/212/450 375/213/450 391/191/450 +f 375/213/450 377/214/450 391/191/450 +f 377/214/450 379/215/450 391/191/450 +f 379/215/450 381/216/450 391/191/450 +f 381/216/450 383/217/450 391/191/450 +f 383/217/450 385/218/450 391/191/450 +f 385/218/450 387/219/450 391/191/450 +f 387/219/450 389/220/450 391/191/450 +v -2.001621 -0.021814 -3.021079 +v -2.001621 1.978186 -3.021079 +v -1.806530 -0.021814 -3.001864 +v -1.806530 1.978186 -3.001864 +v -1.618937 -0.021814 -2.944958 +v -1.618937 1.978186 -2.944958 +v -1.446051 -0.021814 -2.852548 +v -1.446051 1.978186 -2.852548 +v -1.294514 -0.021814 -2.728185 +v -1.294514 1.978186 -2.728185 +v -1.170151 -0.021814 -2.576649 +v -1.170151 1.978186 -2.576649 +v -1.077741 -0.021814 -2.403762 +v -1.077741 1.978186 -2.403762 +v -1.020836 -0.021814 -2.216169 +v -1.020836 1.978186 -2.216169 +v -1.001621 -0.021814 -2.021079 +v -1.001621 1.978186 -2.021079 +v -1.020835 -0.021814 -1.825988 +v -1.020835 1.978186 -1.825988 +v -1.077741 -0.021814 -1.638395 +v -1.077741 1.978186 -1.638395 +v -1.170151 -0.021814 -1.465508 +v -1.170151 1.978186 -1.465508 +v -1.294514 -0.021814 -1.313972 +v -1.294514 1.978186 -1.313972 +v -1.446051 -0.021814 -1.189609 +v -1.446051 1.978186 -1.189609 +v -1.618937 -0.021814 -1.097199 +v -1.618937 1.978186 -1.097199 +v -1.806531 -0.021814 -1.040293 +v -1.806531 1.978186 -1.040293 +v -2.001621 -0.021814 -1.021079 +v -2.001621 1.978186 -1.021079 +v -2.196712 -0.021814 -1.040293 +v -2.196712 1.978186 -1.040293 +v -2.384305 -0.021814 -1.097199 +v -2.384305 1.978186 -1.097199 +v -2.557191 -0.021814 -1.189609 +v -2.557191 1.978186 -1.189609 +v -2.708728 -0.021814 -1.313972 +v -2.708728 1.978186 -1.313972 +v -2.833091 -0.021814 -1.465509 +v -2.833091 1.978186 -1.465509 +v -2.925501 -0.021814 -1.638396 +v -2.925501 1.978186 -1.638396 +v -2.982406 -0.021814 -1.825989 +v -2.982406 1.978186 -1.825989 +v -3.001621 -0.021814 -2.021080 +v -3.001621 1.978186 -2.021080 +v -2.982406 -0.021814 -2.216170 +v -2.982406 1.978186 -2.216170 +v -2.925500 -0.021814 -2.403763 +v -2.925500 1.978186 -2.403763 +v -2.833090 -0.021814 -2.576650 +v -2.833090 1.978186 -2.576650 +v -2.708727 -0.021814 -2.728186 +v -2.708727 1.978186 -2.728186 +v -2.557190 -0.021814 -2.852549 +v -2.557190 1.978186 -2.852549 +v -2.384303 -0.021814 -2.944959 +v -2.384303 1.978186 -2.944959 +v -2.196710 -0.021814 -3.001864 +v -2.196710 1.978186 -3.001864 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.000000 0.500000 +vt 0.009607 0.597546 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.146447 0.853554 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +vn 0.471398 0.000000 -0.881921 +vn -0.634393 0.000000 0.773010 +vn -0.881921 -0.000000 -0.471397 +vn -0.773009 -0.000000 -0.634395 +vn -0.634393 -0.000000 -0.773010 +s off +f 393/221/484 394/222/484 396/223/484 +f 395/221/453 396/222/453 398/223/453 +f 397/221/491 398/222/491 400/223/491 +f 399/221/455 400/222/455 402/223/455 +f 401/221/456 402/222/456 404/223/456 +f 403/221/485 404/222/485 406/223/485 +f 405/221/458 406/222/458 408/223/458 +f 407/221/459 408/222/459 410/223/459 +f 409/221/460 410/222/460 412/223/460 +f 411/221/461 412/222/461 414/223/461 +f 413/221/462 414/222/462 416/223/462 +f 415/221/463 416/222/463 418/223/463 +f 417/221/464 418/222/464 420/223/464 +f 419/221/465 420/222/465 422/223/465 +f 421/221/466 422/222/466 424/223/466 +f 423/221/467 424/222/467 426/223/467 +f 425/221/468 426/222/468 427/224/468 +f 427/221/469 428/222/469 429/224/469 +f 429/221/470 430/222/470 431/224/470 +f 431/221/492 432/222/492 433/224/492 +f 433/221/472 434/222/472 435/224/472 +f 435/221/473 436/222/473 437/224/473 +f 437/221/474 438/222/474 439/224/474 +f 439/221/475 440/222/475 441/224/475 +f 441/221/476 442/222/476 443/224/476 +f 443/221/477 444/222/477 445/224/477 +f 445/221/493 446/222/493 447/224/493 +f 447/221/494 448/222/494 449/224/494 +f 449/221/495 450/222/495 451/224/495 +f 451/221/490 452/222/490 453/224/490 +f 396/225/451 394/226/451 398/227/451 +f 455/221/482 456/222/482 393/224/482 +f 453/221/483 454/222/483 455/224/483 +f 393/225/450 395/226/450 455/227/450 +f 395/224/484 393/221/484 396/223/484 +f 397/224/453 395/221/453 398/223/453 +f 399/224/491 397/221/491 400/223/491 +f 401/224/455 399/221/455 402/223/455 +f 403/224/456 401/221/456 404/223/456 +f 405/224/485 403/221/485 406/223/485 +f 407/224/458 405/221/458 408/223/458 +f 409/224/459 407/221/459 410/223/459 +f 411/224/460 409/221/460 412/223/460 +f 413/224/461 411/221/461 414/223/461 +f 415/224/462 413/221/462 416/223/462 +f 417/224/463 415/221/463 418/223/463 +f 419/224/464 417/221/464 420/223/464 +f 421/224/465 419/221/465 422/223/465 +f 423/224/466 421/221/466 424/223/466 +f 425/224/467 423/221/467 426/223/467 +f 426/222/468 428/223/468 427/224/468 +f 428/222/469 430/223/469 429/224/469 +f 430/222/470 432/223/470 431/224/470 +f 432/222/492 434/223/492 433/224/492 +f 434/222/472 436/223/472 435/224/472 +f 436/222/473 438/223/473 437/224/473 +f 438/222/474 440/223/474 439/224/474 +f 440/222/475 442/223/475 441/224/475 +f 442/222/476 444/223/476 443/224/476 +f 444/222/477 446/223/477 445/224/477 +f 446/222/493 448/223/493 447/224/493 +f 448/222/494 450/223/494 449/224/494 +f 450/222/495 452/223/495 451/224/495 +f 452/222/490 454/223/490 453/224/490 +f 394/226/451 456/228/451 398/227/451 +f 456/228/451 454/229/451 398/227/451 +f 454/229/451 452/230/451 398/227/451 +f 452/230/451 450/231/451 398/227/451 +f 450/231/451 448/232/451 398/227/451 +f 448/232/451 446/233/451 398/227/451 +f 446/233/451 444/234/451 398/227/451 +f 444/234/451 442/235/451 398/227/451 +f 442/235/451 440/236/451 398/227/451 +f 440/236/451 438/237/451 398/227/451 +f 438/237/451 436/238/451 398/227/451 +f 436/238/451 434/239/451 398/227/451 +f 434/239/451 432/240/451 398/227/451 +f 432/240/451 430/241/451 398/227/451 +f 430/241/451 428/242/451 398/227/451 +f 428/242/451 426/243/451 398/227/451 +f 426/243/451 424/244/451 398/227/451 +f 424/244/451 422/245/451 398/227/451 +f 422/245/451 420/246/451 398/227/451 +f 420/246/451 418/247/451 398/227/451 +f 418/247/451 416/248/451 398/227/451 +f 416/248/451 414/249/451 398/227/451 +f 414/249/451 412/250/451 398/227/451 +f 412/250/451 410/251/451 398/227/451 +f 410/251/451 408/252/451 398/227/451 +f 408/252/451 406/253/451 398/227/451 +f 406/253/451 404/254/451 398/227/451 +f 404/254/451 402/255/451 398/227/451 +f 402/255/451 400/256/451 398/227/451 +f 456/222/482 394/223/482 393/224/482 +f 454/222/483 456/223/483 455/224/483 +f 395/226/450 397/228/450 455/227/450 +f 397/228/450 399/229/450 455/227/450 +f 399/229/450 401/230/450 455/227/450 +f 401/230/450 403/231/450 455/227/450 +f 403/231/450 405/232/450 455/227/450 +f 405/232/450 407/233/450 455/227/450 +f 407/233/450 409/234/450 455/227/450 +f 409/234/450 411/235/450 455/227/450 +f 411/235/450 413/236/450 455/227/450 +f 413/236/450 415/237/450 455/227/450 +f 415/237/450 417/238/450 455/227/450 +f 417/238/450 419/239/450 455/227/450 +f 419/239/450 421/240/450 455/227/450 +f 421/240/450 423/241/450 455/227/450 +f 423/241/450 425/242/450 455/227/450 +f 425/242/450 427/243/450 455/227/450 +f 427/243/450 429/244/450 455/227/450 +f 429/244/450 431/245/450 455/227/450 +f 431/245/450 433/246/450 455/227/450 +f 433/246/450 435/247/450 455/227/450 +f 435/247/450 437/248/450 455/227/450 +f 437/248/450 439/249/450 455/227/450 +f 439/249/450 441/250/450 455/227/450 +f 441/250/450 443/251/450 455/227/450 +f 443/251/450 445/252/450 455/227/450 +f 445/252/450 447/253/450 455/227/450 +f 447/253/450 449/254/450 455/227/450 +f 449/254/450 451/255/450 455/227/450 +f 451/255/450 453/256/450 455/227/450 +v 2.021592 0.003623 1.016610 +v 2.021592 2.003623 1.016610 +v 2.216682 0.003623 1.035825 +v 2.216682 2.003623 1.035825 +v 2.404276 0.003623 1.092731 +v 2.404276 2.003623 1.092731 +v 2.577162 0.003623 1.185141 +v 2.577162 2.003623 1.185141 +v 2.728699 0.003623 1.309503 +v 2.728699 2.003623 1.309503 +v 2.853062 0.003623 1.461040 +v 2.853062 2.003623 1.461040 +v 2.945472 0.003623 1.633927 +v 2.945472 2.003623 1.633927 +v 3.002378 0.003623 1.821520 +v 3.002378 2.003623 1.821520 +v 3.021592 0.003623 2.016610 +v 3.021592 2.003623 2.016610 +v 3.002378 0.003623 2.211700 +v 3.002378 2.003623 2.211700 +v 2.945472 0.003623 2.399293 +v 2.945472 2.003623 2.399293 +v 2.853062 0.003623 2.572180 +v 2.853062 2.003623 2.572180 +v 2.728699 0.003623 2.723717 +v 2.728699 2.003623 2.723717 +v 2.577162 0.003623 2.848080 +v 2.577162 2.003623 2.848080 +v 2.404275 0.003623 2.940490 +v 2.404275 2.003623 2.940490 +v 2.216682 0.003623 2.997396 +v 2.216682 2.003623 2.997396 +v 2.021592 0.003623 3.016610 +v 2.021592 2.003623 3.016610 +v 1.826501 0.003623 2.997395 +v 1.826501 2.003623 2.997395 +v 1.638908 0.003623 2.940490 +v 1.638908 2.003623 2.940490 +v 1.466021 0.003623 2.848079 +v 1.466021 2.003623 2.848079 +v 1.314485 0.003623 2.723716 +v 1.314485 2.003623 2.723716 +v 1.190122 0.003623 2.572180 +v 1.190122 2.003623 2.572180 +v 1.097712 0.003623 2.399293 +v 1.097712 2.003623 2.399293 +v 1.040807 0.003623 2.211699 +v 1.040807 2.003623 2.211699 +v 1.021592 0.003623 2.016609 +v 1.021592 2.003623 2.016609 +v 1.040807 0.003623 1.821519 +v 1.040807 2.003623 1.821519 +v 1.097713 0.003623 1.633926 +v 1.097713 2.003623 1.633926 +v 1.190123 0.003623 1.461039 +v 1.190123 2.003623 1.461039 +v 1.314486 0.003623 1.309502 +v 1.314486 2.003623 1.309502 +v 1.466023 0.003623 1.185140 +v 1.466023 2.003623 1.185140 +v 1.638910 0.003623 1.092730 +v 1.638910 2.003623 1.092730 +v 1.826503 0.003623 1.035825 +v 1.826503 2.003623 1.035825 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.000000 0.500000 +vt 0.009607 0.597546 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.146447 0.853554 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +s off +f 457/257/484 458/258/484 460/259/484 +f 459/257/453 460/258/453 462/259/453 +f 461/257/454 462/258/454 464/259/454 +f 463/257/455 464/258/455 466/259/455 +f 465/257/456 466/258/456 468/259/456 +f 467/257/457 468/258/457 470/259/457 +f 469/257/458 470/258/458 472/259/458 +f 471/257/459 472/258/459 474/259/459 +f 473/257/460 474/258/460 476/259/460 +f 475/257/461 476/258/461 478/259/461 +f 477/257/462 478/258/462 480/259/462 +f 479/257/463 480/258/463 482/259/463 +f 481/257/464 482/258/464 484/259/464 +f 483/257/465 484/258/465 486/259/465 +f 485/257/466 486/258/466 488/259/466 +f 487/257/467 488/258/467 490/259/467 +f 489/257/468 490/258/468 491/260/468 +f 491/257/469 492/258/469 493/260/469 +f 493/257/470 494/258/470 495/260/470 +f 495/257/471 496/258/471 497/260/471 +f 497/257/472 498/258/472 499/260/472 +f 499/257/473 500/258/473 501/260/473 +f 501/257/474 502/258/474 503/260/474 +f 503/257/475 504/258/475 505/260/475 +f 505/257/476 506/258/476 507/260/476 +f 507/257/477 508/258/477 509/260/477 +f 509/257/478 510/258/478 511/260/478 +f 511/257/494 512/258/494 513/260/494 +f 513/257/489 514/258/489 515/260/489 +f 515/257/490 516/258/490 517/260/490 +f 460/261/451 458/262/451 462/263/451 +f 519/257/482 520/258/482 457/260/482 +f 517/257/483 518/258/483 519/260/483 +f 457/261/450 459/262/450 519/263/450 +f 459/260/484 457/257/484 460/259/484 +f 461/260/453 459/257/453 462/259/453 +f 463/260/454 461/257/454 464/259/454 +f 465/260/455 463/257/455 466/259/455 +f 467/260/456 465/257/456 468/259/456 +f 469/260/457 467/257/457 470/259/457 +f 471/260/458 469/257/458 472/259/458 +f 473/260/459 471/257/459 474/259/459 +f 475/260/460 473/257/460 476/259/460 +f 477/260/461 475/257/461 478/259/461 +f 479/260/462 477/257/462 480/259/462 +f 481/260/463 479/257/463 482/259/463 +f 483/260/464 481/257/464 484/259/464 +f 485/260/465 483/257/465 486/259/465 +f 487/260/466 485/257/466 488/259/466 +f 489/260/467 487/257/467 490/259/467 +f 490/258/468 492/259/468 491/260/468 +f 492/258/469 494/259/469 493/260/469 +f 494/258/470 496/259/470 495/260/470 +f 496/258/471 498/259/471 497/260/471 +f 498/258/472 500/259/472 499/260/472 +f 500/258/473 502/259/473 501/260/473 +f 502/258/474 504/259/474 503/260/474 +f 504/258/475 506/259/475 505/260/475 +f 506/258/476 508/259/476 507/260/476 +f 508/258/477 510/259/477 509/260/477 +f 510/258/478 512/259/478 511/260/478 +f 512/258/494 514/259/494 513/260/494 +f 514/258/489 516/259/489 515/260/489 +f 516/258/490 518/259/490 517/260/490 +f 458/262/451 520/264/451 462/263/451 +f 520/264/451 518/265/451 462/263/451 +f 518/265/451 516/266/451 462/263/451 +f 516/266/451 514/267/451 462/263/451 +f 514/267/451 512/268/451 462/263/451 +f 512/268/451 510/269/451 462/263/451 +f 510/269/451 508/270/451 462/263/451 +f 508/270/451 506/271/451 462/263/451 +f 506/271/451 504/272/451 462/263/451 +f 504/272/451 502/273/451 462/263/451 +f 502/273/451 500/274/451 462/263/451 +f 500/274/451 498/275/451 462/263/451 +f 498/275/451 496/276/451 462/263/451 +f 496/276/451 494/277/451 462/263/451 +f 494/277/451 492/278/451 462/263/451 +f 492/278/451 490/279/451 462/263/451 +f 490/279/451 488/280/451 462/263/451 +f 488/280/451 486/281/451 462/263/451 +f 486/281/451 484/282/451 462/263/451 +f 484/282/451 482/283/451 462/263/451 +f 482/283/451 480/284/451 462/263/451 +f 480/284/451 478/285/451 462/263/451 +f 478/285/451 476/286/451 462/263/451 +f 476/286/451 474/287/451 462/263/451 +f 474/287/451 472/288/451 462/263/451 +f 472/288/451 470/289/451 462/263/451 +f 470/289/451 468/290/451 462/263/451 +f 468/290/451 466/291/451 462/263/451 +f 466/291/451 464/292/451 462/263/451 +f 520/258/482 458/259/482 457/260/482 +f 518/258/483 520/259/483 519/260/483 +f 459/262/450 461/264/450 519/263/450 +f 461/264/450 463/265/450 519/263/450 +f 463/265/450 465/266/450 519/263/450 +f 465/266/450 467/267/450 519/263/450 +f 467/267/450 469/268/450 519/263/450 +f 469/268/450 471/269/450 519/263/450 +f 471/269/450 473/270/450 519/263/450 +f 473/270/450 475/271/450 519/263/450 +f 475/271/450 477/272/450 519/263/450 +f 477/272/450 479/273/450 519/263/450 +f 479/273/450 481/274/450 519/263/450 +f 481/274/450 483/275/450 519/263/450 +f 483/275/450 485/276/450 519/263/450 +f 485/276/450 487/277/450 519/263/450 +f 487/277/450 489/278/450 519/263/450 +f 489/278/450 491/279/450 519/263/450 +f 491/279/450 493/280/450 519/263/450 +f 493/280/450 495/281/450 519/263/450 +f 495/281/450 497/282/450 519/263/450 +f 497/282/450 499/283/450 519/263/450 +f 499/283/450 501/284/450 519/263/450 +f 501/284/450 503/285/450 519/263/450 +f 503/285/450 505/286/450 519/263/450 +f 505/286/450 507/287/450 519/263/450 +f 507/287/450 509/288/450 519/263/450 +f 509/288/450 511/289/450 519/263/450 +f 511/289/450 513/290/450 519/263/450 +f 513/290/450 515/291/450 519/263/450 +f 515/291/450 517/292/450 519/263/450 diff --git a/examples/datavisualization/graphgallery/data/pipe.obj b/examples/datavisualization/graphgallery/data/pipe.obj new file mode 100644 index 00000000..6ccbb286 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/pipe.obj @@ -0,0 +1,330 @@ +# Blender v2.66 (sub 0) OBJ File: 'cylinder.blend' +# www.blender.org +o Cylinder +v 0.000000 -1.000000 -1.000000 +v 0.000000 1.000000 -1.000000 +v 0.195090 -1.000000 -0.980785 +v 0.195090 1.000000 -0.980785 +v 0.382683 -1.000000 -0.923880 +v 0.382683 1.000000 -0.923880 +v 0.555570 -1.000000 -0.831470 +v 0.555570 1.000000 -0.831470 +v 0.707107 -1.000000 -0.707107 +v 0.707107 1.000000 -0.707107 +v 0.831470 -1.000000 -0.555570 +v 0.831470 1.000000 -0.555570 +v 0.923880 -1.000000 -0.382683 +v 0.923880 1.000000 -0.382683 +v 0.980785 -1.000000 -0.195090 +v 0.980785 1.000000 -0.195090 +v 1.000000 -1.000000 -0.000000 +v 1.000000 1.000000 -0.000000 +v 0.980785 -1.000000 0.195090 +v 0.980785 1.000000 0.195090 +v 0.923880 -1.000000 0.382683 +v 0.923880 1.000000 0.382683 +v 0.831470 -1.000000 0.555570 +v 0.831470 1.000000 0.555570 +v 0.707107 -1.000000 0.707107 +v 0.707107 1.000000 0.707107 +v 0.555570 -1.000000 0.831470 +v 0.555570 1.000000 0.831470 +v 0.382683 -1.000000 0.923880 +v 0.382683 1.000000 0.923880 +v 0.195090 -1.000000 0.980785 +v 0.195090 1.000000 0.980785 +v -0.000000 -1.000000 1.000000 +v -0.000000 1.000000 1.000000 +v -0.195091 -1.000000 0.980785 +v -0.195091 1.000000 0.980785 +v -0.382684 -1.000000 0.923879 +v -0.382684 1.000000 0.923879 +v -0.555571 -1.000000 0.831469 +v -0.555571 1.000000 0.831469 +v -0.707107 -1.000000 0.707106 +v -0.707107 1.000000 0.707106 +v -0.831470 -1.000000 0.555570 +v -0.831470 1.000000 0.555570 +v -0.923880 -1.000000 0.382683 +v -0.923880 1.000000 0.382683 +v -0.980785 -1.000000 0.195089 +v -0.980785 1.000000 0.195089 +v -1.000000 -1.000000 -0.000001 +v -1.000000 1.000000 -0.000001 +v -0.980785 -1.000000 -0.195091 +v -0.980785 1.000000 -0.195091 +v -0.923879 -1.000000 -0.382684 +v -0.923879 1.000000 -0.382684 +v -0.831469 -1.000000 -0.555571 +v -0.831469 1.000000 -0.555571 +v -0.707106 -1.000000 -0.707108 +v -0.707106 1.000000 -0.707108 +v -0.555569 -1.000000 -0.831470 +v -0.555569 1.000000 -0.831470 +v -0.382682 -1.000000 -0.923880 +v -0.382682 1.000000 -0.923880 +v -0.195089 -1.000000 -0.980786 +v -0.195089 1.000000 -0.980786 +vt 0.289718 0.879351 +vt 0.288367 0.438844 +vt 0.330714 0.438714 +vt 0.332066 0.879221 +vt 0.370605 0.438592 +vt 0.371956 0.879099 +vt 0.406505 0.438482 +vt 0.407857 0.878988 +vt 0.437036 0.438388 +vt 0.778904 0.000000 +vt 0.780256 0.440507 +vt 0.749725 0.440601 +vt 0.748373 0.000094 +vt 0.713824 0.440711 +vt 0.712473 0.000204 +vt 0.673934 0.440833 +vt 0.672582 0.000326 +vt 0.631586 0.440963 +vt 0.630235 0.000456 +vt 0.588409 0.441095 +vt 0.587057 0.000588 +vt 0.546061 0.441225 +vt 0.544710 0.000718 +vt 0.506171 0.441348 +vt 0.504819 0.000841 +vt 0.470270 0.441458 +vt 0.468919 0.000951 +vt 0.439739 0.441552 +vt 0.720545 0.882916 +vt 0.719194 0.442409 +vt 0.755094 0.442299 +vt 0.756446 0.882806 +vt 0.794985 0.442176 +vt 0.796336 0.882683 +vt 0.837333 0.442046 +vt 0.838684 0.882553 +vt 0.881861 0.882421 +vt 0.880510 0.441914 +vt 0.924209 0.882291 +vt 0.922857 0.441784 +vt 0.964099 0.882168 +vt 0.962748 0.441662 +vt 1.000000 0.882058 +vt 0.717842 0.441552 +vt 0.719194 0.882058 +vt 0.681942 0.441662 +vt 0.683293 0.882169 +vt 0.642051 0.441784 +vt 0.643403 0.882291 +vt 0.599704 0.441914 +vt 0.601055 0.882421 +vt 0.556526 0.442046 +vt 0.557878 0.882553 +vt 0.514179 0.442176 +vt 0.515530 0.882683 +vt 0.474288 0.442299 +vt 0.475640 0.882806 +vt 0.438388 0.442409 +vt 0.097872 0.879939 +vt 0.096520 0.439433 +vt 0.128403 0.879846 +vt 0.127051 0.439339 +vt 0.164303 0.879735 +vt 0.162952 0.439229 +vt 0.204194 0.879613 +vt 0.000000 0.197605 +vt 0.008423 0.155257 +vt 0.000000 0.240783 +vt 0.246541 0.879483 +vt 0.245190 0.438976 +vt 0.202842 0.439106 +vt 0.438388 0.878895 +vt 0.438388 0.001045 +vt 0.998649 0.441552 +vt 0.439739 0.882916 +vt 0.024947 0.115367 +vt 0.048935 0.079466 +vt 0.079466 0.048935 +vt 0.115366 0.024947 +vt 0.155257 0.008424 +vt 0.197605 0.000000 +vt 0.240782 0.000000 +vt 0.283130 0.008423 +vt 0.323021 0.024947 +vt 0.358922 0.048935 +vt 0.389453 0.079466 +vt 0.413441 0.115367 +vt 0.429964 0.155257 +vt 0.438388 0.197605 +vt 0.438388 0.240783 +vt 0.429964 0.283130 +vt 0.413441 0.323021 +vt 0.389453 0.358922 +vt 0.358922 0.389453 +vt 0.323021 0.413441 +vt 0.283130 0.429964 +vt 0.240783 0.438388 +vt 0.197605 0.438388 +vt 0.155257 0.429964 +vt 0.115367 0.413441 +vt 0.079466 0.389453 +vt 0.048935 0.358922 +vt 0.024947 0.323021 +vt 0.008423 0.283130 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 0.685690 -0.727866 +vn 0.142003 0.685690 -0.713889 +vn 0.195074 0.000000 -0.980773 +vn 0.278542 0.685690 -0.672475 +vn 0.382672 0.000000 -0.923856 +vn 0.404370 0.685690 -0.605213 +vn 0.555559 0.000000 -0.831446 +vn 0.514664 0.685690 -0.514664 +vn 0.707083 0.000000 -0.707083 +vn 0.605213 0.685690 -0.404370 +vn 0.831446 0.000000 -0.555559 +vn 0.672475 0.685690 -0.278542 +vn 0.923856 0.000000 -0.382672 +vn 0.713889 0.685690 -0.142003 +vn 0.980773 0.000000 -0.195074 +vn 0.727866 0.685690 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.713889 0.685690 0.142003 +vn 0.980773 0.000000 0.195074 +vn 0.672475 0.685690 0.278542 +vn 0.923856 0.000000 0.382672 +vn 0.605213 0.685690 0.404370 +vn 0.831446 0.000000 0.555559 +vn 0.514664 0.685690 0.514664 +vn 0.707083 0.000000 0.707083 +vn 0.404370 0.685690 0.605213 +vn 0.555559 0.000000 0.831446 +vn 0.278542 0.685690 0.672475 +vn 0.382672 0.000000 0.923856 +vn 0.142003 0.685690 0.713889 +vn 0.195074 0.000000 0.980773 +vn 0.000000 0.685690 0.727866 +vn 0.000000 0.000000 0.999969 +vn -0.195074 0.000000 0.980773 +vn -0.142003 0.685690 0.713889 +vn -0.382672 0.000000 0.923856 +vn -0.278542 0.685690 0.672475 +vn -0.555559 0.000000 0.831446 +vn -0.404370 0.685690 0.605213 +vn -0.707083 0.000000 0.707083 +vn -0.514664 0.685690 0.514664 +vn -0.831446 0.000000 0.555559 +vn -0.605213 0.685690 0.404370 +vn -0.923856 0.000000 0.382672 +vn -0.672475 0.685690 0.278542 +vn -0.980773 0.000000 0.195074 +vn -0.713889 0.685690 0.142003 +vn -1.000000 0.000000 0.000000 +vn -0.727866 0.685690 0.000000 +vn -0.980773 0.000000 -0.195074 +vn -0.713889 0.685690 -0.142003 +vn -0.923856 0.000000 -0.382672 +vn -0.672475 0.685690 -0.278542 +vn -0.831446 0.000000 -0.555559 +vn -0.605213 0.685690 -0.404370 +vn -0.707083 0.000000 -0.707083 +vn -0.514664 0.685690 -0.514695 +vn -0.555559 0.000000 -0.831446 +vn -0.404370 0.685690 -0.605213 +vn -0.382672 0.000000 -0.923856 +vn -0.195074 0.000000 -0.980773 +vn -0.142003 0.685690 -0.713889 +vn -0.278542 0.685690 -0.672475 +s 1 +f 1/1/1 2/2/2 4/3/3 +f 3/4/4 4/3/3 6/5/5 +f 5/6/6 6/5/5 8/7/7 +f 7/8/8 8/7/7 10/9/9 +f 9/10/10 10/11/9 12/12/11 +f 11/13/12 12/12/11 14/14/13 +f 13/15/14 14/14/13 16/16/15 +f 15/17/16 16/16/15 18/18/17 +f 17/19/18 18/18/17 20/20/19 +f 19/21/20 20/20/19 22/22/21 +f 21/23/22 22/22/21 24/24/23 +f 23/25/24 24/24/23 26/26/25 +f 25/27/26 26/26/25 28/28/27 +f 27/29/28 28/30/27 30/31/29 +f 29/32/30 30/31/29 32/33/31 +f 31/34/32 32/33/31 34/35/33 +f 33/36/34 34/35/33 35/37/35 +f 35/37/35 36/38/36 37/39/37 +f 37/39/37 38/40/38 39/41/39 +f 39/41/39 40/42/40 41/43/41 +f 41/44/41 42/45/42 43/46/43 +f 43/46/43 44/47/44 45/48/45 +f 45/48/45 46/49/46 47/50/47 +f 47/50/47 48/51/48 49/52/49 +f 49/52/49 50/53/50 51/54/51 +f 51/54/51 52/55/52 53/56/53 +f 53/56/53 54/57/54 55/58/55 +f 55/59/55 56/60/56 57/61/57 +f 57/61/57 58/62/58 59/63/59 +f 59/63/59 60/64/60 61/65/61 +f 4/66/3 2/67/2 6/68/5 +f 63/69/62 64/70/63 1/1/1 +f 61/65/61 62/71/64 63/69/62 +f 3/4/4 1/1/1 4/3/3 +f 5/6/6 3/4/4 6/5/5 +f 7/8/8 5/6/6 8/7/7 +f 9/72/10 7/8/8 10/9/9 +f 11/13/12 9/10/10 12/12/11 +f 13/15/14 11/13/12 14/14/13 +f 15/17/16 13/15/14 16/16/15 +f 17/19/18 15/17/16 18/18/17 +f 19/21/20 17/19/18 20/20/19 +f 21/23/22 19/21/20 22/22/21 +f 23/25/24 21/23/22 24/24/23 +f 25/27/26 23/25/24 26/26/25 +f 27/73/28 25/27/26 28/28/27 +f 29/32/30 27/29/28 30/31/29 +f 31/34/32 29/32/30 32/33/31 +f 33/36/34 31/34/32 34/35/33 +f 34/35/33 36/38/36 35/37/35 +f 36/38/36 38/40/38 37/39/37 +f 38/40/38 40/42/40 39/41/39 +f 40/42/40 42/74/42 41/43/41 +f 42/45/42 44/47/44 43/46/43 +f 44/47/44 46/49/46 45/48/45 +f 46/49/46 48/51/48 47/50/47 +f 48/51/48 50/53/50 49/52/49 +f 50/53/50 52/55/52 51/54/51 +f 52/55/52 54/57/54 53/56/53 +f 54/57/54 56/75/56 55/58/55 +f 56/60/56 58/62/58 57/61/57 +f 58/62/58 60/64/60 59/63/59 +f 60/64/60 62/71/64 61/65/61 +f 2/67/2 64/76/63 6/68/5 +f 64/76/63 62/77/64 6/68/5 +f 62/77/64 60/78/60 6/68/5 +f 60/78/60 58/79/58 6/68/5 +f 58/79/58 56/80/56 6/68/5 +f 56/80/56 54/81/54 6/68/5 +f 54/81/54 52/82/52 6/68/5 +f 52/82/52 50/83/50 6/68/5 +f 50/83/50 48/84/48 6/68/5 +f 48/84/48 46/85/46 6/68/5 +f 46/85/46 44/86/44 6/68/5 +f 44/86/44 42/87/42 6/68/5 +f 42/87/42 40/88/40 6/68/5 +f 40/88/40 38/89/38 6/68/5 +f 38/89/38 36/90/36 6/68/5 +f 36/90/36 34/91/33 6/68/5 +f 34/91/33 32/92/31 6/68/5 +f 32/92/31 30/93/29 6/68/5 +f 30/93/29 28/94/27 6/68/5 +f 28/94/27 26/95/25 6/68/5 +f 26/95/25 24/96/23 6/68/5 +f 24/96/23 22/97/21 6/68/5 +f 22/97/21 20/98/19 6/68/5 +f 20/98/19 18/99/17 6/68/5 +f 18/99/17 16/100/15 6/68/5 +f 16/100/15 14/101/13 6/68/5 +f 14/101/13 12/102/11 6/68/5 +f 12/102/11 10/103/9 8/104/7 +f 6/68/5 12/102/11 8/104/7 +f 64/70/63 2/2/2 1/1/1 +f 62/71/64 64/70/63 63/69/62 diff --git a/examples/datavisualization/graphgallery/data/raindata.txt b/examples/datavisualization/graphgallery/data/raindata.txt new file mode 100644 index 00000000..d9558921 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/raindata.txt @@ -0,0 +1,158 @@ +# Rainfall per month from 2010 to 2022 in Northern Finland (Oulu) +# Format: year, month, rainfall +2010,1, 0, +2010,2, 3.4, +2010,3, 52, +2010,4, 33.8, +2010,5, 45.6, +2010,6, 43.8, +2010,7, 104.6, +2010,8, 105.4, +2010,9, 107.2, +2010,10,38.6, +2010,11,17.8, +2010,12,0, +2011,1, 8.2, +2011,2, 1.6, +2011,3, 27.4, +2011,4, 15.8, +2011,5, 57.6, +2011,6, 85.2, +2011,7, 127, +2011,8, 72.2, +2011,9, 82.2, +2011,10,62.4, +2011,11,31.6, +2011,12,53.8, +2012,1, 0, +2012,2, 5, +2012,3, 32.4, +2012,4, 57.6, +2012,5, 71.4, +2012,6, 60.8, +2012,7, 109, +2012,8, 43.6, +2012,9, 79.4, +2012,10,117.2, +2012,11,59, +2012,12,0.2, +2013,1, 28, +2013,2, 19, +2013,3, 0, +2013,4, 37.6, +2013,5, 44.2, +2013,6, 104.8, +2013,7, 84.2, +2013,8, 57.2, +2013,9, 37.2, +2013,10,64.6, +2013,11,77.8, +2013,12,92.8, +2014,1, 23.8, +2014,2, 23.6, +2014,3, 15.4, +2014,4, 13.2, +2014,5, 36.4, +2014,6, 26.4, +2014,7, 95.8, +2014,8, 81.8, +2014,9, 13.8, +2014,10,94.6, +2014,11,44.6, +2014,12,31, +2015,1, 37.4, +2015,2, 21, +2015,3, 42, +2015,4, 8.8, +2015,5, 82.4, +2015,6, 150, +2015,7, 56.8, +2015,8, 67.2, +2015,9, 131.2, +2015,10,38.4, +2015,11,83.4, +2015,12,47.8, +2016,1, 12.4, +2016,2, 34.8, +2016,3, 29, +2016,4, 40.4, +2016,5, 32.4, +2016,6, 80.2, +2016,7, 102.6, +2016,8, 95.6, +2016,9, 40.2, +2016,10,7.8, +2016,11,39.6, +2016,12,8.8, +2017,1, 9.4, +2017,2, 6.6, +2017,3, 29, +2017,4, 46.2, +2017,5, 43.2, +2017,6, 25.2, +2017,7, 72.4, +2017,8, 58.8, +2017,9, 68.8, +2017,10,45.8, +2017,11,36.8, +2017,12,29.6, +2018,1, 19.8, +2018,2, 0.8, +2018,3, 4, +2018,4, 23.2, +2018,5, 13.2, +2018,6, 62.8, +2018,7, 33, +2018,8, 96.6, +2018,9, 72.6, +2018,10,48.8, +2018,11,31.8, +2018,12,12.8, +2019,1, 0.2, +2019,2, 24.8, +2019,3, 32, +2019,4, 8.8, +2019,5, 71.4, +2019,6, 65.8, +2019,7, 17.6, +2019,8, 90, +2019,9, 50, +2019,10,77, +2019,11,27, +2019,12,43.2, +2020,1, 28.8, +2020,2, 45, +2020,3, 18.6, +2020,4, 13, +2020,5, 30.8, +2020,6, 21.4, +2020,7, 163.6, +2020,8, 12, +2020,9, 102.4, +2020,10,133.2, +2020,11,69.8, +2020,12,40.6, +2021,1, 0.4, +2021,2, 21.6, +2021,3, 24, +2021,4, 51.4, +2021,5, 76.4, +2021,6, 29.2, +2021,7, 36.4, +2021,8, 116, +2021,9, 72.4, +2021,10,93.4, +2021,11,21, +2021,12,10.2, +2022,1, 8.6, +2022,2, 6.6, +2022,3, 5.2, +2022,4, 15.2, +2022,5, 37.6, +2022,6, 45, +2022,7, 67.4, +2022,8, 161.6, +2022,9, 22.8, +2022,10,75.2, +2022,11,21.8, +2022,12,0.2 diff --git a/examples/datavisualization/graphgallery/data/refinery.obj b/examples/datavisualization/graphgallery/data/refinery.obj new file mode 100644 index 00000000..ed90c361 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/refinery.obj @@ -0,0 +1,2330 @@ +# Blender v2.66 (sub 0) OBJ File: 'oilrefinery.blend' +# www.blender.org +v -2.719012 -0.196783 4.805554 +v -2.719012 -0.196783 -4.824533 +v 2.730989 -0.196783 -4.824533 +v 2.730989 -0.196783 4.805554 +v -2.719012 0.012961 4.805554 +v -2.719012 0.012961 -4.824533 +v 2.730989 0.012961 -4.824533 +v 2.730989 0.012961 4.805554 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vn -0.577349 0.577349 0.577349 +vn -0.577349 0.577349 -0.577349 +vn -0.577349 -0.577349 0.577349 +vn 0.577349 0.577349 -0.577349 +vn 0.577349 -0.577349 -0.577349 +vn 0.577349 0.577349 0.577349 +vn 0.577349 -0.577349 0.577349 +vn -0.577349 -0.577349 -0.577349 +s 1 +f 5/1/1 6/2/2 1/3/3 +f 6/1/2 7/2/4 3/4/5 +f 7/1/4 8/2/6 4/4/7 +f 8/1/6 5/2/1 1/4/3 +f 1/1/3 2/2/8 3/4/5 +f 8/1/6 7/2/4 6/4/2 +f 6/2/2 2/4/8 1/3/3 +f 2/3/8 6/1/2 3/4/5 +f 3/3/5 7/1/4 4/4/7 +f 4/3/7 8/1/6 1/4/3 +f 4/3/7 1/1/3 3/4/5 +f 5/3/1 8/1/6 6/4/2 +v -1.384247 1.252743 0.422195 +v -1.384247 2.069450 0.422195 +v -1.303725 1.252743 0.430041 +v -1.303725 2.069450 0.430041 +v -1.226298 1.252743 0.453279 +v -1.226298 2.069450 0.453279 +v -1.154941 1.252743 0.491015 +v -1.154941 2.069450 0.491015 +v -1.092396 1.252743 0.541799 +v -1.092396 2.069450 0.541799 +v -1.041066 1.252743 0.603679 +v -1.041066 2.069450 0.603679 +v -1.002925 1.252743 0.674278 +v -1.002925 2.069450 0.674278 +v -0.979437 1.252743 0.750883 +v -0.979437 2.069450 0.750883 +v -0.971507 1.252743 0.830548 +v -0.971507 2.069450 0.830548 +v -0.979437 1.252743 0.910214 +v -0.979437 2.069450 0.910214 +v -1.002925 1.252743 0.986818 +v -1.002925 2.069450 0.986818 +v -1.041066 1.252743 1.057417 +v -1.041066 2.069450 1.057417 +v -1.092396 1.252743 1.119298 +v -1.092396 2.069450 1.119298 +v -1.154941 1.252743 1.170082 +v -1.154941 2.069450 1.170082 +v -1.226298 1.252743 1.207818 +v -1.226298 2.069450 1.207818 +v -1.303726 1.252743 1.231055 +v -1.303726 2.069450 1.231055 +v -1.384247 1.252743 1.238902 +v -1.384247 2.069450 1.238902 +v -1.464769 1.252743 1.231055 +v -1.464769 2.069450 1.231055 +v -1.542196 1.252743 1.207818 +v -1.542196 2.069450 1.207818 +v -1.613554 1.252743 1.170082 +v -1.613554 2.069450 1.170082 +v -1.676099 1.252743 1.119298 +v -1.676099 2.069450 1.119298 +v -1.727429 1.252743 1.057417 +v -1.727429 2.069450 1.057417 +v -1.765570 1.252743 0.986818 +v -1.765570 2.069450 0.986818 +v -1.789057 1.252743 0.910214 +v -1.789057 2.069450 0.910214 +v -1.796988 1.252743 0.830548 +v -1.796988 2.069450 0.830548 +v -1.789057 1.252743 0.750882 +v -1.789057 2.069450 0.750882 +v -1.765570 1.252743 0.674278 +v -1.765570 2.069450 0.674278 +v -1.727428 1.252743 0.603679 +v -1.727428 2.069450 0.603679 +v -1.676098 1.252743 0.541798 +v -1.676098 2.069450 0.541798 +v -1.613553 1.252743 0.491015 +v -1.613553 2.069450 0.491015 +v -1.542196 1.252743 0.453279 +v -1.542196 2.069450 0.453279 +v -1.464768 1.252743 0.430041 +v -1.464768 2.069450 0.430041 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.000000 0.500000 +vt 0.009607 0.597546 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.146447 0.853554 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +vn 0.096985 0.000000 -0.995286 +vn 0.287455 0.000000 -0.957794 +vn 0.467486 0.000000 -0.884001 +vn 0.630338 0.000000 -0.776321 +vn 0.769672 0.000000 -0.638440 +vn 0.879812 0.000000 -0.475321 +vn 0.956070 0.000000 -0.293137 +vn 0.995081 0.000000 -0.099061 +vn 0.995081 0.000000 0.099061 +vn 0.956070 0.000000 0.293137 +vn 0.879813 0.000000 0.475321 +vn 0.769672 0.000000 0.638440 +vn 0.630338 0.000000 0.776321 +vn 0.467486 0.000000 0.884001 +vn 0.287455 0.000000 0.957794 +vn 0.096986 0.000000 0.995286 +vn -0.096986 0.000000 0.995286 +vn -0.287456 0.000000 0.957794 +vn -0.467486 0.000000 0.884001 +vn -0.630338 0.000000 0.776321 +vn -0.769673 0.000000 0.638438 +vn -0.879812 0.000000 0.475322 +vn -0.956071 0.000000 0.293135 +vn -0.995081 0.000000 0.099059 +vn -0.995081 -0.000000 -0.099061 +vn -0.956070 -0.000000 -0.293138 +vn -0.879812 -0.000000 -0.475322 +vn -0.769671 -0.000000 -0.638441 +vn -0.630337 -0.000000 -0.776322 +vn -0.467484 -0.000000 -0.884001 +vn -0.000000 1.000000 0.000000 +vn -0.096985 -0.000000 -0.995286 +vn -0.287454 -0.000000 -0.957794 +vn 0.000000 -1.000000 -0.000000 +s off +f 9/5/9 10/6/9 12/7/9 +f 11/5/10 12/6/10 14/7/10 +f 13/5/11 14/6/11 16/7/11 +f 15/5/12 16/6/12 18/7/12 +f 17/5/13 18/6/13 20/7/13 +f 19/5/14 20/6/14 22/7/14 +f 21/5/15 22/6/15 24/7/15 +f 23/5/16 24/6/16 26/7/16 +f 25/5/17 26/6/17 28/7/17 +f 27/5/18 28/6/18 30/7/18 +f 29/5/19 30/6/19 32/7/19 +f 31/5/20 32/6/20 34/7/20 +f 33/5/21 34/6/21 36/7/21 +f 35/5/22 36/6/22 38/7/22 +f 37/5/23 38/6/23 40/7/23 +f 39/5/24 40/6/24 42/7/24 +f 41/5/25 42/6/25 43/8/25 +f 43/5/26 44/6/26 45/8/26 +f 45/5/27 46/6/27 47/8/27 +f 47/5/28 48/6/28 49/8/28 +f 49/5/29 50/6/29 51/8/29 +f 51/5/30 52/6/30 53/8/30 +f 53/5/31 54/6/31 55/8/31 +f 55/5/32 56/6/32 57/8/32 +f 57/5/33 58/6/33 59/8/33 +f 59/5/34 60/6/34 61/8/34 +f 61/5/35 62/6/35 63/8/35 +f 63/5/36 64/6/36 65/8/36 +f 65/5/37 66/6/37 67/8/37 +f 67/5/38 68/6/38 69/8/38 +f 12/9/39 10/10/39 14/11/39 +f 71/5/40 72/6/40 9/8/40 +f 69/5/41 70/6/41 71/8/41 +f 9/9/42 11/10/42 71/11/42 +f 11/8/9 9/5/9 12/7/9 +f 13/8/10 11/5/10 14/7/10 +f 15/8/11 13/5/11 16/7/11 +f 17/8/12 15/5/12 18/7/12 +f 19/8/13 17/5/13 20/7/13 +f 21/8/14 19/5/14 22/7/14 +f 23/8/15 21/5/15 24/7/15 +f 25/8/16 23/5/16 26/7/16 +f 27/8/17 25/5/17 28/7/17 +f 29/8/18 27/5/18 30/7/18 +f 31/8/19 29/5/19 32/7/19 +f 33/8/20 31/5/20 34/7/20 +f 35/8/21 33/5/21 36/7/21 +f 37/8/22 35/5/22 38/7/22 +f 39/8/23 37/5/23 40/7/23 +f 41/8/24 39/5/24 42/7/24 +f 42/6/25 44/7/25 43/8/25 +f 44/6/26 46/7/26 45/8/26 +f 46/6/27 48/7/27 47/8/27 +f 48/6/28 50/7/28 49/8/28 +f 50/6/29 52/7/29 51/8/29 +f 52/6/30 54/7/30 53/8/30 +f 54/6/31 56/7/31 55/8/31 +f 56/6/32 58/7/32 57/8/32 +f 58/6/33 60/7/33 59/8/33 +f 60/6/34 62/7/34 61/8/34 +f 62/6/35 64/7/35 63/8/35 +f 64/6/36 66/7/36 65/8/36 +f 66/6/37 68/7/37 67/8/37 +f 68/6/38 70/7/38 69/8/38 +f 10/10/39 72/12/39 14/11/39 +f 72/12/39 70/13/39 14/11/39 +f 70/13/39 68/14/39 14/11/39 +f 68/14/39 66/15/39 14/11/39 +f 66/15/39 64/16/39 14/11/39 +f 64/16/39 62/17/39 14/11/39 +f 62/17/39 60/18/39 14/11/39 +f 60/18/39 58/19/39 14/11/39 +f 58/19/39 56/20/39 14/11/39 +f 56/20/39 54/21/39 14/11/39 +f 54/21/39 52/22/39 14/11/39 +f 52/22/39 50/23/39 14/11/39 +f 50/23/39 48/24/39 14/11/39 +f 48/24/39 46/25/39 14/11/39 +f 46/25/39 44/26/39 14/11/39 +f 44/26/39 42/27/39 14/11/39 +f 42/27/39 40/28/39 14/11/39 +f 40/28/39 38/29/39 14/11/39 +f 38/29/39 36/30/39 14/11/39 +f 36/30/39 34/31/39 14/11/39 +f 34/31/39 32/32/39 14/11/39 +f 32/32/39 30/33/39 14/11/39 +f 30/33/39 28/34/39 14/11/39 +f 28/34/39 26/35/39 14/11/39 +f 26/35/39 24/36/39 14/11/39 +f 24/36/39 22/37/39 14/11/39 +f 22/37/39 20/38/39 14/11/39 +f 20/38/39 18/39/39 16/40/39 +f 14/11/39 20/38/39 16/40/39 +f 72/6/40 10/7/40 9/8/40 +f 70/6/41 72/7/41 71/8/41 +f 11/10/42 13/12/42 71/11/42 +f 13/12/42 15/13/42 71/11/42 +f 15/13/42 17/14/42 71/11/42 +f 17/14/42 19/15/42 71/11/42 +f 19/15/42 21/16/42 71/11/42 +f 21/16/42 23/17/42 71/11/42 +f 23/17/42 25/18/42 71/11/42 +f 25/18/42 27/19/42 71/11/42 +f 27/19/42 29/20/42 71/11/42 +f 29/20/42 31/21/42 71/11/42 +f 31/21/42 33/22/42 71/11/42 +f 33/22/42 35/23/42 71/11/42 +f 35/23/42 37/24/42 71/11/42 +f 37/24/42 39/25/42 71/11/42 +f 39/25/42 41/26/42 71/11/42 +f 41/26/42 43/27/42 71/11/42 +f 43/27/42 45/28/42 71/11/42 +f 45/28/42 47/29/42 71/11/42 +f 47/29/42 49/30/42 71/11/42 +f 49/30/42 51/31/42 71/11/42 +f 51/31/42 53/32/42 71/11/42 +f 53/32/42 55/33/42 71/11/42 +f 55/33/42 57/34/42 71/11/42 +f 57/34/42 59/35/42 71/11/42 +f 59/35/42 61/36/42 71/11/42 +f 61/36/42 63/37/42 71/11/42 +f 63/37/42 65/38/42 71/11/42 +f 65/38/42 67/39/42 71/11/42 +f 67/39/42 69/40/42 71/11/42 +v 1.365790 1.252743 0.402799 +v 1.365790 2.069450 0.402799 +v 1.446312 1.252743 0.410646 +v 1.446312 2.069450 0.410646 +v 1.523739 1.252743 0.433883 +v 1.523739 2.069450 0.433883 +v 1.595097 1.252743 0.471619 +v 1.595097 2.069450 0.471619 +v 1.657642 1.252743 0.522403 +v 1.657642 2.069450 0.522403 +v 1.708972 1.252743 0.584284 +v 1.708972 2.069450 0.584284 +v 1.747113 1.252743 0.654883 +v 1.747113 2.069450 0.654883 +v 1.770600 1.252743 0.731487 +v 1.770600 2.069450 0.731487 +v 1.778531 1.252743 0.811153 +v 1.778531 2.069450 0.811153 +v 1.770600 1.252743 0.890818 +v 1.770600 2.069450 0.890818 +v 1.747113 1.252743 0.967423 +v 1.747113 2.069450 0.967423 +v 1.708972 1.252743 1.038022 +v 1.708972 2.069450 1.038022 +v 1.657642 1.252743 1.099902 +v 1.657642 2.069450 1.099902 +v 1.595097 1.252743 1.150686 +v 1.595097 2.069450 1.150686 +v 1.523739 1.252743 1.188422 +v 1.523739 2.069450 1.188422 +v 1.446312 1.252743 1.211660 +v 1.446312 2.069450 1.211660 +v 1.365790 1.252743 1.219506 +v 1.365790 2.069450 1.219506 +v 1.285269 1.252743 1.211660 +v 1.285269 2.069450 1.211660 +v 1.207841 1.252743 1.188422 +v 1.207841 2.069450 1.188422 +v 1.136484 1.252743 1.150686 +v 1.136484 2.069450 1.150686 +v 1.073939 1.252743 1.099902 +v 1.073939 2.069450 1.099902 +v 1.022609 1.252743 1.038021 +v 1.022609 2.069450 1.038021 +v 0.984468 1.252743 0.967422 +v 0.984468 2.069450 0.967422 +v 0.960981 1.252743 0.890818 +v 0.960981 2.069450 0.890818 +v 0.953050 1.252743 0.811152 +v 0.953050 2.069450 0.811152 +v 0.960981 1.252743 0.731486 +v 0.960981 2.069450 0.731486 +v 0.984468 1.252743 0.654882 +v 0.984468 2.069450 0.654882 +v 1.022609 1.252743 0.584283 +v 1.022609 2.069450 0.584283 +v 1.073939 1.252743 0.522403 +v 1.073939 2.069450 0.522403 +v 1.136485 1.252743 0.471619 +v 1.136485 2.069450 0.471619 +v 1.207842 1.252743 0.433883 +v 1.207842 2.069450 0.433883 +v 1.285269 1.252743 0.410646 +v 1.285269 2.069450 0.410646 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.000000 0.500000 +vt 0.009607 0.597546 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.146447 0.853554 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +vn -0.879812 0.000000 0.475321 +vn -0.956071 0.000000 0.293136 +vn -0.956070 -0.000000 -0.293137 +s off +f 73/41/9 74/42/9 76/43/9 +f 75/41/10 76/42/10 78/43/10 +f 77/41/11 78/42/11 80/43/11 +f 79/41/12 80/42/12 82/43/12 +f 81/41/13 82/42/13 84/43/13 +f 83/41/14 84/42/14 86/43/14 +f 85/41/15 86/42/15 88/43/15 +f 87/41/16 88/42/16 90/43/16 +f 89/41/17 90/42/17 92/43/17 +f 91/41/18 92/42/18 94/43/18 +f 93/41/19 94/42/19 96/43/19 +f 95/41/20 96/42/20 98/43/20 +f 97/41/21 98/42/21 100/43/21 +f 99/41/22 100/42/22 102/43/22 +f 101/41/23 102/42/23 104/43/23 +f 103/41/24 104/42/24 106/43/24 +f 105/41/25 106/42/25 107/44/25 +f 107/41/26 108/42/26 109/44/26 +f 109/41/27 110/42/27 111/44/27 +f 111/41/28 112/42/28 113/44/28 +f 113/41/29 114/42/29 115/44/29 +f 115/41/43 116/42/43 117/44/43 +f 117/41/44 118/42/44 119/44/44 +f 119/41/32 120/42/32 121/44/32 +f 121/41/33 122/42/33 123/44/33 +f 123/41/45 124/42/45 125/44/45 +f 125/41/35 126/42/35 127/44/35 +f 127/41/36 128/42/36 129/44/36 +f 129/41/37 130/42/37 131/44/37 +f 131/41/38 132/42/38 133/44/38 +f 76/45/39 74/46/39 78/47/39 +f 135/41/40 136/42/40 73/44/40 +f 133/41/41 134/42/41 135/44/41 +f 73/45/42 75/46/42 135/47/42 +f 75/44/9 73/41/9 76/43/9 +f 77/44/10 75/41/10 78/43/10 +f 79/44/11 77/41/11 80/43/11 +f 81/44/12 79/41/12 82/43/12 +f 83/44/13 81/41/13 84/43/13 +f 85/44/14 83/41/14 86/43/14 +f 87/44/15 85/41/15 88/43/15 +f 89/44/16 87/41/16 90/43/16 +f 91/44/17 89/41/17 92/43/17 +f 93/44/18 91/41/18 94/43/18 +f 95/44/19 93/41/19 96/43/19 +f 97/44/20 95/41/20 98/43/20 +f 99/44/21 97/41/21 100/43/21 +f 101/44/22 99/41/22 102/43/22 +f 103/44/23 101/41/23 104/43/23 +f 105/44/24 103/41/24 106/43/24 +f 106/42/25 108/43/25 107/44/25 +f 108/42/26 110/43/26 109/44/26 +f 110/42/27 112/43/27 111/44/27 +f 112/42/28 114/43/28 113/44/28 +f 114/42/29 116/43/29 115/44/29 +f 116/42/43 118/43/43 117/44/43 +f 118/42/44 120/43/44 119/44/44 +f 120/42/32 122/43/32 121/44/32 +f 122/42/33 124/43/33 123/44/33 +f 124/42/45 126/43/45 125/44/45 +f 126/42/35 128/43/35 127/44/35 +f 128/42/36 130/43/36 129/44/36 +f 130/42/37 132/43/37 131/44/37 +f 132/42/38 134/43/38 133/44/38 +f 74/46/39 136/48/39 78/47/39 +f 136/48/39 134/49/39 78/47/39 +f 134/49/39 132/50/39 78/47/39 +f 132/50/39 130/51/39 78/47/39 +f 130/51/39 128/52/39 78/47/39 +f 128/52/39 126/53/39 78/47/39 +f 126/53/39 124/54/39 78/47/39 +f 124/54/39 122/55/39 78/47/39 +f 122/55/39 120/56/39 78/47/39 +f 120/56/39 118/57/39 78/47/39 +f 118/57/39 116/58/39 78/47/39 +f 116/58/39 114/59/39 78/47/39 +f 114/59/39 112/60/39 78/47/39 +f 112/60/39 110/61/39 78/47/39 +f 110/61/39 108/62/39 78/47/39 +f 108/62/39 106/63/39 78/47/39 +f 106/63/39 104/64/39 78/47/39 +f 104/64/39 102/65/39 78/47/39 +f 102/65/39 100/66/39 78/47/39 +f 100/66/39 98/67/39 78/47/39 +f 98/67/39 96/68/39 78/47/39 +f 96/68/39 94/69/39 78/47/39 +f 94/69/39 92/70/39 78/47/39 +f 92/70/39 90/71/39 78/47/39 +f 90/71/39 88/72/39 78/47/39 +f 88/72/39 86/73/39 78/47/39 +f 86/73/39 84/74/39 78/47/39 +f 84/74/39 82/75/39 80/76/39 +f 78/47/39 84/74/39 80/76/39 +f 136/42/40 74/43/40 73/44/40 +f 134/42/41 136/43/41 135/44/41 +f 75/46/42 77/48/42 135/47/42 +f 77/48/42 79/49/42 135/47/42 +f 79/49/42 81/50/42 135/47/42 +f 81/50/42 83/51/42 135/47/42 +f 83/51/42 85/52/42 135/47/42 +f 85/52/42 87/53/42 135/47/42 +f 87/53/42 89/54/42 135/47/42 +f 89/54/42 91/55/42 135/47/42 +f 91/55/42 93/56/42 135/47/42 +f 93/56/42 95/57/42 135/47/42 +f 95/57/42 97/58/42 135/47/42 +f 97/58/42 99/59/42 135/47/42 +f 99/59/42 101/60/42 135/47/42 +f 101/60/42 103/61/42 135/47/42 +f 103/61/42 105/62/42 135/47/42 +f 105/62/42 107/63/42 135/47/42 +f 107/63/42 109/64/42 135/47/42 +f 109/64/42 111/65/42 135/47/42 +f 111/65/42 113/66/42 135/47/42 +f 113/66/42 115/67/42 135/47/42 +f 115/67/42 117/68/42 135/47/42 +f 117/68/42 119/69/42 135/47/42 +f 119/69/42 121/70/42 135/47/42 +f 121/70/42 123/71/42 135/47/42 +f 123/71/42 125/72/42 135/47/42 +f 125/72/42 127/73/42 135/47/42 +f 127/73/42 129/74/42 135/47/42 +f 129/74/42 131/75/42 135/47/42 +f 131/75/42 133/76/42 135/47/42 +v -2.345663 0.025178 -0.194338 +v -2.345663 0.025178 -0.594338 +v -1.345663 0.025178 -0.594338 +v -1.345663 0.025178 -0.194338 +v -2.345663 1.525178 -0.194338 +v -2.345663 1.525178 -0.594338 +v -1.345663 1.525178 -0.594338 +v -1.345663 1.525178 -0.194338 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vn -1.000000 0.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 1.000000 -0.000000 0.000000 +vn 0.000000 0.000000 1.000000 +s off +f 141/77/46 142/78/46 137/79/46 +f 142/77/47 143/78/47 138/79/47 +f 143/77/48 144/78/48 140/80/48 +f 144/77/49 141/78/49 137/80/49 +f 137/77/42 138/78/42 139/80/42 +f 144/77/39 143/78/39 142/80/39 +f 142/78/46 138/80/46 137/79/46 +f 143/78/47 139/80/47 138/79/47 +f 139/79/48 143/77/48 140/80/48 +f 140/79/49 144/77/49 137/80/49 +f 140/79/42 137/77/42 139/80/42 +f 141/79/39 144/77/39 142/80/39 +v 1.364119 0.019809 -0.205019 +v 1.364119 0.019809 -0.605019 +v 2.364120 0.019809 -0.605019 +v 2.364120 0.019809 -0.205019 +v 1.364119 1.419809 -0.205019 +v 1.364119 1.419809 -0.605019 +v 2.364120 1.419809 -0.605019 +v 2.364120 1.419809 -0.205019 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +s off +f 149/81/46 150/82/46 145/83/46 +f 150/81/47 151/82/47 146/83/47 +f 151/81/48 152/82/48 148/84/48 +f 152/81/49 149/82/49 145/84/49 +f 145/81/42 146/82/42 147/84/42 +f 152/81/39 151/82/39 150/84/39 +f 150/82/46 146/84/46 145/83/46 +f 151/82/47 147/84/47 146/83/47 +f 147/83/48 151/81/48 148/84/48 +f 148/83/49 152/81/49 145/84/49 +f 148/83/42 145/81/42 147/84/42 +f 149/83/39 152/81/39 150/84/39 +v -1.384247 0.015116 3.993316 +v -1.384247 0.015113 -0.006684 +v -1.189157 0.034331 3.993316 +v -1.189157 0.034328 -0.006684 +v -1.001564 0.091236 3.993316 +v -1.001564 0.091234 -0.006684 +v -0.828677 0.183646 3.993316 +v -0.828677 0.183644 -0.006684 +v -0.677140 0.308009 3.993316 +v -0.677140 0.308007 -0.006684 +v -0.552777 0.459546 3.993316 +v -0.552777 0.459543 -0.006684 +v -0.460368 0.632432 3.993316 +v -0.460368 0.632430 -0.006684 +v -0.403462 0.820025 3.993316 +v -0.403462 0.820023 -0.006684 +v -0.384247 1.015116 3.993316 +v -0.384247 1.015113 -0.006684 +v -0.403462 1.210206 3.993316 +v -0.403462 1.210203 -0.006685 +v -0.460368 1.397799 3.993315 +v -0.460368 1.397797 -0.006685 +v -0.552777 1.570686 3.993315 +v -0.552777 1.570683 -0.006685 +v -0.677140 1.722223 3.993315 +v -0.677140 1.722220 -0.006685 +v -0.828677 1.846586 3.993315 +v -0.828677 1.846583 -0.006685 +v -1.001564 1.938995 3.993315 +v -1.001564 1.938993 -0.006685 +v -1.189157 1.995901 3.993315 +v -1.189157 1.995899 -0.006685 +v -1.384247 2.015116 3.993315 +v -1.384247 2.015113 -0.006685 +v -1.579338 1.995901 3.993315 +v -1.579338 1.995898 -0.006685 +v -1.766931 1.938995 3.993315 +v -1.766931 1.938993 -0.006685 +v -1.939818 1.846585 3.993315 +v -1.939818 1.846583 -0.006685 +v -2.091354 1.722222 3.993315 +v -2.091354 1.722220 -0.006685 +v -2.215717 1.570685 3.993315 +v -2.215717 1.570683 -0.006685 +v -2.308127 1.397799 3.993315 +v -2.308127 1.397796 -0.006685 +v -2.365032 1.210205 3.993316 +v -2.365032 1.210203 -0.006685 +v -2.384247 1.015115 3.993316 +v -2.384247 1.015112 -0.006684 +v -2.365032 0.820024 3.993316 +v -2.365032 0.820022 -0.006684 +v -2.308126 0.632431 3.993316 +v -2.308126 0.632429 -0.006684 +v -2.215716 0.459545 3.993316 +v -2.215716 0.459542 -0.006684 +v -2.091353 0.308008 3.993316 +v -2.091353 0.308006 -0.006684 +v -1.939816 0.183645 3.993316 +v -1.939816 0.183643 -0.006684 +v -1.766929 0.091236 3.993316 +v -1.766929 0.091233 -0.006684 +v -1.579336 0.034330 3.993316 +v -1.579336 0.034328 -0.006684 +v -1.384247 0.619839 -0.156215 +v -1.307133 0.627434 -0.156215 +v -1.232982 0.649928 -0.156215 +v -1.164645 0.686455 -0.156215 +v -1.104746 0.735612 -0.156215 +v -1.055589 0.795511 -0.156215 +v -1.019062 0.863848 -0.156216 +v -0.996568 0.937999 -0.156216 +v -0.988973 1.015113 -0.156216 +v -0.996568 1.092227 -0.156216 +v -1.019062 1.166378 -0.156216 +v -1.055589 1.234715 -0.156216 +v -1.104746 1.294614 -0.156216 +v -1.164645 1.343771 -0.156216 +v -1.232982 1.380298 -0.156216 +v -1.307133 1.402792 -0.156216 +v -1.384247 1.410387 -0.156216 +v -1.461361 1.402792 -0.156216 +v -1.535512 1.380298 -0.156216 +v -1.603849 1.343771 -0.156216 +v -1.663748 1.294613 -0.156216 +v -1.712905 1.234715 -0.156216 +v -1.749432 1.166377 -0.156216 +v -1.771926 1.092227 -0.156216 +v -1.779521 1.015113 -0.156216 +v -1.771925 0.937999 -0.156216 +v -1.749432 0.863848 -0.156216 +v -1.712905 0.795510 -0.156215 +v -1.663747 0.735612 -0.156215 +v -1.603849 0.686455 -0.156215 +v -1.535511 0.649928 -0.156215 +v -1.461360 0.627434 -0.156215 +v -1.384247 0.605419 4.159918 +v -1.304319 0.613291 4.159918 +v -1.227463 0.636605 4.159918 +v -1.156632 0.674465 4.159918 +v -1.094548 0.725416 4.159918 +v -1.043596 0.787500 4.159918 +v -1.005736 0.858332 4.159918 +v -0.982422 0.935188 4.159918 +v -0.974550 1.015116 4.159918 +v -0.982422 1.095044 4.159918 +v -1.005736 1.171900 4.159918 +v -1.043596 1.242731 4.159918 +v -1.094548 1.304815 4.159918 +v -1.156632 1.355766 4.159918 +v -1.227463 1.393626 4.159918 +v -1.304319 1.416940 4.159918 +v -1.384247 1.424813 4.159918 +v -1.464175 1.416940 4.159918 +v -1.541031 1.393626 4.159918 +v -1.611863 1.355766 4.159918 +v -1.673947 1.304815 4.159918 +v -1.724898 1.242731 4.159918 +v -1.762758 1.171900 4.159918 +v -1.786072 1.095043 4.159918 +v -1.793944 1.015115 4.159918 +v -1.786072 0.935187 4.159918 +v -1.762757 0.858331 4.159918 +v -1.724897 0.787500 4.159918 +v -1.673946 0.725416 4.159918 +v -1.611862 0.674465 4.159918 +v -1.541031 0.636605 4.159918 +v -1.464174 0.613291 4.159918 +v 0.006492 3.927105 -3.525056 +v 0.006492 4.374522 -3.525055 +v 0.050135 3.927105 -3.520757 +v 0.050135 4.374522 -3.520757 +v 0.092102 3.927104 -3.508027 +v 0.092102 4.374522 -3.508027 +v 0.130778 3.927104 -3.487354 +v 0.130778 4.374522 -3.487354 +v 0.164678 3.927104 -3.459533 +v 0.164678 4.374522 -3.459533 +v 0.192499 3.927104 -3.425632 +v 0.192499 4.374522 -3.425633 +v 0.213172 3.927105 -3.386956 +v 0.213172 4.374522 -3.386957 +v 0.225902 3.927105 -3.344990 +v 0.225902 4.374522 -3.344990 +v 0.230201 3.927105 -3.301347 +v 0.230201 4.374522 -3.301347 +v 0.225902 3.927105 -3.257704 +v 0.225902 4.374522 -3.257703 +v 0.213172 3.927105 -3.215737 +v 0.213172 4.374522 -3.215738 +v 0.192499 3.927105 -3.177061 +v 0.192499 4.374522 -3.177062 +v 0.164678 3.927105 -3.143161 +v 0.164678 4.374522 -3.143161 +v 0.130778 3.927105 -3.115340 +v 0.130778 4.374522 -3.115340 +v 0.092102 3.927105 -3.094666 +v 0.092102 4.374522 -3.094667 +v 0.050135 3.927105 -3.081937 +v 0.050135 4.374522 -3.081936 +v 0.006492 3.927105 -3.077638 +v 0.006492 4.374522 -3.077638 +v -0.037151 3.927105 -3.081937 +v -0.037151 4.374522 -3.081936 +v -0.079118 3.927105 -3.094666 +v -0.079118 4.374522 -3.094667 +v -0.117794 3.927105 -3.115340 +v -0.117794 4.374522 -3.115340 +v -0.151694 3.927105 -3.143161 +v -0.151694 4.374522 -3.143161 +v -0.179515 3.927105 -3.177061 +v -0.179515 4.374522 -3.177062 +v -0.200188 3.927105 -3.215737 +v -0.200188 4.374522 -3.215738 +v -0.212918 3.927105 -3.257704 +v -0.212918 4.374522 -3.257704 +v -0.217217 3.927105 -3.301347 +v -0.217217 4.374522 -3.301347 +v -0.212918 3.927105 -3.344991 +v -0.212918 4.374522 -3.344990 +v -0.200188 3.927105 -3.386957 +v -0.200188 4.374522 -3.386957 +v -0.179515 3.927104 -3.425633 +v -0.179515 4.374522 -3.425633 +v -0.151694 3.927104 -3.459533 +v -0.151694 4.374522 -3.459533 +v -0.117794 3.927104 -3.487354 +v -0.117794 4.374522 -3.487354 +v -0.079117 3.927104 -3.508027 +v -0.079117 4.374522 -3.508027 +v -0.037151 3.927105 -3.520757 +v -0.037151 4.374522 -3.520757 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.853553 0.853553 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.500000 0.000000 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.777785 0.084265 +vt 0.853553 0.146447 +vt 0.084265 0.222215 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.038060 0.308659 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +vt 0.146447 0.853554 +vt 0.990393 0.597545 +vt 0.084266 0.777786 +vt 0.038060 0.691342 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.009607 0.597546 +vt 0.000000 0.500000 +vt 0.915735 0.222215 +vt 0.009607 0.402455 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vn 0.098018 -0.995185 0.000001 +vn 0.290285 -0.956940 0.000001 +vn 0.471397 -0.881921 0.000001 +vn 0.634393 -0.773010 0.000000 +vn 0.773010 -0.634393 0.000000 +vn 0.881921 -0.471397 0.000000 +vn 0.956940 -0.290285 0.000000 +vn 0.995185 -0.098017 0.000000 +vn 0.995185 0.098017 -0.000000 +vn 0.956940 0.290285 -0.000000 +vn 0.881922 0.471396 -0.000000 +vn 0.773010 0.634393 -0.000000 +vn 0.634393 0.773011 -0.000000 +vn 0.471397 0.881921 -0.000001 +vn 0.290284 0.956941 -0.000001 +vn 0.098017 0.995185 -0.000001 +vn -0.098017 0.995185 -0.000001 +vn -0.290285 0.956940 -0.000001 +vn -0.471397 0.881921 -0.000001 +vn -0.634394 0.773010 -0.000000 +vn -0.773011 0.634393 -0.000000 +vn -0.881922 0.471396 -0.000000 +vn -0.956941 0.290283 -0.000000 +vn -0.995185 0.098017 -0.000000 +vn -0.995185 -0.098018 0.000000 +vn -0.956940 -0.290286 0.000000 +vn -0.881920 -0.471398 0.000000 +vn -0.773010 -0.634394 0.000000 +vn -0.634393 -0.773011 0.000000 +vn -0.471395 -0.881922 0.000001 +vn -0.239975 -0.023636 -0.970491 +vn -0.098017 -0.995185 0.000001 +vn -0.290283 -0.956941 0.000001 +vn -0.210907 0.173086 0.962060 +vn 0.152975 0.186400 -0.970492 +vn -0.023635 -0.239975 -0.970491 +vn -0.152975 0.186400 -0.970492 +vn 0.239975 -0.023636 -0.970491 +vn -0.212662 -0.113672 -0.970491 +vn 0.069999 0.230752 -0.970492 +vn 0.113670 -0.212663 -0.970491 +vn -0.212662 0.113670 -0.970492 +vn 0.230752 0.069998 -0.970492 +vn -0.152975 -0.186401 -0.970491 +vn -0.023636 0.239974 -0.970492 +vn 0.186400 -0.152976 -0.970491 +vn -0.239974 0.023634 -0.970491 +vn 0.186401 0.152974 -0.970491 +vn -0.069998 -0.230753 -0.970491 +vn -0.113670 0.212662 -0.970492 +vn 0.230753 -0.069999 -0.970491 +vn -0.230752 -0.069999 -0.970491 +vn 0.113670 0.212662 -0.970491 +vn 0.069998 -0.230753 -0.970491 +vn -0.186401 0.152974 -0.970491 +vn 0.239975 0.023634 -0.970492 +vn -0.186400 -0.152976 -0.970491 +vn 0.023635 0.239974 -0.970492 +vn 0.152975 -0.186401 -0.970491 +vn -0.230752 0.069998 -0.970492 +vn 0.023636 -0.239975 -0.970491 +vn 0.212662 0.113670 -0.970492 +vn -0.113670 -0.212664 -0.970491 +vn -0.069999 0.230751 -0.970492 +vn 0.212663 -0.113671 -0.970491 +vn 0.271524 0.026743 0.962060 +vn -0.210906 -0.173085 0.962060 +vn 0.026743 0.271524 0.962060 +vn 0.173086 -0.210906 0.962060 +vn -0.261089 0.079201 0.962060 +vn 0.026743 -0.271523 0.962060 +vn 0.240621 0.128616 0.962060 +vn -0.128614 -0.240621 0.962060 +vn -0.079201 0.261090 0.962060 +vn 0.240621 -0.128614 0.962060 +vn -0.271524 -0.026742 0.962060 +vn 0.173087 0.210907 0.962060 +vn -0.026742 -0.271523 0.962060 +vn -0.173087 0.210907 0.962060 +vn 0.271524 -0.026742 0.962060 +vn -0.240621 -0.128615 0.962060 +vn 0.079201 0.261090 0.962060 +vn 0.128614 -0.240621 0.962060 +vn -0.240621 0.128616 0.962060 +vn 0.261089 0.079202 0.962060 +vn -0.173086 -0.210906 0.962060 +vn -0.026743 0.271524 0.962060 +vn 0.210907 -0.173085 0.962060 +vn -0.271524 0.026743 0.962060 +vn 0.210907 0.173087 0.962060 +vn -0.079201 -0.261088 0.962060 +vn -0.128615 0.240622 0.962060 +vn 0.261089 -0.079200 0.962060 +vn -0.261089 -0.079201 0.962060 +vn 0.128615 0.240622 0.962060 +vn 0.079201 -0.261088 0.962060 +vn 0.098012 0.000001 -0.995185 +vn 0.290289 -0.000001 -0.956939 +vn 0.471395 0.000001 -0.881922 +vn 0.634385 0.000001 -0.773018 +vn 0.773012 -0.000001 -0.634391 +vn 0.881922 -0.000001 -0.471395 +vn 0.956942 -0.000000 -0.290279 +vn 0.995184 -0.000000 -0.098022 +vn 0.995184 0.000000 0.098020 +vn 0.956940 -0.000000 0.290285 +vn 0.881922 0.000001 0.471395 +vn 0.773012 0.000001 0.634391 +vn 0.634398 0.000001 0.773007 +vn 0.471395 -0.000001 0.881922 +vn 0.290289 0.000001 0.956939 +vn 0.098010 -0.000001 0.995185 +vn -0.098033 0.000001 0.995183 +vn -0.290269 -0.000001 0.956945 +vn -0.471412 0.000001 0.881913 +vn -0.634385 -0.000001 0.773018 +vn -0.773012 0.000001 0.634391 +vn -0.881922 0.000001 0.471395 +vn -0.956942 0.000000 0.290279 +vn -0.995184 0.000000 0.098022 +vn -0.995184 -0.000000 -0.098020 +vn -0.956939 0.000000 -0.290289 +vn -0.881922 0.000001 -0.471395 +vn -0.773003 0.000001 -0.634402 +vn -0.634398 -0.000001 -0.773007 +vn -0.471395 0.000001 -0.881922 +vn -0.098032 -0.000001 -0.995183 +vn -0.290292 0.000001 -0.956938 +vn 0.098017 -0.995185 0.000001 +vn 0.881921 -0.471396 0.000000 +vn 0.881921 0.471397 -0.000000 +vn 0.634394 0.773010 -0.000001 +vn 0.471396 0.881922 -0.000001 +vn 0.290285 0.956940 -0.000001 +vn -0.098018 0.995185 -0.000001 +vn -0.881920 -0.471399 0.000000 +vn -0.239974 -0.023636 -0.970491 +vn -0.210907 0.173087 0.962060 +vn 0.000000 -0.000003 -1.000000 +vn 0.152975 0.186400 -0.970491 +vn 0.239975 -0.023635 -0.970491 +vn -0.212662 -0.113674 -0.970491 +vn 0.069998 0.230752 -0.970492 +vn 0.113671 -0.212663 -0.970491 +vn -0.212664 0.113668 -0.970491 +vn 0.230752 0.069998 -0.970491 +vn -0.023635 0.239974 -0.970492 +vn 0.186401 -0.152975 -0.970491 +vn -0.239974 0.023635 -0.970492 +vn 0.186400 0.152975 -0.970491 +vn 0.230753 -0.069998 -0.970491 +vn -0.230753 -0.069998 -0.970491 +vn -0.186400 0.152974 -0.970492 +vn 0.239974 0.023635 -0.970492 +vn -0.186400 -0.152975 -0.970491 +vn 0.023635 -0.239975 -0.970491 +vn 0.212664 0.113668 -0.970491 +vn -0.113670 -0.212663 -0.970491 +vn -0.069998 0.230752 -0.970492 +vn 0.212662 -0.113674 -0.970491 +vn -0.210905 -0.173086 0.962060 +vn -0.261089 0.079200 0.962060 +vn 0.026743 -0.271523 0.962061 +vn 0.240622 0.128615 0.962060 +vn 0.240621 -0.128615 0.962060 +vn -0.271523 -0.026743 0.962060 +vn 0.271523 -0.026743 0.962060 +vn 0.079200 0.261090 0.962060 +vn 0.128615 -0.240620 0.962060 +vn -0.240622 0.128615 0.962060 +vn 0.261089 0.079200 0.962060 +vn -0.173085 -0.210906 0.962060 +vn 0.210906 -0.173086 0.962060 +vn -0.271524 0.026742 0.962060 +vn -0.079200 -0.261088 0.962061 +vn -0.261089 -0.079200 0.962060 +vn 0.079200 -0.261088 0.962060 +vn 0.098033 -0.000001 -0.995183 +vn 0.290269 0.000001 -0.956945 +vn 0.634398 -0.000001 -0.773007 +vn 0.995184 -0.000000 0.098022 +vn 0.956942 0.000000 0.290279 +vn 0.634385 -0.000001 0.773018 +vn 0.471412 0.000001 0.881913 +vn 0.290269 -0.000001 0.956945 +vn 0.098032 0.000001 0.995183 +vn -0.098012 -0.000001 0.995185 +vn -0.290289 0.000001 0.956939 +vn -0.471395 -0.000001 0.881922 +vn -0.634398 0.000001 0.773007 +vn -0.995184 0.000000 -0.098022 +vn -0.773012 -0.000001 -0.634391 +vn -0.634385 0.000001 -0.773018 +vn -0.098010 0.000001 -0.995185 +vn -0.290272 -0.000001 -0.956944 +vn 0.000000 -1.000000 -0.000019 +vn -0.000002 -1.000000 0.000007 +vn 0.000001 -1.000000 -0.000000 +vn -0.000027 -1.000000 -0.000008 +vn -0.000009 -1.000000 -0.000001 +vn -0.000005 -1.000000 0.000000 +vn -0.000003 -1.000000 0.000001 +vn -0.000002 -1.000000 0.000001 +vn -0.000001 -1.000000 0.000001 +vn 0.000000 -1.000000 0.000001 +s off +f 153/85/50 154/86/50 156/87/50 +f 155/85/51 156/86/51 158/87/51 +f 157/85/52 158/86/52 160/87/52 +f 159/85/53 160/86/53 162/87/53 +f 161/85/54 162/86/54 164/87/54 +f 163/85/55 164/86/55 166/87/55 +f 165/85/56 166/86/56 168/87/56 +f 167/85/57 168/86/57 170/87/57 +f 169/85/58 170/86/58 172/87/58 +f 171/85/59 172/86/59 174/87/59 +f 173/85/60 174/86/60 176/87/60 +f 175/85/61 176/86/61 178/87/61 +f 177/85/62 178/86/62 180/87/62 +f 179/85/63 180/86/63 182/87/63 +f 181/85/64 182/86/64 184/87/64 +f 183/85/65 184/86/65 186/87/65 +f 185/85/66 186/86/66 187/88/66 +f 187/85/67 188/86/67 189/88/67 +f 189/85/68 190/86/68 191/88/68 +f 191/85/69 192/86/69 193/88/69 +f 193/85/70 194/86/70 195/88/70 +f 195/85/71 196/86/71 197/88/71 +f 197/85/72 198/86/72 199/88/72 +f 199/85/73 200/86/73 201/88/73 +f 201/85/74 202/86/74 203/88/74 +f 203/85/75 204/86/75 205/88/75 +f 205/85/76 206/86/76 207/88/76 +f 207/85/77 208/86/77 209/88/77 +f 209/85/78 210/86/78 211/88/78 +f 211/85/79 212/86/79 213/88/79 +f 204/85/80 202/86/80 242/88/80 +f 215/85/81 216/86/81 153/88/81 +f 213/85/82 214/86/82 215/88/82 +f 193/85/83 195/86/83 269/88/83 +f 246/89/47 248/90/47 247/91/47 +f 180/85/84 178/86/84 229/87/84 +f 154/85/85 216/86/85 217/88/85 +f 194/85/86 192/86/86 237/88/86 +f 170/85/87 168/86/87 224/87/87 +f 208/85/88 206/86/88 244/88/88 +f 184/85/89 182/86/89 231/87/89 +f 160/85/90 158/86/90 219/87/90 +f 198/85/91 196/86/91 239/88/91 +f 174/85/92 172/86/92 226/87/92 +f 212/85/93 210/86/93 246/88/93 +f 188/85/94 186/86/94 234/88/94 +f 164/85/95 162/86/95 221/87/95 +f 202/85/96 200/86/96 241/88/96 +f 178/85/97 176/86/97 228/87/97 +f 216/85/98 214/86/98 248/88/98 +f 192/85/99 190/86/99 236/88/99 +f 168/85/100 166/86/100 223/87/100 +f 206/85/101 204/86/101 243/88/101 +f 182/85/102 180/86/102 230/87/102 +f 158/85/103 156/86/103 218/87/103 +f 196/85/104 194/86/104 238/88/104 +f 172/85/105 170/86/105 225/87/105 +f 210/85/106 208/86/106 245/88/106 +f 186/85/107 184/86/107 232/87/107 +f 162/85/108 160/86/108 220/87/108 +f 200/85/109 198/86/109 240/88/109 +f 156/85/110 154/86/110 217/87/110 +f 176/85/111 174/86/111 227/87/111 +f 214/85/112 212/86/112 247/88/112 +f 190/85/113 188/86/113 235/88/113 +f 166/85/114 164/86/114 222/87/114 +f 249/92/49 250/93/49 280/94/49 +f 169/85/115 171/86/115 258/87/115 +f 207/85/116 209/86/116 276/88/116 +f 183/85/117 185/86/117 265/87/117 +f 159/85/118 161/86/118 253/87/118 +f 197/85/119 199/86/119 271/88/119 +f 153/85/120 155/86/120 250/87/120 +f 173/85/121 175/86/121 260/87/121 +f 211/85/122 213/86/122 278/88/122 +f 187/85/123 189/86/123 266/88/123 +f 163/85/124 165/86/124 255/87/124 +f 201/85/125 203/86/125 273/88/125 +f 177/85/126 179/86/126 262/87/126 +f 215/85/127 153/86/127 280/88/127 +f 191/85/128 193/86/128 268/88/128 +f 167/85/129 169/86/129 257/87/129 +f 205/85/130 207/86/130 275/88/130 +f 181/85/131 183/86/131 264/87/131 +f 157/85/132 159/86/132 252/87/132 +f 195/85/133 197/86/133 270/88/133 +f 171/85/134 173/86/134 259/87/134 +f 209/85/135 211/86/135 277/88/135 +f 185/85/136 187/86/136 265/88/136 +f 161/85/137 163/86/137 254/87/137 +f 199/85/138 201/86/138 272/88/138 +f 175/85/139 177/86/139 261/87/139 +f 213/85/140 215/86/140 279/88/140 +f 189/85/141 191/86/141 267/88/141 +f 165/85/142 167/86/142 256/87/142 +f 203/85/143 205/86/143 274/88/143 +f 179/85/144 181/86/144 263/87/144 +f 155/85/145 157/86/145 251/87/145 +f 281/85/146 282/86/146 284/87/146 +f 283/85/147 284/86/147 286/87/147 +f 285/85/148 286/86/148 288/87/148 +f 287/85/149 288/86/149 290/87/149 +f 289/85/150 290/86/150 292/87/150 +f 291/85/151 292/86/151 294/87/151 +f 293/85/152 294/86/152 296/87/152 +f 295/85/153 296/86/153 298/87/153 +f 297/85/154 298/86/154 300/87/154 +f 299/85/155 300/86/155 302/87/155 +f 301/85/156 302/86/156 304/87/156 +f 303/85/157 304/86/157 306/87/157 +f 305/85/158 306/86/158 308/87/158 +f 307/85/159 308/86/159 310/87/159 +f 309/85/160 310/86/160 312/87/160 +f 311/85/161 312/86/161 314/87/161 +f 313/85/162 314/86/162 315/88/162 +f 315/85/163 316/86/163 317/88/163 +f 317/85/164 318/86/164 319/88/164 +f 319/85/165 320/86/165 321/88/165 +f 321/85/166 322/86/166 323/88/166 +f 323/85/167 324/86/167 325/88/167 +f 325/85/168 326/86/168 327/88/168 +f 327/85/169 328/86/169 329/88/169 +f 329/85/170 330/86/170 331/88/170 +f 331/85/171 332/86/171 333/88/171 +f 333/85/172 334/86/172 335/88/172 +f 335/85/173 336/86/173 337/88/173 +f 337/85/174 338/86/174 339/88/174 +f 339/85/175 340/86/175 341/88/175 +f 284/92/39 282/93/39 286/94/39 +f 343/85/176 344/86/176 281/88/176 +f 341/85/177 342/86/177 344/87/177 +f 281/92/42 283/93/42 343/94/42 +f 155/88/178 153/85/178 156/87/178 +f 157/88/51 155/85/51 158/87/51 +f 159/88/52 157/85/52 160/87/52 +f 161/88/53 159/85/53 162/87/53 +f 163/88/54 161/85/54 164/87/54 +f 165/88/179 163/85/179 166/87/179 +f 167/88/56 165/85/56 168/87/56 +f 169/88/57 167/85/57 170/87/57 +f 171/88/58 169/85/58 172/87/58 +f 173/88/59 171/85/59 174/87/59 +f 175/88/180 173/85/180 176/87/180 +f 177/88/61 175/85/61 178/87/61 +f 179/88/181 177/85/181 180/87/181 +f 181/88/182 179/85/182 182/87/182 +f 183/88/183 181/85/183 184/87/183 +f 185/88/65 183/85/65 186/87/65 +f 186/86/184 188/87/184 187/88/184 +f 188/86/67 190/87/67 189/88/67 +f 190/86/68 192/87/68 191/88/68 +f 192/86/69 194/87/69 193/88/69 +f 194/86/70 196/87/70 195/88/70 +f 196/86/71 198/87/71 197/88/71 +f 198/86/72 200/87/72 199/88/72 +f 200/86/73 202/87/73 201/88/73 +f 202/86/74 204/87/74 203/88/74 +f 204/86/75 206/87/75 205/88/75 +f 206/86/185 208/87/185 207/88/185 +f 208/86/77 210/87/77 209/88/77 +f 210/86/78 212/87/78 211/88/78 +f 212/86/79 214/87/79 213/88/79 +f 202/86/186 241/87/186 242/88/186 +f 216/86/81 154/87/81 153/88/81 +f 214/86/82 216/87/82 215/88/82 +f 195/86/187 270/87/187 269/88/187 +f 245/95/47 248/90/47 246/89/47 +f 244/96/47 248/90/47 245/95/47 +f 234/97/47 236/98/47 235/99/47 +f 234/97/47 237/100/47 236/98/47 +f 234/97/47 238/101/47 237/100/47 +f 229/102/47 231/103/47 230/104/47 +f 228/105/47 231/103/47 229/102/47 +f 219/94/47 221/106/47 220/107/47 +f 219/94/47 222/108/47 221/106/47 +f 218/92/47 217/93/47 219/94/47 +f 217/93/47 248/90/47 219/94/47 +f 248/90/47 244/96/47 219/94/47 +f 219/94/47 244/96/47 222/108/47 +f 244/96/188 243/109/188 222/108/188 +f 222/108/188 243/109/188 223/110/188 +f 223/110/47 243/109/47 224/111/47 +f 243/109/47 242/112/47 224/111/47 +f 242/112/47 241/113/47 224/111/47 +f 241/113/47 240/114/47 224/111/47 +f 224/111/47 240/114/47 225/115/47 +f 225/115/47 240/114/47 226/116/47 +f 240/114/47 239/117/47 226/116/47 +f 226/116/47 239/117/47 227/118/47 +f 239/117/188 238/101/188 227/118/188 +f 227/118/188 238/101/188 228/105/188 +f 228/105/47 238/101/47 231/103/47 +f 238/101/47 234/97/47 231/103/47 +f 234/97/47 233/119/47 232/120/47 +f 231/103/47 234/97/47 232/120/47 +f 230/88/189 180/85/189 229/87/189 +f 216/86/85 248/87/85 217/88/85 +f 192/86/86 236/87/86 237/88/86 +f 225/88/190 170/85/190 224/87/190 +f 206/86/191 243/87/191 244/88/191 +f 232/88/192 184/85/192 231/87/192 +f 220/88/193 160/85/193 219/87/193 +f 196/86/194 238/87/194 239/88/194 +f 227/88/195 174/85/195 226/87/195 +f 210/86/93 245/87/93 246/88/93 +f 186/86/196 233/87/196 234/88/196 +f 222/88/197 164/85/197 221/87/197 +f 200/86/198 240/87/198 241/88/198 +f 229/88/199 178/85/199 228/87/199 +f 214/86/98 247/87/98 248/88/98 +f 190/86/99 235/87/99 236/88/99 +f 224/88/200 168/85/200 223/87/200 +f 204/86/201 242/87/201 243/88/201 +f 231/88/102 182/85/102 230/87/102 +f 219/88/103 158/85/103 218/87/103 +f 194/86/202 237/87/202 238/88/202 +f 226/88/203 172/85/203 225/87/203 +f 208/86/204 244/87/204 245/88/204 +f 233/88/107 186/85/107 232/87/107 +f 221/88/108 162/85/108 220/87/108 +f 198/86/109 239/87/109 240/88/109 +f 218/88/205 156/85/205 217/87/205 +f 228/88/206 176/85/206 227/87/206 +f 212/86/207 246/87/207 247/88/207 +f 188/86/208 234/87/208 235/88/208 +f 223/88/209 166/85/209 222/87/209 +f 250/93/49 251/90/49 280/94/49 +f 251/90/49 252/91/49 280/94/49 +f 252/91/49 253/89/49 280/94/49 +f 253/89/49 254/95/49 280/94/49 +f 254/95/49 255/96/49 280/94/49 +f 255/96/49 256/109/49 280/94/49 +f 256/109/49 257/112/49 280/94/49 +f 257/112/49 258/113/49 280/94/49 +f 258/113/49 259/114/49 280/94/49 +f 259/114/49 260/117/49 280/94/49 +f 260/117/49 261/101/49 280/94/49 +f 261/101/49 262/100/49 280/94/49 +f 262/100/49 263/98/49 280/94/49 +f 263/98/49 264/99/49 280/94/49 +f 264/99/49 265/97/49 280/94/49 +f 265/97/49 266/119/49 280/94/49 +f 266/119/49 267/120/49 280/94/49 +f 267/120/49 268/103/49 280/94/49 +f 268/103/49 269/104/49 280/94/49 +f 269/104/49 270/102/49 280/94/49 +f 270/102/49 271/105/49 280/94/49 +f 271/105/49 272/118/49 280/94/49 +f 272/118/49 273/116/49 280/94/49 +f 273/116/49 274/115/49 280/94/49 +f 274/115/49 275/111/49 280/94/49 +f 275/111/49 276/110/49 280/94/49 +f 276/110/49 277/108/49 280/94/49 +f 277/108/49 278/106/49 279/107/49 +f 280/94/49 277/108/49 279/107/49 +f 257/88/115 169/85/115 258/87/115 +f 209/86/210 277/87/210 276/88/210 +f 264/88/117 183/85/117 265/87/117 +f 252/88/118 159/85/118 253/87/118 +f 199/86/211 272/87/211 271/88/211 +f 249/88/212 153/85/212 250/87/212 +f 259/88/213 173/85/213 260/87/213 +f 213/86/122 279/87/122 278/88/122 +f 189/86/123 267/87/123 266/88/123 +f 254/88/214 163/85/214 255/87/214 +f 203/86/215 274/87/215 273/88/215 +f 261/88/126 177/85/126 262/87/126 +f 153/86/127 249/87/127 280/88/127 +f 193/86/128 269/87/128 268/88/128 +f 256/88/216 167/85/216 257/87/216 +f 207/86/130 276/87/130 275/88/130 +f 263/88/217 181/85/217 264/87/217 +f 251/88/218 157/85/218 252/87/218 +f 197/86/219 271/87/219 270/88/219 +f 258/88/220 171/85/220 259/87/220 +f 211/86/221 278/87/221 277/88/221 +f 187/86/136 266/87/136 265/88/136 +f 253/88/222 161/85/222 254/87/222 +f 201/86/223 273/87/223 272/88/223 +f 260/88/139 175/85/139 261/87/139 +f 215/86/224 280/87/224 279/88/224 +f 191/86/141 268/87/141 267/88/141 +f 255/88/142 165/85/142 256/87/142 +f 205/86/225 275/87/225 274/88/225 +f 262/88/144 179/85/144 263/87/144 +f 250/88/226 155/85/226 251/87/226 +f 283/88/227 281/85/227 284/87/227 +f 285/88/228 283/85/228 286/87/228 +f 287/88/148 285/85/148 288/87/148 +f 289/88/229 287/85/229 290/87/229 +f 291/88/150 289/85/150 292/87/150 +f 293/88/151 291/85/151 294/87/151 +f 295/88/152 293/85/152 296/87/152 +f 297/88/153 295/85/153 298/87/153 +f 299/88/230 297/85/230 300/87/230 +f 301/88/231 299/85/231 302/87/231 +f 303/88/156 301/85/156 304/87/156 +f 305/88/157 303/85/157 306/87/157 +f 307/88/232 305/85/232 308/87/232 +f 309/88/233 307/85/233 310/87/233 +f 311/88/234 309/85/234 312/87/234 +f 313/88/235 311/85/235 314/87/235 +f 314/86/236 316/87/236 315/88/236 +f 316/86/237 318/87/237 317/88/237 +f 318/86/238 320/87/238 319/88/238 +f 320/86/239 322/87/239 321/88/239 +f 322/86/166 324/87/166 323/88/166 +f 324/86/167 326/87/167 325/88/167 +f 326/86/168 328/87/168 327/88/168 +f 328/86/169 330/87/169 329/88/169 +f 330/86/240 332/87/240 331/88/240 +f 332/86/171 334/87/171 333/88/171 +f 334/86/172 336/87/172 335/88/172 +f 336/86/241 338/87/241 337/88/241 +f 338/86/242 340/87/242 339/88/242 +f 340/86/175 342/87/175 341/88/175 +f 282/93/39 344/90/39 286/94/39 +f 344/90/39 342/91/39 286/94/39 +f 342/91/39 340/89/39 286/94/39 +f 340/89/39 338/95/39 286/94/39 +f 338/95/39 336/96/39 286/94/39 +f 336/96/39 334/109/39 286/94/39 +f 334/109/39 332/112/39 286/94/39 +f 332/112/39 330/113/39 286/94/39 +f 330/113/39 328/114/39 286/94/39 +f 328/114/39 326/117/39 286/94/39 +f 326/117/39 324/101/39 286/94/39 +f 324/101/39 322/100/39 286/94/39 +f 322/100/39 320/98/39 286/94/39 +f 320/98/39 318/99/39 286/94/39 +f 318/99/39 316/97/39 286/94/39 +f 316/97/39 314/119/39 286/94/39 +f 314/119/39 312/120/39 286/94/39 +f 312/120/39 310/103/39 286/94/39 +f 310/103/39 308/104/39 286/94/39 +f 308/104/39 306/102/39 286/94/39 +f 306/102/39 304/105/39 286/94/39 +f 304/105/39 302/118/39 286/94/39 +f 302/118/39 300/116/39 286/94/39 +f 300/116/39 298/115/39 286/94/39 +f 298/115/39 296/111/39 286/94/39 +f 296/111/39 294/110/39 286/94/39 +f 294/110/39 292/108/39 286/94/39 +f 292/108/39 290/106/39 286/94/39 +f 290/106/39 288/107/39 286/94/39 +f 344/86/243 282/87/243 281/88/243 +f 343/88/244 341/85/244 344/87/244 +f 283/93/245 285/90/245 343/94/245 +f 343/94/245 285/90/245 341/107/245 +f 285/90/42 287/91/42 341/107/42 +f 287/91/42 289/89/42 341/107/42 +f 289/89/42 291/95/42 341/107/42 +f 291/95/246 293/96/246 341/107/246 +f 293/96/247 295/109/247 341/107/247 +f 335/110/248 331/115/248 333/111/248 +f 295/109/247 297/112/247 341/107/247 +f 297/112/247 299/113/247 341/107/247 +f 335/110/249 329/116/249 331/115/249 +f 299/113/247 301/114/247 341/107/247 +f 335/110/250 327/118/250 329/116/250 +f 301/114/247 303/117/247 341/107/247 +f 335/110/251 325/105/251 327/118/251 +f 303/117/42 305/101/42 341/107/42 +f 335/110/252 323/102/252 325/105/252 +f 305/101/42 307/100/42 341/107/42 +f 335/110/253 321/104/253 323/102/253 +f 307/100/42 309/98/42 341/107/42 +f 335/110/253 319/103/253 321/104/253 +f 309/98/254 311/99/254 341/107/254 +f 335/110/254 317/120/254 319/103/254 +f 311/99/254 313/97/254 341/107/254 +f 315/119/254 317/120/254 335/110/254 +f 313/97/254 315/119/254 341/107/254 +f 315/119/254 335/110/254 341/107/254 +f 335/110/42 337/108/42 339/106/42 +f 341/107/42 335/110/42 339/106/42 +v 1.365790 0.015116 4.001092 +v 1.365790 0.015113 0.001092 +v 1.560881 0.034331 4.001092 +v 1.560881 0.034328 0.001092 +v 1.748474 0.091236 4.001092 +v 1.748474 0.091234 0.001092 +v 1.921361 0.183646 4.001092 +v 1.921361 0.183644 0.001092 +v 2.072897 0.308009 4.001092 +v 2.072897 0.308007 0.001092 +v 2.197260 0.459546 4.001092 +v 2.197260 0.459543 0.001092 +v 2.289670 0.632432 4.001092 +v 2.289670 0.632430 0.001092 +v 2.346576 0.820025 4.001092 +v 2.346576 0.820023 0.001092 +v 2.365790 1.015116 4.001092 +v 2.365790 1.015113 0.001092 +v 2.346576 1.210206 4.001091 +v 2.346576 1.210203 0.001091 +v 2.289670 1.397799 4.001091 +v 2.289670 1.397797 0.001091 +v 2.197260 1.570686 4.001091 +v 2.197260 1.570683 0.001091 +v 2.072897 1.722223 4.001091 +v 2.072897 1.722220 0.001091 +v 1.921361 1.846586 4.001091 +v 1.921361 1.846583 0.001091 +v 1.748474 1.938995 4.001091 +v 1.748474 1.938993 0.001091 +v 1.560881 1.995901 4.001091 +v 1.560881 1.995899 0.001091 +v 1.365790 2.015116 4.001091 +v 1.365790 2.015113 0.001091 +v 1.170700 1.995901 4.001091 +v 1.170700 1.995898 0.001091 +v 0.983107 1.938995 4.001091 +v 0.983107 1.938993 0.001091 +v 0.810220 1.846585 4.001091 +v 0.810220 1.846583 0.001091 +v 0.658683 1.722222 4.001091 +v 0.658683 1.722220 0.001091 +v 0.534320 1.570685 4.001091 +v 0.534320 1.570683 0.001091 +v 0.441911 1.397799 4.001091 +v 0.441911 1.397796 0.001091 +v 0.385005 1.210205 4.001091 +v 0.385005 1.210203 0.001091 +v 0.365790 1.015115 4.001092 +v 0.365790 1.015112 0.001092 +v 0.385005 0.820024 4.001092 +v 0.385005 0.820022 0.001092 +v 0.441911 0.632431 4.001092 +v 0.441911 0.632429 0.001092 +v 0.534322 0.459545 4.001092 +v 0.534322 0.459542 0.001092 +v 0.658685 0.308008 4.001092 +v 0.658685 0.308006 0.001092 +v 0.810221 0.183645 4.001092 +v 0.810221 0.183643 0.001092 +v 0.983108 0.091236 4.001092 +v 0.983108 0.091233 0.001092 +v 1.170702 0.034330 4.001092 +v 1.170702 0.034328 0.001092 +v 1.365791 0.649119 -0.167860 +v 1.437192 0.656151 -0.167860 +v 1.505850 0.676978 -0.167860 +v 1.569126 0.710800 -0.167860 +v 1.624587 0.756316 -0.167860 +v 1.670104 0.811778 -0.167860 +v 1.703925 0.875053 -0.167860 +v 1.724752 0.943711 -0.167860 +v 1.731785 1.015113 -0.167860 +v 1.724752 1.086515 -0.167860 +v 1.703925 1.155173 -0.167860 +v 1.670104 1.218448 -0.167861 +v 1.624587 1.273910 -0.167861 +v 1.569126 1.319426 -0.167861 +v 1.505850 1.353247 -0.167861 +v 1.437192 1.374075 -0.167861 +v 1.365790 1.381107 -0.167861 +v 1.294389 1.374075 -0.167861 +v 1.225731 1.353247 -0.167861 +v 1.162455 1.319426 -0.167861 +v 1.106993 1.273910 -0.167861 +v 1.061477 1.218448 -0.167861 +v 1.027656 1.155173 -0.167860 +v 1.006829 1.086515 -0.167860 +v 0.999797 1.015113 -0.167860 +v 1.006829 0.943711 -0.167860 +v 1.027656 0.875053 -0.167860 +v 1.061478 0.811777 -0.167860 +v 1.106994 0.756316 -0.167860 +v 1.162456 0.710800 -0.167860 +v 1.225731 0.676978 -0.167860 +v 1.294389 0.656151 -0.167860 +v 1.365791 0.605024 4.157856 +v 1.445795 0.612904 4.157856 +v 1.522726 0.636241 4.157856 +v 1.593625 0.674137 4.157856 +v 1.655769 0.725137 4.157856 +v 1.706769 0.787281 4.157856 +v 1.744666 0.858181 4.157855 +v 1.768002 0.935111 4.157855 +v 1.775882 1.015116 4.157855 +v 1.768002 1.095121 4.157855 +v 1.744666 1.172051 4.157855 +v 1.706769 1.242950 4.157855 +v 1.655769 1.305094 4.157855 +v 1.593625 1.356094 4.157855 +v 1.522726 1.393991 4.157855 +v 1.445795 1.417328 4.157855 +v 1.365790 1.425207 4.157855 +v 1.285786 1.417327 4.157855 +v 1.208855 1.393991 4.157855 +v 1.137956 1.356094 4.157855 +v 1.075812 1.305094 4.157855 +v 1.024812 1.242950 4.157855 +v 0.986915 1.172051 4.157855 +v 0.963579 1.095120 4.157855 +v 0.955699 1.015115 4.157855 +v 0.963579 0.935110 4.157855 +v 0.986916 0.858180 4.157855 +v 1.024812 0.787281 4.157856 +v 1.075812 0.725137 4.157856 +v 1.137956 0.674137 4.157856 +v 1.208856 0.636240 4.157856 +v 1.285786 0.612904 4.157856 +vt 0.000000 0.000000 +vt 0.450363 0.000088 +vt 0.519265 0.025135 +vt 0.412277 0.001462 +vt 0.592585 0.072108 +vt 0.660664 0.134431 +vt 0.714751 0.214466 +vt 0.755741 0.304410 +vt 0.778531 0.401983 +vt 0.781786 0.502742 +vt 0.147188 0.142771 +vt 0.050179 0.306373 +vt 0.093406 0.218681 +vt 0.765337 0.602148 +vt 0.019781 0.402090 +vt 0.729413 0.696327 +vt 0.000089 0.500905 +vt 0.680707 0.782826 +vt 0.000088 0.601389 +vt 0.627360 0.858268 +vt 0.022810 0.698277 +vt 0.566443 0.918999 +vt 0.063602 0.787572 +vt 0.501474 0.962949 +vt 0.119962 0.865210 +vt 0.436606 0.989270 +vt 0.188187 0.927714 +vt 0.377255 0.998855 +vt 0.263175 0.972402 +vt 0.336538 0.999912 +vt 0.208578 0.081678 +vt 0.280064 0.037078 +vt 0.344761 0.010798 +vn 0.634394 -0.773010 0.000000 +vn 0.773010 -0.634394 0.000000 +vn 0.773010 0.634394 -0.000000 +vn 0.634394 0.773010 -0.000000 +vn -0.956941 0.290284 -0.000000 +vn -0.995185 0.098016 -0.000000 +vn -0.881921 -0.471398 0.000000 +vn -0.634392 -0.773011 0.000000 +vn -0.075085 -0.247523 -0.965968 +vn -0.199427 0.163668 0.966148 +vn -0.121931 0.228117 -0.965969 +vn 0.247522 -0.075086 -0.965968 +vn -0.247522 -0.075086 -0.965968 +vn 0.121931 0.228117 -0.965969 +vn 0.075085 -0.247523 -0.965968 +vn -0.199948 0.164091 -0.965968 +vn 0.257414 0.025352 -0.965968 +vn -0.199947 -0.164093 -0.965968 +vn 0.025353 0.257414 -0.965969 +vn 0.164092 -0.199947 -0.965968 +vn -0.247522 0.075085 -0.965969 +vn 0.025353 -0.257415 -0.965968 +vn 0.228118 0.121931 -0.965969 +vn -0.121931 -0.228119 -0.965968 +vn -0.075086 0.247521 -0.965969 +vn 0.228118 -0.121932 -0.965968 +vn -0.257414 -0.025354 -0.965968 +vn 0.164092 0.199946 -0.965969 +vn -0.025353 -0.257415 -0.965968 +vn -0.164092 0.199946 -0.965969 +vn 0.257415 -0.025354 -0.965968 +vn -0.228117 -0.121932 -0.965968 +vn 0.075086 0.247521 -0.965969 +vn 0.121931 -0.228119 -0.965968 +vn -0.228117 0.121931 -0.965968 +vn 0.247522 0.075085 -0.965969 +vn -0.164092 -0.199947 -0.965968 +vn -0.025353 0.257414 -0.965969 +vn 0.199947 -0.164093 -0.965968 +vn -0.257414 0.025352 -0.965968 +vn 0.199947 0.164091 -0.965968 +vn 0.256746 0.025290 0.966148 +vn -0.199428 -0.163666 0.966148 +vn 0.025287 0.256747 0.966148 +vn 0.163666 -0.199428 0.966148 +vn -0.246880 0.074890 0.966148 +vn 0.025287 -0.256746 0.966148 +vn 0.227526 0.121615 0.966148 +vn -0.121615 -0.227526 0.966148 +vn -0.074890 0.246880 0.966148 +vn 0.227525 -0.121615 0.966148 +vn -0.256745 -0.025287 0.966148 +vn 0.163667 0.199428 0.966148 +vn -0.025287 -0.256746 0.966148 +vn -0.163667 0.199428 0.966148 +vn 0.256746 -0.025287 0.966148 +vn -0.227525 -0.121615 0.966148 +vn 0.074890 0.246880 0.966148 +vn 0.121615 -0.227526 0.966148 +vn -0.227526 0.121615 0.966148 +vn 0.246880 0.074890 0.966148 +vn -0.163666 -0.199428 0.966148 +vn -0.025287 0.256747 0.966148 +vn 0.199428 -0.163666 0.966148 +vn -0.256746 0.025289 0.966148 +vn 0.199427 0.163668 0.966148 +vn -0.074892 -0.246879 0.966148 +vn -0.121615 0.227526 0.966148 +vn 0.246879 -0.074890 0.966148 +vn -0.246879 -0.074890 0.966148 +vn 0.121615 0.227526 0.966148 +vn 0.074892 -0.246879 0.966148 +vn -0.199428 0.163666 0.966148 +vn 0.000000 -0.000004 -1.000000 +vn 0.247522 -0.075085 -0.965968 +vn -0.199947 0.164092 -0.965969 +vn 0.257414 0.025353 -0.965969 +vn -0.199947 -0.164092 -0.965968 +vn 0.228120 0.121929 -0.965968 +vn -0.121931 -0.228118 -0.965968 +vn -0.075085 0.247521 -0.965969 +vn 0.228117 -0.121935 -0.965968 +vn -0.257414 -0.025353 -0.965968 +vn 0.164092 0.199947 -0.965969 +vn 0.257415 -0.025353 -0.965968 +vn -0.228117 -0.121935 -0.965968 +vn 0.075085 0.247521 -0.965969 +vn 0.121932 -0.228118 -0.965968 +vn -0.228120 0.121928 -0.965968 +vn 0.199947 -0.164092 -0.965968 +vn -0.257414 0.025353 -0.965969 +vn 0.199946 0.164092 -0.965969 +vn -0.000004 0.000009 1.000000 +vn 0.000001 -0.000000 1.000000 +vn -0.000029 -0.000009 1.000000 +vn -0.000010 -0.000001 1.000000 +vn -0.000005 0.000001 1.000000 +vn -0.000003 0.000001 1.000000 +vn -0.000002 0.000001 1.000000 +vn -0.000001 0.000001 1.000000 +vn 0.000000 0.000001 1.000000 +vn 0.256746 0.025287 0.966148 +vn -0.074891 0.246880 0.966148 +vn 0.227529 -0.121610 0.966148 +vn -0.256746 -0.025287 0.966148 +vn -0.227529 -0.121610 0.966148 +vn -0.256746 0.025287 0.966148 +vn 0.199428 0.163666 0.966148 +vn -0.074889 -0.246879 0.966148 +vn 0.074890 -0.246879 0.966148 +s off +f 345/121/50 346/121/50 348/121/50 +f 347/121/51 348/121/51 350/121/51 +f 349/121/52 350/121/52 352/121/52 +f 351/121/255 352/121/255 354/121/255 +f 353/121/256 354/121/256 356/121/256 +f 355/121/55 356/121/55 358/121/55 +f 357/121/56 358/121/56 360/121/56 +f 359/121/57 360/121/57 362/121/57 +f 361/121/58 362/121/58 364/121/58 +f 363/121/59 364/121/59 366/121/59 +f 365/121/60 366/121/60 368/121/60 +f 367/121/257 368/121/257 370/121/257 +f 369/121/258 370/121/258 372/121/258 +f 371/121/63 372/121/63 374/121/63 +f 373/121/64 374/121/64 376/121/64 +f 375/121/65 376/121/65 378/121/65 +f 377/121/66 378/121/66 379/121/66 +f 379/121/67 380/121/67 381/121/67 +f 381/121/68 382/121/68 383/121/68 +f 383/121/69 384/121/69 385/121/69 +f 385/121/70 386/121/70 387/121/70 +f 387/121/71 388/121/71 389/121/71 +f 389/121/259 390/121/259 391/121/259 +f 391/121/260 392/121/260 393/121/260 +f 393/121/74 394/121/74 395/121/74 +f 395/121/75 396/121/75 397/121/75 +f 397/121/261 398/121/261 399/121/261 +f 399/121/77 400/121/77 401/121/77 +f 401/121/262 402/121/262 403/121/262 +f 403/121/79 404/121/79 405/121/79 +f 408/121/263 406/121/263 440/121/263 +f 407/121/81 408/121/81 345/121/81 +f 405/121/82 406/121/82 407/121/82 +f 385/121/264 387/121/264 461/121/264 +f 437/121/47 439/121/47 438/121/47 +f 384/121/265 382/121/265 428/121/265 +f 360/121/266 358/121/266 415/121/266 +f 398/121/267 396/121/267 435/121/267 +f 374/121/268 372/121/268 422/121/268 +f 350/121/269 348/121/269 410/121/269 +f 388/121/270 386/121/270 430/121/270 +f 364/121/271 362/121/271 417/121/271 +f 402/121/272 400/121/272 437/121/272 +f 378/121/273 376/121/273 424/121/273 +f 354/121/274 352/121/274 412/121/274 +f 392/121/275 390/121/275 432/121/275 +f 348/121/276 346/121/276 409/121/276 +f 368/121/277 366/121/277 419/121/277 +f 406/121/278 404/121/278 439/121/278 +f 382/121/279 380/121/279 427/121/279 +f 358/121/280 356/121/280 414/121/280 +f 396/121/281 394/121/281 434/121/281 +f 372/121/282 370/121/282 421/121/282 +f 346/121/283 408/121/283 409/121/283 +f 386/121/284 384/121/284 429/121/284 +f 362/121/285 360/121/285 416/121/285 +f 400/121/286 398/121/286 436/121/286 +f 376/121/287 374/121/287 423/121/287 +f 352/121/288 350/121/288 411/121/288 +f 390/121/289 388/121/289 431/121/289 +f 366/121/290 364/121/290 418/121/290 +f 404/121/291 402/121/291 438/121/291 +f 380/121/292 378/121/292 426/121/292 +f 356/121/293 354/121/293 413/121/293 +f 394/121/294 392/121/294 433/121/294 +f 370/121/295 368/121/295 420/121/295 +f 441/122/49 442/123/49 472/124/49 +f 361/121/296 363/121/296 450/121/296 +f 399/121/297 401/121/297 468/121/297 +f 375/121/298 377/121/298 457/121/298 +f 351/121/299 353/121/299 445/121/299 +f 389/121/300 391/121/300 463/121/300 +f 345/121/301 347/121/301 442/121/301 +f 365/121/302 367/121/302 452/121/302 +f 403/121/303 405/121/303 470/121/303 +f 379/121/304 381/121/304 458/121/304 +f 355/121/305 357/121/305 447/121/305 +f 393/121/306 395/121/306 465/121/306 +f 369/121/307 371/121/307 454/121/307 +f 407/121/308 345/121/308 472/121/308 +f 383/121/309 385/121/309 460/121/309 +f 359/121/310 361/121/310 449/121/310 +f 397/121/311 399/121/311 467/121/311 +f 373/121/312 375/121/312 456/121/312 +f 349/121/313 351/121/313 444/121/313 +f 387/121/314 389/121/314 462/121/314 +f 363/121/315 365/121/315 451/121/315 +f 401/121/316 403/121/316 469/121/316 +f 377/121/317 379/121/317 457/121/317 +f 353/121/318 355/121/318 446/121/318 +f 391/121/319 393/121/319 464/121/319 +f 367/121/320 369/121/320 453/121/320 +f 405/121/321 407/121/321 471/121/321 +f 381/121/322 383/121/322 459/121/322 +f 357/121/323 359/121/323 448/121/323 +f 395/121/324 397/121/324 466/121/324 +f 371/121/325 373/121/325 455/121/325 +f 347/121/326 349/121/326 443/121/326 +f 347/121/178 345/121/178 348/121/178 +f 349/121/51 347/121/51 350/121/51 +f 351/121/52 349/121/52 352/121/52 +f 353/121/255 351/121/255 354/121/255 +f 355/121/256 353/121/256 356/121/256 +f 357/121/179 355/121/179 358/121/179 +f 359/121/56 357/121/56 360/121/56 +f 361/121/57 359/121/57 362/121/57 +f 363/121/58 361/121/58 364/121/58 +f 365/121/59 363/121/59 366/121/59 +f 367/121/60 365/121/60 368/121/60 +f 369/121/257 367/121/257 370/121/257 +f 371/121/181 369/121/181 372/121/181 +f 373/121/182 371/121/182 374/121/182 +f 375/121/183 373/121/183 376/121/183 +f 377/121/65 375/121/65 378/121/65 +f 378/121/184 380/121/184 379/121/184 +f 380/121/67 382/121/67 381/121/67 +f 382/121/68 384/121/68 383/121/68 +f 384/121/69 386/121/69 385/121/69 +f 386/121/70 388/121/70 387/121/70 +f 388/121/71 390/121/71 389/121/71 +f 390/121/259 392/121/259 391/121/259 +f 392/121/260 394/121/260 393/121/260 +f 394/121/74 396/121/74 395/121/74 +f 396/121/75 398/121/75 397/121/75 +f 398/121/261 400/121/261 399/121/261 +f 400/121/77 402/121/77 401/121/77 +f 402/121/262 404/121/262 403/121/262 +f 404/121/79 406/121/79 405/121/79 +f 406/121/263 439/121/263 440/121/263 +f 408/121/81 346/121/81 345/121/81 +f 406/121/82 408/121/82 407/121/82 +f 387/121/327 462/121/327 461/121/327 +f 436/121/47 439/121/47 437/121/47 +f 427/121/47 429/121/47 428/121/47 +f 427/121/47 430/121/47 429/121/47 +f 421/121/47 423/121/47 422/121/47 +f 420/121/47 423/121/47 421/121/47 +f 411/121/47 413/121/47 412/121/47 +f 411/121/47 414/121/47 413/121/47 +f 410/121/47 409/121/47 411/121/47 +f 409/121/47 440/121/47 411/121/47 +f 440/121/47 439/121/47 411/121/47 +f 439/121/47 436/121/47 411/121/47 +f 411/121/47 436/121/47 414/121/47 +f 436/121/328 435/121/328 414/121/328 +f 414/121/328 435/121/328 415/121/328 +f 435/121/47 434/121/47 415/121/47 +f 434/121/47 433/121/47 415/121/47 +f 433/121/47 432/121/47 415/121/47 +f 432/121/47 431/121/47 415/121/47 +f 415/121/47 431/121/47 416/121/47 +f 416/121/47 431/121/47 417/121/47 +f 417/121/47 431/121/47 418/121/47 +f 418/121/47 431/121/47 419/121/47 +f 431/121/328 430/121/328 419/121/328 +f 419/121/328 430/121/328 420/121/328 +f 430/121/47 427/121/47 420/121/47 +f 420/121/47 427/121/47 423/121/47 +f 427/121/47 426/121/47 423/121/47 +f 426/121/47 425/121/47 424/121/47 +f 423/121/47 426/121/47 424/121/47 +f 382/121/265 427/121/265 428/121/265 +f 416/121/329 360/121/329 415/121/329 +f 396/121/267 434/121/267 435/121/267 +f 423/121/268 374/121/268 422/121/268 +f 411/121/269 350/121/269 410/121/269 +f 386/121/330 429/121/330 430/121/330 +f 418/121/331 364/121/331 417/121/331 +f 400/121/332 436/121/332 437/121/332 +f 425/121/273 378/121/273 424/121/273 +f 413/121/274 354/121/274 412/121/274 +f 390/121/275 431/121/275 432/121/275 +f 410/121/276 348/121/276 409/121/276 +f 420/121/333 368/121/333 419/121/333 +f 404/121/334 438/121/334 439/121/334 +f 380/121/335 426/121/335 427/121/335 +f 415/121/336 358/121/336 414/121/336 +f 394/121/337 433/121/337 434/121/337 +f 422/121/338 372/121/338 421/121/338 +f 408/121/283 440/121/283 409/121/283 +f 384/121/284 428/121/284 429/121/284 +f 417/121/339 362/121/339 416/121/339 +f 398/121/340 435/121/340 436/121/340 +f 424/121/341 376/121/341 423/121/341 +f 412/121/342 352/121/342 411/121/342 +f 388/121/343 430/121/343 431/121/343 +f 419/121/290 366/121/290 418/121/290 +f 402/121/291 437/121/291 438/121/291 +f 378/121/292 425/121/292 426/121/292 +f 414/121/344 356/121/344 413/121/344 +f 392/121/345 432/121/345 433/121/345 +f 421/121/346 370/121/346 420/121/346 +f 442/123/49 443/125/49 472/124/49 +f 443/125/49 444/126/49 472/124/49 +f 444/126/49 445/127/49 472/124/49 +f 445/127/49 446/128/49 472/124/49 +f 446/128/347 447/129/347 472/124/347 +f 447/129/348 448/130/348 472/124/348 +f 468/131/349 466/132/349 467/133/349 +f 448/130/348 449/134/348 472/124/348 +f 468/131/350 465/135/350 466/132/350 +f 449/134/348 450/136/348 472/124/348 +f 468/131/351 464/137/351 465/135/351 +f 450/136/348 451/138/348 472/124/348 +f 468/131/352 463/139/352 464/137/352 +f 451/138/348 452/140/348 472/124/348 +f 468/131/353 462/141/353 463/139/353 +f 452/140/348 453/142/348 472/124/348 +f 468/131/354 461/143/354 462/141/354 +f 453/142/49 454/144/49 472/124/49 +f 468/131/354 460/145/354 461/143/354 +f 454/144/355 455/146/355 472/124/355 +f 468/131/354 459/147/354 460/145/354 +f 455/146/355 456/148/355 472/124/355 +f 468/131/355 458/149/355 459/147/355 +f 456/148/355 457/150/355 472/124/355 +f 457/150/355 458/149/355 472/124/355 +f 458/149/355 468/131/355 472/124/355 +f 468/131/49 469/151/49 472/124/49 +f 469/151/49 470/152/49 471/153/49 +f 472/124/49 469/151/49 471/153/49 +f 449/121/356 361/121/356 450/121/356 +f 401/121/297 469/121/297 468/121/297 +f 456/121/298 375/121/298 457/121/298 +f 444/121/299 351/121/299 445/121/299 +f 391/121/300 464/121/300 463/121/300 +f 441/121/301 345/121/301 442/121/301 +f 451/121/302 365/121/302 452/121/302 +f 405/121/303 471/121/303 470/121/303 +f 381/121/357 459/121/357 458/121/357 +f 446/121/358 355/121/358 447/121/358 +f 395/121/359 466/121/359 465/121/359 +f 453/121/307 369/121/307 454/121/307 +f 345/121/308 441/121/308 472/121/308 +f 385/121/309 461/121/309 460/121/309 +f 448/121/310 359/121/310 449/121/310 +f 399/121/360 468/121/360 467/121/360 +f 455/121/312 373/121/312 456/121/312 +f 443/121/313 349/121/313 444/121/313 +f 389/121/314 463/121/314 462/121/314 +f 450/121/315 363/121/315 451/121/315 +f 403/121/316 470/121/316 469/121/316 +f 379/121/317 458/121/317 457/121/317 +f 445/121/318 353/121/318 446/121/318 +f 393/121/361 465/121/361 464/121/361 +f 452/121/362 367/121/362 453/121/362 +f 407/121/363 472/121/363 471/121/363 +f 383/121/322 460/121/322 459/121/322 +f 447/121/323 357/121/323 448/121/323 +f 397/121/324 467/121/324 466/121/324 +f 454/121/325 371/121/325 455/121/325 +f 442/121/364 347/121/364 443/121/364 +v 0.000000 0.010026 -4.301087 +v 0.000000 4.010026 -4.301087 +v 0.195090 0.010026 -4.281873 +v 0.195090 4.010026 -4.281873 +v 0.382683 0.010026 -4.224967 +v 0.382683 4.010026 -4.224967 +v 0.555570 0.010026 -4.132557 +v 0.555570 4.010026 -4.132557 +v 0.707107 0.010026 -4.008194 +v 0.707107 4.010026 -4.008194 +v 0.831470 0.010026 -3.856658 +v 0.831470 4.010026 -3.856658 +v 0.923880 0.010026 -3.683771 +v 0.923880 4.010026 -3.683771 +v 0.980785 0.010026 -3.496178 +v 0.980785 4.010026 -3.496178 +v 1.000000 0.010026 -3.301088 +v 1.000000 4.010026 -3.301088 +v 0.980785 0.010026 -3.105997 +v 0.980785 4.010026 -3.105997 +v 0.923880 0.010026 -2.918404 +v 0.923880 4.010026 -2.918404 +v 0.831470 0.010026 -2.745517 +v 0.831470 4.010026 -2.745517 +v 0.707107 0.010026 -2.593981 +v 0.707107 4.010026 -2.593981 +v 0.555570 0.010026 -2.469618 +v 0.555570 4.010026 -2.469618 +v 0.382683 0.010026 -2.377208 +v 0.382683 4.010026 -2.377208 +v 0.195090 0.010026 -2.320302 +v 0.195090 4.010026 -2.320302 +v -0.000000 0.010026 -2.301088 +v -0.000000 4.010026 -2.301088 +v -0.195091 0.010026 -2.320302 +v -0.195091 4.010026 -2.320302 +v -0.382684 0.010026 -2.377208 +v -0.382684 4.010026 -2.377208 +v -0.555571 0.010026 -2.469618 +v -0.555571 4.010026 -2.469618 +v -0.707107 0.010026 -2.593981 +v -0.707107 4.010026 -2.593981 +v -0.831470 0.010026 -2.745518 +v -0.831470 4.010026 -2.745518 +v -0.923880 0.010026 -2.918405 +v -0.923880 4.010026 -2.918405 +v -0.980785 0.010026 -3.105998 +v -0.980785 4.010026 -3.105998 +v -1.000000 0.010026 -3.301089 +v -1.000000 4.010026 -3.301089 +v -0.980785 0.010026 -3.496179 +v -0.980785 4.010026 -3.496179 +v -0.923879 0.010026 -3.683772 +v -0.923879 4.010026 -3.683772 +v -0.831469 0.010026 -3.856659 +v -0.831469 4.010026 -3.856659 +v -0.707106 0.010026 -4.008195 +v -0.707106 4.010026 -4.008195 +v -0.555569 0.010026 -4.132558 +v -0.555569 4.010026 -4.132558 +v -0.382682 0.010026 -4.224968 +v -0.382682 4.010026 -4.224968 +v -0.195089 0.010026 -4.281873 +v -0.195089 4.010026 -4.281873 +v 0.000000 4.270857 -3.619825 +v 0.062183 4.270857 -3.613700 +v 0.121975 4.270857 -3.595562 +v 0.177081 4.270857 -3.566108 +v 0.225381 4.270857 -3.526469 +v 0.265020 4.270857 -3.478168 +v 0.294475 4.270857 -3.423063 +v 0.312612 4.270857 -3.363270 +v 0.318737 4.270857 -3.301088 +v 0.312613 4.270857 -3.238905 +v 0.294475 4.270857 -3.179112 +v 0.265020 4.270857 -3.124007 +v 0.225381 4.270857 -3.075707 +v 0.177081 4.270857 -3.036068 +v 0.121975 4.270857 -3.006613 +v 0.062182 4.270857 -2.988475 +v -0.000000 4.270857 -2.982351 +v -0.062183 4.270857 -2.988475 +v -0.121975 4.270857 -3.006613 +v -0.177081 4.270857 -3.036068 +v -0.225381 4.270857 -3.075707 +v -0.265020 4.270857 -3.124007 +v -0.294475 4.270857 -3.179113 +v -0.312613 4.270857 -3.238906 +v -0.318737 4.270857 -3.301088 +v -0.312612 4.270857 -3.363271 +v -0.294474 4.270857 -3.423064 +v -0.265020 4.270857 -3.478169 +v -0.225381 4.270857 -3.526469 +v -0.177080 4.270857 -3.566108 +v -0.121975 4.270857 -3.595562 +v -0.062182 4.270857 -3.613700 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.500000 1.000000 +vt 0.597545 0.990393 +vt 0.402456 0.990393 +vt 0.691342 0.961940 +vt 0.777785 0.915735 +vt 0.853553 0.853553 +vt 0.915735 0.777785 +vt 0.961940 0.691342 +vt 0.990393 0.597545 +vt 1.000000 0.500000 +vt 0.990393 0.402455 +vt 0.961940 0.308658 +vt 0.915735 0.222215 +vt 0.853553 0.146447 +vt 0.777785 0.084265 +vt 0.691342 0.038060 +vt 0.597545 0.009607 +vt 0.500000 0.000000 +vt 0.402455 0.009607 +vt 0.308658 0.038060 +vt 0.222215 0.084265 +vt 0.146446 0.146447 +vt 0.084265 0.222215 +vt 0.038060 0.308659 +vt 0.009607 0.402455 +vt 0.000000 0.500000 +vt 0.009607 0.597546 +vt 0.038060 0.691342 +vt 0.084266 0.777786 +vt 0.146447 0.853554 +vt 0.222215 0.915735 +vt 0.308659 0.961940 +vn 0.098017 0.000000 -0.995185 +vn 0.290285 0.000000 -0.956940 +vn 0.471396 0.000000 -0.881922 +vn 0.634394 0.000000 -0.773010 +vn 0.773011 0.000000 -0.634393 +vn 0.881921 0.000000 -0.471397 +vn 0.956940 0.000000 -0.290285 +vn 0.995185 0.000000 -0.098017 +vn 0.995185 0.000000 0.098017 +vn 0.881921 0.000000 0.471397 +vn 0.773011 0.000000 0.634393 +vn 0.634394 0.000000 0.773010 +vn 0.471396 0.000000 0.881921 +vn 0.290285 0.000000 0.956940 +vn 0.098017 0.000000 0.995185 +vn -0.098018 0.000000 0.995185 +vn -0.290285 0.000000 0.956940 +vn -0.471397 0.000000 0.881921 +vn -0.634394 0.000000 0.773010 +vn -0.773011 0.000000 0.634393 +vn -0.881922 0.000000 0.471396 +vn -0.956941 0.000000 0.290284 +vn -0.995185 0.000000 0.098016 +vn -0.995185 -0.000000 -0.098018 +vn -0.956940 -0.000000 -0.290286 +vn -0.881921 -0.000000 -0.471398 +vn -0.773010 -0.000000 -0.634394 +vn -0.634392 -0.000000 -0.773012 +vn -0.471397 -0.000000 -0.881921 +vn -0.357332 0.933314 -0.035195 +vn -0.098015 -0.000000 -0.995185 +vn -0.290282 -0.000000 -0.956941 +vn 0.227786 0.933314 0.277558 +vn -0.035193 0.933314 -0.357332 +vn -0.227786 0.933314 0.277558 +vn 0.357332 0.933314 -0.035194 +vn -0.316663 0.933314 -0.169261 +vn 0.104230 0.933314 0.343600 +vn 0.169260 0.933314 -0.316664 +vn -0.316664 0.933314 0.169260 +vn 0.343600 0.933314 0.104230 +vn -0.227785 0.933314 -0.277559 +vn -0.035194 0.933314 0.357332 +vn 0.277558 0.933314 -0.227786 +vn -0.357332 0.933314 0.035194 +vn 0.277558 0.933314 0.227786 +vn -0.104229 0.933314 -0.343600 +vn -0.169260 0.933314 0.316663 +vn 0.343600 0.933314 -0.104230 +vn -0.343600 0.933314 -0.104230 +vn 0.169260 0.933314 0.316664 +vn 0.104230 0.933314 -0.343600 +vn -0.277558 0.933314 0.227786 +vn 0.357332 0.933314 0.035194 +vn -0.277558 0.933314 -0.227786 +vn 0.035194 0.933314 0.357332 +vn 0.227786 0.933314 -0.277558 +vn -0.343600 0.933314 0.104230 +vn 0.035194 0.933314 -0.357332 +vn 0.316664 0.933314 0.169260 +vn -0.169260 0.933314 -0.316664 +vn -0.104230 0.933314 0.343600 +vn 0.316664 0.933314 -0.169260 +vn -0.357332 0.933314 -0.035194 +vn -0.035195 0.933314 -0.357332 +vn -0.316664 0.933314 -0.169260 +vn 0.169261 0.933314 -0.316663 +vn -0.227786 0.933314 -0.277558 +vn -0.357332 0.933314 0.035193 +vn -0.104230 0.933314 -0.343600 +vn -0.169261 0.933314 0.316663 +vn 0.035195 0.933314 -0.357332 +vn -0.169259 0.933314 -0.316664 +s off +f 473/154/365 474/155/365 476/156/365 +f 475/154/366 476/155/366 478/156/366 +f 477/154/367 478/155/367 480/156/367 +f 479/154/368 480/155/368 482/156/368 +f 481/154/369 482/155/369 484/156/369 +f 483/154/370 484/155/370 486/156/370 +f 485/154/371 486/155/371 488/156/371 +f 487/154/372 488/155/372 490/156/372 +f 489/154/373 490/155/373 492/156/373 +f 491/154/155 492/155/155 494/156/155 +f 493/154/374 494/155/374 496/156/374 +f 495/154/375 496/155/375 498/156/375 +f 497/154/376 498/155/376 500/156/376 +f 499/154/377 500/155/377 502/156/377 +f 501/154/378 502/155/378 504/156/378 +f 503/154/379 504/155/379 506/156/379 +f 505/154/380 506/155/380 507/157/380 +f 507/154/381 508/155/381 509/157/381 +f 509/154/382 510/155/382 511/157/382 +f 511/154/383 512/155/383 513/157/383 +f 513/154/384 514/155/384 515/157/384 +f 515/154/385 516/155/385 517/157/385 +f 517/154/386 518/155/386 519/157/386 +f 519/154/387 520/155/387 521/157/387 +f 521/154/388 522/155/388 523/157/388 +f 523/154/389 524/155/389 525/157/389 +f 525/154/390 526/155/390 527/157/390 +f 527/154/391 528/155/391 529/157/391 +f 529/154/392 530/155/392 531/157/392 +f 531/154/393 532/155/393 533/157/393 +f 524/154/394 522/155/394 562/157/394 +f 535/154/395 536/155/395 473/157/395 +f 533/154/396 534/155/396 535/157/396 +f 473/158/42 475/159/42 535/160/42 +f 538/158/39 537/159/39 539/160/39 +f 500/154/397 498/155/397 549/156/397 +f 474/154/398 536/155/398 537/157/398 +f 514/154/399 512/155/399 557/157/399 +f 490/154/400 488/155/400 544/156/400 +f 528/154/401 526/155/401 564/157/401 +f 504/154/402 502/155/402 551/156/402 +f 480/154/403 478/155/403 539/156/403 +f 518/154/404 516/155/404 559/157/404 +f 494/154/405 492/155/405 546/156/405 +f 532/154/406 530/155/406 566/157/406 +f 508/154/407 506/155/407 554/157/407 +f 484/154/408 482/155/408 541/156/408 +f 522/154/409 520/155/409 561/157/409 +f 498/154/410 496/155/410 548/156/410 +f 536/154/411 534/155/411 568/157/411 +f 512/154/412 510/155/412 556/157/412 +f 488/154/413 486/155/413 543/156/413 +f 526/154/414 524/155/414 563/157/414 +f 502/154/415 500/155/415 550/156/415 +f 478/154/416 476/155/416 538/156/416 +f 516/154/417 514/155/417 558/157/417 +f 492/154/418 490/155/418 545/156/418 +f 530/154/419 528/155/419 565/157/419 +f 506/154/420 504/155/420 552/156/420 +f 482/154/421 480/155/421 540/156/421 +f 520/154/422 518/155/422 560/157/422 +f 476/154/423 474/155/423 537/156/423 +f 496/154/424 494/155/424 547/156/424 +f 534/154/425 532/155/425 567/157/425 +f 510/154/426 508/155/426 555/157/426 +f 486/154/427 484/155/427 542/156/427 +f 475/157/365 473/154/365 476/156/365 +f 477/157/366 475/154/366 478/156/366 +f 479/157/367 477/154/367 480/156/367 +f 481/157/368 479/154/368 482/156/368 +f 483/157/369 481/154/369 484/156/369 +f 485/157/370 483/154/370 486/156/370 +f 487/157/371 485/154/371 488/156/371 +f 489/157/372 487/154/372 490/156/372 +f 491/157/373 489/154/373 492/156/373 +f 493/157/155 491/154/155 494/156/155 +f 495/157/374 493/154/374 496/156/374 +f 497/157/375 495/154/375 498/156/375 +f 499/157/376 497/154/376 500/156/376 +f 501/157/377 499/154/377 502/156/377 +f 503/157/378 501/154/378 504/156/378 +f 505/157/379 503/154/379 506/156/379 +f 506/155/380 508/156/380 507/157/380 +f 508/155/381 510/156/381 509/157/381 +f 510/155/382 512/156/382 511/157/382 +f 512/155/383 514/156/383 513/157/383 +f 514/155/384 516/156/384 515/157/384 +f 516/155/385 518/156/385 517/157/385 +f 518/155/386 520/156/386 519/157/386 +f 520/155/387 522/156/387 521/157/387 +f 522/155/388 524/156/388 523/157/388 +f 524/155/389 526/156/389 525/157/389 +f 526/155/390 528/156/390 527/157/390 +f 528/155/391 530/156/391 529/157/391 +f 530/155/392 532/156/392 531/157/392 +f 532/155/393 534/156/393 533/157/393 +f 522/155/428 561/156/428 562/157/428 +f 536/155/395 474/156/395 473/157/395 +f 534/155/396 536/156/396 535/157/396 +f 475/159/42 477/161/42 535/160/42 +f 477/161/42 479/162/42 535/160/42 +f 479/162/42 481/163/42 535/160/42 +f 481/163/42 483/164/42 535/160/42 +f 483/164/42 485/165/42 535/160/42 +f 485/165/42 487/166/42 535/160/42 +f 487/166/42 489/167/42 535/160/42 +f 489/167/42 491/168/42 535/160/42 +f 491/168/42 493/169/42 535/160/42 +f 493/169/42 495/170/42 535/160/42 +f 495/170/42 497/171/42 535/160/42 +f 497/171/42 499/172/42 535/160/42 +f 499/172/42 501/173/42 535/160/42 +f 501/173/42 503/174/42 535/160/42 +f 503/174/42 505/175/42 535/160/42 +f 505/175/42 507/176/42 535/160/42 +f 507/176/42 509/177/42 535/160/42 +f 509/177/42 511/178/42 535/160/42 +f 511/178/42 513/179/42 535/160/42 +f 513/179/42 515/180/42 535/160/42 +f 515/180/42 517/181/42 535/160/42 +f 517/181/42 519/182/42 535/160/42 +f 519/182/42 521/183/42 535/160/42 +f 521/183/42 523/184/42 535/160/42 +f 523/184/42 525/185/42 535/160/42 +f 525/185/42 527/186/42 535/160/42 +f 527/186/42 529/187/42 535/160/42 +f 529/187/42 531/188/42 533/189/42 +f 535/160/42 529/187/42 533/189/42 +f 537/159/39 568/161/39 539/160/39 +f 568/161/39 567/162/39 539/160/39 +f 567/162/39 566/163/39 539/160/39 +f 566/163/39 565/164/39 539/160/39 +f 565/164/39 564/165/39 539/160/39 +f 564/165/39 563/166/39 539/160/39 +f 563/166/39 562/167/39 539/160/39 +f 562/167/39 561/168/39 539/160/39 +f 561/168/39 560/169/39 539/160/39 +f 560/169/39 559/170/39 539/160/39 +f 559/170/39 558/171/39 539/160/39 +f 558/171/39 557/172/39 539/160/39 +f 557/172/39 556/173/39 539/160/39 +f 556/173/39 555/174/39 539/160/39 +f 555/174/39 554/175/39 539/160/39 +f 554/175/39 553/176/39 539/160/39 +f 553/176/39 552/177/39 539/160/39 +f 552/177/39 551/178/39 539/160/39 +f 551/178/39 550/179/39 539/160/39 +f 550/179/39 549/180/39 539/160/39 +f 549/180/39 548/181/39 539/160/39 +f 548/181/39 547/182/39 539/160/39 +f 547/182/39 546/183/39 539/160/39 +f 546/183/39 545/184/39 539/160/39 +f 545/184/39 544/185/39 539/160/39 +f 544/185/39 543/186/39 539/160/39 +f 543/186/39 542/187/39 539/160/39 +f 542/187/39 541/188/39 539/160/39 +f 541/188/39 540/189/39 539/160/39 +f 550/157/397 500/154/397 549/156/397 +f 536/155/429 568/156/429 537/157/429 +f 512/155/399 556/156/399 557/157/399 +f 545/157/400 490/154/400 544/156/400 +f 526/155/430 563/156/430 564/157/430 +f 552/157/402 504/154/402 551/156/402 +f 540/157/431 480/154/431 539/156/431 +f 516/155/404 558/156/404 559/157/404 +f 547/157/405 494/154/405 546/156/405 +f 530/155/432 565/156/432 566/157/432 +f 506/155/407 553/156/407 554/157/407 +f 542/157/408 484/154/408 541/156/408 +f 520/155/433 560/156/433 561/157/433 +f 549/157/410 498/154/410 548/156/410 +f 534/155/434 567/156/434 568/157/434 +f 510/155/435 555/156/435 556/157/435 +f 544/157/413 488/154/413 543/156/413 +f 524/155/414 562/156/414 563/157/414 +f 551/157/415 502/154/415 550/156/415 +f 539/157/416 478/154/416 538/156/416 +f 514/155/417 557/156/417 558/157/417 +f 546/157/418 492/154/418 545/156/418 +f 528/155/419 564/156/419 565/157/419 +f 553/157/420 506/154/420 552/156/420 +f 541/157/421 482/154/421 540/156/421 +f 518/155/422 559/156/422 560/157/422 +f 538/157/436 476/154/436 537/156/436 +f 548/157/424 496/154/424 547/156/424 +f 532/155/437 566/156/437 567/157/437 +f 508/155/426 554/156/426 555/157/426 +f 543/157/427 486/154/427 542/156/427 +v 0.609274 0.018251 -1.901238 +v 0.609274 0.018251 -0.682690 +v -0.609274 0.018251 -0.682690 +v -0.609274 0.018251 -1.901238 +v 0.609274 1.236799 -1.901237 +v 0.609273 1.236799 -0.682690 +v -0.609274 1.236799 -0.682690 +v -0.609274 1.236799 -1.901238 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vn 1.000000 -0.000000 0.000001 +s off +f 569/190/42 570/191/42 571/192/42 +f 573/190/39 576/191/39 575/192/39 +f 569/190/438 573/191/438 574/192/438 +f 570/190/49 574/191/49 571/193/49 +f 571/190/46 575/191/46 572/193/46 +f 573/190/47 569/191/47 572/192/47 +f 572/193/42 569/190/42 571/192/42 +f 574/193/39 573/190/39 575/192/39 +f 570/193/48 569/190/48 574/192/48 +f 574/191/49 575/192/49 571/193/49 +f 575/191/46 576/192/46 572/193/46 +f 576/193/47 573/190/47 572/192/47 diff --git a/examples/datavisualization/graphgallery/data/topography.png b/examples/datavisualization/graphgallery/data/topography.png Binary files differnew file mode 100644 index 00000000..9349cdb3 --- /dev/null +++ b/examples/datavisualization/graphgallery/data/topography.png diff --git a/examples/datavisualization/graphgallery/doc/images/graphgallery-example.png b/examples/datavisualization/graphgallery/doc/images/graphgallery-example.png Binary files differnew file mode 100644 index 00000000..f3320c8e --- /dev/null +++ b/examples/datavisualization/graphgallery/doc/images/graphgallery-example.png diff --git a/examples/datavisualization/graphgallery/doc/src/graphgallery.qdoc b/examples/datavisualization/graphgallery/doc/src/graphgallery.qdoc new file mode 100644 index 00000000..ff7761d2 --- /dev/null +++ b/examples/datavisualization/graphgallery/doc/src/graphgallery.qdoc @@ -0,0 +1,705 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example graphgallery + \meta tags {DataVisualization, Q3DBars, Bar Graph, Custom Proxy, Q3DScatter, Scatter Graph, Custom Input Handler, Q3DSurface, Surface Graph, QCustom3DItem, Textured Surface} + \title Graph Gallery + \ingroup qtdatavisualization_examples + \brief Gallery of Bar, Scatter, and Surface graphs. + + \e {Graph Gallery} demonstrates all three graph types and some of their special features. + The graphs have their own tabs in the application. + + \image graphgallery-example.png + + \include examples-run.qdocinc + + \section1 Bar Graph + + In the \uicontrol {Bar Graph} tab, create a 3D bar graph using Q3DBars and combine the use of + widgets to adjust various bar graph qualities. The example shows how to: + + \list + \li Create an application with Q3DBars and some widgets + \li Use QBar3DSeries and QBarDataProxy to set data to the graph + \li Adjust some graph and series properties using widget controls + \li Select a row or a column by clicking an axis label + \li Create a custom proxy to use with Q3DBars + \endlist + + For information about interacting with the graph, see + \l{Qt Data Visualization Interacting with Data}{this page}. + + \section2 Creating the Application + + First, in \c{bargraph.cpp}, instantiate Q3DBars: + + \snippet graphgallery/bargraph.cpp 0 + + Then, create the widget, and horizontal and vertical layouts. + + The graph is embedded in a window container using + QWidget::createWindowContainer(). This is required because all data + visualization graph classes (Q3DBars, Q3DScatter, Q3DSurface) inherit + QWindow. This is the only way to use a class inheriting QWindow as a widget. + + Add the graph and the vertical layout to the + horizontal one: + + \snippet graphgallery/bargraph.cpp 1 + + Next, create another class to handle the data addition and other interaction with the + graph: + + \snippet graphgallery/bargraph.cpp 2 + + \section2 Setting up the Bar Graph + + Set up the graph in the constructor of the \c GraphModifier class: + + \snippet graphgallery/graphmodifier.cpp 0 + + First, create the axes and the series into member variables to support changing them easily: + + \snippet graphgallery/graphmodifier.cpp 1 + + Then, set some visual qualities for the graph: + + \snippet graphgallery/graphmodifier.cpp 2 + + Set up the axes and make them the active axes of the graph: + + \snippet graphgallery/graphmodifier.cpp 3 + + Give axis labels a small autorotation angle with setLabelAutoRotation() to make them orient + slightly toward the camera. This improves axis label readability at extreme camera angles. + + Next, initialize the visual properties of the series. Note that the second series is initially + not visible: + + \snippet graphgallery/graphmodifier.cpp 4 + + Add the series to the graph: + + \snippet graphgallery/graphmodifier.cpp 5 + + Finally, set the camera angle by calling the same method the camera angle change button + in the UI uses to cycle through various camera angles: + + \snippet graphgallery/graphmodifier.cpp 6 + + The camera is controlled via the scene object of the graph: + + \snippet graphgallery/graphmodifier.cpp 7 + + For more information about using scene and cameras, see Q3DScene and Q3DCamera. + + \section2 Adding Data to the Graph + + At the end of the constructor, call a method that sets up the data: + + \snippet graphgallery/graphmodifier.cpp 8 + + This method adds data to the proxies of the two series: + + \snippet graphgallery/graphmodifier.cpp 9a + \dots 0 + \snippet graphgallery/graphmodifier.cpp 9b + + \section2 Using Widgets to Control the Graph + + Continue by adding some widgets in \c{bargraph.cpp}. Add a slider: + + \snippet graphgallery/bargraph.cpp 3 + + Use the slider to rotate the graph instead of just using a mouse or touch. Add it to the + vertical layout: + + \snippet graphgallery/bargraph.cpp 4 + + Then, connect it to a method in \c GraphModifier: + + \snippet graphgallery/bargraph.cpp 5 + + Create a slot in \c GraphModifier for the signal connection. The camera is controlled via the + scene object. This time, specify the actual camera position along the orbit around the center + point, instead of specifying a preset camera angle: + + \snippet graphgallery/graphmodifier.cpp 10 + + You can now use the slider to rotate the graph. + + Add more widgets to the vertical layout to control: + + \list + \li Graph rotation + \li Label style + \li Camera preset + \li Background visibility + \li Grid visibility + \li Bar shading smoothness + \li Visibility of the second bar series + \li Value axis direction + \li Axis title visibility and rotation + \li Data range to be shown + \li Bar style + \li Selection mode + \li Theme + \li Shadow quality + \li Font + \li Font size + \li Axis label rotation + \li Data mode + \endlist + + Some widget controls are intentionally disabled when in the \uicontrol {Custom Proxy Data} + data mode. + + \section2 Selecting a Row or Column by Clicking an Axis Label + + Selection by axis label is default functionality for bar graphs. As an example, you can select + rows by clicking an axis label in the following way: + + \list 1 + \li Change selection mode to \c SelectionRow + \li Click a year label + \li The row with the clicked year is selected + \endlist + + The same method works with \c SelectionSlice and \c SelectionItem flags, as long as + either \c SelectionRow or \c SelectionColumn is set as well. + + \section2 Zooming to Selection + + As an example of adjusting the camera target, implement an animation of zooming to + selection via a button press. Animation initializations are done in the constructor: + + \snippet graphgallery/graphmodifier.cpp 11 + + Function \c{GraphModifier::zoomToSelectedBar()} contains the zooming functionality. + QPropertyAnimation \c m_animationCameraTarget targets Q3DCamera::target property, + which takes a value normalized to the range (-1, 1). + + Figure out where the selected bar is relative to axes, and use that as the end value for + \c{m_animationCameraTarget}: + + \snippet graphgallery/graphmodifier.cpp 12 + \dots 0 + \snippet graphgallery/graphmodifier.cpp 13 + + Then, rotate the camera so that it always points approximately to the center of + the graph at the end of the animation: + + \snippet graphgallery/graphmodifier.cpp 14 + + \section2 Custom Proxy for Data + + By toggling \uicontrol {Custom Proxy Data} data mode on, a custom dataset and the corresponding + proxy are taken into use. + + Define a simple flexible data set, \c{VariantDataSet}, where each data item is + a variant list. Each item can have multiple values, identified by their index in + the list. In this case, the data set is storing monthly rainfall data, where the value in + index zero is the year, the value in index one is the month, and the value in index two is + the amount of rainfall in that month. + + The custom proxy is similar to itemmodel-based proxies provided by Qt Data Visualization, and + it requires mapping to interpret the data. + + \section3 VariantDataSet + + Define the data items as QVariantList objects. Add functionality for clearing the data set and + querying for a reference to the data contained in the set. Also, add signals to be emitted when + data is added or the set is cleared: + + \snippet graphgallery/variantdataset.h 0 + \dots 0 + \codeline + \snippet graphgallery/variantdataset.h 1 + + \section3 VariantBarDataProxy + + Subclass \c VariantBarDataProxy from QBarDataProxy and provide a simple API of getters and + setters for the data set and the mapping: + + \snippet graphgallery/variantbardataproxy.h 0 + \dots 0 + \codeline + \snippet graphgallery/variantbardataproxy.h 1 + + The proxy listens for the changes in the data set and the mapping, and resolves the data set + if any changes are detected. This is not a particularly efficient implementation, as any change + will cause re-resolving of the entire data set, but that is not an issue for this example. + + In \c resolveDataSet() method, sort the variant data values into rows and columns based on the + mapping. This is very similar to how QItemModelBarDataProxy handles mapping, except you use + list indexes instead of item model roles here. Once the values are sorted, generate + \c QBarDataArray out of them, and call the \c resetArray() method in the parent class: + + \snippet graphgallery/variantbardataproxy.cpp 0 + + \section3 VariantBarDataMapping + + Store the mapping information between \c VariantDataSet data item indexes and rows, columns, + and values of \c QBarDataArray in \c VariantBarDataMapping. It contains the lists of rows and + columns to be included in the resolved data: + + \snippet graphgallery/variantbardatamapping.h 0 + \dots 0 + \codeline + \snippet graphgallery/variantbardatamapping.h 1 + \dots 0 + \codeline + \snippet graphgallery/variantbardatamapping.h 2 + \dots 0 + \codeline + \snippet graphgallery/variantbardatamapping.h 3 + + The primary way to use a \c VariantBarDataMapping object is to give the mappings in the + constructor, though you can use the \c remap() method to set them later, either individually or + all together. Emit a signal if mapping changes. The outcome is a simplified version of the + mapping functionality of QItemModelBarDataProxy, adapted to work with variant lists instead of + item models. + + \section3 RainfallData + + Handle the setup of QBar3DSeries with the custom proxy in the \c RainfallData class: + + \snippet graphgallery/rainfalldata.cpp 0 + + Populate the variant data set in the \c addDataSet() method: + + \snippet graphgallery/rainfalldata.cpp 1 + \dots + + Add the data set to the custom proxy and set the mapping: + + \snippet graphgallery/rainfalldata.cpp 2 + + Finally, add a function for getting the created series for displaying: + + \snippet graphgallery/rainfalldata.h 0 + + \section1 Scatter Graph + + In the \uicontrol {Scatter Graph} tab, create a 3D scatter graph using Q3DScatter. + The example shows how to: + + \list + \li Set up Q3DScatter graph + \li Use QScatterDataProxy to set data to the graph + \li Create a custom input handler by extending Q3DInputHandler + \endlist + + For basic application creation, see \l {Bar Graph}. + + \section2 Setting up the Scatter Graph + + First, set up some visual qualities for the graph in the constructor of the + \c ScatterDataModifier: + + \snippet graphgallery/scatterdatamodifier.cpp 0 + + None of these are mandatory, but are used to override graph defaults. You can try how it looks + with the preset defaults by commenting out the block above. + + Next, create a QScatterDataProxy and the associated QScatter3DSeries. Set a custom label format + and mesh smoothing for the series and add it to the graph: + + \snippet graphgallery/scatterdatamodifier.cpp 1 + + \section2 Adding Scatter Data + + The last thing to do in the \c ScatterDataModifier constructor is to add data to the graph: + + \snippet graphgallery/scatterdatamodifier.cpp 2 + + The actual data addition is done in \c addData() method. First, configure the axes: + + \snippet graphgallery/scatterdatamodifier.cpp 3 + + You could do this also in the constructor of \c {ScatterDataModifier}. Doing it here + keeps the constructor simpler and the axes' configuration near the data. + + Next, create a data array and populate it: + + \snippet graphgallery/scatterdatamodifier.cpp 4 + \dots + \snippet graphgallery/scatterdatamodifier.cpp 5 + + Finally, tell the proxy to start using the data we gave it: + + \snippet graphgallery/scatterdatamodifier.cpp 6 + + Now, the graph has the data and is ready for use. For information about adding widgets + to control the graph, see \l {Using Widgets to Control the Graph}. + + \section2 Replacing Default Input Handling + + Initialize \c m_inputHandler in the constructor with a pointer to the scatter graph instance: + + \snippet graphgallery/scatterdatamodifier.cpp 7 + + Replace the default input handling mechanism by setting the active input handler of + Q3DScatter to \c {AxesInputHandler}, which implements the custom behavior: + + \snippet graphgallery/scatterdatamodifier.cpp 8 + + The input handler needs access to the axes of the graph, so pass them to it: + + \snippet graphgallery/scatterdatamodifier.cpp 9 + + \section2 Extending Mouse Event Handling + + First, inherit the custom input handler from Q3DInputHandler instead of QAbstract3DInputHandler + to keep all the functionality of the default input handling, and to add the custom + functionality on top of it: + + \snippet graphgallery/axesinputhandler.h 0 + + Start extending the default functionality by re-implementing some of the mouse events. + First, extend \c {mousePressEvent}. Add a \c{m_mousePressed} flag for the left mouse button + to it, and keep the rest of the default functionality: + + \snippet graphgallery/axesinputhandler.cpp 0 + + Next, modify \c mouseReleaseEvent to clear the flag, and reset the internal state: + + \snippet graphgallery/axesinputhandler.cpp 1 + + Then, modify \c {mouseMoveEvent}. Check if \c m_mousePressed flag is \c {true} and + the internal state is something other than \c StateNormal. If so, set the input positions + for mouse movement distance calculations, and call the axis dragging function (see + \l {Implementing Axis Dragging} for details): + + \snippet graphgallery/axesinputhandler.cpp 2 + + \section2 Implementing Axis Dragging + + First, start listening to the selection signal from the graph. Do that in the + constructor, and connect it to the \c handleElementSelected method: + + \snippet graphgallery/axesinputhandler.cpp 3 + + In \c {handleElementSelected}, check the type of the selection, and set the internal state + based on it: + + \snippet graphgallery/axesinputhandler.cpp 4 + + The actual dragging logic is implemented in the \c handleAxisDragging method, which is called + from \c {mouseMoveEvent}, if the required conditions are met: + + \snippet graphgallery/axesinputhandler.cpp 5 + + In \c {handleAxisDragging}, first get the scene orientation from the active camera: + + \snippet graphgallery/axesinputhandler.cpp 6 + + Then, calculate the modifiers for mouse movement direction based on the orientation: + + \snippet graphgallery/axesinputhandler.cpp 7 + + After that, calculate the mouse movement, and modify it based on the y rotation of the + camera: + + \snippet graphgallery/axesinputhandler.cpp 8 + + Then, apply the moved distance to the correct axis: + + \snippet graphgallery/axesinputhandler.cpp 9 + + Finally, add a function for setting the dragging speed: + + \snippet graphgallery/axesinputhandler.h 1 + + This is needed, as the mouse movement distance is absolute in screen coordinates, and you + need to adjust it to the axis range. The larger the value, the slower the dragging will be. + Note that in this example, the scene zoom level is not taken into account when determining the + drag speed, so you'll notice changes in the range adjustment as you change the zoom level. + + You could also adjust the modifier automatically based on the axis range and camera zoom level. + + \section1 Surface Graph + + In the \uicontrol {Surface Graph} tab, create a 3D surface graph using Q3DSurface. + The example shows how to: + + \list + \li Set up a basic QSurfaceDataProxy and set data for it. + \li Use QHeightMapSurfaceDataProxy for showing 3D height maps. + \li Use topographic data to create 3D height maps. + \li Use three different selection modes for studying the graph. + \li Use axis ranges to display selected portions of the graph. + \li Set a custom surface gradient. + \li Add custom items and labels with QCustom3DItem and QCustom3DLabel. + \li Use custom input handler to enable zooming and panning. + \li Highlight an area of the surface. + \endlist + + For basic application creation, see \l {Bar Graph}. + + \section2 Simple Surface with Generated Data + + First, instantiate a new QSurfaceDataProxy and attach it to a new QSurface3DSeries: + + \snippet graphgallery/surfacegraphmodifier.cpp 0 + + Then, fill the proxy with a simple square root and sine wave data. Create a new + \c QSurfaceDataArray instance, and add \c QSurfaceDataRow elements to it. + Set the created \c QSurfaceDataArray as the data array for the QSurfaceDataProxy by calling + \c{resetArray()}. + + \snippet graphgallery/surfacegraphmodifier.cpp 1 + + \section2 Multiseries Height Map Data + + Create the height map by instantiating a QHeightMapSurfaceDataProxy with a QImage containing + the height data. Use QHeightMapSurfaceDataProxy::setValueRanges() to define the + value range of the map. In the example, the map is from an imaginary position of + 34.0\unicode 0x00B0 N - 40.0\unicode 0x00B0 N and 18.0\unicode 0x00B0 E - 24.0\unicode 0x00B0 E. + These values are used to position the map on the axes. + + \snippet graphgallery/surfacegraphmodifier.cpp 2 + + Add the other surface layers the same way, by creating a proxy and a series for them using + height map images. + + \section2 Topographic Map Data + + The topographic data is obtained from the National Land Survey of Finland. It provides a product + called \c{Elevation Model 2 m}, which is suitable for this example. + The topography data is from Levi fell. The accuracy of the data is well beyond the need, and + therefore it is compressed and encoded into a PNG file. The height value of the original + ASCII data is encoded into RGB format using a multiplier, which you will see later in + a code extract. The multiplier is calculated by dividing the largest 24-bit value with the + highest point in Finland. + + QHeightMapSurfaceDataProxy converts only one-byte values. To utilize the higher accuracy of + the data from the National Land Survey of Finland, read the data from the PNG file and decode + it into QSurface3DSeries. + + First, define the encoding multiplier: + + \snippet graphgallery/topographicseries.cpp 0 + + Then, perform the actual decoding: + + \snippet graphgallery/topographicseries.cpp 1 + + Now, the data is usable by the proxy. + + \section2 Selecting the Data Set + + To demonstrate different proxies, \uicontrol {Surface Graph} has three radio buttons to + switch between the series. + + With \uicontrol {Sqrt & Sin}, the simple generated series is activated. First, set + the decorative features, such as enabling the grid for the surface, and selecting the flat + shading mode. Next, define the axis label format and value ranges. Set automatic label rotation + to improve label readability at low camera angles. Finally, make sure the correct series is + added to the graph and the others are not: + + \snippet graphgallery/surfacegraphmodifier.cpp 3 + + With \uicontrol {Multiseries Height Map}, the height map series are activated and others + disabled. Auto-adjusting Y-axis range works well for the height map surface, so ensure it is + set. + + \snippet graphgallery/surfacegraphmodifier.cpp 4 + + With \uicontrol {Textured Topography}, the topographic series is activated and others disabled. + Activate a custom input handler for this series, to be able to highlight areas on it: + + \snippet graphgallery/surfacegraphmodifier.cpp 5 + + See \l {Use Custom Input Handler to Enable Zooming and Panning} for information about the + custom input handler for this data set. + + \section2 Selection Modes + + The three selection modes supported by Q3DSurface can be used with radio buttons. + To activate the selected mode or to clear it, add the following inline methods: + + \snippet graphgallery/surfacegraphmodifier.h 0 + + Add \c{QAbstract3DGraph::SelectionSlice} and \c{QAbstract3DGraph::SelectionMultiSeries} flags + for the row and column selection modes to support doing a slice selection to all visible series + in the graph simultaneously. + + \section2 Axis Ranges for Studying the Graph + + The example has four slider controls for adjusting the min and max values for X and Z + axes. When selecting the proxy, these sliders are adjusted to match the axis ranges of the + current data set: + + \snippet graphgallery/surfacegraphmodifier.cpp 6 + + Add support for setting the X range from the widget controls to the graph: + + \snippet graphgallery/surfacegraphmodifier.cpp 7 + + Add the support for Z range the same way. + + \section2 Custom Surface Gradients + + With the \uicontrol {Sqrt & Sin} data set, custom surface gradients can be taken into use + with two push buttons. Define the gradient with QLinearGradient, where the desired colors are + set. Also, change the color style to Q3DTheme::ColorStyleRangeGradient to use the gradient. + + \snippet graphgallery/surfacegraphmodifier.cpp 8 + + \section2 Adding Custom Meshes to the Application + + Add the mesh files to \c{CMakeLists.txt} for cmake build: + + \badcode + set(graphgallery_resource_files + ... + "data/oilrig.obj" + "data/pipe.obj" + "data/refinery.obj" + ... + ) + + qt6_add_resources(graphgallery "graphgallery" + PREFIX + "/" + FILES + ${graphgallery_resource_files} + ) + \endcode + + Also, add them in the qrc resource file for use with qmake: + + \badcode + <RCC> + <qresource prefix="/"> + ... + <file>data/refinery.obj</file> + <file>data/oilrig.obj</file> + <file>data/pipe.obj</file> + ... + </qresource> + </RCC> + \endcode + + \section2 Adding Custom Item to a Graph + + With the \uicontrol {Multiseries Height Map} data set, custom items are inserted into the + graph and can be toggled on or off using checkboxes. Other visual qualities can also be + controlled with another set of checkboxes, including see-through for the two top layers, and + a highlight for the bottom layer. + + Begin by creating a small QImage. Fill it with a single color to use as the color for the + custom object: + + \snippet graphgallery/surfacegraphmodifier.cpp 9 + + Then, specify the position of the item in a variable. The position can then be used for + removing the correct item from the graph: + + \snippet graphgallery/surfacegraphmodifier.cpp 10 + + Then, create a new QCustom3DItem with all the parameters: + + \snippet graphgallery/surfacegraphmodifier.cpp 11 + + Finally, add the item to the graph: + + \snippet graphgallery/surfacegraphmodifier.cpp 12 + + \section2 Adding Custom Label to a Graph + + Adding a custom label is very similar to adding a custom item. For the label, a custom mesh is + not needed, but just a QCustom3DLabel instance: + + \snippet graphgallery/surfacegraphmodifier.cpp 13 + + \section2 Removing Custom Item from a Graph + + To remove a specific item from the graph, call \c removeCustomItemAt() with the position of + the item: + + \snippet graphgallery/surfacegraphmodifier.cpp 14 + + \note Removing a custom item from the graph also deletes the object. If you want to preserve + the item, use the \c releaseCustomItem() method instead. + + \section2 Texture to a Surface Series + + With the \uicontrol {Textured Topography} data set, create a map texture to be used with the + topographic height map. + + Set an image to be used as the texture on a surface with QSurface3DSeries::setTextureFile(). + Add a check box to control if the texture is set or not, and a handler to react to the checkbox + state: + + \snippet graphgallery/surfacegraphmodifier.cpp 15 + + The image in this example is read from a JPG file. Setting an empty file with the method clears + the texture, and the surface uses the gradients or colors from the theme. + + \section2 Use Custom Input Handler to Enable Zooming and Panning + + With the \uicontrol {Textured Topography} data set, create a custom input handler to + highlight the selection on the graph and allow panning the graph. + + The panning implementation is similar to the one shown in \l{Implementing Axis Dragging}. + The difference is that, in this example, you follow only the X and Z axes and don't allow + dragging the surface outside the graph. To limit the dragging, follow the limits of the axes + and do nothing if going outside the graph: + + \snippet graphgallery/custominputhandler.cpp 0 + + For zooming, catch the \c wheelEvent and adjust the X and Y axis ranges according to the delta + value on QWheelEvent. Adjust the Y axis so that the aspect ratio between the Y axis and the XZ + plane stays the same. This prevents getting a graph in which the height is exaggerated: + + \snippet graphgallery/custominputhandler.cpp 1 + + Next, add some limits to the zoom level, so that it won't get too near to or far from the + surface. For instance, if the value for the X axis gets below the allowed limit, i.e. zooming + gets too far, the value is set to the minimum allowed value. If the range is going to below + the range minimum, both ends of the axis are adjusted so that the range stays at the limit: + + \snippet graphgallery/custominputhandler.cpp 2 + + \section2 Highlight an Area of the Surface + + To implement a highlight to be displayed on the surface, create a copy of the series and add + some offset to the y value. In this example, the class \c HighlightSeries implements the + creation of the copy in its \c handlePositionChange method. + + First, give \c HighlightSeries the pointer to the original series, and then start listening to + the QSurface3DSeries::selectedPointChanged signal: + + \snippet graphgallery/highlightseries.cpp 0 + + When the signal triggers, check that the position is valid. Then, calculate the ranges + for the copied area, and check that they stay within the bounds. Finally, fill the data array + of the highlight series with the range from the data array of the topography series: + + \snippet graphgallery/highlightseries.cpp 1 + + \section2 A Gradient to the Highlight Series + + Since the \c HighlightSeries is QSurface3DSeries, all the decoration methods a series can + have are available. In this example, add a gradient to emphasize the elevation. Because the + suitable gradient style depends on the range of the Y axis and we change the range when + zooming, the gradient color positions need to be adjusted as the range changes. Do this by + defining proportional values for the gradient color positions: + + \snippet graphgallery/highlightseries.cpp 2 + + The gradient modification is done in the \c handleGradientChange method, so connect it to + react to changes on the Y axis: + + \snippet graphgallery/surfacegraphmodifier.cpp 16 + + When a change in the Y axis max value happens, calculate the new gradient color positions: + + \snippet graphgallery/highlightseries.cpp 3 + + \section1 Example Contents +*/ diff --git a/examples/datavisualization/graphgallery/graphgallery.pro b/examples/datavisualization/graphgallery/graphgallery.pro new file mode 100644 index 00000000..c9bd96f6 --- /dev/null +++ b/examples/datavisualization/graphgallery/graphgallery.pro @@ -0,0 +1,49 @@ +android|ios|winrt { + error( "This example is not supported for android, ios, or winrt." ) +} + +!include( ../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} + +QT += widgets +requires(qtConfig(fontcombobox)) +requires(qtConfig(combobox)) + +SOURCES += main.cpp \ + bargraph.cpp \ + graphmodifier.cpp \ + rainfalldata.cpp \ + variantdataset.cpp \ + variantbardataproxy.cpp \ + variantbardatamapping.cpp \ + scattergraph.cpp \ + scatterdatamodifier.cpp \ + axesinputhandler.cpp \ + surfacegraph.cpp \ + surfacegraphmodifier.cpp \ + custominputhandler.cpp \ + highlightseries.cpp \ + topographicseries.cpp + +HEADERS += \ + bargraph.h \ + graphmodifier.h \ + rainfalldata.h \ + variantdataset.h \ + variantbardataproxy.h \ + variantbardatamapping.h \ + scattergraph.h \ + scatterdatamodifier.h \ + axesinputhandler.h \ + surfacegraph.h \ + surfacegraphmodifier.h \ + custominputhandler.h \ + highlightseries.h \ + topographicseries.h + +RESOURCES += graphgallery.qrc + +OTHER_FILES += doc/src/* \ + doc/images/* \ + data/* diff --git a/examples/datavisualization/graphgallery/graphgallery.qrc b/examples/datavisualization/graphgallery/graphgallery.qrc new file mode 100644 index 00000000..6df5703d --- /dev/null +++ b/examples/datavisualization/graphgallery/graphgallery.qrc @@ -0,0 +1,13 @@ +<RCC> + <qresource prefix="/"> + <file>data/raindata.txt</file> + <file>data/layer_1.png</file> + <file>data/layer_2.png</file> + <file>data/layer_3.png</file> + <file>data/refinery.obj</file> + <file>data/oilrig.obj</file> + <file>data/pipe.obj</file> + <file>data/maptexture.jpg</file> + <file>data/topography.png</file> + </qresource> +</RCC> diff --git a/examples/datavisualization/graphgallery/graphmodifier.cpp b/examples/datavisualization/graphgallery/graphmodifier.cpp new file mode 100644 index 00000000..b0cb6f48 --- /dev/null +++ b/examples/datavisualization/graphgallery/graphmodifier.cpp @@ -0,0 +1,441 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "graphmodifier.h" +#include "rainfalldata.h" + +#include <QtDataVisualization/qcategory3daxis.h> +#include <QtDataVisualization/qvalue3daxis.h> +#include <QtDataVisualization/qbardataproxy.h> +#include <QtDataVisualization/q3dscene.h> +#include <QtDataVisualization/q3dcamera.h> +#include <QtDataVisualization/qbar3dseries.h> +#include <QtDataVisualization/q3dtheme.h> +#include <QtCore/qmath.h> +#include <QtWidgets/qcombobox.h> + +using namespace Qt::StringLiterals; + +// TODO: Many of the values do not affect custom proxy series now - should be fixed + +//! [0] +GraphModifier::GraphModifier(Q3DBars *bargraph, QObject *parent) : + QObject(parent), + m_graph(bargraph), + //! [0] + //! [1] + m_temperatureAxis(new QValue3DAxis), + m_yearAxis(new QCategory3DAxis), + m_monthAxis(new QCategory3DAxis), + m_primarySeries(new QBar3DSeries), + m_secondarySeries(new QBar3DSeries), + m_celsiusString(u"°C"_s) + //! [1] +{ + //! [2] + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftMedium); + m_graph->activeTheme()->setBackgroundEnabled(false); + m_graph->activeTheme()->setFont(QFont("Times New Roman", m_fontSize)); + m_graph->activeTheme()->setLabelBackgroundEnabled(true); + m_graph->setMultiSeriesUniform(true); + //! [2] + + m_months = {"January", "February", "March", "April", "May", "June" , "July", + "August", "September", "October" , "November", "December"}; + m_years = {"2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022"}; + + //! [3] + m_temperatureAxis->setTitle("Average temperature"); + m_temperatureAxis->setSegmentCount(m_segments); + m_temperatureAxis->setSubSegmentCount(m_subSegments); + m_temperatureAxis->setRange(m_minval, m_maxval); + m_temperatureAxis->setLabelFormat(u"%.1f "_s + m_celsiusString); + m_temperatureAxis->setLabelAutoRotation(30.0f); + m_temperatureAxis->setTitleVisible(true); + + m_yearAxis->setTitle("Year"); + m_yearAxis->setLabelAutoRotation(30.0f); + m_yearAxis->setTitleVisible(true); + m_monthAxis->setTitle("Month"); + m_monthAxis->setLabelAutoRotation(30.0f); + m_monthAxis->setTitleVisible(true); + + m_graph->setValueAxis(m_temperatureAxis); + m_graph->setRowAxis(m_yearAxis); + m_graph->setColumnAxis(m_monthAxis); + //! [3] + + //! [4] + m_primarySeries->setItemLabelFormat(u"Oulu - @colLabel @rowLabel: @valueLabel"_s); + m_primarySeries->setMesh(QAbstract3DSeries::MeshBevelBar); + m_primarySeries->setMeshSmooth(false); + + m_secondarySeries->setItemLabelFormat(u"Helsinki - @colLabel @rowLabel: @valueLabel"_s); + m_secondarySeries->setMesh(QAbstract3DSeries::MeshBevelBar); + m_secondarySeries->setMeshSmooth(false); + m_secondarySeries->setVisible(false); + //! [4] + + //! [5] + m_graph->addSeries(m_primarySeries); + m_graph->addSeries(m_secondarySeries); + //! [5] + + //! [6] + changePresetCamera(); + //! [6] + + //! [8] + resetTemperatureData(); + //! [8] + + // Set up property animations for zooming to the selected bar + //! [11] + Q3DCamera *camera = m_graph->scene()->activeCamera(); + m_defaultAngleX = camera->xRotation(); + m_defaultAngleY = camera->yRotation(); + m_defaultZoom = camera->zoomLevel(); + m_defaultTarget = camera->target(); + + m_animationCameraX.setTargetObject(camera); + m_animationCameraY.setTargetObject(camera); + m_animationCameraZoom.setTargetObject(camera); + m_animationCameraTarget.setTargetObject(camera); + + m_animationCameraX.setPropertyName("xRotation"); + m_animationCameraY.setPropertyName("yRotation"); + m_animationCameraZoom.setPropertyName("zoomLevel"); + m_animationCameraTarget.setPropertyName("target"); + + int duration = 1700; + m_animationCameraX.setDuration(duration); + m_animationCameraY.setDuration(duration); + m_animationCameraZoom.setDuration(duration); + m_animationCameraTarget.setDuration(duration); + + // The zoom always first zooms out above the graph and then zooms in + qreal zoomOutFraction = 0.3; + m_animationCameraX.setKeyValueAt(zoomOutFraction, QVariant::fromValue(0.0f)); + m_animationCameraY.setKeyValueAt(zoomOutFraction, QVariant::fromValue(90.0f)); + m_animationCameraZoom.setKeyValueAt(zoomOutFraction, QVariant::fromValue(50.0f)); + m_animationCameraTarget.setKeyValueAt(zoomOutFraction, + QVariant::fromValue(QVector3D(0.0f, 0.0f, 0.0f))); + //! [11] + + m_customData = new RainfallData(); +} + +GraphModifier::~GraphModifier() +{ + delete m_customData; +} + +void GraphModifier::resetTemperatureData() +{ + //! [9a] + // Set up data + static const float tempOulu[8][12] = { + {-7.4f, -2.4f, 0.0f, 3.0f, 8.2f, 11.6f, 14.7f, 15.4f, 11.4f, 4.2f, 2.1f, -2.3f}, // 2015 + {-13.4f, -3.9f, -1.8f, 3.1f, 10.6f, 13.7f, 17.8f, 13.6f, 10.7f, 3.5f, -3.1f, -4.2f}, // 2016 + //! [9a] + {-5.7f, -6.7f, -3.0f, -0.1f, 4.7f, 12.4f, 16.1f, 14.1f, 9.4f, 3.0f, -0.3f, -3.2f}, // 2017 + {-6.4f, -11.9f, -7.4f, 1.9f, 11.4f, 12.4f, 21.5f, 16.1f, 11.0f, 4.4f, 2.1f, -4.1f}, // 2018 + {-11.7f, -6.1f, -2.4f, 3.9f, 7.2f, 14.5f, 15.6f, 14.4f, 8.5f, 2.0f, -3.0f, -1.5f}, // 2019 + {-2.1f, -3.4f, -1.8f, 0.6f, 7.0f, 17.1f, 15.6f, 15.4f, 11.1f, 5.6f, 1.9f, -1.7f}, // 2020 + {-9.6f, -11.6f, -3.2f, 2.4f, 7.8f, 17.3f, 19.4f, 14.2f, 8.0f, 5.2f, -2.2f, -8.6f}, // 2021 + {-7.3f, -6.4f, -1.8f, 1.3f, 8.1f, 15.5f, 17.6f, 17.6f, 9.1f, 5.4f, -1.5f, -4.4f} // 2022 + }; + + static const float tempHelsinki[8][12] = { + {-2.0f, -0.1f, 1.8f, 5.1f, 9.7f, 13.7f, 16.3f, 17.3f, 12.7f, 5.4f, 4.6f, 2.1f}, // 2015 + {-10.3f, -0.6f, 0.0f, 4.9f, 14.3f, 15.7f, 17.7f, 16.0f, 12.7f, 4.6f, -1.0f, -0.9f}, // 2016 + {-2.9f, -3.3f, 0.7f, 2.3f, 9.9f, 13.8f, 16.1f, 15.9f, 11.4f, 5.0f, 2.7f, 0.7f}, // 2017 + {-2.2f, -8.4f, -4.7f, 5.0f, 15.3f, 15.8f, 21.2f, 18.2f, 13.3f, 6.7f, 2.8f, -2.0f}, // 2018 + {-6.2f, -0.5f, -0.3f, 6.8f, 10.6f, 17.9f, 17.5f, 16.8f, 11.3f, 5.2f, 1.8f, 1.4f}, // 2019 + {1.9f, 0.5f, 1.7f, 4.5f, 9.5f, 18.4f, 16.5f, 16.8f, 13.0f, 8.2f, 4.4f, 0.9f}, // 2020 + {-4.7f, -8.1f, -0.9f, 4.5f, 10.4f, 19.2f, 20.9f, 15.4f, 9.5f, 8.0f, 1.5f, -6.7f}, // 2021 + {-3.3f, -2.2f, -0.2f, 3.3f, 9.6f, 16.9f, 18.1f, 18.9f, 9.2f, 7.6f, 2.3f, -3.4f} // 2022 + }; + + // Create data arrays + //! [9b] + auto *dataSet = new QBarDataArray; + auto *dataSet2 = new QBarDataArray; + + dataSet->reserve(m_years.size()); + for (qsizetype year = 0; year < m_years.size(); ++year) { + // Create a data row + auto *dataRow = new QBarDataRow(m_months.size()); + auto *dataRow2 = new QBarDataRow(m_months.size()); + for (qsizetype month = 0; month < m_months.size(); ++month) { + // Add data to the row + (*dataRow)[month].setValue(tempOulu[year][month]); + (*dataRow2)[month].setValue(tempHelsinki[year][month]); + } + // Add the row to the set + dataSet->append(dataRow); + dataSet2->append(dataRow2); + } + + // Add data to the data proxy (the data proxy assumes ownership of it) + m_primarySeries->dataProxy()->resetArray(dataSet, m_years, m_months); + m_secondarySeries->dataProxy()->resetArray(dataSet2, m_years, m_months); + //! [9b] +} + +void GraphModifier::changeRange(int range) +{ + if (range >= m_years.count()) + m_yearAxis->setRange(0, m_years.count() - 1); + else + m_yearAxis->setRange(range, range); +} + +void GraphModifier::changeStyle(int style) +{ + auto *comboBox = qobject_cast<QComboBox *>(sender()); + if (comboBox) { + m_barMesh = comboBox->itemData(style).value<QAbstract3DSeries::Mesh>(); + m_primarySeries->setMesh(m_barMesh); + m_secondarySeries->setMesh(m_barMesh); + m_customData->customSeries()->setMesh(m_barMesh); + } +} + +void GraphModifier::changePresetCamera() +{ + m_animationCameraX.stop(); + m_animationCameraY.stop(); + m_animationCameraZoom.stop(); + m_animationCameraTarget.stop(); + + // Restore camera target in case animation has changed it + m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f)); + + //! [7] + static int preset = Q3DCamera::CameraPresetFront; + + m_graph->scene()->activeCamera()->setCameraPreset((Q3DCamera::CameraPreset)preset); + + if (++preset > Q3DCamera::CameraPresetDirectlyBelow) + preset = Q3DCamera::CameraPresetFrontLow; + //! [7] +} + +void GraphModifier::changeTheme(int theme) +{ + Q3DTheme *currentTheme = m_graph->activeTheme(); + currentTheme->setType(Q3DTheme::Theme(theme)); + emit backgroundEnabledChanged(currentTheme->isBackgroundEnabled()); + emit gridEnabledChanged(currentTheme->isGridEnabled()); + emit fontChanged(currentTheme->font()); + emit fontSizeChanged(currentTheme->font().pointSize()); +} + +void GraphModifier::changeLabelBackground() +{ + m_graph->activeTheme()->setLabelBackgroundEnabled(!m_graph->activeTheme()->isLabelBackgroundEnabled()); +} + +void GraphModifier::changeSelectionMode(int selectionMode) +{ + auto *comboBox = qobject_cast<QComboBox *>(sender()); + if (comboBox) { + int flags = comboBox->itemData(selectionMode).toInt(); + m_graph->setSelectionMode(QAbstract3DGraph::SelectionFlags(flags)); + } +} + +void GraphModifier::changeFont(const QFont &font) +{ + QFont newFont = font; + m_graph->activeTheme()->setFont(newFont); +} + +void GraphModifier::changeFontSize(int fontsize) +{ + m_fontSize = fontsize; + QFont font = m_graph->activeTheme()->font(); + font.setPointSize(m_fontSize); + m_graph->activeTheme()->setFont(font); +} + +void GraphModifier::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality sq) +{ + int quality = int(sq); + // Updates the UI component to show correct shadow quality + emit shadowQualityChanged(quality); +} + +void GraphModifier::changeLabelRotation(int rotation) +{ + m_temperatureAxis->setLabelAutoRotation(float(rotation)); + m_monthAxis->setLabelAutoRotation(float(rotation)); + m_yearAxis->setLabelAutoRotation(float(rotation)); +} + +void GraphModifier::setAxisTitleVisibility(bool enabled) +{ + m_temperatureAxis->setTitleVisible(enabled); + m_monthAxis->setTitleVisible(enabled); + m_yearAxis->setTitleVisible(enabled); +} + +void GraphModifier::setAxisTitleFixed(bool enabled) +{ + m_temperatureAxis->setTitleFixed(enabled); + m_monthAxis->setTitleFixed(enabled); + m_yearAxis->setTitleFixed(enabled); +} + +void GraphModifier::zoomToSelectedBar() +{ + m_animationCameraX.stop(); + m_animationCameraY.stop(); + m_animationCameraZoom.stop(); + m_animationCameraTarget.stop(); + + Q3DCamera *camera = m_graph->scene()->activeCamera(); + float currentX = camera->xRotation(); + float currentY = camera->yRotation(); + float currentZoom = camera->zoomLevel(); + QVector3D currentTarget = camera->target(); + + m_animationCameraX.setStartValue(QVariant::fromValue(currentX)); + m_animationCameraY.setStartValue(QVariant::fromValue(currentY)); + m_animationCameraZoom.setStartValue(QVariant::fromValue(currentZoom)); + m_animationCameraTarget.setStartValue(QVariant::fromValue(currentTarget)); + + QPoint selectedBar = m_graph->selectedSeries() + ? m_graph->selectedSeries()->selectedBar() + : QBar3DSeries::invalidSelectionPosition(); + + if (selectedBar != QBar3DSeries::invalidSelectionPosition()) { + // Normalize selected bar position within axis range to determine target coordinates + //! [12] + QVector3D endTarget; + float xMin = m_graph->columnAxis()->min(); + float xRange = m_graph->columnAxis()->max() - xMin; + float zMin = m_graph->rowAxis()->min(); + float zRange = m_graph->rowAxis()->max() - zMin; + endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0f - 1.0f); + endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0f - 1.0f); + //! [12] + + // Rotate the camera so that it always points approximately to the graph center + //! [14] + qreal endAngleX = 90.0 - qRadiansToDegrees(qAtan(qreal(endTarget.z() / endTarget.x()))); + if (endTarget.x() > 0.0f) + endAngleX -= 180.0f; + float barValue = m_graph->selectedSeries()->dataProxy()->itemAt(selectedBar.x(), + selectedBar.y())->value(); + float endAngleY = barValue >= 0.0f ? 30.0f : -30.0f; + if (m_graph->valueAxis()->reversed()) + endAngleY *= -1.0f; + //! [14] + + m_animationCameraX.setEndValue(QVariant::fromValue(float(endAngleX))); + m_animationCameraY.setEndValue(QVariant::fromValue(endAngleY)); + m_animationCameraZoom.setEndValue(QVariant::fromValue(250)); + //! [13] + m_animationCameraTarget.setEndValue(QVariant::fromValue(endTarget)); + //! [13] + } else { + // No selected bar, so return to the default view + m_animationCameraX.setEndValue(QVariant::fromValue(m_defaultAngleX)); + m_animationCameraY.setEndValue(QVariant::fromValue(m_defaultAngleY)); + m_animationCameraZoom.setEndValue(QVariant::fromValue(m_defaultZoom)); + m_animationCameraTarget.setEndValue(QVariant::fromValue(m_defaultTarget)); + } + + m_animationCameraX.start(); + m_animationCameraY.start(); + m_animationCameraZoom.start(); + m_animationCameraTarget.start(); +} + +void GraphModifier::setDataModeToWeather(bool enabled) +{ + if (enabled) + changeDataMode(false); +} + +void GraphModifier::setDataModeToCustom(bool enabled) +{ + if (enabled) + changeDataMode(true); +} + +void GraphModifier::changeShadowQuality(int quality) +{ + QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality); + m_graph->setShadowQuality(sq); + emit shadowQualityChanged(quality); +} + +//! [10] +void GraphModifier::rotateX(int rotation) +{ + m_xRotation = rotation; + m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation); +} +//! [10] + +void GraphModifier::rotateY(int rotation) +{ + m_yRotation = rotation; + m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation); +} + +void GraphModifier::setBackgroundEnabled(int enabled) +{ + m_graph->activeTheme()->setBackgroundEnabled(bool(enabled)); +} + +void GraphModifier::setGridEnabled(int enabled) +{ + m_graph->activeTheme()->setGridEnabled(bool(enabled)); +} + +void GraphModifier::setSmoothBars(int smooth) +{ + m_smooth = bool(smooth); + m_primarySeries->setMeshSmooth(m_smooth); + m_secondarySeries->setMeshSmooth(m_smooth); + m_customData->customSeries()->setMeshSmooth(m_smooth); +} + +void GraphModifier::setSeriesVisibility(int enabled) +{ + m_secondarySeries->setVisible(bool(enabled)); +} + +void GraphModifier::setReverseValueAxis(int enabled) +{ + m_graph->valueAxis()->setReversed(enabled); +} + +void GraphModifier::setReflection(bool enabled) +{ + m_graph->setReflection(enabled); +} + +void GraphModifier::changeDataMode(bool customData) +{ + // Change between weather data and data from custom proxy + if (customData) { + m_graph->removeSeries(m_primarySeries); + m_graph->removeSeries(m_secondarySeries); + m_graph->addSeries(m_customData->customSeries()); + m_graph->setValueAxis(m_customData->valueAxis()); + m_graph->setRowAxis(m_customData->rowAxis()); + m_graph->setColumnAxis(m_customData->colAxis()); + } else { + m_graph->removeSeries(m_customData->customSeries()); + m_graph->addSeries(m_primarySeries); + m_graph->addSeries(m_secondarySeries); + m_graph->setValueAxis(m_temperatureAxis); + m_graph->setRowAxis(m_yearAxis); + m_graph->setColumnAxis(m_monthAxis); + } +} diff --git a/examples/datavisualization/graphgallery/graphmodifier.h b/examples/datavisualization/graphgallery/graphmodifier.h new file mode 100644 index 00000000..0d112c0d --- /dev/null +++ b/examples/datavisualization/graphgallery/graphmodifier.h @@ -0,0 +1,87 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef GRAPHMODIFIER_H +#define GRAPHMODIFIER_H + +#include <QtDataVisualization/q3dbars.h> +#include <QtDataVisualization/qbardataproxy.h> +#include <QtDataVisualization/qabstract3dseries.h> +#include <QtCore/qpropertyanimation.h> + +class RainfallData; + +class GraphModifier : public QObject +{ + Q_OBJECT +public: + explicit GraphModifier(Q3DBars *bargraph, QObject *parent); + ~GraphModifier(); + + void resetTemperatureData(); + void changePresetCamera(); + void changeLabelBackground(); + void changeFont(const QFont &font); + void changeFontSize(int fontsize); + void rotateX(int rotation); + void rotateY(int rotation); + void setBackgroundEnabled(int enabled); + void setGridEnabled(int enabled); + void setSmoothBars(int smooth); + void setSeriesVisibility(int enabled); + void setReverseValueAxis(int enabled); + void setReflection(bool enabled); + void changeDataMode(bool customData); + +public Q_SLOTS: + void changeRange(int range); + void changeStyle(int style); + void changeSelectionMode(int selectionMode); + void changeTheme(int theme); + void changeShadowQuality(int quality); + void shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality shadowQuality); + void changeLabelRotation(int rotation); + void setAxisTitleVisibility(bool enabled); + void setAxisTitleFixed(bool enabled); + void zoomToSelectedBar(); + void setDataModeToWeather(bool enabled); + void setDataModeToCustom(bool enabled); + +Q_SIGNALS: + void shadowQualityChanged(int quality); + void backgroundEnabledChanged(bool enabled); + void gridEnabledChanged(bool enabled); + void fontChanged(const QFont &font); + void fontSizeChanged(int size); + +private: + Q3DBars *m_graph = nullptr; + float m_xRotation = 0.f; + float m_yRotation = 0.f; + int m_fontSize = 30; + int m_segments = 4; + int m_subSegments = 3; + float m_minval = -20.f; + float m_maxval = 20.f; + QStringList m_months = {}; + QStringList m_years = {}; + QValue3DAxis *m_temperatureAxis = nullptr; + QCategory3DAxis *m_yearAxis = nullptr; + QCategory3DAxis *m_monthAxis = nullptr; + QBar3DSeries *m_primarySeries = nullptr; + QBar3DSeries *m_secondarySeries = nullptr; + QAbstract3DSeries::Mesh m_barMesh = QAbstract3DSeries::MeshBevelBar; + bool m_smooth = false; + QPropertyAnimation m_animationCameraX = {}; + QPropertyAnimation m_animationCameraY = {}; + QPropertyAnimation m_animationCameraZoom = {}; + QPropertyAnimation m_animationCameraTarget = {}; + float m_defaultAngleX = 0.f; + float m_defaultAngleY = 0.f; + float m_defaultZoom = 0.f; + QVector3D m_defaultTarget = {}; + const QString m_celsiusString; + RainfallData *m_customData = nullptr; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/highlightseries.cpp b/examples/datavisualization/graphgallery/highlightseries.cpp new file mode 100644 index 00000000..3f10b421 --- /dev/null +++ b/examples/datavisualization/graphgallery/highlightseries.cpp @@ -0,0 +1,101 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "highlightseries.h" + +//! [2] +const float darkRedPos = 1.f; +const float redPos = 0.8f; +const float yellowPos = 0.6f; +const float greenPos = 0.4f; +const float darkGreenPos = 0.2f; +//! [2] + +HighlightSeries::HighlightSeries() +{ + setDrawMode(QSurface3DSeries::DrawSurface); + setFlatShadingEnabled(true); + setVisible(false); +} + +HighlightSeries::~HighlightSeries() +{ +} + +//! [0] +void HighlightSeries::setTopographicSeries(TopographicSeries *series) +{ + m_topographicSeries = series; + m_srcWidth = m_topographicSeries->dataProxy()->array()->at(0)->size(); + m_srcHeight = m_topographicSeries->dataProxy()->array()->size(); + + QObject::connect(m_topographicSeries, &QSurface3DSeries::selectedPointChanged, + this, &HighlightSeries::handlePositionChange); +} +//! [0] + +//! [1] +void HighlightSeries::handlePositionChange(const QPoint &position) +{ + m_position = position; + + if (position == invalidSelectionPosition()) { + setVisible(false); + return; + } + + int halfWidth = m_width / 2; + int halfHeight = m_height / 2; + + int startX = position.y() - halfWidth; + if (startX < 0 ) + startX = 0; + int endX = position.y() + halfWidth; + if (endX > (m_srcWidth - 1)) + endX = m_srcWidth - 1; + int startZ = position.x() - halfHeight; + if (startZ < 0 ) + startZ = 0; + int endZ = position.x() + halfHeight; + if (endZ > (m_srcHeight - 1)) + endZ = m_srcHeight - 1; + + QSurfaceDataProxy *srcProxy = m_topographicSeries->dataProxy(); + const QSurfaceDataArray &srcArray = *srcProxy->array(); + + auto *dataArray = new QSurfaceDataArray; + dataArray->reserve(endZ - startZ); + for (int i = startZ; i < endZ; ++i) { + auto *newRow = new QSurfaceDataRow; + newRow->reserve(endX - startX); + QSurfaceDataRow *srcRow = srcArray.at(i); + for (int j = startX; j < endX; ++j) { + QVector3D pos = srcRow->at(j).position(); + pos.setY(pos.y() + 0.1f); + newRow->append(QSurfaceDataItem(pos)); + } + dataArray->append(newRow); + } + + dataProxy()->resetArray(dataArray); + setVisible(true); +} +//! [1] + +//! [3] +void HighlightSeries::handleGradientChange(float value) +{ + float ratio = m_minHeight / value; + + QLinearGradient gr; + gr.setColorAt(0.f, Qt::black); + gr.setColorAt(darkGreenPos * ratio, Qt::darkGreen); + gr.setColorAt(greenPos * ratio, Qt::green); + gr.setColorAt(yellowPos * ratio, Qt::yellow); + gr.setColorAt(redPos * ratio, Qt::red); + gr.setColorAt(darkRedPos * ratio, Qt::darkRed); + + setBaseGradient(gr); + setColorStyle(Q3DTheme::ColorStyleRangeGradient); +} +//! [3] diff --git a/examples/datavisualization/graphgallery/highlightseries.h b/examples/datavisualization/graphgallery/highlightseries.h new file mode 100644 index 00000000..c74a6b84 --- /dev/null +++ b/examples/datavisualization/graphgallery/highlightseries.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef HIGHLIGHTSERIES_H +#define HIGHLIGHTSERIES_H + +#include <QtDataVisualization/qsurface3dseries.h> + +#include "topographicseries.h" + +class HighlightSeries : public QSurface3DSeries +{ + Q_OBJECT +public: + explicit HighlightSeries(); + ~HighlightSeries(); + + void setTopographicSeries(TopographicSeries *series); + inline void setMinHeight(float height) { m_minHeight = height; } + +public Q_SLOTS: + void handlePositionChange(const QPoint &position); + void handleGradientChange(float value); + +private: + int m_width = 100; + int m_height = 100; + int m_srcWidth = 0; + int m_srcHeight = 0; + QPoint m_position = {}; + TopographicSeries *m_topographicSeries = nullptr; + float m_minHeight = 0.f; +}; + +#endif // HIGHLIGHTSERIES_H diff --git a/examples/datavisualization/graphgallery/main.cpp b/examples/datavisualization/graphgallery/main.cpp new file mode 100644 index 00000000..4bffa1c1 --- /dev/null +++ b/examples/datavisualization/graphgallery/main.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "bargraph.h" +#include "scattergraph.h" +#include "surfacegraph.h" + +#include <QtWidgets/qapplication.h> +#include <QtWidgets/qmessagebox.h> +#include <QtWidgets/qwidget.h> +#include <QtWidgets/qtabwidget.h> +#include <QtGui/qscreen.h> + +using namespace Qt::StringLiterals; + +int main(int argc, char **argv) +{ + qputenv("QSG_RHI_BACKEND", "opengl"); + + QApplication app(argc, argv); + + // Create a tab widget for creating own tabs for Q3DBars, Q3DScatter, and Q3DSurface + QTabWidget tabWidget; + tabWidget.setWindowTitle(u"Graph Gallery"_s); + + const QSize screenSize = tabWidget.screen()->size(); + const QSize minimumGraphSize{screenSize.width() / 2, qRound(screenSize.height() / 1.75)}; + + // Create bar graph + BarGraph bars; + // Create scatter graph + ScatterGraph scatter; + // Create surface graph + SurfaceGraph surface; + + if (!bars.initialize(minimumGraphSize, screenSize) + || !scatter.initialize(minimumGraphSize, screenSize) + || !surface.initialize(minimumGraphSize, screenSize)) { + QMessageBox::warning(nullptr, u"Graph Gallery"_s, u"Couldn't initialize the OpenGL context."_s); + return -1; + } + + // Add bars widget + tabWidget.addTab(bars.barsWidget(), u"Bar Graph"_s); + // Add scatter widget + tabWidget.addTab(scatter.scatterWidget(), u"Scatter Graph"_s); + // Add surface widget + tabWidget.addTab(surface.surfaceWidget(), u"Surface Graph"_s); + + tabWidget.show(); + return app.exec(); +} diff --git a/examples/datavisualization/graphgallery/rainfalldata.cpp b/examples/datavisualization/graphgallery/rainfalldata.cpp new file mode 100644 index 00000000..66a2553a --- /dev/null +++ b/examples/datavisualization/graphgallery/rainfalldata.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "rainfalldata.h" +#include <QtDataVisualization/q3dscene.h> +#include <QtDataVisualization/q3dcamera.h> +#include <QtDataVisualization/qbar3dseries.h> +#include <QtDataVisualization/q3dtheme.h> +#include <QtCore/qtextstream.h> +#include <QtCore/qfile.h> + +using namespace Qt::StringLiterals; + +RainfallData::RainfallData() +{ + // In data file the months are in numeric format, so create custom list + for (int i = 1; i <= 12; ++i) + m_numericMonths << QString::number(i); + + m_columnCount = m_numericMonths.size(); + + updateYearsList(2010, 2022); + + // Create proxy and series + //! [0] + m_proxy = new VariantBarDataProxy; + m_series = new QBar3DSeries(m_proxy); + //! [0] + + m_series->setItemLabelFormat(u"%.1f mm"_s); + + // Create the axes + m_rowAxis = new QCategory3DAxis(this); + m_colAxis = new QCategory3DAxis(this); + m_valueAxis = new QValue3DAxis(this); + m_rowAxis->setAutoAdjustRange(true); + m_colAxis->setAutoAdjustRange(true); + m_valueAxis->setAutoAdjustRange(true); + + // Set axis labels and titles + QStringList months{"January", "February", "March", "April", + "May", "June", "July", "August", "September", "October", + "November","December"}; + m_rowAxis->setTitle("Year"); + m_colAxis->setTitle("Month"); + m_valueAxis->setTitle("rainfall (mm)"); + m_valueAxis->setSegmentCount(5); + m_rowAxis->setLabels(m_years); + m_colAxis->setLabels(months); + m_rowAxis->setTitleVisible(true); + m_colAxis->setTitleVisible(true); + m_valueAxis->setTitleVisible(true); + + addDataSet(); +} + +RainfallData::~RainfallData() +{ + delete m_mapping; + delete m_dataSet; + delete m_proxy; +} + +void RainfallData::updateYearsList(int start, int end) +{ + m_years.clear(); + + for (int i = start; i <= end; ++i) + m_years << QString::number(i); + + m_rowCount = m_years.size(); +} + +//! [1] +void RainfallData::addDataSet() +{ + // Create a new variant data set and data item list + m_dataSet = new VariantDataSet; + auto *itemList = new VariantDataItemList; + + // Read data from a data file into the data item list + QFile dataFile(":/data/raindata.txt"); + if (dataFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream stream(&dataFile); + while (!stream.atEnd()) { + QString line = stream.readLine(); + if (line.startsWith('#')) // Ignore comments + continue; + const auto strList = QStringView{line}.split(',', Qt::SkipEmptyParts); + // Each line has three data items: Year, month, and rainfall value + if (strList.size() < 3) { + qWarning() << "Invalid row read from data:" << line; + continue; + } + // Store year and month as strings, and rainfall value as double + // into a variant data item and add the item to the item list. + auto *newItem = new VariantDataItem; + for (int i = 0; i < 2; ++i) + newItem->append(strList.at(i).trimmed().toString()); + newItem->append(strList.at(2).trimmed().toDouble()); + itemList->append(newItem); + } + } else { + qWarning() << "Unable to open data file:" << dataFile.fileName(); + } + //! [1] + + //! [2] + // Add items to the data set and set it to the proxy + m_dataSet->addItems(itemList); + m_proxy->setDataSet(m_dataSet); + + // Create new mapping for the data and set it to the proxy + m_mapping = new VariantBarDataMapping(0, 1, 2, m_years, m_numericMonths); + m_proxy->setMapping(m_mapping); + //! [2] +} diff --git a/examples/datavisualization/graphgallery/rainfalldata.h b/examples/datavisualization/graphgallery/rainfalldata.h new file mode 100644 index 00000000..c71f072b --- /dev/null +++ b/examples/datavisualization/graphgallery/rainfalldata.h @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef RAINFALLDATA_H +#define RAINFALLDATA_H + +#include "variantbardataproxy.h" +#include <QtDataVisualization/qcategory3daxis.h> +#include <QtDataVisualization/qvalue3daxis.h> + +class RainfallData : public QObject +{ + Q_OBJECT +public: + explicit RainfallData(); + ~RainfallData(); + + void addDataSet(); + + //! [0] + QBar3DSeries *customSeries() { return m_series; } + //! [0] + + QValue3DAxis *valueAxis() { return m_valueAxis; } + QCategory3DAxis *rowAxis() { return m_rowAxis; } + QCategory3DAxis *colAxis() { return m_colAxis; } + +private: + void updateYearsList(int start, int end); + int m_columnCount; + int m_rowCount; + QStringList m_years; + QStringList m_numericMonths; + VariantBarDataProxy *m_proxy; + VariantBarDataMapping *m_mapping; + VariantDataSet *m_dataSet; + QBar3DSeries *m_series; + QValue3DAxis *m_valueAxis; + QCategory3DAxis *m_rowAxis; + QCategory3DAxis *m_colAxis; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/scatterdatamodifier.cpp b/examples/datavisualization/graphgallery/scatterdatamodifier.cpp new file mode 100644 index 00000000..1f3ec7ed --- /dev/null +++ b/examples/datavisualization/graphgallery/scatterdatamodifier.cpp @@ -0,0 +1,191 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "scatterdatamodifier.h" +#include <QtDataVisualization/qscatterdataproxy.h> +#include <QtDataVisualization/qvalue3daxis.h> +#include <QtDataVisualization/q3dscene.h> +#include <QtDataVisualization/q3dcamera.h> +#include <QtDataVisualization/qscatter3dseries.h> +#include <QtDataVisualization/q3dtheme.h> +#include <QtCore/qmath.h> +#include <QtCore/qrandom.h> +#include <QtWidgets/qcombobox.h> + +using namespace Qt::StringLiterals; + +//#define RANDOM_SCATTER // Uncomment this to switch to random scatter + +const int numberOfItems = 10000; +const float curveDivider = 7.5f; +const int lowerNumberOfItems = 900; +const float lowerCurveDivider = 0.75f; + +ScatterDataModifier::ScatterDataModifier(Q3DScatter *scatter, QObject *parent) : + QObject(parent), + m_graph(scatter), + m_itemCount(lowerNumberOfItems), + m_curveDivider(lowerCurveDivider), + //! [7] + m_inputHandler(new AxesInputHandler(scatter)) + //! [7] +{ + //! [0] + m_graph->activeTheme()->setType(Q3DTheme::ThemeStoneMoss); + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftHigh); + m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront); + m_graph->scene()->activeCamera()->setZoomLevel(80.f); + //! [0] + + //! [1] + auto *proxy = new QScatterDataProxy; + auto *series = new QScatter3DSeries(proxy); + series->setItemLabelFormat(u"@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel"_s); + series->setMeshSmooth(m_smooth); + m_graph->addSeries(series); + //! [1] + + //! [8] + // Give ownership of the handler to the graph and make it the active handler + m_graph->setActiveInputHandler(m_inputHandler); + //! [8] + + //! [9] + // Give our axes to the input handler + m_inputHandler->setAxes(m_graph->axisX(), m_graph->axisZ(), m_graph->axisY()); + //! [9] + + //! [2] + addData(); + //! [2] +} + +void ScatterDataModifier::addData() +{ + // Configure the axes according to the data + //! [3] + m_graph->axisX()->setTitle("X"); + m_graph->axisY()->setTitle("Y"); + m_graph->axisZ()->setTitle("Z"); + //! [3] + + //! [4] + auto *dataArray = new QScatterDataArray; + dataArray->reserve(m_itemCount); + //! [4] + +#ifdef RANDOM_SCATTER + for (int i = 0; i < m_itemCount; ++i) + dataArray->append(QScatterDataItem(randVector())); +#else + //! [5] + const float limit = qSqrt(m_itemCount) / 2.0f; + for (int i = -limit; i < limit; ++i) { + for (int j = -limit; j < limit; ++j) { + const float x = float(i) + 0.5f; + const float y = qCos(qDegreesToRadians(float(i * j) / m_curveDivider)); + const float z = float(j) + 0.5f; + dataArray->append(QScatterDataItem({x, y, z})); + } + } + //! [5] +#endif + + //! [6] + m_graph->seriesList().at(0)->dataProxy()->resetArray(dataArray); + //! [6] +} + +void ScatterDataModifier::changeStyle(int style) +{ + QComboBox *comboBox = qobject_cast<QComboBox *>(sender()); + if (comboBox) { + m_style = comboBox->itemData(style).value<QAbstract3DSeries::Mesh>(); + if (!m_graph->seriesList().isEmpty()) + m_graph->seriesList().at(0)->setMesh(m_style); + } +} + +void ScatterDataModifier::setSmoothDots(int smooth) +{ + m_smooth = bool(smooth); + QScatter3DSeries *series = m_graph->seriesList().at(0); + series->setMeshSmooth(m_smooth); +} + +void ScatterDataModifier::changeTheme(int theme) +{ + Q3DTheme *currentTheme = m_graph->activeTheme(); + currentTheme->setType(Q3DTheme::Theme(theme)); + emit backgroundEnabledChanged(currentTheme->isBackgroundEnabled()); + emit gridEnabledChanged(currentTheme->isGridEnabled()); +} + +void ScatterDataModifier::changePresetCamera() +{ + static int preset = Q3DCamera::CameraPresetFrontLow; + + m_graph->scene()->activeCamera()->setCameraPreset((Q3DCamera::CameraPreset)preset); + + if (++preset > Q3DCamera::CameraPresetDirectlyBelow) + preset = Q3DCamera::CameraPresetFrontLow; +} + +void ScatterDataModifier::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality sq) +{ + int quality = int(sq); + emit shadowQualityChanged(quality); // connected to a checkbox in scattergraph.cpp +} + +void ScatterDataModifier::changeShadowQuality(int quality) +{ + QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality); + m_graph->setShadowQuality(sq); +} + +void ScatterDataModifier::setBackgroundEnabled(int enabled) +{ + m_graph->activeTheme()->setBackgroundEnabled(enabled == Qt::Checked); +} + +void ScatterDataModifier::setGridEnabled(int enabled) +{ + m_graph->activeTheme()->setGridEnabled(enabled == Qt::Checked); +} + +void ScatterDataModifier::toggleItemCount() +{ + if (m_itemCount == numberOfItems) { + m_itemCount = lowerNumberOfItems; + m_curveDivider = lowerCurveDivider; + } else { + m_itemCount = numberOfItems; + m_curveDivider = curveDivider; + } + m_graph->seriesList().at(0)->dataProxy()->resetArray(0); + addData(); +} + +void ScatterDataModifier::toggleRanges() +{ + if (!m_autoAdjust) { + m_graph->axisX()->setAutoAdjustRange(true); + m_graph->axisZ()->setAutoAdjustRange(true); + m_inputHandler->setDragSpeedModifier(1.5f); + m_autoAdjust = true; + } else { + m_graph->axisX()->setRange(-10.0f, 10.0f); + m_graph->axisZ()->setRange(-10.0f, 10.0f); + m_inputHandler->setDragSpeedModifier(15.0f); + m_autoAdjust = false; + } +} + +QVector3D ScatterDataModifier::randVector() +{ + auto *generator = QRandomGenerator::global(); + const auto x = float(generator->bounded(100)) / 2.0f - float(generator->bounded(100)) / 2.0f; + const auto y = float(generator->bounded(100)) / 100.0f - float(generator->bounded(100)) / 100.0f; + const auto z = float(generator->bounded(100)) / 2.0f - float(generator->bounded(100)) / 2.0f; + return {x, y, z}; +} diff --git a/examples/datavisualization/graphgallery/scatterdatamodifier.h b/examples/datavisualization/graphgallery/scatterdatamodifier.h new file mode 100644 index 00000000..53f557e6 --- /dev/null +++ b/examples/datavisualization/graphgallery/scatterdatamodifier.h @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SCATTERDATAMODIFIER_H +#define SCATTERDATAMODIFIER_H + +#include "axesinputhandler.h" + +#include <QtDataVisualization/q3dscatter.h> +#include <QtDataVisualization/qabstract3dseries.h> + +class ScatterDataModifier : public QObject +{ + Q_OBJECT +public: + explicit ScatterDataModifier(Q3DScatter *scatter, QObject *parent); + + void addData(); + +public Q_SLOTS: + void setBackgroundEnabled(int enabled); + void setGridEnabled(int enabled); + void setSmoothDots(int smooth); + void changePresetCamera(); + void toggleItemCount(); + void toggleRanges(); + void changeStyle(int style); + void changeTheme(int theme); + void changeShadowQuality(int quality); + void shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality shadowQuality); + +Q_SIGNALS: + void backgroundEnabledChanged(bool enabled); + void gridEnabledChanged(bool enabled); + void shadowQualityChanged(int quality); + +private: + QVector3D randVector(); + Q3DScatter *m_graph = nullptr; + QAbstract3DSeries::Mesh m_style = QAbstract3DSeries::MeshSphere; + bool m_smooth = true; + int m_itemCount; + float m_curveDivider; + + AxesInputHandler *m_inputHandler; + bool m_autoAdjust = true; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/scattergraph.cpp b/examples/datavisualization/graphgallery/scattergraph.cpp new file mode 100644 index 00000000..a426c10a --- /dev/null +++ b/examples/datavisualization/graphgallery/scattergraph.cpp @@ -0,0 +1,142 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "scattergraph.h" +#include "scatterdatamodifier.h" + +#include <QtWidgets/qboxlayout.h> +#include <QtWidgets/qcheckbox.h> +#include <QtWidgets/qcombobox.h> +#include <QtWidgets/qlabel.h> +#include <QtWidgets/qcommandlinkbutton.h> + +using namespace Qt::StringLiterals; + +ScatterGraph::ScatterGraph() +{ + m_scatterGraph = new Q3DScatter(); +} + +ScatterGraph::~ScatterGraph() = default; + +bool ScatterGraph::initialize(const QSize &minimumGraphSize, const QSize &maximumGraphSize) +{ + if (!m_scatterGraph->hasContext()) + return false; + + m_scatterWidget = new QWidget; + auto *hLayout = new QHBoxLayout(m_scatterWidget); + m_container = QWidget::createWindowContainer(m_scatterGraph, m_scatterWidget); + m_container->setMinimumSize(minimumGraphSize); + m_container->setMaximumSize(maximumGraphSize); + m_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_container->setFocusPolicy(Qt::StrongFocus); + hLayout->addWidget(m_container, 1); + + auto *vLayout = new QVBoxLayout(); + hLayout->addLayout(vLayout); + + auto *cameraButton = new QCommandLinkButton(m_scatterWidget); + cameraButton->setText(u"Change camera preset"_s); + cameraButton->setDescription(u"Switch between a number of preset camera positions"_s); + cameraButton->setIconSize(QSize(0, 0)); + + auto *itemCountButton = new QCommandLinkButton(m_scatterWidget); + itemCountButton->setText(u"Toggle item count"_s); + itemCountButton->setDescription(u"Switch between 900 and 10000 data points"_s); + itemCountButton->setIconSize(QSize(0, 0)); + + auto *rangeButton = new QCommandLinkButton(m_scatterWidget); + rangeButton->setText(u"Toggle axis ranges"_s); + rangeButton->setDescription(u"Switch between automatic axis ranges and preset ranges"_s); + rangeButton->setIconSize(QSize(0, 0)); + + auto *backgroundCheckBox = new QCheckBox(m_scatterWidget); + backgroundCheckBox->setText(u"Show background"_s); + backgroundCheckBox->setChecked(true); + + auto *gridCheckBox = new QCheckBox(m_scatterWidget); + gridCheckBox->setText(u"Show grid"_s); + gridCheckBox->setChecked(true); + + auto *smoothCheckBox = new QCheckBox(m_scatterWidget); + smoothCheckBox->setText(u"Smooth dots"_s); + smoothCheckBox->setChecked(true); + + auto *itemStyleList = new QComboBox(m_scatterWidget); + itemStyleList->addItem(u"Sphere"_s, QAbstract3DSeries::MeshSphere); + itemStyleList->addItem(u"Cube"_s, QAbstract3DSeries::MeshCube); + itemStyleList->addItem(u"Minimal"_s, QAbstract3DSeries::MeshMinimal); + itemStyleList->addItem(u"Point"_s, QAbstract3DSeries::MeshPoint); + itemStyleList->setCurrentIndex(0); + + auto *themeList = new QComboBox(m_scatterWidget); + themeList->addItem(u"Qt"_s); + themeList->addItem(u"Primary Colors"_s); + themeList->addItem(u"Digia"_s); + themeList->addItem(u"Stone Moss"_s); + themeList->addItem(u"Army Blue"_s); + themeList->addItem(u"Retro"_s); + themeList->addItem(u"Ebony"_s); + themeList->addItem(u"Isabelle"_s); + themeList->setCurrentIndex(3); + + auto *shadowQuality = new QComboBox(m_scatterWidget); + shadowQuality->addItem(u"None"_s); + shadowQuality->addItem(u"Low"_s); + shadowQuality->addItem(u"Medium"_s); + shadowQuality->addItem(u"High"_s); + shadowQuality->addItem(u"Low Soft"_s); + shadowQuality->addItem(u"Medium Soft"_s); + shadowQuality->addItem(u"High Soft"_s); + shadowQuality->setCurrentIndex(6); + + vLayout->addWidget(cameraButton); + vLayout->addWidget(itemCountButton); + vLayout->addWidget(rangeButton); + vLayout->addWidget(backgroundCheckBox); + vLayout->addWidget(gridCheckBox); + vLayout->addWidget(smoothCheckBox); + vLayout->addWidget(new QLabel(u"Change dot style"_s)); + vLayout->addWidget(itemStyleList); + vLayout->addWidget(new QLabel(u"Change theme"_s)); + vLayout->addWidget(themeList); + vLayout->addWidget(new QLabel(u"Adjust shadow quality"_s)); + vLayout->addWidget(shadowQuality, 1, Qt::AlignTop); + + auto *modifier = new ScatterDataModifier(m_scatterGraph, this); + + QObject::connect(cameraButton, &QCommandLinkButton::clicked, modifier, + &ScatterDataModifier::changePresetCamera); + QObject::connect(itemCountButton, &QCommandLinkButton::clicked, modifier, + &ScatterDataModifier::toggleItemCount); + QObject::connect(rangeButton, &QCommandLinkButton::clicked, modifier, + &ScatterDataModifier::toggleRanges); + + QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier, + &ScatterDataModifier::setBackgroundEnabled); + QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier, + &ScatterDataModifier::setGridEnabled); + QObject::connect(smoothCheckBox, &QCheckBox::stateChanged, modifier, + &ScatterDataModifier::setSmoothDots); + + QObject::connect(modifier, &ScatterDataModifier::backgroundEnabledChanged, + backgroundCheckBox, &QCheckBox::setChecked); + QObject::connect(modifier, &ScatterDataModifier::gridEnabledChanged, + gridCheckBox, &QCheckBox::setChecked); + QObject::connect(itemStyleList, &QComboBox::currentIndexChanged, modifier, + &ScatterDataModifier::changeStyle); + + QObject::connect(themeList, &QComboBox::currentIndexChanged, modifier, + &ScatterDataModifier::changeTheme); + + QObject::connect(shadowQuality, &QComboBox::currentIndexChanged, modifier, + &ScatterDataModifier::changeShadowQuality); + + QObject::connect(modifier, &ScatterDataModifier::shadowQualityChanged, shadowQuality, + &QComboBox::setCurrentIndex); + QObject::connect(m_scatterGraph, &Q3DScatter::shadowQualityChanged, modifier, + &ScatterDataModifier::shadowQualityUpdatedByVisual); + + return true; +} diff --git a/examples/datavisualization/graphgallery/scattergraph.h b/examples/datavisualization/graphgallery/scattergraph.h new file mode 100644 index 00000000..eb8c7c52 --- /dev/null +++ b/examples/datavisualization/graphgallery/scattergraph.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SCATTERGRAPH_H +#define SCATTERGRAPH_H + +#include <QtCore/qobject.h> +#include <QtDataVisualization/q3dscatter.h> + +class ScatterGraph : public QObject +{ + Q_OBJECT +public: + ScatterGraph(); + ~ScatterGraph(); + + bool initialize(const QSize &minimumGraphSize, const QSize &maximumGraphSize); + QWidget *scatterWidget() { return m_scatterWidget; } + +private: + Q3DScatter *m_scatterGraph = nullptr; + QWidget *m_container = nullptr; + QWidget *m_scatterWidget = nullptr; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/surfacegraph.cpp b/examples/datavisualization/graphgallery/surfacegraph.cpp new file mode 100644 index 00000000..88cbc72f --- /dev/null +++ b/examples/datavisualization/graphgallery/surfacegraph.cpp @@ -0,0 +1,346 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "surfacegraph.h" +#include "surfacegraphmodifier.h" + +#include <QtWidgets/qboxlayout.h> +#include <QtWidgets/qcheckbox.h> +#include <QtWidgets/qcombobox.h> +#include <QtWidgets/qradiobutton.h> +#include <QtWidgets/qgroupbox.h> +#include <QtWidgets/qlabel.h> +#include <QtWidgets/qcommandlinkbutton.h> +#include <QtGui/qpainter.h> + +using namespace Qt::StringLiterals; + +static QPixmap gradientBtoYPB_Pixmap() +{ + QLinearGradient grBtoY(0, 0, 1, 100); + grBtoY.setColorAt(1.f, Qt::black); + grBtoY.setColorAt(0.67f, Qt::blue); + grBtoY.setColorAt(0.33f, Qt::red); + grBtoY.setColorAt(0.f, Qt::yellow); + QPixmap pm(24, 100); + QPainter pmp(&pm); + pmp.setBrush(QBrush(grBtoY)); + pmp.setPen(Qt::NoPen); + pmp.drawRect(0, 0, 24, 100); + return pm; +} + +static QPixmap gradientGtoRPB_Pixmap() +{ + QLinearGradient grGtoR(0, 0, 1, 100); + grGtoR.setColorAt(1.f, Qt::darkGreen); + grGtoR.setColorAt(0.5f, Qt::yellow); + grGtoR.setColorAt(0.2f, Qt::red); + grGtoR.setColorAt(0.f, Qt::darkRed); + QPixmap pm(24, 100); + QPainter pmp(&pm); + pmp.setBrush(QBrush(grGtoR)); + pmp.drawRect(0, 0, 24, 100); + return pm; +} + +static QPixmap highlightPixmap() +{ + constexpr int height = 400; + constexpr int width = 110; + constexpr int border = 10; + QLinearGradient gr(0, 0, 1, height - 2 * border); + gr.setColorAt(1.f, Qt::black); + gr.setColorAt(0.8f, Qt::darkGreen); + gr.setColorAt(0.6f, Qt::green); + gr.setColorAt(0.4f, Qt::yellow); + gr.setColorAt(0.2f, Qt::red); + gr.setColorAt(0.f, Qt::darkRed); + + QPixmap pmHighlight(width, height); + pmHighlight.fill(Qt::transparent); + QPainter pmpHighlight(&pmHighlight); + pmpHighlight.setBrush(QBrush(gr)); + pmpHighlight.setPen(Qt::NoPen); + pmpHighlight.drawRect(border, border, 35, height - 2 * border); + pmpHighlight.setPen(Qt::black); + int step = (height - 2 * border) / 5; + for (int i = 0; i < 6; ++i) { + int yPos = i * step + border; + pmpHighlight.drawLine(border, yPos, 55, yPos); + const int height = 550 - (i * 110); + pmpHighlight.drawText(60, yPos + 2, QString::number(height) + u" m"_s); + } + return pmHighlight; +} + +SurfaceGraph::SurfaceGraph() +{ + m_surfaceGraph = new Q3DSurface(); +} + +SurfaceGraph::~SurfaceGraph() = default; + +bool SurfaceGraph::initialize(const QSize &minimumGraphSize, const QSize &maximumGraphSize) +{ + if (!m_surfaceGraph->hasContext()) + return false; + + m_surfaceWidget = new QWidget; + auto *hLayout = new QHBoxLayout(m_surfaceWidget); + m_container = QWidget::createWindowContainer(m_surfaceGraph, m_surfaceWidget); + m_container->setMinimumSize(minimumGraphSize); + m_container->setMaximumSize(maximumGraphSize); + m_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_container->setFocusPolicy(Qt::StrongFocus); + hLayout->addWidget(m_container, 1); + + auto *vLayout = new QVBoxLayout(); + hLayout->addLayout(vLayout); + vLayout->setAlignment(Qt::AlignTop); + + // Create control widgets + auto *modelGroupBox = new QGroupBox(u"Model"_s); + + auto *sqrtSinModelRB = new QRadioButton(m_surfaceWidget); + sqrtSinModelRB->setText(u"Sqrt && Sin"_s); + sqrtSinModelRB->setChecked(false); + + auto *heightMapModelRB = new QRadioButton(m_surfaceWidget); + heightMapModelRB->setText(u"Multiseries\nHeight Map"_s); + heightMapModelRB->setChecked(false); + + auto *texturedModelRB = new QRadioButton(m_surfaceWidget); + texturedModelRB->setText(u"Textured\nTopography"_s); + texturedModelRB->setChecked(false); + + auto *modelVBox = new QVBoxLayout; + modelVBox->addWidget(sqrtSinModelRB); + modelVBox->addWidget(heightMapModelRB); + modelVBox->addWidget(texturedModelRB); + modelGroupBox->setLayout(modelVBox); + + auto *selectionGroupBox = new QGroupBox(u"Graph Selection Mode"_s); + + auto *modeNoneRB = new QRadioButton(m_surfaceWidget); + modeNoneRB->setText(u"No selection"_s); + modeNoneRB->setChecked(false); + + auto *modeItemRB = new QRadioButton(m_surfaceWidget); + modeItemRB->setText(u"Item"_s); + modeItemRB->setChecked(false); + + auto *modeSliceRowRB = new QRadioButton(m_surfaceWidget); + modeSliceRowRB->setText(u"Row Slice"_s); + modeSliceRowRB->setChecked(false); + + auto *modeSliceColumnRB = new QRadioButton(m_surfaceWidget); + modeSliceColumnRB->setText(u"Column Slice"_s); + modeSliceColumnRB->setChecked(false); + + auto *selectionVBox = new QVBoxLayout; + selectionVBox->addWidget(modeNoneRB); + selectionVBox->addWidget(modeItemRB); + selectionVBox->addWidget(modeSliceRowRB); + selectionVBox->addWidget(modeSliceColumnRB); + selectionGroupBox->setLayout(selectionVBox); + + auto *axisGroupBox = new QGroupBox(u"Axis ranges"_s); + + auto *axisMinSliderX = new QSlider(Qt::Horizontal); + axisMinSliderX->setMinimum(0); + axisMinSliderX->setTickInterval(1); + axisMinSliderX->setEnabled(true); + auto *axisMaxSliderX = new QSlider(Qt::Horizontal); + axisMaxSliderX->setMinimum(1); + axisMaxSliderX->setTickInterval(1); + axisMaxSliderX->setEnabled(true); + auto *axisMinSliderZ = new QSlider(Qt::Horizontal); + axisMinSliderZ->setMinimum(0); + axisMinSliderZ->setTickInterval(1); + axisMinSliderZ->setEnabled(true); + auto *axisMaxSliderZ = new QSlider(Qt::Horizontal); + axisMaxSliderZ->setMinimum(1); + axisMaxSliderZ->setTickInterval(1); + axisMaxSliderZ->setEnabled(true); + + auto *axisVBox = new QVBoxLayout; + axisVBox->addWidget(new QLabel(u"Column range"_s)); + axisVBox->addWidget(axisMinSliderX); + axisVBox->addWidget(axisMaxSliderX); + axisVBox->addWidget(new QLabel(u"Row range"_s)); + axisVBox->addWidget(axisMinSliderZ); + axisVBox->addWidget(axisMaxSliderZ); + axisGroupBox->setLayout(axisVBox); + + // Mode-dependent controls + // sqrt-sin + auto *colorGroupBox = new QGroupBox(u"Custom gradient"_s); + + QPixmap pixmap = gradientBtoYPB_Pixmap(); + auto *gradientBtoYPB = new QPushButton(m_surfaceWidget); + gradientBtoYPB->setIcon(QIcon(pixmap)); + gradientBtoYPB->setIconSize(pixmap.size()); + + pixmap = gradientGtoRPB_Pixmap(); + auto *gradientGtoRPB = new QPushButton(m_surfaceWidget); + gradientGtoRPB->setIcon(QIcon(pixmap)); + gradientGtoRPB->setIconSize(pixmap.size()); + auto *colorHBox = new QHBoxLayout; + colorHBox->addWidget(gradientBtoYPB); + colorHBox->addWidget(gradientGtoRPB); + colorGroupBox->setLayout(colorHBox); + + // Multiseries heightmap + auto *showGroupBox = new QGroupBox(u"_show Object"_s); + showGroupBox->setVisible(false); + + auto *checkboxShowOilRigOne = new QCheckBox(u"Oil Rig 1"_s); + checkboxShowOilRigOne->setChecked(true); + + auto *checkboxShowOilRigTwo = new QCheckBox(u"Oil Rig 2"_s); + checkboxShowOilRigTwo->setChecked(true); + + auto *checkboxShowRefinery = new QCheckBox(u"Refinery"_s); + + auto *showVBox = new QVBoxLayout; + showVBox->addWidget(checkboxShowOilRigOne); + showVBox->addWidget(checkboxShowOilRigTwo); + showVBox->addWidget(checkboxShowRefinery); + showGroupBox->setLayout(showVBox); + + auto *visualsGroupBox = new QGroupBox(u"Visuals"_s); + visualsGroupBox->setVisible(false); + + auto *checkboxVisualsSeeThrough = new QCheckBox(u"See-Through"_s); + + auto *checkboxHighlightOil = new QCheckBox(u"Highlight Oil"_s); + + auto *checkboxShowShadows = new QCheckBox(u"Shadows"_s); + checkboxShowShadows->setChecked(true); + + auto *visualVBox = new QVBoxLayout; + visualVBox->addWidget(checkboxVisualsSeeThrough); + visualVBox->addWidget(checkboxHighlightOil); + visualVBox->addWidget(checkboxShowShadows); + visualsGroupBox->setLayout(visualVBox); + + auto *labelSelection = new QLabel(u"Selection:"_s); + labelSelection->setVisible(false); + + auto *labelSelectedItem = new QLabel(u"Nothing"_s); + labelSelectedItem->setVisible(false); + + // Textured topography heightmap + auto *enableTexture = new QCheckBox(u"Surface texture"_s); + enableTexture->setVisible(false); + + auto *label = new QLabel(m_surfaceWidget); + label->setPixmap(highlightPixmap()); + + auto *heightMapGroupBox = new QGroupBox(u"Highlight color map"_s); + auto *colorMapVBox = new QVBoxLayout; + colorMapVBox->addWidget(label); + heightMapGroupBox->setLayout(colorMapVBox); + heightMapGroupBox->setVisible(false); + + // Populate vertical layout + // Common + vLayout->addWidget(modelGroupBox); + vLayout->addWidget(selectionGroupBox); + vLayout->addWidget(axisGroupBox); + + // Sqrt Sin + vLayout->addWidget(colorGroupBox); + + // Multiseries heightmap + vLayout->addWidget(showGroupBox); + vLayout->addWidget(visualsGroupBox); + vLayout->addWidget(labelSelection); + vLayout->addWidget(labelSelectedItem); + + // Textured topography + vLayout->addWidget(heightMapGroupBox); + vLayout->addWidget(enableTexture); + + // Create the controller + auto *modifier = new SurfaceGraphModifier(m_surfaceGraph, labelSelectedItem, this); + + // Connect widget controls to controller + QObject::connect(heightMapModelRB, &QRadioButton::toggled, + modifier, &SurfaceGraphModifier::enableHeightMapModel); + QObject::connect(sqrtSinModelRB, &QRadioButton::toggled, + modifier, &SurfaceGraphModifier::enableSqrtSinModel); + QObject::connect(texturedModelRB, &QRadioButton::toggled, + modifier, &SurfaceGraphModifier::enableTopographyModel); + + QObject::connect(modeNoneRB, &QRadioButton::toggled, + modifier, &SurfaceGraphModifier::toggleModeNone); + QObject::connect(modeItemRB, &QRadioButton::toggled, + modifier, &SurfaceGraphModifier::toggleModeItem); + QObject::connect(modeSliceRowRB, &QRadioButton::toggled, + modifier, &SurfaceGraphModifier::toggleModeSliceRow); + QObject::connect(modeSliceColumnRB, &QRadioButton::toggled, + modifier, &SurfaceGraphModifier::toggleModeSliceColumn); + + QObject::connect(axisMinSliderX, &QSlider::valueChanged, + modifier, &SurfaceGraphModifier::adjustXMin); + QObject::connect(axisMaxSliderX, &QSlider::valueChanged, + modifier, &SurfaceGraphModifier::adjustXMax); + QObject::connect(axisMinSliderZ, &QSlider::valueChanged, + modifier, &SurfaceGraphModifier::adjustZMin); + QObject::connect(axisMaxSliderZ, &QSlider::valueChanged, + modifier, &SurfaceGraphModifier::adjustZMax); + + // Mode dependent connections + QObject::connect(gradientBtoYPB, &QPushButton::pressed, + modifier, &SurfaceGraphModifier::setBlackToYellowGradient); + QObject::connect(gradientGtoRPB, &QPushButton::pressed, + modifier, &SurfaceGraphModifier::setGreenToRedGradient); + + QObject::connect(checkboxShowOilRigOne, &QCheckBox::stateChanged, + modifier, &SurfaceGraphModifier::toggleItemOne); + QObject::connect(checkboxShowOilRigTwo, &QCheckBox::stateChanged, + modifier, &SurfaceGraphModifier::toggleItemTwo); + QObject::connect(checkboxShowRefinery, &QCheckBox::stateChanged, + modifier, &SurfaceGraphModifier::toggleItemThree); + + QObject::connect(checkboxVisualsSeeThrough, &QCheckBox::stateChanged, + modifier, &SurfaceGraphModifier::toggleSeeThrough); + QObject::connect(checkboxHighlightOil, &QCheckBox::stateChanged, + modifier, &SurfaceGraphModifier::toggleOilHighlight); + QObject::connect(checkboxShowShadows, &QCheckBox::stateChanged, + modifier, &SurfaceGraphModifier::toggleShadows); + + QObject::connect(enableTexture, &QCheckBox::stateChanged, + modifier, &SurfaceGraphModifier::toggleSurfaceTexture); + + // Connections to disable features depending on mode + QObject::connect(sqrtSinModelRB, &QRadioButton::toggled, + colorGroupBox, &QGroupBox::setVisible); + + QObject::connect(heightMapModelRB, &QRadioButton::toggled, + showGroupBox, &QGroupBox::setVisible); + QObject::connect(heightMapModelRB, &QRadioButton::toggled, + visualsGroupBox, &QGroupBox::setVisible); + QObject::connect(heightMapModelRB, &QRadioButton::toggled, + labelSelection, &QLabel::setVisible); + QObject::connect(heightMapModelRB, &QRadioButton::toggled, + labelSelectedItem, &QLabel::setVisible); + + QObject::connect(texturedModelRB, &QRadioButton::toggled, + enableTexture, &QLabel::setVisible); + QObject::connect(texturedModelRB, &QRadioButton::toggled, + heightMapGroupBox, &QGroupBox::setVisible); + + modifier->setAxisMinSliderX(axisMinSliderX); + modifier->setAxisMaxSliderX(axisMaxSliderX); + modifier->setAxisMinSliderZ(axisMinSliderZ); + modifier->setAxisMaxSliderZ(axisMaxSliderZ); + + sqrtSinModelRB->setChecked(true); + modeItemRB->setChecked(true); + enableTexture->setChecked(true); + + return true; +} diff --git a/examples/datavisualization/graphgallery/surfacegraph.h b/examples/datavisualization/graphgallery/surfacegraph.h new file mode 100644 index 00000000..5ec29930 --- /dev/null +++ b/examples/datavisualization/graphgallery/surfacegraph.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SURFACEGRAPH_H +#define SURFACEGRAPH_H + +#include <QtCore/qobject.h> +#include <QtDataVisualization/q3dsurface.h> + +class SurfaceGraph : public QObject +{ + Q_OBJECT +public: + SurfaceGraph(); + ~SurfaceGraph(); + + bool initialize(const QSize &minimumGraphSize, const QSize &maximumGraphSize); + QWidget *surfaceWidget() { return m_surfaceWidget; } + +private: + Q3DSurface *m_surfaceGraph = nullptr; + QWidget *m_container = nullptr; + QWidget *m_surfaceWidget = nullptr; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/surfacegraphmodifier.cpp b/examples/datavisualization/graphgallery/surfacegraphmodifier.cpp new file mode 100644 index 00000000..190a96b9 --- /dev/null +++ b/examples/datavisualization/graphgallery/surfacegraphmodifier.cpp @@ -0,0 +1,647 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "surfacegraphmodifier.h" +#include "highlightseries.h" +#include "topographicseries.h" +#include "custominputhandler.h" + +#include <QtDataVisualization/qvalue3daxis.h> +#include <QtDataVisualization/q3dtheme.h> +#include <QtGui/qimage.h> +#include <QtCore/qmath.h> + +using namespace Qt::StringLiterals; + +const int sampleCountX = 150; +const int sampleCountZ = 150; +const int heightMapGridStepX = 6; +const int heightMapGridStepZ = 6; +const float sampleMin = -8.f; +const float sampleMax = 8.f; + +const float areaWidth = 8000.f; +const float areaHeight = 8000.f; +const float aspectRatio = 0.1389f; +const float minRange = areaWidth * 0.49f; + +SurfaceGraphModifier::SurfaceGraphModifier(Q3DSurface *surface, QLabel *label, QObject *parent) : + QObject(parent), + m_graph(surface), + m_textField(label) +{ + m_graph->scene()->activeCamera()->setZoomLevel(85.f); + m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRight); + m_graph->activeTheme()->setType(Q3DTheme::ThemeRetro); + + m_graph->setAxisX(new QValue3DAxis); + m_graph->setAxisY(new QValue3DAxis); + m_graph->setAxisZ(new QValue3DAxis); + + // + // Sqrt Sin + // + //! [0] + m_sqrtSinProxy = new QSurfaceDataProxy(); + m_sqrtSinSeries = new QSurface3DSeries(m_sqrtSinProxy); + //! [0] + fillSqrtSinProxy(); + + // + // Multisurface heightmap + // + //! [2] + // Create the first surface layer + QImage heightMapImageOne(":/data/layer_1.png"); + m_heightMapProxyOne = new QHeightMapSurfaceDataProxy(heightMapImageOne); + m_heightMapSeriesOne = new QSurface3DSeries(m_heightMapProxyOne); + m_heightMapSeriesOne->setItemLabelFormat(u"(@xLabel, @zLabel): @yLabel"_s); + m_heightMapProxyOne->setValueRanges(34.f, 40.f, 18.f, 24.f); + //! [2] + + // Create the other 2 surface layers + QImage heightMapImageTwo(":/data/layer_2.png"); + m_heightMapProxyTwo = new QHeightMapSurfaceDataProxy(heightMapImageTwo); + m_heightMapSeriesTwo = new QSurface3DSeries(m_heightMapProxyTwo); + m_heightMapSeriesTwo->setItemLabelFormat(u"(@xLabel, @zLabel): @yLabel"_s); + m_heightMapProxyTwo->setValueRanges(34.f, 40.f, 18.f, 24.f); + + QImage heightMapImageThree(":/data/layer_3.png"); + m_heightMapProxyThree = new QHeightMapSurfaceDataProxy(heightMapImageThree); + m_heightMapSeriesThree = new QSurface3DSeries(m_heightMapProxyThree); + m_heightMapSeriesThree->setItemLabelFormat(u"(@xLabel, @zLabel): @yLabel"_s); + m_heightMapProxyThree->setValueRanges(34.f, 40.f, 18.f, 24.f); + + // The images are the same size, so it's enough to get the dimensions from one + m_heightMapWidth = heightMapImageOne.width(); + m_heightMapHeight = heightMapImageOne.height(); + + // Set the gradients for multi-surface layers + QLinearGradient grOne; + grOne.setColorAt(0.f, Qt::black); + grOne.setColorAt(0.38f, Qt::darkYellow); + grOne.setColorAt(0.39f, Qt::darkGreen); + grOne.setColorAt(0.5f, Qt::darkGray); + grOne.setColorAt(1.f, Qt::gray); + m_heightMapSeriesOne->setBaseGradient(grOne); + m_heightMapSeriesOne->setColorStyle(Q3DTheme::ColorStyleRangeGradient); + + QLinearGradient grTwo; + grTwo.setColorAt(0.39f, Qt::blue); + grTwo.setColorAt(0.4f, Qt::white); + m_heightMapSeriesTwo->setBaseGradient(grTwo); + m_heightMapSeriesTwo->setColorStyle(Q3DTheme::ColorStyleRangeGradient); + + QLinearGradient grThree; + grThree.setColorAt(0.f, Qt::white); + grThree.setColorAt(0.05f, Qt::black); + m_heightMapSeriesThree->setBaseGradient(grThree); + m_heightMapSeriesThree->setColorStyle(Q3DTheme::ColorStyleRangeGradient); + + // Custom items and label + connect(m_graph, &QAbstract3DGraph::selectedElementChanged, + this, &SurfaceGraphModifier::handleElementSelected); + + m_selectionAnimation = new QPropertyAnimation(this); + m_selectionAnimation->setPropertyName("scaling"); + m_selectionAnimation->setDuration(500); + m_selectionAnimation->setLoopCount(-1); + + QFont titleFont = QFont("Century Gothic", 30); + titleFont.setBold(true); + m_titleLabel = new QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont, + QVector3D(0.f, 1.2f, 0.f), + QVector3D(1.f, 1.f, 0.f), + QQuaternion()); + m_titleLabel->setPositionAbsolute(true); + m_titleLabel->setFacingCamera(true); + m_titleLabel->setBackgroundColor(QColor(0x66cdaa)); + m_graph->addCustomItem(m_titleLabel); + m_titleLabel->setVisible(false); + + // Make two of the custom object visible + toggleItemOne(true); + toggleItemTwo(true); + + // + // Topographic map + // + m_topography = new TopographicSeries(); + m_topography->setTopographyFile(":/data/topography.png", areaWidth, areaHeight); + m_topography->setItemLabelFormat(u"@yLabel m"_s); + + m_highlight = new HighlightSeries(); + m_highlight->setTopographicSeries(m_topography); + m_highlight->setMinHeight(minRange * aspectRatio); + m_highlight->handleGradientChange(areaWidth * aspectRatio); + //! [16] + QObject::connect(m_graph->axisY(), &QValue3DAxis::maxChanged, + m_highlight, &HighlightSeries::handleGradientChange); + //! [16] + + m_customInputHandler = new CustomInputHandler(m_graph); + m_customInputHandler->setHighlightSeries(m_highlight); + m_customInputHandler->setAxes(m_graph->axisX(), m_graph->axisY(), m_graph->axisZ()); + m_customInputHandler->setLimits(0.f, areaWidth, minRange); + m_customInputHandler->setAspectRatio(aspectRatio); +} + +void SurfaceGraphModifier::fillSqrtSinProxy() +{ + float stepX = (sampleMax - sampleMin) / float(sampleCountX - 1); + float stepZ = (sampleMax - sampleMin) / float(sampleCountZ - 1); + + //! [1] + auto *dataArray = new QSurfaceDataArray; + dataArray->reserve(sampleCountZ); + for (int i = 0 ; i < sampleCountZ ; ++i) { + auto *newRow = new QSurfaceDataRow; + newRow->reserve(sampleCountX); + // Keep values within range bounds, since just adding step can cause minor drift due + // to the rounding errors. + float z = qMin(sampleMax, (i * stepZ + sampleMin)); + for (int j = 0; j < sampleCountX; ++j) { + float x = qMin(sampleMax, (j * stepX + sampleMin)); + float R = qSqrt(z * z + x * x) + 0.01f; + float y = (qSin(R) / R + 0.24f) * 1.61f; + newRow->append(QSurfaceDataItem({x, y, z})); + } + dataArray->append(newRow); + } + + m_sqrtSinProxy->resetArray(dataArray); + //! [1] +} + +void SurfaceGraphModifier::enableSqrtSinModel(bool enable) +{ + if (enable) { + //! [3] + m_sqrtSinSeries->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe); + m_sqrtSinSeries->setFlatShadingEnabled(true); + + m_graph->axisX()->setLabelFormat("%.2f"); + m_graph->axisZ()->setLabelFormat("%.2f"); + m_graph->axisX()->setRange(sampleMin, sampleMax); + m_graph->axisY()->setRange(0.f, 2.f); + m_graph->axisZ()->setRange(sampleMin, sampleMax); + m_graph->axisX()->setLabelAutoRotation(30.f); + m_graph->axisY()->setLabelAutoRotation(90.f); + m_graph->axisZ()->setLabelAutoRotation(30.f); + + m_graph->removeSeries(m_heightMapSeriesOne); + m_graph->removeSeries(m_heightMapSeriesTwo); + m_graph->removeSeries(m_heightMapSeriesThree); + m_graph->removeSeries(m_topography); + m_graph->removeSeries(m_highlight); + + m_graph->addSeries(m_sqrtSinSeries); + //! [3] + + m_titleLabel->setVisible(false); + m_graph->axisX()->setTitleVisible(false); + m_graph->axisY()->setTitleVisible(false); + m_graph->axisZ()->setTitleVisible(false); + + m_graph->axisX()->setTitle({}); + m_graph->axisY()->setTitle({}); + m_graph->axisZ()->setTitle({}); + + m_graph->setActiveInputHandler(m_defaultInputHandler); + + //! [6] + // Reset range sliders for Sqrt & Sin + m_rangeMinX = sampleMin; + m_rangeMinZ = sampleMin; + m_stepX = (sampleMax - sampleMin) / float(sampleCountX - 1); + m_stepZ = (sampleMax - sampleMin) / float(sampleCountZ - 1); + m_axisMinSliderX->setMinimum(0); + m_axisMinSliderX->setMaximum(sampleCountX - 2); + m_axisMinSliderX->setValue(0); + m_axisMaxSliderX->setMinimum(1); + m_axisMaxSliderX->setMaximum(sampleCountX - 1); + m_axisMaxSliderX->setValue(sampleCountX - 1); + m_axisMinSliderZ->setMinimum(0); + m_axisMinSliderZ->setMaximum(sampleCountZ - 2); + m_axisMinSliderZ->setValue(0); + m_axisMaxSliderZ->setMinimum(1); + m_axisMaxSliderZ->setMaximum(sampleCountZ - 1); + m_axisMaxSliderZ->setValue(sampleCountZ - 1); + //! [6] + } +} + +void SurfaceGraphModifier::enableHeightMapModel(bool enable) +{ + if (enable) { + m_heightMapSeriesOne->setDrawMode(QSurface3DSeries::DrawSurface); + m_heightMapSeriesOne->setFlatShadingEnabled(false); + m_heightMapSeriesTwo->setDrawMode(QSurface3DSeries::DrawSurface); + m_heightMapSeriesTwo->setFlatShadingEnabled(false); + m_heightMapSeriesThree->setDrawMode(QSurface3DSeries::DrawSurface); + m_heightMapSeriesThree->setFlatShadingEnabled(false); + + m_graph->axisX()->setLabelFormat("%.1f N"); + m_graph->axisZ()->setLabelFormat("%.1f E"); + m_graph->axisX()->setRange(34.f, 40.f); + //! [4] + m_graph->axisY()->setAutoAdjustRange(true); + //! [4] + m_graph->axisZ()->setRange(18.f, 24.f); + + m_graph->axisX()->setTitle(u"Latitude"_s); + m_graph->axisY()->setTitle(u"Height"_s); + m_graph->axisZ()->setTitle(u"Longitude"_s); + + m_graph->removeSeries(m_sqrtSinSeries); + m_graph->removeSeries(m_topography); + m_graph->removeSeries(m_highlight); + m_graph->addSeries(m_heightMapSeriesOne); + m_graph->addSeries(m_heightMapSeriesTwo); + m_graph->addSeries(m_heightMapSeriesThree); + + m_graph->setActiveInputHandler(m_defaultInputHandler); + + m_titleLabel->setVisible(true); + m_graph->axisX()->setTitleVisible(true); + m_graph->axisY()->setTitleVisible(true); + m_graph->axisZ()->setTitleVisible(true); + + // Reset range sliders for height map + int mapGridCountX = m_heightMapWidth / heightMapGridStepX; + int mapGridCountZ = m_heightMapHeight / heightMapGridStepZ; + m_rangeMinX = 34.f; + m_rangeMinZ = 18.f; + m_stepX = 6.f / float(mapGridCountX - 1); + m_stepZ = 6.f / float(mapGridCountZ - 1); + m_axisMinSliderX->setMinimum(0); + m_axisMinSliderX->setMaximum(mapGridCountX - 2); + m_axisMinSliderX->setValue(0); + m_axisMaxSliderX->setMinimum(1); + m_axisMaxSliderX->setMaximum(mapGridCountX - 1); + m_axisMaxSliderX->setValue(mapGridCountX - 1); + m_axisMinSliderZ->setMinimum(0); + m_axisMinSliderZ->setMaximum(mapGridCountZ - 2); + m_axisMinSliderZ->setValue(0); + m_axisMaxSliderZ->setMinimum(1); + m_axisMaxSliderZ->setMaximum(mapGridCountZ - 1); + m_axisMaxSliderZ->setValue(mapGridCountZ - 1); + } +} + +void SurfaceGraphModifier::enableTopographyModel(bool enable) +{ + if (enable) { + m_graph->axisX()->setLabelFormat("%i"); + m_graph->axisZ()->setLabelFormat("%i"); + m_graph->axisX()->setRange(0.f, areaWidth); + m_graph->axisY()->setRange(100.f, areaWidth * aspectRatio); + m_graph->axisZ()->setRange(0.f, areaHeight); + m_graph->axisX()->setLabelAutoRotation(30.f); + m_graph->axisY()->setLabelAutoRotation(90.f); + m_graph->axisZ()->setLabelAutoRotation(30.f); + + m_graph->removeSeries(m_heightMapSeriesOne); + m_graph->removeSeries(m_heightMapSeriesTwo); + m_graph->removeSeries(m_heightMapSeriesThree); + m_graph->addSeries(m_topography); + m_graph->addSeries(m_highlight); + + m_titleLabel->setVisible(false); + m_graph->axisX()->setTitleVisible(false); + m_graph->axisY()->setTitleVisible(false); + m_graph->axisZ()->setTitleVisible(false); + + m_graph->axisX()->setTitle({}); + m_graph->axisY()->setTitle({}); + m_graph->axisZ()->setTitle({}); + + //! [5] + m_graph->setActiveInputHandler(m_customInputHandler); + //! [5] + + // Reset range sliders for topography map + m_rangeMinX = 0.f; + m_rangeMinZ = 0.f; + m_stepX = 1.f; + m_stepZ = 1.f; + m_axisMinSliderX->setMinimum(0); + m_axisMinSliderX->setMaximum(areaWidth - 200); + m_axisMinSliderX->setValue(0); + m_axisMaxSliderX->setMinimum(200); + m_axisMaxSliderX->setMaximum(areaWidth); + m_axisMaxSliderX->setValue(areaWidth); + m_axisMinSliderZ->setMinimum(0); + m_axisMinSliderZ->setMaximum(areaHeight - 200); + m_axisMinSliderZ->setValue(0); + m_axisMaxSliderZ->setMinimum(200); + m_axisMaxSliderZ->setMaximum(areaHeight); + m_axisMaxSliderZ->setValue(areaHeight); + } +} + +void SurfaceGraphModifier::adjustXMin(int min) +{ + float minX = m_stepX * float(min) + m_rangeMinX; + + int max = m_axisMaxSliderX->value(); + if (min >= max) { + max = min + 1; + m_axisMaxSliderX->setValue(max); + } + float maxX = m_stepX * max + m_rangeMinX; + + setAxisXRange(minX, maxX); +} + +void SurfaceGraphModifier::adjustXMax(int max) +{ + float maxX = m_stepX * float(max) + m_rangeMinX; + + int min = m_axisMinSliderX->value(); + if (max <= min) { + min = max - 1; + m_axisMinSliderX->setValue(min); + } + float minX = m_stepX * min + m_rangeMinX; + + setAxisXRange(minX, maxX); +} + +void SurfaceGraphModifier::adjustZMin(int min) +{ + float minZ = m_stepZ * float(min) + m_rangeMinZ; + + int max = m_axisMaxSliderZ->value(); + if (min >= max) { + max = min + 1; + m_axisMaxSliderZ->setValue(max); + } + float maxZ = m_stepZ * max + m_rangeMinZ; + + setAxisZRange(minZ, maxZ); +} + +void SurfaceGraphModifier::adjustZMax(int max) +{ + float maxX = m_stepZ * float(max) + m_rangeMinZ; + + int min = m_axisMinSliderZ->value(); + if (max <= min) { + min = max - 1; + m_axisMinSliderZ->setValue(min); + } + float minX = m_stepZ * min + m_rangeMinZ; + + setAxisZRange(minX, maxX); +} + +//! [7] +void SurfaceGraphModifier::setAxisXRange(float min, float max) +{ + m_graph->axisX()->setRange(min, max); +} +//! [7] + +void SurfaceGraphModifier::setAxisZRange(float min, float max) +{ + m_graph->axisZ()->setRange(min, max); +} + +void SurfaceGraphModifier::setBlackToYellowGradient() +{ + //! [8] + QLinearGradient gr; + gr.setColorAt(0.f, Qt::black); + gr.setColorAt(0.33f, Qt::blue); + gr.setColorAt(0.67f, Qt::red); + gr.setColorAt(1.f, Qt::yellow); + + m_sqrtSinSeries->setBaseGradient(gr); + m_sqrtSinSeries->setColorStyle(Q3DTheme::ColorStyleRangeGradient); + //! [8] +} + +void SurfaceGraphModifier::setGreenToRedGradient() +{ + QLinearGradient gr; + gr.setColorAt(0.f, Qt::darkGreen); + gr.setColorAt(0.5f, Qt::yellow); + gr.setColorAt(0.8f, Qt::red); + gr.setColorAt(1.f, Qt::darkRed); + + m_sqrtSinSeries->setBaseGradient(gr); + m_sqrtSinSeries->setColorStyle(Q3DTheme::ColorStyleRangeGradient); +} + +void SurfaceGraphModifier::toggleItemOne(bool show) +{ + //! [10] + QVector3D positionOne = QVector3D(39.f, 77.f, 19.2f); + //! [10] + QVector3D positionOnePipe = QVector3D(39.f, 45.f, 19.2f); + QVector3D positionOneLabel = QVector3D(39.f, 107.f, 19.2f); + if (show) { + //! [9] + QImage color = QImage(2, 2, QImage::Format_RGB32); + color.fill(Qt::red); + //! [9] + //! [11] + auto *item = new QCustom3DItem(":/data/oilrig.obj", positionOne, + QVector3D(0.025f, 0.025f, 0.025f), + QQuaternion::fromAxisAndAngle(0.f, 1.f, 0.f, 45.f), + color); + //! [11] + //! [12] + m_graph->addCustomItem(item); + //! [12] + item = new QCustom3DItem(":/data/pipe.obj", positionOnePipe, + QVector3D(0.005f, 0.5f, 0.005f), + QQuaternion(), + color); + item->setShadowCasting(false); + m_graph->addCustomItem(item); + + //! [13] + auto *label = new QCustom3DLabel(); + label->setText("Oil Rig One"); + label->setPosition(positionOneLabel); + label->setScaling(QVector3D(1.f, 1.f, 1.f)); + m_graph->addCustomItem(label); + //! [13] + } else { + resetSelection(); + //! [14] + m_graph->removeCustomItemAt(positionOne); + //! [14] + m_graph->removeCustomItemAt(positionOnePipe); + m_graph->removeCustomItemAt(positionOneLabel); + } +} + +void SurfaceGraphModifier::toggleItemTwo(bool show) +{ + QVector3D positionTwo = QVector3D(34.5f, 77.f, 23.4f); + QVector3D positionTwoPipe = QVector3D(34.5f, 45.f, 23.4f); + QVector3D positionTwoLabel = QVector3D(34.5f, 107.f, 23.4f); + if (show) { + QImage color = QImage(2, 2, QImage::Format_RGB32); + color.fill(Qt::red); + auto *item = new QCustom3DItem(); + item->setMeshFile(":/data/oilrig.obj"); + item->setPosition(positionTwo); + item->setScaling(QVector3D(0.025f, 0.025f, 0.025f)); + item->setRotation(QQuaternion::fromAxisAndAngle(0.f, 1.f, 0.f, 25.f)); + item->setTextureImage(color); + m_graph->addCustomItem(item); + item = new QCustom3DItem(":/data/pipe.obj", positionTwoPipe, + QVector3D(0.005f, 0.5f, 0.005f), + QQuaternion(), + color); + item->setShadowCasting(false); + m_graph->addCustomItem(item); + + auto *label = new QCustom3DLabel(); + label->setText("Oil Rig Two"); + label->setPosition(positionTwoLabel); + label->setScaling(QVector3D(1.f, 1.f, 1.f)); + m_graph->addCustomItem(label); + } else { + resetSelection(); + m_graph->removeCustomItemAt(positionTwo); + m_graph->removeCustomItemAt(positionTwoPipe); + m_graph->removeCustomItemAt(positionTwoLabel); + } +} + +void SurfaceGraphModifier::toggleItemThree(bool show) +{ + QVector3D positionThree = QVector3D(34.5f, 86.f, 19.1f); + QVector3D positionThreeLabel = QVector3D(34.5f, 116.f, 19.1f); + if (show) { + QImage color = QImage(2, 2, QImage::Format_RGB32); + color.fill(Qt::darkMagenta); + auto *item = new QCustom3DItem(); + item->setMeshFile(":/data/refinery.obj"); + item->setPosition(positionThree); + item->setScaling(QVector3D(0.04f, 0.04f, 0.04f)); + item->setRotation(QQuaternion::fromAxisAndAngle(0.f, 1.f, 0.f, 75.f)); + item->setTextureImage(color); + m_graph->addCustomItem(item); + + auto *label = new QCustom3DLabel(); + label->setText("Refinery"); + label->setPosition(positionThreeLabel); + label->setScaling(QVector3D(1.f, 1.f, 1.f)); + m_graph->addCustomItem(label); + } else { + resetSelection(); + m_graph->removeCustomItemAt(positionThree); + m_graph->removeCustomItemAt(positionThreeLabel); + } +} + +void SurfaceGraphModifier::toggleSeeThrough(bool seethrough) +{ + if (seethrough) { + m_graph->seriesList().at(0)->setDrawMode(QSurface3DSeries::DrawWireframe); + m_graph->seriesList().at(1)->setDrawMode(QSurface3DSeries::DrawWireframe); + } else { + m_graph->seriesList().at(0)->setDrawMode(QSurface3DSeries::DrawSurface); + m_graph->seriesList().at(1)->setDrawMode(QSurface3DSeries::DrawSurface); + } +} + +void SurfaceGraphModifier::toggleOilHighlight(bool highlight) +{ + if (highlight) { + QLinearGradient grThree; + grThree.setColorAt(0.0, Qt::black); + grThree.setColorAt(0.05, Qt::red); + m_graph->seriesList().at(2)->setBaseGradient(grThree); + } else { + QLinearGradient grThree; + grThree.setColorAt(0.0, Qt::white); + grThree.setColorAt(0.05, Qt::black); + m_graph->seriesList().at(2)->setBaseGradient(grThree); + } +} + +void SurfaceGraphModifier::toggleShadows(bool shadows) +{ + if (shadows) + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityMedium); + else + m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); +} + +//! [15] +void SurfaceGraphModifier::toggleSurfaceTexture(bool enable) +{ + if (enable) + m_topography->setTextureFile(":/data/maptexture.jpg"); + else + m_topography->setTextureFile(""); +} +//! [15] + +void SurfaceGraphModifier::handleElementSelected(QAbstract3DGraph::ElementType type) +{ + resetSelection(); + if (type == QAbstract3DGraph::ElementCustomItem) { + QCustom3DItem *item = m_graph->selectedCustomItem(); + QString text; + if (qobject_cast<QCustom3DLabel *>(item) != 0) { + text.append("Custom label: "); + } else { + QStringList split = item->meshFile().split("/"); + text.append(split.last()); + text.append(": "); + } + int index = m_graph->selectedCustomItemIndex(); + text.append(QString::number(index)); + m_textField->setText(text); + m_previouslyAnimatedItem = item; + m_previousScaling = item->scaling(); + m_selectionAnimation->setTargetObject(item); + m_selectionAnimation->setStartValue(item->scaling()); + m_selectionAnimation->setEndValue(item->scaling() * 1.5f); + m_selectionAnimation->start(); + } else if (type == QAbstract3DGraph::ElementSeries) { + QString text = "Surface ("; + QSurface3DSeries *series = m_graph->selectedSeries(); + if (series) { + QPoint point = series->selectedPoint(); + QString posStr; + posStr.setNum(point.x()); + text.append(posStr); + text.append(", "); + posStr.setNum(point.y()); + text.append(posStr); + } + text.append(")"); + m_textField->setText(text); + } else if (type > QAbstract3DGraph::ElementSeries + && type < QAbstract3DGraph::ElementCustomItem) { + int index = m_graph->selectedLabelIndex(); + QString text; + if (type == QAbstract3DGraph::ElementAxisXLabel) + text.append("Axis X label: "); + else if (type == QAbstract3DGraph::ElementAxisYLabel) + text.append("Axis Y label: "); + else + text.append("Axis Z label: "); + text.append(QString::number(index)); + m_textField->setText(text); + } else { + m_textField->setText("Nothing"); + } +} + +void SurfaceGraphModifier::resetSelection() +{ + m_selectionAnimation->stop(); + if (m_previouslyAnimatedItem) + m_previouslyAnimatedItem->setScaling(m_previousScaling); + m_previouslyAnimatedItem = nullptr; +} diff --git a/examples/datavisualization/graphgallery/surfacegraphmodifier.h b/examples/datavisualization/graphgallery/surfacegraphmodifier.h new file mode 100644 index 00000000..1a54958e --- /dev/null +++ b/examples/datavisualization/graphgallery/surfacegraphmodifier.h @@ -0,0 +1,109 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SURFACEGRAPHMODIFIER_H +#define SURFACEGRAPHMODIFIER_H + +#include <QtDataVisualization/q3dsurface.h> +#include <QtDataVisualization/qsurfacedataproxy.h> +#include <QtDataVisualization/qheightmapsurfacedataproxy.h> +#include <QtDataVisualization/qsurface3dseries.h> +#include <QtDataVisualization/qcustom3ditem.h> +#include <QtDataVisualization/qcustom3dlabel.h> +#include <QtDataVisualization/q3dinputhandler.h> +#include <QtWidgets/qslider.h> +#include <QtWidgets/qlabel.h> +#include <QtCore/qpropertyanimation.h> + +class TopographicSeries; +class HighlightSeries; +class CustomInputHandler; + +class SurfaceGraphModifier : public QObject +{ + Q_OBJECT +public: + explicit SurfaceGraphModifier(Q3DSurface *surface, QLabel *label, QObject *parent); + + //! [0] + void toggleModeNone() { m_graph->setSelectionMode(QAbstract3DGraph::SelectionNone); } + void toggleModeItem() { m_graph->setSelectionMode(QAbstract3DGraph::SelectionItem); } + void toggleModeSliceRow() { m_graph->setSelectionMode(QAbstract3DGraph::SelectionItemAndRow + | QAbstract3DGraph::SelectionSlice + | QAbstract3DGraph::SelectionMultiSeries); } + void toggleModeSliceColumn() { m_graph->setSelectionMode(QAbstract3DGraph::SelectionItemAndColumn + | QAbstract3DGraph::SelectionSlice + | QAbstract3DGraph::SelectionMultiSeries); } + //! [0] + + void setBlackToYellowGradient(); + void setGreenToRedGradient(); + + void setAxisMinSliderX(QSlider *slider) { m_axisMinSliderX = slider; } + void setAxisMaxSliderX(QSlider *slider) { m_axisMaxSliderX = slider; } + void setAxisMinSliderZ(QSlider *slider) { m_axisMinSliderZ = slider; } + void setAxisMaxSliderZ(QSlider *slider) { m_axisMaxSliderZ = slider; } + + void adjustXMin(int min); + void adjustXMax(int max); + void adjustZMin(int min); + void adjustZMax(int max); + +public Q_SLOTS: + void enableSqrtSinModel(bool enable); + void enableHeightMapModel(bool enable); + void enableTopographyModel(bool enable); + + void toggleItemOne(bool show); + void toggleItemTwo(bool show); + void toggleItemThree(bool show); + void toggleSeeThrough(bool seethrough); + void toggleOilHighlight(bool highlight); + void toggleShadows(bool shadows); + void toggleSurfaceTexture(bool enable); + +private: + void setAxisXRange(float min, float max); + void setAxisZRange(float min, float max); + void fillSqrtSinProxy(); + void handleElementSelected(QAbstract3DGraph::ElementType type); + void resetSelection(); + +private: + Q3DSurface *m_graph = nullptr; + QSurfaceDataProxy *m_sqrtSinProxy = nullptr; + QSurface3DSeries *m_sqrtSinSeries = nullptr; + QHeightMapSurfaceDataProxy *m_heightMapProxyOne = nullptr; + QHeightMapSurfaceDataProxy *m_heightMapProxyTwo = nullptr; + QHeightMapSurfaceDataProxy *m_heightMapProxyThree = nullptr; + QSurface3DSeries *m_heightMapSeriesOne = nullptr; + QSurface3DSeries *m_heightMapSeriesTwo = nullptr; + QSurface3DSeries *m_heightMapSeriesThree = nullptr; + + QSlider *m_axisMinSliderX = nullptr; + QSlider *m_axisMaxSliderX = nullptr; + QSlider *m_axisMinSliderZ = nullptr; + QSlider *m_axisMaxSliderZ = nullptr; + float m_rangeMinX = 0.f; + float m_rangeMinZ = 0.f; + float m_stepX = 0.f; + float m_stepZ = 0.f; + int m_heightMapWidth = 0; + int m_heightMapHeight = 0; + + QLabel *m_textField = nullptr; + QPropertyAnimation *m_selectionAnimation = nullptr; + QCustom3DLabel *m_titleLabel = nullptr; + QCustom3DItem *m_previouslyAnimatedItem = nullptr; + QVector3D m_previousScaling = {}; + + TopographicSeries *m_topography = nullptr; + HighlightSeries *m_highlight = nullptr; + int m_highlightWidth = 0; + int m_highlightHeight = 0; + + CustomInputHandler *m_customInputHandler = nullptr; + Q3DInputHandler *m_defaultInputHandler = new Q3DInputHandler(); +}; + +#endif // SURFACEGRAPHMODIFIER_H diff --git a/examples/datavisualization/graphgallery/topographicseries.cpp b/examples/datavisualization/graphgallery/topographicseries.cpp new file mode 100644 index 00000000..201a275e --- /dev/null +++ b/examples/datavisualization/graphgallery/topographicseries.cpp @@ -0,0 +1,55 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "topographicseries.h" + +//! [0] +// Value used to encode height data as RGB value on PNG file +const float packingFactor = 11983.f; +//! [0] + +TopographicSeries::TopographicSeries() +{ + setDrawMode(QSurface3DSeries::DrawSurface); + setFlatShadingEnabled(true); + setBaseColor(Qt::white); +} + +TopographicSeries::~TopographicSeries() = default; + +void TopographicSeries::setTopographyFile(const QString file, float width, float height) +{ + //! [1] + QImage heightMapImage(file); + uchar *bits = heightMapImage.bits(); + int imageHeight = heightMapImage.height(); + int imageWidth = heightMapImage.width(); + int widthBits = imageWidth * 4; + float stepX = width / float(imageWidth); + float stepZ = height / float(imageHeight); + + auto *dataArray = new QSurfaceDataArray; + dataArray->reserve(imageHeight); + for (int i = 0; i < imageHeight; ++i) { + int p = i * widthBits; + float z = height - float(i) * stepZ; + auto *newRow = new QSurfaceDataRow; + newRow->reserve(imageWidth); + for (int j = 0; j < imageWidth; ++j) { + uchar aa = bits[p + 0]; + uchar rr = bits[p + 1]; + uchar gg = bits[p + 2]; + uint color = uint((gg << 16) + (rr << 8) + aa); + float y = float(color) / packingFactor; + newRow->append(QSurfaceDataItem({float(j) * stepX, y, z})); + p += 4; + } + dataArray->append(newRow); + } + + dataProxy()->resetArray(dataArray); + //! [1] + + m_sampleCountX = float(imageWidth); + m_sampleCountZ = float(imageHeight); +} diff --git a/examples/datavisualization/graphgallery/topographicseries.h b/examples/datavisualization/graphgallery/topographicseries.h new file mode 100644 index 00000000..41e03b1e --- /dev/null +++ b/examples/datavisualization/graphgallery/topographicseries.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef TOPOGRAPHICSERIES_H +#define TOPOGRAPHICSERIES_H + +#include <QtDataVisualization/qsurface3dseries.h> + +class TopographicSeries : public QSurface3DSeries +{ + Q_OBJECT +public: + TopographicSeries(); + ~TopographicSeries(); + + void setTopographyFile(const QString file, float width, float height); + + float sampleCountX() { return m_sampleCountX; } + float sampleCountZ() { return m_sampleCountZ; } + +private: + float m_sampleCountX = 0.f; + float m_sampleCountZ = 0.f; +}; + +#endif // TOPOGRAPHICSERIES_H diff --git a/examples/datavisualization/graphgallery/variantbardatamapping.cpp b/examples/datavisualization/graphgallery/variantbardatamapping.cpp new file mode 100644 index 00000000..7a2a6cc7 --- /dev/null +++ b/examples/datavisualization/graphgallery/variantbardatamapping.cpp @@ -0,0 +1,85 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "variantbardatamapping.h" + +VariantBarDataMapping::VariantBarDataMapping(int rowIndex, int columnIndex, int valueIndex, + const QStringList &rowCategories, + const QStringList &columnCategories) + : QObject(nullptr) +{ + m_rowIndex = rowIndex; + m_columnIndex = columnIndex; + m_valueIndex = valueIndex; + m_rowCategories = rowCategories; + m_columnCategories = columnCategories; +} + +VariantBarDataMapping::~VariantBarDataMapping() = default; + +void VariantBarDataMapping::setRowIndex(int index) +{ + m_rowIndex = index; + emit mappingChanged(); +} + +int VariantBarDataMapping::rowIndex() const +{ + return m_rowIndex; +} + +void VariantBarDataMapping::setColumnIndex(int index) +{ + m_columnIndex = index; + emit mappingChanged(); +} + +int VariantBarDataMapping::columnIndex() const +{ + return m_columnIndex; +} + +void VariantBarDataMapping::setValueIndex(int index) +{ + m_valueIndex = index; + emit mappingChanged(); +} + +int VariantBarDataMapping::valueIndex() const +{ + return m_valueIndex; +} + +void VariantBarDataMapping::setRowCategories(const QStringList &categories) +{ + m_rowCategories = categories; + emit mappingChanged(); +} + +const QStringList &VariantBarDataMapping::rowCategories() const +{ + return m_rowCategories; +} + +void VariantBarDataMapping::setColumnCategories(const QStringList &categories) +{ + m_columnCategories = categories; + emit mappingChanged(); +} + +const QStringList &VariantBarDataMapping::columnCategories() const +{ + return m_columnCategories; +} + +void VariantBarDataMapping::remap(int rowIndex, int columnIndex, int valueIndex, + const QStringList &rowCategories, + const QStringList &columnCategories) +{ + m_rowIndex = rowIndex; + m_columnIndex = columnIndex; + m_valueIndex = valueIndex; + m_rowCategories = rowCategories; + m_columnCategories = columnCategories; + emit mappingChanged(); +} diff --git a/examples/datavisualization/graphgallery/variantbardatamapping.h b/examples/datavisualization/graphgallery/variantbardatamapping.h new file mode 100644 index 00000000..25bacebd --- /dev/null +++ b/examples/datavisualization/graphgallery/variantbardatamapping.h @@ -0,0 +1,66 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef VARIANTBARDATAMAPPING_H +#define VARIANTBARDATAMAPPING_H + +#include <QtCore/qobject.h> +#include <QtCore/qstringlist.h> + +class VariantBarDataMapping : public QObject +{ + Q_OBJECT + //! [0] + Q_PROPERTY(int rowIndex READ rowIndex WRITE setRowIndex NOTIFY rowIndexChanged) + Q_PROPERTY(int columnIndex READ columnIndex WRITE setColumnIndex NOTIFY columnIndexChanged) + Q_PROPERTY(int valueIndex READ valueIndex WRITE setValueIndex NOTIFY valueIndexChanged) + Q_PROPERTY(QStringList rowCategories READ rowCategories WRITE setRowCategories NOTIFY rowCategoriesChanged) + Q_PROPERTY(QStringList columnCategories READ columnCategories WRITE setColumnCategories NOTIFY columnCategoriesChanged) + //! [0] +public: + //! [1] + explicit VariantBarDataMapping(int rowIndex, int columnIndex, int valueIndex, + const QStringList &rowCategories, + const QStringList &columnCategories); + //! [1] + virtual ~VariantBarDataMapping(); + + void setRowIndex(int index); + int rowIndex() const; + void setColumnIndex(int index); + int columnIndex() const; + void setValueIndex(int index); + int valueIndex() const; + + void setRowCategories(const QStringList &categories); + const QStringList &rowCategories() const; + void setColumnCategories(const QStringList &categories); + const QStringList &columnCategories() const; + + //! [2] + void remap(int rowIndex, int columnIndex, int valueIndex, + const QStringList &rowCategories, + const QStringList &columnCategories); + //! [2] +Q_SIGNALS: + void rowIndexChanged(); + void columnIndexChanged(); + void valueIndexChanged(); + void rowCategoriesChanged(); + void columnCategoriesChanged(); + //! [3] + void mappingChanged(); + //! [3] + +private: + // Indexes of the mapped items in the VariantDataItem + int m_rowIndex = 0; + int m_columnIndex = 1; + int m_valueIndex = 2; + + // For row/column items, sort items into these categories. Other categories are ignored. + QStringList m_rowCategories = {}; + QStringList m_columnCategories = {}; +}; + +#endif diff --git a/examples/datavisualization/graphgallery/variantbardataproxy.cpp b/examples/datavisualization/graphgallery/variantbardataproxy.cpp new file mode 100644 index 00000000..8fe73abc --- /dev/null +++ b/examples/datavisualization/graphgallery/variantbardataproxy.cpp @@ -0,0 +1,114 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "variantbardataproxy.h" + +VariantBarDataProxy::VariantBarDataProxy() = default; + +VariantBarDataProxy::~VariantBarDataProxy() +{ + delete m_dataSet; +} + +void VariantBarDataProxy::setDataSet(VariantDataSet *newSet) +{ + if (!m_dataSet.isNull()) + QObject::disconnect(m_dataSet.data(), nullptr, this, nullptr); + + m_dataSet = newSet; + + if (!m_dataSet.isNull()) { + QObject::connect(m_dataSet.data(), &VariantDataSet::itemsAdded, this, + &VariantBarDataProxy::handleItemsAdded); + QObject::connect(m_dataSet.data(), &VariantDataSet::dataCleared, this, + &VariantBarDataProxy::handleDataCleared); + } + resolveDataSet(); +} + +VariantDataSet *VariantBarDataProxy::dataSet() +{ + return m_dataSet.data(); +} + +void VariantBarDataProxy::setMapping(VariantBarDataMapping *mapping) +{ + if (!m_mapping.isNull()) { + QObject::disconnect(m_mapping.data(), &VariantBarDataMapping::mappingChanged, this, + &VariantBarDataProxy::handleMappingChanged); + } + + m_mapping = mapping; + + if (!m_mapping.isNull()) { + QObject::connect(m_mapping.data(), &VariantBarDataMapping::mappingChanged, this, + &VariantBarDataProxy::handleMappingChanged); + } + + resolveDataSet(); +} + +VariantBarDataMapping *VariantBarDataProxy::mapping() +{ + return m_mapping.data(); +} + +void VariantBarDataProxy::handleItemsAdded(int index, int count) +{ + Q_UNUSED(index); + Q_UNUSED(count); + + // Resolve new items + resolveDataSet(); +} + +void VariantBarDataProxy::handleDataCleared() +{ + // Data cleared, reset array + resetArray(nullptr); +} + +void VariantBarDataProxy::handleMappingChanged() +{ + resolveDataSet(); +} + +// Resolve entire dataset into QBarDataArray. +//! [0] +void VariantBarDataProxy::resolveDataSet() +{ + // If we have no data or mapping, or the categories are not defined, simply clear the array + if (m_dataSet.isNull() || m_mapping.isNull() || !m_mapping->rowCategories().size() + || !m_mapping->columnCategories().size()) { + resetArray(nullptr); + return; + } + const VariantDataItemList &itemList = m_dataSet->itemList(); + + int rowIndex = m_mapping->rowIndex(); + int columnIndex = m_mapping->columnIndex(); + int valueIndex = m_mapping->valueIndex(); + const QStringList &rowList = m_mapping->rowCategories(); + const QStringList &columnList = m_mapping->columnCategories(); + + // Sort values into rows and columns + using ColumnValueMap = QHash<QString, float>; + QHash <QString, ColumnValueMap> itemValueMap; + for (const VariantDataItem *item : itemList) { + itemValueMap[item->at(rowIndex).toString()][item->at(columnIndex).toString()] + = item->at(valueIndex).toReal(); + } + + // Create a new data array in format the parent class understands + auto *newProxyArray = new QBarDataArray; + for (const QString &rowKey : rowList) { + auto *newProxyRow = new QBarDataRow(columnList.size()); + for (qsizetype i = 0; i < columnList.size(); ++i) + (*newProxyRow)[i].setValue(itemValueMap[rowKey][columnList.at(i)]); + newProxyArray->append(newProxyRow); + } + + // Finally, reset the data array in the parent class + resetArray(newProxyArray); +} +//! [0] diff --git a/examples/datavisualization/graphgallery/variantbardataproxy.h b/examples/datavisualization/graphgallery/variantbardataproxy.h new file mode 100644 index 00000000..6a4d2f31 --- /dev/null +++ b/examples/datavisualization/graphgallery/variantbardataproxy.h @@ -0,0 +1,48 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef VARIANTBARDATAPROXY_H +#define VARIANTBARDATAPROXY_H + +#include "variantdataset.h" +#include "variantbardatamapping.h" +#include <QtDataVisualization/qbardataproxy.h> +#include <QtCore/qpointer.h> + +//! [0] +class VariantBarDataProxy : public QBarDataProxy +//! [0] +{ + Q_OBJECT + +public: + VariantBarDataProxy(); + virtual ~VariantBarDataProxy(); + + //! [1] + // Doesn't gain ownership of the dataset, but does connect to it to listen for data changes. + void setDataSet(VariantDataSet *newSet); + VariantDataSet *dataSet(); + + // Map key (row, column, value) to value index in data item (VariantItem). + // Doesn't gain ownership of mapping, but does connect to it to listen for mapping changes. + // Modifying mapping that is set to proxy will trigger dataset re-resolving. + void setMapping(VariantBarDataMapping *mapping); + VariantBarDataMapping *mapping(); + //! [1] + +public Q_SLOTS: + void handleItemsAdded(int index, int count); + void handleDataCleared(); + void handleMappingChanged(); + +private: + void resolveDataSet(); + + QPointer<VariantDataSet> m_dataSet; + QPointer<VariantBarDataMapping> m_mapping; + + Q_DISABLE_COPY(VariantBarDataProxy) +}; + +#endif diff --git a/examples/datavisualization/graphgallery/variantdataset.cpp b/examples/datavisualization/graphgallery/variantdataset.cpp new file mode 100644 index 00000000..3892f02b --- /dev/null +++ b/examples/datavisualization/graphgallery/variantdataset.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "variantdataset.h" + +VariantDataSet::VariantDataSet() = default; + +VariantDataSet::~VariantDataSet() +{ + clear(); +} + +void VariantDataSet::clear() +{ + for (VariantDataItem *item : m_variantData) { + item->clear(); + delete item; + } + m_variantData.clear(); + emit dataCleared(); +} + +int VariantDataSet::addItem(VariantDataItem *item) +{ + m_variantData.append(item); + int addIndex = m_variantData.size(); + + emit itemsAdded(addIndex, 1); + return addIndex; +} + +int VariantDataSet::addItems(VariantDataItemList *itemList) +{ + int newCount = itemList->size(); + int addIndex = m_variantData.size(); + m_variantData.append(*itemList); + delete itemList; + emit itemsAdded(addIndex, newCount); + return addIndex; +} + +const VariantDataItemList &VariantDataSet::itemList() const +{ + return m_variantData; +} diff --git a/examples/datavisualization/graphgallery/variantdataset.h b/examples/datavisualization/graphgallery/variantdataset.h new file mode 100644 index 00000000..ca0d7ffd --- /dev/null +++ b/examples/datavisualization/graphgallery/variantdataset.h @@ -0,0 +1,41 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef VARIANTDATASET_H +#define VARIANTDATASET_H + +#include <QtCore/qvariantlist.h> + +//! [0] +using VariantDataItem = QVariantList; +using VariantDataItemList = QList<VariantDataItem *>; +//! [0] + +class VariantDataSet : public QObject +{ + Q_OBJECT + +public: + explicit VariantDataSet(); + ~VariantDataSet(); + + //! [1] + void clear(); + + int addItem(VariantDataItem *item); + int addItems(VariantDataItemList *itemList); + + const VariantDataItemList &itemList() const; + +Q_SIGNALS: + void itemsAdded(int index, int count); + void dataCleared(); + //! [1] + +private: + VariantDataItemList m_variantData; + + Q_DISABLE_COPY(VariantDataSet) +}; + +#endif |