├── .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 | [](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 | 
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 |
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 |
--------------------------------------------------------------------------------