mirror of https://github.com/qt/qtgraphs.git
Add spline rendering to scatter
Also adding manual and autotests Fixes: QTBUG-125916 Fixes: QTBUG-124742 Change-Id: I152e1d3652c9c97cb3e714e0a80acdfc9e4f9482 Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io> Reviewed-by: Sami Varanka <sami.varanka@qt.io>
This commit is contained in:
parent
1728887e0a
commit
ee5ede3108
|
|
@ -56,6 +56,7 @@ else()
|
|||
"graphs3d/qml/resources/SurfaceMaterial.qml"
|
||||
"graphs3d/qml/resources/ScatterMaterial.qml"
|
||||
"graphs3d/qml/resources/ScatterMaterialInstancing.qml"
|
||||
"graphs3d/qml/resources/SplineMaterial.qml"
|
||||
"graphs3d/qml/resources/GridSurfaceMaterial.qml"
|
||||
"graphs3d/qml/resources/SurfaceSliceMaterial.qml"
|
||||
"graphs3d/qml/resources/SurfaceShadowNoTex.qml"
|
||||
|
|
@ -144,6 +145,8 @@ else()
|
|||
"graphs3d/engine/shaders/scatterinstancing.vert"
|
||||
"graphs3d/engine/shaders/scatterinstancing.frag"
|
||||
"graphs3d/engine/shaders/surfaceSlice.vert"
|
||||
"graphs3d/engine/shaders/spline.vert"
|
||||
"graphs3d/engine/shaders/spline.frag"
|
||||
"graphs3d/engine/shaders/texture3d.frag"
|
||||
"graphs3d/engine/shaders/texture3d.vert"
|
||||
"graphs3d/engine/shaders/texture3dlowdef.frag"
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ else()
|
|||
SOURCES
|
||||
data/qitemmodelscatterdataproxy.cpp data/qitemmodelscatterdataproxy.h data/qitemmodelscatterdataproxy_p.h
|
||||
data/qscatter3dseries.cpp data/qscatter3dseries.h data/qscatter3dseries_p.h
|
||||
data/qspline3dseries.cpp data/qspline3dseries.h data/qspline3dseries_p.h
|
||||
data/qscatterdataitem.cpp data/qscatterdataitem.h
|
||||
data/qscatterdataproxy.cpp data/qscatterdataproxy.h data/qscatterdataproxy_p.h
|
||||
data/scatteritemmodelhandler.cpp data/scatteritemmodelhandler_p.h
|
||||
|
|
@ -78,6 +79,7 @@ else()
|
|||
qml/foreigntypesscatter_p.h
|
||||
qml/qquickgraphsscatter.cpp qml/qquickgraphsscatter_p.h
|
||||
qml/qquickgraphsscatterseries.cpp qml/qquickgraphsscatterseries_p.h
|
||||
qml/qquickgraphssplineseries.cpp qml/qquickgraphssplineseries_p.h
|
||||
INCLUDE_DIRECTORIES
|
||||
data
|
||||
engine
|
||||
|
|
|
|||
|
|
@ -0,0 +1,340 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
#include <private/qspline3dseries_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
* \class QSpline3DSeries
|
||||
* \inmodule QtGraphs
|
||||
* \ingroup graphs_3D
|
||||
* \since 6.9
|
||||
* \brief The QSpline3DSeries class represents a data series as a spline.
|
||||
*
|
||||
* Spline graphs are used to show information as a series of data points connected
|
||||
* by a curved or straight Catmull-Rom spline.
|
||||
*
|
||||
* This class manages the spline specific visual elements.
|
||||
*
|
||||
* Spline3DSeries extends the Scatter3DSeries API.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmltype Spline3DSeries
|
||||
* \instantiates QSpline3DSeries
|
||||
* \inqmlmodule QtGraphs
|
||||
* \ingroup graphs_qml_3D
|
||||
* \inherits Scatter3DSeries
|
||||
* \since 6.9
|
||||
* \brief Represents a data series in a 3D spline graph.
|
||||
*
|
||||
* Spline graphs are used to show information as a series of data points connected
|
||||
* by a curved or straight Catmull-Rom spline.
|
||||
*
|
||||
* This type manages the spline specific visual elements.
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlproperty bool Spline3DSeries::splineVisible
|
||||
*
|
||||
* Visibility of the spline. The default value is \c true.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlproperty float Spline3DSeries::splineTension
|
||||
*
|
||||
* The tension of the spline.
|
||||
*
|
||||
* The spline uses maximum curvature for segments at a value of \c 0.0
|
||||
* Segments are completely straight at a value of \c 1.0
|
||||
* Must be between \c 0.0 and \c 1.0
|
||||
* The default value is \c 0.0
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlproperty float Spline3DSeries::splineKnotting
|
||||
*
|
||||
* The knot parametrization of the spline.
|
||||
*
|
||||
* This parameter can change the profile of the curve.
|
||||
* The spline is classified as a uniform Catmull-Rom spline at a value of \c 0.0,
|
||||
* a centripetal Catmull-Rom spline at a value of \c 0.5,
|
||||
* and a chordal Catmull-Rom spline at a value of \c 1.0.
|
||||
*
|
||||
* The value must be between \c 0.0 and \c 1.0.
|
||||
* The default value is \c 0.5.
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlproperty float Spline3DSeries::splineLooping
|
||||
*
|
||||
* Determines whether the spline loops.
|
||||
*
|
||||
* This adds a spline segment between the first and last points of the series
|
||||
* connecting the spline into a loop.
|
||||
*
|
||||
* The default value is \c false
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlproperty int QSpline3DSeries::splineResolution
|
||||
*
|
||||
* The resolution of the segments spline.
|
||||
*
|
||||
* The number of vertices per spline segment,
|
||||
* which is defined as the part between two points.
|
||||
*
|
||||
* Must be a value above \c 2.
|
||||
* The default value is \c 10.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \qmlproperty color Spline3DSeries::splineColor
|
||||
*
|
||||
* The color of the spline.
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlsignal Scatter3DSeries::splineVisibilityChanged(bool visible)
|
||||
|
||||
This signal is emitted when splineVisible changes to \a visible.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlsignal Scatter3DSeries::splineTensionChanged(float tension)
|
||||
|
||||
This signal is emitted when splineTension changes to \a tension.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlsignal Scatter3DSeries::splineKnottingChanged(float knotting)
|
||||
|
||||
This signal is emitted when splineKnotting changes to \a knotting.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlsignal Scatter3DSeries::splineLoopingChanged(bool looping)
|
||||
|
||||
This signal is emitted when splineLooping changes to \a looping.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlsignal Scatter3DSeries::splineColorChanged(color color)
|
||||
|
||||
This signal is emitted when splineColor changes to \a color.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\qmlsignal Scatter3DSeries::splineResolutionChanged(int resolution)
|
||||
|
||||
This signal is emitted when splineResolution changes to \a resolution.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Constructs a spline 3D series with the parent \a parent.
|
||||
*/
|
||||
QSpline3DSeries ::QSpline3DSeries(QObject *parent)
|
||||
: QScatter3DSeries(*(new QSpline3DSeriesPrivate()), parent)
|
||||
{
|
||||
Q_D(QScatter3DSeries);
|
||||
// Default proxy
|
||||
d->setDataProxy(new QScatterDataProxy);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Constructs a spline 3D series with the data proxy \a dataProxy and the
|
||||
* parent \a parent.
|
||||
*/
|
||||
QSpline3DSeries ::QSpline3DSeries(QScatterDataProxy *dataProxy, QObject *parent)
|
||||
: QScatter3DSeries(*(new QSpline3DSeriesPrivate()), parent)
|
||||
{
|
||||
Q_D(QScatter3DSeries);
|
||||
// Default proxy
|
||||
d->setDataProxy(dataProxy);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
*/
|
||||
QSpline3DSeries ::QSpline3DSeries(QSpline3DSeriesPrivate &dd, QObject *parent)
|
||||
: QScatter3DSeries(dd, parent)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* Deletes the spline 3D series.
|
||||
*/
|
||||
QSpline3DSeries::~QSpline3DSeries() {}
|
||||
|
||||
/*!
|
||||
* \property QSpline3DSeries::splineVisible
|
||||
*
|
||||
* \brief Visibility of the spline.
|
||||
*
|
||||
* Visibility of the spline.
|
||||
* The default value is \c true.
|
||||
*
|
||||
*/
|
||||
void QSpline3DSeries::setSplineVisible(bool visible)
|
||||
{
|
||||
Q_D(QSpline3DSeries);
|
||||
if (d->m_splineVisible != visible) {
|
||||
d->m_splineVisible = visible;
|
||||
emit splineVisibilityChanged(visible);
|
||||
}
|
||||
}
|
||||
|
||||
bool QSpline3DSeries::isSplineVisible() const
|
||||
{
|
||||
const Q_D(QSpline3DSeries);
|
||||
return d->m_splineVisible;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \property QSpline3DSeries::splineTension
|
||||
*
|
||||
* \brief The tension of the spline.
|
||||
*
|
||||
* The spline uses maximum curvature for segments at a value of \c 0.0
|
||||
* Segments are completely straight at a value of \c 1.0
|
||||
* Must be between \c 0.0 and \c 1.0
|
||||
* The default value is \c 0.0
|
||||
*
|
||||
*/
|
||||
void QSpline3DSeries::setSplineTension(float tension)
|
||||
{
|
||||
Q_D(QSpline3DSeries);
|
||||
if (tension < 0.0f || tension > 1.0f) {
|
||||
qWarning("Invalid tension. Valid range for tension is 0.0f...1.0f");
|
||||
} else if (d->m_tension != tension) {
|
||||
d->m_tension = tension;
|
||||
emit splineTensionChanged(tension);
|
||||
}
|
||||
}
|
||||
|
||||
float QSpline3DSeries::splineTension() const
|
||||
{
|
||||
const Q_D(QSpline3DSeries);
|
||||
return d->m_tension;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \property QSpline3DSeries::splineKnotting
|
||||
*
|
||||
* \brief The knot parametrization of the spline.
|
||||
*
|
||||
* This parameter can change the profile of the curve.
|
||||
* The spline is classified as a uniform Catmull-Rom spline at a value of \c 0.0,
|
||||
* a centripetal Catmull-Rom spline at a value of \c 0.5,
|
||||
* and a chordal Catmull-Rom spline at a value of \c 1.0.
|
||||
*
|
||||
* The value must be between \c 0.0 and \c 1.0.
|
||||
* The default value is \c 0.5.
|
||||
*
|
||||
*/
|
||||
void QSpline3DSeries::setSplineKnotting(float knotting)
|
||||
{
|
||||
Q_D(QSpline3DSeries);
|
||||
if (knotting < 0.0f || knotting > 1.0f) {
|
||||
qWarning("Invalid knotting. Valid range for knotting is 0.0f...1.0f");
|
||||
} else if (d->m_knotting != knotting) {
|
||||
d->m_knotting = knotting;
|
||||
emit splineKnottingChanged(knotting);
|
||||
}
|
||||
}
|
||||
|
||||
float QSpline3DSeries::splineKnotting() const
|
||||
{
|
||||
const Q_D(QSpline3DSeries);
|
||||
return d->m_knotting;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \property QSpline3DSeries::splineLooping
|
||||
*
|
||||
* \brief Determines whether the spline loops.
|
||||
*
|
||||
* This adds a spline segment between the first and last points of the series
|
||||
* connecting the spline into a loop.
|
||||
*
|
||||
* The default value is \c false
|
||||
*
|
||||
*/
|
||||
void QSpline3DSeries::setSplineLooping(bool looping)
|
||||
{
|
||||
Q_D(QSpline3DSeries);
|
||||
if (d->m_looping != looping) {
|
||||
d->m_looping = looping;
|
||||
emit splineLoopingChanged(looping);
|
||||
}
|
||||
}
|
||||
|
||||
bool QSpline3DSeries::isSplineLooping() const
|
||||
{
|
||||
const Q_D(QSpline3DSeries);
|
||||
return d->m_looping;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \property QSpline3DSeries::splineColor
|
||||
*
|
||||
* \brief The color of the spline.
|
||||
*
|
||||
*/
|
||||
void QSpline3DSeries::setSplineColor(QColor color)
|
||||
{
|
||||
Q_D(QSpline3DSeries);
|
||||
if (d->m_splineColor != color) {
|
||||
d->m_splineColor = color;
|
||||
emit splineColorChanged(color);
|
||||
}
|
||||
}
|
||||
|
||||
QColor QSpline3DSeries::splineColor() const
|
||||
{
|
||||
const Q_D(QSpline3DSeries);
|
||||
return d->m_splineColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \property QSpline3DSeries::splineResolution
|
||||
*
|
||||
* \brief The resolution of the segments spline.
|
||||
*
|
||||
* The number of vertices per spline segment,
|
||||
* which is defined as the part between two points.
|
||||
*
|
||||
* Must be a value above \c 2.
|
||||
* The default value is \c 10.
|
||||
*/
|
||||
|
||||
void QSpline3DSeries::setSplineResolution(int resolution)
|
||||
{
|
||||
Q_D(QSpline3DSeries);
|
||||
if (resolution < 2) {
|
||||
qWarning("Invalid resolution. The resolution must be 2 or above");
|
||||
} else if (d->m_resolution != resolution) {
|
||||
d->m_resolution = resolution;
|
||||
emit splineResolutionChanged(resolution);
|
||||
}
|
||||
}
|
||||
|
||||
int QSpline3DSeries::splineResolution() const
|
||||
{
|
||||
const Q_D(QSpline3DSeries);
|
||||
return d->m_resolution;
|
||||
}
|
||||
|
||||
// QSpline3DSeriesPrivate
|
||||
|
||||
QSpline3DSeriesPrivate::QSpline3DSeriesPrivate()
|
||||
: QScatter3DSeriesPrivate()
|
||||
{}
|
||||
|
||||
QSpline3DSeriesPrivate::~QSpline3DSeriesPrivate() {}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
#ifndef QSPLINE3DSERIES_H
|
||||
#define QSPLINE3DSERIES_H
|
||||
|
||||
#include <QtGraphs/qscatter3dseries.h>
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QSpline3DSeriesPrivate;
|
||||
|
||||
class Q_GRAPHS_EXPORT QSpline3DSeries : public QScatter3DSeries
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(QSpline3DSeries)
|
||||
Q_PROPERTY(bool splineVisible READ isSplineVisible WRITE setSplineVisible NOTIFY
|
||||
splineVisibilityChanged FINAL)
|
||||
Q_PROPERTY(float splineTension READ splineTension WRITE setSplineTension NOTIFY
|
||||
splineTensionChanged FINAL)
|
||||
Q_PROPERTY(float splineKnotting READ splineKnotting WRITE setSplineKnotting NOTIFY
|
||||
splineKnottingChanged FINAL)
|
||||
Q_PROPERTY(bool splineLooping READ isSplineLooping WRITE setSplineLooping NOTIFY
|
||||
splineLoopingChanged FINAL)
|
||||
Q_PROPERTY(
|
||||
QColor splineColor READ splineColor WRITE setSplineColor NOTIFY splineColorChanged FINAL)
|
||||
Q_PROPERTY(int splineResolution READ splineResolution WRITE setSplineResolution NOTIFY
|
||||
splineResolutionChanged FINAL)
|
||||
public:
|
||||
explicit QSpline3DSeries(QObject *parent = nullptr);
|
||||
explicit QSpline3DSeries(QScatterDataProxy *dataProxy, QObject *parent = nullptr);
|
||||
~QSpline3DSeries() override;
|
||||
|
||||
void setSplineVisible(bool draw);
|
||||
bool isSplineVisible() const;
|
||||
|
||||
void setSplineTension(float tension);
|
||||
float splineTension() const;
|
||||
|
||||
void setSplineKnotting(float knotting);
|
||||
float splineKnotting() const;
|
||||
|
||||
void setSplineLooping(bool looping);
|
||||
bool isSplineLooping() const;
|
||||
|
||||
void setSplineColor(QColor color);
|
||||
QColor splineColor() const;
|
||||
|
||||
void setSplineResolution(int resolution);
|
||||
int splineResolution() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void splineVisibilityChanged(bool visible);
|
||||
void splineTensionChanged(float tension);
|
||||
void splineKnottingChanged(float knotting);
|
||||
void splineLoopingChanged(bool looping);
|
||||
void splineColorChanged(QColor color);
|
||||
void splineResolutionChanged(int resolution);
|
||||
|
||||
protected:
|
||||
explicit QSpline3DSeries(QSpline3DSeriesPrivate &d, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY_MOVE(QSpline3DSeries)
|
||||
|
||||
friend class QQuickGraphsScatter;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSPLINE3DSERIES_H
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt Graphs API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
#ifndef QSPLINE3DSERIES_P_H
|
||||
#define QSPLINE3DSERIES_P_H
|
||||
|
||||
#include <QtGraphs/qspline3dseries.h>
|
||||
#include <private/qscatter3dseries_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QSpline3DSeriesPrivate : public QScatter3DSeriesPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QSpline3DSeries)
|
||||
|
||||
public:
|
||||
QSpline3DSeriesPrivate();
|
||||
~QSpline3DSeriesPrivate() override;
|
||||
|
||||
private:
|
||||
bool m_splineVisible = true;
|
||||
qreal m_tension = 0;
|
||||
qreal m_knotting = 0.5;
|
||||
bool m_looping = false;
|
||||
QColor m_splineColor = QColor(255, 0, 0);
|
||||
qsizetype m_resolution = 10;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSPLINE3DSERIES_P_H
|
||||
|
|
@ -64,11 +64,11 @@
|
|||
|
||||
\section2 3D Scatter Graphs
|
||||
|
||||
3D scatter graphs present data as a collection of points. The
|
||||
3D scatter graphs present data as a collection of points or a spline. The
|
||||
\l Q3DScatterWidgetItem class is used to create a graph. The
|
||||
\l QScatter3DSeries and \l QScatterDataProxy classes are used to set data to
|
||||
\l QScatter3DSeries or \l QSpline3DSeries and \l QScatterDataProxy classes are used to set data to
|
||||
the graph and to control the visual properties of the graph. In QML, the
|
||||
corresponding types are \l Scatter3D, \l Scatter3DSeries, and
|
||||
corresponding types are \l Scatter3D, \l Scatter3DSeries, \l Spline3DSeries, and
|
||||
\l ScatterDataProxy.
|
||||
|
||||
\image q3dscatter-minimal.png
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
void MAIN(){
|
||||
}
|
||||
|
||||
void POST_PROCESS() {
|
||||
COLOR_SUM = color;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
void MAIN() {
|
||||
|
||||
//Catmull-Rom spline
|
||||
|
||||
float curveIndex = UV0.y;
|
||||
float step = 1.0f / float(points);
|
||||
|
||||
//add small offset to avoid sampling the edge of each texture pixel
|
||||
float pixelOffset = step * 0.1f;
|
||||
|
||||
|
||||
vec3 p0 = texture(controlPoints, vec2(curveIndex + pixelOffset, 0.5f)).xyz;
|
||||
vec3 p1 = texture(controlPoints, vec2(curveIndex + step + pixelOffset , 0.5f)).xyz;
|
||||
vec3 p2 = texture(controlPoints, vec2(curveIndex + 2.0f*step + pixelOffset , 0.5f)).xyz;
|
||||
vec3 p3 = texture(controlPoints, vec2(curveIndex + 3.0f*step + pixelOffset , 0.5f)).xyz;
|
||||
|
||||
// check if looping segment
|
||||
float lastThresh = 1.0f - (1.0f / float(points - 2.0f)) - 0.001f;
|
||||
bool lastSegment = (curveIndex * points) / (points - 2.0f) >= lastThresh;
|
||||
|
||||
if (loop && lastSegment) {
|
||||
p2 = texture(controlPoints, vec2(step, 0.5f)).xyz;
|
||||
p3 = texture(controlPoints, vec2(2.0f* step, 0.5f)).xyz;
|
||||
}
|
||||
|
||||
float t01 = pow(distance(p0, p1), knotting);
|
||||
float t12 = pow(distance(p1, p2), knotting);
|
||||
float t23 = pow(distance(p2, p3), knotting);
|
||||
|
||||
vec3 m1 = (1.0f - tension) *
|
||||
(p2 - p1 + t12 * ((p1 - p0) / t01 - (p2 - p0) / (t01 + t12)));
|
||||
vec3 m2 = (1.0f - tension) *
|
||||
(p2 - p1 + t12 * ((p3 - p2) / t23 - (p3 - p1) / (t12 + t23)));
|
||||
|
||||
vec3 A = 2.0f * (p1 - p2) + m1 + m2;
|
||||
vec3 B = -3.0f * (p1 - p2) - m1 - m1 - m2;
|
||||
vec3 C = m1;
|
||||
vec3 D = p1;
|
||||
|
||||
float t = UV0.x;
|
||||
|
||||
vec3 point =
|
||||
A * t * t * t +
|
||||
B * t * t +
|
||||
C * t +
|
||||
D;
|
||||
|
||||
vec4 pos = MODELVIEWPROJECTION_MATRIX * vec4(point, 1.0f);
|
||||
POSITION = pos;
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ static const int insertRemoveRecordReserveSize = 31;
|
|||
*
|
||||
* See \l{Simple Scatter Graph} for more thorough usage example.
|
||||
*
|
||||
* \sa Scatter3DSeries, ScatterDataProxy, Bars3D, Surface3D,
|
||||
* \sa Scatter3DSeries, Spline3DSeries, ScatterDataProxy, Bars3D, Surface3D,
|
||||
* {Qt Graphs C++ Classes for 3D}
|
||||
*/
|
||||
|
||||
|
|
@ -704,11 +704,13 @@ void QQuickGraphsScatter::removeDataItems(ScatterModel *graphModel,
|
|||
deleteDataItem(graphModel->selectionIndicator);
|
||||
deleteDataItem(graphModel->baseRef);
|
||||
deleteDataItem(graphModel->selectionRef);
|
||||
deleteDataItem(graphModel->splineModel);
|
||||
|
||||
graphModel->instancingRootItem = nullptr;
|
||||
graphModel->selectionIndicator = nullptr;
|
||||
graphModel->baseRef = nullptr;
|
||||
graphModel->selectionRef = nullptr;
|
||||
graphModel->splineModel = nullptr;
|
||||
} else {
|
||||
QList<QQuick3DModel *> &items = graphModel->dataItems;
|
||||
removeDataItems(items, items.count());
|
||||
|
|
@ -1529,6 +1531,177 @@ void QQuickGraphsScatter::calculatePolarXZ(const float posX,
|
|||
z = -static_cast<float>(radius * qCos(angle)) * m_polarRadius;
|
||||
}
|
||||
|
||||
void QQuickGraphsScatter::updateSpline(ScatterModel *model)
|
||||
{
|
||||
if (auto series = qobject_cast<QSpline3DSeries *>(model->series)) {
|
||||
if (!series->isSplineVisible()) {
|
||||
if (model->splineModel)
|
||||
model->splineModel->setVisible(false);
|
||||
return;
|
||||
} else {
|
||||
if (!model->splineModel)
|
||||
createSplineModel(model);
|
||||
|
||||
QQmlListReference materialRef(model->splineModel, "materials");
|
||||
QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(
|
||||
materialRef.at(0));
|
||||
|
||||
QVariant splineInputAsVariant = material->property("controlPoints");
|
||||
QQuick3DShaderUtilsTextureInput *splineInput
|
||||
= splineInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
|
||||
QQuick3DTexture *splineTexture = splineInput->texture();
|
||||
QQuick3DTextureData *splineData = splineTexture->textureData();
|
||||
|
||||
bool loop = series->isSplineLooping();
|
||||
material->setProperty("tension", series->splineTension());
|
||||
material->setProperty("knotting", series->splineKnotting());
|
||||
material->setProperty("loop", loop);
|
||||
material->setProperty("color", series->splineColor());
|
||||
|
||||
const QScatterDataArray &array = series->dataArray();
|
||||
qsizetype pointCount = array.size();
|
||||
if (isDataDirty() && array.size() != 0) {
|
||||
QVector<QVector4D> splinePoints;
|
||||
QVector<SplineVertex> vertices;
|
||||
splinePoints.reserve(pointCount + 2);
|
||||
splineData->setSize(QSize(pointCount + 2, 1));
|
||||
|
||||
auto normalizedPos = [this](QVector3D pos) {
|
||||
float posX = static_cast<QValue3DAxis *>(axisX())->positionAt(pos.x())
|
||||
* scale().x()
|
||||
+ translate().x();
|
||||
float posY = static_cast<QValue3DAxis *>(axisY())->positionAt(pos.y())
|
||||
* scale().y()
|
||||
+ translate().y();
|
||||
float posZ = static_cast<QValue3DAxis *>(axisZ())->positionAt(pos.z())
|
||||
* scale().z()
|
||||
+ translate().z();
|
||||
return QVector3D(posX, posY, posZ);
|
||||
};
|
||||
|
||||
QVector3D first = normalizedPos(array.at(0).position());
|
||||
QVector3D second = normalizedPos(array.at(1).position());
|
||||
QVector3D pStart = first + (first - second) * 0.1f;
|
||||
QVector3D last = normalizedPos(array.at(pointCount - 1).position());
|
||||
QVector3D secondLast = normalizedPos(array.at(pointCount - 2).position());
|
||||
QVector3D pEnd = last + (last - secondLast) * 0.1f;
|
||||
|
||||
if (loop)
|
||||
splinePoints.append(QVector4D(last, 1));
|
||||
else
|
||||
splinePoints.append(QVector4D(pStart, 1));
|
||||
|
||||
const qsizetype resolution = series->splineResolution();
|
||||
vertices.reserve(resolution * pointCount);
|
||||
for (int i = 0; i < pointCount; i++) {
|
||||
splinePoints.push_back(QVector4D(normalizedPos(array.at(i).position()), 1));
|
||||
for (int j = 0; j < resolution; j++) {
|
||||
SplineVertex vertex;
|
||||
vertex.position = QVector3D(float(j) / float(resolution), float(i), 0);
|
||||
vertex.uv = QVector2D(float(j) / float(resolution - 1),
|
||||
float(i) / float(pointCount + 2));
|
||||
vertices.push_back(vertex);
|
||||
}
|
||||
}
|
||||
if (loop)
|
||||
splinePoints.append(QVector4D(first, 1));
|
||||
else
|
||||
splinePoints.append(QVector4D(pEnd, 1));
|
||||
|
||||
QByteArray pointData = QByteArray(reinterpret_cast<char *>(splinePoints.data()),
|
||||
splinePoints.size() * sizeof(QVector4D));
|
||||
|
||||
splineData->setTextureData(pointData);
|
||||
material->setProperty("points", splinePoints.size());
|
||||
QQuick3DGeometry *splineGeometry = model->splineModel->geometry();
|
||||
QByteArray vertexBuffer(reinterpret_cast<char *>(vertices.data()),
|
||||
vertices.size() * sizeof(SplineVertex));
|
||||
splineGeometry->setVertexData(vertexBuffer);
|
||||
splineGeometry->update();
|
||||
splineTexture->setTextureData(splineData);
|
||||
splineInput->setTexture(splineTexture);
|
||||
}
|
||||
model->splineModel->setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QQuickGraphsScatter::createSplineModel(ScatterModel *model)
|
||||
{
|
||||
QQuick3DModel *splineModel = new QQuick3DModel();
|
||||
splineModel->setParent(model->series);
|
||||
splineModel->setParentItem(graphNode());
|
||||
splineModel->setObjectName(QStringLiteral("SplineModel"));
|
||||
splineModel->setVisible(true);
|
||||
splineModel->setPickable(false);
|
||||
auto geometry = new QQuick3DGeometry();
|
||||
geometry->setParent(splineModel);
|
||||
geometry->setStride(sizeof(SplineVertex)); //pos + uv
|
||||
geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::LineStrip);
|
||||
geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
|
||||
0,
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
geometry->addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
|
||||
sizeof(QVector3D),
|
||||
QQuick3DGeometry::Attribute::F32Type);
|
||||
splineModel->setGeometry(geometry);
|
||||
|
||||
QQuick3DTexture *splineTex = new QQuick3DTexture();
|
||||
splineTex->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
|
||||
splineTex->setVerticalTiling(QQuick3DTexture::ClampToEdge);
|
||||
splineTex->setMinFilter(QQuick3DTexture::Nearest);
|
||||
splineTex->setMagFilter(QQuick3DTexture::Nearest);
|
||||
QQuick3DTextureData *splineData = new QQuick3DTextureData;
|
||||
splineData->setSize(QSize(0, 1));
|
||||
splineData->setFormat(QQuick3DTextureData::RGBA32F);
|
||||
splineData->setParent(splineTex);
|
||||
splineData->setParentItem(splineTex);
|
||||
splineTex->setTextureData(splineData);
|
||||
|
||||
QQmlListReference materialRef(splineModel, "materials");
|
||||
QQuick3DCustomMaterial *material = createQmlCustomMaterial(
|
||||
QStringLiteral(":/materials/SplineMaterial"));
|
||||
material->setParent(splineModel);
|
||||
material->setParentItem(splineModel);
|
||||
material->setObjectName("splineMaterial");
|
||||
QVariant textureInputAsVariant = material->property("controlPoints");
|
||||
QQuick3DShaderUtilsTextureInput *textureInput = textureInputAsVariant
|
||||
.value<QQuick3DShaderUtilsTextureInput *>();
|
||||
textureInput->setTexture(splineTex);
|
||||
splineTex->setParent(material);
|
||||
materialRef.append(material);
|
||||
|
||||
model->splineModel = splineModel;
|
||||
|
||||
if (auto series = qobject_cast<QSpline3DSeries *>(model->series)) {
|
||||
connect(series,
|
||||
&QSpline3DSeries::splineTensionChanged,
|
||||
this,
|
||||
&QQuickGraphsScatter::handleSplineChanged);
|
||||
connect(series,
|
||||
&QSpline3DSeries::splineKnottingChanged,
|
||||
this,
|
||||
&QQuickGraphsScatter::handleSplineChanged);
|
||||
connect(series,
|
||||
&QSpline3DSeries::splineLoopingChanged,
|
||||
this,
|
||||
&QQuickGraphsScatter::handleSplineChanged);
|
||||
connect(series,
|
||||
&QSpline3DSeries::splineColorChanged,
|
||||
this,
|
||||
&QQuickGraphsScatter::handleSplineChanged);
|
||||
connect(series,
|
||||
&QSpline3DSeries::splineResolutionChanged,
|
||||
this,
|
||||
&QQuickGraphsScatter::handleSplineChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void QQuickGraphsScatter::handleSplineChanged()
|
||||
{
|
||||
m_isDataDirty = true;
|
||||
}
|
||||
|
||||
QQuick3DModel *QQuickGraphsScatter::selected() const
|
||||
{
|
||||
return m_selected;
|
||||
|
|
@ -1640,8 +1813,10 @@ void QQuickGraphsScatter::updateGraph()
|
|||
}
|
||||
}
|
||||
|
||||
if (isDataDirty() || isSeriesVisualsDirty())
|
||||
if (isDataDirty() || isSeriesVisualsDirty()) {
|
||||
updateScatterGraphItemPositions(graphModel);
|
||||
updateSpline(graphModel);
|
||||
}
|
||||
|
||||
if (isSeriesVisualsDirty() || (graphModel->instancing && graphModel->instancing->isDirty()))
|
||||
updateScatterGraphItemVisuals(graphModel);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "qquickgraphsitem_p.h"
|
||||
#include "qscatter3dseries.h"
|
||||
#include "qspline3dseries.h"
|
||||
#include "qvalue3daxis.h"
|
||||
#include <private/scatterinstancing_p.h>
|
||||
|
||||
|
|
@ -148,6 +149,12 @@ private:
|
|||
QList<InsertRemoveRecord> m_insertRemoveRecords;
|
||||
bool m_recordInsertsAndRemoves;
|
||||
|
||||
struct SplineVertex
|
||||
{
|
||||
QVector3D position;
|
||||
QVector2D uv;
|
||||
};
|
||||
|
||||
struct ScatterModel
|
||||
{
|
||||
QList<QQuick3DModel *> dataItems;
|
||||
|
|
@ -162,6 +169,8 @@ private:
|
|||
ScatterInstancing *instancing = nullptr;
|
||||
QQuick3DModel *instancingRootItem = nullptr;
|
||||
QQuick3DModel *selectionIndicator = nullptr;
|
||||
|
||||
QQuick3DModel *splineModel = nullptr;
|
||||
};
|
||||
|
||||
float m_maxItemSize = 0.0f;
|
||||
|
|
@ -235,6 +244,10 @@ private:
|
|||
void updatePointScaleSize();
|
||||
void calculatePolarXZ(const float posX, const float posZ, float &x, float &z) const;
|
||||
|
||||
void updateSpline(ScatterModel *model);
|
||||
void createSplineModel(ScatterModel *model);
|
||||
void handleSplineChanged();
|
||||
|
||||
void generatePointsForScatterModel(ScatterModel *series);
|
||||
void updateScatterGraphItemPositions(ScatterModel *graphModel);
|
||||
void updateScatterGraphItemVisuals(ScatterModel *graphModel);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <QtCore/QMetaMethod>
|
||||
#include "qquickgraphssplineseries_p.h"
|
||||
#include "utils_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QQuickGraphsSpline3DSeries::QQuickGraphsSpline3DSeries(QObject *parent)
|
||||
: QSpline3DSeries(parent)
|
||||
{}
|
||||
|
||||
QQuickGraphsSpline3DSeries::~QQuickGraphsSpline3DSeries() {}
|
||||
|
||||
QQmlListProperty<QObject> QQuickGraphsSpline3DSeries::seriesChildren()
|
||||
{
|
||||
return QQmlListProperty<QObject>(this,
|
||||
this,
|
||||
&QQuickGraphsSpline3DSeries::appendSeriesChildren,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
||||
void QQuickGraphsSpline3DSeries::appendSeriesChildren(QQmlListProperty<QObject> *list,
|
||||
QObject *element)
|
||||
{
|
||||
QScatterDataProxy *proxy = qobject_cast<QScatterDataProxy *>(element);
|
||||
if (proxy)
|
||||
reinterpret_cast<QQuickGraphsSpline3DSeries *>(list->data)->setDataProxy(proxy);
|
||||
}
|
||||
|
||||
void QQuickGraphsSpline3DSeries::setBaseGradient(QQuickGradient *gradient)
|
||||
{
|
||||
if (m_baseGradient != gradient) {
|
||||
setGradientHelper(gradient, m_baseGradient, GradientType::Base);
|
||||
m_baseGradient = gradient;
|
||||
Q_EMIT baseGradientChanged(m_baseGradient);
|
||||
}
|
||||
}
|
||||
|
||||
QQuickGradient *QQuickGraphsSpline3DSeries::baseGradient() const
|
||||
{
|
||||
return m_baseGradient;
|
||||
}
|
||||
|
||||
void QQuickGraphsSpline3DSeries::setSingleHighlightGradient(QQuickGradient *gradient)
|
||||
{
|
||||
if (m_singleHighlightGradient != gradient) {
|
||||
setGradientHelper(gradient, m_singleHighlightGradient, GradientType::Single);
|
||||
m_singleHighlightGradient = gradient;
|
||||
Q_EMIT singleHighlightGradientChanged(m_singleHighlightGradient);
|
||||
}
|
||||
}
|
||||
|
||||
QQuickGradient *QQuickGraphsSpline3DSeries::singleHighlightGradient() const
|
||||
{
|
||||
return m_singleHighlightGradient;
|
||||
}
|
||||
|
||||
void QQuickGraphsSpline3DSeries::setMultiHighlightGradient(QQuickGradient *gradient)
|
||||
{
|
||||
if (m_multiHighlightGradient != gradient) {
|
||||
setGradientHelper(gradient, m_multiHighlightGradient, GradientType::Multi);
|
||||
m_multiHighlightGradient = gradient;
|
||||
Q_EMIT multiHighlightGradientChanged(m_multiHighlightGradient);
|
||||
}
|
||||
}
|
||||
|
||||
QQuickGradient *QQuickGraphsSpline3DSeries::multiHighlightGradient() const
|
||||
{
|
||||
return m_multiHighlightGradient;
|
||||
}
|
||||
|
||||
int QQuickGraphsSpline3DSeries::invalidSelectionIndex() const
|
||||
{
|
||||
return QSpline3DSeries::invalidSelectionIndex();
|
||||
}
|
||||
|
||||
void QQuickGraphsSpline3DSeries::handleBaseGradientUpdate()
|
||||
{
|
||||
if (!m_baseGradient)
|
||||
Utils::setSeriesGradient(this, m_baseGradient, GradientType::Base);
|
||||
}
|
||||
|
||||
void QQuickGraphsSpline3DSeries::handleSingleHighlightGradientUpdate()
|
||||
{
|
||||
if (!m_singleHighlightGradient)
|
||||
Utils::setSeriesGradient(this, m_singleHighlightGradient, GradientType::Single);
|
||||
}
|
||||
|
||||
void QQuickGraphsSpline3DSeries::handleMultiHighlightGradientUpdate()
|
||||
{
|
||||
if (!m_multiHighlightGradient)
|
||||
Utils::setSeriesGradient(this, m_multiHighlightGradient, GradientType::Multi);
|
||||
}
|
||||
|
||||
void QQuickGraphsSpline3DSeries::setGradientHelper(QQuickGradient *newGradient,
|
||||
QQuickGradient *memberGradient,
|
||||
GradientType type)
|
||||
{
|
||||
if (memberGradient)
|
||||
QObject::disconnect(memberGradient, 0, this, 0);
|
||||
Utils::setSeriesGradient(this, newGradient, type);
|
||||
memberGradient = newGradient;
|
||||
if (memberGradient) {
|
||||
switch (type) {
|
||||
case GradientType::Base:
|
||||
QObject::connect(memberGradient,
|
||||
&QQuickGradient::updated,
|
||||
this,
|
||||
&QQuickGraphsSpline3DSeries::handleBaseGradientUpdate);
|
||||
break;
|
||||
case GradientType::Single:
|
||||
QObject::connect(memberGradient,
|
||||
&QQuickGradient::updated,
|
||||
this,
|
||||
&QQuickGraphsSpline3DSeries::handleSingleHighlightGradientUpdate);
|
||||
break;
|
||||
case GradientType::Multi:
|
||||
QObject::connect(memberGradient,
|
||||
&QQuickGradient::updated,
|
||||
this,
|
||||
&QQuickGraphsSpline3DSeries::handleMultiHighlightGradientUpdate);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the QtGraphs API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
#ifndef QQUICKGRAPHSSPLINESERIES_P_H
|
||||
#define QQUICKGRAPHSSPLINESERIES_P_H
|
||||
|
||||
#include "theme/qquickgraphscolor_p.h"
|
||||
#include <qspline3dseries.h>
|
||||
|
||||
#include <QtQml/qqml.h>
|
||||
#include <QtQuick/private/qquickrectangle_p.h>
|
||||
#include <private/qgraphsglobal_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QQuickGraphsSpline3DSeries : public QSpline3DSeries
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QQmlListProperty<QObject> seriesChildren READ seriesChildren CONSTANT)
|
||||
Q_PROPERTY(QQuickGradient *baseGradient READ baseGradient WRITE setBaseGradient NOTIFY
|
||||
baseGradientChanged FINAL)
|
||||
Q_PROPERTY(QQuickGradient *singleHighlightGradient READ singleHighlightGradient WRITE
|
||||
setSingleHighlightGradient NOTIFY singleHighlightGradientChanged FINAL)
|
||||
Q_PROPERTY(QQuickGradient *multiHighlightGradient READ multiHighlightGradient WRITE
|
||||
setMultiHighlightGradient NOTIFY multiHighlightGradientChanged FINAL)
|
||||
// This is static method in parent class, overload as constant property for qml.
|
||||
Q_PROPERTY(int invalidSelectionIndex READ invalidSelectionIndex CONSTANT)
|
||||
Q_CLASSINFO("DefaultProperty", "seriesChildren")
|
||||
|
||||
QML_ADDED_IN_VERSION(6, 9)
|
||||
QML_NAMED_ELEMENT(Spline3DSeries)
|
||||
|
||||
public:
|
||||
QQuickGraphsSpline3DSeries(QObject *parent = 0);
|
||||
~QQuickGraphsSpline3DSeries() override;
|
||||
|
||||
QQmlListProperty<QObject> seriesChildren();
|
||||
static void appendSeriesChildren(QQmlListProperty<QObject> *list, QObject *element);
|
||||
|
||||
void setBaseGradient(QQuickGradient *gradient);
|
||||
QQuickGradient *baseGradient() const;
|
||||
void setSingleHighlightGradient(QQuickGradient *gradient);
|
||||
QQuickGradient *singleHighlightGradient() const;
|
||||
void setMultiHighlightGradient(QQuickGradient *gradient);
|
||||
QQuickGradient *multiHighlightGradient() const;
|
||||
|
||||
int invalidSelectionIndex() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void handleBaseGradientUpdate();
|
||||
void handleSingleHighlightGradientUpdate();
|
||||
void handleMultiHighlightGradientUpdate();
|
||||
|
||||
Q_SIGNALS:
|
||||
void baseGradientChanged(QQuickGradient *gradient);
|
||||
void singleHighlightGradientChanged(QQuickGradient *gradient);
|
||||
void multiHighlightGradientChanged(QQuickGradient *gradient);
|
||||
|
||||
private:
|
||||
QQuickGradient *m_baseGradient = nullptr;
|
||||
QQuickGradient *m_singleHighlightGradient = nullptr;
|
||||
QQuickGradient *m_multiHighlightGradient = nullptr;
|
||||
|
||||
void setGradientHelper(QQuickGradient *newGradient,
|
||||
QQuickGradient *memberGradient,
|
||||
GradientType type);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtQuick3D
|
||||
import QtQuick
|
||||
|
||||
CustomMaterial {
|
||||
property TextureInput controlPoints: TextureInput {}
|
||||
property int points
|
||||
|
||||
property real tension
|
||||
property real knotting
|
||||
property bool loop
|
||||
property color color
|
||||
|
||||
shadingMode: CustomMaterial.Shaded
|
||||
|
||||
vertexShader: "qrc:/shaders/splinevert"
|
||||
fragmentShader: "qrc:/shaders/splinefrag"
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ if(QT_FEATURE_graphs_3d_scatter3d)
|
|||
add_subdirectory(qgscatter-proxy)
|
||||
add_subdirectory(qgscatter-modelproxy)
|
||||
add_subdirectory(qgscatter-series)
|
||||
add_subdirectory(qgspline-series)
|
||||
endif()
|
||||
if(QT_FEATURE_graphs_3d_surface3d)
|
||||
add_subdirectory(qgsurface)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
# Copyright (C) 2024 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
qt_internal_add_test(tst_qgspline_series
|
||||
SOURCES
|
||||
tst_series.cpp
|
||||
INCLUDE_DIRECTORIES
|
||||
../common
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
Qt::Graphs
|
||||
Qt::GraphsWidgets
|
||||
)
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include <QtGraphs/QSpline3DSeries>
|
||||
|
||||
class tst_series : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void construct();
|
||||
|
||||
void initialProperties();
|
||||
void initializeProperties();
|
||||
|
||||
private:
|
||||
QSpline3DSeries *m_series;
|
||||
};
|
||||
|
||||
void tst_series::initTestCase() {}
|
||||
|
||||
void tst_series::cleanupTestCase() {}
|
||||
|
||||
void tst_series::init()
|
||||
{
|
||||
m_series = new QSpline3DSeries();
|
||||
}
|
||||
|
||||
void tst_series::cleanup()
|
||||
{
|
||||
delete m_series;
|
||||
}
|
||||
|
||||
void tst_series::construct()
|
||||
{
|
||||
QSpline3DSeries *series = new QSpline3DSeries();
|
||||
QVERIFY(series);
|
||||
delete series;
|
||||
|
||||
QScatterDataProxy *proxy = new QScatterDataProxy();
|
||||
|
||||
series = new QSpline3DSeries(proxy);
|
||||
QVERIFY(series);
|
||||
QCOMPARE(series->dataProxy(), proxy);
|
||||
delete series;
|
||||
}
|
||||
|
||||
void tst_series::initialProperties()
|
||||
{
|
||||
QVERIFY(m_series);
|
||||
|
||||
//Scatter properties
|
||||
QVERIFY(m_series->dataProxy());
|
||||
QCOMPARE(m_series->itemSize(), 0.0f);
|
||||
QCOMPARE(m_series->selectedItem(), m_series->invalidSelectionIndex());
|
||||
|
||||
// Common properties. The ones identical between different series are tested in QBar3DSeries tests
|
||||
QCOMPARE(m_series->itemLabelFormat(), QString("@xLabel, @yLabel, @zLabel"));
|
||||
QCOMPARE(m_series->mesh(), QAbstract3DSeries::Mesh::Sphere);
|
||||
QCOMPARE(m_series->type(), QAbstract3DSeries::SeriesType::Scatter);
|
||||
|
||||
//Spline properties
|
||||
QCOMPARE(m_series->isSplineVisible(), true);
|
||||
QCOMPARE(m_series->isSplineLooping(), false);
|
||||
QCOMPARE(m_series->splineTension(), 0.0f);
|
||||
QCOMPARE(m_series->splineKnotting(), 0.5f);
|
||||
QCOMPARE(m_series->splineColor(), QColor(255, 0, 0));
|
||||
QCOMPARE(m_series->splineResolution(), 10);
|
||||
}
|
||||
|
||||
void tst_series::initializeProperties()
|
||||
{
|
||||
QVERIFY(m_series);
|
||||
|
||||
//Scatter properties
|
||||
m_series->setDataProxy(new QScatterDataProxy());
|
||||
m_series->setItemSize(0.5f);
|
||||
m_series->setSelectedItem(0);
|
||||
|
||||
QCOMPARE(m_series->itemSize(), 0.5f);
|
||||
QCOMPARE(m_series->selectedItem(), 0);
|
||||
|
||||
// Common properties. The ones identical between different series are tested in QBar3DSeries tests
|
||||
m_series->setMesh(QAbstract3DSeries::Mesh::Point);
|
||||
m_series->setMeshRotation(QQuaternion(1, 1, 10, 20));
|
||||
|
||||
QCOMPARE(m_series->mesh(), QAbstract3DSeries::Mesh::Point);
|
||||
QCOMPARE(m_series->meshRotation(), QQuaternion(1, 1, 10, 20));
|
||||
|
||||
//spline properties
|
||||
m_series->setSplineVisible(false);
|
||||
m_series->setSplineLooping(true);
|
||||
m_series->setSplineTension(1.0f);
|
||||
m_series->setSplineKnotting(1.0f);
|
||||
m_series->setSplineColor(QColor(0, 255, 0));
|
||||
m_series->setSplineResolution(5);
|
||||
|
||||
QCOMPARE(m_series->isSplineVisible(), false);
|
||||
QCOMPARE(m_series->isSplineLooping(), true);
|
||||
QCOMPARE(m_series->splineTension(), 1.0f);
|
||||
QCOMPARE(m_series->splineKnotting(), 1.0f);
|
||||
QCOMPARE(m_series->splineColor(), QColor(0, 255, 0));
|
||||
QCOMPARE(m_series->splineResolution(), 5);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_series)
|
||||
#include "tst_series.moc"
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtGraphs
|
||||
import QtTest 1.0
|
||||
|
||||
Item {
|
||||
id: top
|
||||
height: 150
|
||||
width: 150
|
||||
|
||||
Spline3DSeries {
|
||||
id: initial
|
||||
}
|
||||
|
||||
Gradient {
|
||||
id: gradient1;
|
||||
stops: [
|
||||
GradientStop { color: "red"; position: 0 },
|
||||
GradientStop { color: "blue"; position: 1 }
|
||||
]
|
||||
}
|
||||
|
||||
Gradient {
|
||||
id: gradient2;
|
||||
stops: [
|
||||
GradientStop { color: "green"; position: 0 },
|
||||
GradientStop { color: "red"; position: 1 }
|
||||
]
|
||||
}
|
||||
|
||||
Gradient {
|
||||
id: gradient3;
|
||||
stops: [
|
||||
GradientStop { color: "gray"; position: 0 },
|
||||
GradientStop { color: "darkgray"; position: 1 }
|
||||
]
|
||||
}
|
||||
|
||||
Spline3DSeries {
|
||||
id: initialized
|
||||
dataProxy: ItemModelScatterDataProxy {
|
||||
itemModel: ListModel {
|
||||
ListElement{ xPos: "2.754"; yPos: "1.455"; zPos: "3.362"; }
|
||||
ListElement{ xPos: "3.164"; yPos: "2.022"; zPos: "4.348"; }
|
||||
}
|
||||
xPosRole: "xPos"
|
||||
yPosRole: "yPos"
|
||||
zPosRole: "zPos"
|
||||
}
|
||||
itemSize: 0.5
|
||||
selectedItem: 0
|
||||
|
||||
baseColor: "blue"
|
||||
baseGradient: gradient1
|
||||
colorStyle: GraphsTheme.ColorStyle.ObjectGradient
|
||||
itemLabelFormat: "%f"
|
||||
itemLabelVisible: false
|
||||
mesh: Abstract3DSeries.Mesh.Minimal
|
||||
meshRotation: Qt.quaternion(1, 1, 1, 1)
|
||||
meshSmooth: true
|
||||
multiHighlightColor: "green"
|
||||
multiHighlightGradient: gradient2
|
||||
name: "series1"
|
||||
singleHighlightColor: "red"
|
||||
singleHighlightGradient: gradient3
|
||||
userDefinedMesh: ":/customitem.obj"
|
||||
visible: false
|
||||
|
||||
//spline properties
|
||||
splineVisible: false
|
||||
splineLooping: true
|
||||
splineTension: 1.0
|
||||
splineKnotting: 1.0
|
||||
splineColor: "blue"
|
||||
splineResolution: 5
|
||||
}
|
||||
|
||||
ItemModelScatterDataProxy {
|
||||
id: proxy1
|
||||
itemModel: ListModel {
|
||||
ListElement{ xPos: "2.754"; yPos: "1.455"; zPos: "3.362"; }
|
||||
ListElement{ xPos: "3.164"; yPos: "2.022"; zPos: "4.348"; }
|
||||
ListElement{ xPos: "4.564"; yPos: "1.865"; zPos: "1.346"; }
|
||||
}
|
||||
xPosRole: "xPos"
|
||||
yPosRole: "yPos"
|
||||
zPosRole: "zPos"
|
||||
}
|
||||
|
||||
Spline3DSeries {
|
||||
id: change
|
||||
dataProxy: proxy1
|
||||
}
|
||||
|
||||
Spline3DSeries {
|
||||
id: invalid
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "Spline3DSeries Initial"
|
||||
|
||||
function test_1_initial() {
|
||||
compare(initial.dataProxy.itemCount, 0)
|
||||
compare(initial.invalidSelectionIndex, -1)
|
||||
compare(initial.itemSize, 0.0)
|
||||
compare(initial.selectedItem, -1)
|
||||
|
||||
//Spline properties
|
||||
compare(initial.splineVisible, true)
|
||||
compare(initial.splineLooping, false)
|
||||
compare(initial.splineTension, 0.0)
|
||||
compare(initial.splineKnotting, 0.5)
|
||||
compare(initial.splineColor, "#ff0000")
|
||||
compare(initial.splineResolution, 10)
|
||||
}
|
||||
|
||||
function test_2_initial_common() {
|
||||
// Common properties
|
||||
compare(initial.baseColor, "#000000")
|
||||
verify(!initial.baseGradient)
|
||||
compare(initial.colorStyle, GraphsTheme.ColorStyle.Uniform)
|
||||
compare(initial.itemLabel, "")
|
||||
compare(initial.itemLabelFormat, "@xLabel, @yLabel, @zLabel")
|
||||
compare(initial.itemLabelVisible, true)
|
||||
compare(initial.mesh, Abstract3DSeries.Mesh.Sphere)
|
||||
compare(initial.meshRotation, Qt.quaternion(1, 0, 0, 0))
|
||||
compare(initial.meshSmooth, false)
|
||||
compare(initial.multiHighlightColor, "#000000")
|
||||
verify(!initial.multiHighlightGradient)
|
||||
compare(initial.name, "")
|
||||
compare(initial.singleHighlightColor, "#000000")
|
||||
verify(!initial.singleHighlightGradient)
|
||||
compare(initial.type, Abstract3DSeries.SeriesType.Scatter)
|
||||
compare(initial.userDefinedMesh, "")
|
||||
compare(initial.visible, true)
|
||||
}
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "Spline3DSeries Initialized"
|
||||
|
||||
function test_1_initialized() {
|
||||
compare(initialized.dataProxy.itemCount, 2)
|
||||
compare(initialized.itemSize, 0.5)
|
||||
compare(initialized.selectedItem, 0)
|
||||
|
||||
//spline properties
|
||||
compare(initialized.splineVisible, false)
|
||||
compare(initialized.splineLooping, true)
|
||||
compare(initialized.splineTension, 1.0)
|
||||
compare(initialized.splineKnotting, 1.0)
|
||||
compare(initialized.splineColor, "#0000ff")
|
||||
compare(initialized.splineResolution, 5)
|
||||
}
|
||||
|
||||
function test_2_initialized_common() {
|
||||
// Common properties
|
||||
compare(initialized.baseColor, "#0000ff")
|
||||
compare(initialized.baseGradient, gradient1)
|
||||
compare(initialized.colorStyle, GraphsTheme.ColorStyle.ObjectGradient)
|
||||
compare(initialized.itemLabelFormat, "%f")
|
||||
compare(initialized.itemLabelVisible, false)
|
||||
compare(initialized.mesh, Abstract3DSeries.Mesh.Minimal)
|
||||
compare(initialized.meshRotation, Qt.quaternion(1, 1, 1, 1))
|
||||
compare(initialized.meshSmooth, true)
|
||||
compare(initialized.multiHighlightColor, "#008000")
|
||||
compare(initialized.multiHighlightGradient, gradient2)
|
||||
compare(initialized.name, "series1")
|
||||
compare(initialized.singleHighlightColor, "#ff0000")
|
||||
compare(initialized.singleHighlightGradient, gradient3)
|
||||
compare(initialized.userDefinedMesh, ":/customitem.obj")
|
||||
compare(initialized.visible, false)
|
||||
}
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "Spline3DSeries Change"
|
||||
|
||||
function test_1_change() {
|
||||
change.itemSize = 0.5
|
||||
change.selectedItem = 0
|
||||
|
||||
//spline properties
|
||||
change.splineVisible = false
|
||||
change.splineLooping = true
|
||||
change.splineTension = 1.0
|
||||
change.splineKnotting = 1.0
|
||||
change.splineColor = "green"
|
||||
change.splineResolution = 15
|
||||
}
|
||||
|
||||
function test_2_test_change() {
|
||||
// This test has a dependency to the previous one due to asynchronous item model resolving
|
||||
compare(change.dataProxy.itemCount, 3)
|
||||
compare(change.itemSize, 0.5)
|
||||
compare(change.selectedItem, 0)
|
||||
|
||||
//spline properties
|
||||
compare(change.splineVisible, false)
|
||||
compare(change.splineLooping, true)
|
||||
compare(change.splineTension, 1.0)
|
||||
compare(change.splineKnotting, 1.0)
|
||||
compare(change.splineColor, "#008000")
|
||||
compare(change.splineResolution, 15)
|
||||
}
|
||||
|
||||
function test_3_change_common() {
|
||||
change.baseColor = "blue"
|
||||
change.baseGradient = gradient1
|
||||
change.colorStyle = GraphsTheme.ColorStyle.ObjectGradient
|
||||
change.itemLabelFormat = "%f"
|
||||
change.itemLabelVisible = false
|
||||
change.mesh = Abstract3DSeries.Mesh.Minimal
|
||||
change.meshRotation = Qt.quaternion(1, 1, 1, 1)
|
||||
change.meshSmooth = true
|
||||
change.multiHighlightColor = "green"
|
||||
change.multiHighlightGradient = gradient2
|
||||
change.name = "series1"
|
||||
change.singleHighlightColor = "red"
|
||||
change.singleHighlightGradient = gradient3
|
||||
change.userDefinedMesh = ":/customitem.obj"
|
||||
change.visible = false
|
||||
|
||||
compare(change.baseColor, "#0000ff")
|
||||
compare(change.baseGradient, gradient1)
|
||||
compare(change.colorStyle, GraphsTheme.ColorStyle.ObjectGradient)
|
||||
compare(change.itemLabelFormat, "%f")
|
||||
compare(change.itemLabelVisible, false)
|
||||
compare(change.mesh, Abstract3DSeries.Mesh.Minimal)
|
||||
compare(change.meshRotation, Qt.quaternion(1, 1, 1, 1))
|
||||
compare(change.meshSmooth, true)
|
||||
compare(change.multiHighlightColor, "#008000")
|
||||
compare(change.multiHighlightGradient, gradient2)
|
||||
compare(change.name, "series1")
|
||||
compare(change.singleHighlightColor, "#ff0000")
|
||||
compare(change.singleHighlightGradient, gradient3)
|
||||
compare(change.userDefinedMesh, ":/customitem.obj")
|
||||
compare(change.visible, false)
|
||||
}
|
||||
|
||||
function test_4_change_gradient_stop() {
|
||||
gradient1.stops[0].color = "yellow"
|
||||
compare(change.baseGradient.stops[0].color, "#ffff00")
|
||||
}
|
||||
}
|
||||
TestCase {
|
||||
name: "Spline3DSeries Invalid"
|
||||
|
||||
function test_invalid() {
|
||||
invalid.itemSize = -1.0
|
||||
compare(invalid.itemSize, 0.0)
|
||||
invalid.itemSize = 1.1
|
||||
compare(invalid.itemSize, 0.0)
|
||||
|
||||
invalid.splineTension = -1.0
|
||||
compare(invalid.splineTension, 0.0)
|
||||
invalid.splineKnotting = -1.0
|
||||
compare(invalid.splineKnotting, 0.5)
|
||||
invalid.splineResolution = 1
|
||||
compare(invalid.splineResolution, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ if(QT_FEATURE_graphs_3d)
|
|||
if(QT_FEATURE_graphs_3d_scatter3d)
|
||||
add_subdirectory(qmlcustominput)
|
||||
add_subdirectory(qmldynamicdata)
|
||||
add_subdirectory(qmlspline)
|
||||
endif()
|
||||
if(QT_FEATURE_graphs_3d_surface3d)
|
||||
add_subdirectory(qmlgradient)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
# Copyright (C) 2024 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
qt_internal_add_manual_test(tst_qmlspline
|
||||
GUI
|
||||
SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_sources(tst_qmlspline
|
||||
PRIVATE
|
||||
splinegen.h splinegen.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tst_qmlspline PUBLIC
|
||||
Qt::Gui
|
||||
Qt::Graphs
|
||||
)
|
||||
|
||||
set(qmlspline_resource_files
|
||||
"qml/qmlspline/main.qml"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qmlspline "qmlspline"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${qmlspline_resource_files}
|
||||
)
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "splinegen.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtQml/QQmlEngine>
|
||||
#include <QtQuick/QQuickView>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QQuickView viewer;
|
||||
|
||||
// The following are needed to make examples run without having to install the
|
||||
// module in desktop environments.
|
||||
#ifdef Q_OS_WIN
|
||||
QString extraImportPath(QStringLiteral("%1/../../../%2"));
|
||||
#else
|
||||
QString extraImportPath(QStringLiteral("%1/../../%2"));
|
||||
#endif
|
||||
viewer.engine()->addImportPath(extraImportPath.arg(
|
||||
QGuiApplication::applicationDirPath(), QString::fromLatin1("qml")));
|
||||
QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer,
|
||||
&QWindow::close);
|
||||
|
||||
SplineGen splineGen;
|
||||
viewer.rootContext()->setContextProperty("splineGen", &splineGen);
|
||||
|
||||
viewer.setTitle(QStringLiteral("QML Scatter spline"));
|
||||
viewer.setSource(QUrl("qrc:/qml/qmlspline/main.qml"));
|
||||
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
viewer.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls.Fusion
|
||||
import QtQuick3D.Helpers
|
||||
import QtQuick.Dialogs
|
||||
import QtGraphs
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: mainView
|
||||
width: 1280
|
||||
height: 1024
|
||||
|
||||
|
||||
Scatter3D {
|
||||
id: scatterGraph
|
||||
property int splineType: 0
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
aspectRatio: 1
|
||||
axisX.min: -1.2
|
||||
axisX.max: 1.2
|
||||
axisZ.min: -1.2
|
||||
axisZ.max: 1.2
|
||||
theme: GraphsTheme {
|
||||
theme: GraphsTheme.Theme.QtGreen
|
||||
colorStyle: GraphsTheme.ColorStyle.Uniform
|
||||
}
|
||||
|
||||
Spline3DSeries {
|
||||
id: splineSeries
|
||||
|
||||
splineVisible: true
|
||||
splineTension: tensionSlider.value
|
||||
splineKnotting: knottingSlider.value
|
||||
splineLooping: looping.checked
|
||||
splineResolution: resolution.value
|
||||
itemSize: pointSizeSlider.value
|
||||
baseColor: "white"
|
||||
|
||||
Component.onCompleted: splineGen.generateSpline(splineSeries, scatterGraph.splineType, points.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RowLayout {
|
||||
id: settings
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
||||
ComboBox {
|
||||
id: splineTypeBox
|
||||
model: ["Circle","Helix", "Stitch curve"]
|
||||
onActivated: {
|
||||
scatterGraph.splineType = currentIndex
|
||||
splineGen.generateSpline(splineSeries, scatterGraph.splineType, points.value)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: liveToggle
|
||||
text: liveTimer.running? "Static" : "Live"
|
||||
onClicked: liveTimer.running = !liveTimer.running
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Text {
|
||||
text: qsTr("Number of points")
|
||||
color: "white"
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
id: points
|
||||
from: 0
|
||||
value: 64
|
||||
to: 80
|
||||
editable: true
|
||||
onValueChanged: splineGen.generateSpline(splineSeries, scatterGraph.splineType, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
Text {
|
||||
text: qsTr("Point size")
|
||||
color: "white"
|
||||
}
|
||||
Slider {
|
||||
id: pointSizeSlider
|
||||
from: 0.001
|
||||
to: 0.1
|
||||
value: 0.001
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Text {
|
||||
text: qsTr("Spline color")
|
||||
color: "white"
|
||||
}
|
||||
|
||||
Button{
|
||||
height: 25
|
||||
width: 25
|
||||
|
||||
onClicked: splineCol.open()
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
color: splineSeries.splineColor
|
||||
}
|
||||
}
|
||||
|
||||
ColorDialog {
|
||||
id: splineCol
|
||||
selectedColor: splineSeries.splineColor
|
||||
onAccepted: splineSeries.splineColor = selectedColor
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Text {
|
||||
text: qsTr("Spline resolution")
|
||||
color: "white"
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
id: resolution
|
||||
from: 2
|
||||
value: 10
|
||||
to: 30
|
||||
editable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: splineSettings
|
||||
anchors.top: settings.bottom
|
||||
|
||||
ColumnLayout {
|
||||
Text {
|
||||
id: tensionText
|
||||
text: qsTr("Tension")
|
||||
color: "white"
|
||||
}
|
||||
Slider {
|
||||
id: tensionSlider
|
||||
from: 0
|
||||
to: 1
|
||||
value: 0
|
||||
stepSize: 0.1
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Text {
|
||||
id: knottingText
|
||||
color: "white"
|
||||
text: qsTr("Knotting")
|
||||
}
|
||||
Slider {
|
||||
id: knottingSlider
|
||||
from: 0
|
||||
to: 1
|
||||
value: 0.5
|
||||
stepSize: 0.1
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Text {
|
||||
id: loopingText
|
||||
text: qsTr("Looping")
|
||||
color: "white"
|
||||
}
|
||||
CheckBox {
|
||||
id: looping
|
||||
checked: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: liveTimer
|
||||
interval: 1000 / 60
|
||||
repeat: true
|
||||
running: false
|
||||
onTriggered: splineGen.tickSpline(splineSeries)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
#include "splinegen.h"
|
||||
|
||||
SplineGen::SplineGen(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
qRegisterMetaType<QScatter3DSeries *>();
|
||||
}
|
||||
|
||||
SplineGen::~SplineGen()
|
||||
{
|
||||
m_series->deleteLater();
|
||||
m_splineCache.clear();
|
||||
}
|
||||
|
||||
void SplineGen::generateSpline(QScatter3DSeries *series, SplineType type, int points)
|
||||
{
|
||||
if (m_series != series)
|
||||
m_series = series;
|
||||
|
||||
switch (type) {
|
||||
case (SplineType::Circle):
|
||||
genCircle(points);
|
||||
break;
|
||||
case (SplineType::Helix):
|
||||
genHelix(points);
|
||||
break;
|
||||
case (SplineType::Stitch):
|
||||
genStitch(points);
|
||||
break;
|
||||
}
|
||||
series->dataProxy()->resetArray(m_splineCache.at(0));
|
||||
}
|
||||
|
||||
void SplineGen::tickSpline(QScatter3DSeries *series)
|
||||
{
|
||||
if (!series || series->dataProxy()->itemCount() == 0)
|
||||
return;
|
||||
|
||||
static int index = 0;
|
||||
int points = series->dataProxy()->itemCount();
|
||||
|
||||
QScatterDataArray newArray;
|
||||
newArray.reserve(points);
|
||||
|
||||
const QScatterDataArray &cache = m_splineCache.at(index);
|
||||
for (int i = 0; i < points; i++) {
|
||||
newArray.append(cache.at(i));
|
||||
}
|
||||
|
||||
series->dataProxy()->resetArray(newArray);
|
||||
index++;
|
||||
if (index >= m_cacheCount)
|
||||
index = 0;
|
||||
}
|
||||
|
||||
void SplineGen::genCircle(int points)
|
||||
{
|
||||
for (int i = 0; i < m_splineCache.size(); i++) {
|
||||
QScatterDataArray &array = m_splineCache[i];
|
||||
array.clear();
|
||||
}
|
||||
|
||||
m_splineCache.resize(m_cacheCount);
|
||||
for (int i = 0; i < m_cacheCount; i++) {
|
||||
QScatterDataArray &array = m_splineCache[i];
|
||||
array.reserve(points);
|
||||
float offset = 2 * M_PI * i / m_cacheCount;
|
||||
for (int j = 0; j < points; j++) {
|
||||
float t = 2 * M_PI * j / points;
|
||||
float x = qCos(t + offset);
|
||||
float y = 0.5;
|
||||
float z = qSin(t + offset);
|
||||
array.append(QScatterDataItem(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SplineGen::genHelix(int points)
|
||||
{
|
||||
for (int i = 0; i < m_splineCache.size(); i++) {
|
||||
QScatterDataArray &array = m_splineCache[i];
|
||||
array.clear();
|
||||
}
|
||||
|
||||
// create cache array;
|
||||
|
||||
m_splineCache.resize(m_cacheCount);
|
||||
for (int i = 0; i < m_cacheCount; i++) {
|
||||
QScatterDataArray &array = m_splineCache[i];
|
||||
array.reserve(points);
|
||||
float offset = 2 * M_PI * i / m_cacheCount;
|
||||
for (int j = 0; j < points; j++) {
|
||||
//8 points per revolution
|
||||
|
||||
float t = 4 * M_PI * j / points;
|
||||
float x = qCos(t + offset);
|
||||
float y = t;
|
||||
float z = qSin(t + offset);
|
||||
array.append(QScatterDataItem(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SplineGen::genStitch(int points)
|
||||
{
|
||||
for (int i = 0; i < m_splineCache.size(); i++) {
|
||||
QScatterDataArray &array = m_splineCache[i];
|
||||
array.clear();
|
||||
}
|
||||
m_splineCache.resize(m_cacheCount);
|
||||
for (int i = 0; i < m_cacheCount; i++) {
|
||||
QScatterDataArray &array = m_splineCache[i];
|
||||
|
||||
float t = qSin(2 * M_PI * float(i) / float(m_cacheCount));
|
||||
// round to closest fitting amount
|
||||
int pointsPerAxis = int(qFloor(float(points) / 3.0));
|
||||
int rounds = int(qCeil(float(pointsPerAxis) / 2.0));
|
||||
array.reserve(pointsPerAxis * 3 + rounds);
|
||||
const QVector3D masks[3] = {QVector3D(t, 1, 0), QVector3D(1, 0, t), QVector3D(0, t, 1)};
|
||||
for (int j = 1; j <= rounds; j++) {
|
||||
bool top = true;
|
||||
for (int k = 0; k < 7; k++) {
|
||||
float value = top ? pointsPerAxis + 1 - j : j;
|
||||
value /= pointsPerAxis;
|
||||
top = !top;
|
||||
QVector3D p = masks[k % 3] * value;
|
||||
array.append(QScatterDataItem(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
#ifndef SPLINEGEN_H
|
||||
#define SPLINEGEN_H
|
||||
|
||||
#include <QtGraphs>
|
||||
|
||||
enum class SplineType {
|
||||
Circle,
|
||||
Helix,
|
||||
Stitch,
|
||||
};
|
||||
class SplineGen : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_ENUM(SplineType)
|
||||
public:
|
||||
SplineGen(QObject *parent = 0);
|
||||
~SplineGen() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void generateSpline(QScatter3DSeries *series, SplineType type, int points);
|
||||
void tickSpline(QScatter3DSeries *series);
|
||||
|
||||
private:
|
||||
void genCircle(int points);
|
||||
void genHelix(int points);
|
||||
void genStitch(int points);
|
||||
int m_cacheCount = 60;
|
||||
|
||||
QScatter3DSeries *m_series = nullptr;
|
||||
QList<QScatterDataArray> m_splineCache;
|
||||
};
|
||||
|
||||
#endif // SPLINEGEN_H
|
||||
Loading…
Reference in New Issue