├── .clang-format ├── .gitignore ├── .gitmodules ├── .qmake.conf ├── 3rdparty ├── CMakeLists.txt └── qnanopainter.qbs ├── CMakeLists.txt ├── LICENSE ├── README.md ├── examples ├── CMakeLists.txt ├── examples.qbs ├── line │ ├── CMakeLists.txt │ ├── line.pro │ ├── line.qbs │ ├── line.qrc │ ├── main.cpp │ └── main.qml └── plots │ ├── CMakeLists.txt │ ├── main.cpp │ ├── plots.pro │ ├── plots.qbs │ ├── plots.qrc │ └── qml │ ├── BarPlot.qml │ ├── LinePlot.qml │ ├── ScatterPlot.qml │ ├── SplinePlot.qml │ └── main.qml ├── qnite.prf ├── qnite.qbs ├── qnite_examples.pro ├── src ├── CMakeLists.txt ├── items │ ├── qniteartistpainter.h │ ├── qnitebar.cpp │ ├── qnitebar.h │ ├── qnitebarpainter.cpp │ ├── qnitebarpainter.h │ ├── qnitecircle.cpp │ ├── qnitecircle.h │ ├── qnitecirclepainter.cpp │ ├── qnitecirclepainter.h │ ├── qnitegrid.cpp │ ├── qnitegrid.h │ ├── qnitegridpainter.cpp │ ├── qnitegridpainter.h │ ├── qniteline.cpp │ ├── qniteline.h │ ├── qnitelinepainter.cpp │ ├── qnitelinepainter.h │ ├── qnitepen.cpp │ ├── qnitepen.h │ ├── qnitespline.cpp │ └── qnitespline.h ├── qml │ └── Qnite │ │ ├── Axes.qml │ │ ├── AxisLabel.qml │ │ ├── Figure.qml │ │ └── qmldir ├── qnite.h ├── qnite.qrc ├── qniteartist.cpp ├── qniteartist.h ├── qniteaxes.cpp ├── qniteaxes.h ├── qniteaxis.cpp ├── qniteaxis.h ├── qniteaxistick.cpp ├── qniteaxistick.h ├── qnitecategoryaxis.cpp ├── qnitecategoryaxis.h ├── qniteclipper.cpp ├── qniteclipper.h ├── qnitelinearaxis.cpp ├── qnitelinearaxis.h ├── qnitelinearmapper.cpp ├── qnitelinearmapper.h ├── qnitelinearticker.cpp ├── qnitelinearticker.h ├── qnitemapper.cpp ├── qnitemapper.h ├── qniteticker.cpp ├── qniteticker.h ├── qnitexyartist.cpp ├── qnitexyartist.h ├── src.qbs └── tools │ ├── qnitepathpainter.cpp │ ├── qnitepathpainter.h │ ├── qnitepathselectiontool.cpp │ ├── qnitepathselectiontool.h │ ├── qnitepointselectiontool.cpp │ ├── qnitepointselectiontool.h │ ├── qniteselectiontool.cpp │ ├── qniteselectiontool.h │ ├── qnitetool.cpp │ ├── qnitetool.h │ ├── qnitezoompainter.cpp │ ├── qnitezoompainter.h │ ├── qnitezoomtool.cpp │ └── qnitezoomtool.h └── tests ├── CMakeLists.txt ├── tests.qbs ├── tst_qniteartist ├── CMakeLists.txt ├── tst_qniteartist.cpp └── tst_qniteartist.qbs ├── tst_qniteaxes ├── CMakeLists.txt ├── tst_qniteaxes.cpp └── tst_qniteaxes.qbs ├── tst_qnitecircle ├── CMakeLists.txt ├── data │ └── tst_qnitecircle.qml ├── tst_qnitecircle.cpp └── tst_qnitecircle.qbs ├── tst_qniteclipper ├── CMakeLists.txt ├── tst_qniteclipper.cpp └── tst_qniteclipper.qbs ├── tst_qnitelinearmapper ├── CMakeLists.txt ├── tst_qnitelinearmapper.cpp └── tst_qnitelinearmapper.qbs ├── tst_qnitelinearticker ├── CMakeLists.txt ├── tst_qnitelinearticker.cpp └── tst_qnitelinearticker.qbs ├── tst_qnitemapper ├── CMakeLists.txt ├── tst_qnitemapper.cpp └── tst_qnitemapper.qbs ├── tst_qnitepathselectiontool ├── CMakeLists.txt ├── data │ └── tst_pathselectiontool.qml ├── tst_qnitepathselectiontool.cpp └── tst_qnitepathselectiontool.qbs ├── tst_qniteselectiontool ├── CMakeLists.txt ├── tst_qniteselectiontool.cpp └── tst_qniteselectiontool.qbs ├── tst_qniteticker ├── CMakeLists.txt ├── tst_qniteticker.cpp └── tst_qniteticker.qbs ├── tst_qnitetool ├── CMakeLists.txt ├── tst_qnitetool.cpp └── tst_qnitetool.qbs └── tst_qnitezoomtool ├── CMakeLists.txt ├── data └── tst_qnitezoomtool.qml ├── tst_qnitezoomtool.cpp └── tst_qnitezoomtool.qbs /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: MultiLine 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakInheritanceList: BeforeColon 43 | BreakBeforeTernaryOperators: true 44 | BreakConstructorInitializersBeforeComma: false 45 | BreakConstructorInitializers: BeforeColon 46 | BreakAfterJavaFieldAnnotations: false 47 | BreakStringLiterals: true 48 | ColumnLimit: 80 49 | CommentPragmas: '^ IWYU pragma:' 50 | CompactNamespaces: false 51 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 52 | ConstructorInitializerIndentWidth: 4 53 | ContinuationIndentWidth: 4 54 | Cpp11BracedListStyle: true 55 | DerivePointerAlignment: false 56 | DisableFormat: false 57 | ExperimentalAutoDetectBinPacking: false 58 | FixNamespaceComments: true 59 | ForEachMacros: 60 | - foreach 61 | - Q_FOREACH 62 | - BOOST_FOREACH 63 | IncludeBlocks: Preserve 64 | IncludeCategories: 65 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 66 | Priority: 2 67 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 68 | Priority: 3 69 | - Regex: '.*' 70 | Priority: 1 71 | IncludeIsMainRegex: '(Test)?$' 72 | IndentCaseLabels: false 73 | IndentPPDirectives: None 74 | IndentWidth: 2 75 | IndentWrappedFunctionNames: false 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: true 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: None 83 | ObjCBinPackProtocolList: Auto 84 | ObjCBlockIndentWidth: 2 85 | ObjCSpaceAfterProperty: false 86 | ObjCSpaceBeforeProtocolList: true 87 | PenaltyBreakAssignment: 2 88 | PenaltyBreakBeforeFirstCallParameter: 19 89 | PenaltyBreakComment: 300 90 | PenaltyBreakFirstLessLess: 120 91 | PenaltyBreakString: 1000 92 | PenaltyBreakTemplateDeclaration: 10 93 | PenaltyExcessCharacter: 1000000 94 | PenaltyReturnTypeOnItsOwnLine: 60 95 | PointerAlignment: Right 96 | ReflowComments: true 97 | SortIncludes: true 98 | SortUsingDeclarations: true 99 | SpaceAfterCStyleCast: false 100 | SpaceAfterTemplateKeyword: true 101 | SpaceBeforeAssignmentOperators: true 102 | SpaceBeforeCpp11BracedList: false 103 | SpaceBeforeCtorInitializerColon: true 104 | SpaceBeforeInheritanceColon: true 105 | SpaceBeforeParens: ControlStatements 106 | SpaceBeforeRangeBasedForLoopColon: true 107 | SpaceInEmptyParentheses: false 108 | SpacesBeforeTrailingComments: 1 109 | SpacesInAngles: false 110 | SpacesInContainerLiterals: true 111 | SpacesInCStyleCastParentheses: false 112 | SpacesInParentheses: false 113 | SpacesInSquareBrackets: false 114 | Standard: Cpp11 115 | TabWidth: 8 116 | UseTab: Never 117 | ... 118 | 119 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | *.qmlc 23 | moc_*.cpp 24 | qrc_*.cpp 25 | ui_*.h 26 | Makefile* 27 | *-build-* 28 | 29 | # QtCreator 30 | 31 | *.autosave 32 | 33 | #QtCtreator Qml 34 | *.qmlproject.user 35 | *.qmlproject.user.* 36 | 37 | # Vim YCM 38 | compile_commands.json 39 | .ycm_extra_conf.py 40 | 41 | # other 42 | *.pyc 43 | .tmp 44 | 45 | build/ 46 | 47 | default/ 48 | debug/ 49 | release/ 50 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/qnanopainter"] 2 | path = 3rdparty/qnanopainter 3 | url = git@github.com:QUItCoding/qnanopainter.git 4 | -------------------------------------------------------------------------------- /.qmake.conf: -------------------------------------------------------------------------------- 1 | QNITE_SOURCE_TREE = $$PWD 2 | 3 | exists($$PWD/../.qmake.conf) { 4 | include($$PWD/../.qmake.conf) 5 | } 6 | -------------------------------------------------------------------------------- /3rdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(qnanopainter) 2 | 3 | find_package( 4 | Qt5 5 | COMPONENTS 6 | Quick 7 | REQUIRED 8 | ) 9 | 10 | set(CMAKE_AUTOMOC ON) 11 | set(CMAKE_AUTORCC ON) 12 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 13 | 14 | add_library( 15 | qnanopainter 16 | STATIC 17 | qnanopainter/libqnanopainter/qnanopainter.cpp 18 | qnanopainter/libqnanopainter/qnanocolor.cpp 19 | qnanopainter/libqnanopainter/qnanolineargradient.cpp 20 | qnanopainter/libqnanopainter/qnanoimagepattern.cpp 21 | qnanopainter/libqnanopainter/qnanoimage.cpp 22 | qnanopainter/libqnanopainter/qnanofont.cpp 23 | qnanopainter/libqnanopainter/qnanoradialgradient.cpp 24 | qnanopainter/libqnanopainter/qnanoboxgradient.cpp 25 | qnanopainter/libqnanopainter/qnanowindow.cpp 26 | qnanopainter/libqnanopainter/private/qnanodebug.cpp 27 | qnanopainter/libqnanopainter/qnanoquickitem.cpp 28 | qnanopainter/libqnanopainter/qnanoquickitempainter.cpp 29 | qnanopainter/libqnanopainter/nanovg/nanovg.c 30 | $<$,$,$>:qnanopainter/libqnanopainter/private/qnanobackendgles2.cpp> 31 | $<$,$,$>:qnanopainter/libqnanopainter/private/qnanobackendgles3.cpp> 32 | $<$>:qnanopainter/libqnanopainter/private/qnanobackendgl2.cpp> 33 | $<$>:qnanopainter/libqnanopainter/private/qnanobackendgl3.cpp> 34 | 35 | qnanopainter/libqnanopainter/libqnanopainterdata.qrc 36 | ) 37 | 38 | target_compile_features( 39 | qnanopainter 40 | PUBLIC 41 | cxx_std_14 42 | ) 43 | 44 | target_compile_definitions( 45 | qnanopainter 46 | PRIVATE 47 | QNANO_QT_GL_INCLUDE 48 | QNANO_ENABLE_GLES3 49 | ) 50 | 51 | target_link_libraries( 52 | qnanopainter 53 | PRIVATE 54 | Qt5::Quick 55 | $<$:Qt5::Gui_GLESv2> 56 | $<$>:GL> 57 | ) 58 | 59 | target_include_directories( 60 | qnanopainter 61 | PUBLIC 62 | ${CMAKE_CURRENT_SOURCE_DIR}/qnanopainter/libqnanopainter 63 | ) 64 | -------------------------------------------------------------------------------- /3rdparty/qnanopainter.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | Project { 5 | 6 | StaticLibrary { 7 | name: "qnanopainter" 8 | 9 | Depends { name: "Qt.quick" } 10 | Depends { name: "cpp" } 11 | cpp.includePaths: [ 12 | "qnanopainter/libqnanopainter", 13 | ] 14 | cpp.cxxLanguageVersion: "c++14" 15 | cpp.defines: [ 16 | "QNANO_QT_GL_INCLUDE", 17 | "QNANO_ENABLE_GLES3", 18 | ] 19 | cpp.dynamicLibraries: { 20 | if (qbs.targetOS.contains("windows")) { 21 | if (qbs.debugInformation) { 22 | return ["libGLESV2d"]; 23 | } else { 24 | return ["libGLESV2"]; 25 | } 26 | } else { 27 | return ["GL"]; 28 | } 29 | } 30 | 31 | Group { 32 | files: [ 33 | "qnanopainter.cpp", 34 | "qnanocolor.cpp", 35 | "qnanolineargradient.cpp", 36 | "qnanoimagepattern.cpp", 37 | "qnanoimage.cpp", 38 | "qnanofont.cpp", 39 | "qnanoradialgradient.cpp", 40 | "qnanoboxgradient.cpp", 41 | "qnanowindow.cpp", 42 | "private/qnanodebug.cpp", 43 | "private/qnanobrush.h", 44 | "qnanopainter.h", 45 | "qnanocolor.h", 46 | "qnanolineargradient.h", 47 | "qnanoimagepattern.h", 48 | "qnanoimage.h", 49 | "qnanofont.h", 50 | "qnanoradialgradient.h", 51 | "qnanoboxgradient.h", 52 | "private/qnanodataelement.h", 53 | "private/qnanobackend.h", 54 | "private/qnanobackendfactory.h", 55 | "qnanowindow.h", 56 | "private/qnanodebug.h", 57 | 58 | // qt quick only dependency 59 | "qnanoquickitem.cpp", 60 | "qnanoquickitempainter.cpp", 61 | "qnanoquickitem.h", 62 | "qnanoquickitempainter.h", 63 | 64 | // nanovg 65 | "nanovg/nanovg.c", 66 | "nanovg/nanovg.h", 67 | 68 | // qrc font file 69 | "libqnanopainterdata.qrc" 70 | ] 71 | prefix: "qnanopainter/libqnanopainter/" 72 | } 73 | 74 | Group { 75 | condition: qbs.targetOS.containsAny(["android", "ios", "windows"]) 76 | files: [ 77 | "private/qnanobackendgles2.h", 78 | "private/qnanobackendgles3.h", 79 | "private/qnanobackendgles2.cpp", 80 | "private/qnanobackendgles3.cpp", 81 | ] 82 | prefix: "qnanopainter/libqnanopainter/" 83 | } 84 | 85 | Group { 86 | condition: qbs.targetOS.containsAny(["osx", "linux", "windows"]) && 87 | !qbs.targetOS.contains("android") 88 | files: [ 89 | "private/qnanobackendgl2.h", 90 | "private/qnanobackendgl3.h", 91 | "private/qnanobackendgl2.cpp", 92 | "private/qnanobackendgl3.cpp", 93 | ] 94 | prefix: "qnanopainter/libqnanopainter/" 95 | } 96 | 97 | Export { 98 | Depends { name: "Qt.quick" } 99 | Depends { name: "cpp" } 100 | cpp.includePaths: [ "qnanopainter/libqnanopainter" ] 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | project( 4 | qnite 5 | DESCRIPTION "A Qt/QML charting library" 6 | HOMEPAGE_URL "https://github.com/evonove/qnite" 7 | ) 8 | 9 | add_subdirectory(src) 10 | add_subdirectory(3rdparty) 11 | 12 | # Examples must be compiled explicitly by specifying the examples target 13 | add_subdirectory(examples EXCLUDE_FROM_ALL) 14 | 15 | if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) 16 | include(CTest) 17 | add_subdirectory(tests) 18 | endif() 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Evonove 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qnite 2 | 3 | *qnite* is a charting library that provides a really high level API (via QML) to customize and render **interactive** charts in 4 | your Qt applications. 5 | 6 | ## Building and Testing 7 | 8 | There are currently two supported build systems, Qbs and CMake. 9 | 10 | ### Qbs 11 | 12 | To use Qbs you must have configured it previously with `qbs setup-toolchains` and `qbs setup-qt`, see the official docs for more information. 13 | 14 | Assuming you have setup a Qt profile named `qt-5-9-8` you can start a build with: 15 | 16 | ``` 17 | mkdir build 18 | cd build 19 | qbs build profile:qt-5-9-8 -f .. 20 | ``` 21 | 22 | This will also build tests and examples. 23 | 24 | To only build the library you need to specify the product `qnite`: 25 | 26 | ``` 27 | qbs build -p qnite profile:qt-5-9-8 -f .. 28 | ``` 29 | 30 | To run tests or examples you need to specify the product: 31 | 32 | ``` 33 | qbs run -p plots 34 | qbs run -p tst_qniteartist 35 | ``` 36 | 37 | To know the list of runnable products: 38 | 39 | ``` 40 | qbs list-products profile:qt-5-9-8 -f .. 41 | ``` 42 | 43 | ### CMake 44 | 45 | To use CMake: 46 | 47 | ``` 48 | mkdir build 49 | cd build 50 | cmake .. 51 | cmake --build . 52 | ``` 53 | 54 | This will build only the library, examples are not built by default. 55 | 56 | In case you want to build the examples you must specify the target: 57 | 58 | ``` 59 | cmake --build . --target examples 60 | ``` 61 | 62 | Tests are run with CTest and `BUILD_TESTING` must be set at config time, to run them: 63 | 64 | ``` 65 | cmake -DBUILD_TESTING=ON .. 66 | cmake --build . 67 | ctest --output-on-failure 68 | ``` 69 | 70 | 71 | ## Roadmap 72 | 73 | * provide a step-by-step tutorial 74 | * provide the high-level API documentation for available charts type 75 | * provide a simple tutorial to implement your own chart using low-level components 76 | 77 | ## License 78 | qnite is released under the terms of the **MIT license**. Full details in ``LICENSE`` file. 79 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | add_custom_target( 4 | examples 5 | DEPENDS 6 | line 7 | plots 8 | ) 9 | 10 | add_subdirectory(line) 11 | add_subdirectory(plots) 12 | -------------------------------------------------------------------------------- /examples/examples.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | Project { 5 | references: [ 6 | "plots/plots.qbs", 7 | "line/line.qbs", 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/line/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(line) 2 | 3 | find_package( 4 | Qt5 5 | COMPONENTS 6 | Quick 7 | REQUIRED 8 | ) 9 | 10 | set(CMAKE_AUTOMOC ON) 11 | set(CMAKE_AUTORCC ON) 12 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 13 | 14 | add_executable( 15 | line 16 | main.cpp 17 | 18 | line.qrc 19 | ) 20 | 21 | target_compile_features( 22 | line 23 | PUBLIC 24 | cxx_std_14 25 | ) 26 | 27 | target_link_libraries( 28 | line 29 | PRIVATE 30 | qnite 31 | Qt5::Quick 32 | ) 33 | 34 | target_include_directories( 35 | line 36 | PRIVATE 37 | ${CMAKE_CURRENT_SOURCE_DIR} 38 | ) -------------------------------------------------------------------------------- /examples/line/line.pro: -------------------------------------------------------------------------------- 1 | QT += qml quick 2 | 3 | CONFIG += c++14 4 | 5 | include( ../../qnite.prf ) 6 | 7 | SOURCES += main.cpp 8 | RESOURCES += line.qrc 9 | 10 | TARGET = line 11 | TEMPLATE = app 12 | -------------------------------------------------------------------------------- /examples/line/line.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | Project { 5 | CppApplication { 6 | Depends { name: "qnite" } 7 | Depends { name: "Qt.quick" } 8 | 9 | files: [ 10 | "line.qrc", 11 | "main.cpp", 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/line/line.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/line/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | Q_INIT_RESOURCE(qnite); 8 | 9 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 10 | 11 | #ifdef Q_OS_WIN 12 | QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); 13 | #endif 14 | 15 | QGuiApplication app(argc, argv); 16 | 17 | QQmlApplicationEngine engine; 18 | engine.addImportPath(QStringLiteral("qrc:/qml")); 19 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 20 | 21 | return app.exec(); 22 | } 23 | -------------------------------------------------------------------------------- /examples/line/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.0 3 | import QtQuick.Layouts 1.3 4 | 5 | import Qnite 1.0 6 | 7 | 8 | ApplicationWindow { 9 | width: 1600; height: 900 10 | visible: true 11 | 12 | ColumnLayout { 13 | anchors.fill: parent 14 | 15 | RowLayout 16 | { 17 | CheckBox { 18 | id: _drawSymbols 19 | text: "Draw Symbols" 20 | } 21 | 22 | CheckBox { 23 | id: _drawStepped 24 | text: "Draw Stepped line" 25 | } 26 | } 27 | 28 | Figure { 29 | axes.yBounds: [-0.2, 0.65] 30 | axes.xBounds: [0.0001, 0.0006] 31 | 32 | Grid { } 33 | Line { 34 | xValues: [0.0001, 0.00013, 0.0003, 0.0004, 0.00052] 35 | yValues: [0, 0.3, 0.6, 0.3, 0.4, 0.6] 36 | 37 | drawSymbols: _drawSymbols.checked 38 | drawStepped: _drawStepped.checked 39 | } 40 | 41 | Layout.fillWidth: true 42 | Layout.fillHeight: true 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /examples/plots/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(plots) 2 | 3 | find_package( 4 | Qt5 5 | COMPONENTS 6 | Quick 7 | REQUIRED 8 | ) 9 | 10 | set(CMAKE_AUTOMOC ON) 11 | set(CMAKE_AUTORCC ON) 12 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 13 | 14 | add_executable( 15 | plots 16 | main.cpp 17 | 18 | plots.qrc 19 | ) 20 | 21 | target_compile_features( 22 | plots 23 | PUBLIC 24 | cxx_std_14 25 | ) 26 | 27 | target_link_libraries( 28 | plots 29 | PRIVATE 30 | qnite 31 | Qt5::Quick 32 | ) 33 | 34 | target_include_directories( 35 | plots 36 | PRIVATE 37 | ${CMAKE_CURRENT_SOURCE_DIR} 38 | ) -------------------------------------------------------------------------------- /examples/plots/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | Q_INIT_RESOURCE(qnite); 8 | 9 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 10 | 11 | #ifdef Q_OS_WIN 12 | QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); 13 | #endif 14 | 15 | QGuiApplication app(argc, argv); 16 | 17 | QQmlApplicationEngine engine; 18 | engine.addImportPath(QStringLiteral("qrc:/qml")); 19 | engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); 20 | 21 | return app.exec(); 22 | } 23 | -------------------------------------------------------------------------------- /examples/plots/plots.pro: -------------------------------------------------------------------------------- 1 | QT += qml quick 2 | 3 | CONFIG += c++14 4 | 5 | include( ../../qnite.prf ) 6 | 7 | SOURCES += main.cpp 8 | RESOURCES += plots.qrc 9 | 10 | TARGET = plots 11 | TEMPLATE = app 12 | -------------------------------------------------------------------------------- /examples/plots/plots.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | Project { 5 | CppApplication { 6 | Depends { name: "qnite" } 7 | Depends { name: "Qt.quick" } 8 | 9 | files: [ 10 | "plots.qrc", 11 | "main.cpp", 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/plots/plots.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | qml/main.qml 4 | qml/BarPlot.qml 5 | qml/LinePlot.qml 6 | qml/ScatterPlot.qml 7 | qml/SplinePlot.qml 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/plots/qml/BarPlot.qml: -------------------------------------------------------------------------------- 1 | import Qnite 1.0 2 | 3 | Figure { 4 | axes.axisX: CategoryAxis { 5 | values: ["a", "b", "c", "d"] 6 | } 7 | axes.yBounds: [-10, 10] 8 | 9 | axes.yAxisName: "Y" 10 | axes.xAxisName: "X" 11 | 12 | axes.xLabelsColor: "navy" 13 | axes.yLabelsColor: "navy" 14 | 15 | axes.backgroundColor: "ivory" 16 | 17 | tools: [ 18 | PointSelectionTool { 19 | anchors.fill: parent // TODO: remove boilerplate 20 | } 21 | ] 22 | 23 | Grid { } 24 | Bar { 25 | categories: ["a", "b", "c", "e"] 26 | yValues: [-9, 3, 5, -1] 27 | 28 | pen.fill: "red" 29 | selectedPen.fill: "green" 30 | selectable: true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/plots/qml/LinePlot.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.0 2 | import Qnite 1.0 3 | 4 | Figure { 5 | axes.yBounds: [-0.2, 0.65] 6 | axes.xBounds: [0.0001, 0.0006] 7 | 8 | tools: [ 9 | ZoomTool { 10 | id: zoom 11 | anchors.fill: parent 12 | } 13 | ] 14 | 15 | Grid { 16 | Button { 17 | anchors.top: parent.top 18 | anchors.right: parent.right 19 | 20 | text: "Reset Zoom" 21 | 22 | onClicked: zoom.reset() 23 | } 24 | } 25 | Line { 26 | xValues: [0.0001, 0.00013, 0.0003, 0.0004, 0.00052] 27 | yValues: [0, 0.3, 0.6, 0.3, 0.4, 0.6] 28 | pen.stroke: "#3f51b5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/plots/qml/ScatterPlot.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.0 2 | import QtQuick 2.6 3 | import Qnite 1.0 4 | 5 | Figure { 6 | axes.yBounds: [2.5, 5.8] 7 | axes.xBounds: [0, 10] 8 | 9 | tools: [ 10 | PathSelectionTool { 11 | anchors.fill: parent 12 | }, 13 | ZoomTool { 14 | id: zoom 15 | anchors.fill: parent 16 | } 17 | ] 18 | 19 | Grid { 20 | Button { 21 | anchors.top: parent.top 22 | anchors.right: parent.right 23 | 24 | text: "Reset Zoom" 25 | 26 | onClicked: zoom.reset() 27 | } 28 | } 29 | Circle { 30 | id: circle 31 | selectable: true 32 | pen { 33 | fill: "#aa3f51b5" 34 | radius: 5 35 | } 36 | selectedPen { 37 | fill: "#aab01325" 38 | radius: 6 39 | } 40 | } 41 | 42 | Component.onCompleted: { 43 | var n = 100000; 44 | var xx = []; 45 | var yy = []; 46 | for (var i = 0; i < n; ++i) { 47 | xx.push(Math.random() * 40); 48 | yy.push(Math.random() * 40); 49 | } 50 | circle.xValues = xx; 51 | circle.yValues = yy; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/plots/qml/SplinePlot.qml: -------------------------------------------------------------------------------- 1 | import Qnite 1.0 2 | 3 | Figure { 4 | axes.xBounds: [-5, 10] 5 | axes.yBounds: [-2, 10.8] 6 | 7 | tools: [ 8 | PointSelectionTool { 9 | anchors.fill: parent // TODO: remove boilerplate 10 | } 11 | ] 12 | 13 | Grid { drawYAxes: false } 14 | Spline { 15 | xValues: [-5, -1.5, -0.1, .4, 2.3, 3.4, 5, 6, 9, 10] 16 | yValues: [-3, 0, 2, -1.3, .9, -.2, -.6, 4, 5, 9] 17 | 18 | pen { 19 | stroke: "#3f51b5" 20 | fill: "#99ec407a" 21 | } 22 | } 23 | 24 | Spline { 25 | xValues: [-4.8, -2, .5, 2, 3, 4, 8, 10] 26 | yValues: [ 3, 1, -2, 6, 3, 4, 10, 4] 27 | pen.stroke: "#345f6b" 28 | 29 | interpolation: Spline.CatmullRom 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/plots/qml/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Window 2.2 2 | import QtQuick.Layouts 1.3 3 | 4 | import Qnite 1.0 5 | 6 | 7 | Window { 8 | width: 1600; height: 900 9 | visible: true 10 | 11 | GridLayout { 12 | columns: 2 13 | columnSpacing: 10 14 | rowSpacing: 10 15 | 16 | anchors{ 17 | fill: parent 18 | margins: 10 19 | } 20 | 21 | // bar plot 22 | BarPlot { 23 | Layout.fillWidth: true 24 | Layout.fillHeight: true 25 | } 26 | 27 | // filled curves 28 | LinePlot { 29 | Layout.fillWidth: true 30 | Layout.fillHeight: true 31 | } 32 | 33 | SplinePlot { 34 | Layout.fillWidth: true 35 | Layout.fillHeight: true 36 | } 37 | 38 | ScatterPlot { 39 | Layout.fillWidth: true 40 | Layout.fillHeight: true 41 | } 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /qnite.prf: -------------------------------------------------------------------------------- 1 | 2 | 3 | include ( 3rdparty/qnanopainter/libqnanopainter/include.pri ) 4 | 5 | CONFIG += c++14 6 | QT += qml quick 7 | 8 | INCLUDEPATH += $$PWD/src/ 9 | INCLUDEPATH += $$PWD/src/items 10 | INCLUDEPATH += $$PWD/src/tools 11 | 12 | #DEFINES += QNANO_USE_RENDERNODE # Qt 5.8 and up 13 | 14 | SOURCES += $$PWD/src/qniteartist.cpp \ 15 | $$PWD/src/qnitexyartist.cpp \ 16 | $$PWD/src/qniteaxis.cpp \ 17 | $$PWD/src/qnitelinearaxis.cpp \ 18 | $$PWD/src/qnitecategoryaxis.cpp \ 19 | $$PWD/src/qniteaxistick.cpp \ 20 | $$PWD/src/qniteaxes.cpp \ 21 | $$PWD/src/qniteclipper.cpp \ 22 | $$PWD/src/qnitemapper.cpp \ 23 | $$PWD/src/qnitelinearmapper.cpp \ 24 | $$PWD/src/qniteticker.cpp \ 25 | $$PWD/src/qnitelinearticker.cpp \ 26 | $$PWD/src/items/qnitebar.cpp \ 27 | $$PWD/src/items/qnitebarpainter.cpp \ 28 | $$PWD/src/items/qnitecircle.cpp \ 29 | $$PWD/src/items/qnitecirclepainter.cpp \ 30 | $$PWD/src/items/qnitegrid.cpp \ 31 | $$PWD/src/items/qnitegridpainter.cpp \ 32 | $$PWD/src/items/qniteline.cpp \ 33 | $$PWD/src/items/qnitelinepainter.cpp \ 34 | $$PWD/src/items/qnitepen.cpp \ 35 | $$PWD/src/items/qnitespline.cpp \ 36 | $$PWD/src/tools/qnitetool.cpp \ 37 | $$PWD/src/tools/qniteselectiontool.cpp \ 38 | $$PWD/src/tools/qnitepointselectiontool.cpp \ 39 | $$PWD/src/tools/qnitepathselectiontool.cpp \ 40 | $$PWD/src/tools/qnitepathpainter.cpp \ 41 | $$PWD/src/tools/qnitezoomtool.cpp \ 42 | $$PWD/src/tools/qnitezoompainter.cpp \ 43 | 44 | HEADERS += $$PWD/src/qnite.h \ 45 | $$PWD/src/qniteartist.h \ 46 | $$PWD/src/qnitexyartist.h \ 47 | $$PWD/src/qniteaxis.h \ 48 | $$PWD/src/qnitelinearaxis.h \ 49 | $$PWD/src/qnitecategoryaxis.h \ 50 | $$PWD/src/qniteaxistick.h \ 51 | $$PWD/src/qniteaxes.h \ 52 | $$PWD/src/qniteclipper.h \ 53 | $$PWD/src/qnitemapper.h \ 54 | $$PWD/src/qnitelinearmapper.h \ 55 | $$PWD/src/qniteticker.h \ 56 | $$PWD/src/qnitelinearticker.h \ 57 | $$PWD/src/items/qnitebar.h \ 58 | $$PWD/src/items/qnitebarpainter.h \ 59 | $$PWD/src/items/qnitecircle.h \ 60 | $$PWD/src/items/qnitecirclepainter.h \ 61 | $$PWD/src/items/qniteline.h \ 62 | $$PWD/src/items/qnitegrid.h \ 63 | $$PWD/src/items/qnitegridpainter.h \ 64 | $$PWD/src/items/qnitelinepainter.h \ 65 | $$PWD/src/items/qnitespline.h \ 66 | $$PWD/src/items/qnitepen.h \ 67 | $$PWD/src/tools/qnitetool.h \ 68 | $$PWD/src/tools/qniteselectiontool.h \ 69 | $$PWD/src/tools/qnitepointselectiontool.h \ 70 | $$PWD/src/tools/qnitepathselectiontool.h \ 71 | $$PWD/src/tools/qnitepathpainter.h \ 72 | $$PWD/src/tools/qnitezoomtool.h \ 73 | $$PWD/src/tools/qnitezoompainter.h \ 74 | 75 | RESOURCES += $$PWD/src/qnite.qrc 76 | 77 | -------------------------------------------------------------------------------- /qnite.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | Project { 5 | minimumQbsVersion: "1.7.1" 6 | 7 | property bool withTests: true 8 | 9 | property bool withExamples: true 10 | 11 | SubProject { 12 | filePath: "src/src.qbs" 13 | } 14 | 15 | SubProject { 16 | filePath: "3rdparty/qnanopainter.qbs" 17 | } 18 | 19 | SubProject { 20 | filePath: "tests/tests.qbs" 21 | Properties { 22 | condition: parent.withTests 23 | } 24 | } 25 | 26 | SubProject { 27 | filePath: "examples/examples.qbs" 28 | Properties { 29 | condition: parent.withExamples 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /qnite_examples.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += \ 4 | examples/line \ 5 | examples/plots \ 6 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Quick 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_library( 13 | qnite 14 | STATIC 15 | qniteartist.cpp 16 | qnitexyartist.cpp 17 | qniteaxis.cpp 18 | qnitelinearaxis.cpp 19 | qnitecategoryaxis.cpp 20 | qniteaxistick.cpp 21 | qniteaxes.cpp 22 | qniteclipper.cpp 23 | qnitemapper.cpp 24 | qnitelinearmapper.cpp 25 | qniteticker.cpp 26 | qnitelinearticker.cpp 27 | items/qnitebar.cpp 28 | items/qnitebarpainter.cpp 29 | items/qnitecircle.cpp 30 | items/qnitecirclepainter.cpp 31 | items/qnitegrid.cpp 32 | items/qnitegridpainter.cpp 33 | items/qniteline.cpp 34 | items/qnitelinepainter.cpp 35 | items/qnitepen.cpp 36 | items/qnitespline.cpp 37 | tools/qnitetool.cpp 38 | tools/qniteselectiontool.cpp 39 | tools/qnitepointselectiontool.cpp 40 | tools/qnitepathselectiontool.cpp 41 | tools/qnitepathpainter.cpp 42 | tools/qnitezoomtool.cpp 43 | tools/qnitezoompainter.cpp 44 | 45 | qnite.qrc 46 | ) 47 | 48 | target_link_libraries( 49 | qnite 50 | PUBLIC 51 | Qt5::Quick 52 | qnanopainter 53 | ) 54 | 55 | target_compile_features( 56 | qnite 57 | PUBLIC 58 | cxx_std_14 59 | ) 60 | 61 | target_include_directories( 62 | qnite 63 | PUBLIC 64 | ${CMAKE_CURRENT_SOURCE_DIR} 65 | ${CMAKE_CURRENT_SOURCE_DIR}/tools 66 | ${CMAKE_CURRENT_SOURCE_DIR}/items 67 | ) -------------------------------------------------------------------------------- /src/items/qniteartistpainter.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITEARTISTPAINTER_H 2 | #define QNITEARTISTPAINTER_H 3 | 4 | #include "qnanoquickitempainter.h" 5 | 6 | template 7 | class QniteArtistPainter : public QNanoQuickItemPainter { 8 | public: 9 | QniteArtistPainter(); 10 | 11 | protected: 12 | virtual void synchronize(QNanoQuickItem *item) Q_DECL_OVERRIDE; 13 | 14 | private: 15 | }; 16 | 17 | #endif /* QNITEARTISTPAINTER_H */ 18 | -------------------------------------------------------------------------------- /src/items/qnitebar.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitebar.h" 2 | #include "qniteaxes.h" 3 | #include "qniteaxis.h" 4 | #include "qnitebarpainter.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | QniteBar::QniteBar(QQuickItem *parent) 11 | : QniteXYArtist(parent), m_fixedWidth{10}, m_leftPadding{0}, 12 | m_rightPadding{0}, m_selectedId{-1}, m_selectedIndex{-1}, m_labelAlign{ 13 | NONE} {} 14 | 15 | void QniteBar::setFixedWidth(qreal w) { 16 | if (m_fixedWidth != w) { 17 | m_fixedWidth = w; 18 | emit fixedWidthChanged(); 19 | 20 | update(); 21 | } 22 | } 23 | 24 | void QniteBar::setLeftPadding(qreal leftPadding) { 25 | 26 | if (m_leftPadding != leftPadding) { 27 | m_leftPadding = leftPadding; 28 | emit leftPaddingChanged(); 29 | 30 | update(); 31 | } 32 | } 33 | 34 | void QniteBar::setRightPadding(qreal rightPadding) { 35 | 36 | if (m_rightPadding != rightPadding) { 37 | m_rightPadding = rightPadding; 38 | emit rightPaddingChanged(); 39 | 40 | update(); 41 | } 42 | } 43 | 44 | void QniteBar::setCategories(const QStringList &c) { 45 | if (m_categories != c) { 46 | m_categories = c; 47 | emit categoriesChanged(); 48 | 49 | // updated the xValues according to the category list 50 | auto values = QList{}; 51 | auto step = 1.0 / (m_categories.size() + 1); 52 | for (auto i = 0; i < m_categories.size(); ++i) 53 | values.push_back(step * (i + 1)); 54 | setXValues(values); 55 | 56 | update(); 57 | } 58 | } 59 | 60 | void QniteBar::setLabelAlign(const LabelAlign &position) { 61 | if (m_labelAlign != position) { 62 | m_labelAlign = position; 63 | emit labelAlignChanged(); 64 | } 65 | } 66 | 67 | void QniteBar::setLabelsText(const QStringList &labels) { 68 | if (m_labelsText != labels) { 69 | m_labelsText = labels; 70 | emit labelsTextChanged(); 71 | } 72 | } 73 | 74 | bool QniteBar::select(QPoint p) { 75 | auto accepted = false; 76 | 77 | if (m_xMapped.size() < 1) { 78 | m_selectedId = -1; 79 | } else { 80 | // select the nearest bar 81 | auto upper = std::upper_bound(m_xMapped.cbegin(), m_xMapped.cend(), p.x()); 82 | auto selected = upper; 83 | if (upper != m_xMapped.cbegin() 84 | && std::fabs((upper-1).value() - p.x()) < std::fabs(upper.value() - p.x())) 85 | selected = upper-1; 86 | 87 | m_selectedId = selected.key(); 88 | m_selectedIndex = static_cast(std::distance(m_xMapped.cbegin(), selected)); 89 | 90 | accepted = true; 91 | } 92 | 93 | emit selectedChanged(); 94 | update(); 95 | 96 | return accepted; 97 | } 98 | 99 | bool QniteBar::select(const QList &path) { 100 | if (path.size()) { 101 | return select(path.first()); 102 | } 103 | 104 | return false; 105 | } 106 | 107 | void QniteBar::clearSelection() { 108 | m_selectedId = -1; 109 | m_selectedIndex = -1; 110 | emit selectedChanged(); 111 | update(); 112 | } 113 | 114 | QNanoQuickItemPainter *QniteBar::createItemPainter() const { 115 | return new QniteBarPainter; 116 | } 117 | 118 | bool QniteBar::isSelected() const { return m_selectedId >= 0; } 119 | -------------------------------------------------------------------------------- /src/items/qnitebar.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_BAR_H 2 | #define QNITE_BAR_H 3 | 4 | #include "qnitexyartist.h" 5 | #include 6 | 7 | class QniteBar : public QniteXYArtist { 8 | Q_OBJECT 9 | Q_PROPERTY(qreal fixedWidth READ fixedWidth WRITE setFixedWidth NOTIFY 10 | fixedWidthChanged) 11 | Q_PROPERTY(int selectedIndex READ selectedIndex) 12 | Q_PROPERTY(QStringList categories READ categories WRITE setCategories NOTIFY 13 | categoriesChanged) 14 | Q_PROPERTY(LabelAlign labelAlign READ labelAlign WRITE setLabelAlign NOTIFY 15 | labelAlignChanged) 16 | Q_PROPERTY(QStringList labelsText READ labelsText WRITE setLabelsText NOTIFY 17 | labelsTextChanged) 18 | Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding NOTIFY 19 | leftPaddingChanged) 20 | Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding NOTIFY 21 | rightPaddingChanged) 22 | 23 | public: 24 | explicit QniteBar(QQuickItem *parent = 0); 25 | virtual ~QniteBar() {} 26 | 27 | qreal fixedWidth() const { return m_fixedWidth; } 28 | void setFixedWidth(qreal w); 29 | 30 | qreal leftPadding() const { return m_leftPadding; } 31 | void setLeftPadding(qreal leftPadding); 32 | qreal rightPadding() const { return m_rightPadding; } 33 | void setRightPadding(qreal rightPadding); 34 | 35 | const QStringList &categories() const { return m_categories; } 36 | void setCategories(const QStringList &c); 37 | 38 | enum LabelAlign { 39 | NONE, 40 | TOP, 41 | }; 42 | Q_ENUM(LabelAlign) 43 | 44 | LabelAlign labelAlign() const { return m_labelAlign; }; 45 | void setLabelAlign(const LabelAlign &position); 46 | 47 | const QStringList &labelsText() const { return m_labelsText; }; 48 | void setLabelsText(const QStringList &lables); 49 | 50 | bool select(const QPoint) Q_DECL_OVERRIDE; 51 | bool select(const QList &) Q_DECL_OVERRIDE; 52 | void clearSelection() Q_DECL_OVERRIDE; 53 | 54 | int selectedIndex() const { return m_selectedIndex; } 55 | int selectedId() const { return m_selectedId; } 56 | 57 | QNanoQuickItemPainter *createItemPainter() const Q_DECL_OVERRIDE; 58 | 59 | Q_SIGNALS: 60 | void fixedWidthChanged(); 61 | void categoriesChanged(); 62 | void labelAlignChanged(); 63 | void labelsTextChanged(); 64 | void leftPaddingChanged(); 65 | void rightPaddingChanged(); 66 | 67 | protected: 68 | virtual bool isSelected() const Q_DECL_OVERRIDE; 69 | 70 | private: 71 | qreal m_fixedWidth; 72 | qreal m_leftPadding; 73 | qreal m_rightPadding; 74 | 75 | int m_selectedId; 76 | int m_selectedIndex; 77 | LabelAlign m_labelAlign; 78 | 79 | QStringList m_categories; 80 | QStringList m_labelsText; 81 | }; 82 | 83 | #endif // QNITE_BAR_H 84 | -------------------------------------------------------------------------------- /src/items/qnitebarpainter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitebarpainter.h" 4 | 5 | #include "qniteaxes.h" 6 | #include "qniteaxis.h" 7 | 8 | Q_LOGGING_CATEGORY(qnitebarpainter, "qnite.bar.painter") 9 | 10 | QniteBarPainter::QniteBarPainter() : QNanoQuickItemPainter{} {} 11 | 12 | void QniteBarPainter::synchronize(QNanoQuickItem *item) { 13 | qCDebug(qnitebarpainter) << "synchronizing qnitebar"; 14 | 15 | auto barItem = static_cast(item); 16 | if (barItem != Q_NULLPTR) { 17 | barItem->processData(); 18 | 19 | // TODO: here we could share painting data using a class 20 | // which is copied with all its members in a local instance. 21 | m_xs = barItem->xMapped(); 22 | m_ys = barItem->yMapped(); 23 | 24 | // make a local copy of the pen 25 | m_pen = barItem->pen()->data(); 26 | m_selectedPen = barItem->selectedPen()->data(); 27 | m_selected = barItem->selected(); 28 | 29 | // bar specific properties 30 | m_fixedWidth = barItem->fixedWidth(); 31 | m_leftPadding = barItem->leftPadding(); 32 | m_rightPadding = barItem->rightPadding(); 33 | m_selectedId = barItem->selectedId(); 34 | m_labelAlign = barItem->labelAlign(); 35 | m_labelsText = barItem->labelsText(); 36 | 37 | // the baseline is the position of the y axis 38 | // this is needed to compute a the starting point of the bar 39 | m_baseline = barItem->axes()->axisY()->position(); 40 | } 41 | } 42 | 43 | void QniteBarPainter::paint(QNanoPainter *painter) { 44 | qCDebug(qnitebarpainter) << "painting qnitebar"; 45 | 46 | auto dataSize = m_xs.size(); 47 | if (dataSize < 1) 48 | return; 49 | 50 | painter->setStrokeStyle(QNanoColor::fromQColor(m_pen.stroke)); 51 | painter->setFillStyle(QNanoColor::fromQColor(m_pen.fill)); 52 | painter->setLineWidth(m_pen.width); 53 | painter->setLineJoin(m_pen.join); 54 | painter->setLineCap(m_pen.cap); 55 | 56 | // draw unselected points 57 | auto ids = m_xs.keys(); 58 | for (auto id : ids) { 59 | // we do not draw selected ids because we draw it later 60 | // with a different pen 61 | if (id == m_selectedId) { 62 | continue; 63 | } 64 | 65 | drawBar(m_xs.value(id), m_ys.value(id)); 66 | } 67 | 68 | // Draw selected items only they're currently visible 69 | if (m_selectedId >= 0 && ids.contains(m_selectedId)) { 70 | // use the selectedPen 71 | painter->setStrokeStyle(QNanoColor::fromQColor(m_selectedPen.stroke)); 72 | painter->setFillStyle(QNanoColor::fromQColor(m_selectedPen.fill)); 73 | painter->setLineWidth(m_selectedPen.width); 74 | painter->setLineJoin(m_selectedPen.join); 75 | painter->setLineCap(m_selectedPen.cap); 76 | 77 | drawBar(m_xs.value(m_selectedId), m_ys.value(m_selectedId)); 78 | } 79 | 80 | // Draw some text inside bar items. The text is aligned in the position 81 | // passed as q_property. 82 | switch (m_labelAlign) { 83 | case QniteBar::NONE: 84 | break; 85 | case QniteBar::TOP: 86 | QNanoFont font(QNanoFont::DEFAULT_FONT_BOLD); 87 | font.setPixelSize(14); 88 | painter->setFont(font); 89 | painter->setTextAlign(QNanoPainter::ALIGN_CENTER); 90 | painter->setTextBaseline(QNanoPainter::BASELINE_TOP); 91 | painter->setPixelAlignText(QNanoPainter::PIXEL_ALIGN_FULL); 92 | 93 | for (auto id : ids) { 94 | auto height = m_ys.value(id) - m_baseline; 95 | 96 | // For now we want to see label in items that has an 97 | // height higher or lower to zero. 98 | if (!qFuzzyCompare(height, 0)) { 99 | drawText(m_xs.value(id), m_ys.value(id), m_labelsText.value(id)); 100 | } 101 | } 102 | break; 103 | } 104 | } 105 | 106 | void QniteBarPainter::drawBar(qreal x, qreal y) { 107 | auto cx = m_leftPadding + x - m_fixedWidth / 2.0; 108 | auto height = y - m_baseline; 109 | painter()->beginPath(); 110 | painter()->rect(cx, m_baseline, m_fixedWidth - m_rightPadding - m_leftPadding, 111 | height); 112 | painter()->fill(); 113 | painter()->stroke(); 114 | } 115 | 116 | void QniteBarPainter::drawText(qreal x, qreal y, const QString &text) { 117 | auto height = (y - m_baseline) / 2.0; 118 | 119 | painter()->beginPath(); 120 | m_pen.fill = QColor(255, 255, 255); 121 | painter()->setFillStyle(QNanoColor::fromQColor(m_pen.fill)); 122 | 123 | painter()->fillText(text, QRect(m_leftPadding + x - m_fixedWidth / 2.0, y, 124 | m_fixedWidth - m_rightPadding - m_leftPadding, 125 | height)); 126 | painter()->fill(); 127 | painter()->stroke(); 128 | } 129 | -------------------------------------------------------------------------------- /src/items/qnitebarpainter.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITEBARPAINTER_H 2 | #define QNITEBARPAINTER_H 3 | 4 | #include "qnanoquickitempainter.h" 5 | #include "qnitebar.h" 6 | #include "qnitepen.h" 7 | 8 | class QniteBarPainter : public QNanoQuickItemPainter { 9 | public: 10 | QniteBarPainter(); 11 | 12 | void synchronize(QNanoQuickItem *item) Q_DECL_OVERRIDE; 13 | void paint(QNanoPainter *painter) Q_DECL_OVERRIDE; 14 | 15 | private: 16 | void drawBar(qreal x, qreal y); 17 | void drawText(qreal x, qreal y, const QString &text); 18 | 19 | // base artist data 20 | bool m_selected; 21 | QnitePen::PenData m_pen; 22 | QnitePen::PenData m_selectedPen; 23 | 24 | // xy artist data 25 | QMap m_xs; 26 | QMap m_ys; 27 | 28 | // bar data 29 | qreal m_baseline; 30 | qreal m_fixedWidth; 31 | qreal m_leftPadding; 32 | qreal m_rightPadding; 33 | 34 | int m_selectedId; 35 | QniteBar::LabelAlign m_labelAlign; 36 | QStringList m_labelsText; 37 | }; 38 | 39 | #endif /* QNITEBARPAINTER_H */ 40 | -------------------------------------------------------------------------------- /src/items/qnitecircle.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitecircle.h" 2 | #include "qniteaxes.h" 3 | #include "qniteaxis.h" 4 | #include "qnitecirclepainter.h" 5 | #include "qniteclipper.h" 6 | #include "qnitemapper.h" 7 | 8 | #include 9 | #include 10 | 11 | Q_LOGGING_CATEGORY(qnitecircle, "qnite.circle") 12 | 13 | constexpr auto SELECTION_TOLERANCE = 20; 14 | 15 | QniteCircle::QniteCircle(QQuickItem *parent) 16 | : QniteXYArtist(parent), m_highlightedId{-1}, m_highlightedIndex{-1} { 17 | setFlag(ItemHasContents, true); 18 | setClipper(new QniteClipper(this)); 19 | } 20 | 21 | QniteCircle::~QniteCircle() {} 22 | 23 | bool QniteCircle::select(const QList &path) { 24 | // nothing to select 25 | if (path.isEmpty()) { 26 | return false; 27 | } 28 | 29 | // when there are less than 3 point we use the point selection 30 | // algorithm which should select the nearest point 31 | if (path.size() < 3) { 32 | return select(path.first()); 33 | } 34 | 35 | // create a polygon with the path so it is easier to check 36 | // if a point is in the path or not 37 | auto polygonPath = QPolygonF(QVector::fromList(path)); 38 | 39 | // cycle through all the points and save those contained 40 | // in the polygon 41 | m_selectedIds.clear(); 42 | auto ids = m_xMapped.keys(); 43 | for (auto id : ids) { 44 | QPointF cp(m_xMapped.value(id), m_yMapped.value(id)); 45 | 46 | if (polygonPath.containsPoint(cp, Qt::OddEvenFill)) { 47 | m_selectedIds << id; 48 | } 49 | } 50 | 51 | // no points have been selected 52 | if (m_selectedIds.isEmpty()) { 53 | return false; 54 | } 55 | 56 | emit selectedChanged(); 57 | update(); 58 | return true; 59 | } 60 | 61 | bool QniteCircle::select(const QPoint p) { 62 | // these variables are needed to keep 63 | // track of the nearest point during the search 64 | auto nearestId = -1; 65 | // we only evaluate points whose distance is below a 66 | // predefined tolerance 67 | qreal nearestDistance = SELECTION_TOLERANCE; 68 | 69 | auto ids = m_xMapped.keys(); 70 | auto index = 0; 71 | for (auto id : ids) { 72 | QPointF cp(m_xMapped.value(id), m_yMapped.value(id)); 73 | QPointF d = p - cp; 74 | auto distance = d.manhattanLength(); 75 | if (distance < nearestDistance) { 76 | nearestId = id; 77 | nearestDistance = distance; 78 | } 79 | index++; 80 | } 81 | 82 | // add the index to the selected points pool 83 | if (nearestId >= 0) { 84 | m_selectedIds.insert(nearestId); 85 | auto nearestIndex = m_xValues.keys().indexOf(nearestId); 86 | m_selectedIndexes.insert(nearestIndex); 87 | emit selectedChanged(); 88 | update(); 89 | return true; 90 | } 91 | 92 | return false; 93 | } 94 | 95 | void QniteCircle::clearSelection() { 96 | m_selectedIds.clear(); 97 | m_selectedIndexes.clear(); 98 | m_highlightedId = -1; 99 | m_highlightedIndex = -1; 100 | emit selectedChanged(); 101 | update(); 102 | } 103 | 104 | void QniteCircle::select(QList indexes) { 105 | m_selectedIds.clear(); 106 | m_selectedIndexes.clear(); 107 | for (auto i : indexes) { 108 | if (i >= 0 && i < m_xValues.size()) { 109 | auto it = m_xValues.constBegin(); 110 | it = it + i; 111 | m_selectedIds << it.key(); 112 | m_selectedIndexes << i; 113 | } 114 | } 115 | // TODO: should emit selectedChanged??? 116 | update(); 117 | } 118 | 119 | void QniteCircle::highlight(int index) { 120 | if (index >= 0 && index < m_xValues.size()) { 121 | auto it = m_xValues.constBegin(); 122 | it = it + index; 123 | m_highlightedId = it.key(); 124 | m_highlightedIndex = index; 125 | } 126 | 127 | update(); 128 | } 129 | 130 | QNanoQuickItemPainter *QniteCircle::createItemPainter() const { 131 | return new QniteCirclePainter; 132 | } 133 | -------------------------------------------------------------------------------- /src/items/qnitecircle.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_CIRCLE_H 2 | #define QNITE_CIRCLE_H 3 | 4 | #include "qnitexyartist.h" 5 | 6 | class QniteCircle : public QniteXYArtist { 7 | Q_OBJECT 8 | Q_PROPERTY(QList selectedIndexes READ selectedIndexes) 9 | Q_PROPERTY(int highlightedIndex READ highlightedIndex) 10 | 11 | public: 12 | explicit QniteCircle(QQuickItem *parent = nullptr); 13 | virtual ~QniteCircle() Q_DECL_OVERRIDE; 14 | 15 | QList selectedIndexes() const { return m_selectedIndexes.values(); } 16 | int highlightedIndex() const { return m_highlightedIndex; } 17 | 18 | QList selectedIds() const { return m_selectedIds.values(); } 19 | int highlightedId() const { return m_highlightedId; } 20 | 21 | bool select(const QList &) Q_DECL_OVERRIDE; 22 | bool select(const QPoint) Q_DECL_OVERRIDE; 23 | Q_INVOKABLE void clearSelection() Q_DECL_OVERRIDE; 24 | 25 | Q_INVOKABLE void select(QList indexes); 26 | Q_INVOKABLE void highlight(int index); 27 | 28 | QNanoQuickItemPainter *createItemPainter() const Q_DECL_OVERRIDE; 29 | 30 | private: 31 | QSet m_selectedIds; //! here we store the ids of selected points 32 | int m_highlightedId; 33 | 34 | QSet m_selectedIndexes; 35 | int m_highlightedIndex; 36 | }; 37 | 38 | #endif // QNITE_CIRCLE_H 39 | -------------------------------------------------------------------------------- /src/items/qnitecirclepainter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitecircle.h" 4 | #include "qnitecirclepainter.h" 5 | 6 | Q_LOGGING_CATEGORY(qnitecirclepainter, "qnite.circle.painter") 7 | 8 | QniteCirclePainter::QniteCirclePainter() : QNanoQuickItemPainter{} {} 9 | 10 | void QniteCirclePainter::synchronize(QNanoQuickItem *item) { 11 | qCDebug(qnitecirclepainter) << "synchronizing qnitecircle"; 12 | 13 | auto circleItem = static_cast(item); 14 | if (circleItem != Q_NULLPTR) { 15 | circleItem->processData(); 16 | 17 | // TODO: here we could share painting data using a class 18 | // which is copied with all its members in a local instance. 19 | m_xs = circleItem->xMapped(); 20 | m_ys = circleItem->yMapped(); 21 | 22 | // make a local copy of the pen 23 | m_pen = circleItem->pen()->data(); 24 | m_selectedPen = circleItem->selectedPen()->data(); 25 | m_highlightedPen = circleItem->highlightedPen()->data(); 26 | m_selected = circleItem->selected(); 27 | 28 | // circle specific properties 29 | m_selectedIds = circleItem->selectedIds(); 30 | m_highlightedId = circleItem->highlightedId(); 31 | } 32 | } 33 | 34 | void QniteCirclePainter::paint(QNanoPainter *painter) { 35 | qCDebug(qnitecirclepainter) << "painting qnitecircle"; 36 | 37 | if (m_xs.size() < 1) { 38 | return; 39 | } 40 | 41 | painter->setStrokeStyle(QNanoColor::fromQColor(m_pen.stroke)); 42 | painter->setFillStyle(QNanoColor::fromQColor(m_pen.fill)); 43 | painter->setLineWidth(m_pen.width); 44 | painter->setLineJoin(m_pen.join); 45 | painter->setLineCap(m_pen.cap); 46 | 47 | // draw unselected points 48 | auto ids = m_xs.keys(); 49 | for (auto id : ids) { 50 | // we do not draw selected or highlighted ids because we draw them later 51 | if (m_selectedIds.contains(id) || m_highlightedId == id) { 52 | continue; 53 | } 54 | painter->beginPath(); 55 | painter->circle(m_xs.value(id), m_ys.value(id), m_pen.radius); 56 | painter->fill(); 57 | painter->stroke(); 58 | } 59 | 60 | // draw selected points 61 | if (m_selectedIds.size() > 0) { 62 | painter->setStrokeStyle(QNanoColor::fromQColor(m_selectedPen.stroke)); 63 | painter->setFillStyle(QNanoColor::fromQColor(m_selectedPen.fill)); 64 | painter->setLineWidth(m_selectedPen.width); 65 | painter->setLineJoin(m_selectedPen.join); 66 | painter->setLineCap(m_selectedPen.cap); 67 | 68 | for (auto id : m_selectedIds) { 69 | // we don't draw highlighted ids here, nor those currently not visible 70 | if (id == m_highlightedId || !ids.contains(id)) { 71 | continue; 72 | } 73 | 74 | painter->beginPath(); 75 | painter->circle(m_xs.value(id), m_ys.value(id), m_selectedPen.radius); 76 | painter->fill(); 77 | painter->stroke(); 78 | } 79 | } 80 | 81 | // draw highlighted point if visible 82 | if (m_highlightedId > -1 && ids.contains(m_highlightedId)) { 83 | painter->setStrokeStyle(QNanoColor::fromQColor(m_highlightedPen.stroke)); 84 | painter->setFillStyle(QNanoColor::fromQColor(m_highlightedPen.fill)); 85 | painter->setLineWidth(m_highlightedPen.width); 86 | painter->setLineJoin(m_highlightedPen.join); 87 | painter->setLineCap(m_highlightedPen.cap); 88 | 89 | painter->beginPath(); 90 | painter->circle(m_xs.value(m_highlightedId), m_ys.value(m_highlightedId), 91 | m_highlightedPen.radius); 92 | painter->fill(); 93 | painter->stroke(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/items/qnitecirclepainter.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITECIRCLEPAINTER_H 2 | #define QNITECIRCLEPAINTER_H 3 | 4 | #include "qnanoquickitempainter.h" 5 | #include "qnitepen.h" 6 | 7 | class QniteCirclePainter : public QNanoQuickItemPainter { 8 | public: 9 | QniteCirclePainter(); 10 | 11 | void synchronize(QNanoQuickItem *item) Q_DECL_OVERRIDE; 12 | void paint(QNanoPainter *painter) Q_DECL_OVERRIDE; 13 | 14 | private: 15 | // base artist data 16 | bool m_selected; 17 | QnitePen::PenData m_pen; 18 | QnitePen::PenData m_selectedPen; 19 | QnitePen::PenData m_highlightedPen; 20 | 21 | // xy artist data 22 | QMap m_xs; 23 | QMap m_ys; 24 | 25 | // circle data 26 | qreal m_radius; 27 | qreal m_selectedRadius; 28 | qreal m_highlightedRadius; 29 | QList m_selectedIds; 30 | int m_highlightedId; 31 | }; 32 | 33 | #endif /* QNITECIRCLEPAINTER_H */ 34 | -------------------------------------------------------------------------------- /src/items/qnitegrid.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitegrid.h" 4 | #include "qnitegridpainter.h" 5 | 6 | Q_LOGGING_CATEGORY(qnitegrid, "qnite.grid") 7 | 8 | QniteGrid::QniteGrid(QQuickItem *parent) 9 | : QniteArtist{parent}, m_drawXAxes{true}, m_drawYAxes{true} { 10 | // we set a nicer default for a background grid 11 | pen()->setWidth(1); 12 | pen()->setStroke("#bdbdbd"); 13 | } 14 | 15 | QNanoQuickItemPainter *QniteGrid::createItemPainter() const { 16 | return new QniteGridPainter; 17 | } 18 | 19 | /* When the value variable has a different value the m_drawXAxes variable, 20 | * signal is emitted */ 21 | void QniteGrid::setDrawXAxes(bool value) { 22 | if (m_drawXAxes != value) { 23 | m_drawXAxes = value; 24 | emit drawXAxesChanged(); 25 | } 26 | } 27 | 28 | /* When the value variable has a different value the m_drawYAxes variable, 29 | * signal is emitted */ 30 | void QniteGrid::setDrawYAxes(bool value) { 31 | if (m_drawYAxes != value) { 32 | m_drawYAxes = value; 33 | emit drawYAxesChanged(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/items/qnitegrid.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITEGRID_H 2 | #define QNITEGRID_H 3 | 4 | #include "qniteartist.h" 5 | 6 | class QniteGrid : public QniteArtist { 7 | Q_OBJECT 8 | Q_PROPERTY( 9 | bool drawXAxes READ drawXAxes WRITE setDrawXAxes NOTIFY drawXAxesChanged) 10 | Q_PROPERTY( 11 | bool drawYAxes READ drawYAxes WRITE setDrawYAxes NOTIFY drawYAxesChanged) 12 | 13 | public: 14 | bool drawXAxes() const { return m_drawXAxes; } // get the m_drawXAxes value 15 | bool drawYAxes() const { return m_drawYAxes; } // get the m_drawYAxes value 16 | 17 | void setDrawXAxes(bool value); // set the drawXAxes value 18 | void setDrawYAxes(bool value); // set the drawYAxes value 19 | 20 | QniteGrid(QQuickItem *parent = Q_NULLPTR); 21 | 22 | QNanoQuickItemPainter *createItemPainter() const Q_DECL_OVERRIDE; 23 | 24 | public Q_SLOTS: 25 | virtual void processData() Q_DECL_OVERRIDE {} 26 | 27 | Q_SIGNALS: 28 | void drawXAxesChanged(); // this signal is emitted when the m_drawXaxes value 29 | // changes 30 | void drawYAxesChanged(); // this signal is emitted when the m_drawYaxes value 31 | // changes 32 | 33 | private: 34 | bool m_drawXAxes; 35 | bool m_drawYAxes; 36 | }; 37 | 38 | #endif // QNITEGRID_H 39 | -------------------------------------------------------------------------------- /src/items/qnitegridpainter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitegrid.h" 4 | #include "qnitegridpainter.h" 5 | 6 | #include "qniteaxes.h" 7 | #include "qniteaxis.h" 8 | 9 | Q_LOGGING_CATEGORY(qnitegridpainter, "qnite.grid.painter") 10 | 11 | QniteGridPainter::QniteGridPainter() : QNanoQuickItemPainter{} {} 12 | 13 | void QniteGridPainter::synchronize(QNanoQuickItem *item) { 14 | qCDebug(qnitegridpainter) << "synchronizing grid"; 15 | 16 | auto grid = static_cast(item); 17 | if (grid != Q_NULLPTR) { 18 | // make a local copy of the pen 19 | m_pen = grid->pen()->data(); 20 | 21 | // copy the tick values from both axes 22 | m_xs = grid->axes()->axisX()->majorTicks(); 23 | m_ys = grid->axes()->axisY()->majorTicks(); 24 | 25 | m_xsize = grid->axes()->axisX()->size(); 26 | m_ysize = grid->axes()->axisY()->size(); 27 | 28 | m_drawX = grid->drawXAxes(); 29 | m_drawY = grid->drawYAxes(); 30 | } 31 | } 32 | 33 | void QniteGridPainter::paint(QNanoPainter *painter) { 34 | qCDebug(qnitegridpainter) << "painting grid" << m_xs << m_ys; 35 | 36 | painter->setStrokeStyle(QNanoColor::fromQColor(m_pen.stroke)); 37 | painter->setFillStyle(QNanoColor::fromQColor(m_pen.fill)); 38 | painter->setLineWidth(m_pen.width); 39 | painter->setLineJoin(m_pen.join); 40 | painter->setLineCap(m_pen.cap); 41 | 42 | /* 43 | * if m_drawX and m_drawY are true, the painter draw the full grid 44 | * if m_drawX is false, the painter draw only the grid axes parallel to the y 45 | * axis if m_drawY is false, the painter draw only the grid axes parallel to 46 | * the x axis 47 | */ 48 | painter->beginPath(); 49 | if (m_drawX) { // draw x axes 50 | for (auto y : m_ys) { 51 | painter->moveTo(0, y); 52 | painter->lineTo(m_xsize, y); 53 | } 54 | painter->stroke(); 55 | } 56 | 57 | painter->beginPath(); 58 | if (m_drawY) { // draw y axes 59 | for (auto x : m_xs) { 60 | painter->moveTo(x, 0); 61 | painter->lineTo(x, m_ysize); 62 | } 63 | painter->stroke(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/items/qnitegridpainter.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITEGRIDPAINTER_H 2 | #define QNITEGRIDPAINTER_H 3 | 4 | #include "qnanoquickitempainter.h" 5 | #include "qnitepen.h" 6 | 7 | class QniteGridPainter : public QNanoQuickItemPainter { 8 | public: 9 | QniteGridPainter(); 10 | 11 | void synchronize(QNanoQuickItem *item) Q_DECL_OVERRIDE; 12 | void paint(QNanoPainter *painter) Q_DECL_OVERRIDE; 13 | 14 | private: 15 | QList m_xs; 16 | QList m_ys; 17 | 18 | QnitePen::PenData m_pen; 19 | qreal m_xsize; 20 | qreal m_ysize; 21 | 22 | bool m_drawX; 23 | bool m_drawY; 24 | }; 25 | #endif // QNITEGRIDPAINTER_H 26 | -------------------------------------------------------------------------------- /src/items/qniteline.cpp: -------------------------------------------------------------------------------- 1 | #include "qniteline.h" 2 | #include "qniteaxes.h" 3 | #include "qniteaxis.h" 4 | #include "qnitelinepainter.h" 5 | 6 | #include 7 | #include 8 | 9 | Q_LOGGING_CATEGORY(qniteline, "qnite.line") 10 | 11 | constexpr auto SELECTION_TOLERANCE = 50; 12 | 13 | QniteLine::QniteLine(QQuickItem *parent) 14 | : QniteXYArtist(parent), m_selected{false}, m_drawSymbols{true}, 15 | m_drawStepped{false} {} 16 | 17 | /*! 18 | Temporary implementation of the selection logic for lines. 19 | 20 | TODO: fix the implementation 21 | TODO: can we assume data is alwayws already mapped at this point? 22 | */ 23 | bool QniteLine::select(QPoint p) { 24 | bool accepted = false; 25 | 26 | // get the distance from the first point on the path 27 | auto ids = m_xMapped.keys(); 28 | for (auto id : ids) { 29 | QPointF cp(m_xMapped.value(id), m_yMapped.value(id)); 30 | QPointF d = p - cp; 31 | auto distance = d.manhattanLength(); 32 | if (distance < SELECTION_TOLERANCE) { 33 | m_selected = true; 34 | accepted = true; 35 | axes()->setOnTop(this); 36 | emit selectedChanged(); 37 | update(); 38 | break; 39 | } 40 | } 41 | 42 | return accepted; 43 | } 44 | 45 | bool QniteLine::select(const QList &path) { 46 | if (path.size()) { 47 | return select(path.first()); 48 | } 49 | 50 | return false; 51 | } 52 | 53 | void QniteLine::clearSelection() { 54 | m_selected = false; 55 | emit selectedChanged(); 56 | update(); 57 | } 58 | 59 | void QniteLine::setDrawSymbols(bool drawSymbols) { 60 | if (this->m_drawSymbols != drawSymbols) { 61 | this->m_drawSymbols = drawSymbols; 62 | emit drawSymbolsChanged(); 63 | update(); 64 | } 65 | } 66 | 67 | void QniteLine::setDrawStepped(bool drawStepped) { 68 | if (this->m_drawStepped != drawStepped) { 69 | this->m_drawStepped = drawStepped; 70 | emit drawSteppedChanged(); 71 | update(); 72 | } 73 | } 74 | 75 | QNanoQuickItemPainter *QniteLine::createItemPainter() const { 76 | qCDebug(qniteline) << "creating item painter"; 77 | return new QniteLinePainter(); 78 | } 79 | -------------------------------------------------------------------------------- /src/items/qniteline.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_LINE_H 2 | #define QNITE_LINE_H 3 | 4 | #include "qnitexyartist.h" 5 | 6 | #include 7 | 8 | class QniteLine : public QniteXYArtist { 9 | Q_OBJECT 10 | Q_PROPERTY(bool drawSymbols READ drawSymbols WRITE setDrawSymbols NOTIFY 11 | drawSymbolsChanged) 12 | Q_PROPERTY(bool drawStepped READ drawStepped WRITE setDrawStepped NOTIFY 13 | drawSteppedChanged) 14 | 15 | public: 16 | explicit QniteLine(QQuickItem *parent = 0); 17 | 18 | bool select(const QList &) Q_DECL_OVERRIDE; 19 | bool select(const QPoint) Q_DECL_OVERRIDE; 20 | void clearSelection() Q_DECL_OVERRIDE; 21 | 22 | bool drawSymbols() const { return m_drawSymbols; } 23 | bool drawStepped() const { return m_drawStepped; } 24 | void setDrawSymbols(bool drawSymbols); 25 | void setDrawStepped(bool drawStepped); 26 | 27 | QNanoQuickItemPainter *createItemPainter() const Q_DECL_OVERRIDE; 28 | 29 | signals: 30 | void drawSymbolsChanged(); 31 | void drawSteppedChanged(); 32 | 33 | protected: 34 | virtual bool isSelected() const Q_DECL_OVERRIDE { return m_selected; } 35 | 36 | private: 37 | bool m_selected; 38 | bool m_drawSymbols; 39 | bool m_drawStepped; 40 | }; 41 | 42 | #endif // QNITE_LINE_H 43 | -------------------------------------------------------------------------------- /src/items/qnitelinepainter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qniteline.h" 4 | #include "qnitelinepainter.h" 5 | 6 | #include "qniteaxes.h" 7 | #include "qniteaxis.h" 8 | 9 | Q_LOGGING_CATEGORY(qnitelinepainter, "qnite.line.painter") 10 | 11 | QniteLinePainter::QniteLinePainter() 12 | : QNanoQuickItemPainter{}, m_selected{false}, m_drawSymbols{false}, 13 | m_drawStepped{false} {} 14 | 15 | void QniteLinePainter::synchronize(QNanoQuickItem *item) { 16 | qCDebug(qnitelinepainter) << "synchronizing qniteline"; 17 | 18 | auto lineItem = static_cast(item); 19 | if (lineItem != Q_NULLPTR) { 20 | lineItem->processData(); 21 | 22 | // TODO: here we could share painting data using a class 23 | // which is copied with all its members in a local instance. 24 | m_xs = lineItem->xProcessed(); 25 | m_ys = lineItem->yProcessed(); 26 | 27 | // we need to save mapped points to draw symbols 28 | // because processed points could be more because 29 | // of interpolation. 30 | m_mappedXs = lineItem->xMapped(); 31 | m_mappedYs = lineItem->yMapped(); 32 | 33 | // make a local copy of the pen data (stroke, fill) 34 | m_pen = lineItem->pen()->data(); 35 | m_selectedPen = lineItem->selectedPen()->data(); 36 | m_selected = lineItem->selected(); 37 | 38 | // the baseline is the position of the y axis 39 | // this is needed to compute a fill polygon for the line. 40 | m_baseline = lineItem->axes()->axisY()->position(); 41 | 42 | m_drawSymbols = lineItem->drawSymbols(); 43 | 44 | m_drawStepped = lineItem->drawStepped(); 45 | } 46 | } 47 | 48 | void QniteLinePainter::paint(QNanoPainter *painter) { 49 | qCDebug(qnitelinepainter) << "painting qniteline"; 50 | 51 | auto dataSize = m_xs.size(); 52 | if (dataSize < 2) 53 | return; 54 | 55 | auto &pen = m_selected ? m_selectedPen : m_pen; 56 | painter->setStrokeStyle(QNanoColor::fromQColor(pen.stroke)); 57 | if (pen.fill.isValid()) { 58 | painter->setFillStyle(QNanoColor::fromQColor(pen.fill)); 59 | } 60 | painter->setLineWidth(pen.width); 61 | painter->setLineJoin(pen.join); 62 | painter->setLineCap(pen.cap); 63 | 64 | // first we draw the fill when a valid fill color is available 65 | if (pen.fill.isValid()) { 66 | painter->beginPath(); 67 | painter->moveTo(m_xs.at(0), m_baseline); 68 | painter->lineTo(m_xs.at(0), m_ys.at(0)); 69 | for (auto i = 1; i < dataSize; ++i) { 70 | if (m_drawStepped) { 71 | painter->lineTo(m_xs.at(i), m_ys.at(i - 1)); 72 | } 73 | painter->lineTo(m_xs.at(i), m_ys.at(i)); 74 | } 75 | painter->lineTo(m_xs.at(dataSize - 1), m_baseline); 76 | painter->fill(); 77 | } 78 | 79 | painter->beginPath(); 80 | painter->moveTo(m_xs.at(0), m_ys.at(0)); 81 | for (auto i = 1; i < dataSize; ++i) { 82 | if (m_drawStepped) { 83 | painter->lineTo(m_xs.at(i), m_ys.at(i - 1)); 84 | } 85 | painter->lineTo(m_xs.at(i), m_ys.at(i)); 86 | } 87 | painter->stroke(); 88 | 89 | // draw symbols 90 | if (m_drawSymbols) { 91 | // we force a solid color so that the line under the symbol 92 | // is hidden 93 | if (pen.fill.isValid()) { 94 | auto symbolColor = pen.fill; 95 | symbolColor.setAlphaF(1.0); 96 | painter->setFillStyle(QNanoColor::fromQColor(symbolColor)); 97 | } 98 | auto ids = m_mappedXs.keys(); 99 | for (auto id : ids) { 100 | painter->beginPath(); 101 | painter->circle(m_mappedXs.value(id), m_mappedYs.value(id), 102 | pen.width * 2.5); 103 | painter->fill(); 104 | painter->stroke(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/items/qnitelinepainter.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITELINEPAINTER_H 2 | #define QNITELINEPAINTER_H 3 | 4 | #include "qnanoquickitempainter.h" 5 | #include "qnitepen.h" 6 | 7 | class QniteLinePainter : public QNanoQuickItemPainter { 8 | public: 9 | QniteLinePainter(); 10 | 11 | void synchronize(QNanoQuickItem *item) Q_DECL_OVERRIDE; 12 | void paint(QNanoPainter *painter) Q_DECL_OVERRIDE; 13 | 14 | private: 15 | // base artist data 16 | bool m_selected; 17 | QnitePen::PenData m_pen; 18 | QnitePen::PenData m_selectedPen; 19 | 20 | // xy artist data 21 | QList m_xs; 22 | QList m_ys; 23 | 24 | QMap m_mappedXs; 25 | QMap m_mappedYs; 26 | 27 | // line data 28 | qreal m_baseline; 29 | 30 | // draw symbols flag 31 | bool m_drawSymbols; 32 | // draw stepped flag 33 | bool m_drawStepped; 34 | }; 35 | 36 | #endif /* QNITELINEPAINTER_H */ 37 | -------------------------------------------------------------------------------- /src/items/qnitepen.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitepen.h" 2 | 3 | QnitePen::PenData::PenData() 4 | : stroke{"black"}, fill{}, width{2}, join{LineJoin::JOIN_MITER}, 5 | cap{LineCap::CAP_SQUARE}, radius{5} {} 6 | 7 | QnitePen::QnitePen(QObject *parent) : QObject{parent} {} 8 | 9 | void QnitePen::setStroke(QColor stroke) { 10 | if (m_data.stroke != stroke) { 11 | m_data.stroke = stroke; 12 | emit strokeChanged(); 13 | emit penChanged(); 14 | } 15 | } 16 | 17 | void QnitePen::setFill(QColor fill) { 18 | if (m_data.fill != fill) { 19 | m_data.fill = fill; 20 | emit fillChanged(); 21 | emit penChanged(); 22 | } 23 | } 24 | 25 | void QnitePen::setWidth(qreal width) { 26 | if (m_data.width != width) { 27 | m_data.width = width; 28 | emit widthChanged(); 29 | emit penChanged(); 30 | } 31 | } 32 | 33 | void QnitePen::setJoin(LineJoin join) { 34 | if (m_data.join != join) { 35 | m_data.join = join; 36 | emit joinChanged(); 37 | emit penChanged(); 38 | } 39 | } 40 | 41 | void QnitePen::setCap(LineCap cap) { 42 | if (m_data.cap != cap) { 43 | m_data.cap = cap; 44 | emit capChanged(); 45 | emit penChanged(); 46 | } 47 | } 48 | 49 | void QnitePen::setRadius(qreal radius) { 50 | if (m_data.radius != radius) { 51 | m_data.radius = radius; 52 | emit radiusChanged(); 53 | emit penChanged(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/items/qnitepen.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_PEN_H 2 | #define QNITE_PEN_H 3 | 4 | #include "qnanopainter.h" 5 | #include 6 | 7 | class QnitePen : public QObject { 8 | Q_OBJECT 9 | Q_PROPERTY(QColor stroke READ stroke WRITE setStroke NOTIFY strokeChanged) 10 | Q_PROPERTY(QColor fill READ fill WRITE setFill NOTIFY fillChanged) 11 | Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged) 12 | Q_PROPERTY(LineJoin join READ join WRITE setJoin NOTIFY joinChanged) 13 | Q_PROPERTY(LineCap cap READ cap WRITE setCap NOTIFY capChanged) 14 | Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) 15 | 16 | public: 17 | using LineJoin = QNanoPainter::LineJoin; 18 | Q_ENUM(LineJoin) 19 | 20 | using LineCap = QNanoPainter::LineCap; 21 | Q_ENUM(LineCap) 22 | 23 | QnitePen(QObject *parent = Q_NULLPTR); 24 | 25 | QColor stroke() const { return m_data.stroke; } 26 | void setStroke(QColor stroke); 27 | 28 | QColor fill() const { return m_data.fill; } 29 | void setFill(QColor fill); 30 | 31 | qreal width() const { return m_data.width; } 32 | void setWidth(qreal width); 33 | 34 | LineJoin join() const { return m_data.join; } 35 | void setJoin(LineJoin join); 36 | 37 | LineCap cap() const { return m_data.cap; } 38 | void setCap(LineCap cap); 39 | 40 | qreal radius() const { return m_data.radius; } 41 | void setRadius(qreal radius); 42 | 43 | // An instance of PenData is shared with the painter 44 | // class associated with each artist. 45 | // this has been moved to a separate inner class to allow 46 | // an easy copying between the artist and the painter since 47 | // QObjects cannot be copied (see 48 | // http://doc.qt.io/qt-5/object.html#qt-objects-identity-vs-value) 49 | struct PenData { 50 | PenData(); 51 | 52 | QColor stroke; 53 | QColor fill; 54 | qreal width; 55 | LineJoin join; 56 | LineCap cap; 57 | qreal radius; 58 | }; 59 | 60 | const PenData &data() const { return m_data; } 61 | 62 | Q_SIGNALS: 63 | void strokeChanged(); 64 | void fillChanged(); 65 | void widthChanged(); 66 | void joinChanged(); 67 | void capChanged(); 68 | void radiusChanged(); 69 | void penChanged(); 70 | 71 | private: 72 | PenData m_data; 73 | }; 74 | 75 | #endif // QNITE_PEN_H 76 | -------------------------------------------------------------------------------- /src/items/qnitespline.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitespline.h" 2 | #include "qniteaxes.h" 3 | #include "qniteaxis.h" 4 | 5 | #include 6 | 7 | namespace { 8 | 9 | double CosineInterpolate(double y1, double y2, double mu) { 10 | double mu2; 11 | 12 | mu2 = (1 - cos(mu * M_PI)) / 2; 13 | return (y1 * (1 - mu2) + y2 * mu2); 14 | } 15 | 16 | double CubicInterpolate(double y0, double y1, double y2, double y3, double mu) { 17 | double a0, a1, a2, a3, mu2; 18 | 19 | mu2 = mu * mu; 20 | a0 = y3 - y2 - y0 + y1; 21 | a1 = y0 - y1 - a0; 22 | a2 = y2 - y0; 23 | a3 = y1; 24 | 25 | return (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3); 26 | } 27 | 28 | double CatmullRomInterpolate(double y0, double y1, double y2, double y3, 29 | double mu) { 30 | double a0, a1, a2, a3, mu2; 31 | 32 | mu2 = mu * mu; 33 | a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3; 34 | a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3; 35 | a2 = -0.5 * y0 + 0.5 * y2; 36 | a3 = y1; 37 | 38 | return (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3); 39 | } 40 | 41 | double HermiteInterpolate(double y0, double y1, double y2, double y3, double mu, 42 | double tension = 0, double bias = 0) { 43 | double m0, m1, mu2, mu3; 44 | double a0, a1, a2, a3; 45 | 46 | mu2 = mu * mu; 47 | mu3 = mu2 * mu; 48 | m0 = (y1 - y0) * (1 + bias) * (1 - tension) / 2; 49 | m0 += (y2 - y1) * (1 - bias) * (1 - tension) / 2; 50 | m1 = (y2 - y1) * (1 + bias) * (1 - tension) / 2; 51 | m1 += (y3 - y2) * (1 - bias) * (1 - tension) / 2; 52 | a0 = 2 * mu3 - 3 * mu2 + 1; 53 | a1 = mu3 - 2 * mu2 + mu; 54 | a2 = mu3 - mu2; 55 | a3 = -2 * mu3 + 3 * mu2; 56 | 57 | return (a0 * y1 + a1 * m0 + a2 * m1 + a3 * y2); 58 | } 59 | 60 | } // namespace 61 | 62 | QniteSpline::QniteSpline(QQuickItem *parent) 63 | : QniteLine(parent), 64 | m_interpolation{Interpolation::Cubic}, m_tension{0}, m_bias{0} {} 65 | 66 | void QniteSpline::setInterpolation(Interpolation i) { 67 | if (m_interpolation != i) { 68 | m_interpolation = i; 69 | emit interpolationChanged(); 70 | update(); 71 | } 72 | } 73 | 74 | void QniteSpline::setTension(qreal t) { 75 | if (m_tension != t) { 76 | m_tension = t; 77 | emit tensionChanged(); 78 | update(); 79 | } 80 | } 81 | 82 | void QniteSpline::setBias(qreal b) { 83 | if (m_bias != b) { 84 | m_bias = b; 85 | emit biasChanged(); 86 | update(); 87 | } 88 | } 89 | 90 | void QniteSpline::processData() { 91 | QniteXYArtist::processData(); 92 | 93 | switch (m_interpolation) { 94 | case Interpolation::Cosine: 95 | cosineInterpolation(); 96 | break; 97 | case Interpolation::Cubic: 98 | case Interpolation::CatmullRom: 99 | case Interpolation::Hermite: 100 | cubicInterpolation(); 101 | break; 102 | } 103 | } 104 | 105 | void QniteSpline::cosineInterpolation() { 106 | QList xs; 107 | QList ys; 108 | 109 | const auto &xv = xMapped().values(); 110 | const auto &yv = yMapped().values(); 111 | 112 | int n = xv.size() - 1; 113 | int res = 2; 114 | 115 | for (int i = 0; i < n; ++i) { 116 | qreal distance = xv.at(i + 1) - xv.at(i); 117 | int steps = distance / res; 118 | qreal step = 1. / steps; 119 | 120 | for (int j = 0; j <= steps; ++j) { 121 | qreal s = step * j; 122 | qreal x = xv.at(i) + (xv.at(i + 1) - xv.at(i)) * s; 123 | qreal y = CosineInterpolate(yv.at(i), yv.at(i + 1), s); 124 | 125 | xs.push_back(x); 126 | ys.push_back(y); 127 | } 128 | } 129 | 130 | m_xProcessed = xs; 131 | m_yProcessed = ys; 132 | } 133 | 134 | void QniteSpline::cubicInterpolation() { 135 | QList xs; 136 | QList ys; 137 | 138 | auto xv = xMapped().values(); 139 | auto yv = yMapped().values(); 140 | 141 | auto n = xv.size(); 142 | auto res = 2; 143 | 144 | // dream up two extra points at the start and at the end 145 | // of the sequence. These are needed by the cubic interpolation 146 | // algorithm 147 | xv.insert(0, xv.at(0) - (xv.at(1) - xv.at(0))); 148 | yv.insert(0, yv.at(0) - (yv.at(1) - yv.at(0))); 149 | 150 | auto l = xv.size() - 1; 151 | xv.append(xv.at(l) + (xv.at(l) - xv.at(l - 1))); 152 | yv.append(yv.at(l) + (yv.at(l) - yv.at(l - 1))); 153 | 154 | for (int i = 1; i < n; ++i) { 155 | qreal distance = xv.at(i + 1) - xv.at(i); 156 | int steps = distance / res; 157 | qreal step = 1. / steps; 158 | 159 | for (int j = 0; j <= steps; ++j) { 160 | qreal s = step * j; 161 | qreal x = xv.at(i) + (xv.at(i + 1) - xv.at(i)) * s; 162 | qreal y; 163 | if (m_interpolation == Interpolation::Hermite) { 164 | y = HermiteInterpolate(yv.at(i - 1), yv.at(i), yv.at(i + 1), 165 | yv.at(i + 2), s, m_tension, m_bias); 166 | } else if (m_interpolation == Interpolation::Cubic) { 167 | y = CubicInterpolate(yv.at(i - 1), yv.at(i), yv.at(i + 1), yv.at(i + 2), 168 | s); 169 | } else { 170 | y = CatmullRomInterpolate(yv.at(i - 1), yv.at(i), yv.at(i + 1), 171 | yv.at(i + 2), s); 172 | } 173 | 174 | xs.push_back(x); 175 | ys.push_back(y); 176 | } 177 | } 178 | 179 | m_xProcessed = xs; 180 | m_yProcessed = ys; 181 | } 182 | -------------------------------------------------------------------------------- /src/items/qnitespline.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_SPLINE_H 2 | #define QNITE_SPLINE_H 3 | 4 | #include "qniteline.h" 5 | 6 | class QniteSpline : public QniteLine { 7 | Q_OBJECT 8 | Q_ENUMS(Interpolation) 9 | Q_PROPERTY(Interpolation interpolation READ interpolation WRITE 10 | setInterpolation NOTIFY interpolationChanged) 11 | Q_PROPERTY(qreal tension READ tension WRITE setTension NOTIFY tensionChanged) 12 | Q_PROPERTY(qreal bias READ bias WRITE setBias NOTIFY biasChanged) 13 | 14 | public: 15 | explicit QniteSpline(QQuickItem *parent = 0); 16 | 17 | enum Interpolation { Cosine, Cubic, CatmullRom, Hermite }; 18 | Interpolation interpolation() const { return m_interpolation; } 19 | void setInterpolation(Interpolation i); 20 | 21 | qreal tension() const { return m_tension; } 22 | void setTension(qreal t); 23 | qreal bias() const { return m_bias; } 24 | void setBias(qreal b); 25 | 26 | public Q_SLOTS: 27 | virtual void processData() Q_DECL_OVERRIDE; 28 | 29 | Q_SIGNALS: 30 | void interpolationChanged(); 31 | void tensionChanged(); 32 | void biasChanged(); 33 | 34 | private: 35 | void cosineInterpolation(); 36 | void cubicInterpolation(); 37 | 38 | Interpolation m_interpolation; 39 | qreal m_tension; 40 | qreal m_bias; 41 | }; 42 | 43 | #endif // QNITE_SPLINE_H 44 | -------------------------------------------------------------------------------- /src/qml/Qnite/Axes.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.4 2 | import Qnite 1.0 3 | 4 | /*! \qmltype Axes 5 | \inqmlmodule Qnite 0.1 6 | \ingroup figure 7 | 8 | \brief The container of all the axes and the ploy area 9 | 10 | TODO: add a long description 11 | */ 12 | BasicAxes { 13 | id: __plotarea 14 | 15 | /*! tick properties */ 16 | AxisTick { 17 | id: __axistick 18 | thick: 1 19 | majSize: 10 20 | minSize: 3 21 | color: Qt.rgba(0,0,0,0.2) 22 | } 23 | property alias tick: __axistick 24 | 25 | // Optional names for the plot axis 26 | property string yAxisName: "" 27 | property string xAxisName: "" 28 | 29 | // Color for the plot labels, defaults to black 30 | property color xLabelsColor: "black" 31 | property color yLabelsColor: "black" 32 | 33 | // Plot background color, defaults to white 34 | property color backgroundColor: "white" 35 | 36 | property font labelFont 37 | 38 | property bool rotateBottomLabels: false 39 | property int bottomLabelHeight: 0 40 | 41 | TextMetrics { 42 | id: __metrics 43 | font: __plotarea.labelFont 44 | text: { 45 | if (axisY && axisY.labels && axisY.labels.length > 0) { 46 | return axisY.labels.reduce(function (a, b) { 47 | return a.length > b.length ? a : b; 48 | }); 49 | } else { 50 | return "0"; 51 | } 52 | } 53 | } 54 | anchors { 55 | top: parent.top 56 | bottom: parent.bottom 57 | left: parent.left 58 | right: parent.right 59 | 60 | // We add left axis name height and bottom axis name width so that 61 | // if names are not empty they are correctly visualized 62 | topMargin: __metrics.height / 2 + __leftAxisName.height 63 | bottomMargin: __metrics.height + tick.majSize + 5 + bottomLabelHeight 64 | leftMargin: __metrics.width + tick.majSize + 5 65 | rightMargin: __metrics.width / 2 + __bottomAxisName.width 66 | } 67 | 68 | function majorTicksChanged(axis, labelsitem) { 69 | var values = []; 70 | for(var i = 0, item; (item = axis.majorTicks[i]) !== undefined; i++) { 71 | values.push({ 72 | "value": item, 73 | "label": axis.labels[i] 74 | }); 75 | } 76 | 77 | labelsitem.major = values; 78 | } 79 | 80 | function minorTicksChanged(axis, labelsitem) { 81 | var values = []; 82 | for(var i = 0, item; (item = axis.minorTicks[i]) !== undefined; i++) { 83 | values.push({ "value": item }); 84 | } 85 | 86 | labelsitem.minor = values; 87 | } 88 | 89 | axisY: LinearAxis { flip: true } 90 | axisX: LinearAxis { } 91 | 92 | Binding { 93 | target: axisX 94 | property: "size" 95 | value: __plotarea.width 96 | } 97 | Binding { 98 | target: axisY 99 | property: "size" 100 | value: __plotarea.height 101 | } 102 | 103 | Connections { 104 | target: axisX 105 | 106 | onMajorTicksChanged: __plotarea.majorTicksChanged(axisX, __bottomlabels) 107 | onMinorTicksChanged: __plotarea.minorTicksChanged(axisX, __bottomlabels) 108 | } 109 | Connections { 110 | target: axisY 111 | 112 | onMajorTicksChanged: __plotarea.majorTicksChanged(axisY, __leftlabels) 113 | onMinorTicksChanged: __plotarea.minorTicksChanged(axisY, __leftlabels) 114 | } 115 | 116 | // TODO: expose as a property so it is customizable 117 | Rectangle { 118 | id: background 119 | anchors.fill: parent 120 | color: __plotarea.backgroundColor 121 | z: -1 122 | } 123 | 124 | Rectangle { 125 | id: bottombaseline 126 | implicitWidth: parent.width 127 | implicitHeight: tick.thick 128 | anchors.verticalCenter: parent.bottom 129 | anchors.alignWhenCentered: false 130 | color: tick.color 131 | } 132 | AxisLabel { 133 | id: __bottomlabels 134 | axisType: "bottom" 135 | width: __plotarea.width 136 | anchors.top: __plotarea.bottom 137 | anchors.left: __plotarea.left 138 | 139 | tick: __plotarea.tick 140 | labelFont: __plotarea.labelFont 141 | color: __plotarea.xLabelsColor 142 | 143 | rotateBottomLabels: __plotarea.rotateBottomLabels 144 | } 145 | // Name for the x Axis that the user can optionally set 146 | Text { 147 | id: __bottomAxisName 148 | anchors.left: __bottomlabels.right 149 | anchors.verticalCenter: __bottomlabels.bottom 150 | anchors.margins: 10 151 | text: __plotarea.xAxisName 152 | font: __plotarea.labelFont 153 | color: __plotarea.xLabelsColor 154 | } 155 | 156 | Rectangle { 157 | id: leftbaseline 158 | implicitWidth: tick.thick 159 | implicitHeight: parent.height 160 | anchors.horizontalCenter: parent.left 161 | anchors.alignWhenCentered: false 162 | color: tick.color 163 | } 164 | AxisLabel { 165 | id: __leftlabels 166 | axisType: "left" 167 | height: __plotarea.height 168 | anchors.bottom: __plotarea.bottom 169 | anchors.right: __plotarea.left 170 | 171 | tick: __plotarea.tick 172 | labelFont: __plotarea.labelFont 173 | color: __plotarea.yLabelsColor 174 | } 175 | // Name for the y Axis that the user can optionally set 176 | Text { 177 | id: __leftAxisName 178 | anchors.bottom: __leftlabels.top 179 | anchors.horizontalCenter: __leftlabels.left 180 | anchors.margins: 10 181 | text: __plotarea.yAxisName 182 | font: __plotarea.labelFont 183 | color: __plotarea.yLabelsColor 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/qml/Qnite/AxisLabel.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.4 2 | import Qnite 1.0 3 | 4 | Item { 5 | id: axis 6 | 7 | property AxisTick tick 8 | 9 | property string axisType 10 | property font labelFont 11 | 12 | property var major; 13 | property var minor; 14 | 15 | property color color; 16 | property bool rotateBottomLabels: false; 17 | 18 | Item { 19 | id: ticksnlabels 20 | anchors.fill: parent 21 | 22 | Repeater { 23 | model: axis.major 24 | Loader { 25 | property real val: modelData.value 26 | property string label: modelData.label 27 | property real size: axis.tick.majSize 28 | 29 | anchors { 30 | right: axis.axisType === "left" ? ticksnlabels.right : undefined 31 | rightMargin: axis.axisType === "left" ? tick.thick / 2 : 0 32 | } 33 | sourceComponent: axis.axisType === "left" ? leftTick : bottomTick 34 | } 35 | } 36 | 37 | Repeater { 38 | model: axis.minor 39 | Loader { 40 | property real val: modelData.value 41 | property string label: "" 42 | property real size: axis.tick.minSize 43 | 44 | anchors { 45 | right: axis.axisType === "left" ? ticksnlabels.right : undefined 46 | rightMargin: axis.axisType === "left" ? tick.thick / 2 : 0 47 | } 48 | sourceComponent: axis.axisType === "left" ? leftTick : bottomTick 49 | } 50 | } 51 | } 52 | 53 | Component { 54 | id: leftTick 55 | Row { 56 | spacing: 5 57 | 58 | Binding on y { 59 | // align the center of the tick with the data value 60 | value: val - tick.thick / 2 61 | } 62 | 63 | Text { 64 | id: __text 65 | anchors.verticalCenter: __tick.verticalCenter 66 | font: axis.labelFont 67 | text: label 68 | 69 | color: axis.color 70 | } 71 | 72 | Rectangle { 73 | id: __tick 74 | implicitWidth: size 75 | implicitHeight: tick.thick 76 | 77 | color: tick.color 78 | } 79 | } 80 | } 81 | 82 | Component { 83 | id: bottomTick 84 | 85 | Column { 86 | spacing: 5 87 | 88 | Binding on x { 89 | // align the center of the tick with the data value 90 | value: val - tick.thick / 2 91 | } 92 | 93 | Rectangle { 94 | id: __tick 95 | implicitWidth: tick.thick 96 | implicitHeight: size 97 | 98 | color: tick.color 99 | } 100 | 101 | Text { 102 | id: __text 103 | anchors.horizontalCenter: axis.rotateBottomLabels ? undefined : __tick.horizontalCenter 104 | anchors.right: axis.rotateBottomLabels ? __tick.horizontalCenter : undefined 105 | font: axis.labelFont 106 | text: label 107 | 108 | color: axis.color 109 | transformOrigin: axis.rotateBottomLabels ? Item.Right : Item.Center 110 | rotation: axis.rotateBottomLabels ? -45 : 0 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/qml/Qnite/Figure.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.4 2 | import Qnite 1.0 3 | 4 | /*! \qmltype Figure 5 | \inqmlmodule Qnite 0.1 6 | \ingroup figure 7 | 8 | \brief The container of all the plot items. 9 | 10 | TODO: add a long description 11 | 12 | \qml 13 | 14 | // TODO: add a code example 15 | 16 | \endqml 17 | */ 18 | FocusScope { 19 | id: plot 20 | 21 | /*! \qmlproperty string Figure::title 22 | 23 | The title of the plot. 24 | */ 25 | property string title 26 | 27 | /*! \qmlproperty Axes Figure::axes 28 | 29 | This object is responsible for the placement of 30 | the axes and the alignment with the plot area. 31 | */ 32 | property alias axes: __axes 33 | 34 | /*! \qmlproperty list Figure::tools 35 | 36 | The list of tools available to this figure. 37 | */ 38 | property alias tools: __axes.tools 39 | 40 | default property alias artists: __axes.artists 41 | 42 | Axes { 43 | id: __axes 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/qml/Qnite/qmldir: -------------------------------------------------------------------------------- 1 | module Qnite 2 | Axes 1.0 Axes.qml 3 | Figure 1.0 Figure.qml 4 | AxisLabel 1.0 AxisLabel.qml 5 | 6 | -------------------------------------------------------------------------------- /src/qnite.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qniteartist.h" 4 | #include "qniteaxes.h" 5 | #include "qniteaxis.h" 6 | #include "qniteaxistick.h" 7 | #include "qnitebar.h" 8 | #include "qnitecategoryaxis.h" 9 | #include "qnitecircle.h" 10 | #include "qnitegrid.h" 11 | #include "qniteline.h" 12 | #include "qnitelinearaxis.h" 13 | #include "qnitelinearmapper.h" 14 | #include "qnitelinearticker.h" 15 | #include "qnitemapper.h" 16 | #include "qnitepathselectiontool.h" 17 | #include "qnitepen.h" 18 | #include "qnitepointselectiontool.h" 19 | #include "qnitespline.h" 20 | #include "qniteticker.h" 21 | #include "qnitetool.h" 22 | #include "qnitexyartist.h" 23 | #include "qnitezoomtool.h" 24 | 25 | static void registerQniteQmlTypes() { 26 | Q_INIT_RESOURCE(qnite); 27 | Q_INIT_RESOURCE(libqnanopainterdata); 28 | 29 | auto uri = "Qnite"; 30 | 31 | qmlRegisterUncreatableType( 32 | uri, 1, 0, "Artist", "Cannot create objects of basetype Artist"); 33 | qmlRegisterType(uri, 1, 0, "XYArtist"); 34 | qmlRegisterType(uri, 1, 0, "BasicAxes"); 35 | qmlRegisterUncreatableType( 36 | uri, 1, 0, "Axis", "Cannot create obects of base type Axis"); 37 | qmlRegisterType(uri, 1, 0, "LinearAxis"); 38 | qmlRegisterType(uri, 1, 0, "CategoryAxis"); 39 | qmlRegisterType(uri, 1, 0, "AxisTick"); 40 | qmlRegisterUncreatableType(uri, 1, 0, "Pen", 41 | "Cannot create obects of type Pen"); 42 | qmlRegisterType(uri, 1, 0, "Bar"); 43 | qmlRegisterType(uri, 1, 0, "Circle"); 44 | qmlRegisterType(uri, 1, 0, "Line"); 45 | qmlRegisterType(uri, 1, 0, "Spline"); 46 | qmlRegisterType(uri, 1, 0, "Grid"); 47 | 48 | qmlRegisterUncreatableType( 49 | uri, 1, 0, "Tool", "Cannot create objects of basetype Tool"); 50 | qmlRegisterType(uri, 1, 0, "PointSelectionTool"); 51 | qmlRegisterType(uri, 1, 0, "PathSelectionTool"); 52 | qmlRegisterType(uri, 1, 0, "ZoomTool"); 53 | 54 | qmlRegisterUncreatableType( 55 | uri, 1, 0, "Mapper", "Cannot create objects of basetype Mapper"); 56 | qmlRegisterType(uri, 1, 0, "LinearMapper"); 57 | qmlRegisterUncreatableType( 58 | uri, 1, 0, "Ticker", "Cannot create objects of base type Ticker"); 59 | qmlRegisterType(uri, 1, 0, "LinearTicker"); 60 | } 61 | 62 | Q_COREAPP_STARTUP_FUNCTION(registerQniteQmlTypes) 63 | -------------------------------------------------------------------------------- /src/qnite.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | qml/Qnite/qmldir 4 | qml/Qnite/Axes.qml 5 | qml/Qnite/AxisLabel.qml 6 | qml/Qnite/Figure.qml 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/qniteartist.cpp: -------------------------------------------------------------------------------- 1 | #include "qniteartist.h" 2 | #include "qniteaxes.h" 3 | #include "qnitepen.h" 4 | 5 | QniteArtist::QniteArtist(QQuickItem *parent) 6 | : QNanoQuickItem(parent), m_axes{nullptr}, m_selectable{false}, 7 | m_propagate_selection{false}, m_pen{new QnitePen}, 8 | m_selectedPen{new QnitePen}, m_highlightedPen{new QnitePen} { 9 | // we need to trigger a redraw of the artist when 10 | // the pen or selectedPen's attributes change. 11 | connect(m_pen, &QnitePen::penChanged, this, &QniteArtist::update); 12 | connect(m_selectedPen, &QnitePen::penChanged, this, &QniteArtist::update); 13 | connect(m_highlightedPen, &QnitePen::penChanged, this, &QniteArtist::update); 14 | } 15 | 16 | QniteArtist::~QniteArtist() { 17 | delete m_pen; 18 | delete m_selectedPen; 19 | delete m_highlightedPen; 20 | } 21 | 22 | QniteAxes *QniteArtist::axes() const { return m_axes; } 23 | 24 | void QniteArtist::setAxes(QniteAxes *axes) { 25 | if (m_axes != axes) { 26 | m_axes = axes; 27 | emit axesChanged(); 28 | 29 | updateAxes(); 30 | } 31 | } 32 | 33 | void QniteArtist::updateAxes() { 34 | disconnect(m_axes, SIGNAL(widthChanged()), this, 0); 35 | disconnect(m_axes, SIGNAL(heightChanged()), this, 0); 36 | disconnect(m_axes, &QniteAxes::xBoundsChanged, this, 0); 37 | disconnect(m_axes, &QniteAxes::yBoundsChanged, this, 0); 38 | 39 | if (m_axes != nullptr) { 40 | setWidth(m_axes->width()); 41 | setHeight(m_axes->height()); 42 | 43 | connect(m_axes, &QQuickItem::widthChanged, this, 44 | [=]() { this->setWidth(m_axes->width()); }); 45 | connect(m_axes, &QQuickItem::heightChanged, this, 46 | [=]() { this->setHeight(m_axes->height()); }); 47 | 48 | connect(m_axes, &QniteAxes::xBoundsChanged, this, &QniteArtist::update); 49 | connect(m_axes, &QniteAxes::yBoundsChanged, this, &QniteArtist::update); 50 | } 51 | } 52 | 53 | void QniteArtist::setSelectable(bool selectable) { 54 | if (selectable != m_selectable) { 55 | if (!selectable && selected()) { 56 | clearSelection(); 57 | } 58 | 59 | m_selectable = selectable; 60 | emit selectableChanged(); 61 | } 62 | } 63 | 64 | /*! 65 | If the artist is not selectable, completely skip the logic determining 66 | the selection state. 67 | */ 68 | bool QniteArtist::selected() const { return selectable() && isSelected(); } 69 | 70 | /*! 71 | Perform a selection operation within the path and return whether 72 | the selection event should be accepted or not. If a selection event 73 | is accepted, it should not be propagated to other artists. 74 | */ 75 | bool QniteArtist::select(const QList &) { 76 | return !m_propagate_selection; 77 | } 78 | 79 | /*! 80 | \overload select(const QList&) 81 | 82 | Provided to conveniently select single points 83 | */ 84 | bool QniteArtist::select(QPoint p) { return select(QList{p}); } 85 | 86 | void QniteArtist::setPropagateSelection(bool propagate) { 87 | if (m_propagate_selection != propagate) { 88 | m_propagate_selection = propagate; 89 | 90 | emit propagateSelectionChanged(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/qniteartist.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_ARTIST_H 2 | #define QNITE_ARTIST_H 3 | 4 | #include "qnanoquickitem.h" 5 | 6 | class QnitePen; 7 | class QniteAxes; 8 | class QniteArtist : public QNanoQuickItem { 9 | Q_OBJECT 10 | Q_PROPERTY(QniteAxes *axes READ axes NOTIFY axesChanged) 11 | Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY 12 | selectableChanged) 13 | Q_PROPERTY(bool selected READ selected NOTIFY selectedChanged) 14 | Q_PROPERTY(bool propagateSelection READ propagateSelection WRITE 15 | setPropagateSelection NOTIFY propagateSelectionChanged) 16 | Q_PROPERTY(QnitePen *pen READ pen CONSTANT) 17 | Q_PROPERTY(QnitePen *selectedPen READ selectedPen CONSTANT) 18 | Q_PROPERTY(QnitePen *highlightedPen READ highlightedPen CONSTANT) 19 | 20 | public: 21 | explicit QniteArtist(QQuickItem *parent = Q_NULLPTR); 22 | virtual ~QniteArtist(); 23 | 24 | QniteAxes *axes() const; 25 | bool selectable() const { return m_selectable; } 26 | bool selected() const; 27 | virtual bool select(const QPoint); 28 | virtual bool select(const QList &); 29 | bool propagateSelection() const { return m_propagate_selection; } 30 | 31 | void setAxes(QniteAxes *axes); 32 | void setSelectable(bool selectable); 33 | virtual void clearSelection() {} 34 | void setPropagateSelection(bool); 35 | 36 | QnitePen *pen() const { return m_pen; } 37 | QnitePen *selectedPen() const { return m_selectedPen; } 38 | QnitePen *highlightedPen() const { return m_highlightedPen; } 39 | 40 | QNanoQuickItemPainter *createItemPainter() const Q_DECL_OVERRIDE { 41 | return nullptr; 42 | } 43 | 44 | public Q_SLOTS: 45 | virtual void processData() = 0; 46 | 47 | Q_SIGNALS: 48 | void axesChanged(); 49 | void selectableChanged(); 50 | void selectedChanged(); 51 | void propagateSelectionChanged(); 52 | 53 | protected: 54 | virtual void updateAxes(); 55 | virtual bool isSelected() const { return false; } 56 | 57 | private: 58 | QniteAxes *m_axes; 59 | bool m_selectable; 60 | bool m_propagate_selection; 61 | QnitePen *m_pen; 62 | QnitePen *m_selectedPen; 63 | QnitePen *m_highlightedPen; 64 | }; 65 | 66 | #endif // QNITE_ARTIST_H 67 | -------------------------------------------------------------------------------- /src/qniteaxes.cpp: -------------------------------------------------------------------------------- 1 | #include "qniteaxes.h" 2 | #include "qniteartist.h" 3 | #include "qniteaxis.h" 4 | #include "qnitetool.h" 5 | 6 | QniteAxes::QniteAxes(QQuickItem *parent) 7 | : QQuickItem(parent), m_lowerXBound{0}, m_upperXBound{0}, m_xPadding{0}, 8 | m_lowerYBound{0}, m_upperYBound{0}, m_yPadding{0}, 9 | m_canvas{new QQuickItem}, m_axisX{nullptr}, m_axisY{nullptr} { 10 | m_canvas->setParentItem(this); 11 | m_canvas->setClip(true); 12 | 13 | connect(this, &QQuickItem::widthChanged, 14 | [=]() { this->m_canvas->setWidth(width()); }); 15 | connect(this, &QQuickItem::heightChanged, 16 | [=]() { this->m_canvas->setHeight(height()); }); 17 | } 18 | 19 | void QniteAxes::appendArtist(QniteArtist *artist) { 20 | if (artist != nullptr) { 21 | auto a = this->artists(); 22 | a.append(&a, artist); 23 | } 24 | } 25 | 26 | QQuickItem *QniteAxes::canvas() const { return m_canvas; } 27 | 28 | QQmlListProperty QniteAxes::artists() { 29 | return QQmlListProperty( 30 | this, nullptr, &QniteAxes::append_artists, &QniteAxes::count_artists, 31 | &QniteAxes::at_artists, &QniteAxes::clear_artists); 32 | } 33 | 34 | QQmlListProperty QniteAxes::tools() { 35 | return QQmlListProperty( 36 | this, nullptr, &QniteAxes::append_tools, &QniteAxes::count_tools, 37 | &QniteAxes::at_tools, &QniteAxes::clear_tools); 38 | } 39 | 40 | QList QniteAxes::xBounds() const { 41 | return {m_lowerXBound, m_upperXBound}; 42 | } 43 | 44 | void QniteAxes::setXBounds(const QList &bounds) { 45 | if (bounds.size() != 2) { 46 | return; 47 | } 48 | 49 | auto lowerBound = bounds.at(0); 50 | auto upperBound = bounds.at(1); 51 | 52 | if (lowerBound != m_lowerXBound || upperBound != m_upperXBound) { 53 | m_lowerXBound = lowerBound; 54 | m_upperXBound = upperBound; 55 | emit xBoundsChanged(); 56 | 57 | initAxisX(); 58 | } 59 | } 60 | 61 | void QniteAxes::setXPadding(qreal padding) { 62 | if (m_xPadding != padding) { 63 | m_xPadding = padding; 64 | 65 | emit xPaddingChanged(); 66 | 67 | initAxisX(); 68 | } 69 | } 70 | 71 | QList QniteAxes::yBounds() const { 72 | return {m_lowerYBound, m_upperYBound}; 73 | } 74 | 75 | void QniteAxes::setYBounds(const QList &bounds) { 76 | if (bounds.size() != 2) { 77 | return; 78 | } 79 | 80 | auto lowerBound = bounds.at(0); 81 | auto upperBound = bounds.at(1); 82 | 83 | if (lowerBound != m_lowerYBound || upperBound != m_upperYBound) { 84 | m_lowerYBound = lowerBound; 85 | m_upperYBound = upperBound; 86 | emit yBoundsChanged(); 87 | 88 | initAxisY(); 89 | } 90 | } 91 | 92 | void QniteAxes::setYPadding(qreal padding) { 93 | if (m_yPadding != padding) { 94 | m_yPadding = padding; 95 | 96 | emit yPaddingChanged(); 97 | 98 | initAxisY(); 99 | } 100 | } 101 | 102 | QniteAxis *QniteAxes::axisY() const { return m_axisY; } 103 | 104 | void QniteAxes::setAxisY(QniteAxis *axisY) { 105 | if (m_axisY != axisY) { 106 | m_axisY = axisY; 107 | m_axisY->setParentItem(this); 108 | emit axisYChanged(); 109 | 110 | initAxisY(); 111 | } 112 | } 113 | 114 | QniteAxis *QniteAxes::axisX() const { return m_axisX; } 115 | 116 | void QniteAxes::setAxisX(QniteAxis *axisX) { 117 | if (m_axisX != axisX) { 118 | m_axisX = axisX; 119 | m_axisX->setParentItem(this); 120 | emit axisXChanged(); 121 | 122 | initAxisX(); 123 | } 124 | } 125 | 126 | void QniteAxes::updateArtists() { 127 | for (auto artist : m_artists) { 128 | artist->update(); 129 | } 130 | } 131 | 132 | void QniteAxes::initAxisY() { 133 | if (m_axisY == nullptr) 134 | return; 135 | 136 | m_axisY->setLowerBound(m_lowerYBound - m_yPadding); 137 | m_axisY->setUpperBound(m_upperYBound + m_yPadding); 138 | } 139 | 140 | void QniteAxes::initAxisX() { 141 | if (m_axisX == nullptr) 142 | return; 143 | 144 | m_axisX->setLowerBound(m_lowerXBound - m_xPadding); 145 | m_axisX->setUpperBound(m_upperXBound + m_xPadding); 146 | } 147 | 148 | void QniteAxes::append_artists(QQmlListProperty *property, 149 | QniteArtist *value) { 150 | QniteAxes *axes = qobject_cast(property->object); 151 | value->setAxes(axes); 152 | value->setParentItem(axes->canvas()); // TODO: move down into artist? 153 | axes->m_artists.append(value); 154 | } 155 | 156 | QniteArtist *QniteAxes::at_artists(QQmlListProperty *property, 157 | int index) { 158 | QniteAxes *axes = qobject_cast(property->object); 159 | return axes->m_artists.at(index); 160 | } 161 | 162 | void QniteAxes::clear_artists(QQmlListProperty *property) { 163 | QniteAxes *axes = qobject_cast(property->object); 164 | while (!axes->m_artists.isEmpty()) 165 | delete axes->m_artists.takeFirst(); 166 | } 167 | 168 | int QniteAxes::count_artists(QQmlListProperty *property) { 169 | QniteAxes *axes = qobject_cast(property->object); 170 | return axes->m_artists.count(); 171 | } 172 | 173 | void QniteAxes::append_tools(QQmlListProperty *property, 174 | QniteTool *value) { 175 | QniteAxes *axes = qobject_cast(property->object); 176 | value->setAxes(axes); 177 | value->setParentItem(axes->canvas()); // TODO: move down into artist? 178 | axes->m_tools.append(value); 179 | } 180 | 181 | QniteTool *QniteAxes::at_tools(QQmlListProperty *property, 182 | int index) { 183 | QniteAxes *axes = qobject_cast(property->object); 184 | return axes->m_tools.at(index); 185 | } 186 | 187 | void QniteAxes::clear_tools(QQmlListProperty *property) { 188 | QniteAxes *axes = qobject_cast(property->object); 189 | while (!axes->m_tools.isEmpty()) 190 | delete axes->m_tools.takeFirst(); 191 | } 192 | 193 | int QniteAxes::count_tools(QQmlListProperty *property) { 194 | QniteAxes *axes = qobject_cast(property->object); 195 | return axes->m_tools.count(); 196 | } 197 | 198 | void QniteAxes::setOnTop(QniteArtist *artist) { 199 | if (canvas()->childItems().isEmpty()) 200 | return; 201 | 202 | if (m_artists.contains(artist)) { 203 | // the item is moved at the end of the list. This is NEEDED so that 204 | // items in the back that may be hidden are processed before the items 205 | // in foreground, allowing their selection. 206 | m_artists.removeAll(artist); 207 | m_artists.append(artist); 208 | 209 | // handle visual parenting, move the artist down the children list 210 | // so that it'll be drawn ontop of others 211 | auto lastItem = canvas()->childItems().last(); 212 | if (artist != lastItem) 213 | artist->stackAfter(lastItem); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/qniteaxes.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_AXES_H 2 | #define QNITE_AXES_H 3 | 4 | #include 5 | 6 | class QniteArtist; 7 | class QniteAxis; 8 | class QniteTool; 9 | class QniteAxes : public QQuickItem { 10 | friend class QniteTool; 11 | 12 | Q_OBJECT 13 | Q_PROPERTY(QQuickItem *canvas READ canvas CONSTANT) 14 | Q_PROPERTY( 15 | QList xBounds READ xBounds WRITE setXBounds NOTIFY xBoundsChanged) 16 | Q_PROPERTY( 17 | QList yBounds READ yBounds WRITE setYBounds NOTIFY yBoundsChanged) 18 | Q_PROPERTY( 19 | qreal xPadding READ xPadding WRITE setXPadding NOTIFY xPaddingChanged) 20 | Q_PROPERTY( 21 | qreal yPadding READ yPadding WRITE setYPadding NOTIFY yPaddingChanged) 22 | 23 | Q_PROPERTY(QQmlListProperty artists READ artists) 24 | Q_PROPERTY(QQmlListProperty tools READ tools) 25 | 26 | Q_PROPERTY(QniteAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged) 27 | Q_PROPERTY(QniteAxis *axisX READ axisX WRITE setAxisX NOTIFY axisXChanged) 28 | 29 | public: 30 | explicit QniteAxes(QQuickItem *parent = 0); 31 | virtual ~QniteAxes() {} 32 | 33 | Q_INVOKABLE void appendArtist(QniteArtist *artist); 34 | 35 | QQuickItem *canvas() const; 36 | QQmlListProperty artists(); 37 | QQmlListProperty tools(); 38 | 39 | void setOnTop(QniteArtist *); 40 | 41 | QList xBounds() const; 42 | void setXBounds(const QList &bounds); 43 | qreal xPadding() const { return m_xPadding; } 44 | void setXPadding(qreal padding); 45 | 46 | QList yBounds() const; 47 | void setYBounds(const QList &bounds); 48 | qreal yPadding() const { return m_yPadding; } 49 | void setYPadding(qreal padding); 50 | 51 | QniteAxis *axisY() const; 52 | void setAxisY(QniteAxis *axisY); 53 | QniteAxis *axisX() const; 54 | void setAxisX(QniteAxis *axisX); 55 | 56 | void updateArtists(); 57 | 58 | Q_SIGNALS: 59 | void xBoundsChanged(); 60 | void yBoundsChanged(); 61 | void xPaddingChanged(); 62 | void yPaddingChanged(); 63 | void axisYChanged(); 64 | void axisXChanged(); 65 | 66 | protected: 67 | void initAxisY(); 68 | void initAxisX(); 69 | 70 | private: 71 | static void append_artists(QQmlListProperty *property, 72 | QniteArtist *value); 73 | static QniteArtist *at_artists(QQmlListProperty *property, 74 | int index); 75 | static void clear_artists(QQmlListProperty *property); 76 | static int count_artists(QQmlListProperty *property); 77 | 78 | static void append_tools(QQmlListProperty *property, 79 | QniteTool *value); 80 | static QniteTool *at_tools(QQmlListProperty *property, int index); 81 | static void clear_tools(QQmlListProperty *property); 82 | static int count_tools(QQmlListProperty *property); 83 | 84 | qreal m_lowerXBound; 85 | qreal m_upperXBound; 86 | qreal m_xPadding; 87 | qreal m_lowerYBound; 88 | qreal m_upperYBound; 89 | qreal m_yPadding; 90 | 91 | QQuickItem *m_canvas; 92 | 93 | QList m_artists; 94 | QList m_tools; 95 | 96 | QniteAxis *m_axisX; 97 | QniteAxis *m_axisY; 98 | }; 99 | 100 | #endif // QNITE_AXES_H 101 | -------------------------------------------------------------------------------- /src/qniteaxis.cpp: -------------------------------------------------------------------------------- 1 | #include "qniteaxis.h" 2 | 3 | /*! TODO: add docs 4 | */ 5 | QniteAxis::QniteAxis(QQuickItem *parent) 6 | : QniteArtist(parent), m_size{0}, m_lowerBound{0}, m_upperBound{0}, 7 | m_flip{false}, m_position{0}, m_mapper{nullptr}, m_ticker{nullptr} {} 8 | 9 | void QniteAxis::setSize(qreal size) { 10 | if (m_size != size) { 11 | m_size = size; 12 | emit sizeChanged(); 13 | 14 | processData(); 15 | } 16 | } 17 | 18 | void QniteAxis::setLowerBound(qreal bound) { 19 | if (m_lowerBound != bound) { 20 | m_lowerBound = bound; 21 | emit lowerBoundChanged(); 22 | 23 | processData(); 24 | } 25 | } 26 | 27 | void QniteAxis::setUpperBound(qreal bound) { 28 | if (m_upperBound != bound) { 29 | m_upperBound = bound; 30 | emit upperBoundChanged(); 31 | 32 | processData(); 33 | } 34 | } 35 | 36 | void QniteAxis::setFlip(bool flip) { 37 | if (m_flip != flip) { 38 | m_flip = flip; 39 | emit flipChanged(); 40 | 41 | processData(); 42 | } 43 | } 44 | 45 | void QniteAxis::setTicker(QniteTicker *ticker) { 46 | if (m_ticker != ticker) { 47 | m_ticker = ticker; 48 | emit tickerChanged(); 49 | } 50 | } 51 | 52 | void QniteAxis::setMapper(QniteMapper *mapper) { 53 | if (m_mapper != mapper) { 54 | m_mapper = mapper; 55 | emit mapperChanged(); 56 | } 57 | } 58 | 59 | QList QniteAxis::majorTicks() const { return m_majorTicks; } 60 | 61 | QList QniteAxis::minorTicks() const { return m_minorTicks; } 62 | 63 | qreal QniteAxis::position() const { return m_position; } 64 | -------------------------------------------------------------------------------- /src/qniteaxis.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_AXIS_H 2 | #define QNITE_AXIS_H 3 | 4 | #include "qniteartist.h" 5 | 6 | class QniteMapper; 7 | class QniteTicker; 8 | class QniteAxis : public QniteArtist { 9 | Q_OBJECT 10 | Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged) 11 | Q_PROPERTY(qreal lowerBound READ lowerBound WRITE setLowerBound NOTIFY 12 | lowerBoundChanged) 13 | Q_PROPERTY(qreal upperBound READ upperBound WRITE setUpperBound NOTIFY 14 | upperBoundChanged) 15 | Q_PROPERTY(bool flip READ flip WRITE setFlip NOTIFY flipChanged) 16 | 17 | Q_PROPERTY(QniteMapper *mapper READ mapper CONSTANT) 18 | Q_PROPERTY(QniteTicker *ticker READ ticker CONSTANT) 19 | 20 | Q_PROPERTY(QList majorTicks READ majorTicks NOTIFY majorTicksChanged) 21 | Q_PROPERTY(QList minorTicks READ minorTicks NOTIFY minorTicksChanged) 22 | Q_PROPERTY(QStringList labels READ labels NOTIFY labelsChanged) 23 | 24 | public: 25 | explicit QniteAxis(QQuickItem *parent = 0); 26 | virtual ~QniteAxis() {} 27 | 28 | qreal size() const { return m_size; } 29 | void setSize(qreal size); 30 | qreal lowerBound() const { return m_lowerBound; } 31 | void setLowerBound(qreal bound); 32 | qreal upperBound() const { return m_upperBound; } 33 | void setUpperBound(qreal bound); 34 | bool flip() const { return m_flip; } 35 | void setFlip(bool flip); 36 | 37 | QniteTicker *ticker() const { return m_ticker; } 38 | void setTicker(QniteTicker *ticker); 39 | QniteMapper *mapper() const { return m_mapper; } 40 | void setMapper(QniteMapper *mapper); 41 | 42 | QList majorTicks() const; 43 | QList minorTicks() const; 44 | 45 | QStringList labels() const { return m_labels; } 46 | 47 | qreal position() const; 48 | 49 | Q_SIGNALS: 50 | void flipChanged(); 51 | void sizeChanged(); 52 | void lowerBoundChanged(); 53 | void upperBoundChanged(); 54 | 55 | void majorTicksChanged(); 56 | void minorTicksChanged(); 57 | 58 | void tickerChanged(); 59 | void mapperChanged(); 60 | 61 | void labelsChanged(); 62 | 63 | protected: 64 | qreal m_size; 65 | qreal m_lowerBound; 66 | qreal m_upperBound; 67 | bool m_flip; 68 | qreal m_position; 69 | 70 | QList m_majorTicks; 71 | QList m_minorTicks; 72 | 73 | QniteMapper *m_mapper; 74 | QniteTicker *m_ticker; 75 | 76 | QStringList m_labels; 77 | }; 78 | 79 | #endif // QNITE_AXIS_H 80 | -------------------------------------------------------------------------------- /src/qniteaxistick.cpp: -------------------------------------------------------------------------------- 1 | #include "qniteaxistick.h" 2 | 3 | /*! TODO: add docs 4 | */ 5 | QniteAxisTick::QniteAxisTick(QObject *parent) 6 | : QObject(parent), m_thick{0}, m_majSize{0}, m_minSize{0} {} 7 | 8 | QniteAxisTick::~QniteAxisTick() {} 9 | 10 | qreal QniteAxisTick::thick() const { return m_thick; } 11 | 12 | void QniteAxisTick::setThick(qreal thick) { 13 | if (m_thick != thick) { 14 | m_thick = thick; 15 | emit thickChanged(); 16 | } 17 | } 18 | 19 | qreal QniteAxisTick::majSize() const { return m_majSize; } 20 | 21 | void QniteAxisTick::setMajSize(qreal size) { 22 | if (m_majSize != size) { 23 | m_majSize = size; 24 | emit majSizeChanged(); 25 | } 26 | } 27 | 28 | qreal QniteAxisTick::minSize() const { return m_minSize; } 29 | 30 | void QniteAxisTick::setMinSize(qreal size) { 31 | if (m_minSize != size) { 32 | m_minSize = size; 33 | emit minSizeChanged(); 34 | } 35 | } 36 | 37 | QColor QniteAxisTick::color() const { return m_color; } 38 | 39 | void QniteAxisTick::setColor(const QColor &color) { 40 | if (m_color != color) { 41 | m_color = color; 42 | emit colorChanged(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/qniteaxistick.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_AXIS_TICK_H 2 | #define QNITE_AXIS_TICK_H 3 | 4 | #include 5 | #include 6 | 7 | class QniteAxisTick : public QObject { 8 | Q_OBJECT 9 | 10 | Q_PROPERTY(qreal thick READ thick WRITE setThick NOTIFY thickChanged) 11 | Q_PROPERTY(qreal majSize READ majSize WRITE setMajSize NOTIFY majSizeChanged) 12 | Q_PROPERTY(qreal minSize READ minSize WRITE setMinSize NOTIFY minSizeChanged) 13 | Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) 14 | 15 | public: 16 | explicit QniteAxisTick(QObject *parent = 0); 17 | virtual ~QniteAxisTick(); 18 | 19 | qreal thick() const; 20 | void setThick(qreal thick); 21 | qreal majSize() const; 22 | void setMajSize(qreal size); 23 | qreal minSize() const; 24 | void setMinSize(qreal size); 25 | 26 | QColor color() const; 27 | void setColor(const QColor &color); 28 | 29 | Q_SIGNALS: 30 | void thickChanged(); 31 | void majSizeChanged(); 32 | void minSizeChanged(); 33 | void colorChanged(); 34 | 35 | private: 36 | qreal m_thick; 37 | qreal m_majSize; 38 | qreal m_minSize; 39 | QColor m_color; 40 | }; 41 | 42 | #endif // QNITE_AXIS_TICK_H 43 | -------------------------------------------------------------------------------- /src/qnitecategoryaxis.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitecategoryaxis.h" 2 | #include "qnitelinearmapper.h" 3 | 4 | QniteCategoryAxis::QniteCategoryAxis(QQuickItem *parent) : QniteAxis{parent} { 5 | setMapper(new QniteLinearMapper(this)); 6 | } 7 | 8 | void QniteCategoryAxis::setValues(const QStringList &v) { 9 | if (m_values != v) { 10 | m_values = v; 11 | emit valuesChanged(); 12 | 13 | processData(); 14 | } 15 | } 16 | 17 | void QniteCategoryAxis::processData() { 18 | if (mapper() == nullptr) { 19 | return; 20 | } 21 | 22 | setLowerBound(0.0); 23 | setUpperBound(1.0); 24 | 25 | m_majorTicks.clear(); 26 | 27 | auto step = 1.0 / (m_values.size() + 1); 28 | 29 | for (auto i = 0; i < m_values.size(); ++i) { 30 | auto v = mapper()->mapTo(0.0, 1.0, 0.0, size(), step * (i + 1), flip()); 31 | m_majorTicks.push_back(v); 32 | } 33 | 34 | m_labels = m_values; 35 | 36 | emit labelsChanged(); 37 | emit majorTicksChanged(); 38 | } 39 | -------------------------------------------------------------------------------- /src/qnitecategoryaxis.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_CATEGORY_AXIS_H 2 | #define QNITE_CATEGORY_AXIS_H 3 | 4 | #include "qniteaxis.h" 5 | 6 | class QniteCategoryAxis : public QniteAxis { 7 | Q_OBJECT 8 | Q_PROPERTY( 9 | QStringList values READ values WRITE setValues NOTIFY valuesChanged) 10 | 11 | public: 12 | explicit QniteCategoryAxis(QQuickItem *parent = 0); 13 | virtual ~QniteCategoryAxis() {} 14 | 15 | QStringList values() const { return m_values; } 16 | void setValues(const QStringList &v); 17 | 18 | Q_SIGNALS: 19 | void valuesChanged(); 20 | 21 | public Q_SLOTS: 22 | virtual void processData(); 23 | 24 | private: 25 | QStringList m_values; 26 | }; 27 | 28 | #endif // QNITE_CATEGORY_AXIS_H 29 | -------------------------------------------------------------------------------- /src/qniteclipper.cpp: -------------------------------------------------------------------------------- 1 | #include "qniteclipper.h" 2 | 3 | QniteClipper::QniteClipper(QObject *parent) : QObject(parent) {} 4 | 5 | QniteClipper::~QniteClipper() {} 6 | 7 | void QniteClipper::clip(const QMap &xValues, 8 | const QMap &yValues, qreal xLower, 9 | qreal xUpper, qreal yLower, qreal yUpper, 10 | QMap &outX, QMap &outY) { 11 | // Assume x and y values have same ids 12 | auto ids = xValues.keys(); 13 | 14 | for (auto id : ids) { 15 | auto x = xValues.value(id); 16 | auto y = yValues.value(id); 17 | if (x >= xLower && x <= xUpper) { 18 | if (y >= yLower && y <= yUpper) { 19 | outX.insert(id, x); 20 | outY.insert(id, y); 21 | } 22 | } 23 | } 24 | } 25 | 26 | void QniteClipper::clip(const QList &xValues, 27 | const QList &yValues, qreal xLower, qreal xUpper, 28 | qreal yLower, qreal yUpper, QList &outX, 29 | QList &outY) { 30 | 31 | int size = qMin(xValues.size(), yValues.size()); 32 | 33 | for (int i = 0; i < size; i++) { 34 | auto x = xValues.at(i); 35 | auto y = yValues.at(i); 36 | if (x >= xLower && x <= xUpper) { 37 | if (y >= yLower && y <= yUpper) { 38 | outX << x; 39 | outY << y; 40 | } 41 | } 42 | } 43 | } 44 | 45 | void QniteClipper::clip(const QMap &values, qreal lower, 46 | qreal upper, QMap &out) { 47 | 48 | auto it = values.constBegin(); 49 | while (it != values.constEnd()) { 50 | auto v = it.value(); 51 | if (v >= lower && v <= upper) { 52 | out.insert(it.key(), v); 53 | } 54 | it++; 55 | } 56 | } 57 | 58 | void QniteClipper::clip(const QList &values, qreal lower, qreal upper, 59 | QList &out) { 60 | for (auto v : values) { 61 | if (v >= lower && v <= upper) { 62 | out << v; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/qniteclipper.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_CLIPPER_H 2 | #define QNITE_CLIPPER_H 3 | 4 | #include 5 | #include 6 | 7 | class QniteClipper : public QObject { 8 | Q_OBJECT 9 | public: 10 | explicit QniteClipper(QObject *parent = 0); 11 | virtual ~QniteClipper(); 12 | 13 | virtual void clip(const QMap &xValues, 14 | const QMap &yValues, qreal xLower, qreal xUpper, 15 | qreal yLower, qreal yUpper, QMap &outX, 16 | QMap &outY); 17 | 18 | virtual void clip(const QList &xValues, const QList &yValues, 19 | qreal xLower, qreal xUpper, qreal yLower, qreal yUpper, 20 | QList &outX, QList &outY); 21 | 22 | virtual void clip(const QMap &values, qreal lower, qreal upper, 23 | QMap &out); 24 | 25 | virtual void clip(const QList &values, qreal lower, qreal upper, 26 | QList &out); 27 | }; 28 | #endif // QNITE_CLIPPER_H 29 | -------------------------------------------------------------------------------- /src/qnitelinearaxis.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitelinearaxis.h" 2 | #include "qniteclipper.h" 3 | #include "qnitelinearmapper.h" 4 | #include "qnitelinearticker.h" 5 | 6 | QniteLinearAxis::QniteLinearAxis(QQuickItem *parent) : QniteAxis(parent) { 7 | setMapper(new QniteLinearMapper(this)); 8 | setTicker(new QniteLinearTicker(this)); 9 | } 10 | 11 | void QniteLinearAxis::processData() { 12 | // avoid ticker initialization when mapper is invalid 13 | if (m_mapper == nullptr) { 14 | m_ticker->reset(); 15 | } else { 16 | m_ticker->setBoundaries(m_lowerBound, m_upperBound); 17 | 18 | m_majorTicks.clear(); 19 | m_minorTicks.clear(); 20 | m_labels.clear(); 21 | 22 | // TODO: encapsulate in transformer pipeline 23 | // clip ticks 24 | QList maj, min; 25 | QniteClipper clipper; 26 | clipper.clip(m_ticker->majorTicks(), m_lowerBound, m_upperBound, maj); 27 | clipper.clip(m_ticker->minorTicks(), m_lowerBound, m_upperBound, min); 28 | 29 | // map to display 30 | for (auto v : maj) { 31 | m_majorTicks << m_mapper->mapTo(m_lowerBound, m_upperBound, 0, m_size, v, 32 | m_flip); 33 | } 34 | for (auto v : min) { 35 | m_minorTicks << m_mapper->mapTo(m_lowerBound, m_upperBound, 0, m_size, v, 36 | m_flip); 37 | } 38 | 39 | for (auto i = 0; i < maj.size(); ++i) { 40 | m_labels.push_back(QString("%1").arg(maj.at(i))); 41 | } 42 | 43 | // maps the axis position 44 | m_position = 45 | m_mapper->mapTo(m_lowerBound, m_upperBound, 0., m_size, 0., m_flip); 46 | } 47 | 48 | emit labelsChanged(); 49 | emit majorTicksChanged(); 50 | emit minorTicksChanged(); 51 | } 52 | -------------------------------------------------------------------------------- /src/qnitelinearaxis.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_LINEAR_AXIS_H 2 | #define QNITE_LINEAR_AXIS_H 3 | 4 | #include "qniteaxis.h" 5 | 6 | class QniteLinearAxis : public QniteAxis { 7 | public: 8 | explicit QniteLinearAxis(QQuickItem *parent = 0); 9 | virtual ~QniteLinearAxis() {} 10 | 11 | public Q_SLOTS: 12 | virtual void processData(); 13 | }; 14 | 15 | #endif // QNITE_LINEAR_AXIS_H 16 | -------------------------------------------------------------------------------- /src/qnitelinearmapper.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitelinearmapper.h" 2 | 3 | /*! 4 | \qmltype LinearMapper 5 | \instantiates QniteLinearMapper 6 | \ingroup transforms 7 | \inqmlmodule Qnite 8 | \brief Transform a value from the scale space to the item space 9 | 10 | TODO: add a long description 11 | */ 12 | QniteLinearMapper::QniteLinearMapper(QObject *parent) : QniteMapper(parent) {} 13 | 14 | QniteLinearMapper::~QniteLinearMapper() {} 15 | 16 | qreal QniteLinearMapper::mapTo(qreal sourceLower, qreal sourceUpper, 17 | qreal destLower, qreal destUpper, qreal value, 18 | bool flip) { 19 | qreal sourceSize = qAbs(sourceUpper - sourceLower); 20 | qreal destSize = qAbs(destLower - destUpper); 21 | 22 | if (sourceSize == 0.0 || destSize == 0.0) 23 | return 0.0; // TODO: size is invalid. return 0 or raise exception? 24 | 25 | qreal f = destSize / sourceSize; 26 | qreal v = f * qAbs(sourceLower - value); 27 | 28 | // TODO: maybe raise an exception when the value is out-of-bounds 29 | if (value < sourceLower) 30 | v *= -1; 31 | 32 | if (flip) { 33 | return destUpper - v; 34 | } 35 | 36 | return destLower + v; 37 | } 38 | -------------------------------------------------------------------------------- /src/qnitelinearmapper.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_LINEAR_MAPPER_H 2 | #define QNITE_LINEAR_MAPPER_H 3 | 4 | #include "qnitemapper.h" 5 | 6 | class QniteLinearMapper : public QniteMapper { 7 | Q_OBJECT 8 | 9 | public: 10 | explicit QniteLinearMapper(QObject *parent = 0); 11 | virtual ~QniteLinearMapper(); 12 | 13 | using QniteMapper::mapTo; 14 | 15 | virtual qreal mapTo(qreal sourceLower, qreal sourceUpper, qreal destLower, 16 | qreal destUpper, qreal value, bool flip = false); 17 | }; 18 | 19 | #endif // QNITE_LINEAR_MAPPER_H 20 | -------------------------------------------------------------------------------- /src/qnitelinearticker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "qnitelinearticker.h" 7 | 8 | // defines 9 | #define DEFAULT_NUM_STEPS 10 10 | #define DEFAULT_LOOSENESS true 11 | 12 | namespace { 13 | 14 | double nice(double x, bool round) { 15 | int exp; 16 | double f; 17 | double nicef; 18 | 19 | exp = std::floor(std::log10(x)); 20 | f = x / std::pow(10., exp); 21 | 22 | if (round) { 23 | if (f < 1.5) 24 | nicef = 1.; 25 | else if (f < 3.) 26 | nicef = 2.; 27 | else if (f < 7.) 28 | nicef = 5.; 29 | else 30 | nicef = 10.; 31 | } else { 32 | if (f <= 1.) 33 | nicef = 1.; 34 | else if (f <= 2.) 35 | nicef = 2.; 36 | else if (f <= 5.) 37 | nicef = 5.; 38 | else 39 | nicef = 10.; 40 | } 41 | 42 | return nicef * std::pow(10., exp); 43 | } 44 | 45 | void fill(QList &ticks, qreal min, qreal max, int steps) { 46 | double range = nice(max - min, false); 47 | double d = nice(range / (steps - 1), true); 48 | double graph_min = std::floor(min / d) * d; // loose version of the algo 49 | double graph_max = std::ceil(max / d) * d; // loose version of the algo 50 | // nFrac is the number of decimal numbers it'd be nice to show 51 | double nFrac = std::max(-std::floor(std::log10(d)), 0.); 52 | // we use it to better approximate values 53 | double precision = std::pow(10, nFrac); 54 | 55 | for (double x = graph_min; x <= graph_max + .5 * d; x += d) { 56 | // round to the "precision" decimal place 57 | double val = qRound64(x * precision); 58 | ticks.append(val / precision); 59 | // ticks.append(x); 60 | } 61 | } 62 | 63 | } // namespace 64 | 65 | QniteLinearTicker::QniteLinearTicker(QObject *parent) 66 | : QniteTicker(parent), m_loose{DEFAULT_LOOSENESS} { 67 | setNumSteps(DEFAULT_NUM_STEPS); 68 | } 69 | 70 | void QniteLinearTicker::buildTicks() { 71 | qreal u = upper(); 72 | qreal l = lower(); 73 | qreal delta = u - l; 74 | 75 | if (delta <= 0) { 76 | return; 77 | } 78 | 79 | qreal exp = std::floor(std::log10(delta)); 80 | qreal factor = 0.0; 81 | 82 | if (exp <= 1) { 83 | factor = std::pow(10, std::abs(exp) + 1); 84 | u *= factor; 85 | l *= factor; 86 | } 87 | 88 | // build major ticks 89 | QList majors; 90 | fill(majors, l, u, numSteps()); 91 | 92 | // build min ticks 93 | QList mins; 94 | for (int i = 0; i < majors.size() - 1; i++) { 95 | fill(mins, majors[i], majors[i + 1], std::ceil(numSteps() / 2.)); 96 | } 97 | 98 | // remove duplicates 99 | QSet minSet = mins.toSet(); 100 | QSet majorSet = majors.toSet(); 101 | 102 | auto uniqueset = minSet.subtract(majorSet); 103 | mins = uniqueset.values(); 104 | std::sort(mins.begin(), mins.end(), std::less()); 105 | 106 | // perform downscale if needed 107 | if (factor != 0.0) { 108 | auto downScale = [factor](qreal &n) { n /= factor; }; 109 | std::for_each(mins.begin(), mins.end(), downScale); 110 | std::for_each(majors.begin(), majors.end(), downScale); 111 | } 112 | 113 | // set tick series 114 | setMinorTicks(mins); 115 | setMajorTicks(majors); 116 | } 117 | 118 | void QniteLinearTicker::setLooseNiceness(bool is_loose) { m_loose = is_loose; } 119 | 120 | bool QniteLinearTicker::looseNiceness() const { return m_loose; } 121 | 122 | void QniteLinearTicker::reset() { 123 | QniteTicker::reset(); 124 | m_loose = DEFAULT_LOOSENESS; 125 | setNumSteps(DEFAULT_NUM_STEPS); 126 | } 127 | 128 | void QniteLinearTicker::setNumSteps(int steps) { 129 | if (steps < 2) { 130 | return; 131 | } 132 | 133 | QniteTicker::setNumSteps(steps); 134 | } 135 | -------------------------------------------------------------------------------- /src/qnitelinearticker.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITELINEARTICKER_H 2 | #define QNITELINEARTICKER_H 3 | 4 | #include "qniteticker.h" 5 | 6 | class QniteLinearTicker : public QniteTicker { 7 | Q_OBJECT 8 | 9 | public: 10 | explicit QniteLinearTicker(QObject *parent = 0); 11 | virtual ~QniteLinearTicker() {} 12 | 13 | virtual void reset(); 14 | void setLooseNiceness(bool); 15 | virtual void setNumSteps(int); 16 | 17 | bool looseNiceness() const; 18 | 19 | protected: 20 | virtual void buildTicks(); 21 | 22 | private: 23 | bool m_loose; 24 | }; 25 | 26 | #endif // QNITELINEARTICKER_H 27 | -------------------------------------------------------------------------------- /src/qnitemapper.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitemapper.h" 2 | 3 | /*! 4 | \qmltype Mapper 5 | \instantiates QniteMapper 6 | \ingroup transforms 7 | \inqmlmodule Qnite 8 | \brief Transform a value from the scale space to the item space 9 | 10 | TODO: add a long description 11 | */ 12 | QniteMapper::QniteMapper(QObject *parent) : QObject(parent) {} 13 | 14 | QniteMapper::~QniteMapper() {} 15 | 16 | QMap QniteMapper::mapTo(qreal sourceLower, qreal sourceUpper, 17 | qreal destLower, qreal destUpper, 18 | const QMap &values, bool flip) { 19 | QMap out; 20 | 21 | auto it = values.constBegin(); 22 | while (it != values.constEnd()) { 23 | auto v = 24 | mapTo(sourceLower, sourceUpper, destLower, destUpper, it.value(), flip); 25 | out.insert(it.key(), v); 26 | it++; 27 | } 28 | return out; 29 | } 30 | -------------------------------------------------------------------------------- /src/qnitemapper.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_MAPPER_H 2 | #define QNITE_MAPPER_H 3 | 4 | #include 5 | #include 6 | 7 | class QniteMapper : public QObject { 8 | Q_OBJECT 9 | public: 10 | explicit QniteMapper(QObject *parent = 0); 11 | virtual ~QniteMapper(); 12 | 13 | virtual QMap mapTo(qreal sourceLower, qreal sourceUpper, 14 | qreal destLower, qreal destUpper, 15 | const QMap &values, 16 | bool flip = false); 17 | 18 | virtual qreal mapTo(qreal sourceLower, qreal sourceUpper, qreal destLower, 19 | qreal destUpper, qreal values, bool flip = false) = 0; 20 | }; 21 | #endif // QNITE_MAPPER_H 22 | -------------------------------------------------------------------------------- /src/qniteticker.cpp: -------------------------------------------------------------------------------- 1 | #include "qniteticker.h" 2 | 3 | #define DEFAULT_NUM_STEPS 0 4 | #define DEFAULT_LOWER_B 0 5 | #define DEFAULT_UPPER_B 0 6 | 7 | /*! 8 | \qmltype Ticker 9 | \instantiates QniteTicker 10 | \ingroup scaling 11 | \inqmlmodule Qnite 12 | \brief Divide axes scales into tickers 13 | 14 | TODO: add a long description 15 | */ 16 | QniteTicker::QniteTicker(QObject *parent) 17 | : QObject(parent), m_lowerBound{DEFAULT_LOWER_B}, 18 | m_upperBound{DEFAULT_UPPER_B}, m_numSteps{DEFAULT_NUM_STEPS} {} 19 | 20 | QList QniteTicker::values() const { return m_values; } 21 | 22 | void QniteTicker::setValues(const QList &values) { 23 | if (m_values != values) { 24 | 25 | m_values = values; 26 | emit valuesChanged(); 27 | 28 | // compute min and max bounds 29 | std::sort(m_values.begin(), m_values.end(), std::less()); 30 | setBoundaries(m_values.first(), m_values.last()); 31 | } 32 | } 33 | 34 | QList QniteTicker::minorTicks() const { return m_minorTicks; } 35 | 36 | void QniteTicker::setMinorTicks(const QList &ticks) { 37 | if (m_minorTicks != ticks) { 38 | m_minorTicks = ticks; 39 | emit minorTicksChanged(); 40 | } 41 | } 42 | 43 | const QList &QniteTicker::majorTicks() const { return m_majorTicks; } 44 | 45 | void QniteTicker::setMajorTicks(const QList &ticks) { 46 | if (m_majorTicks != ticks) { 47 | m_majorTicks = ticks; 48 | emit majorTicksChanged(); 49 | } 50 | } 51 | 52 | void QniteTicker::setNumSteps(int steps) { 53 | if (m_numSteps != steps) { 54 | m_numSteps = steps; 55 | emit numStepsChanged(); 56 | 57 | // ticks need to be rebuilt 58 | doBuildTicks(); 59 | } 60 | } 61 | 62 | void QniteTicker::setBoundaries(qreal lower, qreal upper) { 63 | bool modified = false; 64 | 65 | if (m_lowerBound != lower) { 66 | m_lowerBound = lower; 67 | modified = true; 68 | } 69 | 70 | if (m_upperBound != upper) { 71 | m_upperBound = upper; 72 | modified = true; 73 | } 74 | 75 | if (modified) { 76 | emit boundariesChanged(); 77 | 78 | // ticks need to be rebuilt 79 | doBuildTicks(); 80 | } 81 | } 82 | 83 | void QniteTicker::reset() { 84 | m_lowerBound = DEFAULT_LOWER_B; 85 | m_upperBound = DEFAULT_UPPER_B; 86 | m_numSteps = DEFAULT_NUM_STEPS; 87 | m_values.clear(); 88 | m_minorTicks.clear(); 89 | m_majorTicks.clear(); 90 | } 91 | 92 | QList QniteTicker::boundaries() const { 93 | QList ret; 94 | ret << m_lowerBound << m_upperBound; 95 | return ret; 96 | } 97 | 98 | void QniteTicker::doBuildTicks() { 99 | buildTicks(); 100 | emit tickersBuilt(); 101 | } 102 | -------------------------------------------------------------------------------- /src/qniteticker.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITETICKER_H 2 | #define QNITETICKER_H 3 | 4 | #include 5 | 6 | class QniteTicker : public QObject { 7 | Q_OBJECT 8 | Q_PROPERTY( 9 | QList values READ values WRITE setValues NOTIFY valuesChanged) 10 | Q_PROPERTY(QList minorTicks READ minorTicks WRITE setMinorTicks NOTIFY 11 | minorTicksChanged) 12 | Q_PROPERTY(QList majorTicks READ majorTicks WRITE setMajorTicks NOTIFY 13 | majorTicksChanged) 14 | Q_PROPERTY(int numSteps READ numSteps WRITE setNumSteps) 15 | 16 | Q_SIGNALS: 17 | void valuesChanged(); 18 | void minorTicksChanged(); 19 | void majorTicksChanged(); 20 | void numStepsChanged(); 21 | void boundariesChanged(); 22 | void tickersBuilt(); 23 | 24 | public: 25 | explicit QniteTicker(QObject *parent = 0); 26 | virtual ~QniteTicker() {} 27 | 28 | virtual void reset(); 29 | void setValues(const QList &); 30 | void setBoundaries(qreal lower, qreal upper); 31 | void setMinorTicks(const QList &); 32 | void setMajorTicks(const QList &); 33 | virtual void setNumSteps(int); 34 | 35 | QList values() const; 36 | QList minorTicks() const; 37 | const QList &majorTicks() const; 38 | QList boundaries() const; 39 | qreal lower() const { return m_lowerBound; } 40 | qreal upper() const { return m_upperBound; } 41 | int numSteps() const { return m_numSteps; } 42 | 43 | protected: 44 | virtual void buildTicks() = 0; 45 | 46 | private: 47 | void doBuildTicks(); 48 | 49 | QList m_minorTicks; 50 | QList m_majorTicks; 51 | QList m_values; 52 | 53 | qreal m_lowerBound; 54 | qreal m_upperBound; 55 | 56 | int m_numSteps; 57 | }; 58 | 59 | #endif // QNITETICKER_H 60 | -------------------------------------------------------------------------------- /src/qnitexyartist.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitexyartist.h" 2 | #include "qniteaxes.h" 3 | #include "qniteaxis.h" 4 | #include "qniteclipper.h" 5 | #include "qnitemapper.h" 6 | 7 | QniteXYArtist::QniteXYArtist(QQuickItem *parent) 8 | : QniteArtist(parent), m_clipper{nullptr}, m_xMapper{nullptr}, 9 | m_yMapper{nullptr} {} 10 | 11 | QniteXYArtist::~QniteXYArtist() {} 12 | 13 | QList QniteXYArtist::xValues() { return m_xValues.values(); } 14 | 15 | void QniteXYArtist::setXValues(const QList &values) { 16 | if (m_xValues.values() != values) { 17 | m_xValues.clear(); 18 | for (int i = 0; i < values.size(); i++) { 19 | m_xValues.insert(i, values.at(i)); 20 | } 21 | // TODO: transform the values here and cache 22 | emit xValuesChanged(); 23 | update(); 24 | 25 | // clear the current selection after any changes on X values 26 | clearSelection(); 27 | } 28 | } 29 | 30 | QList QniteXYArtist::yValues() { return m_yValues.values(); } 31 | 32 | void QniteXYArtist::setYValues(const QList &values) { 33 | if (m_yValues.values() != values) { 34 | m_yValues.clear(); 35 | for (int i = 0; i < values.size(); i++) { 36 | m_yValues.insert(i, values.at(i)); 37 | } 38 | // TODO: transform the values here and cache 39 | emit yValuesChanged(); 40 | update(); 41 | 42 | // clear the current selection after any changes on Y values 43 | clearSelection(); 44 | } 45 | } 46 | 47 | QniteMapper *QniteXYArtist::xMapper() const { return m_xMapper; } 48 | 49 | void QniteXYArtist::setXMapper(QniteMapper *mapper) { 50 | // TODO: when the mapper is set we should 51 | // connect to its factorChanged event and trigger an update 52 | // and disconnect the previous one 53 | if (mapper != m_xMapper) { 54 | m_xMapper = mapper; 55 | // TODO: emit a signal?? 56 | } 57 | } 58 | 59 | QniteMapper *QniteXYArtist::yMapper() const { return m_yMapper; } 60 | 61 | void QniteXYArtist::setYMapper(QniteMapper *mapper) { 62 | // TODO: when the mapper is set we should 63 | // connect to its factorChanged event and trigger an update 64 | // and disconnect the previous one 65 | if (mapper != m_yMapper) { 66 | m_yMapper = mapper; 67 | // TODO: emit a signal?? 68 | } 69 | } 70 | 71 | QniteClipper *QniteXYArtist::clipper() const { return m_clipper; } 72 | 73 | void QniteXYArtist::setClipper(QniteClipper *clipper) { 74 | if (m_clipper != clipper) { 75 | m_clipper = clipper; 76 | // TODO: signal???? 77 | } 78 | } 79 | 80 | QMap QniteXYArtist::xMapped() const { return m_xMapped; } 81 | 82 | QMap QniteXYArtist::yMapped() const { return m_yMapped; } 83 | 84 | QList QniteXYArtist::xProcessed() const { return m_xProcessed; } 85 | 86 | QList QniteXYArtist::yProcessed() const { return m_yProcessed; } 87 | 88 | void QniteXYArtist::processData() { 89 | if (qMin(xValues().size(), yValues().size()) < 1) { 90 | m_xMapped.clear(); 91 | m_yMapped.clear(); 92 | 93 | m_xProcessed.clear(); 94 | m_yProcessed.clear(); 95 | 96 | return; 97 | } 98 | 99 | // get bounds 100 | qreal xLower = axes()->axisX()->lowerBound(); 101 | qreal xUpper = axes()->axisX()->upperBound(); 102 | qreal yLower = axes()->axisY()->lowerBound(); 103 | qreal yUpper = axes()->axisY()->upperBound(); 104 | 105 | // TODO: this should be improved. clipping should be done only when bounds 106 | // changes and tranforsm should always be performed 107 | QMap xClipped; 108 | QMap yClipped; 109 | // clip non visible data 110 | if (clipper() != nullptr) { 111 | clipper()->clip(m_xValues, m_yValues, xLower, xUpper, yLower, yUpper, 112 | xClipped, yClipped); 113 | } else { 114 | xClipped = m_xValues; 115 | yClipped = m_yValues; 116 | } 117 | 118 | // map to display 119 | m_xMapped = xMapper()->mapTo(xLower, xUpper, 0, width(), xClipped); 120 | m_yMapped = yMapper()->mapTo(yLower, yUpper, 0, height(), yClipped, true); 121 | 122 | // TODO: this is ugly and inefficient. move into a pipelino or something 123 | // similar move to the output area 124 | m_xProcessed = m_xMapped.values(); 125 | m_yProcessed = m_yMapped.values(); 126 | } 127 | 128 | void QniteXYArtist::updateAxes() { 129 | QniteArtist::updateAxes(); 130 | 131 | QniteAxes *axes = this->axes(); 132 | if (axes != nullptr) { 133 | // TODO: find a better way to handle axis bindings 134 | if (axes->axisX() != nullptr) 135 | this->setXMapper(axes->axisX()->mapper()); 136 | 137 | if (axes->axisY() != nullptr) 138 | this->setYMapper(axes->axisY()->mapper()); 139 | 140 | disconnect(axes, SIGNAL(axisXChanged()), this, nullptr); 141 | disconnect(axes, SIGNAL(axisYChanged()), this, nullptr); 142 | 143 | connect(axes, &QniteAxes::axisXChanged, this, 144 | [=]() { this->setXMapper(axes->axisX()->mapper()); }); 145 | connect(axes, &QniteAxes::axisYChanged, this, 146 | [=]() { this->setYMapper(axes->axisY()->mapper()); }); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/qnitexyartist.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_XY_ARTIST_H 2 | #define QNITE_XY_ARTIST_H 3 | 4 | #include "qniteartist.h" 5 | 6 | class QniteClipper; 7 | class QniteMapper; 8 | class QniteXYArtist : public QniteArtist { 9 | Q_OBJECT 10 | // clang-format off 11 | Q_PROPERTY(QList xValues READ xValues WRITE setXValues NOTIFY xValuesChanged) 12 | Q_PROPERTY(QList yValues READ yValues WRITE setYValues NOTIFY yValuesChanged) 13 | // clang-format on 14 | 15 | public: 16 | explicit QniteXYArtist(QQuickItem *parent = nullptr); 17 | virtual ~QniteXYArtist() Q_DECL_OVERRIDE; 18 | 19 | QList xValues(); 20 | void setXValues(const QList &values); 21 | QList yValues(); 22 | void setYValues(const QList &values); 23 | 24 | QniteMapper *xMapper() const; 25 | void setXMapper(QniteMapper *mapper); 26 | QniteMapper *yMapper() const; 27 | void setYMapper(QniteMapper *mapper); 28 | QniteClipper *clipper() const; 29 | void setClipper(QniteClipper *clipper); 30 | 31 | QMap xMapped() const; 32 | QMap yMapped() const; 33 | 34 | QList xProcessed() const; 35 | QList yProcessed() const; 36 | 37 | public Q_SLOTS: 38 | virtual void processData() Q_DECL_OVERRIDE; 39 | 40 | Q_SIGNALS: 41 | void xValuesChanged(); 42 | void yValuesChanged(); 43 | 44 | protected: 45 | virtual void updateAxes() Q_DECL_OVERRIDE; 46 | 47 | QMap m_xMapped; 48 | QMap m_yMapped; 49 | 50 | QList m_xProcessed; 51 | QList m_yProcessed; 52 | 53 | QMap m_xValues; 54 | QMap m_yValues; 55 | 56 | private: 57 | QniteClipper *m_clipper; 58 | QniteMapper *m_xMapper; 59 | QniteMapper *m_yMapper; 60 | }; 61 | 62 | #endif // QNITE_XY_ARTIST_H 63 | -------------------------------------------------------------------------------- /src/src.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | StaticLibrary { 5 | name: "qnite" 6 | 7 | Depends { name: "qnanopainter" } 8 | 9 | Depends { name: "cpp" } 10 | cpp.includePaths: [ ".", "tools", "items" ] 11 | cpp.cxxLanguageVersion: "c++14" 12 | 13 | files: [ 14 | "qniteartist.cpp", 15 | "qnitexyartist.cpp", 16 | "qniteaxis.cpp", 17 | "qnitelinearaxis.cpp", 18 | "qnitecategoryaxis.cpp", 19 | "qniteaxistick.cpp", 20 | "qniteaxes.cpp", 21 | "qniteclipper.cpp", 22 | "qnitemapper.cpp", 23 | "qnitelinearmapper.cpp", 24 | "qniteticker.cpp", 25 | "qnitelinearticker.cpp", 26 | "items/qnitebar.cpp", 27 | "items/qnitebarpainter.cpp", 28 | "items/qnitecircle.cpp", 29 | "items/qnitecirclepainter.cpp", 30 | "items/qnitegrid.cpp", 31 | "items/qnitegridpainter.cpp", 32 | "items/qniteline.cpp", 33 | "items/qnitelinepainter.cpp", 34 | "items/qnitepen.cpp", 35 | "items/qnitespline.cpp", 36 | "tools/qnitetool.cpp", 37 | "tools/qniteselectiontool.cpp", 38 | "tools/qnitepointselectiontool.cpp", 39 | "tools/qnitepathselectiontool.cpp", 40 | "tools/qnitepathpainter.cpp", 41 | "tools/qnitezoomtool.cpp", 42 | "tools/qnitezoompainter.cpp", 43 | "qnite.h", 44 | "qniteartist.h", 45 | "qnitexyartist.h", 46 | "qniteaxis.h", 47 | "qnitelinearaxis.h", 48 | "qnitecategoryaxis.h", 49 | "qniteaxistick.h", 50 | "qniteaxes.h", 51 | "qniteclipper.h", 52 | "qnitemapper.h", 53 | "qnitelinearmapper.h", 54 | "qniteticker.h", 55 | "qnitelinearticker.h", 56 | "items/qnitebar.h", 57 | "items/qnitebarpainter.h", 58 | "items/qnitecircle.h", 59 | "items/qnitecirclepainter.h", 60 | "items/qniteline.h", 61 | "items/qnitegrid.h", 62 | "items/qnitegridpainter.h", 63 | "items/qnitelinepainter.h", 64 | "items/qnitespline.h", 65 | "items/qnitepen.h", 66 | "tools/qnitetool.h", 67 | "tools/qniteselectiontool.h", 68 | "tools/qnitepointselectiontool.h", 69 | "tools/qnitepathselectiontool.h", 70 | "tools/qnitepathpainter.h", 71 | "tools/qnitezoomtool.h", 72 | "tools/qnitezoompainter.h", 73 | "qnite.qrc", 74 | ] 75 | 76 | Export { 77 | Depends { name: "qnanopainter" } 78 | Depends { name: "Qt.quick" } 79 | Depends { name: "cpp" } 80 | cpp.includePaths: [ ".", "tools", "items" ] 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/tools/qnitepathpainter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitepathpainter.h" 4 | #include "qnitepathselectiontool.h" 5 | 6 | Q_LOGGING_CATEGORY(qnitepathpainter, "qnite.path.painter") 7 | 8 | QnitePathPainter::QnitePathPainter() : QNanoQuickItemPainter{} {} 9 | 10 | void QnitePathPainter::synchronize(QNanoQuickItem *item) { 11 | qCDebug(qnitepathpainter) << "synchronizing qnitepath"; 12 | 13 | auto path = static_cast(item); 14 | if (path != Q_NULLPTR) { 15 | 16 | m_points = path->m_selection; 17 | 18 | m_pen = path->pen()->data(); 19 | } 20 | } 21 | 22 | void QnitePathPainter::paint(QNanoPainter *painter) { 23 | qCDebug(qnitepathpainter) << "painting qnitepath"; 24 | 25 | // avoid drawing when there are less than 2 points 26 | if (m_points.size() < 2) { 27 | return; 28 | } 29 | 30 | painter->setStrokeStyle(QNanoColor::fromQColor(m_pen.stroke)); 31 | if (m_pen.fill.isValid()) { 32 | painter->setFillStyle(QNanoColor::fromQColor(m_pen.fill)); 33 | } 34 | painter->setLineWidth(m_pen.width); 35 | painter->setLineJoin(m_pen.join); 36 | painter->setLineCap(m_pen.cap); 37 | 38 | painter->beginPath(); 39 | painter->moveTo(m_points.at(0).x(), m_points.at(0).y()); 40 | for (const auto &p : m_points) { 41 | painter->lineTo(p.x(), p.y()); 42 | } 43 | painter->closePath(); 44 | painter->fill(); 45 | painter->stroke(); 46 | } 47 | -------------------------------------------------------------------------------- /src/tools/qnitepathpainter.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITEPATHPAINTER_H 2 | #define QNITEPATHPAINTER_H 3 | 4 | #include "qnanoquickitempainter.h" 5 | #include "qnitepen.h" 6 | 7 | class QnitePathPainter : public QNanoQuickItemPainter { 8 | public: 9 | QnitePathPainter(); 10 | 11 | void synchronize(QNanoQuickItem *item) Q_DECL_OVERRIDE; 12 | void paint(QNanoPainter *painter) Q_DECL_OVERRIDE; 13 | 14 | private: 15 | QList m_points; 16 | 17 | QnitePen::PenData m_pen; 18 | }; 19 | 20 | #endif // QNITEPATHPAINTER_H 21 | -------------------------------------------------------------------------------- /src/tools/qnitepathselectiontool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "qniteartist.h" 6 | #include "qnitepathpainter.h" 7 | #include "qnitepathselectiontool.h" 8 | #include "qnitepen.h" 9 | 10 | QnitePathSelectionTool::QnitePathSelectionTool(QQuickItem *parent) 11 | : QniteSelectionTool(parent), m_pen{new QnitePen} { 12 | m_pen->setFill("#e3f2fd"); 13 | m_pen->setStroke("#2196f3"); 14 | m_pen->setWidth(2.0); 15 | } 16 | 17 | QVariantList QnitePathSelectionTool::selectionPath() const { 18 | QVariantList values; 19 | for (auto &p : m_selection) 20 | values.push_back(QVariant{p}); 21 | return values; 22 | } 23 | 24 | void QnitePathSelectionTool::begin(const QPoint &point) { 25 | m_selection << point; 26 | clearSelection(); // TODO: this is a "select exclusive" behaviour, should not 27 | // be hardcoded 28 | select(); 29 | emit selectionPathChanged(); 30 | update(); 31 | } 32 | 33 | void QnitePathSelectionTool::append(const QPoint &point) { 34 | m_selection << point; 35 | select(); 36 | emit selectionPathChanged(); 37 | update(); 38 | } 39 | 40 | void QnitePathSelectionTool::end() { 41 | m_selection.clear(); 42 | emit selectionPathChanged(); 43 | update(); 44 | } 45 | 46 | QNanoQuickItemPainter *QnitePathSelectionTool::createItemPainter() const { 47 | return new QnitePathPainter; 48 | } 49 | 50 | void QnitePathSelectionTool::mousePressEvent(QMouseEvent *event) { 51 | begin(event->pos()); 52 | } 53 | 54 | void QnitePathSelectionTool::mouseMoveEvent(QMouseEvent *event) { 55 | append(event->pos()); 56 | } 57 | 58 | void QnitePathSelectionTool::mouseReleaseEvent(QMouseEvent *) { end(); } 59 | 60 | // QSGNode* QnitePathSelectionTool::updatePaintNode(QSGNode *oldNode, 61 | // UpdatePaintNodeData *) 62 | //{ 63 | // auto node = static_cast(oldNode); 64 | // QSGGeometry* geometry; 65 | 66 | // if (!node) { 67 | // node = new QSGGeometryNode; 68 | // geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 69 | // m_selection.size()); geometry->setDrawingMode(GL_TRIANGLE_FAN); 70 | // node->setGeometry(geometry); 71 | // node->setFlag(QSGNode::OwnsGeometry); 72 | 73 | // QSGFlatColorMaterial *material = new QSGFlatColorMaterial; 74 | // material->setColor(QColor(119, 204, 204, 50)); // TODO: this color should 75 | // be a property node->setMaterial(material); 76 | // node->setFlag(QSGNode::OwnsMaterial); 77 | // } else { 78 | // geometry = node->geometry(); 79 | // geometry->allocate(m_selection.size()); 80 | // } 81 | 82 | // QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D(); 83 | // for(int i = 0; i < m_selection.size(); ++i) { 84 | // auto& f = m_selection.at(i); 85 | // vertices[i].set(f.x(), f.y()); 86 | // } 87 | 88 | // node->markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial); 89 | 90 | // return node; 91 | //} 92 | 93 | bool QnitePathSelectionTool::doSelect(QniteArtist *artist) { 94 | return artist->select(m_selection); 95 | } 96 | -------------------------------------------------------------------------------- /src/tools/qnitepathselectiontool.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_SELECT_TOOL_H 2 | #define QNITE_SELECT_TOOL_H 3 | 4 | #include "qniteselectiontool.h" 5 | 6 | class QnitePen; 7 | class QniteArtist; 8 | class QnitePathSelectionTool : public QniteSelectionTool { 9 | Q_OBJECT 10 | Q_PROPERTY( 11 | QVariantList selectionPath READ selectionPath NOTIFY selectionPathChanged) 12 | Q_PROPERTY(QnitePen *pen READ pen CONSTANT) 13 | 14 | public: 15 | explicit QnitePathSelectionTool(QQuickItem *parent = 0); 16 | virtual ~QnitePathSelectionTool() {} 17 | 18 | QVariantList selectionPath() const; 19 | 20 | QnitePen *pen() const { return m_pen; } 21 | 22 | virtual void begin(const QPoint &point); 23 | virtual void append(const QPoint &point); 24 | virtual void end(); 25 | 26 | QNanoQuickItemPainter *createItemPainter() const Q_DECL_OVERRIDE; 27 | 28 | Q_SIGNALS: 29 | void selectionPathChanged(); 30 | 31 | protected: 32 | virtual void mousePressEvent(QMouseEvent *event); 33 | virtual void mouseMoveEvent(QMouseEvent *event); 34 | virtual void mouseReleaseEvent(QMouseEvent *event); 35 | virtual bool doSelect(QniteArtist *); 36 | 37 | // QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); 38 | 39 | QList m_selection; 40 | 41 | private: 42 | friend class QnitePathPainter; 43 | 44 | QnitePen *m_pen; 45 | }; 46 | 47 | #endif // QNITE_SELECT_TOOL_H 48 | -------------------------------------------------------------------------------- /src/tools/qnitepointselectiontool.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitepointselectiontool.h" 2 | #include "qniteartist.h" 3 | 4 | QnitePointSelectionTool::QnitePointSelectionTool(QQuickItem *parent) 5 | : QniteSelectionTool(parent) { 6 | setFlag(ItemHasContents, false); 7 | } 8 | 9 | void QnitePointSelectionTool::select(const QPoint &point) { 10 | clearSelection(); 11 | m_selection = point; 12 | select(); // calls doSelect on all artists of the attached axes 13 | } 14 | 15 | bool QnitePointSelectionTool::doSelect(QniteArtist *artist) { 16 | return artist->select(m_selection); 17 | } 18 | 19 | void QnitePointSelectionTool::mouseReleaseEvent(QMouseEvent *event) { 20 | select(event->pos()); 21 | } 22 | -------------------------------------------------------------------------------- /src/tools/qnitepointselectiontool.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITEPOINTSELECTIONTOOL_H 2 | #define QNITEPOINTSELECTIONTOOL_H 3 | 4 | #include "qniteselectiontool.h" 5 | 6 | class QnitePointSelectionTool : public QniteSelectionTool { 7 | Q_OBJECT 8 | 9 | public: 10 | explicit QnitePointSelectionTool(QQuickItem *parent = 0); 11 | virtual ~QnitePointSelectionTool() {} 12 | 13 | void select(const QPoint &point); 14 | 15 | protected: 16 | using QniteSelectionTool::select; 17 | 18 | virtual bool doSelect(QniteArtist *); 19 | virtual void mousePressEvent(QMouseEvent *) {} 20 | virtual void mouseReleaseEvent(QMouseEvent *event); 21 | 22 | private: 23 | QPoint m_selection; 24 | }; 25 | 26 | #endif // QNITEPOINTSELECTIONTOOL_H 27 | -------------------------------------------------------------------------------- /src/tools/qniteselectiontool.cpp: -------------------------------------------------------------------------------- 1 | #include "tools/qniteselectiontool.h" 2 | #include "qniteartist.h" 3 | #include "qniteaxes.h" 4 | 5 | QniteSelectionTool::QniteSelectionTool(QQuickItem *parent) : QniteTool(parent) { 6 | setAcceptedMouseButtons(Qt::LeftButton); 7 | } 8 | 9 | /*! 10 | Iterate through artists present in current axes, skip if not selectable, 11 | perform selection and break iteration if some artist accepts the selection 12 | event. 13 | */ 14 | void QniteSelectionTool::select() { 15 | for (auto artist : artists()) { 16 | if (!artist->selectable()) { 17 | continue; 18 | } else if (doSelect(artist)) { 19 | break; 20 | } 21 | } 22 | } 23 | 24 | void QniteSelectionTool::clearSelection() { 25 | for (auto artist : artists()) { 26 | if (artist->selectable()) { 27 | artist->clearSelection(); 28 | } 29 | } 30 | } 31 | 32 | void QniteSelectionTool::reset() { clearSelection(); } 33 | -------------------------------------------------------------------------------- /src/tools/qniteselectiontool.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITESELECTIONTOOL_H 2 | #define QNITESELECTIONTOOL_H 3 | 4 | #include "qnitetool.h" 5 | 6 | class QniteArtist; 7 | class QniteSelectionTool : public QniteTool { 8 | Q_OBJECT 9 | 10 | public: 11 | explicit QniteSelectionTool(QQuickItem *parent = 0); 12 | virtual ~QniteSelectionTool() {} 13 | Q_INVOKABLE void reset(); 14 | 15 | protected: 16 | void select(); 17 | void clearSelection(); 18 | virtual bool doSelect(QniteArtist *) = 0; 19 | }; 20 | 21 | #endif // QNITESELECTIONTOOL_H 22 | -------------------------------------------------------------------------------- /src/tools/qnitetool.cpp: -------------------------------------------------------------------------------- 1 | #include "qnitetool.h" 2 | #include "qniteaxes.h" 3 | 4 | QniteTool::QniteTool(QQuickItem *parent) 5 | : QNanoQuickItem{parent}, m_axes{nullptr} {} 6 | 7 | void QniteTool::setAxes(QniteAxes *axes) { 8 | if (m_axes != axes) { 9 | m_axes = axes; 10 | emit axesChanged(); 11 | } 12 | } 13 | 14 | /*! 15 | A protected proxy method to propagate friendship between 16 | QniteTool and QniteAxes to the children of QniteTool 17 | */ 18 | QList QniteTool::artists() { 19 | if (m_axes != nullptr) { 20 | return m_axes->m_artists; 21 | } 22 | 23 | return QList{}; 24 | } 25 | -------------------------------------------------------------------------------- /src/tools/qnitetool.h: -------------------------------------------------------------------------------- 1 | #ifndef QNITE_TOOL_H 2 | #define QNITE_TOOL_H 3 | 4 | #include "qnanoquickitem.h" 5 | 6 | class QniteAxes; 7 | class QniteArtist; 8 | class QniteTool : public QNanoQuickItem { 9 | Q_OBJECT 10 | Q_PROPERTY(QniteAxes *axes READ axes NOTIFY 11 | axesChanged) // TODO: axes needs to be a property? 12 | 13 | public: 14 | explicit QniteTool(QQuickItem *parent = 0); 15 | virtual ~QniteTool() {} 16 | 17 | QniteAxes *axes() const { return m_axes; } 18 | void setAxes(QniteAxes *axes); 19 | 20 | virtual QNanoQuickItemPainter *createItemPainter() const Q_DECL_OVERRIDE { 21 | return nullptr; 22 | } 23 | 24 | Q_SIGNALS: 25 | void axesChanged(); 26 | 27 | protected: 28 | QList artists(); 29 | 30 | private: 31 | QniteAxes *m_axes; 32 | }; 33 | 34 | #endif // QNITE_TOOL_H 35 | -------------------------------------------------------------------------------- /src/tools/qnitezoompainter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitezoompainter.h" 4 | #include "qnitezoomtool.h" 5 | 6 | Q_LOGGING_CATEGORY(qnitezoompainter, "qnite.zoom.painter") 7 | 8 | QniteZoomPainter::QniteZoomPainter() : QNanoQuickItemPainter{} {} 9 | 10 | void QniteZoomPainter::synchronize(QNanoQuickItem *item) { 11 | qCDebug(qnitezoompainter) << "synchronizing qnitezoom"; 12 | 13 | auto zoom = static_cast(item); 14 | if (zoom) { 15 | m_rect = zoom->rect(); 16 | m_pen = zoom->pen()->data(); 17 | } 18 | } 19 | 20 | void QniteZoomPainter::paint(QNanoPainter *painter) { 21 | qCDebug(qnitezoompainter) << "painting qnitezoom"; 22 | 23 | if (m_rect.isNull()) { 24 | return; 25 | } 26 | 27 | painter->setStrokeStyle(QNanoColor::fromQColor(m_pen.stroke)); 28 | if (m_pen.fill.isValid()) { 29 | painter->setFillStyle(QNanoColor::fromQColor(m_pen.fill)); 30 | } 31 | painter->setLineWidth(static_cast(m_pen.width)); 32 | painter->setLineJoin(m_pen.join); 33 | painter->setLineCap(m_pen.cap); 34 | 35 | painter->beginPath(); 36 | painter->moveTo(m_rect.topLeft()); 37 | painter->lineTo(m_rect.topRight()); 38 | painter->lineTo(m_rect.bottomRight()); 39 | painter->lineTo(m_rect.bottomLeft()); 40 | painter->closePath(); 41 | painter->fill(); 42 | painter->stroke(); 43 | } 44 | -------------------------------------------------------------------------------- /src/tools/qnitezoompainter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "qnanoquickitempainter.h" 4 | #include "qnitepen.h" 5 | 6 | class QniteZoomPainter : public QNanoQuickItemPainter { 7 | public: 8 | QniteZoomPainter(); 9 | 10 | void synchronize(QNanoQuickItem *item) Q_DECL_OVERRIDE; 11 | void paint(QNanoPainter *painter) Q_DECL_OVERRIDE; 12 | 13 | private: 14 | QRectF m_rect; 15 | QnitePen::PenData m_pen; 16 | }; 17 | -------------------------------------------------------------------------------- /src/tools/qnitezoomtool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qniteaxes.h" 4 | #include "qniteaxis.h" 5 | #include "qnitemapper.h" 6 | #include "qnitezoompainter.h" 7 | #include "qnitezoomtool.h" 8 | 9 | QniteZoomTool::QniteZoomTool(QQuickItem *parent) 10 | : QniteTool{parent}, m_minZoomFactor{4}, m_pen{new QnitePen} { 11 | setAcceptedMouseButtons(Qt::RightButton); 12 | 13 | // Default pen style 14 | m_pen->setFill("#6EDCDCDC"); 15 | m_pen->setStroke("#7E7E7E"); 16 | m_pen->setWidth(1.0); 17 | 18 | connect(this, &QniteZoomTool::axesChanged, this, 19 | &QniteZoomTool::connectAxesBoundsSignals); 20 | 21 | connect(this, &QniteZoomTool::minZoomFactorChanged, this, 22 | &QniteZoomTool::updateEnabled); 23 | } 24 | 25 | QniteZoomTool::~QniteZoomTool() {} 26 | 27 | QNanoQuickItemPainter *QniteZoomTool::createItemPainter() const { 28 | return new QniteZoomPainter; 29 | } 30 | 31 | void QniteZoomTool::reset() { 32 | auto xAxis = axes()->axisX(); 33 | auto yAxis = axes()->axisY(); 34 | auto xPadding = axes()->xPadding(); 35 | auto yPadding = axes()->yPadding(); 36 | 37 | xAxis->setLowerBound(m_baseZoomRect.x() - xPadding); 38 | xAxis->setUpperBound(m_baseZoomRect.width() + xPadding); 39 | yAxis->setLowerBound(m_baseZoomRect.y() - yPadding); 40 | yAxis->setUpperBound(m_baseZoomRect.height() + yPadding); 41 | setEnabled(true); 42 | update(); 43 | axes()->updateArtists(); 44 | } 45 | 46 | void QniteZoomTool::setMinZoomFactor(int factor) { 47 | if (m_minZoomFactor != factor) { 48 | m_minZoomFactor = factor; 49 | emit minZoomFactorChanged(); 50 | } 51 | } 52 | 53 | void QniteZoomTool::mousePressEvent(QMouseEvent *event) { 54 | if (!isEnabled()) { 55 | return; 56 | } 57 | 58 | m_zoomRect.setTopLeft(event->pos()); 59 | } 60 | 61 | void QniteZoomTool::mouseMoveEvent(QMouseEvent *event) { 62 | if (!isEnabled()) { 63 | return; 64 | } 65 | 66 | m_zoomRect.setBottomRight(event->pos()); 67 | update(); 68 | } 69 | 70 | void QniteZoomTool::mouseReleaseEvent(QMouseEvent *event) { 71 | if (m_zoomRect.isNull() || event->pos() == m_zoomRect.topLeft()) { 72 | m_zoomRect = QRectF{}; 73 | return; 74 | } 75 | 76 | // Calculates new axes bounds and updates them 77 | auto yMin = qMin(m_zoomRect.top(), m_zoomRect.bottom()); 78 | auto yMax = qMax(m_zoomRect.top(), m_zoomRect.bottom()); 79 | 80 | auto yAxis = axes()->axisY(); 81 | auto yMappedMin = 82 | yAxis->mapper()->mapTo(0, axes()->height(), yAxis->lowerBound(), 83 | yAxis->upperBound(), yMin, yAxis->flip()); 84 | auto yMappedMax = 85 | yAxis->mapper()->mapTo(0, axes()->height(), yAxis->lowerBound(), 86 | yAxis->upperBound(), yMax, yAxis->flip()); 87 | 88 | auto xMin = qMin(m_zoomRect.left(), m_zoomRect.right()); 89 | auto xMax = qMax(m_zoomRect.left(), m_zoomRect.right()); 90 | 91 | auto xAxis = axes()->axisX(); 92 | auto xMappedMin = 93 | xAxis->mapper()->mapTo(0, axes()->width(), xAxis->lowerBound(), 94 | xAxis->upperBound(), xMin, xAxis->flip()); 95 | 96 | auto xMappedMax = 97 | xAxis->mapper()->mapTo(0, axes()->width(), xAxis->lowerBound(), 98 | xAxis->upperBound(), xMax, xAxis->flip()); 99 | 100 | yAxis->setLowerBound(qMin(yMappedMin, yMappedMax)); 101 | yAxis->setUpperBound(qMax(yMappedMin, yMappedMax)); 102 | xAxis->setLowerBound(qMin(xMappedMin, xMappedMax)); 103 | xAxis->setUpperBound(qMax(xMappedMin, xMappedMax)); 104 | 105 | m_zoomRect = QRectF{}; 106 | 107 | update(); 108 | axes()->updateArtists(); 109 | 110 | // Disables tool if calculated bounds are smaller than the minimum zoom size 111 | auto minSize = minimumZoomSize(); 112 | auto axisYSize = qAbs(yMappedMin - yMappedMax); 113 | auto axisXSize = qAbs(xMappedMin - xMappedMax); 114 | 115 | if (axisXSize <= minSize.width() || axisYSize <= minSize.height()) { 116 | setEnabled(false); 117 | } 118 | } 119 | 120 | void QniteZoomTool::connectAxesBoundsSignals() const { 121 | connect(axes(), &QniteAxes::xBoundsChanged, this, 122 | &QniteZoomTool::updateBaseZoomRectXBounds); 123 | connect(axes(), &QniteAxes::yBoundsChanged, this, 124 | &QniteZoomTool::updateBaseZoomRectYBounds); 125 | disconnect(this, &QniteZoomTool::axesChanged, this, 126 | &QniteZoomTool::connectAxesBoundsSignals); 127 | } 128 | 129 | void QniteZoomTool::updateBaseZoomRectXBounds() { 130 | auto xBounds = axes()->xBounds(); 131 | m_baseZoomRect.setX(xBounds.first()); 132 | m_baseZoomRect.setWidth(xBounds.last()); 133 | disconnect(axes(), &QniteAxes::xBoundsChanged, this, 134 | &QniteZoomTool::updateBaseZoomRectXBounds); 135 | } 136 | 137 | void QniteZoomTool::updateBaseZoomRectYBounds() { 138 | auto yBounds = axes()->yBounds(); 139 | m_baseZoomRect.setY(yBounds.first()); 140 | m_baseZoomRect.setHeight(yBounds.last()); 141 | disconnect(axes(), &QniteAxes::yBoundsChanged, this, 142 | &QniteZoomTool::updateBaseZoomRectYBounds); 143 | } 144 | 145 | QSizeF QniteZoomTool::minimumZoomSize() const { 146 | qreal factor = std::pow(10, m_minZoomFactor); 147 | return {qAbs(m_baseZoomRect.width() / factor), 148 | qAbs(m_baseZoomRect.height() / factor)}; 149 | } 150 | 151 | void QniteZoomTool::updateEnabled() { 152 | // Disables tool if current bounds are smaller than the minimum zoom size, 153 | // enables it otherwise 154 | auto axisX = axes()->axisX(); 155 | auto axisY = axes()->axisY(); 156 | 157 | auto axisXSize = qAbs(axisX->lowerBound() - axisX->upperBound()); 158 | auto axisYSize = qAbs(axisY->lowerBound() - axisY->upperBound()); 159 | 160 | auto minSize = minimumZoomSize(); 161 | auto enable = axisXSize > minSize.width() || axisYSize > minSize.height(); 162 | setEnabled(enable); 163 | } 164 | -------------------------------------------------------------------------------- /src/tools/qnitezoomtool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "qnitepen.h" 4 | #include "qnitetool.h" 5 | 6 | class QniteZoomTool : public QniteTool { 7 | Q_OBJECT 8 | Q_PROPERTY(QnitePen *pen READ pen CONSTANT) 9 | Q_PROPERTY(int minZoomFactor READ minZoomFactor WRITE setMinZoomFactor NOTIFY 10 | minZoomFactorChanged) 11 | 12 | public: 13 | explicit QniteZoomTool(QQuickItem *parent = nullptr); 14 | virtual ~QniteZoomTool() Q_DECL_OVERRIDE; 15 | 16 | QNanoQuickItemPainter *createItemPainter() const Q_DECL_OVERRIDE; 17 | 18 | QRectF rect() const { return m_zoomRect; } 19 | QnitePen *pen() const { return m_pen.data(); } 20 | 21 | int minZoomFactor() const { return m_minZoomFactor; } 22 | void setMinZoomFactor(int factor); 23 | 24 | Q_INVOKABLE void reset(); 25 | 26 | signals: 27 | void minZoomFactorChanged() const; 28 | void limitZoomChanged() const; 29 | 30 | protected: 31 | virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; 32 | virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; 33 | virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; 34 | 35 | private slots: 36 | void connectAxesBoundsSignals() const; 37 | void updateBaseZoomRectXBounds(); 38 | void updateBaseZoomRectYBounds(); 39 | void updateEnabled(); 40 | 41 | private: 42 | QRectF m_zoomRect; 43 | QRectF m_baseZoomRect; 44 | int m_minZoomFactor; 45 | QScopedPointer m_pen; 46 | 47 | QSizeF minimumZoomSize() const; 48 | }; 49 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(tst_qniteartist) 2 | add_subdirectory(tst_qniteaxes) 3 | add_subdirectory(tst_qnitecircle) 4 | add_subdirectory(tst_qniteclipper) 5 | add_subdirectory(tst_qnitelinearmapper) 6 | add_subdirectory(tst_qnitelinearticker) 7 | add_subdirectory(tst_qnitemapper) 8 | add_subdirectory(tst_qnitepathselectiontool) 9 | add_subdirectory(tst_qniteselectiontool) 10 | add_subdirectory(tst_qniteticker) 11 | add_subdirectory(tst_qnitetool) 12 | add_subdirectory(tst_qnitezoomtool) 13 | -------------------------------------------------------------------------------- /tests/tests.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | Project { 5 | references: [ 6 | "tst_qniteartist/tst_qniteartist.qbs", 7 | "tst_qniteaxes/tst_qniteaxes.qbs", 8 | "tst_qnitecircle/tst_qnitecircle.qbs", 9 | "tst_qniteclipper/tst_qniteclipper.qbs", 10 | "tst_qnitelinearmapper/tst_qnitelinearmapper.qbs", 11 | "tst_qnitelinearticker/tst_qnitelinearticker.qbs", 12 | "tst_qnitemapper/tst_qnitemapper.qbs", 13 | "tst_qnitepathselectiontool/tst_qnitepathselectiontool.qbs", 14 | "tst_qniteselectiontool/tst_qniteselectiontool.qbs", 15 | "tst_qniteticker/tst_qniteticker.qbs", 16 | "tst_qnitetool/tst_qnitetool.qbs", 17 | "tst_qnitezoomtool/tst_qnitezoomtool.qbs", 18 | ] 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tests/tst_qniteartist/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qniteartist 14 | tst_qniteartist.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qniteartist 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qniteartist 26 | tst_qniteartist 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qniteartist/tst_qniteartist.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qniteartist.h" 4 | 5 | class FooArtist : public QniteArtist { 6 | Q_OBJECT 7 | 8 | public Q_SLOTS: 9 | void processData() {} 10 | 11 | public: 12 | bool sel; 13 | 14 | FooArtist(QQuickItem *p = 0) : QniteArtist(p), sel(false) {} 15 | void clearSelection() { sel = false; } 16 | bool isSelected() const { return sel; } 17 | }; 18 | 19 | class TestQniteArtist : public QObject { 20 | Q_OBJECT 21 | 22 | FooArtist artist; 23 | 24 | private slots: 25 | void initTestCase() {} 26 | 27 | void cleanupTestCase() {} 28 | 29 | void testSelectableFlag() { 30 | QCOMPARE(artist.selectable(), false); 31 | 32 | artist.setSelectable(true); 33 | QCOMPARE(artist.selectable(), true); 34 | 35 | artist.sel = true; 36 | artist.setSelectable(false); 37 | QCOMPARE(artist.selectable(), false); 38 | QCOMPARE(artist.sel, false); 39 | } 40 | 41 | void testSelected() { 42 | artist.sel = true; 43 | 44 | artist.setSelectable(true); 45 | QCOMPARE(artist.selected(), true); 46 | 47 | artist.setSelectable(false); 48 | QCOMPARE(artist.selected(), false); 49 | } 50 | 51 | void testPropagateSelection() { 52 | QCOMPARE(artist.propagateSelection(), false); 53 | QCOMPARE(artist.select(QPoint()), true); // accepted 54 | 55 | artist.setPropagateSelection(true); 56 | 57 | QCOMPARE(artist.propagateSelection(), true); 58 | QCOMPARE(artist.select(QPoint()), false); // rejected 59 | } 60 | }; 61 | 62 | QTEST_MAIN(TestQniteArtist) 63 | #include "tst_qniteartist.moc" 64 | -------------------------------------------------------------------------------- /tests/tst_qniteartist/tst_qniteartist.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qniteartist" 5 | type: "application" 6 | 7 | Depends { name: "Qt.testlib" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | files: [ 17 | "tst_qniteartist.cpp", 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/tst_qniteaxes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qniteaxes 14 | tst_qniteaxes.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qniteaxes 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qniteaxes 26 | tst_qniteaxes 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qniteaxes/tst_qniteaxes.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qniteaxes.h" 4 | #include "qniteaxis.h" 5 | #include "tools/qnitetool.h" 6 | 7 | #include "qniteartist.h" 8 | 9 | class FooArtist : public QniteArtist { 10 | Q_OBJECT 11 | 12 | public Q_SLOTS: 13 | void processData() {} 14 | 15 | public: 16 | bool sel; 17 | 18 | FooArtist(QQuickItem *p = 0) : QniteArtist(p), sel(false) {} 19 | void clearSelection() { sel = false; } 20 | bool isSelected() const { return sel; } 21 | }; 22 | 23 | class FooAxis : public QniteAxis { 24 | Q_OBJECT 25 | 26 | public Q_SLOTS: 27 | void processData() {} 28 | 29 | public: 30 | FooAxis(QQuickItem *p = 0) : QniteAxis(p) {} 31 | }; 32 | 33 | class TestQniteAxes : public QObject { 34 | Q_OBJECT 35 | 36 | private slots: 37 | void testDefaults(); 38 | 39 | void testAppendTool(); 40 | void testSetOnTop(); 41 | void testPadding(); 42 | }; 43 | 44 | void TestQniteAxes::testDefaults() { 45 | QniteAxes axes; 46 | QCOMPARE(axes.canvas()->clip(), true); 47 | } 48 | 49 | void TestQniteAxes::testAppendTool() { 50 | QniteAxes axes; 51 | QniteTool tool; 52 | 53 | auto property = axes.tools(); 54 | property.append(&property, &tool); 55 | 56 | // axes has been set 57 | QCOMPARE(tool.axes(), &axes); 58 | // tool has been visually parented to axes canvas 59 | QCOMPARE(axes.canvas(), tool.parentItem()); 60 | } 61 | 62 | void TestQniteAxes::testSetOnTop() { 63 | QniteAxes axes; 64 | FooArtist a1, a2; 65 | 66 | auto prop = axes.artists(); 67 | prop.append(&prop, &a1); 68 | prop.append(&prop, &a2); 69 | 70 | QList l{&a1, &a2}; 71 | QCOMPARE(l, axes.canvas()->childItems()); 72 | 73 | axes.setOnTop(&a1); 74 | 75 | QList l2{&a2, &a1}; 76 | QCOMPARE(l2, axes.canvas()->childItems()); 77 | } 78 | 79 | void TestQniteAxes::testPadding() { 80 | QniteAxes axes; 81 | 82 | // Axes bounds 83 | QList xBounds{-10.0, 100.0}; 84 | QList yBounds{-20.0, 200.0}; 85 | 86 | // Axes padding 87 | qreal xPadding{20.0}; 88 | qreal yPadding{10.0}; 89 | 90 | // Axis necessary to correctly 91 | // set bounds and padding 92 | auto xAxis = new FooAxis(); 93 | auto yAxis = new FooAxis(); 94 | 95 | axes.setAxisX(xAxis); 96 | axes.setAxisY(yAxis); 97 | 98 | axes.setXBounds(xBounds); 99 | axes.setYBounds(yBounds); 100 | 101 | axes.setXPadding(xPadding); 102 | axes.setYPadding(yPadding); 103 | 104 | QCOMPARE(axes.xPadding(), 20.0); 105 | QCOMPARE(axes.yPadding(), 10.0); 106 | 107 | // Check that bounds are correctly calculated 108 | // depending on the padding set 109 | auto expectedXLowerBound = xBounds.at(0) - xPadding; 110 | auto expectedXUpperBound = xBounds.at(1) + xPadding; 111 | auto expectedYLowerBound = yBounds.at(0) - yPadding; 112 | auto expectedYUpperBound = yBounds.at(1) + yPadding; 113 | 114 | QCOMPARE(axes.axisX()->lowerBound(), expectedXLowerBound); 115 | QCOMPARE(axes.axisX()->upperBound(), expectedXUpperBound); 116 | QCOMPARE(axes.axisY()->lowerBound(), expectedYLowerBound); 117 | QCOMPARE(axes.axisY()->upperBound(), expectedYUpperBound); 118 | } 119 | 120 | QTEST_MAIN(TestQniteAxes) 121 | #include "tst_qniteaxes.moc" 122 | -------------------------------------------------------------------------------- /tests/tst_qniteaxes/tst_qniteaxes.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qniteaxes" 5 | type: "application" 6 | 7 | Depends { name: "Qt.testlib" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | files: [ 17 | "tst_qniteaxes.cpp", 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/tst_qnitecircle/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | QuickTest 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qnitecircle 14 | tst_qnitecircle.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qnitecircle 19 | PRIVATE 20 | qnite 21 | Qt5::QuickTest 22 | ) 23 | 24 | target_compile_definitions( 25 | tst_qnitecircle 26 | PRIVATE 27 | QUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" 28 | # This is kept for compatibility with qbs, when it's 29 | # removed from the test main it can be removed here 30 | INSTALL_ROOT="" 31 | ) 32 | 33 | add_test( 34 | tst_qnitecircle 35 | tst_qnitecircle 36 | ) 37 | -------------------------------------------------------------------------------- /tests/tst_qnitecircle/data/tst_qnitecircle.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.6 2 | import QtTest 1.1 3 | 4 | import Qnite 1.0 5 | 6 | TestCase { 7 | id: testCase 8 | width: 300; height: 300 9 | visible: true 10 | when: windowShown 11 | name: "QniteCircle" 12 | 13 | Component { 14 | id: figure 15 | 16 | Figure { 17 | axes.yBounds: [0, 10] 18 | axes.xBounds: [0, 10] 19 | 20 | tools: [ 21 | PointSelectionTool { 22 | anchors.fill: parent 23 | } 24 | ] 25 | 26 | Circle { 27 | id: __circle 28 | objectName: "__circle" 29 | 30 | selectable: true 31 | pen.fill: "#aa90caf9" 32 | selectedPen.fill: "#ef5350" 33 | highlightedPen.fill: "#53ef50" 34 | 35 | // Displayed values 36 | xValues: [1, 2, 3, 4, 5, 6, 7, 8] 37 | yValues: [1, 2, 3, 4, 5, 6, 7, 8] 38 | } 39 | } 40 | } 41 | 42 | // Tests selection of multiple plotted points 43 | function test_select_circle() { 44 | var fig = figure.createObject(testCase); 45 | var circle = findChild(fig, '__circle'); 46 | 47 | // Does some operations on the circle dataset 48 | circle.processData(); 49 | 50 | // Check that nothing is selected 51 | compare(circle.selectedIndexes, []); 52 | 53 | // Selects points at indexes 1, 2 and 3 54 | circle.select([1, 2, 3]); 55 | 56 | // Check selectedIndexes length and if it contains 57 | // the right indexes 58 | compare(circle.selectedIndexes.length, 3); 59 | verify(circle.selectedIndexes.indexOf(1) !== -1); 60 | verify(circle.selectedIndexes.indexOf(2) !== -1); 61 | verify(circle.selectedIndexes.indexOf(3) !== -1); 62 | 63 | 64 | } 65 | 66 | // Tests highlighting of a single plotted point 67 | function test_highlight_circle() { 68 | var fig = figure.createObject(testCase); 69 | 70 | var circle = findChild(fig, '__circle'); 71 | 72 | // Does some operations on the circle dataset 73 | circle.processData(); 74 | 75 | // Check that nothing is highlighted 76 | compare(circle.highlightedIndex, -1); 77 | 78 | // Highlights point at index 5 79 | circle.highlight(5); 80 | 81 | // Check if points has been highlighted 82 | compare(circle.highlightedIndex, 5); 83 | } 84 | 85 | // Test the deselection of selected and highlighted points 86 | function test_clear_selection() { 87 | var fig = figure.createObject(testCase); 88 | var circle = findChild(fig, '__circle'); 89 | 90 | // Does some operations on the circle dataset 91 | circle.processData(); 92 | 93 | // Check that no point is selected or highlighted 94 | compare(circle.selectedIndexes, []); 95 | compare(circle.highlightedIndex, -1); 96 | 97 | // Selects points at indexes 1, 2 and 3 98 | // and highlights point at index 5 99 | circle.select([1, 2, 3]); 100 | circle.highlight(5); 101 | 102 | // Check if points have been selected and highlighted 103 | compare(circle.selectedIndexes.length, 3); 104 | verify(circle.selectedIndexes.indexOf(1) !== -1); 105 | verify(circle.selectedIndexes.indexOf(2) !== -1); 106 | verify(circle.selectedIndexes.indexOf(3) !== -1); 107 | compare(circle.highlightedIndex, 5); 108 | 109 | // Clears selection and highlighting 110 | circle.clearSelection(); 111 | 112 | // Check that no point is selected or highlighted 113 | compare(circle.selectedIndexes, []); 114 | compare(circle.highlightedIndex, -1); 115 | } 116 | 117 | // Tests that various radius are set correctly 118 | function test_circle_radius() { 119 | var fig = figure.createObject(testCase); 120 | var circle = findChild(fig, '__circle'); 121 | 122 | // Does some operations on the circle dataset 123 | circle.processData(); 124 | 125 | // Tests default radii 126 | compare(circle.pen.radius, 5); 127 | compare(circle.selectedPen.radius, 5); 128 | compare(circle.highlightedPen.radius, 5); 129 | 130 | // Sets the various circle radii 131 | circle.pen.radius = 6; 132 | circle.selectedPen.radius = 8; 133 | circle.highlightedPen.radius = 9; 134 | 135 | // Tests radii are set correctly 136 | compare(circle.pen.radius, 6); 137 | compare(circle.selectedPen.radius, 8); 138 | compare(circle.highlightedPen.radius, 9); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /tests/tst_qnitecircle/tst_qnitecircle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "qnite.h" 5 | 6 | // We need to set the QML import paths before the test file is read and this is 7 | // the only working way 8 | int main(int argc, char **argv) { 9 | QTEST_ADD_GPU_BLACKLIST_SUPPORT 10 | QTEST_SET_MAIN_SOURCE_PATH 11 | QVector newArgv; 12 | int i = 0; 13 | for (; i < argc; i++) { 14 | newArgv << argv[i]; 15 | } 16 | newArgv << strdup("-import"); 17 | newArgv << strdup("qrc:/"); 18 | newArgv << strdup("-import"); 19 | newArgv << strdup("qrc:/qml"); 20 | newArgv << strdup("-import"); 21 | newArgv << strdup(INSTALL_ROOT); 22 | return quick_test_main(newArgv.size(), newArgv.data(), "tst_qnitecircle", 23 | QUICK_TEST_SOURCE_DIR); 24 | } 25 | -------------------------------------------------------------------------------- /tests/tst_qnitecircle/tst_qnitecircle.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | CppApplication { 5 | name: "tst_qnitecircle" 6 | type: ["application"] 7 | 8 | Depends { name: "cpp" } 9 | cpp.defines: [ 10 | "QUICK_TEST_SOURCE_DIR=\"" + path + "\"", 11 | "INSTALL_ROOT=\"" + qbs.installRoot + "\"" 12 | ] 13 | 14 | Depends { name: "Qt.qmltest" } 15 | Depends { 16 | name: "qnite" 17 | Properties { 18 | withTests: false 19 | withExamples: false 20 | } 21 | } 22 | 23 | files: [ 24 | "tst_qnitecircle.cpp", 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /tests/tst_qniteclipper/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qniteclipper 14 | tst_qniteclipper.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qniteclipper 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qniteclipper 26 | tst_qniteclipper 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qniteclipper/tst_qniteclipper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "qniteclipper.h" 7 | 8 | class TestQniteClipper : public QObject { 9 | Q_OBJECT 10 | 11 | QniteClipper clipper; 12 | QList m_xValues; 13 | QList m_yValues; 14 | 15 | private slots: 16 | void initTestCase(); 17 | void testClip(); 18 | }; 19 | 20 | void TestQniteClipper::initTestCase() { 21 | int n = 10000; 22 | std::random_device rd; 23 | std::mt19937 gen(rd()); 24 | std::uniform_real_distribution d(0, 10); 25 | std::uniform_real_distribution o(-10, 0); 26 | 27 | for (int i = 0; i < n; ++i) { 28 | m_xValues.append(d(gen)); 29 | m_xValues.append(o(gen)); 30 | m_yValues.append(d(gen)); 31 | m_yValues.append(o(gen)); 32 | } 33 | 34 | std::sort(m_xValues.begin(), m_xValues.end()); 35 | } 36 | 37 | void TestQniteClipper::testClip() { 38 | QList outX; 39 | QList outY; 40 | 41 | QBENCHMARK { 42 | outX.clear(); 43 | outY.clear(); 44 | clipper.clip(m_xValues, m_yValues, 0., 10., 0., 10., outX, outY); 45 | } 46 | 47 | QCOMPARE(outX.size(), 5000); 48 | QCOMPARE(outY.size(), 5000); 49 | } 50 | 51 | QTEST_MAIN(TestQniteClipper) 52 | #include "tst_qniteclipper.moc" 53 | -------------------------------------------------------------------------------- /tests/tst_qniteclipper/tst_qniteclipper.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qniteclipper" 5 | type: "application" 6 | 7 | Depends { name: "Qt.testlib" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | files: [ 17 | "tst_qniteclipper.cpp", 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/tst_qnitelinearmapper/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qnitelinearmapper 14 | tst_qnitelinearmapper.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qnitelinearmapper 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qnitelinearmapper 26 | tst_qnitelinearmapper 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qnitelinearmapper/tst_qnitelinearmapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitelinearmapper.h" 4 | 5 | class TestQniteLinearMapper : public QObject { 6 | Q_OBJECT 7 | 8 | QniteLinearMapper mapper; 9 | 10 | private slots: 11 | void testMapTo_data(); 12 | void testMapTo(); 13 | void testMapToMulti(); 14 | }; 15 | 16 | void TestQniteLinearMapper::testMapTo_data() { 17 | QTest::addColumn("sourceLower"); 18 | QTest::addColumn("sourceUpper"); 19 | QTest::addColumn("destLower"); 20 | QTest::addColumn("destUpper"); 21 | QTest::addColumn("flip"); 22 | QTest::addColumn("value"); 23 | QTest::addColumn("result"); 24 | 25 | QTest::newRow("invalid source") 26 | << 0.0 << 0.0 << 1.0 << 5.0 << false << 1.0 << 0.0; 27 | QTest::newRow("invalid dest") 28 | << 1.0 << 5.0 << 0.0 << 0.0 << false << 1.0 << 0.; 29 | QTest::newRow("positive") 30 | << 0.0 << 10.0 << 0.0 << 100.0 << false << 1.5 << 15.0; 31 | QTest::newRow("negative") 32 | << -10.0 << -5.0 << 0.0 << 100.0 << false << -9.0 << 20.0; 33 | QTest::newRow("flipped positive") 34 | << 0.0 << 10.0 << 0.0 << 100.0 << true << 1.5 << 85.0; 35 | QTest::newRow("flipped negative") 36 | << -10.0 << -5.0 << 0.0 << 100.0 << true << -9.0 << 80.0; 37 | QTest::newRow("positive dest not 0") 38 | << 0.0 << 200.0 << 10.0 << 20.0 << false << 150.0 << 17.5; 39 | QTest::newRow("positive dest not 0 flipped") 40 | << 0.0 << 200.0 << 10.0 << 20.0 << true << 150.0 << 12.5; 41 | QTest::newRow("negative dest not 0") 42 | << 0.0 << 200.0 << -20.0 << -10.0 << false << 150.0 << -12.5; 43 | QTest::newRow("negative dest not 0 flipped") 44 | << 0.0 << 200.0 << -20.0 << -10.0 << true << 150.0 << -17.5; 45 | } 46 | 47 | void TestQniteLinearMapper::testMapTo() { 48 | QFETCH(qreal, sourceLower); 49 | QFETCH(qreal, sourceUpper); 50 | QFETCH(qreal, destLower); 51 | QFETCH(qreal, destUpper); 52 | QFETCH(bool, flip); 53 | QFETCH(qreal, value); 54 | QFETCH(qreal, result); 55 | 56 | auto r = 57 | mapper.mapTo(sourceLower, sourceUpper, destLower, destUpper, value, flip); 58 | QCOMPARE(r, result); 59 | } 60 | 61 | void TestQniteLinearMapper::testMapToMulti() { 62 | auto r = mapper.mapTo(0.0, 10.0, 0.0, 100.0, {{0, 1.5}, {3, 3.5}}); 63 | QCOMPARE(r.value(0), 15.0); 64 | QCOMPARE(r.value(3), 35.0); 65 | } 66 | 67 | QTEST_MAIN(TestQniteLinearMapper) 68 | #include "tst_qnitelinearmapper.moc" 69 | -------------------------------------------------------------------------------- /tests/tst_qnitelinearmapper/tst_qnitelinearmapper.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qnitelinearmapper" 5 | type: "application" 6 | 7 | Depends { name: "Qt.testlib" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | files: [ 17 | "tst_qnitelinearmapper.cpp", 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/tst_qnitelinearticker/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qnitelinearticker 14 | tst_qnitelinearticker.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qnitelinearticker 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qnitelinearticker 26 | tst_qnitelinearticker 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qnitelinearticker/tst_qnitelinearticker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitelinearticker.h" 4 | 5 | class TestQniteLinearTicker : public QObject { 6 | Q_OBJECT 7 | 8 | QniteLinearTicker ticker; 9 | QList alist; 10 | 11 | private slots: 12 | void initTestCase() { alist << 105. << 343. << 443. << 543.; } 13 | 14 | void testReset() { 15 | ticker.setLooseNiceness(false); 16 | ticker.reset(); 17 | QCOMPARE(ticker.looseNiceness(), true); 18 | QCOMPARE(ticker.numSteps(), 10); 19 | } 20 | 21 | void testDefaults() { 22 | QniteLinearTicker t; 23 | QCOMPARE(t.looseNiceness(), true); 24 | QCOMPARE(ticker.numSteps(), 10); 25 | } 26 | 27 | void testLooseProperty() { 28 | ticker.reset(); 29 | ticker.setLooseNiceness(false); 30 | QCOMPARE(ticker.looseNiceness(), false); 31 | } 32 | 33 | void testBuildTicks() { 34 | ticker.reset(); 35 | ticker.setNumSteps(5); 36 | ticker.setValues(alist); 37 | 38 | QList l{100, 200, 300, 400, 500, 600}; 39 | QCOMPARE(ticker.majorTicks(), l); 40 | 41 | QList l2{150, 250, 350, 450, 550}; 42 | QCOMPARE(ticker.minorTicks(), l2); 43 | } 44 | 45 | void testBuildTicksDefault() { 46 | ticker.reset(); 47 | ticker.setBoundaries(0, 10); 48 | 49 | QList l{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 50 | QCOMPARE(ticker.majorTicks(), l); 51 | 52 | QList l2{0.2, 0.4, 0.6, 0.8, 1.2, 1.4, 1.6, 1.8, 2.2, 2.4, 53 | 2.6, 2.8, 3.2, 3.4, 3.6, 3.8, 4.2, 4.4, 4.6, 4.8, 54 | 5.2, 5.4, 5.6, 5.8, 6.2, 6.4, 6.6, 6.8, 7.2, 7.4, 55 | 7.6, 7.8, 8.2, 8.4, 8.6, 8.8, 9.2, 9.4, 9.6, 9.8}; 56 | QCOMPARE(ticker.minorTicks(), l2); 57 | } 58 | }; 59 | QTEST_MAIN(TestQniteLinearTicker) 60 | #include "tst_qnitelinearticker.moc" 61 | -------------------------------------------------------------------------------- /tests/tst_qnitelinearticker/tst_qnitelinearticker.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qnitelinearticker" 5 | type: "application" 6 | 7 | Depends { name: "Qt.testlib" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | files: [ 17 | "tst_qnitelinearticker.cpp", 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/tst_qnitemapper/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qnitemapper 14 | tst_qnitemapper.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qnitemapper 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qnitemapper 26 | tst_qnitemapper 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qnitemapper/tst_qnitemapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qnitemapper.h" 4 | 5 | class FooMapper : public QniteMapper { 6 | Q_OBJECT 7 | 8 | public: 9 | FooMapper(QObject *parent = 0) : QniteMapper(parent) {} 10 | 11 | qreal mapTo(qreal, qreal, qreal, qreal, qreal, bool) { return 1; } 12 | }; 13 | 14 | class TestQniteMapper : public QObject { 15 | Q_OBJECT 16 | 17 | QniteMapper *mapper; 18 | 19 | private slots: 20 | void testMapTo(); 21 | }; 22 | 23 | void TestQniteMapper::testMapTo() { 24 | mapper = new FooMapper(); 25 | 26 | auto result = mapper->mapTo(0, 0, 0, 0, 0); 27 | QCOMPARE(result, 1.); 28 | 29 | auto results = mapper->mapTo(0, 0, 0, 0, {{0, 0}, {0, 0}}); 30 | for (auto v : results) { 31 | QCOMPARE(v, 1.); 32 | } 33 | 34 | delete mapper; 35 | } 36 | 37 | QTEST_MAIN(TestQniteMapper) 38 | #include "tst_qnitemapper.moc" 39 | -------------------------------------------------------------------------------- /tests/tst_qnitemapper/tst_qnitemapper.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qnitemapper" 5 | type: "application" 6 | 7 | Depends { name: "Qt.testlib" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | files: [ 17 | "tst_qnitemapper.cpp", 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/tst_qnitepathselectiontool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | QuickTest 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qnitepathselectiontool 14 | tst_qnitepathselectiontool.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qnitepathselectiontool 19 | PRIVATE 20 | qnite 21 | Qt5::QuickTest 22 | ) 23 | 24 | target_compile_definitions( 25 | tst_qnitepathselectiontool 26 | PRIVATE 27 | QUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" 28 | # This is kept for compatibility with qbs, when it's 29 | # removed from the test main it can be removed here 30 | INSTALL_ROOT="" 31 | ) 32 | 33 | add_test( 34 | tst_qnitepathselectiontool 35 | tst_qnitepathselectiontool 36 | ) 37 | -------------------------------------------------------------------------------- /tests/tst_qnitepathselectiontool/data/tst_pathselectiontool.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.4 2 | import QtTest 1.0 3 | 4 | import Qnite 1.0 5 | 6 | TestCase { 7 | id: testCase 8 | width: 300; height: 300 9 | visible: true 10 | when: windowShown 11 | name: "QnitePathSelectionTool" 12 | 13 | Component { 14 | id: pathSelectionTool 15 | PathSelectionTool { 16 | anchors.fill: parent 17 | } 18 | } 19 | 20 | function test_selection() { 21 | var selectionTool = pathSelectionTool.createObject(testCase); 22 | 23 | mousePress(selectionTool, 0, 0); 24 | mouseMove(selectionTool, 0, 1); 25 | 26 | compare(selectionTool.selectionPath.length, 2); 27 | compare(selectionTool.selectionPath, [Qt.point(0,0), Qt.point(0,1)]); 28 | 29 | mouseRelease(selectionTool); 30 | compare(selectionTool.selectionPath.length, 0); 31 | 32 | selectionTool.destroy(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/tst_qnitepathselectiontool/tst_qnitepathselectiontool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "qnite.h" 5 | 6 | // We need to set the QML import paths before the test file is read and this is 7 | // the only working way 8 | int main(int argc, char **argv) { 9 | QTEST_ADD_GPU_BLACKLIST_SUPPORT 10 | QTEST_SET_MAIN_SOURCE_PATH 11 | QVector newArgv; 12 | int i = 0; 13 | for (; i < argc; i++) { 14 | newArgv << argv[i]; 15 | } 16 | newArgv << strdup("-import"); 17 | newArgv << strdup("qrc:/"); 18 | newArgv << strdup("-import"); 19 | newArgv << strdup("qrc:/qml"); 20 | newArgv << strdup("-import"); 21 | newArgv << strdup(INSTALL_ROOT); 22 | return quick_test_main(newArgv.size(), newArgv.data(), "tst_qnitepathselectiontool", 23 | QUICK_TEST_SOURCE_DIR); 24 | } 25 | -------------------------------------------------------------------------------- /tests/tst_qnitepathselectiontool/tst_qnitepathselectiontool.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | 4 | CppApplication { 5 | name: "tst_qnitepathselectiontool" 6 | type: ["application"] 7 | 8 | Depends { name: "cpp" } 9 | cpp.defines: [ 10 | "QUICK_TEST_SOURCE_DIR=\"" + path + "\"", 11 | "INSTALL_ROOT=\"" + qbs.installRoot + "\"" 12 | ] 13 | 14 | Depends { name: "Qt.qmltest" } 15 | Depends { 16 | name: "qnite" 17 | Properties { 18 | withTests: false 19 | withExamples: false 20 | } 21 | } 22 | 23 | files: [ 24 | "tst_qnitepathselectiontool.cpp", 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /tests/tst_qniteselectiontool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qniteselectiontool 14 | tst_qniteselectiontool.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qniteselectiontool 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qniteselectiontool 26 | tst_qniteselectiontool 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qniteselectiontool/tst_qniteselectiontool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qniteartist.h" 4 | #include "qniteaxes.h" 5 | #include "tools/qniteselectiontool.h" 6 | 7 | class FooArtist : public QniteArtist { 8 | Q_OBJECT 9 | 10 | public Q_SLOTS: 11 | void processData() {} 12 | 13 | public: 14 | bool sel; 15 | 16 | FooArtist(QQuickItem *p = 0) : QniteArtist(p), sel(false) {} 17 | void clearSelection() { sel = false; } 18 | bool isSelected() const { return sel; } 19 | bool select(QPoint) { 20 | sel = true; 21 | return !propagateSelection(); 22 | } 23 | }; 24 | 25 | class FooSelector : public QniteSelectionTool { 26 | Q_OBJECT 27 | 28 | public: 29 | FooSelector(QQuickItem *parent = 0) : QniteSelectionTool(parent) {} 30 | QList getFooArtists() { return artists(); } 31 | void fooSelect() { select(); } 32 | void fooClearSelection() { clearSelection(); } 33 | 34 | protected: 35 | bool doSelect(QniteArtist *a) { return a->select(QPoint()); } 36 | }; 37 | 38 | class TestQniteSelectionTool : public QObject { 39 | Q_OBJECT 40 | 41 | FooArtist a1, a2, a3, a4; 42 | FooSelector sel; 43 | QniteAxes axes; 44 | 45 | private slots: 46 | 47 | void initTestCase() { 48 | auto p1 = axes.artists(); 49 | p1.append(&p1, &a1); 50 | p1.append(&p1, &a2); 51 | p1.append(&p1, &a3); 52 | p1.append(&p1, &a4); 53 | 54 | auto p2 = axes.tools(); 55 | p2.append(&p2, &sel); 56 | } 57 | 58 | void init() { 59 | a1.setSelectable(true); 60 | a2.setSelectable(true); 61 | a3.setSelectable(true); 62 | a4.setSelectable(true); 63 | 64 | a1.setPropagateSelection(true); 65 | a2.setPropagateSelection(true); 66 | a3.setPropagateSelection(true); 67 | a4.setPropagateSelection(true); 68 | } 69 | 70 | void testSelection() { 71 | a2.setSelectable(false); 72 | a3.setPropagateSelection(false); 73 | 74 | sel.fooSelect(); 75 | 76 | QCOMPARE(a1.selected(), true); 77 | QCOMPARE(a2.selected(), false); // not selectable 78 | QCOMPARE(a3.selected(), true); 79 | QCOMPARE(a4.selected(), false); // a3 didn't propagate selection 80 | } 81 | 82 | void testClearSelection() { 83 | sel.fooSelect(); 84 | QCOMPARE(a1.selected(), true); 85 | QCOMPARE(a2.selected(), true); 86 | QCOMPARE(a3.selected(), true); 87 | QCOMPARE(a4.selected(), true); 88 | 89 | sel.fooClearSelection(); 90 | QCOMPARE(a1.selected(), false); 91 | QCOMPARE(a2.selected(), false); 92 | QCOMPARE(a3.selected(), false); 93 | QCOMPARE(a4.selected(), false); 94 | } 95 | 96 | void testReset() { 97 | sel.fooSelect(); 98 | QCOMPARE(a1.selected(), true); 99 | QCOMPARE(a2.selected(), true); 100 | QCOMPARE(a3.selected(), true); 101 | QCOMPARE(a4.selected(), true); 102 | 103 | sel.reset(); 104 | QCOMPARE(a1.selected(), false); 105 | QCOMPARE(a2.selected(), false); 106 | QCOMPARE(a3.selected(), false); 107 | QCOMPARE(a4.selected(), false); 108 | } 109 | }; 110 | 111 | QTEST_MAIN(TestQniteSelectionTool) 112 | #include "tst_qniteselectiontool.moc" 113 | -------------------------------------------------------------------------------- /tests/tst_qniteselectiontool/tst_qniteselectiontool.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qniteselectiontool" 5 | type: "application" 6 | 7 | Depends { name: "Qt.testlib" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | files: [ 17 | "tst_qniteselectiontool.cpp", 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/tst_qniteticker/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qniteticker 14 | tst_qniteticker.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qniteticker 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qniteticker 26 | tst_qniteticker 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qniteticker/tst_qniteticker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "qniteticker.h" 5 | 6 | class FooTicker : public QniteTicker { 7 | Q_OBJECT 8 | 9 | public: 10 | FooTicker(QObject *p = 0) : QniteTicker(p) {} 11 | void buildTicks() { 12 | QList t; 13 | for (int i = 0; i < numSteps(); ++i) { 14 | t << i * 1.5; 15 | } 16 | 17 | setMajorTicks(t); 18 | } 19 | }; 20 | 21 | class TestQniteTicker : public QObject { 22 | Q_OBJECT 23 | 24 | FooTicker ticker; 25 | QList alist; 26 | QList anotherlist; 27 | QList anotherlistagain; 28 | 29 | private slots: 30 | void initTestCase() { 31 | alist << 1. << 2. << 3. << 4.; 32 | anotherlist << -1. << 1.1 << 1.2; 33 | anotherlistagain << -1. << 1.1 << 1.2 << 2. << 2.2; 34 | } 35 | 36 | void testBoundaries() { 37 | ticker.reset(); 38 | 39 | QSignalSpy spy(&ticker, SIGNAL(boundariesChanged())); 40 | QSignalSpy buildSpy(&ticker, SIGNAL(tickersBuilt())); 41 | 42 | ticker.setBoundaries(-1., 1.); 43 | ticker.setBoundaries(-1., 1.); // ensure the signal isn't emitted twice 44 | 45 | QCOMPARE(ticker.lower(), -1.); 46 | QCOMPARE(ticker.upper(), 1.); 47 | QCOMPARE(spy.count(), 1); 48 | QCOMPARE(buildSpy.count(), 1); 49 | 50 | // check the signal is emitted also if only one value changes 51 | ticker.setBoundaries(-1., 2.); 52 | QCOMPARE(buildSpy.count(), 2); 53 | } 54 | 55 | void testSetValues() { 56 | ticker.reset(); 57 | 58 | QSignalSpy spy(&ticker, SIGNAL(valuesChanged())); 59 | QSignalSpy buildSpy(&ticker, SIGNAL(tickersBuilt())); 60 | 61 | ticker.setValues(alist); 62 | ticker.setValues(alist); // ensure the signal isn't emitted twice 63 | 64 | QList l; 65 | 66 | QCOMPARE(ticker.values(), alist); 67 | QCOMPARE(ticker.lower(), 1.); 68 | QCOMPARE(ticker.upper(), 4.); 69 | QCOMPARE(ticker.majorTicks(), l); 70 | QCOMPARE(spy.count(), 1); 71 | QCOMPARE(buildSpy.count(), 1); 72 | } 73 | 74 | void testSetTicks() { 75 | ticker.reset(); 76 | 77 | QSignalSpy spy1(&ticker, SIGNAL(minorTicksChanged())); 78 | 79 | ticker.setMinorTicks(alist); 80 | ticker.setMinorTicks(alist); // ensure the signal isn't emitted twice 81 | ticker.setMajorTicks(anotherlistagain); 82 | ticker.setMajorTicks(anotherlistagain); // idem 83 | 84 | QCOMPARE(ticker.minorTicks(), alist); 85 | QCOMPARE(ticker.majorTicks(), anotherlistagain); 86 | QCOMPARE(spy1.count(), 1); 87 | } 88 | 89 | void testReset() { 90 | ticker.setValues(alist); 91 | ticker.setNumSteps(5); 92 | ticker.buildTicks(); 93 | 94 | ticker.reset(); 95 | 96 | QCOMPARE(ticker.numSteps(), 0); 97 | QCOMPARE(ticker.lower(), 0.); 98 | QCOMPARE(ticker.upper(), 0.); 99 | QCOMPARE(ticker.values(), QList()); 100 | QCOMPARE(ticker.minorTicks(), QList()); 101 | QCOMPARE(ticker.majorTicks(), QList()); 102 | } 103 | 104 | void testDefaults() { 105 | FooTicker foo; 106 | QCOMPARE(foo.numSteps(), 0); 107 | QCOMPARE(foo.lower(), 0.); 108 | QCOMPARE(foo.upper(), 0.); 109 | QCOMPARE(foo.values(), QList()); 110 | QCOMPARE(foo.minorTicks(), QList()); 111 | QCOMPARE(foo.majorTicks(), QList()); 112 | } 113 | 114 | void testSetNumSteps() { 115 | ticker.reset(); 116 | 117 | QSignalSpy spy(&ticker, SIGNAL(numStepsChanged())); 118 | QSignalSpy buildSpy(&ticker, SIGNAL(tickersBuilt())); 119 | 120 | ticker.setNumSteps(5); 121 | QCOMPARE(buildSpy.count(), 1); // ensure the build was triggered 122 | 123 | ticker.setValues(alist); 124 | QCOMPARE(ticker.majorTicks().size(), 5); 125 | ticker.setNumSteps(6); 126 | ticker.setNumSteps(6); // ensure the signal isn't emitted twice 127 | QCOMPARE(ticker.majorTicks().size(), 6); 128 | QCOMPARE(spy.count(), 2); 129 | } 130 | }; 131 | QTEST_MAIN(TestQniteTicker) 132 | #include "tst_qniteticker.moc" 133 | -------------------------------------------------------------------------------- /tests/tst_qniteticker/tst_qniteticker.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qniteticker" 5 | type: "application" 6 | 7 | Depends { name: "Qt.qmltest" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | cpp.defines: [ 17 | "QUICK_TEST_SOURCE_DIR=\"" + path + "\"", 18 | "INSTALL_ROOT=\"" + qbs.installRoot + "\"" 19 | ] 20 | 21 | files: [ 22 | "tst_qniteticker.cpp", 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /tests/tst_qnitetool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | Test 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qnitetool 14 | tst_qnitetool.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qnitetool 19 | PRIVATE 20 | qnite 21 | Qt5::Test 22 | ) 23 | 24 | add_test( 25 | tst_qnitetool 26 | tst_qnitetool 27 | ) 28 | -------------------------------------------------------------------------------- /tests/tst_qnitetool/tst_qnitetool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qniteartist.h" 4 | #include "qniteaxes.h" 5 | #include "tools/qnitetool.h" 6 | 7 | class FooArtist : public QniteArtist { 8 | Q_OBJECT 9 | 10 | public Q_SLOTS: 11 | void processData() {} 12 | 13 | public: 14 | FooArtist(QQuickItem *p = 0) : QniteArtist(p) {} 15 | }; 16 | 17 | class FooTool : public QniteTool { 18 | Q_OBJECT 19 | 20 | public: 21 | FooTool(QQuickItem *parent = 0) : QniteTool(parent) {} 22 | QList getFooArtists() { return artists(); } 23 | }; 24 | 25 | class TestQniteTool : public QObject { 26 | Q_OBJECT 27 | 28 | private slots: 29 | 30 | void testInstance() { 31 | FooTool f; 32 | QniteAxes axes; 33 | FooArtist a1, a2; 34 | 35 | auto p1 = axes.artists(); 36 | p1.append(&p1, &a1); 37 | p1.append(&p1, &a2); 38 | 39 | auto p2 = axes.tools(); 40 | p2.append(&p2, &f); 41 | 42 | QList l{&a1, &a2}; 43 | QCOMPARE(f.getFooArtists(), l); 44 | } 45 | }; 46 | 47 | QTEST_MAIN(TestQniteTool) 48 | #include "tst_qnitetool.moc" 49 | -------------------------------------------------------------------------------- /tests/tst_qnitetool/tst_qnitetool.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qnitetool" 5 | type: "application" 6 | 7 | Depends { name: "Qt.testlib" } 8 | Depends { 9 | name: "qnite" 10 | Properties { 11 | withTests: false 12 | withExamples: false 13 | } 14 | } 15 | 16 | files: [ 17 | "tst_qnitetool.cpp", 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/tst_qnitezoomtool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( 2 | Qt5 3 | COMPONENTS 4 | QuickTest 5 | REQUIRED 6 | ) 7 | 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | 12 | add_executable( 13 | tst_qnitezoomtool 14 | tst_qnitezoomtool.cpp 15 | ) 16 | 17 | target_link_libraries( 18 | tst_qnitezoomtool 19 | PRIVATE 20 | qnite 21 | Qt5::QuickTest 22 | ) 23 | 24 | target_compile_definitions( 25 | tst_qnitezoomtool 26 | PRIVATE 27 | QUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" 28 | # This is kept for compatibility with qbs, when it's 29 | # removed from the test main it can be removed here 30 | INSTALL_ROOT="" 31 | ) 32 | 33 | add_test( 34 | tst_qnitezoomtool 35 | tst_qnitezoomtool 36 | ) 37 | -------------------------------------------------------------------------------- /tests/tst_qnitezoomtool/data/tst_qnitezoomtool.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.4 2 | import QtTest 1.0 3 | 4 | import Qnite 1.0 5 | 6 | TestCase { 7 | id: testCase 8 | width: 300 9 | height: 200 10 | when: windowShown 11 | name: "QniteZoomTool" 12 | visible: true 13 | 14 | Component { 15 | id: figure 16 | Figure { 17 | anchors.fill: parent 18 | axes.xBounds: [-0.5, 0.15] 19 | axes.yBounds: [-10, 0] 20 | 21 | tools: [ 22 | ZoomTool { 23 | id: zoomTool 24 | objectName: "zoomTool" 25 | anchors.fill: parent 26 | } 27 | ] 28 | } 29 | } 30 | 31 | // Verifies both axis bounds are updated when zooming the plot 32 | function test_zoom_bounds_update() { 33 | var fig = figure.createObject(testCase); 34 | var zoom = findChild(fig, "zoomTool"); 35 | verify(zoom); 36 | 37 | // Verifies defaults 38 | compare(fig.axes.axisX.lowerBound, -0.5); 39 | compare(fig.axes.axisX.upperBound, 0.15); 40 | compare(fig.axes.axisY.lowerBound, -10); 41 | compare(fig.axes.axisY.upperBound, 0); 42 | compare(fig.axes.width, 258); 43 | compare(fig.axes.height, 140); 44 | compare(fig.width, 300); 45 | compare(fig.height, 200); 46 | 47 | // Zooms 48 | mouseDrag(zoom, 100, 50, 20, 10, Qt.RightButton); 49 | 50 | // Verifies bounds are correctly updated 51 | fuzzyCompare(fig.axes.axisX.lowerBound, -0.24, 0.01); 52 | fuzzyCompare(fig.axes.axisX.upperBound, -0.19, 0.01); 53 | fuzzyCompare(fig.axes.axisY.lowerBound, -4.28, 0.01); 54 | fuzzyCompare(fig.axes.axisY.upperBound, -3.57, 0.01); 55 | 56 | fig.destroy(); 57 | } 58 | 59 | 60 | // Verifies reset changes zoom level correctly and reenables it if necessary 61 | function test_reset_zoom() { 62 | var fig = figure.createObject(testCase); 63 | var zoom = findChild(fig, "zoomTool"); 64 | verify(zoom); 65 | 66 | // Verifies defaults 67 | compare(fig.axes.axisX.lowerBound, -0.5); 68 | compare(fig.axes.axisX.upperBound, 0.15); 69 | compare(fig.axes.axisY.lowerBound, -10); 70 | compare(fig.axes.axisY.upperBound, 0); 71 | compare(zoom.enabled, true); 72 | 73 | // Zooms 74 | mouseDrag(zoom, 100, 50, 20, 10, Qt.RightButton); 75 | 76 | // Verifies updated bounds 77 | fuzzyCompare(fig.axes.axisX.lowerBound, -0.24, 0.01); 78 | fuzzyCompare(fig.axes.axisX.upperBound, -0.19, 0.01); 79 | fuzzyCompare(fig.axes.axisY.lowerBound, -4.28, 0.01); 80 | fuzzyCompare(fig.axes.axisY.upperBound, -3.57, 0.01); 81 | compare(zoom.enabled, true); 82 | 83 | zoom.reset(); 84 | 85 | // Verifies zoom is reset 86 | compare(fig.axes.axisX.lowerBound, -0.5); 87 | compare(fig.axes.axisX.upperBound, 0.15); 88 | compare(fig.axes.axisY.lowerBound, -10); 89 | compare(fig.axes.axisY.upperBound, 0); 90 | compare(zoom.enabled, true); 91 | 92 | // Zooms some times 93 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 94 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 95 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 96 | 97 | // Verifies bounds are updated and tool is now disabled 98 | fuzzyCompare(fig.axes.axisX.lowerBound, -0.242766, 0.000001); 99 | fuzzyCompare(fig.axes.axisX.upperBound, -0.242761, 0.000001); 100 | fuzzyCompare(fig.axes.axisY.lowerBound, -7.407525, 0.000001); 101 | fuzzyCompare(fig.axes.axisY.upperBound, -7.407069, 0.000001); 102 | compare(zoom.enabled, false); 103 | 104 | zoom.reset(); 105 | 106 | // Verifies zoom is reset 107 | compare(fig.axes.axisX.lowerBound, -0.5); 108 | compare(fig.axes.axisX.upperBound, 0.15); 109 | compare(fig.axes.axisY.lowerBound, -10); 110 | compare(fig.axes.axisY.upperBound, 0); 111 | compare(zoom.enabled, true); 112 | 113 | fig.destroy(); 114 | } 115 | 116 | // Verifies plot doesn't zoom more than minimum zoom size calculated from 117 | // the current zoom factor 118 | function test_default_zoom_factor() { 119 | var fig = figure.createObject(testCase); 120 | var zoom = findChild(fig, "zoomTool"); 121 | verify(zoom); 122 | 123 | // Verifies defaults 124 | compare(fig.axes.axisX.lowerBound, -0.5); 125 | compare(fig.axes.axisX.upperBound, 0.15); 126 | compare(fig.axes.axisY.lowerBound, -10); 127 | compare(fig.axes.axisY.upperBound, 0); 128 | compare(zoom.minZoomFactor, 4); 129 | compare(zoom.enabled, true); 130 | 131 | // Zooms some times 132 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 133 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 134 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 135 | 136 | // Verifies bounds are updated and tool is now disabled 137 | fuzzyCompare(fig.axes.axisX.lowerBound, -0.242766, 0.000001); 138 | fuzzyCompare(fig.axes.axisX.upperBound, -0.242761, 0.000001); 139 | fuzzyCompare(fig.axes.axisY.lowerBound, -7.407525, 0.000001); 140 | fuzzyCompare(fig.axes.axisY.upperBound, -7.407069, 0.000001); 141 | compare(zoom.enabled, false); 142 | 143 | // Zooms some more 144 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 145 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 146 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 147 | 148 | // Verifies bound are unchanged 149 | fuzzyCompare(fig.axes.axisX.lowerBound, -0.242766, 0.000001); 150 | fuzzyCompare(fig.axes.axisX.upperBound, -0.242761, 0.000001); 151 | fuzzyCompare(fig.axes.axisY.lowerBound, -7.407525, 0.000001); 152 | fuzzyCompare(fig.axes.axisY.upperBound, -7.407069, 0.000001); 153 | compare(zoom.enabled, false); 154 | 155 | // Increments default zoom factor 156 | zoom.minZoomFactor = 5; 157 | 158 | // Verifies zoom is enabled again 159 | compare(zoom.enabled, true); 160 | 161 | // Zooms again 162 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 163 | mouseDrag(zoom, 100, 100, 5, 5, Qt.RightButton); 164 | 165 | // Verifies bounds are now changed and new minimum zoom is reached 166 | fuzzyCompare(fig.axes.axisX.lowerBound, -0.242763, 0.000001); 167 | fuzzyCompare(fig.axes.axisX.upperBound, -0.242763, 0.000001); 168 | fuzzyCompare(fig.axes.axisY.lowerBound, -7.407411, 0.000001); 169 | fuzzyCompare(fig.axes.axisY.upperBound, -7.407395, 0.000001); 170 | compare(zoom.enabled, false); 171 | 172 | fig.destroy(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /tests/tst_qnitezoomtool/tst_qnitezoomtool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "qnite.h" 5 | 6 | // We need to set the QML import paths before the test file is read and this is 7 | // the only working way 8 | int main(int argc, char **argv) { 9 | QTEST_ADD_GPU_BLACKLIST_SUPPORT 10 | QTEST_SET_MAIN_SOURCE_PATH 11 | QVector newArgv; 12 | int i = 0; 13 | for (; i < argc; i++) { 14 | newArgv << argv[i]; 15 | } 16 | newArgv << strdup("-import"); 17 | newArgv << strdup("qrc:/"); 18 | newArgv << strdup("-import"); 19 | newArgv << strdup("qrc:/qml"); 20 | newArgv << strdup("-import"); 21 | newArgv << strdup(INSTALL_ROOT); 22 | return quick_test_main(newArgv.size(), newArgv.data(), "tst_qnitezoomtool", 23 | QUICK_TEST_SOURCE_DIR); 24 | } 25 | -------------------------------------------------------------------------------- /tests/tst_qnitezoomtool/tst_qnitezoomtool.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | CppApplication { 4 | name: "tst_qnitezoomtool" 5 | 6 | cpp.defines: [ 7 | "QUICK_TEST_SOURCE_DIR=\"" + path + "\"", 8 | "INSTALL_ROOT=\"" + qbs.installRoot + "\"" 9 | ] 10 | 11 | Depends { name: "Qt.qmltest" } 12 | Depends { 13 | name: "qnite" 14 | Properties { 15 | withTests: false 16 | withExamples: false 17 | } 18 | } 19 | 20 | files: [ 21 | "tst_qnitezoomtool.cpp", 22 | ] 23 | } 24 | 25 | --------------------------------------------------------------------------------