├── .clang-format ├── .concourse.yml ├── .concourse └── buildrpm.sh ├── .gitignore ├── .travis.yml ├── .wafcustom └── unites.py ├── LICENSE ├── README.md ├── catalog ├── goatplot.xml └── wscript ├── meta ├── goatplot.pc.in ├── goatplot.spec.in └── wscript ├── screenshot.png ├── src ├── goat-dataset-interface.c ├── goat-dataset-interface.h ├── goat-dataset-simple.c ├── goat-dataset-simple.h ├── goat-dataset-store.c ├── goat-dataset-store.h ├── goat-plot-enum.c ├── goat-plot-enum.h ├── goat-plot-internal.h ├── goat-plot.c ├── goat-plot.h ├── goat-scale-interface.c ├── goat-scale-interface.h ├── goat-scale-linear.c ├── goat-scale-linear.h ├── goat-scale-log.c ├── goat-scale-log.h ├── goat-utils.c ├── goat-utils.h ├── goatplot.h └── wscript ├── tests ├── dynamic.c ├── glade-line.c ├── glade-line.glade ├── screenshot.c └── wscript ├── waf └── wscript /.clang-format: -------------------------------------------------------------------------------- 1 | Language : Cpp 2 | AccessModifierOffset : -4 3 | ConstructorInitializerIndentWidth : 4 4 | AlignEscapedNewlinesLeft: false 5 | AlignTrailingComments: true 6 | AllowAllParametersOfDeclarationOnNextLine: true 7 | AllowShortBlocksOnASingleLine: false 8 | AllowShortIfStatementsOnASingleLine: false 9 | AllowShortLoopsOnASingleLine: false 10 | AllowShortFunctionsOnASingleLine: false 11 | AlwaysBreakTemplateDeclarations: true 12 | AlwaysBreakBeforeMultilineStrings: false 13 | #AlwaysBreakAfterDefinitionReturnType : true 14 | BreakBeforeBinaryOperators: false 15 | BreakBeforeTernaryOperators: true 16 | BreakConstructorInitializersBeforeComma: false 17 | BinPackParameters: true 18 | ColumnLimit : 120 19 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 20 | DerivePointerAlignment: false 21 | ExperimentalAutoDetectBinPacking: false 22 | IndentCaseLabels: false 23 | IndentWrappedFunctionNames: false 24 | IndentFunctionDeclarationAfterType: false 25 | MaxEmptyLinesToKeep : 4 26 | KeepEmptyLinesAtTheStartOfBlocks: true 27 | NamespaceIndentation: None 28 | ObjCSpaceAfterProperty: false 29 | ObjCSpaceBeforeProtocolList: true 30 | PenaltyBreakBeforeFirstCallParameter : 19 31 | PenaltyBreakComment : 300 32 | PenaltyBreakString : 1000 33 | PenaltyBreakFirstLessLess : 120 34 | PenaltyExcessCharacter : 1000000 35 | PenaltyReturnTypeOnItsOwnLine : 60 36 | PointerAlignment: Right 37 | SpacesBeforeTrailingComments : 1 38 | Cpp11BracedListStyle: true 39 | Standard: Cpp11 40 | IndentWidth : 4 41 | TabWidth : 4 42 | UseTab: ForIndentation 43 | BreakBeforeBraces: Linux 44 | SpacesInParentheses: false 45 | SpacesInAngles: false 46 | SpaceInEmptyParentheses: false 47 | SpacesInCStyleCastParentheses: false 48 | SpacesInContainerLiterals: true 49 | SpaceBeforeAssignmentOperators: true 50 | ContinuationIndentWidth : 4 51 | CommentPragmas: '^IWYU pragma:' 52 | SpaceBeforeParens : Always 53 | DisableFormat : false 54 | -------------------------------------------------------------------------------- /.concourse.yml: -------------------------------------------------------------------------------- 1 | resource_types: 2 | - name: copr 3 | type: docker-image 4 | source: 5 | repository: quay.io/ahoi/concourse-copr-resource 6 | 7 | resources: 8 | - name: container-test-fedora 9 | type: docker-image 10 | source: 11 | repository: quay.io/ratpoison/oregano-test-fedora 12 | username: {{quay-drahnr-username}} 13 | password: {{quay-drahnr-password}} 14 | 15 | - name: git-tag-resource 16 | type: git 17 | source: 18 | branch: master 19 | uri: https://github.com/drahnr/goatplot.git 20 | tag_filter: v[0-9]*.[0-9]*.[0-9]* 21 | 22 | - name: copr-resource 23 | type: copr 24 | source: 25 | login: {{copr-login}} 26 | token: {{copr-token}} 27 | url: {{copr-url}} 28 | 29 | # - name: rpm-release 30 | # type: s3 31 | # source: 32 | # endpoint: https://minio.spearow.io 33 | # bucket: goatplot-rpm 34 | # regexp: (.*).rpm 35 | # access_key_id: {{minio-access-key}} 36 | # secret_access_key: {{minio-secret-key}} 37 | 38 | jobs: 39 | - name: build-pkg-rpm 40 | build_logs_to_retain: 5 41 | public: true 42 | plan: 43 | - in_parallel: 44 | - get: container-test-fedora 45 | trigger: true 46 | - get: git-tag-resource 47 | trigger: true 48 | 49 | - task: create-rpm 50 | timeout: 7m 51 | image: container-test-fedora 52 | config: 53 | platform: linux 54 | inputs: 55 | - name: git-tag-resource 56 | outputs: 57 | - name: srpm 58 | - name: rpm 59 | run: 60 | path: sh 61 | args: 62 | - -exec 63 | - | 64 | ./.concourse/buildrpm.sh goatplot "$(./waf version | head -n 1)" 65 | dir: git-tag-resource 66 | 67 | # - in_parallel: 68 | # - put: rpm-release 69 | # params: 70 | # file: rpm/*.rpm 71 | # acl: public-read 72 | 73 | - put: copr 74 | resource: copr-resource 75 | params: 76 | project_id: 826 77 | rpmbuild_dir: "srpm" 78 | chroots: ["fedora-rawhide-x86_64", "fedora-30-x86_64", "fedora-29-x86_64"] 79 | enable_net: false 80 | max_n_bytes: 250000000 81 | regex: ".*goatplot-.*\\.src\\.rpm$" 82 | 83 | -------------------------------------------------------------------------------- /.concourse/buildrpm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -x 4 | set -e 5 | 6 | ./waf configure build --no-tests 7 | 8 | name="${1}" 9 | version="${2}" 10 | pwd 2>&1 11 | RPMBUILD_DIR="$(pwd)/rpmbuild" 12 | 13 | mkdir -p ${RPMBUILD_DIR}/{SOURCES,BUILD,RPMS,SRPMS,SPECS} 14 | 15 | cp -v build/meta/${name}.spec ${RPMBUILD_DIR}/SPECS/ 16 | 17 | git archive --format=tar --prefix=${name}-${version}/ HEAD | gzip > v${version}.tar.gz 18 | 19 | cp -v v${version}*.tar.* ${RPMBUILD_DIR}/SOURCES/ 20 | 21 | cd ${RPMBUILD_DIR} 22 | rpmbuild \ 23 | --define "_topdir %(pwd)" \ 24 | --define "_builddir %{_topdir}/BUILD" \ 25 | --define "_rpmdir %{_topdir}/RPMS" \ 26 | --define "_srcrpmdir %{_topdir}/SRPMS" \ 27 | --define "_specdir %{_topdir}/SPECS" \ 28 | --define "_sourcedir %{_topdir}/SOURCES" \ 29 | -ba SPECS/${name}.spec 30 | 31 | mkdir -p $(pwd)/${1}/{,s}rpm/ 32 | rm -vf ${RPMBUILD_DIR}/RPMS/*/${name}-*debug*.rpm 33 | cp -vf ${RPMBUILD_DIR}/RPMS/*/${name}-*.rpm $(pwd)/../../rpm/ 34 | cp -vf ${RPMBUILD_DIR}/SRPMS/${name}-*.src.rpm $(pwd)/../../srpm/ 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | *~ 3 | .lock* 4 | *.lock 5 | *.pyc 6 | .waf-* 7 | *.tar.gz 8 | *.tar.xz 9 | rpmbuild -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | before_install: 3 | - "sudo apt-get update" 4 | - "export DISPLAY=:99.0" 5 | - "sh -e /etc/init.d/xvfb start" 6 | install: sudo apt-get -q -y install intltool libgtk-3-dev 7 | compiler: 8 | - gcc 9 | - clang 10 | script: ./waf configure --prefix=/usr debug -j4 --no-glade-dtd-check 11 | -------------------------------------------------------------------------------- /.wafcustom/unites.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | # Carlos Rafael Giani, 2006 4 | # Thomas Nagy, 2010 5 | # Bernhard Schuster, 2014-2016 6 | 7 | """ 8 | Unit testing system for C/C++/D providing test execution: 9 | 10 | * in parallel, by using ``waf -j`` 11 | * partial (only the tests that have changed) or full (by using ``waf``) 12 | 13 | The tests are declared by adding the **test** feature to programs:: 14 | 15 | def options(opt): 16 | opt.load('compiler_c unites') 17 | def configure(conf): 18 | conf.load('compiler_c unites') 19 | def build(bld): 20 | bld(features='c cprogram unites', source='main.cpp', target='app') 21 | # or 22 | bld.program(features='unites', source='main2.cpp', target='app2') 23 | 24 | When the build is executed, the program 'test' will be built and executed without arguments. 25 | The success/failure is detected by looking at the return code. The status and the standard output/error 26 | are stored on the build context. 27 | """ 28 | 29 | import os, sys 30 | from waflib.TaskGen import feature, after_method 31 | from waflib import Utils, Task, Logs, Options, Errors, Context 32 | 33 | @feature('unites') 34 | @after_method('apply_link') 35 | def make_test(self): 36 | """Create the unit test task. There can be only one unit test task per task generator.""" 37 | if getattr(self, 'link_task', None): 38 | self.create_task('unites', self.link_task.outputs) 39 | 40 | 41 | class unites(Task.Task): 42 | """ 43 | Execute a unit test 44 | """ 45 | color = 'PINK' 46 | after = ['vnum','inst'] 47 | vars = [] 48 | def runnable_status(self): 49 | """ 50 | Always execute the task if ``waf --no-tests`` was not used 51 | """ 52 | if getattr(Options.options, 'no_tests', False): 53 | return Task.SKIP_ME 54 | 55 | ret = super(unites, self).runnable_status() 56 | if ret == Task.SKIP_ME: 57 | return Task.RUN_ME 58 | return ret 59 | 60 | def run(self): 61 | """ 62 | Execute the test. This can fail. 63 | """ 64 | 65 | testname = str(self.inputs[0]) 66 | filename = self.inputs[0].abspath() 67 | 68 | 69 | 70 | self.ut_exec = getattr(self.generator, 'ut_exec', [filename]) 71 | 72 | try: 73 | fu = getattr(self.generator.bld, 'all_test_paths') 74 | except AttributeError: 75 | # this operation may be performed by at most #maxjobs 76 | fu = os.environ.copy() 77 | 78 | lst = [] 79 | for g in self.generator.bld.groups: 80 | for tg in g: 81 | if getattr(tg, 'link_task', None): 82 | s = tg.link_task.outputs[0].parent.abspath() 83 | if s not in lst: 84 | lst.append(s) 85 | 86 | def add_path(dct, path, var): 87 | dct[var] = os.pathsep.join(Utils.to_list(path) + [os.environ.get(var, '')]) 88 | 89 | if Utils.is_win32: 90 | add_path(fu, lst, 'PATH') 91 | elif Utils.unversioned_sys_platform() == 'darwin': 92 | add_path(fu, lst, 'DYLD_LIBRARY_PATH') 93 | add_path(fu, lst, 'LD_LIBRARY_PATH') 94 | else: 95 | add_path(fu, lst, 'LD_LIBRARY_PATH') 96 | self.generator.bld.all_test_paths = fu 97 | 98 | cwd = getattr(self.generator, 'ut_cwd', '') or self.inputs[0].parent.abspath() 99 | testcmd = getattr(Options.options, 'testcmd', False) 100 | 101 | if testcmd: 102 | self.ut_exec = (testcmd % self.ut_exec[0]).split(' ') 103 | 104 | #overwrite the default logger to prevent duplicate logging 105 | proc = Utils.subprocess.Popen(self.ut_exec,\ 106 | cwd=cwd,\ 107 | env=fu,\ 108 | stderr=Utils.subprocess.PIPE,\ 109 | stdout=Utils.subprocess.PIPE) 110 | 111 | (out, err) = proc.communicate() 112 | 113 | msg = [] 114 | if out: 115 | msg.append('stdout:%s%s' % (os.linesep, out.decode('utf-8'))) 116 | if err: 117 | msg.append('stderr:%s%s' % (os.linesep, err.decode('utf-8'))) 118 | msg = os.linesep.join(msg) 119 | Logs.debug(msg) 120 | 121 | return proc.returncode 122 | 123 | 124 | def options(opt): 125 | """ 126 | Provide the ``permissive``, ``--notests`` and ``--testcmd`` command-line options. 127 | """ 128 | opt.add_option('--permissive-tests', action='store_true', default=False, help='Do not force exit if tests fail', dest='permissive_tests') 129 | opt.add_option('--no-tests', action='store_true', default=False, help='Exec no unit tests', dest='no_tests') 130 | opt.add_option('--testcmd', action='store', default=False, 131 | help = 'Run the unit tests using the test-cmd string' 132 | ' example "--test-cmd="valgrind --error-exitcode=1' 133 | ' %s" to run under valgrind', dest='testcmd') 134 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | 60 | Finally, software patents pose a constant threat to the existence of 61 | any free program. We wish to make sure that a company cannot 62 | effectively restrict the users of a free program by obtaining a 63 | restrictive license from a patent holder. Therefore, we insist that 64 | any patent license obtained for a version of the library must be 65 | consistent with the full freedom of use specified in this license. 66 | 67 | Most GNU software, including some libraries, is covered by the 68 | ordinary GNU General Public License. This license, the GNU Lesser 69 | General Public License, applies to certain designated libraries, and 70 | is quite different from the ordinary General Public License. We use 71 | this license for certain libraries in order to permit linking those 72 | libraries into non-free programs. 73 | 74 | When a program is linked with a library, whether statically or using 75 | a shared library, the combination of the two is legally speaking a 76 | combined work, a derivative of the original library. The ordinary 77 | General Public License therefore permits such linking only if the 78 | entire combination fits its criteria of freedom. The Lesser General 79 | Public License permits more lax criteria for linking other code with 80 | the library. 81 | 82 | We call this license the "Lesser" General Public License because it 83 | does Less to protect the user's freedom than the ordinary General 84 | Public License. It also provides other free software developers Less 85 | of an advantage over competing non-free programs. These disadvantages 86 | are the reason we use the ordinary General Public License for many 87 | libraries. However, the Lesser license provides advantages in certain 88 | special circumstances. 89 | 90 | For example, on rare occasions, there may be a special need to 91 | encourage the widest possible use of a certain library, so that it becomes 92 | a de-facto standard. To achieve this, non-free programs must be 93 | allowed to use the library. A more frequent case is that a free 94 | library does the same job as widely used non-free libraries. In this 95 | case, there is little to gain by limiting the free library to free 96 | software only, so we use the Lesser General Public License. 97 | 98 | In other cases, permission to use a particular library in non-free 99 | programs enables a greater number of people to use a large body of 100 | free software. For example, permission to use the GNU C Library in 101 | non-free programs enables many more people to use the whole GNU 102 | operating system, as well as its variant, the GNU/Linux operating 103 | system. 104 | 105 | Although the Lesser General Public License is Less protective of the 106 | users' freedom, it does ensure that the user of a program that is 107 | linked with the Library has the freedom and the wherewithal to run 108 | that program using a modified version of the Library. 109 | 110 | The precise terms and conditions for copying, distribution and 111 | modification follow. Pay close attention to the difference between a 112 | "work based on the library" and a "work that uses the library". The 113 | former contains code derived from the library, whereas the latter must 114 | be combined with the library in order to run. 115 | 116 | 117 | GNU LESSER GENERAL PUBLIC LICENSE 118 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 119 | 120 | 0. This License Agreement applies to any software library or other 121 | program which contains a notice placed by the copyright holder or 122 | other authorized party saying it may be distributed under the terms of 123 | this Lesser General Public License (also called "this License"). 124 | Each licensee is addressed as "you". 125 | 126 | A "library" means a collection of software functions and/or data 127 | prepared so as to be conveniently linked with application programs 128 | (which use some of those functions and data) to form executables. 129 | 130 | The "Library", below, refers to any such software library or work 131 | which has been distributed under these terms. A "work based on the 132 | Library" means either the Library or any derivative work under 133 | copyright law: that is to say, a work containing the Library or a 134 | portion of it, either verbatim or with modifications and/or translated 135 | straightforwardly into another language. (Hereinafter, translation is 136 | included without limitation in the term "modification".) 137 | 138 | "Source code" for a work means the preferred form of the work for 139 | making modifications to it. For a library, complete source code means 140 | all the source code for all modules it contains, plus any associated 141 | interface definition files, plus the scripts used to control compilation 142 | and installation of the library. 143 | 144 | Activities other than copying, distribution and modification are not 145 | covered by this License; they are outside its scope. The act of 146 | running a program using the Library is not restricted, and output from 147 | such a program is covered only if its contents constitute a work based 148 | on the Library (independent of the use of the Library in a tool for 149 | writing it). Whether that is true depends on what the Library does 150 | and what the program that uses the Library does. 151 | 152 | 1. You may copy and distribute verbatim copies of the Library's 153 | complete source code as you receive it, in any medium, provided that 154 | you conspicuously and appropriately publish on each copy an 155 | appropriate copyright notice and disclaimer of warranty; keep intact 156 | all the notices that refer to this License and to the absence of any 157 | warranty; and distribute a copy of this License along with the 158 | Library. 159 | 160 | You may charge a fee for the physical act of transferring a copy, 161 | and you may at your option offer warranty protection in exchange for a 162 | fee. 163 | 164 | 165 | 2. You may modify your copy or copies of the Library or any portion 166 | of it, thus forming a work based on the Library, and copy and 167 | distribute such modifications or work under the terms of Section 1 168 | above, provided that you also meet all of these conditions: 169 | 170 | a) The modified work must itself be a software library. 171 | 172 | b) You must cause the files modified to carry prominent notices 173 | stating that you changed the files and the date of any change. 174 | 175 | c) You must cause the whole of the work to be licensed at no 176 | charge to all third parties under the terms of this License. 177 | 178 | d) If a facility in the modified Library refers to a function or a 179 | table of data to be supplied by an application program that uses 180 | the facility, other than as an argument passed when the facility 181 | is invoked, then you must make a good faith effort to ensure that, 182 | in the event an application does not supply such function or 183 | table, the facility still operates, and performs whatever part of 184 | its purpose remains meaningful. 185 | 186 | (For example, a function in a library to compute square roots has 187 | a purpose that is entirely well-defined independent of the 188 | application. Therefore, Subsection 2d requires that any 189 | application-supplied function or table used by this function must 190 | be optional: if the application does not supply it, the square 191 | root function must still compute square roots.) 192 | 193 | These requirements apply to the modified work as a whole. If 194 | identifiable sections of that work are not derived from the Library, 195 | and can be reasonably considered independent and separate works in 196 | themselves, then this License, and its terms, do not apply to those 197 | sections when you distribute them as separate works. But when you 198 | distribute the same sections as part of a whole which is a work based 199 | on the Library, the distribution of the whole must be on the terms of 200 | this License, whose permissions for other licensees extend to the 201 | entire whole, and thus to each and every part regardless of who wrote 202 | it. 203 | 204 | Thus, it is not the intent of this section to claim rights or contest 205 | your rights to work written entirely by you; rather, the intent is to 206 | exercise the right to control the distribution of derivative or 207 | collective works based on the Library. 208 | 209 | In addition, mere aggregation of another work not based on the Library 210 | with the Library (or with a work based on the Library) on a volume of 211 | a storage or distribution medium does not bring the other work under 212 | the scope of this License. 213 | 214 | 3. You may opt to apply the terms of the ordinary GNU General Public 215 | License instead of this License to a given copy of the Library. To do 216 | this, you must alter all the notices that refer to this License, so 217 | that they refer to the ordinary GNU General Public License, version 2, 218 | instead of to this License. (If a newer version than version 2 of the 219 | ordinary GNU General Public License has appeared, then you can specify 220 | that version instead if you wish.) Do not make any other change in 221 | these notices. 222 | 223 | 224 | Once this change is made in a given copy, it is irreversible for 225 | that copy, so the ordinary GNU General Public License applies to all 226 | subsequent copies and derivative works made from that copy. 227 | 228 | This option is useful when you wish to copy part of the code of 229 | the Library into a program that is not a library. 230 | 231 | 4. You may copy and distribute the Library (or a portion or 232 | derivative of it, under Section 2) in object code or executable form 233 | under the terms of Sections 1 and 2 above provided that you accompany 234 | it with the complete corresponding machine-readable source code, which 235 | must be distributed under the terms of Sections 1 and 2 above on a 236 | medium customarily used for software interchange. 237 | 238 | If distribution of object code is made by offering access to copy 239 | from a designated place, then offering equivalent access to copy the 240 | source code from the same place satisfies the requirement to 241 | distribute the source code, even though third parties are not 242 | compelled to copy the source along with the object code. 243 | 244 | 5. A program that contains no derivative of any portion of the 245 | Library, but is designed to work with the Library by being compiled or 246 | linked with it, is called a "work that uses the Library". Such a 247 | work, in isolation, is not a derivative work of the Library, and 248 | therefore falls outside the scope of this License. 249 | 250 | However, linking a "work that uses the Library" with the Library 251 | creates an executable that is a derivative of the Library (because it 252 | contains portions of the Library), rather than a "work that uses the 253 | library". The executable is therefore covered by this License. 254 | Section 6 states terms for distribution of such executables. 255 | 256 | When a "work that uses the Library" uses material from a header file 257 | that is part of the Library, the object code for the work may be a 258 | derivative work of the Library even though the source code is not. 259 | Whether this is true is especially significant if the work can be 260 | linked without the Library, or if the work is itself a library. The 261 | threshold for this to be true is not precisely defined by law. 262 | 263 | If such an object file uses only numerical parameters, data 264 | structure layouts and accessors, and small macros and small inline 265 | functions (ten lines or less in length), then the use of the object 266 | file is unrestricted, regardless of whether it is legally a derivative 267 | work. (Executables containing this object code plus portions of the 268 | Library will still fall under Section 6.) 269 | 270 | Otherwise, if the work is a derivative of the Library, you may 271 | distribute the object code for the work under the terms of Section 6. 272 | Any executables containing that work also fall under Section 6, 273 | whether or not they are linked directly with the Library itself. 274 | 275 | 276 | 6. As an exception to the Sections above, you may also combine or 277 | link a "work that uses the Library" with the Library to produce a 278 | work containing portions of the Library, and distribute that work 279 | under terms of your choice, provided that the terms permit 280 | modification of the work for the customer's own use and reverse 281 | engineering for debugging such modifications. 282 | 283 | You must give prominent notice with each copy of the work that the 284 | Library is used in it and that the Library and its use are covered by 285 | this License. You must supply a copy of this License. If the work 286 | during execution displays copyright notices, you must include the 287 | copyright notice for the Library among them, as well as a reference 288 | directing the user to the copy of this License. Also, you must do one 289 | of these things: 290 | 291 | a) Accompany the work with the complete corresponding 292 | machine-readable source code for the Library including whatever 293 | changes were used in the work (which must be distributed under 294 | Sections 1 and 2 above); and, if the work is an executable linked 295 | with the Library, with the complete machine-readable "work that 296 | uses the Library", as object code and/or source code, so that the 297 | user can modify the Library and then relink to produce a modified 298 | executable containing the modified Library. (It is understood 299 | that the user who changes the contents of definitions files in the 300 | Library will not necessarily be able to recompile the application 301 | to use the modified definitions.) 302 | 303 | b) Use a suitable shared library mechanism for linking with the 304 | Library. A suitable mechanism is one that (1) uses at run time a 305 | copy of the library already present on the user's computer system, 306 | rather than copying library functions into the executable, and (2) 307 | will operate properly with a modified version of the library, if 308 | the user installs one, as long as the modified version is 309 | interface-compatible with the version that the work was made with. 310 | 311 | c) Accompany the work with a written offer, valid for at 312 | least three years, to give the same user the materials 313 | specified in Subsection 6a, above, for a charge no more 314 | than the cost of performing this distribution. 315 | 316 | d) If distribution of the work is made by offering access to copy 317 | from a designated place, offer equivalent access to copy the above 318 | specified materials from the same place. 319 | 320 | e) Verify that the user has already received a copy of these 321 | materials or that you have already sent this user a copy. 322 | 323 | For an executable, the required form of the "work that uses the 324 | Library" must include any data and utility programs needed for 325 | reproducing the executable from it. However, as a special exception, 326 | the materials to be distributed need not include anything that is 327 | normally distributed (in either source or binary form) with the major 328 | components (compiler, kernel, and so on) of the operating system on 329 | which the executable runs, unless that component itself accompanies 330 | the executable. 331 | 332 | It may happen that this requirement contradicts the license 333 | restrictions of other proprietary libraries that do not normally 334 | accompany the operating system. Such a contradiction means you cannot 335 | use both them and the Library together in an executable that you 336 | distribute. 337 | 338 | 339 | 7. You may place library facilities that are a work based on the 340 | Library side-by-side in a single library together with other library 341 | facilities not covered by this License, and distribute such a combined 342 | library, provided that the separate distribution of the work based on 343 | the Library and of the other library facilities is otherwise 344 | permitted, and provided that you do these two things: 345 | 346 | a) Accompany the combined library with a copy of the same work 347 | based on the Library, uncombined with any other library 348 | facilities. This must be distributed under the terms of the 349 | Sections above. 350 | 351 | b) Give prominent notice with the combined library of the fact 352 | that part of it is a work based on the Library, and explaining 353 | where to find the accompanying uncombined form of the same work. 354 | 355 | 8. You may not copy, modify, sublicense, link with, or distribute 356 | the Library except as expressly provided under this License. Any 357 | attempt otherwise to copy, modify, sublicense, link with, or 358 | distribute the Library is void, and will automatically terminate your 359 | rights under this License. However, parties who have received copies, 360 | or rights, from you under this License will not have their licenses 361 | terminated so long as such parties remain in full compliance. 362 | 363 | 9. You are not required to accept this License, since you have not 364 | signed it. However, nothing else grants you permission to modify or 365 | distribute the Library or its derivative works. These actions are 366 | prohibited by law if you do not accept this License. Therefore, by 367 | modifying or distributing the Library (or any work based on the 368 | Library), you indicate your acceptance of this License to do so, and 369 | all its terms and conditions for copying, distributing or modifying 370 | the Library or works based on it. 371 | 372 | 10. Each time you redistribute the Library (or any work based on the 373 | Library), the recipient automatically receives a license from the 374 | original licensor to copy, distribute, link with or modify the Library 375 | subject to these terms and conditions. You may not impose any further 376 | restrictions on the recipients' exercise of the rights granted herein. 377 | You are not responsible for enforcing compliance by third parties with 378 | this License. 379 | 380 | 381 | 11. If, as a consequence of a court judgment or allegation of patent 382 | infringement or for any other reason (not limited to patent issues), 383 | conditions are imposed on you (whether by court order, agreement or 384 | otherwise) that contradict the conditions of this License, they do not 385 | excuse you from the conditions of this License. If you cannot 386 | distribute so as to satisfy simultaneously your obligations under this 387 | License and any other pertinent obligations, then as a consequence you 388 | may not distribute the Library at all. For example, if a patent 389 | license would not permit royalty-free redistribution of the Library by 390 | all those who receive copies directly or indirectly through you, then 391 | the only way you could satisfy both it and this License would be to 392 | refrain entirely from distribution of the Library. 393 | 394 | If any portion of this section is held invalid or unenforceable under any 395 | particular circumstance, the balance of the section is intended to apply, 396 | and the section as a whole is intended to apply in other circumstances. 397 | 398 | It is not the purpose of this section to induce you to infringe any 399 | patents or other property right claims or to contest validity of any 400 | such claims; this section has the sole purpose of protecting the 401 | integrity of the free software distribution system which is 402 | implemented by public license practices. Many people have made 403 | generous contributions to the wide range of software distributed 404 | through that system in reliance on consistent application of that 405 | system; it is up to the author/donor to decide if he or she is willing 406 | to distribute software through any other system and a licensee cannot 407 | impose that choice. 408 | 409 | This section is intended to make thoroughly clear what is believed to 410 | be a consequence of the rest of this License. 411 | 412 | 12. If the distribution and/or use of the Library is restricted in 413 | certain countries either by patents or by copyrighted interfaces, the 414 | original copyright holder who places the Library under this License may add 415 | an explicit geographical distribution limitation excluding those countries, 416 | so that distribution is permitted only in or among countries not thus 417 | excluded. In such case, this License incorporates the limitation as if 418 | written in the body of this License. 419 | 420 | 13. The Free Software Foundation may publish revised and/or new 421 | versions of the Lesser General Public License from time to time. 422 | Such new versions will be similar in spirit to the present version, 423 | but may differ in detail to address new problems or concerns. 424 | 425 | Each version is given a distinguishing version number. If the Library 426 | specifies a version number of this License which applies to it and 427 | "any later version", you have the option of following the terms and 428 | conditions either of that version or of any later version published by 429 | the Free Software Foundation. If the Library does not specify a 430 | license version number, you may choose any version ever published by 431 | the Free Software Foundation. 432 | 433 | 434 | 14. If you wish to incorporate parts of the Library into other free 435 | programs whose distribution conditions are incompatible with these, 436 | write to the author to ask for permission. For software which is 437 | copyrighted by the Free Software Foundation, write to the Free 438 | Software Foundation; we sometimes make exceptions for this. Our 439 | decision will be guided by the two goals of preserving the free status 440 | of all derivatives of our free software and of promoting the sharing 441 | and reuse of software generally. 442 | 443 | NO WARRANTY 444 | 445 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 446 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 447 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 448 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 449 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 450 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 451 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 452 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 453 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 454 | 455 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 456 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 457 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 458 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 459 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 460 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 461 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 462 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 463 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 464 | DAMAGES. 465 | 466 | END OF TERMS AND CONDITIONS 467 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## goatplot 2 | 3 | [![Join the chat at https://gitter.im/drahnr/goatplot](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/drahnr/goatplot?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | Plotting library for Gtk+ 3.x using cairo. 6 | 7 | #### Status 8 | 9 | The basics are working, one can plot arbitrary 2D data with various plotting styles. 10 | 11 | There are a few open points remaining, listed in under [enhancements](https://github.com/drahnr/goatplot/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement). 12 | 13 | *Recommendation: Just see if the demos provide what you are looking for.* 14 | 15 | ![widget screenshot from test-screenshot demo](screenshot.png) 16 | 17 | 18 | ### Build 19 | 20 | To build the library and the demo just do 21 | ```bash 22 | ./waf configure debug -j2 23 | ``` 24 | 25 | For automated building i.e. in order to create a RPM package use 26 | ``` 27 | ./waf configure --prefix=/usr build install --destdir=/tmp --notests --no-glade-dtd-check 28 | ``` 29 | as is used in `./meta/goatplot.spec.in`. 30 | 31 | _Note_: The use of `build` instead of `release`/`debug`, which does not inject any additional `CFLAGS` but only uses those from the environment. 32 | 33 | _Note_: The demo will launch, a screenshot is created, the demo closes, this is expected behaviour and the demo can be launched again manually. 34 | -------------------------------------------------------------------------------- /catalog/goatplot.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /catalog/wscript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | from waflib import Logs 5 | 6 | def options(opt): 7 | opt.load('gnu_dirs') 8 | opt.add_option('--no-glade-dtd-check', action='store_false', dest='glade_dtd_check', default=False, help='enable the glade dtd check') 9 | opt.add_option('--glade-dtd-check', action='store_true', dest='glade_dtd_check', help='disable the glade dtd check') 10 | 11 | def configure(cfg): 12 | cfg.load('gnu_dirs') 13 | if cfg.options.glade_dtd_check: 14 | cfg.find_program('xmllint', var='XMLLINT') 15 | 16 | def build(bld): 17 | bld.load('gnu_dirs') 18 | 19 | if bld.options.glade_dtd_check: 20 | bld(rule="${XMLLINT} --dtdvalid ${DATADIR}/glade/catalogs/glade-catalog.dtd ${SRC} > /dev/null", 21 | source='goatplot.xml', 22 | install_path='${DATADIR}/glade/catalogs') 23 | else: 24 | bld.install_files('${DATADIR}/glade/catalogs/', 'goatplot.xml') 25 | 26 | -------------------------------------------------------------------------------- /meta/goatplot.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: goatplot 7 | Description: Plotting library for gtk+3.x 8 | Version: @VERSION@ 9 | Requires: gtk+-3.0 10 | Libs: -L${libdir} -lgoatplot 11 | Cflags: -I${includedir}/goatplot 12 | -------------------------------------------------------------------------------- /meta/goatplot.spec.in: -------------------------------------------------------------------------------- 1 | Name: goatplot 2 | Version: @VERSION@ 3 | Release: 1%{?dist} 4 | Summary: Gtk+ widget for displaying dynamic data 5 | 6 | Group: System Environment/Libraries 7 | License: GPLv2 8 | URL: https://github.com/drahnr/goatplot 9 | Source0: https://github.com/drahnr/goatplot/archive/v%{version}.tar.gz 10 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 11 | 12 | Requires: gtk3 13 | BuildRequires: gtk-doc 14 | BuildRequires: gtk3-devel 15 | 16 | %description 17 | A Gtk+ widget to display two dimensional data in dynamic manner with 18 | adjustable style and a model-view manner. 19 | 20 | %package devel 21 | Summary: Development files for the osm-gps-map Gtk+ widget 22 | Group: Development/Libraries 23 | Requires: %{name}%{?_isa} = %{version}-%{release} 24 | 25 | %description devel 26 | The development files for the GoatPlot Gtk+ widget 27 | 28 | %prep 29 | %setup -q -n goatplot-%{version} 30 | 31 | %build 32 | ./waf configure --prefix=%{_prefix} --destdir=%{buildroot} build --no-tests --no-glade-dtd-check 33 | 34 | %install 35 | ./waf --prefix=%{_prefix} --destdir=%{buildroot} install --no-tests --no-glade-dtd-check 36 | 37 | %clean 38 | rm -rf %{buildroot} 39 | 40 | %post -p /sbin/ldconfig 41 | %postun -p /sbin/ldconfig 42 | 43 | 44 | %files 45 | %doc LICENSE README.md 46 | %{_libdir}/libgoatplot.so 47 | 48 | %files devel 49 | %{_includedir}/goatplot 50 | %{_libdir}/pkgconfig/goatplot.pc 51 | %{_datadir}/glade/catalogs/goatplot.xml 52 | 53 | %changelog 54 | * Thu Apr 21 2016 Bernhard Schuster - 0.0.2-1 55 | - First version for Fedora 56 | -------------------------------------------------------------------------------- /meta/wscript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | def options(opt): 5 | opt.load('gnu_dirs') 6 | 7 | def configure(cfg): 8 | cfg.load('gnu_dirs') 9 | 10 | def build(bld): 11 | bld.load('gnu_dirs') 12 | 13 | bld(features='subst', source='goatplot.pc.in', target='goatplot.pc', install_path='${LIBDIR}/pkgconfig/', VERSION=bld.env.VERSION) 14 | bld(features='subst', source='goatplot.spec.in', target='goatplot.spec', install_path=None, VERSION=bld.env.VERSION) 15 | 16 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drahnr/goatplot/a946ebff57f4269a4c44ca516ab02dc9e7d59a53/screenshot.png -------------------------------------------------------------------------------- /src/goat-dataset-interface.c: -------------------------------------------------------------------------------- 1 | #include "goat-dataset-interface.h" 2 | 3 | G_DEFINE_INTERFACE (GoatDataset, goat_dataset, G_TYPE_OBJECT) 4 | 5 | 6 | GoatMarkerStyle goat_dataset_get_marker_style (GoatDataset *self) 7 | { 8 | GoatDatasetInterface *iface; 9 | 10 | iface = GOAT_DATASET_GET_IFACE (self); 11 | if (iface->get_marker_style) { 12 | return iface->get_marker_style (self); 13 | } 14 | return GOAT_MARKER_STYLE_INVALID; 15 | } 16 | 17 | 18 | void goat_dataset_get_color (GoatDataset *self, GdkRGBA *rgba) 19 | { 20 | GoatDatasetInterface *iface; 21 | 22 | iface = GOAT_DATASET_GET_IFACE (self); 23 | if (iface->get_color) { 24 | iface->get_color (self, rgba); 25 | } else { 26 | gdk_rgba_parse (rgba, "red"); 27 | } 28 | } 29 | 30 | int goat_dataset_get_length (GoatDataset *self) 31 | { 32 | GoatDatasetInterface *iface; 33 | 34 | iface = GOAT_DATASET_GET_IFACE (self); 35 | if (iface->get_length) { 36 | return iface->get_length (self); 37 | } else { 38 | return 0; 39 | } 40 | } 41 | 42 | 43 | gboolean goat_dataset_get_marker_fill (GoatDataset *self) 44 | { 45 | GoatDatasetInterface *iface; 46 | 47 | iface = GOAT_DATASET_GET_IFACE (self); 48 | if (iface->get_marker_fill) { 49 | return iface->get_marker_fill (self); 50 | } else { 51 | return FALSE; 52 | } 53 | } 54 | 55 | void goat_dataset_get_marker_line_color (GoatDataset *self, GdkRGBA *rgba) 56 | { 57 | GoatDatasetInterface *iface; 58 | 59 | iface = GOAT_DATASET_GET_IFACE (self); 60 | if (iface->get_marker_line_color) { 61 | iface->get_marker_line_color (self, rgba); 62 | } else { 63 | gdk_rgba_parse (rgba, "red"); 64 | } 65 | } 66 | 67 | void goat_dataset_get_marker_fill_color (GoatDataset *self, GdkRGBA *rgba) 68 | { 69 | GoatDatasetInterface *iface; 70 | 71 | iface = GOAT_DATASET_GET_IFACE (self); 72 | if (iface->get_marker_fill_color) { 73 | iface->get_marker_fill_color (self, rgba); 74 | } else { 75 | gdk_rgba_parse (rgba, "red"); 76 | } 77 | } 78 | 79 | void goat_dataset_get_marker_line_width (GoatDataset *self, double *width) 80 | { 81 | GoatDatasetInterface *iface; 82 | 83 | iface = GOAT_DATASET_GET_IFACE (self); 84 | if (iface->get_marker_line_width) { 85 | iface->get_marker_line_width (self, width); 86 | } else { 87 | *width = 1.5; 88 | } 89 | } 90 | 91 | void goat_dataset_get_line_width (GoatDataset *self, double *width) 92 | { 93 | GoatDatasetInterface *iface; 94 | 95 | iface = GOAT_DATASET_GET_IFACE (self); 96 | if (iface->get_line_width) { 97 | iface->get_line_width (self, width); 98 | } else { 99 | *width = 1.5; 100 | } 101 | } 102 | 103 | void goat_dataset_get_marker_size (GoatDataset *self, double *size) 104 | { 105 | GoatDatasetInterface *iface; 106 | 107 | iface = GOAT_DATASET_GET_IFACE (self); 108 | if (iface->get_marker_size) { 109 | iface->get_marker_size (self, size); 110 | } else { 111 | *size = 8.; 112 | } 113 | } 114 | 115 | 116 | gboolean goat_dataset_get_iter_first (GoatDataset *self, GoatDatasetIter *iter) 117 | { 118 | GoatDatasetInterface *iface; 119 | 120 | iface = GOAT_DATASET_GET_IFACE (self); 121 | if (iface->iter_init) { 122 | return iface->iter_init (self, iter); 123 | } else { 124 | g_error ("Implementing the `iter_init` interface for GoatDataset is necessary!"); 125 | } 126 | return FALSE; 127 | } 128 | 129 | gboolean goat_dataset_iter_next (GoatDataset *self, GoatDatasetIter *iter) 130 | { 131 | GoatDatasetInterface *iface; 132 | 133 | iface = GOAT_DATASET_GET_IFACE (self); 134 | if (iface->iter_next) { 135 | return iface->iter_next (self, iter); 136 | } else { 137 | g_error ("Implementing the `iter_next` interface for GoatDataset is necessary!"); 138 | } 139 | return FALSE; 140 | } 141 | 142 | gboolean goat_dataset_get (GoatDataset *self, GoatDatasetIter *iter, gdouble *x, gdouble *y, gdouble *ystddev) 143 | { 144 | GoatDatasetInterface *iface; 145 | 146 | iface = GOAT_DATASET_GET_IFACE (self); 147 | if (iface->get) { 148 | return iface->get (self, iter, x, y, ystddev); 149 | } else { 150 | g_error ("Implementing the `get` interface for GoatDataset is necessary!"); 151 | } 152 | return FALSE; 153 | } 154 | 155 | gboolean goat_dataset_get_extrema (GoatDataset *self, gdouble *xmin, gdouble *xmax, gdouble *ymin, gdouble *ymax) 156 | { 157 | GoatDatasetInterface *iface; 158 | 159 | iface = GOAT_DATASET_GET_IFACE (self); 160 | if (iface->get_extrema) { 161 | return iface->get_extrema (self, xmin, xmax, ymin, ymax); 162 | } else { 163 | g_error ("Implementing the `get` interface for GoatDataset is necessary!"); 164 | } 165 | return FALSE; 166 | } 167 | 168 | gboolean goat_dataset_get_log_extrema (GoatDataset *dataset, gdouble *xmin, gdouble *xmax, gdouble *ymin, gdouble *ymax) 169 | { 170 | GoatDatasetInterface *iface; 171 | 172 | iface = GOAT_DATASET_GET_IFACE (dataset); 173 | if (iface->get_log_extrema) { 174 | return iface->get_log_extrema (dataset, xmin, xmax, ymin, ymax); 175 | } else if (iface->get_extrema) { 176 | return iface->get_extrema (dataset, xmin, xmax, ymin, ymax); 177 | } else { 178 | *xmin = *xmax = *ymin = *ymax = 0.; 179 | } 180 | return FALSE; 181 | } 182 | 183 | gboolean goat_dataset_interpolate (GoatDataset *self) 184 | { 185 | GoatDatasetInterface *iface; 186 | 187 | iface = GOAT_DATASET_GET_IFACE (self); 188 | if (iface->is_interpolation_enabled) { 189 | return iface->is_interpolation_enabled (self); 190 | } else { 191 | g_error ("Implementing the `is_interpolation_enabled` interface for GoatDataset is necessary!"); 192 | } 193 | return FALSE; 194 | } 195 | 196 | gboolean goat_dataset_has_valid_standard_deviation (GoatDataset *self) 197 | { 198 | GoatDatasetInterface *iface; 199 | 200 | iface = GOAT_DATASET_GET_IFACE (self); 201 | if (iface->has_valid_standard_deviation) { 202 | return iface->has_valid_standard_deviation (self); 203 | } else { 204 | g_error ("Implementing the `has_valid_standard_deviation` interface for GoatDataset is necessary!"); 205 | } 206 | return FALSE; 207 | } 208 | 209 | /** 210 | * default implementation for get extrema 211 | * 212 | * @attention this is slow, it is recommended to cache this if possible, see #GoatDatasetSimple 213 | */ 214 | static gboolean get_extrema (GoatDataset *self, gdouble *xmin, gdouble *xmax, gdouble *ymin, gdouble *ymax) 215 | { 216 | GoatDatasetIter iter; 217 | double x, y, ystddev; 218 | double register x_min, y_min; 219 | double register x_max, y_max; 220 | double register y_upper; 221 | double register y_lower; 222 | const gboolean register valid_stddev = goat_dataset_has_valid_standard_deviation (GOAT_DATASET (self)); 223 | 224 | x_min = +G_MAXDOUBLE; 225 | y_min = +G_MAXDOUBLE; 226 | x_max = -G_MAXDOUBLE; 227 | y_max = -G_MAXDOUBLE; 228 | 229 | if (goat_dataset_get_iter_first (GOAT_DATASET (self), &iter)) { 230 | goat_dataset_get (GOAT_DATASET (self), &iter, &x, &y, &ystddev); 231 | x_min = x_max = x; 232 | y_min = y_max = y; 233 | if (valid_stddev) { 234 | y_min -= ystddev; 235 | y_max += ystddev; 236 | } 237 | while (goat_dataset_iter_next (GOAT_DATASET (self), &iter)) { 238 | goat_dataset_get (GOAT_DATASET (self), &iter, &x, &y, &ystddev); 239 | if (x < x_min) { 240 | x_min = x; 241 | } 242 | if (x > x_max) { 243 | x_max = x; 244 | } 245 | y_upper = y_lower = y; 246 | if (valid_stddev) { 247 | g_assert (ystddev >= 0.); 248 | y_upper += ystddev; 249 | y_lower -= ystddev; 250 | } 251 | if (y_lower < y_min) { 252 | y_min = y_lower; 253 | } 254 | if (y_upper > y_max) { 255 | y_max = y_upper; 256 | } 257 | } 258 | } else { 259 | return FALSE; 260 | } 261 | 262 | *xmin = x_min; 263 | *ymin = y_min; 264 | *xmax = x_max; 265 | *ymax = y_max; 266 | return TRUE; 267 | } 268 | 269 | static void goat_dataset_default_init (GoatDatasetInterface *iface) 270 | { 271 | iface->get_marker_style = NULL; 272 | iface->iter_init = NULL; 273 | iface->iter_next = NULL; 274 | iface->get = NULL; 275 | iface->get_extrema = get_extrema; 276 | iface->get_log_extrema = NULL; 277 | iface->get_color = NULL; 278 | iface->get_marker_line_color = NULL; 279 | iface->get_marker_fill_color = NULL; 280 | iface->get_marker_line_width = NULL; 281 | iface->get_line_width = NULL; 282 | iface->get_marker_size = NULL; 283 | iface->get_marker_fill = NULL; 284 | iface->has_valid_standard_deviation = NULL; 285 | iface->is_interpolation_enabled = NULL; 286 | iface->get_length = NULL; 287 | } 288 | -------------------------------------------------------------------------------- /src/goat-dataset-interface.h: -------------------------------------------------------------------------------- 1 | #ifndef GOAT_DATASET_H 2 | #define GOAT_DATASET_H 3 | 4 | #include 5 | #include 6 | 7 | G_BEGIN_DECLS 8 | 9 | 10 | #define GOAT_TYPE_DATASET (goat_dataset_get_type ()) 11 | 12 | G_DECLARE_INTERFACE (GoatDataset, goat_dataset, GOAT, DATASET, GObject) 13 | 14 | typedef struct { 15 | GoatDataset *dataset; 16 | 17 | gpointer state; 18 | gpointer p1; 19 | gpointer p2; 20 | gpointer p3; 21 | } GoatDatasetIter; 22 | 23 | struct _GoatDatasetInterface { 24 | GTypeInterface parent; 25 | 26 | GoatMarkerStyle (*get_marker_style) (GoatDataset *dataset); 27 | gboolean (*iter_init) (GoatDataset *dataset, GoatDatasetIter *iter); 28 | gboolean (*iter_next) (GoatDataset *dataset, GoatDatasetIter *iter); 29 | gboolean (*get) (GoatDataset *dataset, GoatDatasetIter *iter, gdouble *x, gdouble *y, gdouble *ystddev); 30 | gboolean (*get_extrema) (GoatDataset *dataset, gdouble *xmin, gdouble *xmax, gdouble *ymin, gdouble *ymax); 31 | gboolean (*get_log_extrema) (GoatDataset *dataset, gdouble *xmin, gdouble *xmax, gdouble *ymin, gdouble *ymax); 32 | void (*get_color) (GoatDataset *dataset, GdkRGBA *color); 33 | void (*get_marker_line_color) (GoatDataset *dataset, GdkRGBA *color); 34 | gboolean (*get_marker_fill) (GoatDataset *dataset); 35 | void (*get_marker_fill_color) (GoatDataset *self, GdkRGBA *color); 36 | void (*get_marker_line_width) (GoatDataset *self, double *width); 37 | void (*get_line_width) (GoatDataset *self, double *width); 38 | void (*get_marker_size) (GoatDataset *self, double *size); 39 | int (*get_length) (GoatDataset *dataset); 40 | // TODO is this really necessary to be part of the interface and not be filled with some property 41 | gboolean (*has_valid_standard_deviation) (GoatDataset *dataset); 42 | gboolean (*is_interpolation_enabled) (GoatDataset *dataset); 43 | }; 44 | 45 | /** 46 | * @param dataset 47 | * @returns node style of #dataset 48 | */ 49 | GoatMarkerStyle goat_dataset_get_marker_style (GoatDataset *self); 50 | 51 | /** 52 | * @param dataset 53 | * @param xmin [out] 54 | * @param xmax [out] 55 | * @param ymin [out] 56 | * @param ymax [out] 57 | */ 58 | gboolean goat_dataset_get_extrema (GoatDataset *self, gdouble *xmin, gdouble *xmax, gdouble *ymin, gdouble *ymax); 59 | 60 | 61 | /** 62 | * @param dataset 63 | * @param xmin [out] 64 | * @param ymin [out] 65 | */ 66 | gboolean goat_dataset_get_log_extrema (GoatDataset *dataset, gdouble *xmin, gdouble *xmax, gdouble *ymin, 67 | gdouble *ymax); 68 | 69 | /** 70 | * @param dataset 71 | * @param color [out] 72 | */ 73 | void goat_dataset_get_color (GoatDataset *self, GdkRGBA *color); 74 | 75 | 76 | /** 77 | * @param dataset 78 | * @param marker line color [out] 79 | */ 80 | void goat_dataset_get_marker_line_color (GoatDataset *self, GdkRGBA *color); 81 | 82 | /** 83 | * @param dataset 84 | * @param marker fill color [out] 85 | */ 86 | void goat_dataset_get_marker_fill_color (GoatDataset *self, GdkRGBA *color); 87 | 88 | /** 89 | * @param dataset 90 | * @param marker line width [out] 91 | */ 92 | void goat_dataset_get_marker_line_width (GoatDataset *self, double *width); 93 | 94 | /** 95 | * @param dataset 96 | * @param line width [out] 97 | */ 98 | void goat_dataset_get_line_width (GoatDataset *self, double *width); 99 | 100 | /** 101 | * @param dataset 102 | * @param marker size (diameter) [out] 103 | */ 104 | void goat_dataset_get_marker_size (GoatDataset *self, double *size); 105 | 106 | gboolean goat_dataset_get_marker_fill (GoatDataset *self); 107 | 108 | gboolean goat_dataset_get_iter_first (GoatDataset *self, GoatDatasetIter *iter); 109 | 110 | gboolean goat_dataset_iter_next (GoatDataset *self, GoatDatasetIter *iter); 111 | 112 | gboolean goat_dataset_get (GoatDataset *self, GoatDatasetIter *iter, gdouble *x, gdouble *y, gdouble *ystddev); 113 | 114 | gboolean goat_dataset_interpolate (GoatDataset *self); 115 | 116 | gboolean goat_dataset_has_valid_standard_deviation (GoatDataset *self); 117 | 118 | int goat_dataset_get_length (GoatDataset *self); 119 | 120 | G_END_DECLS 121 | 122 | #endif /* GOAT_DATASET_H */ 123 | -------------------------------------------------------------------------------- /src/goat-dataset-simple.c: -------------------------------------------------------------------------------- 1 | /* 2 | * goat-dataset.c 3 | * This file is part of GoatPlot 4 | * 5 | * Copyright (C) 2014,2016 - Bernhard Schuster 6 | * 7 | * GoatPlot is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * GoatPlot is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with GoatPlot. If not, see . 19 | */ 20 | 21 | #include "goat-dataset-simple.h" 22 | #include 23 | #include 24 | #include 25 | 26 | static void update_extrema_cache (GoatDatasetSimple *dataset); 27 | 28 | struct _GoatDatasetSimplePrivate { 29 | GList *list; 30 | gint count; 31 | double x_min; 32 | double y_min; 33 | double x_max; 34 | double y_max; 35 | double x_log_min; 36 | double y_log_min; 37 | GdkRGBA color; 38 | GdkRGBA marker_line_color; 39 | GdkRGBA marker_fill_color; 40 | double line_width; 41 | double marker_line_width; 42 | double marker_size; 43 | gboolean marker_fill; 44 | 45 | GoatMarkerStyle style; 46 | 47 | gboolean interpolation_enabled; 48 | gboolean valid_stddev; 49 | }; 50 | 51 | static void goat_dataset_simple_interface_init (GoatDatasetInterface *iface); 52 | 53 | G_DEFINE_TYPE_WITH_CODE (GoatDatasetSimple, goat_dataset_simple, G_TYPE_OBJECT, 54 | G_IMPLEMENT_INTERFACE (GOAT_TYPE_DATASET, goat_dataset_simple_interface_init) 55 | G_ADD_PRIVATE (GoatDatasetSimple)); 56 | 57 | static void goat_dataset_simple_finalize (GObject *object) 58 | { 59 | G_OBJECT_CLASS (goat_dataset_simple_parent_class)->finalize (object); 60 | } 61 | 62 | enum { 63 | PROP_0, 64 | 65 | PROP_LIST, 66 | PROP_COUNT, 67 | PROP_VALID_STDDEV, 68 | PROP_INTERPOLATION_ENABLED, 69 | 70 | N_PROPERTIES 71 | }; 72 | 73 | static GParamSpec *obj_properties[N_PROPERTIES] = { 74 | NULL, 75 | }; 76 | 77 | static void goat_dataset_simple_set_gproperty (GObject *object, guint prop_id, const GValue *value, GParamSpec *spec) 78 | { 79 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (object); 80 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 81 | 82 | switch (prop_id) { 83 | case PROP_INTERPOLATION_ENABLED: 84 | priv->interpolation_enabled = g_value_get_boolean (value); 85 | break; 86 | case PROP_VALID_STDDEV: 87 | priv->valid_stddev = g_value_get_boolean (value); 88 | break; 89 | case PROP_COUNT: 90 | priv->count = g_value_get_int (value); 91 | break; 92 | case PROP_LIST: 93 | g_list_free_full (priv->list, g_free); 94 | priv->list = g_value_get_pointer (value); 95 | update_extrema_cache (self); 96 | break; 97 | default: 98 | G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, spec); 99 | } 100 | } 101 | 102 | static void goat_dataset_simple_get_gproperty (GObject *object, guint prop_id, GValue *value, GParamSpec *spec) 103 | { 104 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (object); 105 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 106 | 107 | switch (prop_id) { 108 | case PROP_INTERPOLATION_ENABLED: 109 | g_value_set_boolean (value, priv->interpolation_enabled); 110 | break; 111 | case PROP_VALID_STDDEV: 112 | g_value_set_boolean (value, priv->valid_stddev); 113 | break; 114 | case PROP_COUNT: 115 | g_value_set_int (value, priv->count); 116 | break; 117 | case PROP_LIST: 118 | g_value_set_pointer (value, priv->list); 119 | break; 120 | default: 121 | G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, spec); 122 | } 123 | } 124 | 125 | static void goat_dataset_simple_class_init (GoatDatasetSimpleClass *klass) 126 | { 127 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 128 | 129 | object_class->finalize = goat_dataset_simple_finalize; 130 | 131 | object_class->set_property = goat_dataset_simple_set_gproperty; 132 | object_class->get_property = goat_dataset_simple_get_gproperty; 133 | 134 | obj_properties[PROP_LIST] = g_param_spec_pointer ("list", "GoatDataset::list", "the store data", 135 | G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); 136 | 137 | obj_properties[PROP_COUNT] = 138 | g_param_spec_int ("count", "GoatDataset::count", "count of datapoints", -1, 10000, -1, G_PARAM_READABLE); 139 | 140 | obj_properties[PROP_VALID_STDDEV] = 141 | g_param_spec_boolean ("valid_stddev", "GoatDataset::valid_stddev", 142 | "is the stdandard deviation associated to this datatset valid", FALSE, G_PARAM_READWRITE); 143 | 144 | obj_properties[PROP_INTERPOLATION_ENABLED] = 145 | g_param_spec_boolean ("interpolation_enabled", "GoatDataset::interpolation_enabled", 146 | "shall the points be interpolated", FALSE, G_PARAM_READWRITE); 147 | 148 | g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); 149 | } 150 | 151 | static void goat_dataset_simple_init (GoatDatasetSimple *self) 152 | { 153 | GoatDatasetSimplePrivate *priv = self->priv = goat_dataset_simple_get_instance_private (self); 154 | priv->list = NULL; 155 | priv->count = -1; 156 | priv->x_min = +G_MAXDOUBLE; 157 | priv->y_min = +G_MAXDOUBLE; 158 | priv->x_log_min = +G_MAXDOUBLE; 159 | priv->y_log_min = +G_MAXDOUBLE; 160 | priv->x_max = -G_MAXDOUBLE; 161 | priv->y_max = -G_MAXDOUBLE; 162 | gdk_rgba_parse (&priv->color, "blue"); 163 | gdk_rgba_parse (&priv->marker_line_color, "blue"); 164 | gdk_rgba_parse (&priv->marker_fill_color, "blue"); 165 | priv->line_width = 1.5; 166 | priv->marker_line_width = 1.5; 167 | priv->marker_size = 8.0; 168 | priv->marker_fill = 0; 169 | priv->style = GOAT_MARKER_STYLE_SQUARE; 170 | } 171 | 172 | /** 173 | * create a new dataset from a GList containing GoatTriple values 174 | */ 175 | GoatDatasetSimple *goat_dataset_simple_new (GList *list, gboolean valid_stddev, gboolean interpolate) 176 | { 177 | return g_object_new (GOAT_TYPE_DATASET_SIMPLE, "valid_stddev", valid_stddev, "interpolation_enabled", interpolate, 178 | "list", list, NULL); 179 | } 180 | 181 | 182 | /** 183 | * @param dataset 184 | * @param oxq node style to use for drawing of #dataset 185 | */ 186 | void goat_dataset_simple_set_style (GoatDatasetSimple *self, GoatMarkerStyle oxq) 187 | { 188 | 189 | g_return_if_fail (self); 190 | g_return_if_fail (GOAT_IS_DATASET_SIMPLE (self)); 191 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 192 | priv->style = oxq; 193 | } 194 | 195 | 196 | 197 | 198 | static void update_extrema_cache (GoatDatasetSimple *self) 199 | { 200 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 201 | 202 | GoatDatasetIter iter; 203 | double x, y, ystddev; 204 | double register x_min, y_min, x_log_min, y_log_min; 205 | double register x_max, y_max; 206 | double register y_upper; 207 | double register y_lower; 208 | const gboolean register valid_stddev = goat_dataset_has_valid_standard_deviation (GOAT_DATASET (self)); 209 | 210 | x_log_min = x_min = +G_MAXDOUBLE; 211 | y_log_min = y_min = +G_MAXDOUBLE; 212 | x_max = -G_MAXDOUBLE; 213 | y_max = -G_MAXDOUBLE; 214 | 215 | if (goat_dataset_get_iter_first (GOAT_DATASET (self), &iter)) { 216 | goat_dataset_get (GOAT_DATASET (self), &iter, &x, &y, &ystddev); 217 | x_min = x_max = x; 218 | y_min = y_max = y; 219 | 220 | if (x > 0.) 221 | x_log_min = x; 222 | if (y > 0.) 223 | y_log_min = y; 224 | 225 | if (valid_stddev) { 226 | y_min -= ystddev; 227 | y_max += ystddev; 228 | } 229 | while (goat_dataset_iter_next (GOAT_DATASET (self), &iter)) { 230 | goat_dataset_get (GOAT_DATASET (self), &iter, &x, &y, &ystddev); 231 | if (x < x_min) { 232 | x_min = x; 233 | } 234 | if (x > x_max) { 235 | x_max = x; 236 | } 237 | if (x < x_log_min && x > 0.) { 238 | x_log_min = x; 239 | } 240 | y_upper = y_lower = y; 241 | if (valid_stddev) { 242 | g_assert (ystddev >= 0.); 243 | y_upper += ystddev; 244 | y_lower -= ystddev; 245 | } 246 | if (y_lower < y_min) { 247 | y_min = y_lower; 248 | } 249 | if (y_upper > y_max) { 250 | y_max = y_upper; 251 | } 252 | if (y < y_log_min && y > 0.) { 253 | y_log_min = y; 254 | } 255 | } 256 | } 257 | 258 | priv->x_min = x_min; 259 | priv->y_min = y_min; 260 | priv->x_max = x_max; 261 | priv->y_max = y_max; 262 | priv->x_log_min = x_log_min; 263 | priv->y_log_min = y_log_min; 264 | } 265 | 266 | void goat_dataset_simple_append (GoatDatasetSimple *self, gdouble x, gdouble y, gdouble ystddev) 267 | { 268 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 269 | 270 | GoatTriple *pair = g_new0 (GoatTriple, 1); 271 | pair->x = x; 272 | pair->y = y; 273 | if (ystddev < 0.) { 274 | g_warning ("goat_dataset_simple_append: ystddev has to be positive"); 275 | ystddev = fabs (ystddev); 276 | } 277 | pair->ystddev = ystddev; 278 | if (priv->count < 0) { 279 | priv->count = g_list_length (priv->list); 280 | } 281 | priv->count++; 282 | if (priv->x_min > x) 283 | priv->x_min = x; 284 | if (priv->x_max < x) 285 | priv->x_max = x; 286 | if (priv->x_log_min > x && x > 0.) 287 | priv->x_log_min = x; 288 | 289 | const gboolean register valid_stddev = goat_dataset_has_valid_standard_deviation (GOAT_DATASET (self)); 290 | double register y_lower = y; 291 | double register y_upper = y; 292 | if (valid_stddev) { 293 | g_assert (ystddev >= 0.); 294 | y_upper += ystddev; 295 | y_lower -= ystddev; 296 | } 297 | if (priv->y_min > y_lower) 298 | priv->y_min = y_lower; 299 | if (priv->y_max < y_upper) 300 | priv->y_max = y_upper; 301 | if (priv->y_log_min > y_lower && y_lower > 0.) 302 | priv->y_log_min = y_lower; 303 | 304 | /* g_printf ("calc x range: %lf..%lf\n", priv->x_min, priv->x_max); */ 305 | /* g_printf ("calc y range: %lf..%lf\n", priv->y_min, priv->y_max); */ 306 | priv->list = g_list_append (priv->list, pair); 307 | } 308 | 309 | void goat_dataset_simple_clear (GoatDatasetSimple *self) 310 | { 311 | g_object_set (self, "list", NULL, NULL); 312 | } 313 | 314 | void goat_dataset_simple_set_color (GoatDatasetSimple *self, GdkRGBA *color) 315 | { 316 | g_return_if_fail (self); 317 | g_return_if_fail (color); 318 | 319 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 320 | 321 | priv->color = *color; 322 | } 323 | 324 | void goat_dataset_simple_set_marker_line_color (GoatDatasetSimple *self, GdkRGBA *color) 325 | { 326 | g_return_if_fail (self); 327 | g_return_if_fail (color); 328 | 329 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 330 | 331 | priv->marker_line_color = *color; 332 | } 333 | 334 | void goat_dataset_simple_set_marker_fill_color (GoatDatasetSimple *self, GdkRGBA *color) 335 | { 336 | g_return_if_fail (self); 337 | g_return_if_fail (color); 338 | 339 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 340 | 341 | priv->marker_fill_color = *color; 342 | priv->marker_fill = TRUE; 343 | } 344 | 345 | void goat_dataset_simple_set_marker_line_width (GoatDatasetSimple *self, double width) 346 | { 347 | g_return_if_fail (self); 348 | 349 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 350 | 351 | priv->marker_line_width = width; 352 | } 353 | 354 | void goat_dataset_simple_set_line_width (GoatDatasetSimple *self, double width) 355 | { 356 | g_return_if_fail (self); 357 | 358 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 359 | 360 | priv->line_width = width; 361 | } 362 | 363 | void goat_dataset_simple_set_marker_size (GoatDatasetSimple *self, double size) 364 | { 365 | g_return_if_fail (self); 366 | 367 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 368 | 369 | priv->marker_size = size; 370 | } 371 | 372 | void goat_dataset_simple_set_marker_fill (GoatDatasetSimple *self, gboolean marker_fill) 373 | { 374 | g_return_if_fail (self); 375 | 376 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 377 | 378 | priv->marker_fill = marker_fill; 379 | } 380 | 381 | static void get_color (GoatDataset *dataset, GdkRGBA *color) 382 | { 383 | g_return_if_fail (dataset); 384 | g_return_if_fail (color); 385 | 386 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 387 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 388 | 389 | *color = priv->color; 390 | } 391 | 392 | static void get_marker_line_color (GoatDataset *dataset, GdkRGBA *color) 393 | { 394 | g_return_if_fail (dataset); 395 | g_return_if_fail (color); 396 | 397 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 398 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 399 | 400 | *color = priv->marker_line_color; 401 | } 402 | 403 | static void get_marker_fill_color (GoatDataset *dataset, GdkRGBA *color) 404 | { 405 | g_return_if_fail (dataset); 406 | g_return_if_fail (color); 407 | 408 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 409 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 410 | 411 | *color = priv->marker_fill_color; 412 | } 413 | 414 | static void get_marker_line_width (GoatDataset *dataset, double *width) 415 | { 416 | g_return_if_fail (dataset); 417 | g_return_if_fail (width); 418 | 419 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 420 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 421 | 422 | *width = priv->marker_line_width; 423 | } 424 | 425 | static void get_line_width (GoatDataset *dataset, double *width) 426 | { 427 | g_return_if_fail (dataset); 428 | g_return_if_fail (width); 429 | 430 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 431 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 432 | 433 | *width = priv->line_width; 434 | } 435 | 436 | static void get_marker_size (GoatDataset *dataset, double *size) 437 | { 438 | g_return_if_fail (dataset); 439 | g_return_if_fail (size); 440 | 441 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 442 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 443 | 444 | *size = priv->marker_size; 445 | } 446 | 447 | static gboolean get_marker_fill (GoatDataset *dataset) 448 | { 449 | if (!dataset) 450 | return FALSE; 451 | 452 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 453 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 454 | 455 | return priv->marker_fill; 456 | } 457 | 458 | static gboolean get_extrema (GoatDataset *dataset, gdouble *xmin, gdouble *xmax, gdouble *ymin, gdouble *ymax) 459 | { 460 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 461 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 462 | 463 | if (priv->list != NULL) { 464 | if (xmin) 465 | *xmin = priv->x_min; 466 | if (xmax) 467 | *xmax = priv->x_max; 468 | if (ymin) 469 | *ymin = priv->y_min; 470 | if (ymax) 471 | *ymax = priv->y_max; 472 | return TRUE; 473 | } 474 | priv->list = NULL; 475 | priv->count = -1; 476 | priv->x_min = +G_MAXDOUBLE; 477 | priv->y_min = +G_MAXDOUBLE; 478 | priv->x_max = -G_MAXDOUBLE; 479 | priv->y_max = -G_MAXDOUBLE; 480 | return FALSE; 481 | } 482 | 483 | static gboolean get_log_extrema (GoatDataset *dataset, gdouble *xmin, gdouble *xmax, gdouble *ymin, gdouble *ymax) 484 | { 485 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 486 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 487 | 488 | if (priv->list != NULL) { 489 | if (xmin) 490 | *xmin = priv->x_log_min; 491 | if (xmax) 492 | *xmax = priv->x_max; 493 | if (ymin) 494 | *ymin = priv->y_log_min; 495 | if (ymax) 496 | *ymax = priv->y_max; 497 | return TRUE; 498 | } 499 | 500 | priv->list = NULL; 501 | priv->count = -1; 502 | priv->x_log_min = +G_MAXDOUBLE; 503 | priv->y_log_min = +G_MAXDOUBLE; 504 | priv->x_max = -G_MAXDOUBLE; 505 | priv->y_max = -G_MAXDOUBLE; 506 | return FALSE; 507 | } 508 | 509 | 510 | /** 511 | * initilialize the iterator #iter for #dataset 512 | * @param iter 513 | * @param dataset 514 | */ 515 | gboolean iter_init (GoatDataset *dataset, GoatDatasetIter *iter) 516 | { 517 | g_return_val_if_fail (dataset, FALSE); 518 | g_return_val_if_fail (GOAT_IS_DATASET_SIMPLE (dataset), FALSE); 519 | g_return_val_if_fail (iter, FALSE); 520 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 521 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 522 | iter->dataset = dataset; 523 | iter->state = priv->list; 524 | return (iter->state != NULL); 525 | } 526 | 527 | static gboolean iter_next (GoatDataset *dataset, GoatDatasetIter *iter) 528 | { 529 | g_return_val_if_fail (iter, FALSE); 530 | g_return_val_if_fail (iter->state, FALSE); 531 | g_return_val_if_fail (dataset == iter->dataset, FALSE); 532 | iter->state = ((GList *)(iter->state))->next; 533 | return (iter->state != NULL); 534 | } 535 | 536 | static gboolean get (GoatDataset *dataset, GoatDatasetIter *iter, gdouble *x, gdouble *y, gdouble *ystddev) 537 | { 538 | g_return_val_if_fail (iter, FALSE); 539 | g_return_val_if_fail (iter->state, FALSE); 540 | g_return_val_if_fail (dataset == iter->dataset, FALSE); 541 | GoatTriple *pair = ((GList *)(iter->state))->data; 542 | if (G_LIKELY (pair)) { 543 | if (G_LIKELY (x)) 544 | *((double *)x) = pair->x; 545 | if (G_LIKELY (y)) 546 | *((double *)y) = pair->y; 547 | if (G_LIKELY (ystddev)) 548 | *((double *)ystddev) = pair->ystddev; 549 | return TRUE; 550 | } 551 | return FALSE; 552 | } 553 | 554 | 555 | static GoatMarkerStyle get_marker_style (GoatDataset *dataset) 556 | { 557 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 558 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 559 | return priv->style; 560 | } 561 | 562 | static gboolean has_valid_standard_deviation (GoatDataset *dataset) 563 | { 564 | g_return_val_if_fail (GOAT_IS_DATASET_SIMPLE (dataset), FALSE); 565 | return GOAT_DATASET_SIMPLE (dataset)->priv->valid_stddev; 566 | } 567 | 568 | static gboolean is_interpolation_enabled (GoatDataset *dataset) 569 | { 570 | g_return_val_if_fail (GOAT_IS_DATASET_SIMPLE (dataset), FALSE); 571 | return GOAT_DATASET_SIMPLE (dataset)->priv->interpolation_enabled; 572 | } 573 | 574 | /** 575 | * return the number of items contained in the dataset 576 | * @param dataset 577 | */ 578 | gint get_length (GoatDataset *dataset) 579 | { 580 | g_return_val_if_fail (GOAT_IS_DATASET_SIMPLE (dataset), FALSE); 581 | GoatDatasetSimple *self = GOAT_DATASET_SIMPLE (dataset); 582 | GoatDatasetSimplePrivate *priv = goat_dataset_simple_get_instance_private (self); 583 | if (priv->count >= 0) 584 | return priv->count; 585 | return priv->count = (gint)g_list_length (priv->list); 586 | } 587 | 588 | 589 | static void goat_dataset_simple_interface_init (GoatDatasetInterface *iface) 590 | { 591 | iface->get_color = get_color; 592 | iface->get_marker_line_color = get_marker_line_color; 593 | iface->get_marker_fill_color = get_marker_fill_color; 594 | iface->get_marker_line_width = get_marker_line_width; 595 | iface->get_line_width = get_line_width; 596 | iface->get_marker_size = get_marker_size; 597 | iface->get_extrema = get_extrema; 598 | iface->get_log_extrema = get_log_extrema; 599 | iface->is_interpolation_enabled = is_interpolation_enabled; 600 | iface->has_valid_standard_deviation = has_valid_standard_deviation; 601 | iface->iter_init = iter_init; 602 | iface->iter_next = iter_next; 603 | iface->get_marker_style = get_marker_style; 604 | iface->get = get; 605 | iface->get_marker_fill = get_marker_fill; 606 | iface->get_length = get_length; 607 | } 608 | -------------------------------------------------------------------------------- /src/goat-dataset-simple.h: -------------------------------------------------------------------------------- 1 | /* 2 | * goat-dataset.h 3 | * This file is part of GoatPlot 4 | * 5 | * Copyright (C) 2014 - Bernhard Schuster 6 | * 7 | * GoatPlot is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * GoatPlot is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with GoatPlot. If not, see . 19 | */ 20 | 21 | #ifndef GOAT_DATASET_SIMPLE_H 22 | #define GOAT_DATASET_SIMPLE_H 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | G_BEGIN_DECLS 30 | 31 | #define GOAT_TYPE_DATASET_SIMPLE (goat_dataset_simple_get_type ()) 32 | #define GOAT_DATASET_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE_DATASET_SIMPLE, GoatDatasetSimple)) 33 | #define GOAT_DATASET_SIMPLE_CONST(obj) \ 34 | (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE_DATASET_SIMPLE, GoatDatasetSimple const)) 35 | #define GOAT_DATASET_SIMPLE_CLASS(klass) \ 36 | (G_TYPE_CHECK_CLASS_CAST ((klass), GOAT_TYPE_DATASET_SIMPLE, GoatDatasetSimpleClass)) 37 | #define GOAT_IS_DATASET_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOAT_TYPE_DATASET_SIMPLE)) 38 | #define GOAT_IS_DATASET_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOAT_TYPE_DATASET_SIMPLE)) 39 | #define GOAT_DATASET_SIMPLE_GET_CLASS(obj) \ 40 | (G_TYPE_INSTANCE_GET_CLASS ((obj), GOAT_TYPE_DATASET_SIMPLE, GoatDatasetSimpleClass)) 41 | 42 | typedef struct _GoatDatasetSimple GoatDatasetSimple; 43 | typedef struct _GoatDatasetSimpleClass GoatDatasetSimpleClass; 44 | typedef struct _GoatDatasetSimplePrivate GoatDatasetSimplePrivate; 45 | 46 | struct _GoatDatasetSimple { 47 | GObject parent; 48 | GoatDatasetSimplePrivate *priv; 49 | }; 50 | 51 | 52 | 53 | struct _GoatDatasetSimpleClass { 54 | GObjectClass parent_class; 55 | }; 56 | 57 | 58 | typedef struct _GoatTriple GoatTriple; 59 | 60 | struct _GoatTriple { 61 | gdouble x, y, ystddev; 62 | }; 63 | 64 | GType goat_dataset_simple_get_type (void) G_GNUC_CONST; 65 | 66 | // TODO use enums instead of boolean 67 | GoatDatasetSimple *goat_dataset_simple_new (GList *list, gboolean valid_stddev, gboolean interpolate); 68 | 69 | gint goat_dataset_simple_get_length (GoatDatasetSimple *dataset); 70 | 71 | void goat_dataset_simple_set_style (GoatDatasetSimple *dataset, GoatMarkerStyle oxq); 72 | 73 | void goat_dataset_simple_append (GoatDatasetSimple *dataset, gdouble x, gdouble y, gdouble ystddev); 74 | 75 | void goat_dataset_simple_set_color (GoatDatasetSimple *dataset, GdkRGBA *color); 76 | 77 | void goat_dataset_simple_set_marker_line_color (GoatDatasetSimple *self, GdkRGBA *color); 78 | 79 | void goat_dataset_simple_set_marker_fill_color (GoatDatasetSimple *self, GdkRGBA *color); 80 | 81 | void goat_dataset_simple_set_marker_line_width (GoatDatasetSimple *self, double width); 82 | 83 | void goat_dataset_simple_set_line_width (GoatDatasetSimple *self, double width); 84 | 85 | void goat_dataset_simple_set_marker_size (GoatDatasetSimple *self, double size); 86 | 87 | void goat_dataset_simple_set_marker_fill (GoatDatasetSimple *self, gboolean marker_fill); 88 | 89 | 90 | G_END_DECLS 91 | 92 | #endif /* GOAT_DATASET_SIMPLE_H */ 93 | -------------------------------------------------------------------------------- /src/goat-dataset-store.c: -------------------------------------------------------------------------------- 1 | #include "goat-dataset-store.h" 2 | 3 | typedef struct { 4 | GtkTreeModel *tree_model; 5 | gint x_index; 6 | gint y_index; 7 | gint ystddev_index; 8 | gchar *color; // TODO move to GdkRGBA, but need to figure out GValue handling 9 | GoatMarkerStyle marker_style; 10 | } GoatDatasetStorePrivate; 11 | 12 | static void goat_dataset_store_interface_init (GoatDatasetInterface *iface); 13 | 14 | G_DEFINE_TYPE_WITH_CODE (GoatDatasetStore, goat_dataset_store, G_TYPE_OBJECT, 15 | G_IMPLEMENT_INTERFACE (GOAT_TYPE_DATASET, goat_dataset_store_interface_init) 16 | G_ADD_PRIVATE (GoatDatasetStore)) 17 | 18 | enum { 19 | PROP_0, 20 | 21 | PROP_TREE_MODEL, 22 | PROP_X_INDEX, 23 | PROP_Y_INDEX, 24 | PROP_YSTDDEV_INDEX, 25 | PROP_MARKER_STYLE, 26 | PROP_COLOR, 27 | 28 | N_PROPS 29 | }; 30 | 31 | static GParamSpec *obj_properties[N_PROPS]; 32 | 33 | GoatDatasetStore *goat_dataset_store_new (GtkTreeModel *tree_model) 34 | { 35 | return g_object_new (GOAT_TYPE_DATASET_STORE, "tree-model", tree_model, NULL); 36 | } 37 | 38 | 39 | static void goat_dataset_store_finalize (GObject *object) 40 | { 41 | /* GoatDatasetStore *self = (GoatDatasetStore *)object; */ 42 | /* GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); */ 43 | 44 | G_OBJECT_CLASS (goat_dataset_store_parent_class)->finalize (object); 45 | } 46 | 47 | static void goat_dataset_store_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 48 | { 49 | GoatDatasetStore *self = GOAT_DATASET_STORE (object); 50 | GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); 51 | 52 | switch (prop_id) { 53 | case PROP_TREE_MODEL: 54 | g_value_set_pointer (value, priv->tree_model); 55 | break; 56 | case PROP_X_INDEX: 57 | g_value_set_int (value, priv->x_index); 58 | break; 59 | case PROP_Y_INDEX: 60 | g_value_set_int (value, priv->y_index); 61 | break; 62 | case PROP_YSTDDEV_INDEX: 63 | g_value_set_int (value, priv->ystddev_index); 64 | break; 65 | case PROP_COLOR: 66 | g_value_take_string (value, priv->color); 67 | break; 68 | case PROP_MARKER_STYLE: 69 | g_value_set_enum (value, priv->marker_style); 70 | break; 71 | default: 72 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 73 | } 74 | } 75 | 76 | static void goat_dataset_store_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 77 | { 78 | GoatDatasetStore *self = GOAT_DATASET_STORE (object); 79 | GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); 80 | 81 | switch (prop_id) { 82 | case PROP_TREE_MODEL: 83 | priv->tree_model = g_value_get_pointer (value); 84 | break; 85 | case PROP_X_INDEX: 86 | priv->x_index = g_value_get_int (value); 87 | break; 88 | case PROP_Y_INDEX: 89 | priv->y_index = g_value_get_int (value); 90 | break; 91 | case PROP_YSTDDEV_INDEX: 92 | priv->ystddev_index = g_value_get_int (value); 93 | break; 94 | case PROP_COLOR: 95 | priv->color = g_value_dup_string (value); // TODO verify dup is needed 96 | break; 97 | case PROP_MARKER_STYLE: 98 | priv->marker_style = g_value_get_enum (value); 99 | break; 100 | default: 101 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 102 | } 103 | } 104 | 105 | static void goat_dataset_store_class_init (GoatDatasetStoreClass *klass) 106 | { 107 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 108 | 109 | object_class->finalize = goat_dataset_store_finalize; 110 | object_class->get_property = goat_dataset_store_get_property; 111 | object_class->set_property = goat_dataset_store_set_property; 112 | 113 | obj_properties[PROP_TREE_MODEL] = 114 | g_param_spec_object ("tree-model", "GoatDatasetStore::tree-model", "the backend tree store", 115 | GTK_TYPE_TREE_MODEL, G_PARAM_READWRITE | G_PARAM_STATIC_BLURB); 116 | 117 | obj_properties[PROP_X_INDEX] = g_param_spec_int ("x-index", "GoatDatasetStore::x-index", "mapping", -1, G_MAXINT, 118 | -1, G_PARAM_READWRITE | G_PARAM_STATIC_BLURB); 119 | 120 | obj_properties[PROP_Y_INDEX] = g_param_spec_int ("y-index", "GoatDatasetStore::y-index", "mapping", -1, G_MAXINT, 121 | -1, G_PARAM_READWRITE | G_PARAM_STATIC_BLURB); 122 | 123 | obj_properties[PROP_YSTDDEV_INDEX] = 124 | g_param_spec_int ("ystddev-index", "GoatDatasetStore::ystddev-index", "mapping", -1, G_MAXINT, -1, 125 | G_PARAM_READWRITE | G_PARAM_STATIC_BLURB); 126 | 127 | g_object_class_install_properties (object_class, N_PROPS, obj_properties); 128 | } 129 | 130 | static void goat_dataset_store_init (GoatDatasetStore *self) 131 | { 132 | GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); 133 | priv->tree_model = NULL; 134 | priv->x_index = -1; // minus one so gtk_tree_model_get stops parsing 135 | priv->y_index = -1; 136 | priv->ystddev_index = -1; 137 | priv->color = g_strdup ("mediumseagreen"); 138 | priv->marker_style = GOAT_MARKER_STYLE_POINT; 139 | } 140 | 141 | static gboolean iter_init (GoatDataset *dataset, GoatDatasetIter *iter) 142 | { 143 | g_return_val_if_fail (dataset, FALSE); 144 | g_return_val_if_fail (GOAT_IS_DATASET_STORE (dataset), FALSE); 145 | g_return_val_if_fail (iter, FALSE); 146 | GoatDatasetStore *self = GOAT_DATASET_STORE (dataset); 147 | GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); 148 | return gtk_tree_model_get_iter_first (priv->tree_model, (GtkTreeIter *)(iter->state)); 149 | } 150 | 151 | static gboolean iter_next (GoatDataset *dataset, GoatDatasetIter *iter) 152 | { 153 | g_return_val_if_fail (dataset, FALSE); 154 | g_return_val_if_fail (GOAT_IS_DATASET_STORE (dataset), FALSE); 155 | g_return_val_if_fail (iter, FALSE); 156 | GoatDatasetStore *self = GOAT_DATASET_STORE (dataset); 157 | GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); 158 | return gtk_tree_model_iter_next (priv->tree_model, (GtkTreeIter *)(iter->state)); 159 | } 160 | 161 | static gboolean get (GoatDataset *dataset, GoatDatasetIter *iter, gdouble *x, gdouble *y, gdouble *ystddev) 162 | { 163 | g_return_val_if_fail (dataset, FALSE); 164 | g_return_val_if_fail (GOAT_IS_DATASET_STORE (dataset), FALSE); 165 | g_return_val_if_fail (iter, FALSE); 166 | GoatDatasetStore *self = GOAT_DATASET_STORE (dataset); 167 | GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); 168 | 169 | gtk_tree_model_get (priv->tree_model, (GtkTreeIter *)(iter->state), priv->x_index, x, priv->y_index, y, 170 | priv->ystddev_index, ystddev, -1); 171 | return TRUE; 172 | } 173 | 174 | static GoatMarkerStyle get_marker_style (GoatDataset *dataset) 175 | { 176 | g_return_val_if_fail (dataset, FALSE); 177 | g_return_val_if_fail (GOAT_IS_DATASET_STORE (dataset), FALSE); 178 | GoatDatasetStore *self = GOAT_DATASET_STORE (dataset); 179 | GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); 180 | return priv->marker_style; 181 | } 182 | 183 | static void get_color (GoatDataset *dataset, GdkRGBA *color) 184 | { 185 | g_return_if_fail (color); 186 | g_return_if_fail (dataset); 187 | g_return_if_fail (GOAT_IS_DATASET_STORE (dataset)); 188 | GoatDatasetStore *self = GOAT_DATASET_STORE (dataset); 189 | GoatDatasetStorePrivate *priv = goat_dataset_store_get_instance_private (self); 190 | gdk_rgba_parse (color, priv->color); // FIXME see GoatDatasetStorePrivate 191 | } 192 | 193 | static void goat_dataset_store_interface_init (GoatDatasetInterface *iface) 194 | { 195 | iface->iter_init = iter_init; 196 | iface->iter_next = iter_next; 197 | iface->get = get; 198 | // use the default implementation for iface->get_extrema 199 | iface->get_marker_style = get_marker_style; 200 | iface->get_color = get_color; 201 | } 202 | -------------------------------------------------------------------------------- /src/goat-dataset-store.h: -------------------------------------------------------------------------------- 1 | #ifndef GOAT_DATASET_STORE_H 2 | #define GOAT_DATASET_STORE_H 3 | 4 | #include 5 | #include 6 | 7 | G_BEGIN_DECLS 8 | 9 | #define GOAT_TYPE_DATASET_STORE (goat_dataset_store_get_type ()) 10 | 11 | G_DECLARE_DERIVABLE_TYPE (GoatDatasetStore, goat_dataset_store, GOAT, DATASET_STORE, GObject) 12 | 13 | struct _GoatDatasetStoreClass { 14 | GObjectClass parent; 15 | }; 16 | 17 | GoatDatasetStore *goat_dataset_store_new (GtkTreeModel *tree_model); 18 | 19 | G_END_DECLS 20 | 21 | #endif /* GOAT_DATASET_STORE_H */ 22 | -------------------------------------------------------------------------------- /src/goat-plot-enum.c: -------------------------------------------------------------------------------- 1 | #include "goat-plot-enum.h" 2 | 3 | GType goat_orientation_get_type (void) 4 | { 5 | static GType t = 0; 6 | 7 | if (!t) { 8 | static GEnumValue ts[] = { 9 | {GOAT_ORIENTATION_INVALID, "INVALID", "invalid"}, 10 | {GOAT_ORIENTATION_VERTICAL, "VERTICAL", "vertical"}, 11 | {GOAT_ORIENTATION_HORIZONTAL, "HORIZONTAL", "horizontal"}, 12 | {0, NULL, NULL}, 13 | }; 14 | t = g_enum_register_static ("GoatOrientationTypes", ts); 15 | } 16 | return t; 17 | } 18 | 19 | GType goat_position_get_type (void) 20 | { 21 | static GType t = 0; 22 | 23 | if (!t) { 24 | static GEnumValue ts[] = { 25 | {GOAT_POSITION_INVALID, "INVALID", "invalid"}, {GOAT_POSITION_TOP, "TOP", "top"}, 26 | {GOAT_POSITION_BOTTOM, "BOTTOM", "bottom"}, {GOAT_POSITION_LEFT, "LEFT", "left"}, 27 | {GOAT_POSITION_RIGHT, "RIGHT", "right"}, {0, NULL, NULL}, 28 | }; 29 | t = g_enum_register_static ("GoatPositionTypes", ts); 30 | } 31 | 32 | return t; 33 | } 34 | 35 | GType goat_heading_get_type (void) 36 | { 37 | static GType t = 0; 38 | 39 | if (!t) { 40 | static GEnumValue ts[] = { 41 | {GOAT_HEADING_IN, "INSIDE", "in"}, 42 | {GOAT_HEADING_OUT, "OUTSIDE", "out"}, 43 | {0, NULL, NULL}, 44 | }; 45 | t = g_enum_register_static ("GoatHeadingTypes", ts); 46 | } 47 | 48 | return t; 49 | } 50 | 51 | GType goat_marker_style_get_type (void) 52 | { 53 | static GType t = 0; 54 | 55 | if (!t) { 56 | static GEnumValue ts[] = { 57 | {GOAT_MARKER_STYLE_INVALID, "INVALID", "invalid marker style"}, 58 | {GOAT_MARKER_STYLE_NONE, "NONE", "none"}, 59 | {GOAT_MARKER_STYLE_POINT, "POINT", "•"}, 60 | {GOAT_MARKER_STYLE_SQUARE, "SQUARE", "■"}, 61 | {GOAT_MARKER_STYLE_TRIANGLE, "TRIANGLE", "▲"}, 62 | {GOAT_MARKER_STYLE_CROSS, "CROSS", "❌"}, 63 | {0, NULL, NULL}, 64 | }; 65 | t = g_enum_register_static ("GoatMarkerStyleTypes", ts); 66 | } 67 | 68 | return t; 69 | } 70 | -------------------------------------------------------------------------------- /src/goat-plot-enum.h: -------------------------------------------------------------------------------- 1 | #ifndef __GOAT_ENUM_H__ 2 | #define __GOAT_ENUM_H__ 3 | 4 | #include 5 | 6 | typedef enum { 7 | GOAT_ORIENTATION_INVALID = 0, 8 | GOAT_ORIENTATION_VERTICAL = 1, 9 | GOAT_ORIENTATION_HORIZONTAL = 2 10 | } GoatOrientation; 11 | 12 | #define GOAT_TYPE_ORIENTATION (goat_orientation_get_type ()) 13 | GType goat_orientation_get_type (void); 14 | 15 | 16 | 17 | typedef enum { 18 | GOAT_POSITION_INVALID = 0, 19 | GOAT_POSITION_TOP = 1, 20 | GOAT_POSITION_BOTTOM = 2, 21 | GOAT_POSITION_LEFT = 3, 22 | GOAT_POSITION_RIGHT = 4 23 | } GoatPosition; 24 | 25 | #define GOAT_TYPE_POSITION (goat_position_get_type ()) 26 | GType goat_position_get_type (void); 27 | 28 | 29 | 30 | 31 | typedef enum { 32 | GOAT_HEADING_IN = 1, 33 | GOAT_HEADING_OUT = 2, 34 | } GoatHeading; 35 | 36 | #define GOAT_TYPE_HEADING (goat_heading_get_type ()) 37 | GType goat_heading_get_type (void); 38 | 39 | 40 | typedef enum { 41 | GOAT_PLOT_SCALE_EXP = 1, 42 | GOAT_PLOT_SCALE_LIN = 2, 43 | GOAT_PLOT_SCALE_LOG = 3, 44 | } GoatPlotScaleType; 45 | 46 | // FIXME this does not make much sense here, make this an option for the GoatPlot widget itself, 47 | // FIXME it is the users duty to convert his data to the proper form 48 | typedef enum { 49 | GOAT_MARKER_STYLE_INVALID = 0, 50 | GOAT_MARKER_STYLE_NONE = 1, 51 | GOAT_MARKER_STYLE_POINT = 2, 52 | GOAT_MARKER_STYLE_SQUARE = 3, 53 | GOAT_MARKER_STYLE_TRIANGLE = 4, 54 | GOAT_MARKER_STYLE_CROSS = 5 55 | } GoatMarkerStyle; 56 | 57 | #define GOAT_TYPE_MARKER_STYLE (goat_marker_style_get_type ()) 58 | GType goat_marker_style_get_type (void); 59 | 60 | 61 | #endif /* __GOAT_ENUM_H__ */ 62 | -------------------------------------------------------------------------------- /src/goat-plot-internal.h: -------------------------------------------------------------------------------- 1 | 2 | #include "goat-plot.h" 3 | #include 4 | #include 5 | #include 6 | -------------------------------------------------------------------------------- /src/goat-plot.c: -------------------------------------------------------------------------------- 1 | /* 2 | * goat-plot.c 3 | * This file is part of GoatPlot 4 | * 5 | * Copyright (C) 2014 - Bernhard Schuster 6 | * 7 | * GoatPlot is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * GoatPlot is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with GoatPlot. If not, see . 19 | */ 20 | 21 | #include "goat-plot.h" 22 | #include "goat-scale-linear.h" 23 | #include "goat-scale-log.h" 24 | #include "goat-utils.h" 25 | 26 | #include 27 | #include 28 | 29 | static gboolean draw (GtkWidget *widget, cairo_t *cr); 30 | static void get_prefered_width (GtkWidget *widget, int *minimal, int *natural); 31 | static void get_prefered_height (GtkWidget *widget, int *minimal, int *natural); 32 | static gboolean event (GtkWidget *widget, GdkEvent *event); 33 | static gboolean scroll_event (GtkWidget *widget, GdkEventScroll *event); 34 | 35 | struct _GoatPlotPrivate { 36 | GArray *array; // array of GoatDataset pointers 37 | // remove this, its the users duty to prescale data properly FIXME 38 | 39 | GdkRGBA color_background; 40 | GdkRGBA color_border; 41 | 42 | GoatScale *scale_x; 43 | GoatScale *scale_y; 44 | }; 45 | 46 | G_DEFINE_TYPE_WITH_PRIVATE (GoatPlot, goat_plot, GTK_TYPE_DRAWING_AREA); 47 | 48 | #include "goat-plot-internal.h" 49 | 50 | static void goat_plot_finalize (GObject *object) 51 | { 52 | GoatPlot *plot = GOAT_PLOT (object); 53 | GoatPlotPrivate *priv = goat_plot_get_instance_private (plot); 54 | gint register i = priv->array->len; 55 | while (--i >= 0) { 56 | g_object_unref (g_array_index (priv->array, GoatDataset *, i)); 57 | } 58 | g_array_free (plot->priv->array, TRUE); 59 | 60 | G_OBJECT_CLASS (goat_plot_parent_class)->finalize (object); 61 | } 62 | 63 | enum { 64 | PROP_0, 65 | 66 | PROP_SCALE_X, 67 | PROP_SCALE_Y, 68 | 69 | N_PROPERTIES 70 | }; 71 | 72 | static GParamSpec *obj_properties[N_PROPERTIES] = { 73 | NULL, 74 | }; 75 | 76 | static void goat_plot_set_gproperty (GObject *object, guint prop_id, const GValue *value, GParamSpec *spec) 77 | { 78 | GoatPlot *dataset = GOAT_PLOT (object); 79 | GoatPlotPrivate *priv = goat_plot_get_instance_private (dataset); 80 | 81 | switch (prop_id) { 82 | case PROP_SCALE_X: 83 | priv->scale_x = g_value_get_pointer (value); 84 | break; 85 | case PROP_SCALE_Y: 86 | priv->scale_y = g_value_get_pointer (value); 87 | break; 88 | default: 89 | G_OBJECT_WARN_INVALID_PROPERTY_ID (dataset, prop_id, spec); 90 | } 91 | } 92 | 93 | static void goat_plot_get_gproperty (GObject *object, guint prop_id, GValue *value, GParamSpec *spec) 94 | { 95 | GoatPlot *dataset = GOAT_PLOT (object); 96 | GoatPlotPrivate *priv = goat_plot_get_instance_private (dataset); 97 | 98 | switch (prop_id) { 99 | case PROP_SCALE_X: 100 | g_value_set_pointer (value, priv->scale_x); 101 | break; 102 | case PROP_SCALE_Y: 103 | g_value_set_pointer (value, priv->scale_y); 104 | break; 105 | default: 106 | G_OBJECT_WARN_INVALID_PROPERTY_ID (dataset, prop_id, spec); 107 | } 108 | } 109 | 110 | static void goat_plot_class_init (GoatPlotClass *klass) 111 | { 112 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 113 | object_class->finalize = goat_plot_finalize; 114 | 115 | object_class->set_property = goat_plot_set_gproperty; 116 | object_class->get_property = goat_plot_get_gproperty; 117 | 118 | obj_properties[PROP_SCALE_X] = g_param_spec_pointer ("scale_x", "GoatPlot::scale_x", "scale x", G_PARAM_READWRITE); 119 | 120 | obj_properties[PROP_SCALE_Y] = g_param_spec_pointer ("scale_y", "GoatPlot::scale_y", "scale y", G_PARAM_READWRITE); 121 | 122 | g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); 123 | 124 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); 125 | widget_class->draw = draw; 126 | 127 | #if 0 128 | widget_class->button_press_event = goat_plot_button_press_event; 129 | widget_class->button_release_event = goat_plot_button_release_event; 130 | widget_class->key_press_event = goat_plot_key_press_event; 131 | widget_class->key_release_event = goat_plot_key_release_event; 132 | #endif 133 | widget_class->scroll_event = scroll_event; 134 | widget_class->get_preferred_width = get_prefered_width; 135 | widget_class->get_preferred_height = get_prefered_height; 136 | } 137 | 138 | static void goat_plot_init (GoatPlot *self) 139 | { 140 | gboolean assure; 141 | GtkWidget *widget = GTK_WIDGET (self); 142 | gtk_widget_set_has_window (widget, FALSE); 143 | 144 | self->priv = goat_plot_get_instance_private (self); 145 | 146 | self->priv->array = g_array_new (FALSE, TRUE, sizeof (void *)); 147 | 148 | assure = gdk_rgba_parse (&(self->priv->color_background), "white"); 149 | g_assert (assure); 150 | assure = gdk_rgba_parse (&(self->priv->color_border), "black"); 151 | g_assert (assure); 152 | 153 | gtk_widget_add_events (widget, GDK_SCROLL_MASK); 154 | g_assert ((gtk_widget_get_events (widget) & GDK_SCROLL_MASK) != 0); 155 | 156 | gtk_widget_set_sensitive (widget, TRUE); 157 | gtk_widget_set_can_focus (widget, TRUE); 158 | gtk_widget_grab_focus (widget); 159 | } 160 | 161 | /** 162 | * create a new GoatPlot widget 163 | */ 164 | GoatPlot *goat_plot_new (GoatScale *x, GoatScale *y) 165 | { 166 | return GOAT_PLOT (gtk_widget_new (GOAT_TYPE_PLOT, "scale_x", x, "scale_y", y, NULL)); 167 | } 168 | 169 | /** 170 | * 171 | */ 172 | void goat_plot_set_scale_x (GoatPlot *plot, GoatScale *scale) 173 | { 174 | g_object_set (G_OBJECT (plot), "scale_x", scale, NULL); 175 | } 176 | 177 | /** 178 | * 179 | */ 180 | void goat_plot_set_scale_y (GoatPlot *plot, GoatScale *scale) 181 | { 182 | g_object_set (G_OBJECT (plot), "scale_y", scale, NULL); 183 | } 184 | 185 | /** 186 | * add a dataset to the plot 187 | * @returns identifier to use for removal 188 | */ 189 | gint goat_plot_add_dataset (GoatPlot *plot, GoatDataset *dataset) 190 | { 191 | g_return_val_if_fail (plot != NULL, -1); 192 | g_return_val_if_fail (GOAT_IS_PLOT (plot), -1); 193 | g_return_val_if_fail (dataset != NULL, -1); 194 | g_return_val_if_fail (GOAT_IS_DATASET (dataset), -1); 195 | 196 | gpointer tmp = g_object_ref (dataset); 197 | g_array_append_val (plot->priv->array, tmp); 198 | 199 | return plot->priv->array->len - 1; 200 | } 201 | 202 | /** 203 | * remove a dataset from the plot 204 | */ 205 | GoatDataset *goat_plot_remove_dataset (GoatPlot *plot, gint datasetid) 206 | { 207 | GoatDataset *dataset = NULL; 208 | GoatPlotPrivate *priv = goat_plot_get_instance_private (plot); 209 | GoatDataset **datasetptr = &(g_array_index (priv->array, GoatDataset *, datasetid)); 210 | if (datasetptr != NULL) { 211 | dataset = *datasetptr; 212 | *datasetptr = NULL; 213 | } 214 | return dataset; 215 | } 216 | 217 | 218 | 219 | /** 220 | * return dataset 221 | */ 222 | GoatDataset *goat_plot_get_dataset (GoatPlot *plot, gint datasetid) 223 | { 224 | GoatPlotPrivate *priv = goat_plot_get_instance_private (plot); 225 | GoatDataset *dataset = g_array_index (priv->array, GoatDataset *, datasetid); 226 | return dataset; 227 | } 228 | 229 | /** 230 | * @param window window height or width, or if fixed scale, use a const for that 231 | * @param min in units 232 | * @param max in units 233 | * @param unit_to_pixel [out] 234 | */ 235 | gboolean get_unit_to_pixel_factor (int window, gdouble min, gdouble max, gdouble *unit_to_pixel) 236 | { 237 | gdouble delta = max - min; 238 | if (delta > 0.) { 239 | *unit_to_pixel = (double)window / delta; 240 | // g_printf ("MAPPING: %i --{%lf}--> %lf\n", window, (*unit_to_pixel), delta); 241 | return TRUE; 242 | } 243 | *unit_to_pixel = 1.; 244 | return FALSE; 245 | } 246 | 247 | static gboolean draw (GtkWidget *widget, cairo_t *cr) 248 | { 249 | GoatPlot *plot; 250 | GoatDataset *dataset; 251 | GoatPlotPrivate *priv; 252 | GtkAllocation allocation; //==gint x,y,width,heightynamically 253 | // TODO this needs to be done dynamically 254 | // TODO based on the style context and where the scales are 255 | // TODO https://github.com/drahnr/goatplot/issues/8 256 | GtkBorder padding = {10, 10, 10, 10}; // left, right, top, bottom 257 | gint i; 258 | gdouble x_nil_pixel; 259 | gdouble y_nil_pixel; 260 | gdouble x_unit_to_pixel; 261 | gdouble y_unit_to_pixel; 262 | int width, height; 263 | GdkRGBA color, color_meta, marker_line_color, marker_fill_color; 264 | double diameter; 265 | double line_width; 266 | double marker_line_width; 267 | gdouble x, y, ystddev, x_log_min, y_log_min; 268 | GoatDatasetIter dit; 269 | gboolean x_log = FALSE, y_log = FALSE; 270 | gboolean draw = TRUE; 271 | int count = 0; 272 | 273 | 274 | if (gtk_widget_is_drawable (widget)) { 275 | plot = GOAT_PLOT (widget); 276 | priv = goat_plot_get_instance_private (plot); 277 | cairo_save (cr); 278 | 279 | g_assert (priv); 280 | g_assert (priv->scale_x); 281 | g_assert (GOAT_IS_SCALE (priv->scale_x)); 282 | g_assert (priv->scale_y); 283 | g_assert (GOAT_IS_SCALE (priv->scale_y)); 284 | 285 | gtk_widget_get_allocation (widget, &allocation); 286 | 287 | gdouble ref_x_min = G_MAXDOUBLE; 288 | gdouble ref_x_max = -G_MAXDOUBLE; 289 | gdouble ref_y_min = G_MAXDOUBLE; 290 | gdouble ref_y_max = -G_MAXDOUBLE; 291 | 292 | // get the common extends of all data sets if any scale used is set to autorange 293 | // 294 | // vfunc, we use it in a loop, so caching is good idea 295 | const gboolean register autorange_x = goat_scale_is_auto_range (priv->scale_x); 296 | const gboolean register autorange_y = goat_scale_is_auto_range (priv->scale_y); 297 | 298 | x_log = GOAT_IS_SCALE_LOG (priv->scale_x); 299 | y_log = GOAT_IS_SCALE_LOG (priv->scale_y); 300 | 301 | goat_scale_get_range (priv->scale_x, &ref_x_min, &ref_x_max); 302 | goat_scale_get_range (priv->scale_y, &ref_y_min, &ref_y_max); 303 | 304 | if (autorange_x || autorange_y) { 305 | /* For each dataset -> find min/max */ 306 | for (i = 0; i < priv->array->len; i++) { 307 | gdouble x_min, x_max, y_min, y_max; 308 | 309 | dataset = g_array_index (priv->array, GoatDataset *, i); 310 | 311 | if (!dataset) 312 | continue; 313 | 314 | goat_dataset_get_extrema (dataset, &x_min, &x_max, &y_min, &y_max); 315 | 316 | if (autorange_x) { 317 | if (ref_x_min > x_min) 318 | ref_x_min = x_min; 319 | if (ref_x_max < x_max) 320 | ref_x_max = x_max; 321 | } 322 | if (autorange_y) { 323 | if (ref_y_min > y_min) 324 | ref_y_min = y_min; 325 | if (ref_y_max < y_max) 326 | ref_y_max = y_max; 327 | } 328 | } 329 | } 330 | 331 | if (x_log) { 332 | /* May need to find smallest value > 0. */ 333 | if (ref_x_min <= 0.) { 334 | double xlm; 335 | ref_x_min = G_MAXDOUBLE; 336 | for (i = 0; i < priv->array->len; i++) { 337 | dataset = g_array_index (priv->array, GoatDataset *, i); 338 | goat_dataset_get_log_extrema (dataset, &xlm, NULL, NULL, NULL); 339 | if (ref_x_min > xlm) 340 | ref_x_min = xlm; 341 | } 342 | } 343 | 344 | /* Final check to avoid negative values on scale */ 345 | if (ref_x_min <= 0.) 346 | ref_x_min = 1.; 347 | 348 | /* Round upper scale so whole number of order 10 steps */ 349 | i = (int)(ceil (log10 (ref_x_max / ref_x_min) / 350 | goat_scale_log_get_major_delta (GOAT_SCALE_LOG (priv->scale_x)))); 351 | if (i < 1) 352 | i = 1; 353 | ref_x_max = ref_x_min * pow (10, i); 354 | 355 | goat_scale_update_range (priv->scale_x, ref_x_min, ref_x_max); 356 | 357 | /* these are now used for data -> pixel mapping - need to be on log scale */ 358 | x_log_min = ref_x_min; 359 | ref_x_min = log10 (ref_x_min); 360 | ref_x_max = log10 (ref_x_max); 361 | } else { 362 | if (ref_x_max <= ref_x_min) 363 | ref_x_max = ref_x_min + 1.; 364 | goat_scale_update_range (priv->scale_x, ref_x_min, ref_x_max); 365 | } 366 | 367 | 368 | if (y_log) { 369 | if (ref_y_min <= 0.) { 370 | double ylm; 371 | ref_y_min = G_MAXDOUBLE; 372 | for (i = 0; i < priv->array->len; i++) { 373 | dataset = g_array_index (priv->array, GoatDataset *, i); 374 | goat_dataset_get_log_extrema (dataset, NULL, NULL, &ylm, NULL); 375 | if (ref_y_min > ylm) 376 | ref_y_min = ylm; 377 | } 378 | } 379 | 380 | /* Final check to avoid negative values on scale */ 381 | if (ref_y_min <= 0.) 382 | ref_y_min = 1.; 383 | 384 | /* Round upper scale so whole number of order 10 steps */ 385 | i = (int)(ceil (log10 (ref_y_max / ref_y_min) / 386 | goat_scale_log_get_major_delta (GOAT_SCALE_LOG (priv->scale_y)))); 387 | if (i < 1) 388 | i = 1; 389 | ref_y_max = ref_y_min * pow (10, i); 390 | 391 | goat_scale_update_range (priv->scale_y, ref_y_min, ref_y_max); 392 | 393 | /* these are now used for data -> pixel mapping - need to be on log scale */ 394 | y_log_min = ref_y_min; 395 | ref_y_min = log10 (ref_y_min); 396 | ref_y_max = log10 (ref_y_max); 397 | } else { 398 | if (ref_y_max <= ref_y_min) 399 | ref_y_max = ref_y_min + 1.; 400 | goat_scale_update_range (priv->scale_y, ref_y_min, ref_y_max); 401 | } 402 | 403 | /* calculate maximum number of displayed */ 404 | /* TODO move into goat scale draw, but mind the perf implications doing it once per scale! */ 405 | /* TODO migrate this into the scale itself, which knows best how much space is needed */ 406 | { 407 | int dummy = 0; 408 | int num_x_padding_max_num = 0; 409 | int num_x_padding_min_num = 0; 410 | int num_y_padding_max_num = 0; 411 | int num_y_padding_min_num = 0; 412 | goat_util_calc_num_extents(ref_x_min, &num_x_padding_min_num, &dummy); 413 | goat_util_calc_num_extents(ref_x_max, &num_x_padding_max_num, &dummy); 414 | goat_util_calc_num_extents(ref_y_min, &dummy, &num_y_padding_min_num); 415 | goat_util_calc_num_extents(ref_y_max, &dummy, &num_y_padding_max_num); 416 | const gint16 num_x_padding = MAX(num_x_padding_min_num,num_x_padding_max_num); 417 | const gint16 num_y_padding = MAX(num_y_padding_min_num,num_y_padding_max_num); 418 | if(priv->scale_x != NULL) { 419 | switch (goat_scale_get_position(priv->scale_x)) { 420 | case GOAT_POSITION_LEFT: 421 | padding.left += num_x_padding + 30; 422 | break; 423 | case GOAT_POSITION_RIGHT: 424 | padding.right += num_x_padding + 30; 425 | break; 426 | case GOAT_POSITION_TOP: 427 | padding.top += num_y_padding + 30; 428 | break; 429 | case GOAT_POSITION_BOTTOM: 430 | padding.bottom += num_y_padding + 30; 431 | break; 432 | default: 433 | g_error("Scale X does not have position"); 434 | break; 435 | } 436 | } 437 | if(priv->scale_y != NULL) { 438 | switch (goat_scale_get_position(priv->scale_y)) { 439 | case GOAT_POSITION_LEFT: 440 | padding.left += num_x_padding + 30; 441 | break; 442 | case GOAT_POSITION_RIGHT: 443 | padding.right += num_x_padding + 30; 444 | break; 445 | case GOAT_POSITION_TOP: 446 | padding.top += num_y_padding + 30; 447 | break; 448 | case GOAT_POSITION_BOTTOM: 449 | padding.bottom += num_y_padding + 30; 450 | break; 451 | default: 452 | g_error("Scale Y does not have position"); 453 | break; 454 | } 455 | } 456 | } 457 | 458 | // translate origin to plot graphs to (0,0) of our plot 459 | cairo_translate (cr, padding.left, allocation.height - padding.bottom); 460 | 461 | // make it plot naturally +up, -down 462 | cairo_scale (cr, 1., -1.); 463 | 464 | /* Generate data->pixel mapping */ 465 | height = allocation.height - padding.bottom - padding.top; 466 | width = allocation.width - padding.right - padding.left; 467 | 468 | if (!get_unit_to_pixel_factor (width, ref_x_min, ref_x_max, &x_unit_to_pixel)) { 469 | g_warning ("Bad x range: %lf..%lf, delta of %lf", ref_x_min, ref_x_max, ref_x_max - ref_x_min); 470 | cairo_restore (cr); 471 | return FALSE; 472 | } 473 | x_nil_pixel = ref_x_min * -x_unit_to_pixel; 474 | 475 | 476 | if (!get_unit_to_pixel_factor (height, ref_y_min, ref_y_max, &y_unit_to_pixel)) { 477 | g_warning ("Bad y range: %lf..%lf, delta of %lf", ref_y_min, ref_y_max, ref_y_max - ref_y_min); 478 | cairo_restore (cr); 479 | return FALSE; 480 | } 481 | y_nil_pixel = ref_y_min * -y_unit_to_pixel; 482 | 483 | 484 | /* Draw background */ 485 | cairo_rectangle (cr, 0, 0, width, height); 486 | gdk_cairo_set_source_rgba (cr, &priv->color_background); 487 | cairo_fill (cr); 488 | 489 | /* Draw border */ 490 | cairo_rectangle (cr, 0, 0, width, height); 491 | gdk_cairo_set_source_rgba (cr, &priv->color_border); 492 | cairo_set_line_width (cr, 1.); 493 | cairo_stroke (cr); 494 | 495 | /* Draw scales */ 496 | if (draw) { 497 | goat_scale_draw (priv->scale_x, cr, 0, width, 0, height); 498 | goat_scale_draw (priv->scale_y, cr, 0, width, 0, height); 499 | } 500 | 501 | /* Draw datasets */ 502 | if (draw) { 503 | /* make sure we do not draw outside the frame or under the scale*/ 504 | const int top = 0; 505 | const int left = 0; 506 | const int bottom = allocation.height - padding.bottom - padding.top; 507 | const int right = allocation.width - padding.right - padding.left; 508 | cairo_rectangle (cr, left, top, right - left, bottom - top); 509 | cairo_clip (cr); 510 | 511 | for (i = 0; i < priv->array->len; i++) { 512 | dataset = g_array_index (priv->array, GoatDataset *, i); 513 | 514 | if (!dataset) 515 | continue; 516 | 517 | /* Load colours for rendering */ 518 | goat_dataset_get_color (dataset, &color); 519 | goat_dataset_get_marker_line_color (dataset, &marker_line_color); 520 | goat_dataset_get_marker_fill_color (dataset, &marker_fill_color); 521 | goat_dataset_get_line_width (dataset, &line_width); 522 | goat_dataset_get_marker_line_width (dataset, &marker_line_width); 523 | goat_dataset_get_marker_size (dataset, &diameter); 524 | color_meta = color; 525 | color_meta.alpha *= 0.5; 526 | 527 | if (!goat_dataset_get_iter_first (dataset, &dit)) { 528 | g_debug ("Dataset appears to be empty"); 529 | return FALSE; 530 | } 531 | 532 | /* Draw line */ 533 | gdk_cairo_set_source_rgba (cr, &color_meta); 534 | 535 | // draw interconnection in the first round 536 | if (goat_dataset_interpolate (dataset)) { 537 | cairo_set_line_width (cr, line_width); 538 | if (goat_dataset_get_iter_first (dataset, &dit)) { 539 | 540 | goat_dataset_get (dataset, &dit, &x, &y, &ystddev); 541 | if (x_log) { 542 | if (x < x_log_min) 543 | x = x_log_min; 544 | x = log10 (x); 545 | } 546 | x = x * x_unit_to_pixel + x_nil_pixel; 547 | if (y_log) { 548 | if (y < y_log_min) 549 | y = y_log_min; 550 | y = log10 (y); 551 | ystddev = log10 (ystddev); 552 | } 553 | y = y * y_unit_to_pixel + y_nil_pixel; 554 | cairo_move_to (cr, x, y); 555 | 556 | while (goat_dataset_iter_next (dataset, &dit)) { 557 | goat_dataset_get (dataset, &dit, &x, &y, &ystddev); 558 | if (x_log) { 559 | if (x < x_log_min) 560 | x = x_log_min; 561 | x = log10 (x); 562 | } 563 | x = x * x_unit_to_pixel + x_nil_pixel; 564 | if (y_log) { 565 | if (y < y_log_min) 566 | y = y_log_min; 567 | y = log10 (y); 568 | ystddev = log10 (ystddev); 569 | } 570 | y = y * y_unit_to_pixel + y_nil_pixel; 571 | cairo_line_to (cr, x, y); 572 | } 573 | cairo_stroke (cr); 574 | } 575 | } 576 | // draw the variation (if desired) on top in a separate loop 577 | if (goat_dataset_has_valid_standard_deviation (dataset)) { 578 | if (goat_dataset_get_iter_first (dataset, &dit)) { 579 | cairo_set_line_width (cr, line_width); 580 | do { 581 | goat_dataset_get (dataset, &dit, &x, &y, &ystddev); 582 | if (x_log) { 583 | if (x < x_log_min) 584 | x = x_log_min; 585 | x = log10 (x); 586 | } 587 | x = x * x_unit_to_pixel + x_nil_pixel; 588 | if (y_log) { 589 | if (y < y_log_min) 590 | y = y_log_min; 591 | y = log10 (y); 592 | ystddev = log10 (ystddev); 593 | } 594 | y = y * y_unit_to_pixel + y_nil_pixel; 595 | ystddev *= y_unit_to_pixel; 596 | if (fabs (ystddev) > G_MAXFLOAT) { 597 | cairo_move_to (cr, x, 0); 598 | cairo_line_to (cr, x, height); 599 | } 600 | cairo_move_to (cr, x + diameter / 2., y + ystddev); 601 | cairo_line_to (cr, x - diameter / 2., y + ystddev); 602 | cairo_move_to (cr, x + diameter / 2., y - ystddev); 603 | cairo_line_to (cr, x - diameter / 2., y - ystddev); 604 | cairo_move_to (cr, x, y + ystddev); 605 | cairo_line_to (cr, x, y - ystddev); 606 | } while (goat_dataset_iter_next (dataset, &dit)); 607 | cairo_stroke (cr); 608 | } 609 | } 610 | 611 | // draw the indivdual data markers on top 612 | if (goat_dataset_get_iter_first (dataset, &dit) && 613 | goat_dataset_get_marker_style (dataset) != GOAT_MARKER_STYLE_NONE) { 614 | /* TODO: Switch outside loops? */ 615 | do { 616 | goat_dataset_get (dataset, &dit, &x, &y, NULL); 617 | if (x_log) { 618 | if (x < x_log_min) 619 | x = x_log_min; 620 | x = log10 (x); 621 | } 622 | x = x * x_unit_to_pixel + x_nil_pixel; 623 | if (y_log) { 624 | if (y < y_log_min) 625 | y = y_log_min; 626 | y = log10 (y); 627 | ystddev = log10 (ystddev); 628 | } 629 | y = y * y_unit_to_pixel + y_nil_pixel; 630 | switch (goat_dataset_get_marker_style (dataset)) { 631 | case GOAT_MARKER_STYLE_TRIANGLE: 632 | cairo_move_to (cr, x + diameter / 2., y - diameter / 2.); 633 | cairo_line_to (cr, x - diameter / 2., y - diameter / 2.); 634 | cairo_line_to (cr, x + 0., y + diameter / 2.); 635 | cairo_line_to (cr, x + diameter / 2., y - diameter / 2.); 636 | break; 637 | case GOAT_MARKER_STYLE_SQUARE: 638 | cairo_rectangle (cr, x - diameter / 2., y - diameter / 2., diameter, diameter); 639 | break; 640 | case GOAT_MARKER_STYLE_POINT: 641 | cairo_move_to (cr, x + diameter / 2., y + diameter / 2.); 642 | cairo_arc (cr, x, y, diameter / 2., 0., 2 * M_PI); 643 | break; 644 | case GOAT_MARKER_STYLE_CROSS: 645 | cairo_move_to (cr, x + diameter / 2., y + diameter / 2.); 646 | cairo_line_to (cr, x - diameter / 2., y - diameter / 2.); 647 | cairo_move_to (cr, x - diameter / 2., y + diameter / 2.); 648 | cairo_line_to (cr, x + diameter / 2., y - diameter / 2.); 649 | break; 650 | case GOAT_MARKER_STYLE_NONE: 651 | break; 652 | case GOAT_MARKER_STYLE_INVALID: 653 | default: { 654 | gint gds = (gint)goat_dataset_get_marker_style (dataset); 655 | g_warning ("DatasetStyle enum out of bounds %i", gds); 656 | } 657 | return FALSE; 658 | } 659 | } while (goat_dataset_iter_next (dataset, &dit)); 660 | 661 | if (goat_dataset_get_marker_fill (dataset)) { 662 | gdk_cairo_set_source_rgba (cr, &marker_fill_color); 663 | cairo_fill_preserve (cr); 664 | } 665 | 666 | gdk_cairo_set_source_rgba (cr, &marker_line_color); 667 | cairo_stroke (cr); 668 | } 669 | } 670 | } 671 | cairo_restore (cr); 672 | 673 | return TRUE; 674 | } 675 | return FALSE; 676 | } 677 | 678 | static void get_prefered_width (GtkWidget *widget, int *minimal, int *natural) 679 | { 680 | *minimal = 200; 681 | *natural = 350; 682 | } 683 | 684 | static void get_prefered_height (GtkWidget *widget, int *minimal, int *natural) 685 | { 686 | *minimal = 200; 687 | *natural = 350; 688 | } 689 | 690 | void goat_plot_set_background_color (GoatPlot *plot, GdkRGBA *color) 691 | { 692 | g_return_if_fail (plot); 693 | g_return_if_fail (GOAT_IS_PLOT (plot)); 694 | g_return_if_fail (color != NULL); 695 | 696 | GoatPlotPrivate *priv; 697 | 698 | priv = goat_plot_get_instance_private (plot); 699 | 700 | priv->color_background = *color; 701 | } 702 | 703 | void goat_plot_set_border_color (GoatPlot *plot, GdkRGBA *color) 704 | { 705 | g_return_if_fail (plot); 706 | g_return_if_fail (GOAT_IS_PLOT (plot)); 707 | g_return_if_fail (color != NULL); 708 | 709 | GoatPlotPrivate *priv; 710 | 711 | priv = goat_plot_get_instance_private (plot); 712 | 713 | priv->color_border = *color; 714 | } 715 | 716 | /** 717 | * TODO handle zooming and scrolling 718 | * https://github.com/drahnr/goatplot/issues/9 719 | */ 720 | static gboolean scroll_event (GtkWidget *widget, GdkEventScroll *event) 721 | { 722 | g_print ("scroll event\n"); 723 | return FALSE; 724 | } 725 | 726 | /** 727 | * TODO handle envents 728 | * https://github.com/drahnr/goatplot/issues/9 729 | */ 730 | static gboolean event (GtkWidget *widget, GdkEvent *event) 731 | { 732 | g_warning ("ehllllo\n??\n"); 733 | switch (event->type) { 734 | case GDK_SCROLL: 735 | case GDK_SCROLL_UP: 736 | case GDK_SCROLL_DOWN: 737 | g_print ("got a scroll event!"); 738 | break; 739 | default: 740 | break; 741 | } 742 | return TRUE; 743 | } 744 | 745 | -------------------------------------------------------------------------------- /src/goat-plot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * goat-plot.h 3 | * This file is part of GoatPlot 4 | * 5 | * Copyright (C) 2014 - Bernhard Schuster 6 | * 7 | * GoatPlot is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * GoatPlot is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with GoatPlot. If not, see . 19 | */ 20 | 21 | #ifndef GOAT_PLOT_H 22 | #define GOAT_PLOT_H 23 | 24 | #include "goat-dataset-interface.h" 25 | #include "goat-plot-enum.h" 26 | #include "goat-scale-interface.h" 27 | #include 28 | 29 | G_BEGIN_DECLS 30 | 31 | #define GOAT_TYPE_PLOT (goat_plot_get_type ()) 32 | #define GOAT_PLOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE_PLOT, GoatPlot)) 33 | #define GOAT_PLOT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE_PLOT, GoatPlot const)) 34 | #define GOAT_PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOAT_TYPE_PLOT, GoatPlotClass)) 35 | #define GOAT_IS_PLOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOAT_TYPE_PLOT)) 36 | #define GOAT_IS_PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOAT_TYPE_PLOT)) 37 | #define GOAT_PLOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GOAT_TYPE_PLOT, GoatPlotClass)) 38 | 39 | typedef struct _GoatPlot GoatPlot; 40 | typedef struct _GoatPlotClass GoatPlotClass; 41 | typedef struct _GoatPlotPrivate GoatPlotPrivate; 42 | 43 | struct _GoatPlot { 44 | GtkDrawingArea parent; 45 | 46 | GoatPlotPrivate *priv; 47 | }; 48 | 49 | struct _GoatPlotClass { 50 | GtkDrawingAreaClass parent_class; 51 | }; 52 | 53 | GType goat_plot_get_type (void) G_GNUC_CONST; 54 | GoatPlot *goat_plot_new (GoatScale *scale_x, GoatScale *scale_y); 55 | void goat_plot_prepend_value (GoatPlot *graph, float x, float y); 56 | gint goat_plot_add_dataset (GoatPlot *plot, GoatDataset *dataset); 57 | GoatDataset *goat_plot_remove_dataset (GoatPlot *plot, gint datasetid); 58 | GoatDataset *goat_plot_get_dataset (GoatPlot *plot, gint datasetid); 59 | void goat_plot_set_fixed_x_size (GoatPlot *plot, gdouble min_x, gdouble max_x); 60 | void goat_plot_set_fixed_y_size (GoatPlot *plot, gdouble min_y, gdouble max_y); 61 | void goat_plot_set_dynamic_x_size (GoatPlot *plot, gboolean dyn); 62 | void goat_plot_set_dynamic_y_size (GoatPlot *plot, gboolean dyn); 63 | void goat_plot_set_grid_visible (GoatPlot *plot, gboolean visible); 64 | void goat_plot_set_scale_x (GoatPlot *plot, GoatScale *scale_x); 65 | void goat_plot_set_scale_y (GoatPlot *plot, GoatScale *scale_y); 66 | void goat_plot_set_background_color (GoatPlot *plot, GdkRGBA *color); 67 | void goat_plot_set_border_color (GoatPlot *plot, GdkRGBA *color); 68 | G_END_DECLS 69 | 70 | #endif /* GOAT_PLOT_H */ 71 | -------------------------------------------------------------------------------- /src/goat-scale-interface.c: -------------------------------------------------------------------------------- 1 | #include "goat-scale-interface.h" 2 | 3 | G_DEFINE_INTERFACE (GoatScale, goat_scale, G_TYPE_OBJECT); 4 | 5 | static void goat_scale_default_init (GoatScaleInterface *iface) 6 | { 7 | iface->draw = NULL; 8 | iface->render = NULL; 9 | iface->is_auto_range = NULL; 10 | iface->set_range_auto = NULL; 11 | iface->set_range = NULL; 12 | iface->update_range = NULL; 13 | iface->get_range = NULL; 14 | iface->get_position = NULL; 15 | iface->set_position = NULL; 16 | iface->get_orientation = NULL; 17 | iface->set_orientation = NULL; 18 | } 19 | 20 | void goat_scale_draw (GoatScale *self, cairo_t *cr, gint left, gint right, gint top, gint bottom) 21 | { 22 | GoatScaleInterface *iface; 23 | 24 | iface = GOAT_SCALE_GET_IFACE (self); 25 | if (iface->draw) { 26 | iface->draw (self, cr, left, right, top, bottom); 27 | } else { 28 | g_error ("Missing draw handler for GoatScaleInterface!"); 29 | } 30 | } 31 | 32 | void goat_scale_render (GoatScale *self) 33 | { 34 | GoatScaleInterface *iface; 35 | 36 | iface = GOAT_SCALE_GET_IFACE (self); 37 | if (iface->render) { 38 | iface->render (self); 39 | } 40 | } 41 | 42 | void goat_scale_get_range (GoatScale *self, gdouble *min, gdouble *max) 43 | { 44 | GoatScaleInterface *iface; 45 | 46 | iface = GOAT_SCALE_GET_IFACE (self); 47 | if (iface->get_range) { 48 | iface->get_range (self, min, max); 49 | } else { 50 | *min = -1.; 51 | *max = 1.; 52 | } 53 | } 54 | 55 | void goat_scale_set_range_auto (GoatScale *self) 56 | { 57 | GoatScaleInterface *iface; 58 | 59 | iface = GOAT_SCALE_GET_IFACE (self); 60 | if (iface->set_range_auto) { 61 | iface->set_range_auto (self); 62 | } 63 | } 64 | 65 | void goat_scale_set_range (GoatScale *self, gdouble min, gdouble max) 66 | { 67 | GoatScaleInterface *iface; 68 | 69 | iface = GOAT_SCALE_GET_IFACE (self); 70 | if (iface->set_range) { 71 | iface->set_range (self, min, max); 72 | } 73 | } 74 | 75 | void goat_scale_update_range (GoatScale *self, gdouble min, gdouble max) 76 | { 77 | GoatScaleInterface *iface; 78 | 79 | iface = GOAT_SCALE_GET_IFACE (self); 80 | if (iface->update_range) { 81 | iface->update_range (self, min, max); 82 | } 83 | } 84 | 85 | gboolean goat_scale_is_auto_range (GoatScale *self) 86 | { 87 | GoatScaleInterface *iface; 88 | 89 | iface = GOAT_SCALE_GET_IFACE (self); 90 | if (iface->is_auto_range) { 91 | return iface->is_auto_range (self); 92 | } 93 | return FALSE; 94 | } 95 | 96 | void goat_scale_grid_show (GoatScale *self, gboolean show) 97 | { 98 | GoatScaleInterface *iface; 99 | 100 | iface = GOAT_SCALE_GET_IFACE (self); 101 | if (iface->show_grid) { 102 | iface->show_grid (self, show); 103 | } 104 | } 105 | 106 | void goat_scale_set_position (GoatScale *self, GoatPosition position) { 107 | GoatScaleInterface* iface = GOAT_SCALE_GET_IFACE(self); 108 | if (iface->set_position) { 109 | iface->set_position(self,position); 110 | } 111 | } 112 | void goat_scale_set_orientation (GoatScale *self, GoatOrientation orientation) { 113 | GoatScaleInterface* iface = GOAT_SCALE_GET_IFACE(self); 114 | if (iface->set_orientation) { 115 | iface->set_orientation(self,orientation); 116 | } 117 | } 118 | GoatPosition goat_scale_get_position (GoatScale *self) { 119 | GoatScaleInterface* iface = GOAT_SCALE_GET_IFACE(self); 120 | if (iface->get_position) { 121 | return iface->get_position(self); 122 | } 123 | } 124 | GoatOrientation goat_scale_get_orientation (GoatScale *self) { 125 | GoatScaleInterface* iface = GOAT_SCALE_GET_IFACE(self); 126 | if (iface->get_orientation) { 127 | return iface->get_orientation(self); 128 | } 129 | } -------------------------------------------------------------------------------- /src/goat-scale-interface.h: -------------------------------------------------------------------------------- 1 | #ifndef GOAT_SCALE_INTERFACE_H 2 | #define GOAT_SCALE_INTERFACE_H 3 | 4 | #include 5 | #include 6 | G_BEGIN_DECLS 7 | 8 | #define GOAT_TYPE_SCALE (goat_scale_get_type ()) 9 | 10 | G_DECLARE_INTERFACE (GoatScale, goat_scale, GOAT, SCALE, GObject) 11 | 12 | struct _GoatScaleInterface { 13 | GTypeInterface parent_iface; 14 | void (*draw) (GoatScale *self, cairo_t *cr, gint left, gint right, gint top, gint bottom); 15 | void (*render) (GoatScale *self); 16 | void (*get_range) (GoatScale *self, gdouble *min, gdouble *max); 17 | void (*set_range) (GoatScale *self, gdouble min, gdouble max); 18 | void (*update_range) (GoatScale *self, gdouble min, gdouble max); 19 | void (*set_range_auto) (GoatScale *self); 20 | void (*set_auto_range) (GoatScale *self); 21 | gboolean (*is_auto_range) (GoatScale *scale); 22 | void (*show_grid) (GoatScale *scale, gboolean show); 23 | GoatPosition (*get_position)(GoatScale *scale); 24 | GoatOrientation (*get_orientation)(GoatScale *scale); 25 | void (*set_position)(GoatScale *scale, GoatPosition position); 26 | void (*set_orientation)(GoatScale *scale, GoatOrientation orientation); 27 | }; 28 | 29 | 30 | /** 31 | * in case rendering the scale is expensive 32 | * this may be used to pre-draw the scale 33 | */ 34 | void goat_scale_render (GoatScale *self); 35 | 36 | /** 37 | * does the actual drawing 38 | */ 39 | void goat_scale_draw (GoatScale *self, cairo_t *cr, gint left, gint right, gint top, gint bottom); 40 | void goat_scale_get_range (GoatScale *scale, gdouble *min, gdouble *max); 41 | void goat_scale_set_range_auto (GoatScale *scale); 42 | void goat_scale_set_range (GoatScale *scale, gdouble min, gdouble max); 43 | void goat_scale_update_range (GoatScale *scale, gdouble min, gdouble max); 44 | gboolean goat_scale_is_auto_range (GoatScale *scale); 45 | void goat_scale_grid_show (GoatScale *scale, gboolean show); 46 | void goat_scale_set_position (GoatScale *scale, GoatPosition position); 47 | void goat_scale_set_orientation (GoatScale *scale, GoatOrientation orientation); 48 | GoatPosition goat_scale_get_position (GoatScale *scale); 49 | GoatOrientation goat_scale_get_orientation (GoatScale *scale); 50 | 51 | G_END_DECLS 52 | 53 | #endif /* GOAT_SCALE_INTERFACE_H */ 54 | -------------------------------------------------------------------------------- /src/goat-scale-linear.c: -------------------------------------------------------------------------------- 1 | #include "goat-scale-linear.h" 2 | #include "goat-plot-enum.h" 3 | #include "goat-utils.h" 4 | 5 | struct _GoatScaleLinearPrivate { 6 | gboolean scale_fixed; 7 | 8 | // scale bounds 9 | gdouble min, max; 10 | // automatically choose x_min, ... y_max according to the datasets 11 | gboolean autorange; 12 | 13 | // ticks probably move into GoatTicks/Scale object 14 | gdouble major_delta; 15 | gint minors_per_major; 16 | 17 | GdkRGBA color_major; 18 | GdkRGBA color_minor; 19 | 20 | GdkRGBA color_major_grid; 21 | GdkRGBA color_minor_grid; 22 | 23 | GdkRGBA color_background; 24 | GdkRGBA color_border; 25 | 26 | gint width_minor; 27 | gint width_major; 28 | 29 | gboolean draw_grid; 30 | 31 | GoatOrientation orientation; 32 | GoatPosition position; 33 | }; 34 | 35 | static void goat_scale_linear_interface_init (GoatScaleInterface *iface); 36 | 37 | G_DEFINE_TYPE_WITH_CODE (GoatScaleLinear, goat_scale_linear, G_TYPE_OBJECT, 38 | G_IMPLEMENT_INTERFACE (GOAT_TYPE_SCALE, goat_scale_linear_interface_init) 39 | G_ADD_PRIVATE (GoatScaleLinear)) 40 | 41 | enum { 42 | PROP_0, 43 | 44 | PROP_GRID_VISIBLE, 45 | PROP_ORIENTATION, 46 | PROP_POSITION, 47 | 48 | N_PROPERTIES 49 | }; 50 | 51 | static GParamSpec *obj_properties[N_PROPERTIES] = { 52 | NULL, 53 | }; 54 | 55 | static void goat_scale_linear_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) 56 | { 57 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (object); 58 | GoatScaleLinearPrivate *priv = goat_scale_linear_get_instance_private (self); 59 | 60 | switch (property_id) { 61 | case PROP_GRID_VISIBLE: 62 | priv->draw_grid = g_value_get_boolean (value); 63 | break; 64 | case PROP_ORIENTATION: 65 | priv->orientation = g_value_get_enum (value); 66 | break; 67 | case PROP_POSITION: 68 | priv->position = g_value_get_enum (value); 69 | break; 70 | default: 71 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 72 | break; 73 | } 74 | } 75 | 76 | static void goat_scale_linear_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) 77 | { 78 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (object); 79 | GoatScaleLinearPrivate *priv = goat_scale_linear_get_instance_private (self); 80 | 81 | switch (property_id) { 82 | case PROP_GRID_VISIBLE: 83 | g_value_set_boolean (value, priv->draw_grid); 84 | break; 85 | case PROP_ORIENTATION: 86 | g_value_set_enum (value, priv->orientation); 87 | break; 88 | case PROP_POSITION: 89 | g_value_set_enum (value, priv->position); 90 | break; 91 | default: 92 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 93 | break; 94 | } 95 | } 96 | 97 | static void goat_scale_linear_finalize (GObject *object) 98 | { 99 | G_OBJECT_CLASS (goat_scale_linear_parent_class)->finalize (object); 100 | } 101 | 102 | static void goat_scale_linear_class_init (GoatScaleLinearClass *klass) 103 | { 104 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 105 | 106 | object_class->set_property = goat_scale_linear_set_property; 107 | object_class->get_property = goat_scale_linear_get_property; 108 | 109 | obj_properties[PROP_GRID_VISIBLE] = 110 | g_param_spec_boolean ("grid-visible", "Show the grid", "To show or not to show", FALSE, G_PARAM_READWRITE); 111 | 112 | obj_properties[PROP_ORIENTATION] = 113 | g_param_spec_enum ("orientation", "Set orientation property", "Set the orientation ot vertical of horizontal", 114 | GOAT_TYPE_ORIENTATION, GOAT_ORIENTATION_INVALID, G_PARAM_READWRITE); 115 | obj_properties[PROP_POSITION] = 116 | g_param_spec_enum ("position", "Set position property", "Set the position to left,right,top or bottom", 117 | GOAT_TYPE_POSITION, GOAT_POSITION_LEFT, G_PARAM_READWRITE); 118 | 119 | g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); 120 | 121 | object_class->finalize = goat_scale_linear_finalize; 122 | } 123 | 124 | static void goat_scale_linear_init (GoatScaleLinear *self) 125 | { 126 | GoatScaleLinearPrivate *priv = self->priv = goat_scale_linear_get_instance_private (self); 127 | goat_scale_set_range_auto (GOAT_SCALE (self)); 128 | priv->draw_grid = TRUE; 129 | priv->minors_per_major = 4; 130 | priv->major_delta = 10.; 131 | priv->width_minor = 5; 132 | priv->width_major = 8; 133 | priv->color_major.red = 0.8; 134 | priv->color_major.green = 0.2; 135 | priv->color_major.blue = 0.2; 136 | priv->color_major.alpha = 1.0; 137 | priv->color_minor.red = 0.4; 138 | priv->color_minor.green = 0.4; 139 | priv->color_minor.blue = 0.4; 140 | priv->color_minor.alpha = 1.0; 141 | priv->color_minor_grid = priv->color_minor; 142 | priv->color_minor_grid.alpha = 0.3; 143 | priv->color_major_grid = priv->color_major; 144 | priv->color_major_grid.alpha = 0.3; 145 | priv->orientation = GOAT_ORIENTATION_INVALID; 146 | priv->position = GOAT_POSITION_INVALID; 147 | } 148 | 149 | GoatScaleLinear *goat_scale_linear_new (GoatPosition position, GoatOrientation orientation) 150 | { 151 | GoatPosition assure_pos; 152 | GoatOrientation assure_ori; 153 | GoatScaleLinear *self = 154 | g_object_new (GOAT_TYPE_SCALE_LINEAR, "position", position, "orientation", orientation, NULL); 155 | g_object_get (G_OBJECT (self), "position", &assure_pos, "orientation", &assure_ori, NULL); 156 | g_assert (assure_pos == position); 157 | g_assert (assure_ori == orientation); 158 | g_assert (position == self->priv->position); 159 | g_assert (position == goat_scale_get_position(GOAT_SCALE(self))); 160 | return self; 161 | } 162 | 163 | void goat_scale_linear_set_ticks (GoatScaleLinear *scale, gdouble major, gint minors_per_major) 164 | { 165 | g_return_if_fail (major > 0.); 166 | g_return_if_fail (scale); 167 | g_return_if_fail (GOAT_IS_SCALE_LINEAR (scale)); 168 | 169 | GoatScaleLinearPrivate *priv; 170 | 171 | priv = goat_scale_linear_get_instance_private (scale); 172 | 173 | priv->major_delta = major; 174 | priv->minors_per_major = minors_per_major; 175 | } 176 | 177 | 178 | static inline gboolean is_major_tick (gint i, gint minors_per_major) 179 | { 180 | return (i == 0) || (i % minors_per_major == 0); 181 | } 182 | /** 183 | * @param x/y-nil in pixel 184 | * @param x/y-factor convert unit to pixel 185 | */ 186 | static void draw (GoatScale *scale, cairo_t *cr, gint left, gint right, gint top, gint bottom) 187 | { 188 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (scale); 189 | GoatScaleLinearPrivate *priv = goat_scale_linear_get_instance_private (self); 190 | 191 | const double step_minor = (priv->major_delta / priv->minors_per_major); 192 | gint register i; 193 | const gint width_major = priv->width_major; 194 | const gint width_minor = priv->width_minor; 195 | GdkRGBA color_minor = priv->color_minor; 196 | GdkRGBA color_major = priv->color_major; 197 | GdkRGBA color_minor_grid = priv->color_minor_grid; 198 | GdkRGBA color_major_grid = priv->color_major_grid; 199 | 200 | GoatPosition where = goat_scale_get_position(scale);; 201 | gboolean grid = priv->draw_grid; 202 | 203 | gdouble scale_min, scale_max; 204 | gdouble nil, factor; 205 | 206 | int register start; 207 | int register end; 208 | 209 | 210 | scale_min = priv->min; 211 | scale_max = priv->max; 212 | 213 | cairo_set_line_width (cr, 1.); 214 | 215 | if (where == GOAT_POSITION_LEFT) { 216 | if (!get_unit_to_pixel_factor (bottom - top, scale_min, scale_max, &factor)) { 217 | g_warning ("Bad y range\n"); 218 | return; 219 | } 220 | nil = scale_min * -factor; 221 | 222 | start = (top - nil) / step_minor / factor; 223 | end = (bottom - nil) / step_minor / factor; 224 | 225 | for (i = start; i <= end; i++) { 226 | const gboolean register majorstip = is_major_tick (i, priv->minors_per_major); 227 | const double register y = nil + top + step_minor * factor * i; 228 | 229 | if (y > bottom || y < top) 230 | continue; 231 | 232 | if (grid) { 233 | cairo_move_to (cr, right, y); 234 | cairo_line_to (cr, left, y); 235 | if (majorstip) { 236 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 237 | } else { 238 | gdk_cairo_set_source_rgba (cr, &color_minor_grid); 239 | } 240 | cairo_stroke (cr); 241 | } 242 | cairo_move_to (cr, left, y); 243 | if (majorstip) { 244 | cairo_line_to (cr, left - width_major, y); 245 | gdk_cairo_set_source_rgba (cr, &color_major); 246 | } else { 247 | cairo_line_to (cr, left - width_minor, y); 248 | gdk_cairo_set_source_rgba (cr, &color_minor); 249 | } 250 | cairo_stroke (cr); 251 | 252 | if (majorstip) { 253 | goat_util_draw_num (cr, left - width_major, y, step_minor * i, where); 254 | } 255 | } 256 | 257 | /* Zero axis */ 258 | if (nil >= top && nil <= bottom) { 259 | cairo_set_line_width (cr, 1.); 260 | cairo_set_source_rgba (cr, 0.7, 0., 0., 1.); 261 | cairo_move_to (cr, left, nil); 262 | cairo_line_to (cr, right, nil); 263 | cairo_stroke (cr); 264 | } 265 | } 266 | if (where == GOAT_POSITION_RIGHT) { 267 | if (!get_unit_to_pixel_factor (bottom - top, scale_min, scale_max, &factor)) { 268 | g_warning ("Bad y range\n"); 269 | return; 270 | } 271 | nil = scale_min * -factor; 272 | 273 | start = (top - nil) / step_minor / factor; 274 | end = (bottom - nil) / step_minor / factor; 275 | 276 | for (i = start; i <= end; i++) { 277 | const gboolean register majorstip = is_major_tick (i, priv->minors_per_major); 278 | const double register y = nil + top + step_minor * factor * i; 279 | 280 | if (y > bottom || y < top) 281 | continue; 282 | 283 | if (grid) { 284 | cairo_move_to (cr, left, y); 285 | cairo_line_to (cr, right, y); 286 | if (majorstip) { 287 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 288 | } else { 289 | gdk_cairo_set_source_rgba (cr, &color_minor_grid); 290 | } 291 | cairo_stroke (cr); 292 | } 293 | cairo_move_to (cr, right, y); 294 | if (majorstip) { 295 | cairo_line_to (cr, right + width_major, y); 296 | gdk_cairo_set_source_rgba (cr, &color_major); 297 | } else { 298 | cairo_line_to (cr, right + width_minor, y); 299 | gdk_cairo_set_source_rgba (cr, &color_minor); 300 | } 301 | cairo_stroke (cr); 302 | 303 | if (majorstip) { 304 | goat_util_draw_num (cr, right + width_major, y, step_minor * i, where); 305 | } 306 | } 307 | 308 | /* Zero axis */ 309 | if (nil >= top && nil <= bottom) { 310 | cairo_set_line_width (cr, 1.); 311 | cairo_set_source_rgba (cr, 0.7, 0., 0., 1.); 312 | cairo_move_to (cr, left, nil); 313 | cairo_line_to (cr, right, nil); 314 | cairo_stroke (cr); 315 | } 316 | } 317 | if (where == GOAT_POSITION_BOTTOM) { 318 | if (!get_unit_to_pixel_factor (right - left, scale_min, scale_max, &factor)) { 319 | g_warning ("Bad x range\n"); 320 | return; 321 | } 322 | nil = scale_min * -factor; 323 | 324 | start = (left - nil) / step_minor / factor; 325 | end = (right - nil) / step_minor / factor; 326 | 327 | for (i = start; i <= end; i++) { 328 | const gboolean register majorstip = is_major_tick (i, priv->minors_per_major); 329 | const double register x = nil + left + step_minor * factor * i; 330 | 331 | if (x < left || x > right) 332 | continue; 333 | 334 | if (grid) { 335 | cairo_move_to (cr, x, top); 336 | cairo_line_to (cr, x, bottom); 337 | if (majorstip) { 338 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 339 | } else { 340 | gdk_cairo_set_source_rgba (cr, &color_minor_grid); 341 | } 342 | cairo_stroke (cr); 343 | } 344 | cairo_move_to (cr, x, top); 345 | if (majorstip) { 346 | cairo_line_to (cr, x, top - width_major); 347 | gdk_cairo_set_source_rgba (cr, &color_major); 348 | } else { 349 | cairo_line_to (cr, x, top - width_minor); 350 | gdk_cairo_set_source_rgba (cr, &color_minor); 351 | } 352 | cairo_stroke (cr); 353 | 354 | if (majorstip) { 355 | goat_util_draw_num (cr, x, top - width_major, step_minor * i, where); 356 | } 357 | } 358 | 359 | /* Zero axis */ 360 | if (nil <= right && nil >= left) { 361 | cairo_set_line_width (cr, 1.); 362 | cairo_set_source_rgba (cr, 0.7, 0., 0., 1.); 363 | cairo_move_to (cr, nil, top); 364 | cairo_line_to (cr, nil, bottom); 365 | cairo_stroke (cr); 366 | } 367 | } 368 | if (where == GOAT_POSITION_TOP) { 369 | if (!get_unit_to_pixel_factor (right - left, scale_min, scale_max, &factor)) { 370 | g_warning ("Bad x range\n"); 371 | return; 372 | } 373 | nil = scale_min * -factor; 374 | 375 | start = (left - nil) / step_minor / factor; 376 | end = (right - nil) / step_minor / factor; 377 | 378 | for (i = start; i <= end; i++) { 379 | const gboolean register majorstip = is_major_tick (i, priv->minors_per_major); 380 | const double register x = nil + left + step_minor * factor * i; 381 | 382 | if (x < left || x > right) 383 | continue; 384 | 385 | if (grid) { 386 | cairo_move_to (cr, x, bottom); 387 | cairo_line_to (cr, x, top); 388 | if (majorstip) { 389 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 390 | } else { 391 | gdk_cairo_set_source_rgba (cr, &color_minor_grid); 392 | } 393 | cairo_stroke (cr); 394 | } 395 | cairo_move_to (cr, x, bottom); 396 | if (majorstip) { 397 | cairo_line_to (cr, x, bottom + width_major); 398 | gdk_cairo_set_source_rgba (cr, &color_major); 399 | } else { 400 | cairo_line_to (cr, x, bottom + width_minor); 401 | gdk_cairo_set_source_rgba (cr, &color_minor); 402 | } 403 | cairo_stroke (cr); 404 | 405 | if (majorstip) { 406 | goat_util_draw_num (cr, x, bottom + width_major, step_minor * i, where); 407 | } 408 | } 409 | /* Zero axis */ 410 | if (nil <= right && nil >= left) { 411 | cairo_set_line_width (cr, 1.); 412 | cairo_set_source_rgba (cr, 0.7, 0., 0., 1.); 413 | cairo_move_to (cr, nil, top); 414 | cairo_line_to (cr, nil, bottom); 415 | cairo_stroke (cr); 416 | } 417 | } 418 | } 419 | 420 | static void set_range_auto (GoatScale *scale) 421 | { 422 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (scale); 423 | 424 | self->priv->min = +G_MAXDOUBLE; 425 | self->priv->max = -G_MAXDOUBLE; 426 | self->priv->autorange = TRUE; 427 | } 428 | 429 | /** 430 | * used for goat plot to rescale depending on data 431 | */ 432 | static void update_range (GoatScale *scale, gdouble min, gdouble max) 433 | { 434 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (scale); 435 | 436 | self->priv->min = min; 437 | self->priv->max = max; 438 | } 439 | 440 | static void set_range (GoatScale *scale, gdouble min, gdouble max) 441 | { 442 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (scale); 443 | 444 | self->priv->autorange = FALSE; 445 | self->priv->min = min; 446 | self->priv->max = max; 447 | } 448 | 449 | static void get_range (GoatScale *scale, gdouble *min, gdouble *max) 450 | { 451 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (scale); 452 | 453 | if (min) { 454 | *min = self->priv->min; 455 | } 456 | if (max) { 457 | *max = self->priv->max; 458 | } 459 | } 460 | 461 | static void set_auto_range (GoatScale *scale) 462 | { 463 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (scale); 464 | 465 | self->priv->autorange = TRUE; 466 | } 467 | 468 | static gboolean is_auto_range (GoatScale *scale) 469 | { 470 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (scale); 471 | 472 | return self->priv->autorange; 473 | } 474 | 475 | static void show_grid (GoatScale *scale, gboolean show) 476 | { 477 | GoatScaleLinear *self = GOAT_SCALE_LINEAR (scale); 478 | 479 | self->priv->draw_grid = show; 480 | } 481 | 482 | 483 | static GoatPosition get_position (GoatScale *self) 484 | { 485 | g_return_val_if_fail(self != NULL, GOAT_POSITION_INVALID); 486 | return GOAT_SCALE_LINEAR(self)->priv->position; 487 | } 488 | 489 | static GoatOrientation get_orientation (GoatScale *self) 490 | { 491 | g_return_val_if_fail(self != NULL, GOAT_ORIENTATION_INVALID); 492 | return GOAT_SCALE_LINEAR(self)->priv->orientation; 493 | } 494 | 495 | static void set_position (GoatScale *self, GoatPosition position) 496 | { 497 | g_return_if_fail(self != NULL); 498 | GOAT_SCALE_LINEAR(self)->priv->position = position; 499 | } 500 | 501 | static void set_orientation (GoatScale *self, GoatOrientation orientation) 502 | { 503 | g_return_if_fail(self != NULL); 504 | GOAT_SCALE_LINEAR(self)->priv->orientation = orientation; 505 | } 506 | 507 | 508 | static void goat_scale_linear_interface_init (GoatScaleInterface *iface) 509 | { 510 | iface->draw = draw; 511 | iface->render = NULL; 512 | iface->set_auto_range = set_auto_range; 513 | iface->set_range = set_range; 514 | iface->set_range_auto = set_range_auto; 515 | iface->update_range = update_range; 516 | iface->is_auto_range = is_auto_range; 517 | iface->get_range = get_range; 518 | iface->show_grid = show_grid; 519 | iface->set_position = set_position; 520 | iface->get_position = get_position; 521 | iface->set_orientation = set_orientation; 522 | iface->get_orientation = get_orientation; 523 | } -------------------------------------------------------------------------------- /src/goat-scale-linear.h: -------------------------------------------------------------------------------- 1 | #ifndef __GOAT_SCALE_LINEAR_H__ 2 | #define __GOAT_SCALE_LINEAR_H__ 3 | 4 | #include "goat-plot-enum.h" 5 | #include "goat-scale-interface.h" 6 | #include 7 | 8 | G_BEGIN_DECLS 9 | 10 | #define GOAT_TYPE_SCALE_LINEAR (goat_scale_linear_get_type ()) 11 | #define GOAT_SCALE_LINEAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE_SCALE_LINEAR, GoatScaleLinear)) 12 | #define GOAT_SCALE_LINEAR_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE_SCALE_LINEAR, GoatScaleLinear const)) 13 | #define GOAT_SCALE_LINEAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOAT_TYPE_SCALE_LINEAR, GoatScaleLinearClass)) 14 | #define GOAT_IS_SCALE_LINEAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOAT_TYPE_SCALE_LINEAR)) 15 | #define GOAT_IS_SCALE_LINEAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOAT_TYPE_SCALE_LINEAR)) 16 | #define GOAT_SCALE_LINEAR_GET_CLASS(obj) \ 17 | (G_TYPE_INSTANCE_GET_CLASS ((obj), GOAT_TYPE_SCALE_LINEAR, GoatScaleLinearClass)) 18 | 19 | typedef struct _GoatScaleLinear GoatScaleLinear; 20 | typedef struct _GoatScaleLinearClass GoatScaleLinearClass; 21 | typedef struct _GoatScaleLinearPrivate GoatScaleLinearPrivate; 22 | 23 | struct _GoatScaleLinear { 24 | GObject parent; 25 | 26 | GoatScaleLinearPrivate *priv; 27 | }; 28 | 29 | struct _GoatScaleLinearClass { 30 | GObjectClass parent_class; 31 | }; 32 | 33 | GType goat_scale_linear_get_type (void) G_GNUC_CONST; 34 | GoatScaleLinear *goat_scale_linear_new (GoatPosition position, GoatOrientation orientation); 35 | 36 | void goat_scale_linear_set_ticks (GoatScaleLinear *scale, gdouble major_step, gint minors_per_major); 37 | void goat_scale_linear_set_label (GoatScaleLinear *plot, gchar *label); 38 | void goat_scale_linear_set_position (GoatScaleLinear *scale, GoatPosition position); 39 | void goat_scale_linear_set_orientation (GoatScaleLinear *scale, GoatOrientation orientation); 40 | GoatPosition goat_scale_linear_get_position (GoatScaleLinear *scale); 41 | GoatOrientation goat_scale_linear_get_orientation (GoatScaleLinear *scale); 42 | 43 | G_END_DECLS 44 | 45 | #endif /* __GOAT_SCALE_H__ */ 46 | -------------------------------------------------------------------------------- /src/goat-scale-log.c: -------------------------------------------------------------------------------- 1 | #include "goat-scale-log.h" 2 | #include "goat-plot-enum.h" 3 | #include "goat-utils.h" 4 | 5 | struct _GoatScaleLogPrivate { 6 | gboolean scale_fixed; 7 | 8 | // scale bounds 9 | gdouble min, max; 10 | // automatically choose x_min, ... y_max according to the datasets 11 | gboolean autorange; 12 | 13 | // ticks probably move into GoatTicks/Scale object (much of this should be in parent object, only draw function 14 | // needs to be overriden - PLE 06/17) 15 | gdouble major_delta; 16 | gint minors_per_major; 17 | 18 | GdkRGBA color_major; 19 | GdkRGBA color_minor; 20 | 21 | GdkRGBA color_major_grid; 22 | GdkRGBA color_minor_grid; 23 | 24 | GdkRGBA color_background; 25 | GdkRGBA color_border; 26 | 27 | gint width_minor; 28 | gint width_major; 29 | 30 | gboolean draw_grid; 31 | 32 | GoatOrientation orientation; 33 | GoatPosition position; 34 | }; 35 | 36 | static void goat_scale_log_interface_init (GoatScaleInterface *iface); 37 | 38 | G_DEFINE_TYPE_WITH_CODE (GoatScaleLog, goat_scale_log, G_TYPE_OBJECT, 39 | G_IMPLEMENT_INTERFACE (GOAT_TYPE_SCALE, goat_scale_log_interface_init) 40 | G_ADD_PRIVATE (GoatScaleLog)) 41 | 42 | enum { 43 | PROP_0, 44 | 45 | PROP_GRID_VISIBLE, 46 | PROP_ORIENTATION, 47 | PROP_POSITION, 48 | 49 | N_PROPERTIES 50 | }; 51 | 52 | static GParamSpec *obj_properties[N_PROPERTIES] = { 53 | NULL, 54 | }; 55 | 56 | static void goat_scale_log_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) 57 | { 58 | GoatScaleLog *self = GOAT_SCALE_LOG (object); 59 | GoatScaleLogPrivate *priv = goat_scale_log_get_instance_private (self); 60 | 61 | switch (property_id) { 62 | case PROP_GRID_VISIBLE: 63 | priv->draw_grid = g_value_get_boolean (value); 64 | break; 65 | case PROP_ORIENTATION: 66 | priv->orientation = g_value_get_enum (value); 67 | break; 68 | case PROP_POSITION: 69 | priv->position = g_value_get_enum (value); 70 | break; 71 | default: 72 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 73 | break; 74 | } 75 | } 76 | 77 | static void goat_scale_log_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) 78 | { 79 | GoatScaleLog *self = GOAT_SCALE_LOG (object); 80 | GoatScaleLogPrivate *priv = goat_scale_log_get_instance_private (self); 81 | 82 | switch (property_id) { 83 | case PROP_GRID_VISIBLE: 84 | g_value_set_boolean (value, priv->draw_grid); 85 | break; 86 | case PROP_ORIENTATION: 87 | g_value_set_enum (value, priv->orientation); 88 | break; 89 | case PROP_POSITION: 90 | g_value_set_enum (value, priv->position); 91 | break; 92 | default: 93 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 94 | break; 95 | } 96 | } 97 | 98 | static void goat_scale_log_finalize (GObject *object) 99 | { 100 | G_OBJECT_CLASS (goat_scale_log_parent_class)->finalize (object); 101 | } 102 | 103 | static void goat_scale_log_class_init (GoatScaleLogClass *klass) 104 | { 105 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 106 | 107 | object_class->set_property = goat_scale_log_set_property; 108 | object_class->get_property = goat_scale_log_get_property; 109 | 110 | obj_properties[PROP_GRID_VISIBLE] = 111 | g_param_spec_boolean ("grid-visible", "Show the grid", "To show or not to show", FALSE, G_PARAM_READWRITE); 112 | 113 | obj_properties[PROP_ORIENTATION] = 114 | g_param_spec_enum ("orientation", "Set orientation property", "Set the orientation ot vertical of horizontal", 115 | GOAT_TYPE_ORIENTATION, GOAT_ORIENTATION_INVALID, G_PARAM_READWRITE); 116 | obj_properties[PROP_POSITION] = 117 | g_param_spec_enum ("position", "Set position property", "Set the position to left,right,top or bottom", 118 | GOAT_TYPE_POSITION, GOAT_POSITION_LEFT, G_PARAM_READWRITE); 119 | 120 | g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); 121 | 122 | object_class->finalize = goat_scale_log_finalize; 123 | } 124 | 125 | static void goat_scale_log_init (GoatScaleLog *self) 126 | { 127 | GoatScaleLogPrivate *priv = self->priv = goat_scale_log_get_instance_private (self); 128 | goat_scale_set_range_auto (GOAT_SCALE (self)); 129 | priv->draw_grid = TRUE; 130 | priv->minors_per_major = 4; 131 | priv->major_delta = 10.; 132 | priv->width_minor = 5; 133 | priv->width_major = 8; 134 | priv->color_major.red = 0.8; 135 | priv->color_major.green = 0.2; 136 | priv->color_major.blue = 0.2; 137 | priv->color_major.alpha = 1.0; 138 | priv->color_minor.red = 0.4; 139 | priv->color_minor.green = 0.4; 140 | priv->color_minor.blue = 0.4; 141 | priv->color_minor.alpha = 1.0; 142 | priv->color_minor_grid = priv->color_minor; 143 | priv->color_minor_grid.alpha = 0.3; 144 | priv->color_major_grid = priv->color_major; 145 | priv->color_major_grid.alpha = 0.3; 146 | priv->orientation = GOAT_ORIENTATION_INVALID; 147 | priv->position = GOAT_POSITION_INVALID; 148 | } 149 | 150 | GoatScaleLog *goat_scale_log_new (GoatPosition position, GoatOrientation orientation) 151 | { 152 | GoatPosition assure_pos; 153 | GoatOrientation assure_ori; 154 | GoatScaleLog *self = g_object_new (GOAT_TYPE_SCALE_LOG, "position", position, "orientation", orientation, NULL); 155 | g_object_get (G_OBJECT (self), "position", &assure_pos, "orientation", &assure_ori, NULL); 156 | g_assert (assure_pos == position); 157 | g_assert (assure_ori == orientation); 158 | return self; 159 | } 160 | 161 | void goat_scale_log_set_ticks (GoatScaleLog *scale, gdouble major, gint minors_per_major) 162 | { 163 | g_return_if_fail (major > 0.); 164 | g_return_if_fail (scale); 165 | g_return_if_fail (GOAT_IS_SCALE_LOG (scale)); 166 | 167 | GoatScaleLogPrivate *priv; 168 | 169 | priv = goat_scale_log_get_instance_private (scale); 170 | 171 | priv->major_delta = major; 172 | priv->minors_per_major = minors_per_major; 173 | } 174 | 175 | gdouble goat_scale_log_get_major_delta (GoatScaleLog *scale) 176 | { 177 | if (!scale || !GOAT_IS_SCALE_LOG (scale)) 178 | return 0.; 179 | 180 | GoatScaleLogPrivate *priv; 181 | 182 | priv = goat_scale_log_get_instance_private (scale); 183 | 184 | return priv->major_delta; 185 | } 186 | 187 | 188 | 189 | /** 190 | * @param x/y-nil in pixel (nil becomes void i 191 | * @param x/y-factor convert unit to pixel 192 | */ 193 | static void draw (GoatScale *scale, cairo_t *cr, gint left, gint right, gint top, gint bottom) 194 | { 195 | GoatScaleLog *self = GOAT_SCALE_LOG (scale); 196 | GoatScaleLogPrivate *priv = goat_scale_log_get_instance_private (self); 197 | double minor_steps[10]; 198 | 199 | int major_steps, major_spacing; 200 | 201 | gint register i, j; 202 | double register x, y; 203 | const gint width_major = priv->width_major; 204 | const gint width_minor = priv->width_minor; 205 | GdkRGBA color_minor = priv->color_minor; 206 | GdkRGBA color_major = priv->color_major; 207 | GdkRGBA color_minor_grid = priv->color_minor_grid; 208 | GdkRGBA color_major_grid = priv->color_major_grid; 209 | 210 | GoatPosition where = goat_scale_get_position(scale); 211 | gboolean grid = priv->draw_grid; 212 | 213 | /* Determine offset of minor grid / tick lines from linearly spaced majors */ 214 | gint minors_per_major = priv->minors_per_major + 1; 215 | 216 | double major_val = priv->min; 217 | double major_factor; 218 | 219 | if (minors_per_major > 11) 220 | minors_per_major = 11; 221 | 222 | for (i = 0; i < minors_per_major - 1; i++) { 223 | minor_steps[i] = 1. + log10 ((double)(i + 1) / minors_per_major); 224 | } 225 | 226 | minors_per_major -= 1; 227 | 228 | /* Determine location of major ticks */ 229 | major_steps = (int)(ceil (log10 (priv->max / priv->min) / 230 | priv->major_delta)); // major delta = 1 for steps of 10^1, 2 for steps of 10^2 etc 231 | major_factor = pow (10, priv->major_delta); 232 | 233 | cairo_set_line_width (cr, 1.); 234 | 235 | if (where == GOAT_POSITION_LEFT) { 236 | major_spacing = abs (top - bottom) / major_steps; 237 | 238 | for (i = 0; i < major_steps; i++) { 239 | y = top + i * major_spacing; 240 | 241 | /* Grid lines - major */ 242 | if (grid) { 243 | cairo_move_to (cr, right, y); 244 | cairo_line_to (cr, left, y); 245 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 246 | cairo_stroke (cr); 247 | } 248 | 249 | /* Major tick */ 250 | cairo_move_to (cr, left, y); 251 | cairo_line_to (cr, left - width_major, y); 252 | gdk_cairo_set_source_rgba (cr, &color_major); 253 | cairo_stroke (cr); 254 | 255 | /* Major numnber */ 256 | x = left - width_major; 257 | goat_util_draw_num (cr, x, y, major_val, where); 258 | major_val *= major_factor; 259 | 260 | x = left - width_minor; 261 | for (j = 0; j < minors_per_major; j++) { 262 | y = top + (i + minor_steps[j]) * major_spacing; 263 | 264 | /* Grid lines - minor over next major period */ 265 | if (grid) { 266 | cairo_move_to (cr, right, y); 267 | cairo_line_to (cr, left, y); 268 | gdk_cairo_set_source_rgba (cr, &color_minor_grid); 269 | cairo_stroke (cr); 270 | } 271 | 272 | /* Minor ticks over next period */ 273 | cairo_move_to (cr, left, y); 274 | cairo_line_to (cr, left - width_minor, y); 275 | gdk_cairo_set_source_rgba (cr, &color_minor); 276 | cairo_stroke (cr); 277 | } 278 | } 279 | 280 | /* Draw final major line & tick */ 281 | y = top + i * major_spacing; 282 | 283 | /* Grid line */ 284 | if (grid) { 285 | cairo_move_to (cr, right, y); 286 | cairo_line_to (cr, left, y); 287 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 288 | cairo_stroke (cr); 289 | } 290 | 291 | /* tick */ 292 | cairo_move_to (cr, left, y); 293 | cairo_line_to (cr, left - width_major, y); 294 | gdk_cairo_set_source_rgba (cr, &color_major); 295 | cairo_stroke (cr); 296 | 297 | /* Number */ 298 | x = left - width_major; 299 | goat_util_draw_num (cr, x, y, major_val, where); 300 | } 301 | if (where == GOAT_POSITION_RIGHT) { 302 | major_spacing = abs (top - bottom) / major_steps; 303 | 304 | for (i = 0; i < major_steps; i++) { 305 | y = top + i * major_spacing; 306 | 307 | /* Grid lines - major */ 308 | if (grid) { 309 | cairo_move_to (cr, right, y); 310 | cairo_line_to (cr, left, y); 311 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 312 | cairo_stroke (cr); 313 | } 314 | 315 | /* Major tick */ 316 | cairo_move_to (cr, right, y); 317 | cairo_line_to (cr, right + width_major, y); 318 | gdk_cairo_set_source_rgba (cr, &color_major); 319 | cairo_stroke (cr); 320 | 321 | /* Major numnber */ 322 | x = right + width_major; 323 | goat_util_draw_num (cr, x, y, major_val, where); 324 | major_val *= major_factor; 325 | 326 | x = right + width_minor; 327 | for (j = 0; j < minors_per_major; j++) { 328 | y = top + (i + minor_steps[j]) * major_spacing; 329 | 330 | /* Grid lines - minor over next major period */ 331 | if (grid) { 332 | cairo_move_to (cr, right, y); 333 | cairo_line_to (cr, left, y); 334 | gdk_cairo_set_source_rgba (cr, &color_minor_grid); 335 | cairo_stroke (cr); 336 | } 337 | 338 | /* Minor ticks over next period */ 339 | cairo_move_to (cr, right, y); 340 | cairo_line_to (cr, right + width_minor, y); 341 | gdk_cairo_set_source_rgba (cr, &color_minor); 342 | cairo_stroke (cr); 343 | } 344 | } 345 | 346 | /* Draw final major line & tick */ 347 | y = top + i * major_spacing; 348 | 349 | /* Grid line */ 350 | if (grid) { 351 | cairo_move_to (cr, right, y); 352 | cairo_line_to (cr, left, y); 353 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 354 | cairo_stroke (cr); 355 | } 356 | 357 | /* tick */ 358 | cairo_move_to (cr, right, y); 359 | cairo_line_to (cr, right + width_major, y); 360 | gdk_cairo_set_source_rgba (cr, &color_major); 361 | cairo_stroke (cr); 362 | 363 | /* Number */ 364 | x = right + width_major; 365 | goat_util_draw_num (cr, x, y, major_val, where); 366 | major_val *= major_factor; 367 | } 368 | if (where == GOAT_POSITION_TOP) { 369 | major_spacing = abs (right - left) / major_steps; 370 | 371 | for (i = 0; i < major_steps; i++) { 372 | x = left + i * major_spacing; 373 | 374 | /* Grid lines - major */ 375 | if (grid) { 376 | cairo_move_to (cr, x, top); 377 | cairo_line_to (cr, x, bottom); 378 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 379 | cairo_stroke (cr); 380 | } 381 | 382 | /* Major tick */ 383 | cairo_move_to (cr, x, bottom); 384 | cairo_line_to (cr, x, bottom + width_major); 385 | gdk_cairo_set_source_rgba (cr, &color_major); 386 | cairo_stroke (cr); 387 | 388 | /* Major numnber */ 389 | y = bottom + width_major; 390 | goat_util_draw_num (cr, x, y, major_val, where); 391 | major_val *= major_factor; 392 | 393 | y = bottom + width_minor; 394 | for (j = 0; j < minors_per_major; j++) { 395 | x = left + (i + minor_steps[j]) * major_spacing; 396 | 397 | /* Grid lines - minor over next major period */ 398 | if (grid) { 399 | cairo_move_to (cr, x, top); 400 | cairo_line_to (cr, x, bottom); 401 | gdk_cairo_set_source_rgba (cr, &color_minor_grid); 402 | cairo_stroke (cr); 403 | } 404 | 405 | /* Minor ticks over next period */ 406 | cairo_move_to (cr, x, bottom); 407 | cairo_line_to (cr, x, bottom + width_minor); 408 | gdk_cairo_set_source_rgba (cr, &color_minor); 409 | cairo_stroke (cr); 410 | } 411 | } 412 | 413 | /* Draw final major line & tick */ 414 | x = left + i * major_spacing; 415 | 416 | /* Grid line */ 417 | if (grid) { 418 | cairo_move_to (cr, x, top); 419 | cairo_line_to (cr, x, bottom); 420 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 421 | cairo_stroke (cr); 422 | } 423 | 424 | /* tick */ 425 | cairo_move_to (cr, x, bottom); 426 | cairo_line_to (cr, x, bottom + width_major); 427 | gdk_cairo_set_source_rgba (cr, &color_major); 428 | cairo_stroke (cr); 429 | 430 | /* Number */ 431 | y = bottom + width_major; 432 | goat_util_draw_num (cr, x, y, major_val, where); 433 | major_val *= major_factor; 434 | } 435 | if (where == GOAT_POSITION_BOTTOM) { 436 | major_spacing = abs (right - left) / major_steps; 437 | 438 | for (i = 0; i < major_steps; i++) { 439 | x = left + i * major_spacing; 440 | 441 | /* Grid lines - major */ 442 | if (grid) { 443 | cairo_move_to (cr, x, top); 444 | cairo_line_to (cr, x, bottom); 445 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 446 | cairo_stroke (cr); 447 | } 448 | 449 | /* Major tick */ 450 | cairo_move_to (cr, x, top); 451 | cairo_line_to (cr, x, top - width_major); 452 | gdk_cairo_set_source_rgba (cr, &color_major); 453 | cairo_stroke (cr); 454 | 455 | /* Major numnber */ 456 | y = top - width_major; 457 | goat_util_draw_num (cr, x, y, major_val, where); 458 | major_val *= major_factor; 459 | 460 | y = top - width_minor; 461 | for (j = 0; j < minors_per_major; j++) { 462 | x = left + (i + minor_steps[j]) * major_spacing; 463 | 464 | /* Grid lines - minor over next major period */ 465 | if (grid) { 466 | cairo_move_to (cr, x, top); 467 | cairo_line_to (cr, x, bottom); 468 | gdk_cairo_set_source_rgba (cr, &color_minor_grid); 469 | cairo_stroke (cr); 470 | } 471 | 472 | /* Minor ticks over next period */ 473 | cairo_move_to (cr, x, top); 474 | cairo_line_to (cr, x, top - width_minor); 475 | gdk_cairo_set_source_rgba (cr, &color_minor); 476 | cairo_stroke (cr); 477 | } 478 | } 479 | 480 | /* Draw final major line & tick */ 481 | x = left + i * major_spacing; 482 | 483 | /* Grid line */ 484 | if (grid) { 485 | cairo_move_to (cr, x, top); 486 | cairo_line_to (cr, x, bottom); 487 | gdk_cairo_set_source_rgba (cr, &color_major_grid); 488 | cairo_stroke (cr); 489 | } 490 | 491 | /* tick */ 492 | cairo_move_to (cr, x, top); 493 | cairo_line_to (cr, x, top - width_major); 494 | gdk_cairo_set_source_rgba (cr, &color_major); 495 | cairo_stroke (cr); 496 | 497 | /* Number */ 498 | y = top - width_major; 499 | goat_util_draw_num (cr, x, y, major_val, where); 500 | major_val *= major_factor; 501 | } 502 | } 503 | 504 | static void set_range_auto (GoatScale *scale) 505 | { 506 | GoatScaleLog *self = GOAT_SCALE_LOG (scale); 507 | 508 | self->priv->min = +G_MAXDOUBLE; 509 | self->priv->max = -G_MAXDOUBLE; 510 | self->priv->autorange = TRUE; 511 | } 512 | 513 | /** 514 | * used for goat plot to rescale depending on data 515 | */ 516 | static void update_range (GoatScale *scale, gdouble min, gdouble max) 517 | { 518 | GoatScaleLog *self = GOAT_SCALE_LOG (scale); 519 | 520 | self->priv->min = min; 521 | self->priv->max = max; 522 | } 523 | 524 | static void set_range (GoatScale *scale, gdouble min, gdouble max) 525 | { 526 | GoatScaleLog *self = GOAT_SCALE_LOG (scale); 527 | 528 | self->priv->autorange = FALSE; 529 | self->priv->min = min; 530 | self->priv->max = max; 531 | } 532 | 533 | static void get_range (GoatScale *scale, gdouble *min, gdouble *max) 534 | { 535 | GoatScaleLog *self = GOAT_SCALE_LOG (scale); 536 | 537 | if (min) { 538 | *min = self->priv->min; 539 | } 540 | if (max) { 541 | *max = self->priv->max; 542 | } 543 | } 544 | 545 | static void set_auto_range (GoatScale *scale) 546 | { 547 | GoatScaleLog *self = GOAT_SCALE_LOG (scale); 548 | 549 | self->priv->autorange = TRUE; 550 | } 551 | 552 | static gboolean is_auto_range (GoatScale *scale) 553 | { 554 | GoatScaleLog *self = GOAT_SCALE_LOG (scale); 555 | 556 | return self->priv->autorange; 557 | } 558 | 559 | static void show_grid (GoatScale *scale, gboolean show) 560 | { 561 | GoatScaleLog *self = GOAT_SCALE_LOG (scale); 562 | 563 | self->priv->draw_grid = show; 564 | } 565 | 566 | static GoatPosition get_position (GoatScale *self) 567 | { 568 | g_return_val_if_fail(self != NULL, GOAT_POSITION_INVALID); 569 | return GOAT_SCALE_LOG(self)->priv->position; 570 | } 571 | 572 | static GoatOrientation get_orientation (GoatScale *self) 573 | { 574 | g_return_val_if_fail(self != NULL, GOAT_ORIENTATION_INVALID); 575 | return GOAT_SCALE_LOG(self)->priv->orientation; 576 | } 577 | 578 | static void set_position (GoatScale *self, GoatPosition position) 579 | { 580 | g_return_if_fail(self != NULL); 581 | GOAT_SCALE_LOG(self)->priv->position = position; 582 | } 583 | 584 | static void set_orientation (GoatScale *self, GoatOrientation orientation) 585 | { 586 | g_return_if_fail(self != NULL); 587 | GOAT_SCALE_LOG(self)->priv->orientation = orientation; 588 | } 589 | 590 | 591 | static void goat_scale_log_interface_init (GoatScaleInterface *iface) 592 | { 593 | iface->draw = draw; 594 | iface->render = NULL; 595 | iface->set_auto_range = set_auto_range; 596 | iface->set_range = set_range; 597 | iface->set_range_auto = set_range_auto; 598 | iface->update_range = update_range; 599 | iface->is_auto_range = is_auto_range; 600 | iface->get_range = get_range; 601 | iface->show_grid = show_grid; 602 | iface->set_position = set_position; 603 | iface->get_position = get_position; 604 | iface->set_orientation = set_orientation; 605 | iface->get_orientation = get_orientation; 606 | } -------------------------------------------------------------------------------- /src/goat-scale-log.h: -------------------------------------------------------------------------------- 1 | #ifndef __GOAT_SCALE_LOG_H__ 2 | #define __GOAT_SCALE_LOG_H__ 3 | 4 | #include "goat-plot-enum.h" 5 | #include "goat-scale-interface.h" 6 | #include 7 | 8 | G_BEGIN_DECLS 9 | 10 | #define GOAT_TYPE_SCALE_LOG (goat_scale_log_get_type ()) 11 | #define GOAT_SCALE_LOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE_SCALE_LOG, GoatScaleLog)) 12 | #define GOAT_SCALE_LOG_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOAT_TYPE_SCALE_LOG, GoatScaleLog const)) 13 | #define GOAT_SCALE_LOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOAT_TYPE_SCALE_LOG, GoatScaleLogClass)) 14 | #define GOAT_IS_SCALE_LOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOAT_TYPE_SCALE_LOG)) 15 | #define GOAT_IS_SCALE_LOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOAT_TYPE_SCALE_LOG)) 16 | #define GOAT_SCALE_LOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GOAT_TYPE_SCALE_LOG, GoatScaleLogClass)) 17 | 18 | typedef struct _GoatScaleLog GoatScaleLog; 19 | typedef struct _GoatScaleLogClass GoatScaleLogClass; 20 | typedef struct _GoatScaleLogPrivate GoatScaleLogPrivate; 21 | 22 | struct _GoatScaleLog { 23 | GObject parent; 24 | 25 | GoatScaleLogPrivate *priv; 26 | }; 27 | 28 | struct _GoatScaleLogClass { 29 | GObjectClass parent_class; 30 | }; 31 | 32 | GType goat_scale_log_get_type (void) G_GNUC_CONST; 33 | GoatScaleLog *goat_scale_log_new (GoatPosition position, GoatOrientation orientation); 34 | 35 | void goat_scale_log_set_ticks (GoatScaleLog *scale, gdouble major_step, gint minors_per_major); 36 | void goat_scale_log_set_label (GoatScaleLog *plot, gchar *label); 37 | gdouble goat_scale_log_get_major_delta (GoatScaleLog *scale); 38 | G_END_DECLS 39 | 40 | #endif /* __GOAT_SCALE_H__ */ 41 | -------------------------------------------------------------------------------- /src/goat-utils.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * draw a numer at a position 9 | */ 10 | void goat_util_draw_num (cairo_t *cr, double x, double y, double d, GoatPosition penalty) 11 | { 12 | cairo_save (cr); 13 | 14 | gchar *text = g_strdup_printf ("%.2g", d); 15 | g_assert (text); 16 | PangoLayout *lay = pango_cairo_create_layout (cr); 17 | PangoFontDescription *fontdesc = pango_font_description_new (); 18 | pango_font_description_set_size (fontdesc, 8 * PANGO_SCALE); 19 | pango_layout_set_font_description (lay, fontdesc); 20 | pango_layout_set_text (lay, text, -1); 21 | 22 | PangoRectangle logrect; 23 | pango_layout_get_pixel_extents (lay, NULL, &logrect); 24 | double modifierx, modifiery; 25 | switch (penalty) { 26 | case GOAT_POSITION_BOTTOM: 27 | modifierx = (double)(logrect.width) * -0.5; 28 | modifiery = (double)(logrect.height) * 0.; 29 | break; 30 | case GOAT_POSITION_LEFT: 31 | modifierx = (double)(logrect.width) * -1.0; 32 | modifiery = (double)(logrect.height) * 0.5; 33 | break; 34 | case GOAT_POSITION_RIGHT: 35 | modifierx = (double)(logrect.width) * 0.; 36 | modifiery = (double)(logrect.height) * 0.5; 37 | break; 38 | case GOAT_POSITION_TOP: 39 | modifierx = (double)(logrect.width) * -0.5; 40 | modifiery = (double)(logrect.height) * 1.0; 41 | break; 42 | case GOAT_POSITION_INVALID: 43 | default: 44 | g_warning ("Invalid Scale Position"); 45 | break; 46 | } 47 | cairo_move_to (cr, x + modifierx, y + modifiery); 48 | cairo_scale (cr, 1., -1.); 49 | 50 | pango_cairo_show_layout (cr, lay); 51 | g_object_unref (lay); 52 | g_free (text); 53 | pango_font_description_free (fontdesc); 54 | // restore the initial context properties 55 | cairo_restore (cr); 56 | } 57 | 58 | void goat_util_calc_num_extents (double d, int *width, int *height) 59 | { 60 | cairo_surface_t* surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, 100, 300 ); 61 | cairo_t* cr = cairo_create( surface ); 62 | PangoLayout *lay = pango_cairo_create_layout (cr); 63 | PangoFontDescription *fontdesc = pango_font_description_new (); 64 | pango_font_description_set_size (fontdesc, 8 * PANGO_SCALE); 65 | pango_layout_set_font_description (lay, fontdesc); 66 | 67 | gchar *text = g_strdup_printf ("%.2g", d); 68 | g_assert (text); 69 | pango_layout_set_text (lay, text, -1); 70 | 71 | pango_layout_get_pixel_size(lay, width, height); 72 | 73 | g_object_unref (lay); 74 | g_free (text); 75 | pango_font_description_free (fontdesc); 76 | 77 | cairo_surface_destroy(surface); 78 | cairo_destroy(cr); 79 | } 80 | 81 | /** 82 | * provides nice numerical limits when scaling an axis 83 | */ 84 | double goat_util_nice_num (double x, int round) 85 | { 86 | float exp, f, niced; 87 | float signx = (x >= 0) ? 1.f : -1.f; 88 | float absx = (float)x * signx; 89 | 90 | exp = floorf (log10f (absx)); 91 | f = absx / powf (10.f, exp); // bounded between 1 and 10 92 | if (round) { 93 | if (f < 1.5) 94 | niced = 1.; 95 | else if (f < 3.) 96 | niced = 2.; 97 | else if (f < 7.) 98 | niced = 5.; 99 | else 100 | niced = 10.; 101 | } else { 102 | if (f <= 1.) 103 | niced = 1.; 104 | else if (f <= 2.) 105 | niced = 2.; 106 | else if (f <= 5.) 107 | niced = 5.; 108 | else 109 | niced = 10.; 110 | } 111 | return signx * niced * powf (10.f, exp); 112 | } 113 | -------------------------------------------------------------------------------- /src/goat-utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __GOAT_UTILS_H__ 2 | #define __GOAT_UTILS_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * draw a numer at a position 10 | */ 11 | void goat_util_draw_num (cairo_t *cr, double x, double y, double d, GoatPosition penalty); 12 | 13 | 14 | /** 15 | * calculate the bounding box for @d 16 | */ 17 | void goat_util_calc_num_extents(double d, int* width, int* height); 18 | 19 | /** 20 | * provides nice numerical limits when scaling an axis 21 | */ 22 | double goat_util_nice_num (double x, int round); 23 | 24 | 25 | #endif /* __GOAT_UTILS_H__ */ 26 | -------------------------------------------------------------------------------- /src/goatplot.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBGOATPLOT 2 | #define LIBGOATPLOT 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #endif /* LIBGOATPLOT */ 13 | -------------------------------------------------------------------------------- /src/wscript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | def options(opt): 5 | opt.load('compiler_c gnu_dirs glib2') 6 | 7 | def configure(cfg): 8 | cfg.load('compiler_c gnu_dirs glib2') 9 | 10 | cfg.check_cc(lib='m', uselib_store='M', mandatory=True) 11 | 12 | cfg.check_cfg(atleast_pkgconfig_version='0.26') 13 | cfg.check_cfg(package='glib-2.0', uselib_store='GLIB', args=['glib-2.0 >= 2.24', '--cflags', '--libs'], mandatory=True) 14 | cfg.check_cfg(package='gobject-2.0', uselib_store='GOBJECT', args=['--cflags', '--libs'], mandatory=True) 15 | cfg.check_cfg(package='gtk+-3.0', uselib_store='GTK3', args=['--cflags', '--libs'], mandatory=True) 16 | 17 | cfg.check_large_file(mandatory=False) 18 | cfg.check_endianness(mandatory=False) 19 | cfg.check_inline(mandatory=False) 20 | 21 | 22 | def build(bld): 23 | objects = bld.objects( 24 | features = ['c', 'glib2'], 25 | target = 'objects', 26 | source = bld.path.ant_glob(['*.c'], excl='main.c'), 27 | includes = ['.'], 28 | export_includes = ['.'], 29 | cflags = ['-fPIC'], 30 | uselib = 'M GOBJECT GLIB GTK3' 31 | ) 32 | 33 | shlib = bld.shlib( 34 | features = ['c', 'cshlib', 'glib2'], 35 | target = bld.env.LIBNAME, 36 | source = [], 37 | use = 'objects', 38 | includes = ['.'], 39 | export_includes = ['.'], 40 | uselib = 'M GOBJECT GLIB GTK3', 41 | install_path = "${LIBDIR}" 42 | ) 43 | bld.install_files('${INCLUDEDIR}/goatplot/', bld.path.ant_glob(['*.h'], excl=['*-internal.h'])) 44 | 45 | 46 | 47 | from waflib.Context import Context 48 | 49 | def codestyle_fun(ctx): 50 | import os 51 | cmd = "clang-format -i -style=file {}".format(' '.join(map(lambda node: str(node.abspath()), ctx.path.ant_glob(['*.[ch]'])))) 52 | os.system(cmd) 53 | 54 | class codestyle(Context): 55 | """format code properly""" 56 | cmd = 'codestyle' 57 | fun = 'codestyle_fun' 58 | -------------------------------------------------------------------------------- /tests/dynamic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | typedef struct { 9 | GoatPlot *plot; 10 | GoatDatasetSimple *dataset; 11 | } Both; 12 | 13 | gboolean dynamic_add (Both *both) 14 | { 15 | static uint16_t idx = 0; 16 | idx++; 17 | double x = idx * 0.1; 18 | double y = cos (x + 0.5 * M_PI) * (sqrt (idx * idx * idx)); 19 | goat_dataset_simple_append (both->dataset, x, y, fabs(sin(x) * x * 20)); 20 | if (idx < 200) { 21 | gtk_widget_queue_draw (GTK_WIDGET (both->plot)); 22 | return G_SOURCE_CONTINUE; 23 | } 24 | // g_timeout_add (500, (GSourceFunc)gtk_main_quit, NULL); 25 | return G_SOURCE_REMOVE; 26 | } 27 | 28 | int main (int argc, char *argv[]) 29 | { 30 | GtkWidget *window; 31 | GoatPlot *plot; 32 | 33 | gtk_init (&argc, &argv); 34 | 35 | window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 36 | 37 | GoatScale *scale_x = GOAT_SCALE (goat_scale_linear_new (GOAT_POSITION_BOTTOM, GOAT_ORIENTATION_HORIZONTAL)); 38 | GoatScale *scale_y = GOAT_SCALE (goat_scale_linear_new (GOAT_POSITION_RIGHT, GOAT_ORIENTATION_VERTICAL)); 39 | 40 | goat_scale_linear_set_ticks (GOAT_SCALE_LINEAR (scale_y), 100, 2); 41 | 42 | plot = goat_plot_new (scale_x, scale_y); 43 | 44 | GoatDatasetSimple *dataset; 45 | 46 | GdkRGBA color1, color2, color3; 47 | gdk_rgba_parse(&color1, "black"); 48 | gdk_rgba_parse(&color2, "green"); 49 | gdk_rgba_parse(&color3, "red"); 50 | 51 | dataset = goat_dataset_simple_new (NULL, TRUE, TRUE); 52 | goat_dataset_simple_set_style (dataset, GOAT_MARKER_STYLE_TRIANGLE); 53 | goat_dataset_simple_set_color(dataset, &color1); 54 | goat_dataset_simple_set_marker_line_color(dataset, &color2); 55 | goat_dataset_simple_set_marker_fill_color(dataset, &color3); 56 | goat_plot_add_dataset (plot, GOAT_DATASET (dataset)); 57 | 58 | gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (plot)); 59 | gtk_widget_show_all (window); 60 | g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (gtk_main_quit), NULL); 61 | 62 | Both both; 63 | both.plot = plot; 64 | both.dataset = dataset; 65 | g_timeout_add (10, (GSourceFunc)dynamic_add, &both); 66 | 67 | gtk_main (); 68 | 69 | return EXIT_SUCCESS; 70 | } 71 | -------------------------------------------------------------------------------- /tests/glade-line.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.c 3 | * @date Apr 20, 2016 4 | * @author beattie 5 | * 6 | * @brief file contents 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | typedef struct { 20 | GoatPlot *plot; 21 | GoatDatasetSimple *dataset; 22 | } Both; 23 | 24 | gboolean dynamic_add (Both *both) 25 | { 26 | static uint16_t idx = 0; 27 | double x = 0; 28 | double y = 0; 29 | 30 | if (idx > 100) { 31 | g_timeout_add (500, (GSourceFunc)gtk_main_quit, NULL); 32 | return 0; 33 | } 34 | idx++; 35 | x = idx * 0.1; 36 | y = cos (x + 0.5 * M_PI) * (sqrt (0.01 * idx * idx * idx)); 37 | goat_dataset_simple_append (both->dataset, x, y, (x + 1) / (y + 1)); 38 | gtk_widget_queue_draw (GTK_WIDGET (both->plot)); 39 | return 1; 40 | } 41 | 42 | int main (int argc, char *argv[]) 43 | { 44 | GtkBuilder *builder; 45 | GtkWidget *window; 46 | GoatPlot *plot; 47 | GoatDatasetSimple *dataset; 48 | Both both; 49 | GdkRGBA datasetColor; 50 | 51 | gtk_init (&argc, &argv); 52 | 53 | #if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 10 54 | if ((builder = gtk_builder_new_from_file ("../../tests/glade-line.glade")) == NULL) { 55 | fprintf (stderr, "gtk_builder_new failed\n"); 56 | exit (-1); 57 | } 58 | 59 | if ((window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"))) == NULL) { 60 | fprintf (stderr, "gtk_builder_get_object failed\n"); 61 | exit (-1); 62 | }; 63 | 64 | if ((plot = GOAT_PLOT (gtk_builder_get_object (builder, "GoatPlot"))) == NULL) { 65 | fprintf (stderr, "gtk_builder_get_object GoatPlot failed\n"); 66 | exit (-1); 67 | } 68 | 69 | GoatScale *scale_x = GOAT_SCALE (goat_scale_linear_new (GOAT_POSITION_BOTTOM, GOAT_ORIENTATION_HORIZONTAL)); 70 | GoatScale *scale_y = GOAT_SCALE (goat_scale_linear_new (GOAT_POSITION_LEFT, GOAT_ORIENTATION_VERTICAL)); 71 | 72 | goat_scale_set_range (scale_x, 0.0, 10.0); 73 | goat_scale_set_range (scale_y, -100.0, 100.0); 74 | 75 | goat_plot_set_scale_x (plot, scale_x); 76 | goat_plot_set_scale_y (plot, scale_y); 77 | 78 | gtk_widget_show_all (window); 79 | g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (gtk_main_quit), NULL); 80 | 81 | g_object_unref (builder); 82 | 83 | dataset = goat_dataset_simple_new (NULL, FALSE, FALSE); 84 | goat_dataset_simple_set_style (dataset, GOAT_MARKER_STYLE_SQUARE); 85 | gdk_rgba_parse (&datasetColor, "cyan"); 86 | goat_dataset_simple_set_color (dataset, &datasetColor); 87 | goat_plot_add_dataset (plot, GOAT_DATASET (dataset)); 88 | 89 | both.plot = plot; 90 | both.dataset = dataset; 91 | g_timeout_add (20, (GSourceFunc)dynamic_add, &both); 92 | 93 | gtk_main (); 94 | #else 95 | #warning "GTK version too old for glade-line test!" 96 | #endif 97 | return EXIT_SUCCESS; 98 | } 99 | 100 | // called when window is closed 101 | void on_window_main_destroy () 102 | { 103 | gtk_main_quit (); 104 | } 105 | -------------------------------------------------------------------------------- /tests/glade-line.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | False 8 | GoatPlot Line Plot Test 9 | 10 | 11 | True 12 | False 13 | vertical 14 | 15 | 16 | True 17 | False 18 | Line Plot Test 19 | 20 | 21 | False 22 | True 23 | 0 24 | 25 | 26 | 27 | 28 | 500 29 | 400 30 | True 31 | True 32 | 33 | 34 | False 35 | True 36 | 1 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/screenshot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | void destroy (GtkWidget *widget, gpointer data) 9 | { 10 | g_print ("\n\n>>>>> creating screenshot...\n"); 11 | { 12 | cairo_surface_t *surface; 13 | cairo_t *cr; 14 | GtkAllocation allocation; 15 | 16 | gtk_widget_get_allocation (widget, &allocation); 17 | surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); 18 | g_assert (surface); 19 | cr = cairo_create (surface); 20 | gtk_widget_draw (GTK_WIDGET (widget), cr); 21 | const cairo_status_t status = cairo_surface_write_to_png (surface, "./screenshot.png"); 22 | g_assert (status == CAIRO_STATUS_SUCCESS); 23 | cairo_surface_destroy (surface); 24 | cairo_destroy (cr); 25 | } 26 | g_print ("\n\n>>>>> exiting test-simple...\n"); 27 | gtk_main_quit (); 28 | } 29 | 30 | gboolean self_destruct (GtkWidget *widget) 31 | { 32 | destroy (widget, NULL); 33 | return G_SOURCE_REMOVE; 34 | } 35 | 36 | #define TEST_MULTIPLE 1 37 | 38 | int main (int argc, char *argv[]) 39 | { 40 | GtkWidget *window; 41 | GtkWidget *box; 42 | GoatPlot *plot, *plot_clone; 43 | GoatScale *scale_x, *scale_y, *scale_x_auto; 44 | int i; 45 | GdkRGBA color; 46 | 47 | gtk_init (&argc, &argv); 48 | 49 | window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 50 | box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); 51 | scale_x = GOAT_SCALE (goat_scale_log_new (GOAT_POSITION_TOP, GOAT_ORIENTATION_HORIZONTAL)); 52 | goat_scale_set_range (scale_x, -44., +30.); 53 | goat_scale_linear_set_ticks (GOAT_SCALE_LINEAR (scale_x), 100, 5); 54 | scale_y = GOAT_SCALE (goat_scale_linear_new (GOAT_POSITION_LEFT, GOAT_ORIENTATION_VERTICAL)); 55 | goat_scale_set_range_auto (scale_y); 56 | goat_scale_linear_set_ticks (GOAT_SCALE_LINEAR (scale_y), 50, 5); 57 | plot = goat_plot_new (scale_x, scale_y); 58 | 59 | scale_x_auto = GOAT_SCALE (goat_scale_linear_new (GOAT_POSITION_BOTTOM, GOAT_ORIENTATION_HORIZONTAL)); 60 | goat_scale_set_range (scale_x, -100., +3.2e10); 61 | plot_clone = goat_plot_new (scale_x_auto, scale_y); // yep, we can re-use those 62 | 63 | GList *list1 = NULL; 64 | #if TEST_MULTIPLE 65 | GList *list2 = NULL; 66 | GList *list3 = NULL; 67 | #endif 68 | for (i = -10; i < 20; i++) { 69 | GoatTriple *pair; 70 | 71 | pair = g_new (GoatTriple, 1); 72 | pair->x = i; 73 | pair->y = (double)i * ((double)i - 16.) - 4.; 74 | pair->ystddev = fabs (pair->y * pair->x * 2 + 1) / fabs (MAX (fabs (pair->x), fabs (pair->y))); 75 | list1 = g_list_prepend (list1, pair); 76 | #if TEST_MULTIPLE 77 | pair = g_new (GoatTriple, 1); 78 | pair->x = i; 79 | pair->y = sin (2 * M_PI / 64 * pow(3,i)) * 25; 80 | pair->ystddev = sqrt (pair->y * pair->x); 81 | list2 = g_list_prepend (list2, pair); 82 | 83 | pair = g_new (GoatTriple, 1); 84 | pair->x = i * 15.f; 85 | pair->y = sin (2 * M_PI / 64 * i + M_PI / 2) * 55; 86 | pair->ystddev = sqrt (fabs (log (fabs (pair->y * pair->x)) * pair->x)); 87 | list3 = g_list_prepend (list3, pair); 88 | #endif 89 | } 90 | GoatDatasetSimple *dataset; 91 | 92 | dataset = goat_dataset_simple_new (list1, FALSE, FALSE); 93 | goat_dataset_simple_set_style (dataset, GOAT_MARKER_STYLE_TRIANGLE); 94 | gdk_rgba_parse (&color, "royalblue"); 95 | goat_dataset_simple_set_color (dataset, &color); 96 | goat_plot_add_dataset (plot, GOAT_DATASET (dataset)); 97 | goat_plot_add_dataset (plot_clone, GOAT_DATASET (dataset)); 98 | #if TEST_MULTIPLE 99 | dataset = goat_dataset_simple_new (list2, TRUE, FALSE); 100 | goat_dataset_simple_set_style (dataset, GOAT_MARKER_STYLE_POINT); 101 | gdk_rgba_parse (&color, "green"); 102 | goat_dataset_simple_set_color (dataset, &color); 103 | goat_plot_add_dataset (plot, GOAT_DATASET (dataset)); 104 | 105 | dataset = goat_dataset_simple_new (list3, FALSE, TRUE); 106 | goat_dataset_simple_set_style (dataset, GOAT_MARKER_STYLE_SQUARE); 107 | gdk_rgba_parse (&color, "goldenrod"); 108 | goat_dataset_simple_set_color (dataset, &color); 109 | goat_plot_add_dataset (plot, GOAT_DATASET (dataset)); 110 | #endif 111 | 112 | gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (plot)); 113 | gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (plot_clone)); 114 | gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (box)); 115 | gtk_widget_show_all (window); 116 | g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (destroy), NULL); 117 | 118 | /* g_timeout_add (1000, (GSourceFunc)self_destruct, GTK_WIDGET (window)); */ 119 | 120 | gtk_main (); 121 | 122 | return EXIT_SUCCESS; 123 | } 124 | -------------------------------------------------------------------------------- /tests/wscript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | def options(opt): 5 | opt.load('compiler_c glib2') 6 | opt.load('unites', tooldir='.wafcustom') 7 | 8 | def configure(cfg): 9 | cfg.load('compiler_c glib2') 10 | cfg.load('unites', tooldir='.wafcustom') 11 | 12 | def build(bld): 13 | bld.load('compiler_c glib2') 14 | bld.load('unites', tooldir='.wafcustom') 15 | 16 | includes = ['../src'] 17 | 18 | bld.env.CFLAGS = ['-ggdb', '-Wall'] 19 | 20 | test_screenshot = bld.program( 21 | features = ['c', 'glib2', 'unites'], 22 | target = 'test-screenshot', 23 | source = ['screenshot.c'], 24 | includes = includes, 25 | export_includes = includes, 26 | use = 'objects', 27 | uselib = 'M GOBJECT GLIB GTK3', 28 | install_path = None 29 | ) 30 | 31 | test_dynamic = bld.program( 32 | features = ['c', 'glib2', 'unites'], 33 | target = 'test-dynamic', 34 | source = ['dynamic.c'], 35 | includes = includes, 36 | export_includes = includes, 37 | use = 'objects', 38 | uselib = 'M GOBJECT GLIB GTK3', 39 | install_path = None 40 | ) 41 | 42 | test_glade = bld.program( 43 | features = ['c', 'glib2', 'unites'], 44 | target = 'test-glade-line', 45 | source = ['glade-line.c'], 46 | includes = includes, 47 | export_includes = includes, 48 | use = 'objects', 49 | uselib = 'M GOBJECT GLIB GTK3', 50 | install_path = None 51 | ) 52 | 53 | 54 | from waflib.Context import Context 55 | 56 | def codestyle_fun(ctx): 57 | import os 58 | cmd = "clang-format -i -style=file {}".format(' '.join(map(lambda node: str(node.abspath()), ctx.path.ant_glob(['*.[ch]'])))) 59 | os.system(cmd) 60 | 61 | class codestyle(Context): 62 | """format code properly""" 63 | cmd = 'codestyle' 64 | fun = 'codestyle_fun' 65 | 66 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drahnr/goatplot/a946ebff57f4269a4c44ca516ab02dc9e7d59a53/waf -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | VERSION = '0.1.3' 5 | LIBNAME = 'goatplot' 6 | 7 | top = '.' 8 | out = 'build' 9 | 10 | import os 11 | from waflib import Logs as logs 12 | from waflib import Utils as utils 13 | 14 | recurse = ['src','catalog','meta','tests'] 15 | 16 | def options(opt): 17 | opt.recurse(recurse) 18 | opt.load('compiler_c gnu_dirs glib2') 19 | opt.load('unites', tooldir='.wafcustom') 20 | 21 | def configure(cfg): 22 | cfg.env.LIBNAME = LIBNAME 23 | cfg.env.VERSION = VERSION 24 | 25 | cfg.recurse(recurse) 26 | cfg.load('compiler_c gnu_dirs glib2') 27 | cfg.load('unites', tooldir='.wafcustom') 28 | 29 | cfg.define('VERSION', VERSION) 30 | cfg.define('GETTEXT_PACKAGE', LIBNAME) 31 | 32 | 33 | cfg.check_cc(lib='m', uselib_store='M', mandatory=True) 34 | 35 | cfg.check_cfg(atleast_pkgconfig_version='0.26') 36 | cfg.check_cfg(package='glib-2.0', uselib_store='GLIB', args=['glib-2.0 >= 2.24', '--cflags', '--libs'], mandatory=True) 37 | cfg.check_cfg(package='gobject-2.0', uselib_store='GOBJECT', args=['--cflags', '--libs'], mandatory=True) 38 | cfg.check_cfg(package='gtk+-3.0', uselib_store='GTK3', args=['--cflags', '--libs'], mandatory=True) 39 | 40 | cfg.check_large_file(mandatory=False) 41 | cfg.check_endianness(mandatory=False) 42 | cfg.check_inline(mandatory=False) 43 | 44 | # -ggdb vs -g -- http://stackoverflow.com/questions/668962 45 | cfg.setenv('debug', env=cfg.env.derive()) 46 | cfg.env.CFLAGS = ['-ggdb', '-Wall'] 47 | cfg.define('DEBUG',1) 48 | 49 | cfg.setenv('release', env=cfg.env.derive()) 50 | cfg.env.CFLAGS = ['-O2', '-Wall'] 51 | cfg.define('RELEASE',1) 52 | 53 | 54 | def dist(bld): 55 | bld.base_name = LIBNAME+'-'+VERSION 56 | bld.algo = 'tar.xz' 57 | bld.excl = ['.*', '*~','./build','*.'+bld.algo], 58 | bld.files = bld.path.ant_glob('**/wscript') 59 | 60 | 61 | 62 | def pre(bld): 63 | if bld.cmd != 'install': 64 | logs.info ('Variant: \'' + bld.variant + '\'') 65 | 66 | 67 | 68 | def build(bld): 69 | bld.load('compiler_c gnu_dirs glib2') 70 | bld.recurse(recurse) 71 | bld.add_pre_fun(pre) 72 | 73 | from waflib.Build import BuildContext 74 | 75 | class release(BuildContext): 76 | """compile release binary""" 77 | cmd = 'release' 78 | variant = 'release' 79 | 80 | class debug(BuildContext): 81 | """compile debug binary""" 82 | cmd = 'debug' 83 | variant = 'debug' 84 | 85 | from waflib.Context import Context 86 | 87 | def codestyle_fun(ctx): 88 | ctx.recurse(['src','tests']) 89 | 90 | class codestyle(Context): 91 | """format code properly""" 92 | cmd = 'codestyle' 93 | fun = 'codestyle_fun' 94 | 95 | 96 | 97 | from waflib.Context import Context 98 | 99 | def version_fun(ctx): 100 | print(VERSION) 101 | 102 | class version(Context): 103 | """get current goatplot version""" 104 | cmd = 'version' 105 | fun = 'version_fun' 106 | --------------------------------------------------------------------------------