Import solvespace_3.0.rc2+repack1-3.debian.tar.xz
authorRyan Pavlik <ryan@ryanpavlik.com>
Thu, 11 Feb 2021 21:06:33 +0000 (21:06 +0000)
committerRyan Pavlik <ryan@ryanpavlik.com>
Thu, 11 Feb 2021 21:06:33 +0000 (21:06 +0000)
[dgit import tarball solvespace 3.0.rc2+repack1-3 solvespace_3.0.rc2+repack1-3.debian.tar.xz]

36 files changed:
.gitlab-ci.yml [new file with mode: 0644]
changelog [new file with mode: 0644]
control [new file with mode: 0644]
copyright [new file with mode: 0644]
docs [new file with mode: 0644]
get_orig.sh [new file with mode: 0755]
libslvs1-dev.install [new file with mode: 0644]
libslvs1.install [new file with mode: 0644]
missing_sources/res/threejs/hammer-2.0.8.js [new file with mode: 0644]
patches/01_RemoveGitHash.patch [new file with mode: 0644]
patches/02_add_debian_pkg_version.patch [new file with mode: 0644]
patches/03_use_system_threejs.patch [new file with mode: 0644]
patches/04_use_system_unifont.patch [new file with mode: 0644]
patches/10_mimalloc_restrict_cpu_yield.patch [new file with mode: 0644]
patches/11_update_js_three_r111.patch [new file with mode: 0644]
patches/12_Remove-march-native-from-mimalloc-build.patch [new file with mode: 0644]
patches/series [new file with mode: 0644]
rules [new file with mode: 0755]
solvespace-cli.1 [new file with mode: 0644]
solvespace.1 [new file with mode: 0644]
solvespace.install [new file with mode: 0644]
solvespace.links [new file with mode: 0644]
solvespace.manpages [new file with mode: 0644]
solvespace.sharedmimeinfo [new file with mode: 0644]
source/format [new file with mode: 0644]
source/options [new file with mode: 0644]
tests/common [new file with mode: 0644]
tests/control [new file with mode: 0644]
tests/htmlmesh [new file with mode: 0755]
tests/stlmesh [new file with mode: 0755]
tests/surfaces [new file with mode: 0755]
tests/thumbnail [new file with mode: 0755]
tests/view [new file with mode: 0755]
tests/wireframe [new file with mode: 0755]
upstream/metadata [new file with mode: 0644]
watch [new file with mode: 0644]

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644 (file)
index 0000000..b9b8b88
--- /dev/null
@@ -0,0 +1,6 @@
+include:
+ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
+ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
+
+reprotest:
+  allow_failure: true
\ No newline at end of file
diff --git a/changelog b/changelog
new file mode 100644 (file)
index 0000000..a189355
--- /dev/null
+++ b/changelog
@@ -0,0 +1,146 @@
+solvespace (3.0.rc2+repack1-3) unstable; urgency=medium
+
+  * Add 12_Remove-march-native-from-mimalloc-build.patch:
+    fix buildd-discovered issue.
+  * Update patch metadata.
+
+ -- Ryan Pavlik <ryan@ryanpavlik.com>  Thu, 11 Feb 2021 15:06:33 -0600
+
+solvespace (3.0.rc2+repack1-2) unstable; urgency=medium
+
+  * Upload into unstable
+
+ -- Anton Gladky <gladk@debian.org>  Sat, 06 Feb 2021 20:04:57 +0100
+
+solvespace (3.0.rc2+repack1-2~exp1) experimental; urgency=medium
+
+  * Team upload.
+  [ Ryan Pavlik ]
+  * Enable LTO and OpenMP in build system
+
+  [ Michael R. Crusoe ]
+  * Remove erroneously added debian/salsa-ci.yml.
+
+ -- Michael R. Crusoe <crusoe@debian.org>  Sat, 23 Jan 2021 20:31:05 +0100
+
+solvespace (3.0.rc2+repack1-1) unstable; urgency=medium
+
+  * Team upload.
+
+  [ Ryan Pavlik ]
+  * New upstream version 3.0.rc2+repack1
+  * Adjust get_orig.sh script for RC2
+  * d/rules: Specify package version for CMake.
+  * Drop 20_use_system_flatbuffers: Upstream dep removed.
+  * Add 02_add_debian_pkg_version.patch to provide better
+    version ID to upstream
+  * Add 03_use_system_threejs.patch, update d/control to use
+    system-provided three.js
+  * Add 04_use_system_unifont.patch, update d/control to use
+    system unifont
+  * Add 10_mimalloc_restrict_cpu_yield.patch to fix mimalloc
+    armel build.
+  * Add 11_update_js_three_r111.patch to support three.js r111
+    as found in unstable, instead of old r76 bundled in tree.
+  * d/control
+    - Fix warning about substitution variable
+    - Update description to cover new operations
+    - Update version in Build-Depends for CMake
+    - Remove Build-Depends on libflatbuffers-dev.
+  * Add missing_sources for hammer js
+  * Update copyright file
+  * Package more docs
+  * Add manpage for solvespace-cli from notesalexp repo.
+  * Update solvespace manpage
+  * Ensure build flags are exported and used
+  * Add autopkgtests
+
+  [ Michael R. Crusoe ]
+  * Do not parse d/changelog (routine-update)
+  * Add salsa-ci file (routine-update)
+  * debian/upstream/metadata: mention upstream's GitHub repository
+  * debian/patches/03_use_system_threejs.patch: refreshed
+  * debian/patches/10_mimalloc_restrict_cpu_yield.patch: enhanced with better
+    atomic yield for SSE2 (if available) and better/added ops for arm64,
+    armel, and ppc64{el,}/powerpc. Closes: #980169
+  * Run the build-time tests as part of the build on amd64.
+
+ -- Michael R. Crusoe <crusoe@debian.org>  Sat, 23 Jan 2021 20:15:11 +0100
+
+solvespace (3.0.rc1+repack1-4) unstable; urgency=medium
+
+  * [3fa5dd1] Remove arm-patch
+
+ -- Anton Gladky <gladk@debian.org>  Fri, 15 Jan 2021 15:40:02 +0100
+
+solvespace (3.0.rc1+repack1-3) unstable; urgency=medium
+
+  * [fa2ba63] Use CXX for mimalloc compilation
+
+ -- Anton Gladky <gladk@debian.org>  Thu, 14 Jan 2021 23:41:14 +0100
+
+solvespace (3.0.rc1+repack1-2) unstable; urgency=medium
+
+  * [7192813] Update d/copyright
+  * [93f958e] Fix compilation on armel
+
+ -- Anton Gladky <gladk@debian.org>  Thu, 14 Jan 2021 19:41:26 +0100
+
+solvespace (3.0.rc1+repack1-1) unstable; urgency=medium
+
+  * [2c974c1] New upstream version 3.0.rc1+repack1
+  * [d29ee68] Update get_orig script
+  * [73cbc23] Refresh patches
+  * [d8e0c6c] Use gtk3. (Closes: #967751)
+  * [09453c9] Update watch file format version to 4.
+  * [70475f7] Set Standards-Version to 4.5.1
+
+ -- Anton Gladky <gladk@debian.org>  Tue, 12 Jan 2021 23:27:41 +0100
+
+solvespace (2.3+repack1-4) unstable; urgency=medium
+
+  * [f5f8b5c] Add link to the fixed by upstream issue
+  * [4ac4b71] Use secure URI in Homepage field.
+  * [ef3f5da] Bump debhelper from old 10 to 13.
+  * [047d9c4] Set debhelper-compat version in Build-Depends.
+  * [8b28a26] Set upstream metadata fields: Bug-Database, Bug-Submit.
+  * [fba85d0] Update Vcs-* headers from URL redirect.
+  * [ab740f9] Use canonical URL in Vcs-Git.
+  * [84a98ae] Replace libglu-dev by libglu1-mesa-dev in BD
+  * [fdf3c0d] Set Standards-version to 4.5.0. No changes
+  * [cefcb01] Set Rules-Requires-Root: no
+  * [9869a0d] Add .gitlab-ci.yml
+  * [53ab556] Install usr/share/pixmaps
+
+ -- Anton Gladky <gladk@debian.org>  Mon, 01 Jun 2020 22:17:17 +0200
+
+solvespace (2.3+repack1-3) unstable; urgency=medium
+
+  * [6d84f29] Set minimal cmake version to 3.1.0.
+  * [be690c9] Fix FTBFS with glibc 2.25. (Closes: #882167)
+  * [6c8697c] Set Standards-Version: 4.1.1
+  * [2c3d9bd] Set compat level 10
+  * [fb232b5] Set maximal hardening options
+
+ -- Anton Gladky <gladk@debian.org>  Wed, 22 Nov 2017 09:54:12 +0100
+
+solvespace (2.3+repack1-2) unstable; urgency=medium
+
+  * [0d4dc2b] Add missing dependency on libslvs1. (Closes: #856937)
+
+ -- Anton Gladky <gladk@debian.org>  Mon, 06 Mar 2017 20:59:20 +0100
+
+solvespace (2.3+repack1-1) unstable; urgency=medium
+
+  * [a7825d4] Add d/watch.
+  * [15facd8] Update get_orig.sh script.
+  * [4bcf775] New upstream version 2.3+repack1
+  * [5bfcd9a] Refresh patch.
+
+ -- Anton Gladky <gladk@debian.org>  Sat, 31 Dec 2016 09:54:59 +0100
+
+solvespace (2.1+repack2-1) unstable; urgency=medium
+
+  * Initial Release. (Closes: #797997)
+
+ -- Anton Gladky <gladk@debian.org>  Mon, 15 Aug 2016 22:31:44 +0200
diff --git a/control b/control
new file mode 100644 (file)
index 0000000..937c4f4
--- /dev/null
+++ b/control
@@ -0,0 +1,71 @@
+Source: solvespace
+Maintainer: Debian Science Maintainers <debian-science-maintainers@lists.alioth.debian.org>
+Uploaders: whitequark <whitequark@whitequark.org>,
+           Anton Gladky <gladk@debian.org>
+Section: graphics
+Priority: optional
+Build-Depends: cmake (>= 3.7.2),
+               debhelper-compat (= 13),
+               libfontconfig1-dev,
+               libgl-dev,
+               libglew-dev,
+               libglu1-mesa-dev,
+               libgtkmm-3.0-dev,
+               libjs-three,
+               libjson-c-dev,
+               libpangomm-1.4-dev,
+               libspnav-dev,
+               libpng-dev,
+               unifont,
+               zlib1g-dev
+Standards-Version: 4.5.1
+Vcs-Browser: https://salsa.debian.org/science-team/solvespace
+Vcs-Git: https://salsa.debian.org/science-team/solvespace.git
+Homepage: https://solvespace.com
+Rules-Requires-Root: no
+
+Package: solvespace
+Architecture: any
+Multi-Arch: foreign
+Depends: ${misc:Depends},
+         ${shlibs:Depends},
+         libjs-three,
+         unifont
+Description: Parametric 2d/3d CAD
+ SolveSpace is a parametric 2d/3d CAD program. Applications include:
+ .
+  * modeling 3d parts — draw with extrudes, revolves, helix creation,
+    and Boolean (union / difference / intersection) operations;
+  * modeling 2d parts — draw the part as a single section, and export DXF,
+    PDF, SVG; use 3d assembly to verify fit;
+  * 3d-printed parts — export the STL or other triangle mesh expected by
+    most 3d printers;
+  * preparing CAM data — export 2d vector art for a waterjet machine or
+    laser cutter; or generate STEP or STL, for import into third-party
+    CAM software for machining;
+  * mechanism design — use the constraint solver to simulate planar or
+    spatial linkages, with pin, ball, or slide joints;
+  * plane and solid geometry — replace hand-solved trigonometry and
+    spreadsheets with a live dimensioned drawing.
+
+Package: libslvs1
+Architecture: any
+Multi-Arch: same
+Section: libs
+Depends: ${misc:Depends},
+         ${shlibs:Depends}
+Description: SolveSpace geometric kernel
+ SolveSpace is a parametric 2d/3d CAD. libslvs contains the geometric
+ kernel of SolveSpace, built as a library.
+
+Package: libslvs1-dev
+Architecture: any
+Multi-Arch: same
+Section: libdevel
+Depends: ${misc:Depends},
+         libslvs1 (= ${binary:Version})
+Description: SolveSpace geometric kernel (development files)
+ SolveSpace is a parametric 2d/3d CAD. libslvs contains the geometric
+ kernel of SolveSpace, built as a library.
+ .
+ This package includes development files for libslvs.
diff --git a/copyright b/copyright
new file mode 100644 (file)
index 0000000..8229718
--- /dev/null
+++ b/copyright
@@ -0,0 +1,251 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: solvespace
+Source: https://github.com/solvespace/solvespace
+
+Files: *
+Copyright: 2015-2020, whitequark <whitequark@whitequark.org>
+  2015, 2016, Aleksey Egorov <anandamide@mail.ru>
+  2008-2013, Jonathan Westhues
+License: GPL-3.0+
+
+Files: cmake/*
+Copyright: 2008-2020, Jonathan Westhues and other authors
+License: GPL-3.0+
+
+Files: debian/*
+Copyright: 2021, Ryan Pavlik <ryan@ryanpavlik.com>
+  2016, 2017, 2020, 2021, Anton Gladky <gladk@debian.org>
+  2015, 2016, whitequark <whitequark@whitequark.org>
+License: GPL-3.0+
+
+Files: debian/solvespace-cli.1
+Copyright: 2016 Alexander Pozdnyakov <almipo@mail.ru>
+License: GPL-3.0+
+
+Files: debian/missing_sources/res/threejs/hammer-2.0.8.js
+  res/threejs/hammer-2.0.8.js.gz
+Copyright: 2016, Jorik Tangelder
+License: Expat
+
+Files: extlib/libdxfrw/*
+Copyright: 2011-2015, José F. Soriano (Rallaz) <rallazz@gmail.com>
+License: GPL-2.0+
+
+Files: extlib/mimalloc/*
+Copyright: 2018-2020, Microsoft Research
+  2019, Daan Leijen
+License: Expat
+
+Files: src/importidf.cpp
+Copyright: 2020, Paul Kahler.
+License: GPL-3.0+
+
+Files: src/polyline.cpp
+Copyright: 2016, M-Labs Ltd
+License: GPL-3.0+
+
+Files: src/ttf.cpp
+  src/ttf.h
+Copyright: 2016, whitequark, Peter Barfuss.
+License: GPL-3.0+
+
+Files: res/fonts/unicode.lff.gz
+Copyright: 2011-2012, José F. Soriano <rallazz@gmail.com>
+ 2011, Eugene Osintsev <osgene@omskelecom.ru>
+ 2002, Yoshimune Kobayashi <cfg47520@syd.odn.ne.jp>
+License: GPL-2.0+
+Comment: From LibreCAD, referenced explicitly by path.
+
+Files: test/Gentium-R.ttf
+Copyright: 2003-2011, SIL International
+License: OFL-1.1-RFN
+Comment: Reserved Font Names "Gentium" and "SIL". Used for test cases.
+
+Files: res/fonts/BitstreamVeraSans-Roman-builtin.ttf
+Copyright: 2003, Bitstream, Inc.
+License: other
+ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a
+ trademark of Bitstream, Inc.
+ .
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ the fonts accompanying this license (“Fonts”) and associated documentation files
+ (the “Font Software”), to reproduce and distribute the Font Software, including
+ without limitation the rights to use, copy, merge, publish, distribute, and/or
+ sell copies of the Font Software, and to permit persons to whom the Font
+ Software is furnished to do so, subject to the following conditions:
+ .
+ The above copyright and trademark notices and this permission notice shall be
+ included in all copies of one or more of the Font Software typefaces.
+ .
+ The Font Software may be modified, altered, or added to, and in particular the
+ designs of glyphs or characters in the Fonts may be modified and additional
+ glyphs or characters may be added to the Fonts, only if the fonts are renamed to
+ names not containing either the words “Bitstream” or the word “Vera”.
+ .
+ This License becomes null and void to the extent applicable to Fonts or Font
+ Software that has been modified and is distributed under the “Bitstream Vera”
+ names.
+ .
+ The Font Software may be sold as part of a larger software package but no copy
+ of one or more of the Font Software typefaces may be sold by itself.
+ .
+ THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR
+ OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT,
+ INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR
+ FROM OTHER DEALINGS IN THE FONT SOFTWARE.
+ .
+ Except as contained in this notice, the names of GNOME, the GNOME Foundation,
+ and Bitstream Inc., shall not be used in advertising or otherwise to promote the
+ sale, use or other dealings in this Font Software without prior written
+ authorization from the GNOME Foundation or Bitstream Inc., respectively. For
+ further information, contact: fonts at gnome dot org.
+
+License: Expat
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+License: GPL-2.0+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
+
+License: GPL-3.0+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
+
+License: OFL-1.1-RFN
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
+ This license is copied below, and is also available with a FAQ at:
+ http://scripts.sil.org/OFL
+ .
+ .
+ -----------------------------------------------------------
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+ -----------------------------------------------------------
+ .
+ PREAMBLE
+ The goals of the Open Font License (OFL) are to stimulate worldwide
+ development of collaborative font projects, to support the font creation
+ efforts of academic and linguistic communities, and to provide a free and
+ open framework in which fonts may be shared and improved in partnership
+ with others.
+ .
+ The OFL allows the licensed fonts to be used, studied, modified and
+ redistributed freely as long as they are not sold by themselves. The
+ fonts, including any derivative works, can be bundled, embedded,
+ redistributed and/or sold with any software provided that any reserved
+ names are not used by derivative works. The fonts and derivatives,
+ however, cannot be released under any other type of license. The
+ requirement for fonts to remain under this license does not apply
+ to any document created using the fonts or their derivatives.
+ .
+ DEFINITIONS
+ "Font Software" refers to the set of files released by the Copyright
+ Holder(s) under this license and clearly marked as such. This may
+ include source files, build scripts and documentation.
+ .
+ "Reserved Font Name" refers to any names specified as such after the
+ copyright statement(s).
+ .
+ "Original Version" refers to the collection of Font Software components as
+ distributed by the Copyright Holder(s).
+ .
+ "Modified Version" refers to any derivative made by adding to, deleting,
+ or substituting -- in part or in whole -- any of the components of the
+ Original Version, by changing formats or by porting the Font Software to a
+ new environment.
+ .
+ "Author" refers to any designer, engineer, programmer, technical
+ writer or other person who contributed to the Font Software.
+ .
+ PERMISSION & CONDITIONS
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
+ redistribute, and sell modified and unmodified copies of the Font
+ Software, subject to the following conditions:
+ .
+ 1) Neither the Font Software nor any of its individual components,
+ in Original or Modified Versions, may be sold by itself.
+ .
+ 2) Original or Modified Versions of the Font Software may be bundled,
+ redistributed and/or sold with any software, provided that each copy
+ contains the above copyright notice and this license. These can be
+ included either as stand-alone text files, human-readable headers or
+ in the appropriate machine-readable metadata fields within text or
+ binary files as long as those fields can be easily viewed by the user.
+ .
+ 3) No Modified Version of the Font Software may use the Reserved Font
+ Name(s) unless explicit written permission is granted by the corresponding
+ Copyright Holder. This restriction only applies to the primary font name as
+ presented to the users.
+ .
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+ Software shall not be used to promote, endorse or advertise any
+ Modified Version, except to acknowledge the contribution(s) of the
+ Copyright Holder(s) and the Author(s) or with their explicit written
+ permission.
+ .
+ 5) The Font Software, modified or unmodified, in part or in whole,
+ must be distributed entirely under this license, and must not be
+ distributed under any other license. The requirement for fonts to
+ remain under this license does not apply to any document created
+ using the Font Software.
+ .
+ TERMINATION
+ This license becomes null and void if any of the above conditions are
+ not met.
+ .
+ DISCLAIMER
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+ OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/docs b/docs
new file mode 100644 (file)
index 0000000..e48692b
--- /dev/null
+++ b/docs
@@ -0,0 +1,3 @@
+README.md
+CHANGELOG.md
+THIRD_PARTIES.txt
diff --git a/get_orig.sh b/get_orig.sh
new file mode 100755 (executable)
index 0000000..c9e598b
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+RELEASE_NAME=v3.0.rc2
+SUFFIX=+repack1
+
+UPSTREAM="https://github.com/solvespace/solvespace.git"
+git clone --depth=1 -b ${RELEASE_NAME} ${UPSTREAM}
+(
+    cd solvespace
+    git submodule init extlib/libdxfrw extlib/mimalloc
+    git submodule update
+)
+
+#rm -rf ./solvespace
+#cp -r ./solvespace_copy ./solvespace
+#cp -r ./solvespace ./solvespace_copy
+
+
+rm -rfv solvespace/extlib/freetype
+rm -rfv solvespace/extlib/libpng
+rm -rfv solvespace/extlib/zlib
+rm -rfv solvespace/extlib/si
+rm -rfv solvespace/extlib/angle
+rm -rfv solvespace/extlib/cairo
+rm -rfv solvespace/extlib/pixman
+rm -rfv solvespace/extlib/mimalloc/do*
+rm -rfv solvespace/extlib/mimalloc/bin
+rm -rfv solvespace/debian
+rm -rfv solvespace/.gi*
+rm -rfv solvespace/.travis
+rm -rfv solvespace/res/fonts/unifont.hex.gz
+rm -rfv solvespace/res/threejs/three-r76.js.gz
+
+tar cJf solvespace_${RELEASE_NAME}${SUFFIX}.tar.xz solvespace
diff --git a/libslvs1-dev.install b/libslvs1-dev.install
new file mode 100644 (file)
index 0000000..8de0fcd
--- /dev/null
@@ -0,0 +1,2 @@
+usr/include/slvs.h
+usr/lib/*/libslvs.so
diff --git a/libslvs1.install b/libslvs1.install
new file mode 100644 (file)
index 0000000..ffa6d6e
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/*/libslvs.so.*
diff --git a/missing_sources/res/threejs/hammer-2.0.8.js b/missing_sources/res/threejs/hammer-2.0.8.js
new file mode 100644 (file)
index 0000000..00d2965
--- /dev/null
@@ -0,0 +1,2643 @@
+/*! Hammer.JS - v2.0.7 - 2016-04-22
+ * http://hammerjs.github.io/
+ *
+ * Copyright (c) 2016 Jorik Tangelder;
+ * Licensed under the MIT license */
+(function(window, document, exportName, undefined) {
+  'use strict';
+
+var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
+var TEST_ELEMENT = document.createElement('div');
+
+var TYPE_FUNCTION = 'function';
+
+var round = Math.round;
+var abs = Math.abs;
+var now = Date.now;
+
+/**
+ * set a timeout with a given scope
+ * @param {Function} fn
+ * @param {Number} timeout
+ * @param {Object} context
+ * @returns {number}
+ */
+function setTimeoutContext(fn, timeout, context) {
+    return setTimeout(bindFn(fn, context), timeout);
+}
+
+/**
+ * if the argument is an array, we want to execute the fn on each entry
+ * if it aint an array we don't want to do a thing.
+ * this is used by all the methods that accept a single and array argument.
+ * @param {*|Array} arg
+ * @param {String} fn
+ * @param {Object} [context]
+ * @returns {Boolean}
+ */
+function invokeArrayArg(arg, fn, context) {
+    if (Array.isArray(arg)) {
+        each(arg, context[fn], context);
+        return true;
+    }
+    return false;
+}
+
+/**
+ * walk objects and arrays
+ * @param {Object} obj
+ * @param {Function} iterator
+ * @param {Object} context
+ */
+function each(obj, iterator, context) {
+    var i;
+
+    if (!obj) {
+        return;
+    }
+
+    if (obj.forEach) {
+        obj.forEach(iterator, context);
+    } else if (obj.length !== undefined) {
+        i = 0;
+        while (i < obj.length) {
+            iterator.call(context, obj[i], i, obj);
+            i++;
+        }
+    } else {
+        for (i in obj) {
+            obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
+        }
+    }
+}
+
+/**
+ * wrap a method with a deprecation warning and stack trace
+ * @param {Function} method
+ * @param {String} name
+ * @param {String} message
+ * @returns {Function} A new function wrapping the supplied method.
+ */
+function deprecate(method, name, message) {
+    var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
+    return function() {
+        var e = new Error('get-stack-trace');
+        var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
+            .replace(/^\s+at\s+/gm, '')
+            .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
+
+        var log = window.console && (window.console.warn || window.console.log);
+        if (log) {
+            log.call(window.console, deprecationMessage, stack);
+        }
+        return method.apply(this, arguments);
+    };
+}
+
+/**
+ * extend object.
+ * means that properties in dest will be overwritten by the ones in src.
+ * @param {Object} target
+ * @param {...Object} objects_to_assign
+ * @returns {Object} target
+ */
+var assign;
+if (typeof Object.assign !== 'function') {
+    assign = function assign(target) {
+        if (target === undefined || target === null) {
+            throw new TypeError('Cannot convert undefined or null to object');
+        }
+
+        var output = Object(target);
+        for (var index = 1; index < arguments.length; index++) {
+            var source = arguments[index];
+            if (source !== undefined && source !== null) {
+                for (var nextKey in source) {
+                    if (source.hasOwnProperty(nextKey)) {
+                        output[nextKey] = source[nextKey];
+                    }
+                }
+            }
+        }
+        return output;
+    };
+} else {
+    assign = Object.assign;
+}
+
+/**
+ * extend object.
+ * means that properties in dest will be overwritten by the ones in src.
+ * @param {Object} dest
+ * @param {Object} src
+ * @param {Boolean} [merge=false]
+ * @returns {Object} dest
+ */
+var extend = deprecate(function extend(dest, src, merge) {
+    var keys = Object.keys(src);
+    var i = 0;
+    while (i < keys.length) {
+        if (!merge || (merge && dest[keys[i]] === undefined)) {
+            dest[keys[i]] = src[keys[i]];
+        }
+        i++;
+    }
+    return dest;
+}, 'extend', 'Use `assign`.');
+
+/**
+ * merge the values from src in the dest.
+ * means that properties that exist in dest will not be overwritten by src
+ * @param {Object} dest
+ * @param {Object} src
+ * @returns {Object} dest
+ */
+var merge = deprecate(function merge(dest, src) {
+    return extend(dest, src, true);
+}, 'merge', 'Use `assign`.');
+
+/**
+ * simple class inheritance
+ * @param {Function} child
+ * @param {Function} base
+ * @param {Object} [properties]
+ */
+function inherit(child, base, properties) {
+    var baseP = base.prototype,
+        childP;
+
+    childP = child.prototype = Object.create(baseP);
+    childP.constructor = child;
+    childP._super = baseP;
+
+    if (properties) {
+        assign(childP, properties);
+    }
+}
+
+/**
+ * simple function bind
+ * @param {Function} fn
+ * @param {Object} context
+ * @returns {Function}
+ */
+function bindFn(fn, context) {
+    return function boundFn() {
+        return fn.apply(context, arguments);
+    };
+}
+
+/**
+ * let a boolean value also be a function that must return a boolean
+ * this first item in args will be used as the context
+ * @param {Boolean|Function} val
+ * @param {Array} [args]
+ * @returns {Boolean}
+ */
+function boolOrFn(val, args) {
+    if (typeof val == TYPE_FUNCTION) {
+        return val.apply(args ? args[0] || undefined : undefined, args);
+    }
+    return val;
+}
+
+/**
+ * use the val2 when val1 is undefined
+ * @param {*} val1
+ * @param {*} val2
+ * @returns {*}
+ */
+function ifUndefined(val1, val2) {
+    return (val1 === undefined) ? val2 : val1;
+}
+
+/**
+ * addEventListener with multiple events at once
+ * @param {EventTarget} target
+ * @param {String} types
+ * @param {Function} handler
+ */
+function addEventListeners(target, types, handler) {
+    each(splitStr(types), function(type) {
+        target.addEventListener(type, handler, false);
+    });
+}
+
+/**
+ * removeEventListener with multiple events at once
+ * @param {EventTarget} target
+ * @param {String} types
+ * @param {Function} handler
+ */
+function removeEventListeners(target, types, handler) {
+    each(splitStr(types), function(type) {
+        target.removeEventListener(type, handler, false);
+    });
+}
+
+/**
+ * find if a node is in the given parent
+ * @method hasParent
+ * @param {HTMLElement} node
+ * @param {HTMLElement} parent
+ * @return {Boolean} found
+ */
+function hasParent(node, parent) {
+    while (node) {
+        if (node == parent) {
+            return true;
+        }
+        node = node.parentNode;
+    }
+    return false;
+}
+
+/**
+ * small indexOf wrapper
+ * @param {String} str
+ * @param {String} find
+ * @returns {Boolean} found
+ */
+function inStr(str, find) {
+    return str.indexOf(find) > -1;
+}
+
+/**
+ * split string on whitespace
+ * @param {String} str
+ * @returns {Array} words
+ */
+function splitStr(str) {
+    return str.trim().split(/\s+/g);
+}
+
+/**
+ * find if a array contains the object using indexOf or a simple polyFill
+ * @param {Array} src
+ * @param {String} find
+ * @param {String} [findByKey]
+ * @return {Boolean|Number} false when not found, or the index
+ */
+function inArray(src, find, findByKey) {
+    if (src.indexOf && !findByKey) {
+        return src.indexOf(find);
+    } else {
+        var i = 0;
+        while (i < src.length) {
+            if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
+                return i;
+            }
+            i++;
+        }
+        return -1;
+    }
+}
+
+/**
+ * convert array-like objects to real arrays
+ * @param {Object} obj
+ * @returns {Array}
+ */
+function toArray(obj) {
+    return Array.prototype.slice.call(obj, 0);
+}
+
+/**
+ * unique array with objects based on a key (like 'id') or just by the array's value
+ * @param {Array} src [{id:1},{id:2},{id:1}]
+ * @param {String} [key]
+ * @param {Boolean} [sort=False]
+ * @returns {Array} [{id:1},{id:2}]
+ */
+function uniqueArray(src, key, sort) {
+    var results = [];
+    var values = [];
+    var i = 0;
+
+    while (i < src.length) {
+        var val = key ? src[i][key] : src[i];
+        if (inArray(values, val) < 0) {
+            results.push(src[i]);
+        }
+        values[i] = val;
+        i++;
+    }
+
+    if (sort) {
+        if (!key) {
+            results = results.sort();
+        } else {
+            results = results.sort(function sortUniqueArray(a, b) {
+                return a[key] > b[key];
+            });
+        }
+    }
+
+    return results;
+}
+
+/**
+ * get the prefixed property
+ * @param {Object} obj
+ * @param {String} property
+ * @returns {String|Undefined} prefixed
+ */
+function prefixed(obj, property) {
+    var prefix, prop;
+    var camelProp = property[0].toUpperCase() + property.slice(1);
+
+    var i = 0;
+    while (i < VENDOR_PREFIXES.length) {
+        prefix = VENDOR_PREFIXES[i];
+        prop = (prefix) ? prefix + camelProp : property;
+
+        if (prop in obj) {
+            return prop;
+        }
+        i++;
+    }
+    return undefined;
+}
+
+/**
+ * get a unique id
+ * @returns {number} uniqueId
+ */
+var _uniqueId = 1;
+function uniqueId() {
+    return _uniqueId++;
+}
+
+/**
+ * get the window object of an element
+ * @param {HTMLElement} element
+ * @returns {DocumentView|Window}
+ */
+function getWindowForElement(element) {
+    var doc = element.ownerDocument || element;
+    return (doc.defaultView || doc.parentWindow || window);
+}
+
+var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
+
+var SUPPORT_TOUCH = ('ontouchstart' in window);
+var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
+var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
+
+var INPUT_TYPE_TOUCH = 'touch';
+var INPUT_TYPE_PEN = 'pen';
+var INPUT_TYPE_MOUSE = 'mouse';
+var INPUT_TYPE_KINECT = 'kinect';
+
+var COMPUTE_INTERVAL = 25;
+
+var INPUT_START = 1;
+var INPUT_MOVE = 2;
+var INPUT_END = 4;
+var INPUT_CANCEL = 8;
+
+var DIRECTION_NONE = 1;
+var DIRECTION_LEFT = 2;
+var DIRECTION_RIGHT = 4;
+var DIRECTION_UP = 8;
+var DIRECTION_DOWN = 16;
+
+var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
+var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
+var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
+
+var PROPS_XY = ['x', 'y'];
+var PROPS_CLIENT_XY = ['clientX', 'clientY'];
+
+/**
+ * create new input type manager
+ * @param {Manager} manager
+ * @param {Function} callback
+ * @returns {Input}
+ * @constructor
+ */
+function Input(manager, callback) {
+    var self = this;
+    this.manager = manager;
+    this.callback = callback;
+    this.element = manager.element;
+    this.target = manager.options.inputTarget;
+
+    // smaller wrapper around the handler, for the scope and the enabled state of the manager,
+    // so when disabled the input events are completely bypassed.
+    this.domHandler = function(ev) {
+        if (boolOrFn(manager.options.enable, [manager])) {
+            self.handler(ev);
+        }
+    };
+
+    this.init();
+
+}
+
+Input.prototype = {
+    /**
+     * should handle the inputEvent data and trigger the callback
+     * @virtual
+     */
+    handler: function() { },
+
+    /**
+     * bind the events
+     */
+    init: function() {
+        this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
+        this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
+        this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
+    },
+
+    /**
+     * unbind the events
+     */
+    destroy: function() {
+        this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
+        this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
+        this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
+    }
+};
+
+/**
+ * create new input type manager
+ * called by the Manager constructor
+ * @param {Hammer} manager
+ * @returns {Input}
+ */
+function createInputInstance(manager) {
+    var Type;
+    var inputClass = manager.options.inputClass;
+
+    if (inputClass) {
+        Type = inputClass;
+    } else if (SUPPORT_POINTER_EVENTS) {
+        Type = PointerEventInput;
+    } else if (SUPPORT_ONLY_TOUCH) {
+        Type = TouchInput;
+    } else if (!SUPPORT_TOUCH) {
+        Type = MouseInput;
+    } else {
+        Type = TouchMouseInput;
+    }
+    return new (Type)(manager, inputHandler);
+}
+
+/**
+ * handle input events
+ * @param {Manager} manager
+ * @param {String} eventType
+ * @param {Object} input
+ */
+function inputHandler(manager, eventType, input) {
+    var pointersLen = input.pointers.length;
+    var changedPointersLen = input.changedPointers.length;
+    var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
+    var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
+
+    input.isFirst = !!isFirst;
+    input.isFinal = !!isFinal;
+
+    if (isFirst) {
+        manager.session = {};
+    }
+
+    // source event is the normalized value of the domEvents
+    // like 'touchstart, mouseup, pointerdown'
+    input.eventType = eventType;
+
+    // compute scale, rotation etc
+    computeInputData(manager, input);
+
+    // emit secret event
+    manager.emit('hammer.input', input);
+
+    manager.recognize(input);
+    manager.session.prevInput = input;
+}
+
+/**
+ * extend the data with some usable properties like scale, rotate, velocity etc
+ * @param {Object} manager
+ * @param {Object} input
+ */
+function computeInputData(manager, input) {
+    var session = manager.session;
+    var pointers = input.pointers;
+    var pointersLength = pointers.length;
+
+    // store the first input to calculate the distance and direction
+    if (!session.firstInput) {
+        session.firstInput = simpleCloneInputData(input);
+    }
+
+    // to compute scale and rotation we need to store the multiple touches
+    if (pointersLength > 1 && !session.firstMultiple) {
+        session.firstMultiple = simpleCloneInputData(input);
+    } else if (pointersLength === 1) {
+        session.firstMultiple = false;
+    }
+
+    var firstInput = session.firstInput;
+    var firstMultiple = session.firstMultiple;
+    var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
+
+    var center = input.center = getCenter(pointers);
+    input.timeStamp = now();
+    input.deltaTime = input.timeStamp - firstInput.timeStamp;
+
+    input.angle = getAngle(offsetCenter, center);
+    input.distance = getDistance(offsetCenter, center);
+
+    computeDeltaXY(session, input);
+    input.offsetDirection = getDirection(input.deltaX, input.deltaY);
+
+    var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
+    input.overallVelocityX = overallVelocity.x;
+    input.overallVelocityY = overallVelocity.y;
+    input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
+
+    input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
+    input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
+
+    input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
+        session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
+
+    computeIntervalInputData(session, input);
+
+    // find the correct target
+    var target = manager.element;
+    if (hasParent(input.srcEvent.target, target)) {
+        target = input.srcEvent.target;
+    }
+    input.target = target;
+}
+
+function computeDeltaXY(session, input) {
+    var center = input.center;
+    var offset = session.offsetDelta || {};
+    var prevDelta = session.prevDelta || {};
+    var prevInput = session.prevInput || {};
+
+    if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
+        prevDelta = session.prevDelta = {
+            x: prevInput.deltaX || 0,
+            y: prevInput.deltaY || 0
+        };
+
+        offset = session.offsetDelta = {
+            x: center.x,
+            y: center.y
+        };
+    }
+
+    input.deltaX = prevDelta.x + (center.x - offset.x);
+    input.deltaY = prevDelta.y + (center.y - offset.y);
+}
+
+/**
+ * velocity is calculated every x ms
+ * @param {Object} session
+ * @param {Object} input
+ */
+function computeIntervalInputData(session, input) {
+    var last = session.lastInterval || input,
+        deltaTime = input.timeStamp - last.timeStamp,
+        velocity, velocityX, velocityY, direction;
+
+    if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
+        var deltaX = input.deltaX - last.deltaX;
+        var deltaY = input.deltaY - last.deltaY;
+
+        var v = getVelocity(deltaTime, deltaX, deltaY);
+        velocityX = v.x;
+        velocityY = v.y;
+        velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
+        direction = getDirection(deltaX, deltaY);
+
+        session.lastInterval = input;
+    } else {
+        // use latest velocity info if it doesn't overtake a minimum period
+        velocity = last.velocity;
+        velocityX = last.velocityX;
+        velocityY = last.velocityY;
+        direction = last.direction;
+    }
+
+    input.velocity = velocity;
+    input.velocityX = velocityX;
+    input.velocityY = velocityY;
+    input.direction = direction;
+}
+
+/**
+ * create a simple clone from the input used for storage of firstInput and firstMultiple
+ * @param {Object} input
+ * @returns {Object} clonedInputData
+ */
+function simpleCloneInputData(input) {
+    // make a simple copy of the pointers because we will get a reference if we don't
+    // we only need clientXY for the calculations
+    var pointers = [];
+    var i = 0;
+    while (i < input.pointers.length) {
+        pointers[i] = {
+            clientX: round(input.pointers[i].clientX),
+            clientY: round(input.pointers[i].clientY)
+        };
+        i++;
+    }
+
+    return {
+        timeStamp: now(),
+        pointers: pointers,
+        center: getCenter(pointers),
+        deltaX: input.deltaX,
+        deltaY: input.deltaY
+    };
+}
+
+/**
+ * get the center of all the pointers
+ * @param {Array} pointers
+ * @return {Object} center contains `x` and `y` properties
+ */
+function getCenter(pointers) {
+    var pointersLength = pointers.length;
+
+    // no need to loop when only one touch
+    if (pointersLength === 1) {
+        return {
+            x: round(pointers[0].clientX),
+            y: round(pointers[0].clientY)
+        };
+    }
+
+    var x = 0, y = 0, i = 0;
+    while (i < pointersLength) {
+        x += pointers[i].clientX;
+        y += pointers[i].clientY;
+        i++;
+    }
+
+    return {
+        x: round(x / pointersLength),
+        y: round(y / pointersLength)
+    };
+}
+
+/**
+ * calculate the velocity between two points. unit is in px per ms.
+ * @param {Number} deltaTime
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Object} velocity `x` and `y`
+ */
+function getVelocity(deltaTime, x, y) {
+    return {
+        x: x / deltaTime || 0,
+        y: y / deltaTime || 0
+    };
+}
+
+/**
+ * get the direction between two points
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Number} direction
+ */
+function getDirection(x, y) {
+    if (x === y) {
+        return DIRECTION_NONE;
+    }
+
+    if (abs(x) >= abs(y)) {
+        return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
+    }
+    return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
+}
+
+/**
+ * calculate the absolute distance between two points
+ * @param {Object} p1 {x, y}
+ * @param {Object} p2 {x, y}
+ * @param {Array} [props] containing x and y keys
+ * @return {Number} distance
+ */
+function getDistance(p1, p2, props) {
+    if (!props) {
+        props = PROPS_XY;
+    }
+    var x = p2[props[0]] - p1[props[0]],
+        y = p2[props[1]] - p1[props[1]];
+
+    return Math.sqrt((x * x) + (y * y));
+}
+
+/**
+ * calculate the angle between two coordinates
+ * @param {Object} p1
+ * @param {Object} p2
+ * @param {Array} [props] containing x and y keys
+ * @return {Number} angle
+ */
+function getAngle(p1, p2, props) {
+    if (!props) {
+        props = PROPS_XY;
+    }
+    var x = p2[props[0]] - p1[props[0]],
+        y = p2[props[1]] - p1[props[1]];
+    return Math.atan2(y, x) * 180 / Math.PI;
+}
+
+/**
+ * calculate the rotation degrees between two pointersets
+ * @param {Array} start array of pointers
+ * @param {Array} end array of pointers
+ * @return {Number} rotation
+ */
+function getRotation(start, end) {
+    return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
+}
+
+/**
+ * calculate the scale factor between two pointersets
+ * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
+ * @param {Array} start array of pointers
+ * @param {Array} end array of pointers
+ * @return {Number} scale
+ */
+function getScale(start, end) {
+    return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
+}
+
+var MOUSE_INPUT_MAP = {
+    mousedown: INPUT_START,
+    mousemove: INPUT_MOVE,
+    mouseup: INPUT_END
+};
+
+var MOUSE_ELEMENT_EVENTS = 'mousedown';
+var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
+
+/**
+ * Mouse events input
+ * @constructor
+ * @extends Input
+ */
+function MouseInput() {
+    this.evEl = MOUSE_ELEMENT_EVENTS;
+    this.evWin = MOUSE_WINDOW_EVENTS;
+
+    this.pressed = false; // mousedown state
+
+    Input.apply(this, arguments);
+}
+
+inherit(MouseInput, Input, {
+    /**
+     * handle mouse events
+     * @param {Object} ev
+     */
+    handler: function MEhandler(ev) {
+        var eventType = MOUSE_INPUT_MAP[ev.type];
+
+        // on start we want to have the left mouse button down
+        if (eventType & INPUT_START && ev.button === 0) {
+            this.pressed = true;
+        }
+
+        if (eventType & INPUT_MOVE && ev.which !== 1) {
+            eventType = INPUT_END;
+        }
+
+        // mouse must be down
+        if (!this.pressed) {
+            return;
+        }
+
+        if (eventType & INPUT_END) {
+            this.pressed = false;
+        }
+
+        this.callback(this.manager, eventType, {
+            pointers: [ev],
+            changedPointers: [ev],
+            pointerType: INPUT_TYPE_MOUSE,
+            srcEvent: ev
+        });
+    }
+});
+
+var POINTER_INPUT_MAP = {
+    pointerdown: INPUT_START,
+    pointermove: INPUT_MOVE,
+    pointerup: INPUT_END,
+    pointercancel: INPUT_CANCEL,
+    pointerout: INPUT_CANCEL
+};
+
+// in IE10 the pointer types is defined as an enum
+var IE10_POINTER_TYPE_ENUM = {
+    2: INPUT_TYPE_TOUCH,
+    3: INPUT_TYPE_PEN,
+    4: INPUT_TYPE_MOUSE,
+    5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
+};
+
+var POINTER_ELEMENT_EVENTS = 'pointerdown';
+var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
+
+// IE10 has prefixed support, and case-sensitive
+if (window.MSPointerEvent && !window.PointerEvent) {
+    POINTER_ELEMENT_EVENTS = 'MSPointerDown';
+    POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
+}
+
+/**
+ * Pointer events input
+ * @constructor
+ * @extends Input
+ */
+function PointerEventInput() {
+    this.evEl = POINTER_ELEMENT_EVENTS;
+    this.evWin = POINTER_WINDOW_EVENTS;
+
+    Input.apply(this, arguments);
+
+    this.store = (this.manager.session.pointerEvents = []);
+}
+
+inherit(PointerEventInput, Input, {
+    /**
+     * handle mouse events
+     * @param {Object} ev
+     */
+    handler: function PEhandler(ev) {
+        var store = this.store;
+        var removePointer = false;
+
+        var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
+        var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
+        var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
+
+        var isTouch = (pointerType == INPUT_TYPE_TOUCH);
+
+        // get index of the event in the store
+        var storeIndex = inArray(store, ev.pointerId, 'pointerId');
+
+        // start and mouse must be down
+        if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
+            if (storeIndex < 0) {
+                store.push(ev);
+                storeIndex = store.length - 1;
+            }
+        } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
+            removePointer = true;
+        }
+
+        // it not found, so the pointer hasn't been down (so it's probably a hover)
+        if (storeIndex < 0) {
+            return;
+        }
+
+        // update the event in the store
+        store[storeIndex] = ev;
+
+        this.callback(this.manager, eventType, {
+            pointers: store,
+            changedPointers: [ev],
+            pointerType: pointerType,
+            srcEvent: ev
+        });
+
+        if (removePointer) {
+            // remove from the store
+            store.splice(storeIndex, 1);
+        }
+    }
+});
+
+var SINGLE_TOUCH_INPUT_MAP = {
+    touchstart: INPUT_START,
+    touchmove: INPUT_MOVE,
+    touchend: INPUT_END,
+    touchcancel: INPUT_CANCEL
+};
+
+var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
+var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
+
+/**
+ * Touch events input
+ * @constructor
+ * @extends Input
+ */
+function SingleTouchInput() {
+    this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
+    this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
+    this.started = false;
+
+    Input.apply(this, arguments);
+}
+
+inherit(SingleTouchInput, Input, {
+    handler: function TEhandler(ev) {
+        var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
+
+        // should we handle the touch events?
+        if (type === INPUT_START) {
+            this.started = true;
+        }
+
+        if (!this.started) {
+            return;
+        }
+
+        var touches = normalizeSingleTouches.call(this, ev, type);
+
+        // when done, reset the started state
+        if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
+            this.started = false;
+        }
+
+        this.callback(this.manager, type, {
+            pointers: touches[0],
+            changedPointers: touches[1],
+            pointerType: INPUT_TYPE_TOUCH,
+            srcEvent: ev
+        });
+    }
+});
+
+/**
+ * @this {TouchInput}
+ * @param {Object} ev
+ * @param {Number} type flag
+ * @returns {undefined|Array} [all, changed]
+ */
+function normalizeSingleTouches(ev, type) {
+    var all = toArray(ev.touches);
+    var changed = toArray(ev.changedTouches);
+
+    if (type & (INPUT_END | INPUT_CANCEL)) {
+        all = uniqueArray(all.concat(changed), 'identifier', true);
+    }
+
+    return [all, changed];
+}
+
+var TOUCH_INPUT_MAP = {
+    touchstart: INPUT_START,
+    touchmove: INPUT_MOVE,
+    touchend: INPUT_END,
+    touchcancel: INPUT_CANCEL
+};
+
+var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
+
+/**
+ * Multi-user touch events input
+ * @constructor
+ * @extends Input
+ */
+function TouchInput() {
+    this.evTarget = TOUCH_TARGET_EVENTS;
+    this.targetIds = {};
+
+    Input.apply(this, arguments);
+}
+
+inherit(TouchInput, Input, {
+    handler: function MTEhandler(ev) {
+        var type = TOUCH_INPUT_MAP[ev.type];
+        var touches = getTouches.call(this, ev, type);
+        if (!touches) {
+            return;
+        }
+
+        this.callback(this.manager, type, {
+            pointers: touches[0],
+            changedPointers: touches[1],
+            pointerType: INPUT_TYPE_TOUCH,
+            srcEvent: ev
+        });
+    }
+});
+
+/**
+ * @this {TouchInput}
+ * @param {Object} ev
+ * @param {Number} type flag
+ * @returns {undefined|Array} [all, changed]
+ */
+function getTouches(ev, type) {
+    var allTouches = toArray(ev.touches);
+    var targetIds = this.targetIds;
+
+    // when there is only one touch, the process can be simplified
+    if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
+        targetIds[allTouches[0].identifier] = true;
+        return [allTouches, allTouches];
+    }
+
+    var i,
+        targetTouches,
+        changedTouches = toArray(ev.changedTouches),
+        changedTargetTouches = [],
+        target = this.target;
+
+    // get target touches from touches
+    targetTouches = allTouches.filter(function(touch) {
+        return hasParent(touch.target, target);
+    });
+
+    // collect touches
+    if (type === INPUT_START) {
+        i = 0;
+        while (i < targetTouches.length) {
+            targetIds[targetTouches[i].identifier] = true;
+            i++;
+        }
+    }
+
+    // filter changed touches to only contain touches that exist in the collected target ids
+    i = 0;
+    while (i < changedTouches.length) {
+        if (targetIds[changedTouches[i].identifier]) {
+            changedTargetTouches.push(changedTouches[i]);
+        }
+
+        // cleanup removed touches
+        if (type & (INPUT_END | INPUT_CANCEL)) {
+            delete targetIds[changedTouches[i].identifier];
+        }
+        i++;
+    }
+
+    if (!changedTargetTouches.length) {
+        return;
+    }
+
+    return [
+        // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
+        uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
+        changedTargetTouches
+    ];
+}
+
+/**
+ * Combined touch and mouse input
+ *
+ * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
+ * This because touch devices also emit mouse events while doing a touch.
+ *
+ * @constructor
+ * @extends Input
+ */
+
+var DEDUP_TIMEOUT = 2500;
+var DEDUP_DISTANCE = 25;
+
+function TouchMouseInput() {
+    Input.apply(this, arguments);
+
+    var handler = bindFn(this.handler, this);
+    this.touch = new TouchInput(this.manager, handler);
+    this.mouse = new MouseInput(this.manager, handler);
+
+    this.primaryTouch = null;
+    this.lastTouches = [];
+}
+
+inherit(TouchMouseInput, Input, {
+    /**
+     * handle mouse and touch events
+     * @param {Hammer} manager
+     * @param {String} inputEvent
+     * @param {Object} inputData
+     */
+    handler: function TMEhandler(manager, inputEvent, inputData) {
+        var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
+            isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
+
+        if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
+            return;
+        }
+
+        // when we're in a touch event, record touches to  de-dupe synthetic mouse event
+        if (isTouch) {
+            recordTouches.call(this, inputEvent, inputData);
+        } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
+            return;
+        }
+
+        this.callback(manager, inputEvent, inputData);
+    },
+
+    /**
+     * remove the event listeners
+     */
+    destroy: function destroy() {
+        this.touch.destroy();
+        this.mouse.destroy();
+    }
+});
+
+function recordTouches(eventType, eventData) {
+    if (eventType & INPUT_START) {
+        this.primaryTouch = eventData.changedPointers[0].identifier;
+        setLastTouch.call(this, eventData);
+    } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
+        setLastTouch.call(this, eventData);
+    }
+}
+
+function setLastTouch(eventData) {
+    var touch = eventData.changedPointers[0];
+
+    if (touch.identifier === this.primaryTouch) {
+        var lastTouch = {x: touch.clientX, y: touch.clientY};
+        this.lastTouches.push(lastTouch);
+        var lts = this.lastTouches;
+        var removeLastTouch = function() {
+            var i = lts.indexOf(lastTouch);
+            if (i > -1) {
+                lts.splice(i, 1);
+            }
+        };
+        setTimeout(removeLastTouch, DEDUP_TIMEOUT);
+    }
+}
+
+function isSyntheticEvent(eventData) {
+    var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;
+    for (var i = 0; i < this.lastTouches.length; i++) {
+        var t = this.lastTouches[i];
+        var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
+        if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
+            return true;
+        }
+    }
+    return false;
+}
+
+var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
+var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
+
+// magical touchAction value
+var TOUCH_ACTION_COMPUTE = 'compute';
+var TOUCH_ACTION_AUTO = 'auto';
+var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
+var TOUCH_ACTION_NONE = 'none';
+var TOUCH_ACTION_PAN_X = 'pan-x';
+var TOUCH_ACTION_PAN_Y = 'pan-y';
+var TOUCH_ACTION_MAP = getTouchActionProps();
+
+/**
+ * Touch Action
+ * sets the touchAction property or uses the js alternative
+ * @param {Manager} manager
+ * @param {String} value
+ * @constructor
+ */
+function TouchAction(manager, value) {
+    this.manager = manager;
+    this.set(value);
+}
+
+TouchAction.prototype = {
+    /**
+     * set the touchAction value on the element or enable the polyfill
+     * @param {String} value
+     */
+    set: function(value) {
+        // find out the touch-action by the event handlers
+        if (value == TOUCH_ACTION_COMPUTE) {
+            value = this.compute();
+        }
+
+        if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
+            this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
+        }
+        this.actions = value.toLowerCase().trim();
+    },
+
+    /**
+     * just re-set the touchAction value
+     */
+    update: function() {
+        this.set(this.manager.options.touchAction);
+    },
+
+    /**
+     * compute the value for the touchAction property based on the recognizer's settings
+     * @returns {String} value
+     */
+    compute: function() {
+        var actions = [];
+        each(this.manager.recognizers, function(recognizer) {
+            if (boolOrFn(recognizer.options.enable, [recognizer])) {
+                actions = actions.concat(recognizer.getTouchAction());
+            }
+        });
+        return cleanTouchActions(actions.join(' '));
+    },
+
+    /**
+     * this method is called on each input cycle and provides the preventing of the browser behavior
+     * @param {Object} input
+     */
+    preventDefaults: function(input) {
+        var srcEvent = input.srcEvent;
+        var direction = input.offsetDirection;
+
+        // if the touch action did prevented once this session
+        if (this.manager.session.prevented) {
+            srcEvent.preventDefault();
+            return;
+        }
+
+        var actions = this.actions;
+        var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
+        var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
+        var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
+
+        if (hasNone) {
+            //do not prevent defaults if this is a tap gesture
+
+            var isTapPointer = input.pointers.length === 1;
+            var isTapMovement = input.distance < 2;
+            var isTapTouchTime = input.deltaTime < 250;
+
+            if (isTapPointer && isTapMovement && isTapTouchTime) {
+                return;
+            }
+        }
+
+        if (hasPanX && hasPanY) {
+            // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
+            return;
+        }
+
+        if (hasNone ||
+            (hasPanY && direction & DIRECTION_HORIZONTAL) ||
+            (hasPanX && direction & DIRECTION_VERTICAL)) {
+            return this.preventSrc(srcEvent);
+        }
+    },
+
+    /**
+     * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
+     * @param {Object} srcEvent
+     */
+    preventSrc: function(srcEvent) {
+        this.manager.session.prevented = true;
+        srcEvent.preventDefault();
+    }
+};
+
+/**
+ * when the touchActions are collected they are not a valid value, so we need to clean things up. *
+ * @param {String} actions
+ * @returns {*}
+ */
+function cleanTouchActions(actions) {
+    // none
+    if (inStr(actions, TOUCH_ACTION_NONE)) {
+        return TOUCH_ACTION_NONE;
+    }
+
+    var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
+    var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
+
+    // if both pan-x and pan-y are set (different recognizers
+    // for different directions, e.g. horizontal pan but vertical swipe?)
+    // we need none (as otherwise with pan-x pan-y combined none of these
+    // recognizers will work, since the browser would handle all panning
+    if (hasPanX && hasPanY) {
+        return TOUCH_ACTION_NONE;
+    }
+
+    // pan-x OR pan-y
+    if (hasPanX || hasPanY) {
+        return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
+    }
+
+    // manipulation
+    if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
+        return TOUCH_ACTION_MANIPULATION;
+    }
+
+    return TOUCH_ACTION_AUTO;
+}
+
+function getTouchActionProps() {
+    if (!NATIVE_TOUCH_ACTION) {
+        return false;
+    }
+    var touchMap = {};
+    var cssSupports = window.CSS && window.CSS.supports;
+    ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {
+
+        // If css.supports is not supported but there is native touch-action assume it supports
+        // all values. This is the case for IE 10 and 11.
+        touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
+    });
+    return touchMap;
+}
+
+/**
+ * Recognizer flow explained; *
+ * All recognizers have the initial state of POSSIBLE when a input session starts.
+ * The definition of a input session is from the first input until the last input, with all it's movement in it. *
+ * Example session for mouse-input: mousedown -> mousemove -> mouseup
+ *
+ * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
+ * which determines with state it should be.
+ *
+ * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
+ * POSSIBLE to give it another change on the next cycle.
+ *
+ *               Possible
+ *                  |
+ *            +-----+---------------+
+ *            |                     |
+ *      +-----+-----+               |
+ *      |           |               |
+ *   Failed      Cancelled          |
+ *                          +-------+------+
+ *                          |              |
+ *                      Recognized       Began
+ *                                         |
+ *                                      Changed
+ *                                         |
+ *                                  Ended/Recognized
+ */
+var STATE_POSSIBLE = 1;
+var STATE_BEGAN = 2;
+var STATE_CHANGED = 4;
+var STATE_ENDED = 8;
+var STATE_RECOGNIZED = STATE_ENDED;
+var STATE_CANCELLED = 16;
+var STATE_FAILED = 32;
+
+/**
+ * Recognizer
+ * Every recognizer needs to extend from this class.
+ * @constructor
+ * @param {Object} options
+ */
+function Recognizer(options) {
+    this.options = assign({}, this.defaults, options || {});
+
+    this.id = uniqueId();
+
+    this.manager = null;
+
+    // default is enable true
+    this.options.enable = ifUndefined(this.options.enable, true);
+
+    this.state = STATE_POSSIBLE;
+
+    this.simultaneous = {};
+    this.requireFail = [];
+}
+
+Recognizer.prototype = {
+    /**
+     * @virtual
+     * @type {Object}
+     */
+    defaults: {},
+
+    /**
+     * set options
+     * @param {Object} options
+     * @return {Recognizer}
+     */
+    set: function(options) {
+        assign(this.options, options);
+
+        // also update the touchAction, in case something changed about the directions/enabled state
+        this.manager && this.manager.touchAction.update();
+        return this;
+    },
+
+    /**
+     * recognize simultaneous with an other recognizer.
+     * @param {Recognizer} otherRecognizer
+     * @returns {Recognizer} this
+     */
+    recognizeWith: function(otherRecognizer) {
+        if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
+            return this;
+        }
+
+        var simultaneous = this.simultaneous;
+        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
+        if (!simultaneous[otherRecognizer.id]) {
+            simultaneous[otherRecognizer.id] = otherRecognizer;
+            otherRecognizer.recognizeWith(this);
+        }
+        return this;
+    },
+
+    /**
+     * drop the simultaneous link. it doesnt remove the link on the other recognizer.
+     * @param {Recognizer} otherRecognizer
+     * @returns {Recognizer} this
+     */
+    dropRecognizeWith: function(otherRecognizer) {
+        if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
+            return this;
+        }
+
+        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
+        delete this.simultaneous[otherRecognizer.id];
+        return this;
+    },
+
+    /**
+     * recognizer can only run when an other is failing
+     * @param {Recognizer} otherRecognizer
+     * @returns {Recognizer} this
+     */
+    requireFailure: function(otherRecognizer) {
+        if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
+            return this;
+        }
+
+        var requireFail = this.requireFail;
+        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
+        if (inArray(requireFail, otherRecognizer) === -1) {
+            requireFail.push(otherRecognizer);
+            otherRecognizer.requireFailure(this);
+        }
+        return this;
+    },
+
+    /**
+     * drop the requireFailure link. it does not remove the link on the other recognizer.
+     * @param {Recognizer} otherRecognizer
+     * @returns {Recognizer} this
+     */
+    dropRequireFailure: function(otherRecognizer) {
+        if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
+            return this;
+        }
+
+        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
+        var index = inArray(this.requireFail, otherRecognizer);
+        if (index > -1) {
+            this.requireFail.splice(index, 1);
+        }
+        return this;
+    },
+
+    /**
+     * has require failures boolean
+     * @returns {boolean}
+     */
+    hasRequireFailures: function() {
+        return this.requireFail.length > 0;
+    },
+
+    /**
+     * if the recognizer can recognize simultaneous with an other recognizer
+     * @param {Recognizer} otherRecognizer
+     * @returns {Boolean}
+     */
+    canRecognizeWith: function(otherRecognizer) {
+        return !!this.simultaneous[otherRecognizer.id];
+    },
+
+    /**
+     * You should use `tryEmit` instead of `emit` directly to check
+     * that all the needed recognizers has failed before emitting.
+     * @param {Object} input
+     */
+    emit: function(input) {
+        var self = this;
+        var state = this.state;
+
+        function emit(event) {
+            self.manager.emit(event, input);
+        }
+
+        // 'panstart' and 'panmove'
+        if (state < STATE_ENDED) {
+            emit(self.options.event + stateStr(state));
+        }
+
+        emit(self.options.event); // simple 'eventName' events
+
+        if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
+            emit(input.additionalEvent);
+        }
+
+        // panend and pancancel
+        if (state >= STATE_ENDED) {
+            emit(self.options.event + stateStr(state));
+        }
+    },
+
+    /**
+     * Check that all the require failure recognizers has failed,
+     * if true, it emits a gesture event,
+     * otherwise, setup the state to FAILED.
+     * @param {Object} input
+     */
+    tryEmit: function(input) {
+        if (this.canEmit()) {
+            return this.emit(input);
+        }
+        // it's failing anyway
+        this.state = STATE_FAILED;
+    },
+
+    /**
+     * can we emit?
+     * @returns {boolean}
+     */
+    canEmit: function() {
+        var i = 0;
+        while (i < this.requireFail.length) {
+            if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
+                return false;
+            }
+            i++;
+        }
+        return true;
+    },
+
+    /**
+     * update the recognizer
+     * @param {Object} inputData
+     */
+    recognize: function(inputData) {
+        // make a new copy of the inputData
+        // so we can change the inputData without messing up the other recognizers
+        var inputDataClone = assign({}, inputData);
+
+        // is is enabled and allow recognizing?
+        if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
+            this.reset();
+            this.state = STATE_FAILED;
+            return;
+        }
+
+        // reset when we've reached the end
+        if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
+            this.state = STATE_POSSIBLE;
+        }
+
+        this.state = this.process(inputDataClone);
+
+        // the recognizer has recognized a gesture
+        // so trigger an event
+        if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
+            this.tryEmit(inputDataClone);
+        }
+    },
+
+    /**
+     * return the state of the recognizer
+     * the actual recognizing happens in this method
+     * @virtual
+     * @param {Object} inputData
+     * @returns {Const} STATE
+     */
+    process: function(inputData) { }, // jshint ignore:line
+
+    /**
+     * return the preferred touch-action
+     * @virtual
+     * @returns {Array}
+     */
+    getTouchAction: function() { },
+
+    /**
+     * called when the gesture isn't allowed to recognize
+     * like when another is being recognized or it is disabled
+     * @virtual
+     */
+    reset: function() { }
+};
+
+/**
+ * get a usable string, used as event postfix
+ * @param {Const} state
+ * @returns {String} state
+ */
+function stateStr(state) {
+    if (state & STATE_CANCELLED) {
+        return 'cancel';
+    } else if (state & STATE_ENDED) {
+        return 'end';
+    } else if (state & STATE_CHANGED) {
+        return 'move';
+    } else if (state & STATE_BEGAN) {
+        return 'start';
+    }
+    return '';
+}
+
+/**
+ * direction cons to string
+ * @param {Const} direction
+ * @returns {String}
+ */
+function directionStr(direction) {
+    if (direction == DIRECTION_DOWN) {
+        return 'down';
+    } else if (direction == DIRECTION_UP) {
+        return 'up';
+    } else if (direction == DIRECTION_LEFT) {
+        return 'left';
+    } else if (direction == DIRECTION_RIGHT) {
+        return 'right';
+    }
+    return '';
+}
+
+/**
+ * get a recognizer by name if it is bound to a manager
+ * @param {Recognizer|String} otherRecognizer
+ * @param {Recognizer} recognizer
+ * @returns {Recognizer}
+ */
+function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
+    var manager = recognizer.manager;
+    if (manager) {
+        return manager.get(otherRecognizer);
+    }
+    return otherRecognizer;
+}
+
+/**
+ * This recognizer is just used as a base for the simple attribute recognizers.
+ * @constructor
+ * @extends Recognizer
+ */
+function AttrRecognizer() {
+    Recognizer.apply(this, arguments);
+}
+
+inherit(AttrRecognizer, Recognizer, {
+    /**
+     * @namespace
+     * @memberof AttrRecognizer
+     */
+    defaults: {
+        /**
+         * @type {Number}
+         * @default 1
+         */
+        pointers: 1
+    },
+
+    /**
+     * Used to check if it the recognizer receives valid input, like input.distance > 10.
+     * @memberof AttrRecognizer
+     * @param {Object} input
+     * @returns {Boolean} recognized
+     */
+    attrTest: function(input) {
+        var optionPointers = this.options.pointers;
+        return optionPointers === 0 || input.pointers.length === optionPointers;
+    },
+
+    /**
+     * Process the input and return the state for the recognizer
+     * @memberof AttrRecognizer
+     * @param {Object} input
+     * @returns {*} State
+     */
+    process: function(input) {
+        var state = this.state;
+        var eventType = input.eventType;
+
+        var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
+        var isValid = this.attrTest(input);
+
+        // on cancel input and we've recognized before, return STATE_CANCELLED
+        if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
+            return state | STATE_CANCELLED;
+        } else if (isRecognized || isValid) {
+            if (eventType & INPUT_END) {
+                return state | STATE_ENDED;
+            } else if (!(state & STATE_BEGAN)) {
+                return STATE_BEGAN;
+            }
+            return state | STATE_CHANGED;
+        }
+        return STATE_FAILED;
+    }
+});
+
+/**
+ * Pan
+ * Recognized when the pointer is down and moved in the allowed direction.
+ * @constructor
+ * @extends AttrRecognizer
+ */
+function PanRecognizer() {
+    AttrRecognizer.apply(this, arguments);
+
+    this.pX = null;
+    this.pY = null;
+}
+
+inherit(PanRecognizer, AttrRecognizer, {
+    /**
+     * @namespace
+     * @memberof PanRecognizer
+     */
+    defaults: {
+        event: 'pan',
+        threshold: 10,
+        pointers: 1,
+        direction: DIRECTION_ALL
+    },
+
+    getTouchAction: function() {
+        var direction = this.options.direction;
+        var actions = [];
+        if (direction & DIRECTION_HORIZONTAL) {
+            actions.push(TOUCH_ACTION_PAN_Y);
+        }
+        if (direction & DIRECTION_VERTICAL) {
+            actions.push(TOUCH_ACTION_PAN_X);
+        }
+        return actions;
+    },
+
+    directionTest: function(input) {
+        var options = this.options;
+        var hasMoved = true;
+        var distance = input.distance;
+        var direction = input.direction;
+        var x = input.deltaX;
+        var y = input.deltaY;
+
+        // lock to axis?
+        if (!(direction & options.direction)) {
+            if (options.direction & DIRECTION_HORIZONTAL) {
+                direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
+                hasMoved = x != this.pX;
+                distance = Math.abs(input.deltaX);
+            } else {
+                direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
+                hasMoved = y != this.pY;
+                distance = Math.abs(input.deltaY);
+            }
+        }
+        input.direction = direction;
+        return hasMoved && distance > options.threshold && direction & options.direction;
+    },
+
+    attrTest: function(input) {
+        return AttrRecognizer.prototype.attrTest.call(this, input) &&
+            (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
+    },
+
+    emit: function(input) {
+
+        this.pX = input.deltaX;
+        this.pY = input.deltaY;
+
+        var direction = directionStr(input.direction);
+
+        if (direction) {
+            input.additionalEvent = this.options.event + direction;
+        }
+        this._super.emit.call(this, input);
+    }
+});
+
+/**
+ * Pinch
+ * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
+ * @constructor
+ * @extends AttrRecognizer
+ */
+function PinchRecognizer() {
+    AttrRecognizer.apply(this, arguments);
+}
+
+inherit(PinchRecognizer, AttrRecognizer, {
+    /**
+     * @namespace
+     * @memberof PinchRecognizer
+     */
+    defaults: {
+        event: 'pinch',
+        threshold: 0,
+        pointers: 2
+    },
+
+    getTouchAction: function() {
+        return [TOUCH_ACTION_NONE];
+    },
+
+    attrTest: function(input) {
+        return this._super.attrTest.call(this, input) &&
+            (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
+    },
+
+    emit: function(input) {
+        if (input.scale !== 1) {
+            var inOut = input.scale < 1 ? 'in' : 'out';
+            input.additionalEvent = this.options.event + inOut;
+        }
+        this._super.emit.call(this, input);
+    }
+});
+
+/**
+ * Press
+ * Recognized when the pointer is down for x ms without any movement.
+ * @constructor
+ * @extends Recognizer
+ */
+function PressRecognizer() {
+    Recognizer.apply(this, arguments);
+
+    this._timer = null;
+    this._input = null;
+}
+
+inherit(PressRecognizer, Recognizer, {
+    /**
+     * @namespace
+     * @memberof PressRecognizer
+     */
+    defaults: {
+        event: 'press',
+        pointers: 1,
+        time: 251, // minimal time of the pointer to be pressed
+        threshold: 9 // a minimal movement is ok, but keep it low
+    },
+
+    getTouchAction: function() {
+        return [TOUCH_ACTION_AUTO];
+    },
+
+    process: function(input) {
+        var options = this.options;
+        var validPointers = input.pointers.length === options.pointers;
+        var validMovement = input.distance < options.threshold;
+        var validTime = input.deltaTime > options.time;
+
+        this._input = input;
+
+        // we only allow little movement
+        // and we've reached an end event, so a tap is possible
+        if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
+            this.reset();
+        } else if (input.eventType & INPUT_START) {
+            this.reset();
+            this._timer = setTimeoutContext(function() {
+                this.state = STATE_RECOGNIZED;
+                this.tryEmit();
+            }, options.time, this);
+        } else if (input.eventType & INPUT_END) {
+            return STATE_RECOGNIZED;
+        }
+        return STATE_FAILED;
+    },
+
+    reset: function() {
+        clearTimeout(this._timer);
+    },
+
+    emit: function(input) {
+        if (this.state !== STATE_RECOGNIZED) {
+            return;
+        }
+
+        if (input && (input.eventType & INPUT_END)) {
+            this.manager.emit(this.options.event + 'up', input);
+        } else {
+            this._input.timeStamp = now();
+            this.manager.emit(this.options.event, this._input);
+        }
+    }
+});
+
+/**
+ * Rotate
+ * Recognized when two or more pointer are moving in a circular motion.
+ * @constructor
+ * @extends AttrRecognizer
+ */
+function RotateRecognizer() {
+    AttrRecognizer.apply(this, arguments);
+}
+
+inherit(RotateRecognizer, AttrRecognizer, {
+    /**
+     * @namespace
+     * @memberof RotateRecognizer
+     */
+    defaults: {
+        event: 'rotate',
+        threshold: 0,
+        pointers: 2
+    },
+
+    getTouchAction: function() {
+        return [TOUCH_ACTION_NONE];
+    },
+
+    attrTest: function(input) {
+        return this._super.attrTest.call(this, input) &&
+            (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
+    }
+});
+
+/**
+ * Swipe
+ * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
+ * @constructor
+ * @extends AttrRecognizer
+ */
+function SwipeRecognizer() {
+    AttrRecognizer.apply(this, arguments);
+}
+
+inherit(SwipeRecognizer, AttrRecognizer, {
+    /**
+     * @namespace
+     * @memberof SwipeRecognizer
+     */
+    defaults: {
+        event: 'swipe',
+        threshold: 10,
+        velocity: 0.3,
+        direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
+        pointers: 1
+    },
+
+    getTouchAction: function() {
+        return PanRecognizer.prototype.getTouchAction.call(this);
+    },
+
+    attrTest: function(input) {
+        var direction = this.options.direction;
+        var velocity;
+
+        if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
+            velocity = input.overallVelocity;
+        } else if (direction & DIRECTION_HORIZONTAL) {
+            velocity = input.overallVelocityX;
+        } else if (direction & DIRECTION_VERTICAL) {
+            velocity = input.overallVelocityY;
+        }
+
+        return this._super.attrTest.call(this, input) &&
+            direction & input.offsetDirection &&
+            input.distance > this.options.threshold &&
+            input.maxPointers == this.options.pointers &&
+            abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
+    },
+
+    emit: function(input) {
+        var direction = directionStr(input.offsetDirection);
+        if (direction) {
+            this.manager.emit(this.options.event + direction, input);
+        }
+
+        this.manager.emit(this.options.event, input);
+    }
+});
+
+/**
+ * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
+ * between the given interval and position. The delay option can be used to recognize multi-taps without firing
+ * a single tap.
+ *
+ * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
+ * multi-taps being recognized.
+ * @constructor
+ * @extends Recognizer
+ */
+function TapRecognizer() {
+    Recognizer.apply(this, arguments);
+
+    // previous time and center,
+    // used for tap counting
+    this.pTime = false;
+    this.pCenter = false;
+
+    this._timer = null;
+    this._input = null;
+    this.count = 0;
+}
+
+inherit(TapRecognizer, Recognizer, {
+    /**
+     * @namespace
+     * @memberof PinchRecognizer
+     */
+    defaults: {
+        event: 'tap',
+        pointers: 1,
+        taps: 1,
+        interval: 300, // max time between the multi-tap taps
+        time: 250, // max time of the pointer to be down (like finger on the screen)
+        threshold: 9, // a minimal movement is ok, but keep it low
+        posThreshold: 10 // a multi-tap can be a bit off the initial position
+    },
+
+    getTouchAction: function() {
+        return [TOUCH_ACTION_MANIPULATION];
+    },
+
+    process: function(input) {
+        var options = this.options;
+
+        var validPointers = input.pointers.length === options.pointers;
+        var validMovement = input.distance < options.threshold;
+        var validTouchTime = input.deltaTime < options.time;
+
+        this.reset();
+
+        if ((input.eventType & INPUT_START) && (this.count === 0)) {
+            return this.failTimeout();
+        }
+
+        // we only allow little movement
+        // and we've reached an end event, so a tap is possible
+        if (validMovement && validTouchTime && validPointers) {
+            if (input.eventType != INPUT_END) {
+                return this.failTimeout();
+            }
+
+            var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
+            var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
+
+            this.pTime = input.timeStamp;
+            this.pCenter = input.center;
+
+            if (!validMultiTap || !validInterval) {
+                this.count = 1;
+            } else {
+                this.count += 1;
+            }
+
+            this._input = input;
+
+            // if tap count matches we have recognized it,
+            // else it has began recognizing...
+            var tapCount = this.count % options.taps;
+            if (tapCount === 0) {
+                // no failing requirements, immediately trigger the tap event
+                // or wait as long as the multitap interval to trigger
+                if (!this.hasRequireFailures()) {
+                    return STATE_RECOGNIZED;
+                } else {
+                    this._timer = setTimeoutContext(function() {
+                        this.state = STATE_RECOGNIZED;
+                        this.tryEmit();
+                    }, options.interval, this);
+                    return STATE_BEGAN;
+                }
+            }
+        }
+        return STATE_FAILED;
+    },
+
+    failTimeout: function() {
+        this._timer = setTimeoutContext(function() {
+            this.state = STATE_FAILED;
+        }, this.options.interval, this);
+        return STATE_FAILED;
+    },
+
+    reset: function() {
+        clearTimeout(this._timer);
+    },
+
+    emit: function() {
+        if (this.state == STATE_RECOGNIZED) {
+            this._input.tapCount = this.count;
+            this.manager.emit(this.options.event, this._input);
+        }
+    }
+});
+
+/**
+ * Simple way to create a manager with a default set of recognizers.
+ * @param {HTMLElement} element
+ * @param {Object} [options]
+ * @constructor
+ */
+function Hammer(element, options) {
+    options = options || {};
+    options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
+    return new Manager(element, options);
+}
+
+/**
+ * @const {string}
+ */
+Hammer.VERSION = '2.0.7';
+
+/**
+ * default settings
+ * @namespace
+ */
+Hammer.defaults = {
+    /**
+     * set if DOM events are being triggered.
+     * But this is slower and unused by simple implementations, so disabled by default.
+     * @type {Boolean}
+     * @default false
+     */
+    domEvents: false,
+
+    /**
+     * The value for the touchAction property/fallback.
+     * When set to `compute` it will magically set the correct value based on the added recognizers.
+     * @type {String}
+     * @default compute
+     */
+    touchAction: TOUCH_ACTION_COMPUTE,
+
+    /**
+     * @type {Boolean}
+     * @default true
+     */
+    enable: true,
+
+    /**
+     * EXPERIMENTAL FEATURE -- can be removed/changed
+     * Change the parent input target element.
+     * If Null, then it is being set the to main element.
+     * @type {Null|EventTarget}
+     * @default null
+     */
+    inputTarget: null,
+
+    /**
+     * force an input class
+     * @type {Null|Function}
+     * @default null
+     */
+    inputClass: null,
+
+    /**
+     * Default recognizer setup when calling `Hammer()`
+     * When creating a new Manager these will be skipped.
+     * @type {Array}
+     */
+    preset: [
+        // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
+        [RotateRecognizer, {enable: false}],
+        [PinchRecognizer, {enable: false}, ['rotate']],
+        [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
+        [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
+        [TapRecognizer],
+        [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
+        [PressRecognizer]
+    ],
+
+    /**
+     * Some CSS properties can be used to improve the working of Hammer.
+     * Add them to this method and they will be set when creating a new Manager.
+     * @namespace
+     */
+    cssProps: {
+        /**
+         * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
+         * @type {String}
+         * @default 'none'
+         */
+        userSelect: 'none',
+
+        /**
+         * Disable the Windows Phone grippers when pressing an element.
+         * @type {String}
+         * @default 'none'
+         */
+        touchSelect: 'none',
+
+        /**
+         * Disables the default callout shown when you touch and hold a touch target.
+         * On iOS, when you touch and hold a touch target such as a link, Safari displays
+         * a callout containing information about the link. This property allows you to disable that callout.
+         * @type {String}
+         * @default 'none'
+         */
+        touchCallout: 'none',
+
+        /**
+         * Specifies whether zooming is enabled. Used by IE10>
+         * @type {String}
+         * @default 'none'
+         */
+        contentZooming: 'none',
+
+        /**
+         * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
+         * @type {String}
+         * @default 'none'
+         */
+        userDrag: 'none',
+
+        /**
+         * Overrides the highlight color shown when the user taps a link or a JavaScript
+         * clickable element in iOS. This property obeys the alpha value, if specified.
+         * @type {String}
+         * @default 'rgba(0,0,0,0)'
+         */
+        tapHighlightColor: 'rgba(0,0,0,0)'
+    }
+};
+
+var STOP = 1;
+var FORCED_STOP = 2;
+
+/**
+ * Manager
+ * @param {HTMLElement} element
+ * @param {Object} [options]
+ * @constructor
+ */
+function Manager(element, options) {
+    this.options = assign({}, Hammer.defaults, options || {});
+
+    this.options.inputTarget = this.options.inputTarget || element;
+
+    this.handlers = {};
+    this.session = {};
+    this.recognizers = [];
+    this.oldCssProps = {};
+
+    this.element = element;
+    this.input = createInputInstance(this);
+    this.touchAction = new TouchAction(this, this.options.touchAction);
+
+    toggleCssProps(this, true);
+
+    each(this.options.recognizers, function(item) {
+        var recognizer = this.add(new (item[0])(item[1]));
+        item[2] && recognizer.recognizeWith(item[2]);
+        item[3] && recognizer.requireFailure(item[3]);
+    }, this);
+}
+
+Manager.prototype = {
+    /**
+     * set options
+     * @param {Object} options
+     * @returns {Manager}
+     */
+    set: function(options) {
+        assign(this.options, options);
+
+        // Options that need a little more setup
+        if (options.touchAction) {
+            this.touchAction.update();
+        }
+        if (options.inputTarget) {
+            // Clean up existing event listeners and reinitialize
+            this.input.destroy();
+            this.input.target = options.inputTarget;
+            this.input.init();
+        }
+        return this;
+    },
+
+    /**
+     * stop recognizing for this session.
+     * This session will be discarded, when a new [input]start event is fired.
+     * When forced, the recognizer cycle is stopped immediately.
+     * @param {Boolean} [force]
+     */
+    stop: function(force) {
+        this.session.stopped = force ? FORCED_STOP : STOP;
+    },
+
+    /**
+     * run the recognizers!
+     * called by the inputHandler function on every movement of the pointers (touches)
+     * it walks through all the recognizers and tries to detect the gesture that is being made
+     * @param {Object} inputData
+     */
+    recognize: function(inputData) {
+        var session = this.session;
+        if (session.stopped) {
+            return;
+        }
+
+        // run the touch-action polyfill
+        this.touchAction.preventDefaults(inputData);
+
+        var recognizer;
+        var recognizers = this.recognizers;
+
+        // this holds the recognizer that is being recognized.
+        // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
+        // if no recognizer is detecting a thing, it is set to `null`
+        var curRecognizer = session.curRecognizer;
+
+        // reset when the last recognizer is recognized
+        // or when we're in a new session
+        if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
+            curRecognizer = session.curRecognizer = null;
+        }
+
+        var i = 0;
+        while (i < recognizers.length) {
+            recognizer = recognizers[i];
+
+            // find out if we are allowed try to recognize the input for this one.
+            // 1.   allow if the session is NOT forced stopped (see the .stop() method)
+            // 2.   allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
+            //      that is being recognized.
+            // 3.   allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
+            //      this can be setup with the `recognizeWith()` method on the recognizer.
+            if (session.stopped !== FORCED_STOP && ( // 1
+                    !curRecognizer || recognizer == curRecognizer || // 2
+                    recognizer.canRecognizeWith(curRecognizer))) { // 3
+                recognizer.recognize(inputData);
+            } else {
+                recognizer.reset();
+            }
+
+            // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
+            // current active recognizer. but only if we don't already have an active recognizer
+            if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
+                curRecognizer = session.curRecognizer = recognizer;
+            }
+            i++;
+        }
+    },
+
+    /**
+     * get a recognizer by its event name.
+     * @param {Recognizer|String} recognizer
+     * @returns {Recognizer|Null}
+     */
+    get: function(recognizer) {
+        if (recognizer instanceof Recognizer) {
+            return recognizer;
+        }
+
+        var recognizers = this.recognizers;
+        for (var i = 0; i < recognizers.length; i++) {
+            if (recognizers[i].options.event == recognizer) {
+                return recognizers[i];
+            }
+        }
+        return null;
+    },
+
+    /**
+     * add a recognizer to the manager
+     * existing recognizers with the same event name will be removed
+     * @param {Recognizer} recognizer
+     * @returns {Recognizer|Manager}
+     */
+    add: function(recognizer) {
+        if (invokeArrayArg(recognizer, 'add', this)) {
+            return this;
+        }
+
+        // remove existing
+        var existing = this.get(recognizer.options.event);
+        if (existing) {
+            this.remove(existing);
+        }
+
+        this.recognizers.push(recognizer);
+        recognizer.manager = this;
+
+        this.touchAction.update();
+        return recognizer;
+    },
+
+    /**
+     * remove a recognizer by name or instance
+     * @param {Recognizer|String} recognizer
+     * @returns {Manager}
+     */
+    remove: function(recognizer) {
+        if (invokeArrayArg(recognizer, 'remove', this)) {
+            return this;
+        }
+
+        recognizer = this.get(recognizer);
+
+        // let's make sure this recognizer exists
+        if (recognizer) {
+            var recognizers = this.recognizers;
+            var index = inArray(recognizers, recognizer);
+
+            if (index !== -1) {
+                recognizers.splice(index, 1);
+                this.touchAction.update();
+            }
+        }
+
+        return this;
+    },
+
+    /**
+     * bind event
+     * @param {String} events
+     * @param {Function} handler
+     * @returns {EventEmitter} this
+     */
+    on: function(events, handler) {
+        if (events === undefined) {
+            return;
+        }
+        if (handler === undefined) {
+            return;
+        }
+
+        var handlers = this.handlers;
+        each(splitStr(events), function(event) {
+            handlers[event] = handlers[event] || [];
+            handlers[event].push(handler);
+        });
+        return this;
+    },
+
+    /**
+     * unbind event, leave emit blank to remove all handlers
+     * @param {String} events
+     * @param {Function} [handler]
+     * @returns {EventEmitter} this
+     */
+    off: function(events, handler) {
+        if (events === undefined) {
+            return;
+        }
+
+        var handlers = this.handlers;
+        each(splitStr(events), function(event) {
+            if (!handler) {
+                delete handlers[event];
+            } else {
+                handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
+            }
+        });
+        return this;
+    },
+
+    /**
+     * emit event to the listeners
+     * @param {String} event
+     * @param {Object} data
+     */
+    emit: function(event, data) {
+        // we also want to trigger dom events
+        if (this.options.domEvents) {
+            triggerDomEvent(event, data);
+        }
+
+        // no handlers, so skip it all
+        var handlers = this.handlers[event] && this.handlers[event].slice();
+        if (!handlers || !handlers.length) {
+            return;
+        }
+
+        data.type = event;
+        data.preventDefault = function() {
+            data.srcEvent.preventDefault();
+        };
+
+        var i = 0;
+        while (i < handlers.length) {
+            handlers[i](data);
+            i++;
+        }
+    },
+
+    /**
+     * destroy the manager and unbinds all events
+     * it doesn't unbind dom events, that is the user own responsibility
+     */
+    destroy: function() {
+        this.element && toggleCssProps(this, false);
+
+        this.handlers = {};
+        this.session = {};
+        this.input.destroy();
+        this.element = null;
+    }
+};
+
+/**
+ * add/remove the css properties as defined in manager.options.cssProps
+ * @param {Manager} manager
+ * @param {Boolean} add
+ */
+function toggleCssProps(manager, add) {
+    var element = manager.element;
+    if (!element.style) {
+        return;
+    }
+    var prop;
+    each(manager.options.cssProps, function(value, name) {
+        prop = prefixed(element.style, name);
+        if (add) {
+            manager.oldCssProps[prop] = element.style[prop];
+            element.style[prop] = value;
+        } else {
+            element.style[prop] = manager.oldCssProps[prop] || '';
+        }
+    });
+    if (!add) {
+        manager.oldCssProps = {};
+    }
+}
+
+/**
+ * trigger dom event
+ * @param {String} event
+ * @param {Object} data
+ */
+function triggerDomEvent(event, data) {
+    var gestureEvent = document.createEvent('Event');
+    gestureEvent.initEvent(event, true, true);
+    gestureEvent.gesture = data;
+    data.target.dispatchEvent(gestureEvent);
+}
+
+assign(Hammer, {
+    INPUT_START: INPUT_START,
+    INPUT_MOVE: INPUT_MOVE,
+    INPUT_END: INPUT_END,
+    INPUT_CANCEL: INPUT_CANCEL,
+
+    STATE_POSSIBLE: STATE_POSSIBLE,
+    STATE_BEGAN: STATE_BEGAN,
+    STATE_CHANGED: STATE_CHANGED,
+    STATE_ENDED: STATE_ENDED,
+    STATE_RECOGNIZED: STATE_RECOGNIZED,
+    STATE_CANCELLED: STATE_CANCELLED,
+    STATE_FAILED: STATE_FAILED,
+
+    DIRECTION_NONE: DIRECTION_NONE,
+    DIRECTION_LEFT: DIRECTION_LEFT,
+    DIRECTION_RIGHT: DIRECTION_RIGHT,
+    DIRECTION_UP: DIRECTION_UP,
+    DIRECTION_DOWN: DIRECTION_DOWN,
+    DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
+    DIRECTION_VERTICAL: DIRECTION_VERTICAL,
+    DIRECTION_ALL: DIRECTION_ALL,
+
+    Manager: Manager,
+    Input: Input,
+    TouchAction: TouchAction,
+
+    TouchInput: TouchInput,
+    MouseInput: MouseInput,
+    PointerEventInput: PointerEventInput,
+    TouchMouseInput: TouchMouseInput,
+    SingleTouchInput: SingleTouchInput,
+
+    Recognizer: Recognizer,
+    AttrRecognizer: AttrRecognizer,
+    Tap: TapRecognizer,
+    Pan: PanRecognizer,
+    Swipe: SwipeRecognizer,
+    Pinch: PinchRecognizer,
+    Rotate: RotateRecognizer,
+    Press: PressRecognizer,
+
+    on: addEventListeners,
+    off: removeEventListeners,
+    each: each,
+    merge: merge,
+    extend: extend,
+    assign: assign,
+    inherit: inherit,
+    bindFn: bindFn,
+    prefixed: prefixed
+});
+
+// this prevents errors when Hammer is loaded in the presence of an AMD
+//  style loader but by script tag, not by the loader.
+var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
+freeGlobal.Hammer = Hammer;
+
+if (typeof define === 'function' && define.amd) {
+    define(function() {
+        return Hammer;
+    });
+} else if (typeof module != 'undefined' && module.exports) {
+    module.exports = Hammer;
+} else {
+    window[exportName] = Hammer;
+}
+
+})(window, document, 'Hammer');
diff --git a/patches/01_RemoveGitHash.patch b/patches/01_RemoveGitHash.patch
new file mode 100644 (file)
index 0000000..4d46fed
--- /dev/null
@@ -0,0 +1,16 @@
+Description: Remove git hash commit consideration
+Forwarded: not-needed
+Author: Anton Gladky <gladk@debian.org>
+Last-Update: 2016-12-31
+Index: solvespace/CMakeLists.txt
+===================================================================
+--- solvespace.orig/CMakeLists.txt
++++ solvespace/CMakeLists.txt
+@@ -36,7 +36,6 @@ endif()
+ # NOTE TO PACKAGERS: The embedded git commit hash is critical for rapid bug triage when the builds
+ # can come from a variety of sources. If you are mirroring the sources or otherwise build when
+ # the .git directory is not present, please comment the following line:
+-include(GetGitCommitHash)
+ # and instead uncomment the following, adding the complete git hash of the checkout you are using:
+ # set(GIT_COMMIT_HASH 0000000000000000000000000000000000000000)
diff --git a/patches/02_add_debian_pkg_version.patch b/patches/02_add_debian_pkg_version.patch
new file mode 100644 (file)
index 0000000..e345437
--- /dev/null
@@ -0,0 +1,24 @@
+Subject: Add debian package version to ver string
+Forwarded: not-needed
+From: Ryan Pavlik <ryan.pavlik@collabora.com>
+
+Date: Tue, 19 Jan 2021 13:51:09 -0600
+
+
+---
+ src/config.h.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/config.h.in b/src/config.h.in
+index 144c46d..b129808 100644
+--- a/src/config.h.in
++++ b/src/config.h.in
+@@ -1,7 +1,7 @@
+ #ifndef SOLVESPACE_CONFIG_H
+ #define SOLVESPACE_CONFIG_H
+-#define PACKAGE_VERSION "@solvespace_VERSION_MAJOR@.@solvespace_VERSION_MINOR@~@solvespace_GIT_HASH@"
++#define PACKAGE_VERSION "@solvespace_VERSION_MAJOR@.@solvespace_VERSION_MINOR@ (Debian package version @DEBVERSION@)"
+ /* Non-OS X *nix only */
+ #define UNIX_DATADIR "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_DATAROOTDIR@/solvespace"
diff --git a/patches/03_use_system_threejs.patch b/patches/03_use_system_threejs.patch
new file mode 100644 (file)
index 0000000..d4b206e
--- /dev/null
@@ -0,0 +1,33 @@
+Subject: Use threejs from system.
+Forwarded: not-needed
+From: Ryan Pavlik <ryan.pavlik@collabora.com>
+
+Date: Tue, 19 Jan 2021 15:05:06 -0600
+
+
+---
+ res/CMakeLists.txt | 1 -
+ src/export.cpp     | 2 +-
+ 2 files changed, 1 insertion(+), 2 deletions(-)
+
+--- solvespace.orig/res/CMakeLists.txt
++++ solvespace/res/CMakeLists.txt
+@@ -284,7 +284,6 @@
+     shaders/edge.frag
+     shaders/edge.vert
+     shaders/outline.vert
+-    threejs/three-r76.js.gz
+     threejs/hammer-2.0.8.js.gz
+     threejs/SolveSpaceControls.js)
+--- solvespace.orig/src/export.cpp
++++ solvespace/src/export.cpp
+@@ -1014,7 +1014,7 @@
+     if(filename.HasExtension("html")) {
+         fprintf(f, htmlbegin,
+-                LoadStringFromGzip("threejs/three-r76.js.gz").c_str(),
++                LoadString("threejs/three.js").c_str(),
+                 LoadStringFromGzip("threejs/hammer-2.0.8.js.gz").c_str(),
+                 LoadString("threejs/SolveSpaceControls.js").c_str());
+     }
diff --git a/patches/04_use_system_unifont.patch b/patches/04_use_system_unifont.patch
new file mode 100644 (file)
index 0000000..0dc276d
--- /dev/null
@@ -0,0 +1,37 @@
+Subject: Use Unifont from system
+Forwarded: not-needed
+From: Ryan Pavlik <ryan.pavlik@collabora.com>
+
+Date: Tue, 19 Jan 2021 15:06:55 -0600
+
+
+---
+ res/CMakeLists.txt | 1 -
+ src/resource.cpp   | 2 +-
+ 2 files changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/res/CMakeLists.txt b/res/CMakeLists.txt
+index d893f93..85b6e49 100644
+--- a/res/CMakeLists.txt
++++ b/res/CMakeLists.txt
+@@ -259,7 +259,6 @@ add_resources(
+     locales/uk_UA.po
+     locales/ru_RU.po
+     locales/zh_CN.po
+-    fonts/unifont.hex.gz
+     fonts/private/0-check-false.png
+     fonts/private/1-check-true.png
+     fonts/private/2-radio-false.png
+diff --git a/src/resource.cpp b/src/resource.cpp
+index 7b19081..e8a51bb 100644
+--- a/src/resource.cpp
++++ b/src/resource.cpp
+@@ -665,7 +665,7 @@ size_t BitmapFont::GetWidth(const std::string &str) {
+ }
+ BitmapFont BitmapFont::Create() {
+-    BitmapFont Font = BitmapFont::From(LoadStringFromGzip("fonts/unifont.hex.gz"));
++    BitmapFont Font = BitmapFont::From(LoadString("fonts/unifont.hex"));
+     // Unifont doesn't have a glyph for U+0020.
+     Font.AddGlyph(0x0020, Pixmap::Create(Pixmap::Format::RGB, 8, 16));
+     Font.AddGlyph(0xE000, LoadPng("fonts/private/0-check-false.png"));
diff --git a/patches/10_mimalloc_restrict_cpu_yield.patch b/patches/10_mimalloc_restrict_cpu_yield.patch
new file mode 100644 (file)
index 0000000..f05babe
--- /dev/null
@@ -0,0 +1,63 @@
+Subject: Restricts cpu yield instructions a little.
+Forwarded: https://github.com/microsoft/mimalloc/pull/350
+From: Ryan Pavlik <ryan.pavlik@collabora.com>
+Last-Update: 2021-02-11
+Applied-Upstream: 3.0.0, https://github.com/solvespace/solvespace/commit/cdeb6c90fb753a2b0f6d033affedbcceab046758
+
+
+adding clobber for ARM and preventing older 32 bits chips not supporting this instruction.
+
+Backport of https://github.com/microsoft/mimalloc/commit/33a10b48605f8bb419487a03125815ad6ee00a70
+
+Enhanced via https://github.com/mr-c/misc/wiki/pause-techniques-on-many-architectures
+
+---
+ extlib/mimalloc/include/mimalloc-atomic.h | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+--- solvespace.orig/extlib/mimalloc/include/mimalloc-atomic.h
++++ solvespace/extlib/mimalloc/include/mimalloc-atomic.h
+@@ -266,17 +266,37 @@
+   static inline void mi_atomic_yield(void) {
+     std::this_thread::yield();
+   }
+-#elif (defined(__GNUC__) || defined(__clang__)) && \
+-      (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__))
+-#if defined(__x86_64__) || defined(__i386__)
++#elif defined(__SSE2__)  // AMD and Intel
++  #include <emmintrin.h>
++  static inline void mi_atomic_yield(void) {
++    _mm_pause();
++  }
++#elif defined(__x86_64__) || defined(__i386__)
+   static inline void mi_atomic_yield(void) {
+     asm volatile ("pause" ::: "memory");
+   }
+-#elif defined(__arm__) || defined(__aarch64__)
++#elif defined(__aarch64__)
++  static inline void mi_atomic_yield(void) {
++    asm volatile("wfe");
++  }
++#elif defined(__arm__) && __ARM_ARCH__ >= 7
++  static inline void mi_atomic_yield(void) {
++    __asm__ volatile("yield" ::: "memory");
++  }
++#elif defined(__armel__) || defined(__ARMEL__)
++  static inline void mi_atomic_yield(void) {
++    asm volatile ("nop" ::: "memory");  // default operation - does nothing => Might lead to passive spinning.
++  }
++#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) // PowerPC
++  static inline void mi_atomic_yield(void) {
++     __asm__ __volatile__ ("or 27,27,27" ::: "memory");
++  }
++#elif defined(__sun)
++  // Fallback for other archs
++  #include <synch.h>
+   static inline void mi_atomic_yield(void) {
+-    asm volatile("yield");
++    smt_pause();
+   }
+-#endif
+ #elif defined(__wasi__)
+   #include <sched.h>
+   static inline void mi_atomic_yield(void) {
diff --git a/patches/11_update_js_three_r111.patch b/patches/11_update_js_three_r111.patch
new file mode 100644 (file)
index 0000000..5115862
--- /dev/null
@@ -0,0 +1,39 @@
+Subject: Update javascript to be compatible with threejs r111
+Forwarded: https://github.com/solvespace/solvespace/pull/906
+From: Ryan Pavlik <ryan.pavlik@collabora.com>
+Last-Update: 2021-02-11
+Applied-Upstream: 3.0.0, https://github.com/solvespace/solvespace/commit/d16e33ac48cb717f24a06a30e268227c860476d2
+
+Date: Tue, 19 Jan 2021 17:22:35 -0600
+
+
+---
+ res/threejs/SolveSpaceControls.js | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/res/threejs/SolveSpaceControls.js b/res/threejs/SolveSpaceControls.js
+index e141ace..ca74fd5 100644
+--- a/res/threejs/SolveSpaceControls.js
++++ b/res/threejs/SolveSpaceControls.js
+@@ -470,9 +470,9 @@ solvespace = function(obj, params) {
+         changeBasis.makeBasis(camera.right, camera.up, n);
+         for (var i = 0; i < 2; i++) {
+-            var newLightPos = changeBasis.applyToVector3Array(
+-                [obj.lights.d[i].direction[0], obj.lights.d[i].direction[1],
+-                    obj.lights.d[i].direction[2]]);
++            var newLightPos = new THREE.Vector3(obj.lights.d[i].direction[0],
++                obj.lights.d[i].direction[1],
++                obj.lights.d[i].direction[2]).applyMatrix4(changeBasis);
+             directionalLightArray[i].position.set(newLightPos[0],
+                 newLightPos[1], newLightPos[2]);
+         }
+@@ -515,7 +515,7 @@ solvespace = function(obj, params) {
+         }
+         geometry.computeBoundingSphere();
+-        return new THREE.Mesh(geometry, new THREE.MultiMaterial(materialList));
++        return new THREE.Mesh(geometry, materialList);
+     }
+     function createEdges(meshObj) {
diff --git a/patches/12_Remove-march-native-from-mimalloc-build.patch b/patches/12_Remove-march-native-from-mimalloc-build.patch
new file mode 100644 (file)
index 0000000..40f0282
--- /dev/null
@@ -0,0 +1,29 @@
+Subject: Remove -march=native from mimalloc build.
+Bug: https://github.com/microsoft/mimalloc/issues/361
+Bug: https://github.com/solvespace/solvespace/issues/934
+From: Ryan Pavlik <ryan.pavlik@collabora.com>
+Last-Update: 2021-02-11
+
+Date: Wed, 10 Feb 2021 16:20:25 -0600
+
+
+---
+ extlib/mimalloc/CMakeLists.txt | 5 -----
+ 1 file changed, 5 deletions(-)
+
+diff --git a/extlib/mimalloc/CMakeLists.txt b/extlib/mimalloc/CMakeLists.txt
+index 37616eb..e8e7f88 100644
+--- a/extlib/mimalloc/CMakeLists.txt
++++ b/extlib/mimalloc/CMakeLists.txt
+@@ -183,11 +183,6 @@ if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM
+   endif()
+ endif()
+-# Architecture flags
+-if(${CMAKE_HOST_SYSTEM_PROCESSOR} MATCHES "arm")
+-    list(APPEND mi_cflags -march=native)
+-endif()
+-
+ # extra needed libraries
+ if(WIN32)
+   list(APPEND mi_libraries psapi shell32 user32 bcrypt)
diff --git a/patches/series b/patches/series
new file mode 100644 (file)
index 0000000..7cad336
--- /dev/null
@@ -0,0 +1,7 @@
+01_RemoveGitHash.patch
+02_add_debian_pkg_version.patch
+03_use_system_threejs.patch
+04_use_system_unifont.patch
+10_mimalloc_restrict_cpu_yield.patch
+11_update_js_three_r111.patch
+12_Remove-march-native-from-mimalloc-build.patch
diff --git a/rules b/rules
new file mode 100755 (executable)
index 0000000..be9e87b
--- /dev/null
+++ b/rules
@@ -0,0 +1,36 @@
+#!/usr/bin/make -f
+
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/default.mk
+
+ifneq (,$(filter $(DEB_HOST_ARCH), armel m68k mips mipsel powerpc sh4))
+       export DEB_CXXFLAGS_MAINT_APPEND += -Wl,--no-as-needed -latomic -Wl,--as-needed
+       export DEB_LDFLAGS_MAINT_APPEND += -Wl,--no-as-needed -latomic -Wl,--as-needed
+endif
+
+# Tell CMake what the package version will be, to embed in the version string.
+# We do this for upstream's benefit, in lieu of including a git commit hash.
+CMAKEOPTS=-DDEBVERSION=$(DEB_VERSION)
+CMAKEOPTS+= -DENABLE_OPENMP=yes
+CMAKEOPTS+= -DENABLE_LTO=yes
+
+#speed up the build if we are skipping the tests
+ifneq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
+ifneq (amd64,$(DEB_HOST_ARCH))
+CMAKEOPTS+= -DENABLE_TESTS=off
+endif
+endif
+
+%:
+       dh $@
+
+override_dh_auto_configure:
+       dh_auto_configure -- $(CMAKEOPTS)
+
+override_dh_auto_test:
+ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
+ifeq (amd64,$(DEB_HOST_ARCH))
+       cd obj-* && $(MAKE) -j$(nproc) test_solvespace
+endif
+endif
diff --git a/solvespace-cli.1 b/solvespace-cli.1
new file mode 100644 (file)
index 0000000..f1d77c5
--- /dev/null
@@ -0,0 +1,96 @@
+.TH SOLVESPACE 1 "06 Oct 2019" "3.0.0"
+.SH NAME
+solvespace \- parametric 2d/3d CAD
+.SH SYNOPSIS
+.B solvespace-cli
+.RI <command> " [options] " <filename>
+.SH "DESCRIPTION"
+.B solvespace
+is a parametric 2d/3d CAD program. Applications include:
+  * modeling 3d parts — draw with extrudes, revolves, and Boolean
+    (union / difference) operations;
+  * modeling 2d parts — draw the part as a single section, and export DXF,
+    PDF, SVG; use 3d assembly to verify fit;
+  * 3d-printed parts — export the STL or other triangle mesh expected by
+    most 3d printers;
+  * preparing CAM data — export 2d vector art for a waterjet machine or
+    laser cutter; or generate STEP or STL, for import into third-party
+    CAM software for machining;
+  * mechanism design — use the constraint solver to simulate planar or
+    spatial linkages, with pin, ball, or slide joints;
+  * plane and solid geometry — replace hand-solved trigonometry and
+    spreadsheets with a live dimensioned drawing. 
+.PP
+.SH COMMON OPTIONS
+.TP
+.B \-o, \-\-output <pattern>
+For an input file <name>.slvs, replaces the '%' symbol in <pattern>
+with <name> and uses it as output file. For example, when using
+\-\-output %-2d.png for input files f/a.slvs and f/b.slvs, output files
+f/a-2d.png and f/b-2d.png will be written.
+.TP
+.B \-v, \-\-view <direction>
+Selects the camera direction. <direction> can be one of "top", "bottom",
+"left", "right", "front", "back", or "isometric".
+.TP
+.B \-t, \-\-chord-tol <tolerance>
+Selects the chord tolerance, used for converting exact curves to
+piecewise linear, and exact surfaces into triangle meshes.
+For export commands, the unit is mm, and the default is 1.0 mm.
+For non-export commands, the unit is %, and the default is 1.0 %.
+.PP
+.SH COMMANDS
+.TP
+.B thumbnail \-\-output <pattern> \-\-size <size> \-\-view <direction> [\-\-chord-tol <tolerance>]
+Outputs a rendered view of the sketch, like the SolveSpace GUI would. <size> is <width>x<height>, in pixels. Graphics acceleration is not used, and the output may look slightly different from the GUI.
+.TP
+.B export-view \-\-output <pattern> \-\-view <direction> [\-\-chord-tol <tolerance>]
+Exports a view of the sketch, in a 2d vector format.
+.TP
+.B export-wireframe \-\-output <pattern> [\-\-chord-tol <tolerance>]
+Exports a wireframe of the sketch, in a 3d vector format.
+.TP
+.B export-mesh \-\-output <pattern> [\-\-chord-tol <tolerance>]
+Exports a triangle mesh of solids in the sketch, with exact surfaces
+being triangulated first.
+.TP
+.B export-surfaces \-\-output <pattern>
+Exports exact surfaces of solids in the sketch, if any.
+.TP
+.B regenerate [\-\-chord-tol <tolerance>]
+Reloads all imported files, regenerates the sketch, and saves it.
+Note that, although this is not an export command, it uses absolute
+chord tolerance, and can be used to prepare assemblies for export.
+.PP
+.SH FILE FORMATS
+.TP
+.B thumbnail:
+    PNG image (png)
+.TP
+.B export-view:
+    PDF file (pdf)
+    Encapsulated PostScript (eps, ps)
+    Scalable Vector Graphics (svg)
+    STEP file (step, stp)
+    DXF file (AutoCAD 2007) (dxf)
+    HPGL file (plt, hpgl)
+    G Code (ngc, txt)
+.TP
+.B export-wireframe:
+    STEP file (step, stp)
+    DXF file (AutoCAD 2007) (dxf)
+.TP
+.B export-mesh:
+    STL mesh (stl)
+    Wavefront OBJ mesh (obj)
+    Three.js-compatible mesh, with viewer (html)
+    Three.js-compatible mesh, mesh only (js)
+    Q3D Object file (q3do)
+    VRML text file (wrl)
+.TP
+.B export-surfaces:
+    STEP file (step, stp)
+.PP
+.SH "AUTHORS"
+This manual page was written by Alexander Pozdnyakov <almipo@mail.ru>,
diff --git a/solvespace.1 b/solvespace.1
new file mode 100644 (file)
index 0000000..7885dc4
--- /dev/null
@@ -0,0 +1,28 @@
+.\"Created with GNOME Manpages Editor Wizard
+.\"http://sourceforge.net/projects/gmanedit2
+.TH solvespace 1 "August 15, 2016" "" "SOLVESPACE"
+
+.SH NAME
+solvespace \- a parametric 3d CAD program 
+
+.SH DESCRIPTION
+.PP
+.B modeling 3d parts -
+draw with extrudes, revolves, helix creation, and Boolean (union / difference / intersection) operations
+.PP
+.B modeling 2d parts -
+draw the part as a single section, and export DXF, PDF, SVG; use 3d assembly to verify fit
+.PP
+.B 3d-printed parts -
+export the STL or other triangle mesh expected by most 3d printers
+.PP
+.B preparing CAM data - 
+export 2d vector art for a waterjet machine or laser cutter; or generate STEP or STL, for import into third-party CAM software for machining
+.PP
+.B  mechanism design -
+use the constraint solver to simulate planar or spatial linkages, with pin, ball, or slide joints
+.PP
+.B  plane and solid geometry -
+replace hand-solved trigonometry and spreadsheets with a live dimensioned drawing
+.PP
+
diff --git a/solvespace.install b/solvespace.install
new file mode 100644 (file)
index 0000000..6819914
--- /dev/null
@@ -0,0 +1,3 @@
+usr/bin/solvespace
+usr/bin/solvespace-cli
+usr/share
diff --git a/solvespace.links b/solvespace.links
new file mode 100644 (file)
index 0000000..1b4d5d6
--- /dev/null
@@ -0,0 +1,2 @@
+usr/share/javascript/three/three.js usr/share/solvespace/threejs/three.js
+usr/share/unifont/unifont.hex usr/share/solvespace/fonts/unifont.hex
diff --git a/solvespace.manpages b/solvespace.manpages
new file mode 100644 (file)
index 0000000..46ff2a2
--- /dev/null
@@ -0,0 +1,3 @@
+debian/solvespace.1
+debian/solvespace-cli.1
+
diff --git a/solvespace.sharedmimeinfo b/solvespace.sharedmimeinfo
new file mode 100644 (file)
index 0000000..4f6e2cf
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
+    <mime-type type="application/x-solvespace">
+    <comment>SolveSpace sketch</comment>
+    <glob pattern="*.slvs"/>
+    <generic-icon name="x-office-document"/>
+  </mime-type>
+</mime-info>
diff --git a/source/format b/source/format
new file mode 100644 (file)
index 0000000..163aaf8
--- /dev/null
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/source/options b/source/options
new file mode 100644 (file)
index 0000000..a799080
--- /dev/null
@@ -0,0 +1,5 @@
+## This file was written by cme command.
+## You can run 'cme edit dpkg' to modify this file.
+## You may also modify the content of this file with your favorite editor.
+
+tar-ignore=".git"
diff --git a/tests/common b/tests/common
new file mode 100644 (file)
index 0000000..df86ee9
--- /dev/null
@@ -0,0 +1,14 @@
+
+get_all_files() {
+    find -name "*.slvs"
+}
+
+get_files_with_surfaces() {
+    # Can't export the mesh/surfaces of a file with no 3d in it, just 2d
+    get_all_files | grep -E -v "(constraint)|(request)|(link)|(contour_area)"
+}
+
+get_files_to_thumbnail() {
+    # Can't thumbnail files with embedded images: https://github.com/solvespace/solvespace/issues/905
+    get_all_files | grep -v "image"
+}
diff --git a/tests/control b/tests/control
new file mode 100644 (file)
index 0000000..a26a8db
--- /dev/null
@@ -0,0 +1,3 @@
+Tests: htmlmesh stlmesh surfaces thumbnail view wireframe
+Restrictions: allow-stderr
+Depends: solvespace, findutils, grep
diff --git a/tests/htmlmesh b/tests/htmlmesh
new file mode 100755 (executable)
index 0000000..f2a6323
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+set -e
+set -x
+. debian/tests/common
+logname=$AUTOPKGTEST_TMP/html_mesh.txt
+(
+    get_files_with_surfaces | while read fn; do
+        echo $fn
+        mkdir -p $AUTOPKGTEST_TMP/$(dirname $fn)
+        solvespace-cli export-mesh --output $AUTOPKGTEST_TMP/%_mesh.html $fn
+    done
+) 2>&1 | tee $logname
+
+# We don't care about self-intersecting errors.
+! (grep "Error" $logname | grep -v "self-intersecting")
diff --git a/tests/stlmesh b/tests/stlmesh
new file mode 100755 (executable)
index 0000000..eb3b218
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+set -e
+set -x
+. debian/tests/common
+logname=$AUTOPKGTEST_TMP/stl_mesh.txt
+(
+    get_files_with_surfaces | while read fn; do
+        echo $fn
+        mkdir -p $AUTOPKGTEST_TMP/$(dirname $fn)
+        solvespace-cli export-mesh --output $AUTOPKGTEST_TMP/%_mesh.stl $fn
+    done
+) 2>&1 | tee $logname
+
+# We don't care about self-intersecting errors.
+! (grep "Error" $logname | grep -v "self-intersecting")
diff --git a/tests/surfaces b/tests/surfaces
new file mode 100755 (executable)
index 0000000..42c5bd1
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -e
+set -x
+. debian/tests/common
+logname=$AUTOPKGTEST_TMP/surfaces.txt
+(
+    get_files_with_surfaces | while read fn; do
+        echo $fn
+        mkdir -p $AUTOPKGTEST_TMP/$(dirname $fn)
+        solvespace-cli export-surfaces --output $AUTOPKGTEST_TMP/%_surfaces.step $fn
+    done
+) 2>&1 | tee $logname
+
+! grep "Error" $logname
diff --git a/tests/thumbnail b/tests/thumbnail
new file mode 100755 (executable)
index 0000000..7adf97e
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+set -e
+set -x
+. debian/tests/common
+logname=$AUTOPKGTEST_TMP/thumbnail.txt
+(
+    get_files_to_thumbnail | while read fn; do
+        echo $fn
+        mkdir -p $AUTOPKGTEST_TMP/$(dirname $fn)
+        solvespace-cli thumbnail --output $AUTOPKGTEST_TMP/%_thumb.png --size 48x48 --view isometric $fn
+    done
+) 2>&1 | tee $logname
+
+# We don't care about self-intersecting errors.
+! grep "Error" $logname
diff --git a/tests/view b/tests/view
new file mode 100755 (executable)
index 0000000..7f709cc
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -e
+set -x
+. debian/tests/common
+logname=$AUTOPKGTEST_TMP/view.txt
+(
+    get_all_files | while read fn; do
+        echo $fn
+        mkdir -p $AUTOPKGTEST_TMP/$(dirname $fn)
+        solvespace-cli export-view --output $AUTOPKGTEST_TMP/%_view.pdf --view isometric $fn
+    done
+) 2>&1 | tee $logname
+
+! grep "Error" $logname
diff --git a/tests/wireframe b/tests/wireframe
new file mode 100755 (executable)
index 0000000..15d5205
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -e
+set -x
+. debian/tests/common
+logname=$AUTOPKGTEST_TMP/wireframe.txt
+(
+    get_all_files | while read fn; do
+        echo $fn
+        mkdir -p $AUTOPKGTEST_TMP/$(dirname $fn)
+        solvespace-cli export-wireframe --output $AUTOPKGTEST_TMP/%_wireframe.dxf $fn
+    done
+) 2>&1 | tee $logname
+
+! grep "Error" $logname
diff --git a/upstream/metadata b/upstream/metadata
new file mode 100644 (file)
index 0000000..4425fa5
--- /dev/null
@@ -0,0 +1,4 @@
+Repository-Browse: https://github.com/solvespace/solvespace
+Repository: https://github.com/solvespace/solvespace.git
+Bug-Database: https://github.com/solvespace/solvespace/issues
+Bug-Submit: https://github.com/solvespace/solvespace/issues/new
diff --git a/watch b/watch
new file mode 100644 (file)
index 0000000..4935f7f
--- /dev/null
+++ b/watch
@@ -0,0 +1,2 @@
+version=4
+opts=repacksuffix=+repack1,uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha|b|a)\d*)$/$1~$2/,dversionmangle=s/\+(debian|dfsg|ds|deb)\d*$// https://github.com/solvespace/solvespace/tags .*/v?(\d\S*)\.(?:tgz|tbz2|txz|tar\.(?:gz|bz2|xz))