[Pkg-octave-commit] [octave-level-set] 01/02: Imported Upstream version 0.3.0
Rafael Laboissière
rlaboiss-guest at moszumanska.debian.org
Wed Sep 7 23:58:52 UTC 2016
This is an automated email from the git hooks/post-receive script.
rlaboiss-guest pushed a commit to branch master
in repository octave-level-set.
commit 5122f454165a1f506c45cb7a21a288e043719776
Author: Rafael Laboissiere <rafael at debian.org>
Date: Sat Aug 20 16:19:21 2016 -0300
Imported Upstream version 0.3.0
---
CITATION | 17 +
COPYING | 674 +++++++
DESCRIPTION | 10 +
INDEX | 49 +
NEWS | 35 +
autogen.sh | 25 +
inst/fastmarching.m | 498 ++++++
inst/ls_absolute_geom.m | 82 +
inst/ls_animate_evolution.m | 71 +
inst/ls_build_mesh.m | 271 +++
inst/ls_check.m | 98 +
inst/ls_complement.m | 66 +
inst/ls_disjoint.m | 83 +
inst/ls_distance_fcn.m | 133 ++
inst/ls_enforce.m | 107 ++
inst/ls_enforce_speed.m | 148 ++
inst/ls_equal.m | 67 +
inst/ls_extract_solution.m | 174 ++
inst/ls_find_geometry.m | 572 ++++++
inst/ls_genbasic.m | 309 ++++
inst/ls_get_tests.m | 80 +
inst/ls_hausdorff_dist.m | 132 ++
inst/ls_init_narrowband.m | 154 ++
inst/ls_inside.m | 60 +
inst/ls_intersect.m | 90 +
inst/ls_isempty.m | 54 +
inst/ls_issubset.m | 61 +
inst/ls_nb_from_geom.m | 174 ++
inst/ls_normalise.m | 74 +
inst/ls_setdiff.m | 122 ++
inst/ls_setxor.m | 95 +
inst/ls_sign_colourmap.m | 244 +++
inst/ls_signed_distance.m | 144 ++
inst/ls_solve_stationary.m | 299 ++++
inst/ls_time_step.m | 253 +++
inst/ls_union.m | 86 +
inst/maze.png | Bin 0 -> 54138 bytes
inst/private/ls_copy_sign.m | 81 +
inst/private/ls_union_intersect.m | 43 +
inst/private/so_load_compressed.m | 35 +
inst/so_example_problem.m | 309 ++++
inst/so_explore_descent.m | 362 ++++
inst/so_init_params.m | 82 +
inst/so_replay_descent.m | 479 +++++
inst/so_run_descent.m | 458 +++++
inst/so_save_descent.m | 190 ++
inst/so_step_armijo.m | 311 ++++
inst/upwind_gradient_norm.m | 140 ++
src/FastMarching.cpp | 417 +++++
src/FastMarching.hpp | 616 +++++++
src/FastMarching.tpp | 82 +
src/Heap.hpp | 262 +++
src/Heap.tpp | 238 +++
src/Makefile.in | 63 +
src/Utils.cpp | 82 +
src/Utils.hpp | 78 +
src/Utils.tpp | 83 +
src/configure | 3584 +++++++++++++++++++++++++++++++++++++
src/configure.ac | 31 +
src/geomBoundary.cpp | 376 ++++
src/geomElements.cpp | 130 ++
src/geomGamma.cpp | 378 ++++
src/heapsort.cpp | 87 +
src/internal_fastmarching.cpp | 131 ++
src/internal_init_narrowband.cpp | 145 ++
src/internal_mesh.cpp | 525 ++++++
src/marching.cpp | 103 ++
src/nbFromGeom.cpp | 166 ++
src/upwindGrad.cpp | 152 ++
69 files changed, 15830 insertions(+)
diff --git a/CITATION b/CITATION
new file mode 100644
index 0000000..b9c4328
--- /dev/null
+++ b/CITATION
@@ -0,0 +1,17 @@
+The level-set package was created for a PhD project at the University of Graz
+in the International Research Training Group IGDK 1754. To cite the package
+in publications, use:
+
+ Daniel Kraft. A Level-Set Framework for Shape Optimisation.
+ PhD thesis, University of Graz, 2015.
+ http://www.domob.eu/research/phd/thesis.pdf
+
+A BibTeX entry for LaTeX users is:
+
+ @phdthesis{phdKraft,
+ author = {Daniel Kraft},
+ title = {A Level-Set Framework for Shape Optimisation},
+ school = {University of Graz},
+ year = {2015},
+ url = {http://www.domob.eu/research/phd/thesis.pdf}
+ }
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 program 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..552847e
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,10 @@
+Name: level-set
+Version: 0.3.0
+Date: 2015-12-17
+Author: Daniel Kraft <daniel.kraft at uni-graz.at>
+Maintainer: Daniel Kraft <d at domob.eu>
+Title: Level Set
+Description: Routines for calculating the time-evolution of the level-set equation and extracting geometric information from the level-set function.
+Depends: octave (>= 3.6.0)
+Autoload: no
+License: GPLv3+
diff --git a/INDEX b/INDEX
new file mode 100644
index 0000000..5ac4e4c
--- /dev/null
+++ b/INDEX
@@ -0,0 +1,49 @@
+level-set >> Level Set
+The Fast Marching Algorithm
+ fastmarching
+Distance Functions
+ ls_init_narrowband
+ ls_distance_fcn
+ ls_signed_distance
+ ls_hausdorff_dist
+Time Evolution of Level-Sets
+ ls_solve_stationary
+ ls_extract_solution
+Time Stepping of the Level-Set Equation
+ upwind_gradient_norm
+ ls_time_step
+Geometric Constraints
+ ls_check
+ ls_enforce
+ ls_enforce_speed
+Set Predicates
+ ls_inside
+ ls_issubset
+ ls_isempty
+ ls_equal
+ ls_disjoint
+Set Operations and Construction of Shapes
+ ls_genbasic
+ ls_complement
+ ls_union
+ ls_intersect
+ ls_setdiff
+ ls_setxor
+Geometry of Level-Sets in 2D
+ ls_find_geometry
+ ls_absolute_geom
+ ls_nb_from_geom
+ ls_build_mesh
+Miscellaneous Routines
+ ls_animate_evolution
+ ls_get_tests
+ ls_normalise
+ ls_sign_colourmap
+Shape Optimisation with Level Sets
+ so_init_params
+ so_step_armijo
+ so_run_descent
+ so_save_descent
+ so_replay_descent
+ so_explore_descent
+ so_example_problem
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..51ce305
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,35 @@
+Version 0.3.0 (2015-12-17):
+ * Changed the build system to use autoconf.
+ * ls_genbasic can now be called with arbitrary coordinates, they need
+ not correspond to the full grid. In particular, single points
+ can be passed as arguments as well.
+ * Add new routines for shape optimisation with level sets. The main
+ one is so_run_descent.
+ * Allow saving and replaying shape optimisation descents. This is
+ accomplished with so_save_descent and so_replay_descent. Also
+ so_explore_descent can be used to interactively explore a descent log.
+
+Version 0.2.0 (2015-02-18):
+ * The .m file functions don't check nargout() any longer. This is checked
+ by the interpreter anyway. Also, usage() as replaced by either
+ print_usage() or error().
+ * New function 'ls_normalise' that does the normalisation of exactly zero
+ values that was previously done only in ls_find_geometry.
+ * 'ls_inside' as well as 'ls_equal' (and all other set functions that use
+ them internally) now consider negative zeros to be part of the level-set
+ domain. This is consistent with the behaviour of 'ls_normalise'.
+ * 'ls_animate_evolution' only clears the contour between frames now,
+ so that the "jitter" is drastically reduced.
+ * The usage of 'ls_sign_colourmap' was slightly changed. It allows
+ now to specify the colours making up the gradients.
+ * New feature: Time stepping of the level-set equation as alternative
+ method for evolution. New methods 'upwind_gradient_norm' and
+ 'ls_time_step'.
+ * New function 'ls_distance_fcn'. It calculates the non-signed distance
+ function, and is faster than 'ls_signed_distance'. Only exterior points
+ are processed with Fast Marching.
+ * New function 'ls_hausdorff_dist' to approximate the Hausdorff
+ distance between two sets from their distance functions.
+
+Version 0.1.0 (2014-06-09):
+ * Initial release.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..6ea4272
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# GNU Octave level-set package.
+# Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+#
+# 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 program 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/>.
+
+# Perform the autoconf initialisation in src.
+
+set -e
+
+cd src/
+aclocal
+autoconf
diff --git a/inst/fastmarching.m b/inst/fastmarching.m
new file mode 100644
index 0000000..f4489fd
--- /dev/null
+++ b/inst/fastmarching.m
@@ -0,0 +1,498 @@
+## Copyright (C) 2013-2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{u} =} fastmarching (@var{u0}, @var{f})
+## @deftypefnx {Function File} {[@var{u}, @var{g}] =} fastmarching (@var{u0}, @var{g0}, @var{f})
+##
+## Solve the Eikonal equation using the Fast-Marching Method. In particular,
+## the equation
+## @tex
+## \begin{equation*}
+## \left| \nabla u \right| = f
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+##
+## @example
+## | grad u | = f
+## @end example
+##
+## @end ifnottex
+## is solved on a multi-dimensional domain. @var{u0} and @var{f} must be
+## multi-dimensional arrays of the same dimension and size, and the returned
+## solution @var{u} will be also of this size. @var{f} should be positive,
+## and we assume the grid spacing is unity (or, equivalently, has been
+## absorbed into f already).
+##
+## @var{u0} defines the initial setting and the domain. It should contain the
+## initial distances for points that are used as initially alive points,
+## be @code{Inf} for points not part of the domain, and @code{NA} at all
+## other points. The latter are considered as far-away at the beginning
+## and their distances will be calculated. @var{f} is ignored at all but
+## the far-away points.
+##
+## If @var{g0} is also given, it should contain function values on initially
+## alive points that will be extended onto the domain along the
+## ``normal directions''. This means that
+## @tex
+## \begin{equation*}
+## \nabla g \cdot \nabla u = 0
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+##
+## @example
+## grad g * grad u = 0
+## @end example
+##
+## @end ifnottex
+## will be fulfilled for the solution @var{u} of the Eikonal equation.
+## @var{g} will be @code{NA} at points not in the domain or not reached by the
+## fast marching.
+##
+## At return, @var{u} will contain the calculated distances.
+## @code{Inf} values will be retained, and it may still contain @code{NA}
+## at points to which no suitable path can be found.
+##
+## @seealso{ls_signed_distance, ls_solve_stationary}
+## @end deftypefn
+
+function [u, g] = fastmarching (u0, arg2, arg3)
+ if (nargin () == 2)
+ f = arg2;
+ g0 = NA (size (u0));
+ if (nargout () > 1)
+ print_usage ();
+ endif
+ elseif (nargin () ~= 3)
+ print_usage ();
+ else
+ assert (nargin () == 3)
+ f = arg3;
+ g0 = arg2;
+ endif
+
+ where = isna (u0);
+ if (~all (f(where) > 0))
+ error ("F must be positive");
+ endif
+
+ sz = size (u0);
+ if (~all (sz == size (f)))
+ error ("U0 and F must be of the same size");
+ endif
+ if (~all (sz == size (g0)))
+ error ("U0 and G0 must be of the same size");
+ endif
+
+ domain = ~isinf (u0);
+ alive = ~isna (u0);
+ if (~all (alive(domain) == (isfinite (u0(domain)))))
+ error ("U0 shold only contain Inf, NA and finite values");
+ endif
+
+ [u, g, reached] = __levelset_internal_fastmarching (domain, u0, g0, alive, f);
+ u(~reached) = NA;
+ u(~domain) = Inf;
+ g(~reached | ~domain) = NA;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! fastmarching ()
+%!error <Invalid call to>
+%! fastmarching (1, 2, 3, 4)
+%!error <Invalid call to>
+%! [a, b] = fastmarching (1, 2)
+%!error <U0 and F must be of the same size>
+%! fastmarching ([1, 2], [1; 2])
+%!error <U0 and G0 must be of the same size>
+%! fastmarching ([1, 2], [1; 2], [1, 2])
+%!error <F must be positive>
+%! fastmarching ([NA, 1], [0, 5]);
+
+% Basic test for obstacle with a square for which propagation is only
+% allowed along the outer edges.
+%!test
+%! n = 600;
+%! x = linspace (0, 1, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%! u0 = NA (n, n);
+%! u0(2 : end - 1, 2 : end - 1) = Inf;
+%! u0(1, 1) = 0;
+%! f = 1 + YY;
+%! u = fastmarching (u0, h * f);
+%!
+%! % Length of vertical side: integrate (1 + y, y, 0, x)
+%! vertLength = @(x) x/2 .* (x + 2);
+%!
+%! % Speed difference at top and bottom, gives where the
+%! % paths will meet on the bottom side.
+%! % solve (x * fBot = fTop + (1 - x) * fBot, x)
+%! fTop = mean (f(1, :));
+%! fBot = mean (f(end, :));
+%! xMeet = (fTop + fBot) / 2 / fBot;
+%! botRow = vertLength (1) + fBot * x;
+%! where = (x > xMeet);
+%! botRow(where) = vertLength (1) + 1 + fBot * (1 - x(where));
+%!
+%! assert (u(1, :), x, sqrt (eps));
+%! assert (u(:, 1), vertLength (x'), 1e-3);
+%! assert (u(:, end), 1 + vertLength (x'), 1e-3);
+%! assert (u(end, :), botRow, 1e-3);
+
+% Check 1D situation. Also verifies that it works with non-zero initial values.
+%!test
+%! n = 1100;
+%! x = linspace (0, 1, n);
+%! h = x(2) - x(1);
+%!
+%! obst1 = round (0.5 / h);
+%! obst2 = round (0.75 / h);
+%! s1 = round (0.2 / h);
+%! s1val = 1;
+%! s2 = round (0.9 / h);
+%! s2val = -1;
+%!
+%! u0 = NA (n, 1);
+%! u0([obst1, obst2]) = Inf;
+%! u0([s1, s2]) = [s1val, s2val];
+%! f = ones (size (u0));
+%! g0 = NA (size (u0));
+%! g0([s1, s2]) = [s1val, s2val];
+%!
+%! [u, g] = fastmarching (u0, g0, f * h);
+%!
+%! gDesired = NA (size (u0));
+%! gDesired(1 : obst1 - 1) = s1val;
+%! gDesired(obst2 + 1 : end) = s2val;
+%! assert (g, gDesired, sqrt (eps));
+%!
+%! uDesired = NA (size (u0));
+%! uDesired([obst1, obst2]) = Inf;
+%! uDesired(1 : obst1 - 1) = s1val + abs (s1 * h - x(1 : obst1 - 1));
+%! uDesired(obst2 + 1 : end) = s2val + abs (s2 * h - x(obst2 + 1 : end));
+%! assert (u, uDesired, 1e-3);
+
+% Check 0D situation (which is trivial but nevertheless).
+%!test
+%! assert (fastmarching ([], []), []);
+
+% Extend from a circular initial region outwards, also including a function
+% value which is the angle. For this configuration, the expected result
+% is easy to calculate. Test in 3D.
+%!test
+%! n = 42;
+%! r = 1;
+%! R = 2;
+%! x = linspace (-R, R, n);
+%! h = x(2) - x(1);
+%!
+%! [XX, YY, ZZ] = ndgrid (x);
+%! RR = sqrt (XX.^2 + YY.^2 + ZZ.^2);
+%! PPhi = NA (size (XX));
+%! for i = 1 : numel (PPhi)
+%! pt = [XX(i), YY(i), ZZ(i)];
+%! PPhi(i) = acos (dot (pt, [0, 0, 1]) / norm (pt));
+%! endfor
+%!
+%! initial = (RR < r);
+%! u0 = NA (size (XX));
+%! u0(initial) = 0;
+%! g0 = NA (size (XX));
+%! g0(initial) = PPhi(initial);
+%! f = ones (size (u0));
+%! [u, g] = fastmarching (u0, g0, h * f);
+%!
+%! assert (u(~initial), RR(~initial) - r, 1e-1);
+%! assert (g(~initial), PPhi(~initial), 2e-1);
+
+% Function for calculating the approximate gradient of a given function
+% using central differences. This is used to verify grad g * grad u = 0.
+%!function [dx, dy] = gradient (fcn, h)
+%! ext = NA (size (fcn) + 2);
+%! ext(2 : end - 1, 2 : end - 1) = fcn;
+%! ext(:, 1) = 2 * ext(:, 2) - ext(:, 3);
+%! ext(:, end) = 2 * ext(:, end - 1) - ext(:, end - 2);
+%! ext(1, :) = 2 * ext(2, :) - ext(3, :);
+%! ext(end, :) = 2 * ext(end - 1, :) - ext(end - 2, :);
+%! dx = (ext(2 : end - 1, 3 : end) - ext(2 : end - 1, 1 : end - 2)) / (2 * h);
+%! dy = (ext(3 : end, 2 : end - 1) - ext(1 : end - 2, 2 : end - 1)) / (2 * h);
+%!endfunction
+
+% Test extending of function values in a more general setting. We verify
+% that the "defining equation"
+%
+% grad g * grad u = 0
+%
+% is indeed valid.
+%!test
+%! n = 1000;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi0 = 2 * XX.^2 + 0.4 * YY.^2 - 1;
+%! initial = (phi0 < 0);
+%! %contour (XX, YY, phi0, [0, 0], "r");
+%!
+%! f = ones (size (XX));
+%! u0 = NA (size (XX));
+%! u0(initial) = 0;
+%! g0 = NA (size (XX));
+%! g0(initial) = sin (atan2 (YY(initial), XX(initial)));
+%!
+%! [u, g] = fastmarching (u0, g0, h * f);
+%!
+%! [dxu, dyu] = gradient (u, h);
+%! [dxg, dyg] = gradient (g, h);
+%!
+%! % We only verify the mean error here, since there tend
+%! % to be lower-dimensional lines on which the error is
+%! % large while it is very low otherwise.
+%! err = abs (dxu .* dxg + dyu .* dyg);
+%! assert (mean (err(initial)), 0, 1e-3);
+
+% Particular test situation where during the fast marching
+% procedure the distance of a point is later calculated to
+% be further from the initial set.
+%!test
+%! u0 = [1.257221, NA, NA; Inf, 0.275128, 0.433001];
+%! f = [NA, 0.85714, 0.66667; NA, NA, NA];
+%! u = fastmarching (u0, f);
+
+% Test the case of far-away alive point.
+%!test
+%! u0 = [0, NA; Inf, 2];
+%! f = ones (size (u0));
+%! u = fastmarching (u0, f);
+%! assert (u(1, 2), 1, sqrt (eps));
+
+% Test for the case with multiple alive neighbours along a single dimension.
+%!test
+%! u0 = [0, NA, NA, NA, 0.1; ...
+%! Inf, Inf, Inf, Inf, Inf; ...
+%! 0.1, NA, NA, NA, 0];
+%! f = ones (size (u0));
+%! u = fastmarching (u0, f);
+%!
+%! uExpected = [0, 1, 2, 1.1, 0.1; ...
+%! Inf, Inf, Inf, Inf, Inf; ...
+%! 0.1, 1.1, 2, 1, 0];
+%! assert (u, uExpected, sqrt (eps));
+
+% Estimate the runtime complexity.
+%!test
+%! ns = [101, 201, 501, 1001, 2001];
+%! realNs = NA (size (ns));
+%! times = NA (size (ns));
+%! for i = 1 : length (ns)
+%! nMid = (ns(i) + 1) / 2;
+%! assert (nMid, round (nMid));
+%! u0 = NA (ns(i), ns(i));
+%! u0(nMid, nMid) = 0;
+%! f = ones (size (u0));
+%! realNs(i) = numel (u0);
+%!
+%! old = cputime ();
+%! u = fastmarching (u0, f);
+%! new = cputime ();
+%! times(i) = new - old;
+%!
+%! printf ("n = %4d: t = %6.3f s\n", ns(i), times(i));
+%! endfor
+%!
+%! nLogN = realNs .* log (realNs);
+%! p = polyfit (log (realNs), log (times), 1);
+%! pLogN = polyfit (log(nLogN), log (times), 1);
+%! printf ("Exponents:\n O(n): %4.2f\n O(n log n): %4.2f\n", ...
+%! p(1), pLogN(1));
+%! assert (p(1), 1, 1e-1);
+%! assert (pLogN(1), 1, 1e-1);
+
+% Compare the solution of the level-set equation using finite differences
+% and a viscosity term to our formula. If the initial profile is constructed
+% by fastmarching to satisfy F | grad phi0 | = 1, then it should just
+% be shifted down in time.
+%!test
+%! T = 1.6;
+%! timeSteps = 10000;
+%! L = 2;
+%! gridSteps = 10000;
+%! eps = 1e-4;
+%! floorValue = -1/2;
+%! tol = 7e-2;
+%!
+%! t = linspace (0, T, timeSteps);
+%! dt = t(2) - t(1);
+%! x = linspace (-L/2, L/2, gridSteps);
+%! h = x(2) - x(1);
+%!
+%! F = sqrt (abs (x));
+%! phi0 = NA (size (x));
+%! phi0(1 : end/4) = floorValue;
+%! phi0(3*end/4 : end) = floorValue;
+%! phi0 = fastmarching (phi0, h ./ F);
+%!
+%! % Plot the speed field and initial data.
+%! %{
+%! figure ();
+%! plot (x, F, "r", x, phi0, "k");
+%! legend ("F", "\\phi_0");
+%! title ("Speed Field and Initial Data");
+%! ax = [-L/2, L/2, -1, 1];
+%! axis (ax);
+%! %}
+%!
+%! % Create the Laplacian as matrix. We assume homogeneous Neumann boundary
+%! % conditions so that the initial data can be chosen as we like.
+%! Delta = spdiags (repmat ([1, -2, 1], gridSteps, 1), [-1, 0, 1], ...
+%! gridSteps, gridSteps);
+%! Delta(1, 1) += Delta(1, 2);
+%! Delta(end, end) += Delta(end - 1, end);
+%! Delta /= h^2;
+%!
+%! % Perform the calculation. The Laplacian is done implicitly, the
+%! % non-linear term explicitly:
+%! %
+%! % phi+ - phi0 = dt (eps Delta phi+ - F |grad phi0|)
+%! % => (I - dt eps Delta) phi+ = phi0 - F |grad phi0|
+%!
+%! %figure ();
+%! phi = phi0';
+%! M = eye (gridSteps) - dt * eps * Delta;
+%! for tInd = 2 : timeSteps
+%!
+%! % Calculate derivatives as symmetric differences, and zero on the
+%! % boundary. Since the data is flat there, this is correct.
+%! d = NA (size (phi));
+%! low = 1 : gridSteps - 2;
+%! mid = 2 : gridSteps - 1;
+%! high = 3 : gridSteps;
+%! d(mid) = (phi(high) - phi(low)) / (2 * h);
+%! d([1, end]) = 0;
+%! gradNorm = abs (d);
+%!
+%! % Do the solve.
+%! phi = M \ (phi - dt * F' .* gradNorm);
+%! phiEx = max (phi0 - t(tInd), floorValue);
+%! assert (phi, phiEx', tol);
+%!
+%! % Show solution as animation in real time.
+%! %{
+%! if (mod (tInd, 100) == 0)
+%! plot (x, phi, "r", x, phiEx, "k");
+%! legend ("Approximate", "Exact");
+%! axis (ax);
+%! title (sprintf ("t = %.2f", t(tInd)));
+%! drawnow ();
+%! %sleep (0.1);
+%! endif
+%! %}
+%! endfor
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Plot how the distance and an extended function "flow" around an obstacle.
+%!demo
+%! n = 100;
+%! x = linspace (-1, 1, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! u0 = NA (size (XX));
+%! u0(sqrt (XX.^2 + YY.^2) < 0.5) = Inf;
+%! u0(end, :) = 0;
+%! f = 3 + XX;
+%! g0 = NA (size (XX));
+%! g0(end, :) = x;
+%!
+%! [u, g] = fastmarching (u0, g0, h * f);
+%!
+%! figure ();
+%! subplot (2, 2, 1);
+%! imagesc (f);
+%! colorbar ();
+%! title ("Speed Field");
+%! subplot (2, 2, 2);
+%! imagesc (u0);
+%! colorbar ();
+%! title ("Initial Distances");
+%! subplot (2, 2, 3);
+%! imagesc (u);
+%! colorbar ();
+%! title ("Distance u");
+%! subplot (2, 2, 4);
+%! imagesc (g);
+%! colorbar ();
+%! title ("Extended Function g");
+
+% Demo solving a "maze".
+%!demo
+%! pkgs = pkg ("list");
+%! file = false;
+%! for i = 1 : length (pkgs)
+%! if (strcmp (pkgs{i}.name, "level-set"))
+%! file = fullfile (pkgs{i}.dir, "maze.png");
+%! endif
+%! endfor
+%! if (!file)
+%! error ("could not locate example image file");
+%! endif
+%! img = double (imread (file)) / 255;
+%!
+%! % Red is the start area, white (not black) the accessible domain.
+%! start = (img(:, :, 1) > img(:, :, 2) + img(:, :, 3));
+%! domain = (img(:, :, 2) + img(:, :, 3) > 1.0) | start;
+%!
+%! u0 = NA (size (domain));
+%! u0(start) = 0;
+%! u0(~domain) = Inf;
+%! f = ones (size (u0));
+%!
+%! u = fastmarching (u0, f);
+%! u = u / max (u(isfinite (u)));
+%!
+%! % Format result as image.
+%! walls = isinf (u);
+%! notReached = isna (u);
+%! ur = u;
+%! ug = u;
+%! ub = u;
+%! ur(notReached) = 1;
+%! ug(notReached) = 0;
+%! ub(notReached) = 0;
+%! ur(walls) = 0;
+%! ug(walls) = 0;
+%! ub(walls) = 1;
+%! ur(start) = 1;
+%! ug(start) = 1;
+%! ub(start) = 0;
+%! u = NA (size (u, 1), size (u, 2), 3);
+%! u(:, :, 1) = ur;
+%! u(:, :, 2) = ug;
+%! u(:, :, 3) = ub;
+%! figure ();
+%! imshow (u);
+%! title ("Solved Maze. Blue: Walls, Red: Unreachable, Yellow: Start.");
diff --git a/inst/ls_absolute_geom.m b/inst/ls_absolute_geom.m
new file mode 100644
index 0000000..bfbd0da
--- /dev/null
+++ b/inst/ls_absolute_geom.m
@@ -0,0 +1,82 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{geom} =} ls_absolute_geom (@var{geom}, @var{XX}, @var{YY})
+##
+## Extend the geometry structure @var{geom} of @code{ls_find_geometry}
+## to include absolute coordinates. In addition to @code{ls_find_geometry},
+## this function has access to absolute grid-point coordinates, and uses them
+## to set the additional @code{@var{geom}.ispts.coord} field to absolute
+## coordinates. The format is the same as the relative coordinates
+## in @code{@var{geom}.ispts.incoord}. Also, it adds the new entry
+## @code{nodes} to @var{geom}, with the following fields:
+##
+## @table @code
+## @item n
+## Total number of nodes. This is the number of entries in @var{phi}.
+##
+## @item coord
+## @code{n} x 2 matrix containing the absolute coordinates of each node.
+## The nodes are numbered in the range 1-- at code{n} in the internal
+## ordering of Octave.
+## @end table
+##
+## Currently, only 2D is supported. @var{XX} and @var{YY} should be the
+## grid-point coordinates according to @code{meshgrid} or @code{ndgrid}.
+##
+## @seealso{ls_find_geometry, meshgrid, ndgrid}
+## @end deftypefn
+
+function geom = ls_absolute_geom (geom, XX, YY)
+ if (nargin () ~= 3)
+ print_usage ();
+ endif
+
+ sz = size (XX);
+ if (length (sz) ~= 2 || any (sz < 2))
+ error ("ls_absolute_geom is only implemented for 2D");
+ endif
+ if (any (sz ~= size (YY)))
+ error ("sizes mismatch");
+ endif
+
+ xCoords = XX(:);
+ yCoords = YY(:);
+ inpts = geom.ispts.inout(:, 1);
+ geom.ispts.coord = [xCoords(inpts), yCoords(inpts)] + geom.ispts.incoord;
+ geom.nodes = struct ("n", prod (sz), "coord", [xCoords, yCoords]);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_absolute_geom (1, 2);
+%!error <Invalid call to>
+%! ls_absolute_geom (1, 2, 3, 4);
+%!error <ls_absolute_geom is only implemented for 2D>
+%! x = [-1, 1];
+%! [XX, YY, ZZ] = ndgrid (x);
+%! ls_absolute_geom (struct (), XX, YY);
+%!error <ls_absolute_geom is only implemented for 2D>
+%! ls_absolute_geom (struct (), [-1, 0, 1], [0, 0, 0]);
+%!error <sizes mismatch>
+%! ls_absolute_geom (struct (), zeros (3, 2), zeros (2, 2));
+
+% No real functional test for now. It is 'tested' in the demo
+% of ls_find_geometry, though.
diff --git a/inst/ls_animate_evolution.m b/inst/ls_animate_evolution.m
new file mode 100644
index 0000000..e0335a0
--- /dev/null
+++ b/inst/ls_animate_evolution.m
@@ -0,0 +1,71 @@
+## Copyright (C) 2014-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} ls_animate_evolution (@var{phi}, @var{f}, @var{h}, @var{times}, @var{wait})
+##
+## Animate the evolution of a level-set geometry. The evolution
+## is calculated with @code{ls_solve_stationary} and @code{ls_extract_solution}
+## for the given arguments, and the result plotted in the current figure
+## for the times given in @var{times}. Between updating to the next
+## ``movie frame'', sleep for @var{wait} seconds.
+##
+## @seealso{ls_solve_stationary, ls_extract_solution}
+## @end deftypefn
+
+function ls_animate_evolution (phi, f, h, times, wait)
+ if (nargin () != 5)
+ print_usage ();
+ endif
+
+ d = ls_solve_stationary (phi, f, h);
+
+ % The figure is cleared and the speed plotted only once,
+ % to prevent ugly "jittering". At each step, only the
+ % last contour plot object is removed to re-draw it.
+
+ clf ();
+ hold ("on");
+ imagesc (f);
+ ls_sign_colourmap ("highlight");
+ set (gca (), "ydir", "normal");
+
+ handle = false;
+ for t = times
+ phit = ls_extract_solution (t, d, phi, f);
+ if (handle)
+ delete (handle);
+ endif
+ [~, handle] = contour (phit, [0, 0], "k", "LineWidth", 2);
+ axis ("equal");
+ drawnow ();
+ sleep (wait);
+ endfor
+ hold ("off");
+endfunction
+
+% Simple demo with a basic situation.
+%!demo
+%! n = 100;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], 1.7);
+%! f = -XX.^2 - YY.^2;
+%!
+%! times = linspace (0, 1, 60);
+%! ls_animate_evolution (phi, f, h, times, 0.05);
diff --git a/inst/ls_build_mesh.m b/inst/ls_build_mesh.m
new file mode 100644
index 0000000..5f55212
--- /dev/null
+++ b/inst/ls_build_mesh.m
@@ -0,0 +1,271 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {[@var{mesh}, @var{vmap}] =} ls_build_mesh (@var{geom}, @var{phi}, @var{glob} = false)
+##
+## Build a triangle mesh for the level-set geometry described by
+## @var{geom} and by the level-set function @var{phi}. Note that
+## @var{geom} must contain absolute coordinates as set by
+## @code{ls_absolute_geom}.
+##
+## If @var{glob} is set to @code{true}, then the mesh will contain
+## the whole hold-all domain and not just the interior of the level-set
+## geometry. It will, however, be constructed such that the boundary
+## of the level-set domain is resolved precisely, and such that the
+## level-set domain can be described as a subset of the mesh triangles.
+##
+## The mesh points will be made up @emph{both} of grid nodes as well as
+## intersection points. While all intersection points will appear
+## as mesh nodes, unless @var{glob} is set, only @strong{inner} grid nodes
+## will correspond to mesh points. The points on the mesh are numbered
+## differently to the points in @var{geom}, although the indices can be
+## translated both ways using @var{vmap} (see below).
+##
+## On the other hand, the boundary edges of the mesh match precisely the
+## edges given in @code{@var{geom}.bedges}. They have the same indices
+## in both data structures.
+##
+## The returned struct @var{mesh} follows the format of the @code{msh}
+## package, which means that it contains these fields:
+##
+## @table @code
+## @item p
+## 2 x @var{np} matrix which contains the x- and y-coordinates
+## of the mesh points in its two rows.
+##
+## @item t
+## 4 x @var{nt} matrix describing the mesh triangles.
+## The first three rows contain the indices of mesh points making up the
+## triangle, and the fourth row contains the index of the geometrical
+## surface this triangle belongs to. It is always set to @code{1} for now.
+## The points are ordered @strong{counter-clockwise} (in the coordinate-system
+## interpretation of the grid).
+##
+## @item e
+## 7 x @var{ne} matrix describing the edges of the mesh. They
+## correspond precisely to @code{@var{geom}.bedges}.
+## The rows of this matrix are:
+##
+## @table @asis
+## @item 1--2
+## Start- and end-point index. They are ordered such that
+## the level-set domain is @strong{on the left} of the edge.
+##
+## @item 3--4
+## Always @code{0} for compatibility.
+##
+## @item 5
+## The gamma component index (as per @code{@var{geom}.gamma}) that
+## contains this edge. Will be in the range 1-- at code{@var{geom}.gamma.n}.
+##
+## @item 6--7
+## Geometrical surface index of the domain parts to the right
+## and left. They are set to @code{0} and @code{1} for now.
+## @end table
+## @end table
+##
+## The second output, @var{vmap}, describes the mappings between geometrical
+## node / intersection-point indices in @var{geom} and the indices of the
+## mesh points used in @var{mesh}. It is a struct with these fields:
+##
+## @table @code
+## @item grid
+## Maps the index of a node on the grid to the index of the
+## corresponding mesh point. The value is @code{NA} if the mesh
+## does not contain the point.
+##
+## @item ispt
+## Maps the index of an intersection point (according to
+## @code{@var{geom}.ispts}) to the index of the corresponding mesh point.
+##
+## @item mesh
+## Maps the index of a mesh point back to either the grid or intersection-point
+## index. A @strong{positive} value means that the mesh point is on the
+## grid and has the respective index, while a @strong{negative} value means
+## that it is an intersection point with index equal to the absolute value
+## of the entry.
+## @end table
+##
+## @seealso{ls_find_geometry, ls_absolute_geom, msh2m_structured_mesh}
+## @end deftypefn
+
+function [mesh, vmap] = ls_build_mesh (geom, phi, glob = false)
+ if (nargin () < 2 || nargin () > 3)
+ print_usage ();
+ endif
+
+ if (!isfield (geom.ispts, "coord") || !isfield (geom, "nodes"))
+ error ("GEOM misses absolute coordinates, use ls_absolute_geom first");
+ endif
+
+ % Find inside array.
+ L = geom.dim(1);
+ M = geom.dim(2);
+ inside = ls_inside (phi(:));
+
+ % Call internal routine.
+ [mesh, vmap] ...
+ = __levelset_internal_mesh (L, M, inside, ...
+ geom.nodes.coord, geom.ispts.coord, ...
+ geom.elem.nodelist, geom.elem.index.inner, ...
+ geom.elem.index.outer, geom.elem.index.bdry, ...
+ geom.internal.bdryelSegments, glob);
+ % FIXME: Should be 0 for outside triangles (in global mesh)?
+ mesh.t(4, :) = 1; % Geometrical component.
+
+ % Set mesh vertexMap entries to NA where they are -1. This is not already
+ % done in the C++ code since it uses integers instead of doubles
+ % for the index arrays being built up.
+ vmap.grid(vmap.grid < 0) = NA;
+ assert (all (vmap.ispt > 0));
+ assert (all (isfinite (vmap.mesh)));
+
+ % Set boundary table from geom.bedges. We only have to convert
+ % geometrical ispt indices to mesh vertex indices.
+ mesh.e = NA (7, geom.bedges.n);
+ mesh.e(1 : 2, :) = vmap.ispt(geom.bedges.ispts');
+ mesh.e([3:4, 6], :) = 0; % Reserved and outside on the right.
+ mesh.e(5, :) = geom.bedges.comp; % Boundary component.
+ mesh.e(7, :) = 1; % Domain on the left.
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_build_mesh (1)
+%!error <Invalid call to>
+%! ls_build_mesh (1, 2, 3, 4)
+
+% Test for missing absolute coordinates.
+%!error <GEOM misses absolute coordinates, use ls_absolute_geom first>
+%! x = linspace (-10, 10, 10);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], 5);
+%! phi = ls_normalise (phi, h);
+%! geom = ls_find_geometry (phi, h);
+%! ls_build_mesh (geom, phi);
+
+% Helper function to verify that two points are in counter-clockwise
+% direction with respect to a third one.
+%!function verifyDirection (a, b, centre)
+%! v1 = [a - centre; 0];
+%! v2 = [b - centre; 0];
+%! x = cross (v1, v2);
+%! assert (x(1 : 2), zeros (2, 1));
+%! assert (x(3) > 0);
+%!endfunction
+
+% A function to verify certain expected properties / postconditions
+% for a given mesh.
+%!function verifyMesh (geom, phi, glob)
+%! [mesh, vmap] = ls_build_mesh (geom, phi, glob);
+%! nPts = prod (geom.dim);
+%!
+%! % Verify ordering of triangle points.
+%! for i = 1 : size (mesh.t, 2)
+%! verifyDirection (mesh.p(:, mesh.t(1, i)), mesh.p(:, mesh.t(2, i)), ...
+%! mesh.p(:, mesh.t(3, i)));
+%! endfor
+%!
+%! % Go through all grid points and verify them with the mesh.
+%! for i = 1 : nPts
+%! gridInd = vmap.grid(i);
+%! if (isna (gridInd))
+%! assert (!glob);
+%! continue;
+%! endif
+%!
+%! assert (vmap.mesh(gridInd), i);
+%! assert (mesh.p(:, gridInd), geom.nodes.coord(i, :)');
+%! endfor
+%!
+%! % Go through all intersection points and verify them.
+%! for i = 1 : geom.ispts.n
+%! gridInd = vmap.ispt(i);
+%! assert (vmap.mesh(gridInd), -i);
+%! assert (mesh.p(:, gridInd), geom.ispts.coord(i, :)');
+%! endfor
+%!
+%! % Verify mesh.e against geom.bedges.
+%! for i = 1 : geom.bedges.n
+%! assert (mesh.e(1 : 2, i), vmap.ispt(geom.bedges.ispts(i, :)'));
+%! assert (mesh.e(5, i), geom.bedges.comp(i));
+%! endfor
+%!endfunction
+%
+% Verify a given phi, by first constructing the geometry and mesh
+% both in global and local mode as well as trying both phi and -phi.
+%!function verifyPhi (phi, XX, YY, h)
+%! for s = [-1, 1]
+%! cur = phi * s;
+%! cur = ls_normalise (cur, h);
+%! geom = ls_find_geometry (cur, h);
+%! geom = ls_absolute_geom (geom, XX, YY);
+%!
+%! verifyMesh (geom, cur, false);
+%! verifyMesh (geom, cur, true);
+%! endfor
+%!endfunction
+
+% Test against some example phis. We do not use ls_get_tests, since
+% we only want phis where we have a grid, too.
+%!test
+%! n = 20;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_union (ls_genbasic (XX, YY, "sphere", [-6, -6], 3), ...
+%! ls_genbasic (XX, YY, "sphere", [6, 6], 3));
+%! verifyPhi (phi, XX, YY, h);
+%!
+%! phi = ls_setdiff (ls_genbasic (XX, YY, "sphere", [0, 0], 8), ...
+%! ls_genbasic (XX, YY, "sphere", [0, 0], 4));
+%! verifyPhi (phi, XX, YY, h);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+% Build a mesh both in global and non-global mode and plot the result.
+%!demo
+%! x = linspace (-10, 10, 11);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_union (ls_genbasic (XX, YY, "sphere", [5, 5], 4.5), ...
+%! ls_genbasic (XX, YY, "sphere", [-2, -2], 4));
+%! phi = ls_normalise (phi, h);
+%! geom = ls_find_geometry (phi, h);
+%! geom = ls_absolute_geom (geom, XX, YY);
+%!
+%! mesh = ls_build_mesh (geom, phi, false);
+%! meshGlob = ls_build_mesh (geom, phi, true);
+%!
+%! figure ();
+%! hold ("on");
+%! trimesh (meshGlob.t(1 : 3, :)', meshGlob.p(1, :), meshGlob.p(2, :), "r");
+%! trimesh (mesh.t(1 : 3, :)', mesh.p(1, :), mesh.p(2, :), "g");
+%! for i = 1 : size (mesh.e, 2)
+%! plot (mesh.p(1, mesh.e(1 : 2, i)), mesh.p(2, mesh.e(1 : 2, i)), "b");
+%! endfor
+%! hold ("off");
+%! legend ("Global", "Domain", "Boundary");
+%! axis ("equal");
+%! axis ("square");
diff --git a/inst/ls_check.m b/inst/ls_check.m
new file mode 100644
index 0000000..1e0db64
--- /dev/null
+++ b/inst/ls_check.m
@@ -0,0 +1,98 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{ok} =} ls_check (@var{phi}, @var{type}, @var{where})
+##
+## Check a geometric constraint for the level-set function @var{phi}.
+##
+## @var{type} specifies what the constraint
+## should be, and @var{where} should be a logical array of the same size
+## as the grid (and thus @var{phi}), specifying which grid points are
+## part of the set that defines the constraint.
+## Possible values for @var{type}:
+##
+## @table @asis
+## @item @qcode{"inside"}
+## The domain should be inside the region marked as @var{where}.
+##
+## @item @qcode{"outside"}
+## The domain should not intersect the region marked as @var{where}.
+##
+## @item @qcode{"contain"}
+## The domain should always contain the region marked in @var{where}.
+## @end table
+##
+## @seealso{ls_enforce, ls_enforce_speed, ls_inside}
+## @end deftypefn
+
+function ok = ls_check (phi, type, where)
+ if (nargin () ~= 3)
+ print_usage ();
+ endif
+
+ if (~all (size (phi) == size (where)))
+ error ("PHI and WHERE must be of the same size");
+ endif
+ if (~strcmp (typeinfo (type), "string"))
+ error ("TYPE must be a string");
+ endif
+
+ inside = ls_inside (phi);
+ switch (type)
+ case "inside"
+ ok = all (~inside(~where));
+ case "outside"
+ ok = all (~inside(where));
+ case "contain"
+ ok = all (inside(where));
+ otherwise
+ error ("invalid value '%s' for TYPE argument", type);
+ endswitch
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_check (1, 2)
+%!error <Invalid call to>
+%! ls_check (1, 2, 3, 4)
+%!error <PHI and WHERE must be of the same size>
+%! ls_check (1, "inside", [1, 2]);
+%!error <invalid value 'foo' for TYPE argument>
+%! ls_check (1, "foo", true);
+%!error <TYPE must be a string>
+%! ls_check (1, NA, false);
+
+% Basic tests for the cases.
+%!test
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! circ2 = (XX.^2 + YY.^2 < 2^2);
+%! circ8 = (XX.^2 + YY.^2 < 8^2);
+%! phi = (XX.^2 + YY.^2 - 5^2);
+%!
+%! assert (ls_check (phi, "inside", circ8));
+%! assert (ls_check (phi, "contain", circ2));
+%! assert (ls_check (phi, "outside", ~circ8));
+%!
+%! assert (~ls_check (phi, "inside", circ2));
+%! assert (~ls_check (phi, "contain", circ8));
+%! assert (~ls_check (phi, "outside", circ8));
diff --git a/inst/ls_complement.m b/inst/ls_complement.m
new file mode 100644
index 0000000..b822131
--- /dev/null
+++ b/inst/ls_complement.m
@@ -0,0 +1,66 @@
+## Copyright (C) 2014-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_complement (@var{phi})
+##
+## Construct a level-set function for the complement of the set
+## described by @var{phi}.
+##
+## This operation preserves a signed distance function: If @var{phi}
+## is actually the signed distance function of the set, then also
+## the return value will be the signed distance function of the set's
+## complement.
+##
+## @seealso{ls_union, ls_intersect, ls_setdiff, ls_setxor}
+## @end deftypefn
+
+function phi = ls_complement (phi)
+ if (nargin () ~= 1)
+ print_usage ();
+ endif
+
+ phi = -phi;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_complement ()
+%!error <Invalid call to>
+%! ls_complement (1, 2)
+
+% Basic tests for the function.
+%!test
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 2).^2 + (YY - 2).^2 - 2^2;
+%! phi2 = (XX + 2).^2 + (YY + 2).^2 - 2^2;
+%!
+%! assert (ls_disjoint (phi1, ls_complement (phi1)));
+%! assert (ls_disjoint (phi2, ls_complement (phi2)));
+%!
+%! assert (~ls_disjoint (phi1, ls_complement (phi2)));
+%! assert (~ls_disjoint (phi2, ls_complement (phi1)));
+%!
+%! sd1 = ls_signed_distance (ls_complement (phi1), h);
+%! sd2 = ls_complement (ls_signed_distance (phi1, h));
+%! assert (sd1, sd2, sqrt (eps));
diff --git a/inst/ls_disjoint.m b/inst/ls_disjoint.m
new file mode 100644
index 0000000..47f2fe8
--- /dev/null
+++ b/inst/ls_disjoint.m
@@ -0,0 +1,83 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{res} =} ls_disjoint (@var{phi1}, @var{phi2})
+## @deftypefnx {Function File} {@var{res} =} ls_disjoint (@var{phi}, ...)
+##
+## Check if all the sets described by the given level-set functions
+## are disjoint.
+##
+## @seealso{ls_inside, ls_equal, is_issubset, ls_check}
+## @end deftypefn
+
+function res = ls_disjoint (varargin)
+ if (length (varargin) < 1)
+ print_usage ();
+ endif
+
+ % The algorithm is like this: Go over the list of sets once
+ % and keep a "running union" of the sets already processed. Check
+ % that each new set is disjoint to this union.
+ total = varargin{1};
+ for i = 2 : length (varargin)
+ phi = varargin{i};
+ if (~all (size (total) == size (phi)))
+ error ("size mismatch in the arguments");
+ endif
+
+ common = (ls_inside (phi) & ls_inside (total));
+ if (any (common(:)))
+ res = false;
+ return;
+ endif
+
+ total = ls_union (total, phi);
+ endfor
+
+ % No intersection found.
+ res = true;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_disjoint ()
+%!error <size mismatch in the arguments>
+%! ls_disjoint (1, -2, [1, 2])
+
+% Basic test with some constructed examples.
+%!test
+%! assert (ls_disjoint ([-1, 0, 1], [0, 0, 0], [1, 0, -1]));
+%! assert (~ls_disjoint ([Inf, -Inf, 1], [-1, -1, 1]));
+%! assert (~ls_disjoint ([-1, -1, 1], [0, 0, 0], [1, -1, -1]));
+
+% Test in 2D with some circles.
+%!test
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 2).^2 + (YY - 2).^2 - 2^2;
+%! phi2 = (XX + 2).^2 + (YY + 2).^2 - 2^2;
+%! phi3 = XX.^2 + YY.^2 - 2^2;
+%!
+%! assert (ls_disjoint (phi1, phi2));
+%! assert (~ls_disjoint (phi1, phi3));
+%! assert (~ls_disjoint (phi2, phi3));
+%! assert (~ls_disjoint (phi1, phi2, phi3));
diff --git a/inst/ls_distance_fcn.m b/inst/ls_distance_fcn.m
new file mode 100644
index 0000000..8b54435
--- /dev/null
+++ b/inst/ls_distance_fcn.m
@@ -0,0 +1,133 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{d} =} ls_distance_fcn (@var{phi}, @var{h} = 1)
+##
+## Calculate the distance function of a set. The domain is described by
+## its level-set function, whose values on a rectangular grid with spacing
+## @var{h} are given in the array @var{phi}.
+##
+## In contrast to @code{ls_signed_distance}, which calculates the
+## @emph{signed} distance function, @var{d} will be set to zero
+## on interior points. This allows @code{ls_distance_fcn} to be
+## faster, as only exterior points are processed.
+##
+## It may be a good idea to use @code{ls_normalise} on the level-set function
+## before using this method, to prevent almost-zero values from underflowing
+## due to the performed calculations.
+##
+## @seealso{fastmarching, ls_signed_distance, ls_normalise}
+## @end deftypefn
+
+function d = ls_distance_fcn (phi, h = 1)
+ if (nargin () < 1 || nargin () > 2)
+ print_usage ();
+ endif
+
+ d = __levelset_internal_init_narrowband (phi, h);
+ d(ls_inside (phi)) = 0;
+
+ f = ones (size (phi));
+ d = fastmarching (d, h * f);
+
+ assert (all (d(:) >= 0));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_distance_fcn ()
+%!error <Invalid call to>
+%! ls_distance_fcn (1, 2, 3)
+
+% Check 0D case.
+%!test
+%! assert (ls_distance_fcn ([]), []);
+
+% Check 1D case, for which the expected result is trivial to calculate
+% and should be almost exact.
+%!test
+%! n = 10;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! phi = abs (x) - 1;
+%!
+%! d = ls_distance_fcn (phi, h);
+%! assert (d, max (phi, 0), sqrt (eps));
+
+% Test with circular region in 3D.
+%!test
+%! n = 50;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! [XX, YY, ZZ] = ndgrid (x);
+%! RRsq = XX.^2 + YY.^2 + ZZ.^2;
+%! phi = RRsq - 1;
+%!
+%! d = ls_distance_fcn (phi, h);
+%! assert (d, max (sqrt (RRsq) - 1, 0), h);
+
+% Compare timing to ls_signed_distance.
+%!test
+%! n = 500;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%!
+%! [XX, YY] = meshgrid (x, x);
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], 8);
+%!
+%! id = tic ();
+%! d = ls_distance_fcn (phi, h);
+%! time1 = toc (id);
+%!
+%! id = tic ();
+%! sd = ls_signed_distance (phi, h);
+%! time2 = toc (id);
+%!
+%! assert (d, max (sd, 0), sqrt (eps));
+%! printf ("ls_distance_fcn faster than ls_signed_distance by %.1f%%\n", ...
+%! 100 * ((time2 - time1) / time2));
+%! assert (time1 < time2);
+
+% Check that ls_distance_fcn returns the same as ls_signed_distance.
+%!test
+%! phis = ls_get_tests ();
+%! for i = 1 : length (phis)
+%! phi = ls_normalise (phis{i});
+%! d = ls_distance_fcn (phi);
+%! sd = ls_signed_distance (phi);
+%! assert (d, max (sd, 0), sqrt (eps));
+%! endfor
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Calculate distance from an ellipsoid and plot it.
+%!demo
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], [8, 5]);
+%! d = ls_distance_fcn (phi, h);
+%!
+%! figure ();
+%! mesh (XX, YY, d);
+%! view ([45, 45]);
diff --git a/inst/ls_enforce.m b/inst/ls_enforce.m
new file mode 100644
index 0000000..98f71fe
--- /dev/null
+++ b/inst/ls_enforce.m
@@ -0,0 +1,107 @@
+## Copyright (C) 2014-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_enforce (@var{phi}, @var{type}, @var{where})
+##
+## Enforce a geometric constraint for the level-set function @var{phi}. It
+## will be updated to satisfy the constraint.
+##
+## @var{type} specifies what the constraint
+## should be, and @var{where} should be a logical array of the same size
+## as the grid (and thus @var{phi}), specifying which grid points are
+## part of the set that defines the constraint.
+## Possible values for @var{type}:
+##
+## @table @asis
+## @item @qcode{"inside"}
+## The domain should be inside the region marked as @var{where}.
+##
+## @item @qcode{"outside"}
+## The domain should not intersect the region marked as @var{where}.
+##
+## @item @qcode{"contain"}
+## The domain should always contain the region marked in @var{where}.
+## @end table
+##
+## @seealso{ls_check, ls_enforce_speed}
+## @end deftypefn
+
+function phi = ls_enforce (phi, type, where)
+ if (nargin () ~= 3)
+ print_usage ();
+ endif
+
+ if (~all (size (phi) == size (where)))
+ error ("PHI and WHERE must be of the same size");
+ endif
+ if (~strcmp (typeinfo (type), "string"))
+ error ("TYPE must be a string");
+ endif
+
+ switch (type)
+ case "inside"
+ phi(~where) = Inf;
+ case "outside"
+ phi(where) = Inf;
+ case "contain"
+ phi(where) = -Inf;
+ otherwise
+ error ("invalid value '%s' for TYPE argument", type);
+ endswitch
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_enforce (1, 2)
+%!error <Invalid call to>
+%! ls_enforce (1, 2, 3, 4)
+%!error <PHI and WHERE must be of the same size>
+%! ls_enforce (1, "inside", [1, 2]);
+%!error <invalid value 'foo' for TYPE argument>
+%! ls_enforce (1, "foo", true);
+%!error <TYPE must be a string>
+%! ls_enforce (1, NA, false);
+
+% Basic tests for the cases.
+%!test
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! circ2 = (XX.^2 + YY.^2 < 2^2);
+%! circ8 = (XX.^2 + YY.^2 < 8^2);
+%! phi = (XX.^2 + YY.^2 - 5^2);
+%!
+%! phi2 = ls_enforce (phi, "inside", circ8);
+%! assert (ls_equal (phi2, phi));
+%! phi2 = ls_enforce (phi, "inside", circ2);
+%! assert (ls_check (phi2, "inside", circ2));
+%!
+%! phi2 = ls_enforce (phi, "contain", circ2);
+%! assert (ls_equal (phi2, phi));
+%! phi2 = ls_enforce (phi, "contain", circ8);
+%! assert (ls_check (phi2, "contain", circ8));
+%!
+%! phi2 = ls_enforce (phi, "outside", ~circ8);
+%! assert (ls_equal (phi2, phi));
+%! phi2 = ls_enforce (phi, "outside", ~circ2);
+%! ls_check (phi2, "outside", ~circ2);
+%! phi2 = ls_enforce (phi, "outside", circ2);
+%! ls_check (phi2, "outside", circ2);
diff --git a/inst/ls_enforce_speed.m b/inst/ls_enforce_speed.m
new file mode 100644
index 0000000..e73de37
--- /dev/null
+++ b/inst/ls_enforce_speed.m
@@ -0,0 +1,148 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{f} =} ls_enforce_speed (@var{f}, @var{type}, @var{where})
+##
+## Enforce geometric constraints by changing the
+## speed field @var{f} accordingly. This change ensures that
+## the constraint is not violated while evolving a level-set function
+## with the new speed field.
+##
+## @var{type} specifies what the constraint
+## should be, and @var{where} should be a logical array of the same size
+## as the grid (and thus @var{f}), specifying which grid points are
+## part of the set that defines the constraint.
+## Possible values for @var{type}:
+##
+## @table @asis
+## @item @qcode{"inside"}
+## The domain should be inside the region marked as @var{where}.
+##
+## @item @qcode{"outside"}
+## The domain should not intersect the region marked as @var{where}.
+##
+## @item @qcode{"contain"}
+## The domain should always contain the region marked in @var{where}.
+## @end table
+##
+## @seealso{ls_check, ls_enforce, ls_solve_stationary, ls_extract_solution}
+## @end deftypefn
+
+function f = ls_enforce_speed (f, type, where)
+ if (nargin () ~= 3)
+ print_usage ();
+ endif
+
+ if (~all (size (f) == size (where)))
+ error ("F and WHERE must be of the same size");
+ endif
+ if (~strcmp (typeinfo (type), "string"))
+ error ("TYPE must be a string");
+ endif
+
+ switch (type)
+ case "inside"
+ f(~where & (f > 0)) = 0;
+ case "outside"
+ f(where & (f > 0)) = 0;
+ case "contain"
+ f(where & (f < 0)) = 0;
+ otherwise
+ error ("invalid value '%s' for TYPE argument", type);
+ endswitch
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_enforce_speed (1, 2)
+%!error <Invalid call to>
+%! ls_enforce_speed (1, 2, 3, 4)
+%!error <F and WHERE must be of the same size>
+%! ls_enforce_speed (1, "inside", [1, 2]);
+%!error <invalid value 'foo' for TYPE argument>
+%! ls_enforce_speed (1, "foo", true);
+%!error <TYPE must be a string>
+%! ls_enforce_speed (1, NA, false);
+
+% For the following test, define a utility function that
+% checks whether a given domain is (approximately) a circle
+% with given radius.
+%!function checkCircleRadius (XX, YY, h, phi, r)
+%! phiCorrect = XX.^2 + YY.^2 - r^2;
+%! sd1 = ls_signed_distance (phi, h);
+%! sd2 = ls_signed_distance (phiCorrect, h);
+%! assert (sd1, sd2, h);
+%!endfunction
+
+% Shrink and grow a circle, touching constraints. This checks that the
+% constraints are correctly enforced and also that non-applicable constraints
+% do not interfere with the speed.
+%!test
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = XX.^2 + YY.^2 - 3^2;
+%! F = ones (size (XX));
+%! whereContain = (XX.^2 + YY.^2 < 5^2);
+%! F = ls_enforce_speed (F, "contain", whereContain);
+%! whereInside = (XX.^2 + YY.^2 < 8^2);
+%! F = ls_enforce_speed (F, "inside", whereInside);
+%! dists = ls_solve_stationary (phi, F, h);
+%! phi2 = ls_extract_solution (10, dists, phi, F);
+%! assert (ls_check (phi2, "contain", whereContain));
+%! assert (ls_check (phi2, "inside", whereInside));
+%! checkCircleRadius (XX, YY, h, phi2, 8);
+%!
+%! phi = XX.^2 + YY.^2 - 8^2;
+%! F = -ones (size (XX));
+%! whereContain = (XX.^2 + YY.^2 < 5^2);
+%! F = ls_enforce_speed (F, "contain", whereContain);
+%! whereOutside = (XX.^2 + YY.^2 >= 7^2);
+%! F = ls_enforce_speed (F, "outside", whereOutside);
+%! dists = ls_solve_stationary (phi, F, h);
+%! phi2 = ls_extract_solution (10, dists, phi, F);
+%! assert (ls_check (phi2, "contain", whereContain));
+%! assert (ls_check (phi2, "outside", whereOutside));
+%! checkCircleRadius (XX, YY, h, phi2, 5);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Shrink and grow a circle.
+%!demo
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = XX.^2 + YY.^2 - 8^2;
+%! F = -ones (size (XX));
+%! F = ls_enforce_speed (F, "outside", XX.^2 + YY.^2 >= 7^2);
+%! F = ls_enforce_speed (F, "contain", XX.^2 + YY.^2 < 3^2);
+%! times = linspace (1, 6, 40);
+%! ls_animate_evolution (phi, F, h, times, 0.05);
+%!
+%! phi = XX.^2 + YY.^2 - 3^2;
+%! F = ones (size (XX));
+%! F = ls_enforce_speed (F, "inside", XX.^2 + YY.^2 < 8^2);
+%! F = ls_enforce_speed (F, "contain", XX.^2 + YY.^2 < 5^2);
+%! ls_animate_evolution (phi, F, h, times, 0.05);
diff --git a/inst/ls_equal.m b/inst/ls_equal.m
new file mode 100644
index 0000000..d157c73
--- /dev/null
+++ b/inst/ls_equal.m
@@ -0,0 +1,67 @@
+## Copyright (C) 2014-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{res} =} ls_equal (@var{phi1}, @var{phi2})
+##
+## Check if the sets described by @var{phi1} and @var{phi2} are equal.
+## This compares the domains according to @code{ls_inside}, but does not
+## interpret magnitudes of the values and thus it does not take into
+## account where exactly the ``approximate boundary'' intersects
+## @emph{between} grid points.
+##
+## @seealso{ls_inside, ls_issubset, ls_disjoint, ls_hausdorff_dist}
+## @end deftypefn
+
+function res = ls_equal (phi1, phi2)
+ if (nargin () ~= 2)
+ print_usage ();
+ endif
+
+ if (~all (size (phi1) == size (phi2)))
+ error ("PHI1 and PHI2 must be of the same size");
+ endif
+
+ ins1 = ls_inside (phi1);
+ ins2 = ls_inside (phi2);
+ res = all (ins1(:) == ins2(:));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_equal (1)
+%!error <Invalid call to>
+%! ls_equal (1, 2, 3)
+%!error <PHI1 and PHI2 must be of the same size>
+%! ls_equal (1, [1, 2])
+
+% Basic test.
+%!test
+%! assert (ls_equal ([Inf, 0, -2, -Inf, eps], ...
+%! [1, 0, -Inf, -eps, 1]));
+%! assert (ls_equal ([1, 0; -1, 0], [Inf, 0; -5, 0]));
+%! assert (!ls_equal ([1, -1; 0, 0], [1, 0; 0, 0]));
+
+% Test involving signed zero.
+%!test
+%! if (exist ("signbit") == 5)
+%! assert (ls_equal ([0, -0], [1, -1]));
+%! else
+%! warning ("'signbit' function not available, skipping test.");
+%! endif
diff --git a/inst/ls_extract_solution.m b/inst/ls_extract_solution.m
new file mode 100644
index 0000000..946f575
--- /dev/null
+++ b/inst/ls_extract_solution.m
@@ -0,0 +1,174 @@
+## Copyright (C) 2014-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_extract_solution (@var{t}, @var{d}, @var{phi0}, @var{f})
+##
+## Calculate a level-set function of an evolving geometry from the
+## result of @code{ls_solve_stationary}. In particular, it is assumed
+## that @code{@var{d} = ls_solve_stationary (@var{phi0}, @var{f}, @var{h})}.
+## @var{phi} is set to a level-set function for the evolved geometry at
+## time @var{t}. The zero level-set of @var{phi} will describe the same
+## geometry as the solution of the level-set equation
+## @tex
+## \begin{equation*}
+## \phi_t + f \left| \nabla \phi \right| = 0,
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## d/dt phi + f | grad phi | = 0,
+## @end example
+##
+## @end ifnottex
+## although @var{phi} will not be the full solution to this equation.
+##
+## @seealso{ls_solve_stationary, ls_time_step, ls_animate_evolution}
+## @end deftypefn
+
+function phi = ls_extract_solution (t, d, phi0, f)
+ if (nargin () ~= 4)
+ print_usage ();
+ endif
+
+ sz = size (phi0);
+ if (~all (sz == size (f)))
+ error ("PHI0 and F must be of the same size");
+ endif
+ if (~all (sz == size (d)))
+ error ("PHI0 and D must be of the same size");
+ endif
+
+ fp = (f > 0);
+ fz = (f == 0);
+ fn = (f < 0);
+
+ phi = NA (size (d));
+ phi(fp) = d(fp) - t;
+ phi(fn) = d(fn) + t;
+ phi(fz) = phi0(fz);
+
+ assert (all (isfinite (phi(:)) | isinf (phi(:))));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_extract_solution (1, 2, 3)
+%!error <Invalid call to>
+%! ls_extract_solution (1, 2, 3, 4, 5)
+%!error <PHI0 and F must be of the same size>
+%! ls_extract_solution (1, [1, 2], [1, 2], [3; 4]);
+%!error <PHI0 and D must be of the same size>
+%! ls_extract_solution (1, [1; 2], [3, 4], [3, 4]);
+
+% Check 0D case.
+%!test
+%! assert (ls_extract_solution (1, [], [], []), []);
+
+% For the test phi's, just check that solving works
+% without any errors.
+%!test
+%! ts = [0, 1, 2, 3];
+%! phis = ls_get_tests ();
+%! for i = 1 : length (phis)
+%! f = ones (size (phis{i}));
+%! for j = -1 : 1
+%! curF = j * f;
+%! d = ls_solve_stationary (phis{i}, curF);
+%! for t = ts
+%! phit = ls_extract_solution (t, d, phis{i}, curF);
+%! assert (phit(curF == 0), phis{i}(curF == 0));
+%! endfor
+%! endfor
+%! endfor
+
+% Check vanishing of domain.
+%!test
+%! n = 100;
+%! x = linspace (-1.5, 1.5, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], 1);
+%! f = -ones (size (XX));
+%!
+%! d = ls_solve_stationary (phi, f, h);
+%! phiNew = ls_extract_solution (2, d, phi, f);
+%! assert (ls_isempty (phiNew));
+
+% Test that the solution is correct for an expanding sphere.
+%!test
+%! n = 50;
+%! x = linspace (-3, 3, n);
+%! h = x(2) - x(1);
+%! [XX, YY, ZZ] = ndgrid (x);
+%!
+%! phi0 = ls_genbasic (XX, YY, ZZ, "sphere", [0, 0, 0], 1);
+%! phit = ls_genbasic (XX, YY, ZZ, "sphere", [0, 0, 0], 2);
+%! f = ones (size (phi0));
+%!
+%! d = ls_solve_stationary (phi0, f, h);
+%! phi = ls_extract_solution (1, d, phi0, f);
+%!
+%! sd1 = ls_signed_distance (phi, h);
+%! sd2 = ls_signed_distance (phit, h);
+%! diff = norm (sd1(:) - sd2(:), Inf);
+%! assert (diff, 0, h);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Visualise refraction and diffraction of a wave front.
+%
+%!demo
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_genbasic (XX, YY, "half", [5, -10], [1, -1]);
+%! f = sign (XX) + 2;
+%!
+%! times = linspace (1, 9, 80);
+%! ls_animate_evolution (phi, f, h, times, 0.05);
+%
+%!demo
+%! n = 101;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_genbasic (XX, YY, "half", [-9, 0], [-1, 0]);
+%! f = ones (size (phi));
+%! f(XX == 0 & abs (YY) > 2) = 0;
+%!
+%! times = linspace (1, 19, 80);
+%! ls_animate_evolution (phi, f, h, times, 0.05);
+
+% Visualise evolution for more complicated case of positive and negative
+%!demo
+%! n = 1000;
+%! x = linspace (-5, 5, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! F = sin (XX .* YY);
+%! phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
+%! ls_animate_evolution (phi0, F, h, linspace (0, 3, 40), 0.01);
diff --git a/inst/ls_find_geometry.m b/inst/ls_find_geometry.m
new file mode 100644
index 0000000..a66075b
--- /dev/null
+++ b/inst/ls_find_geometry.m
@@ -0,0 +1,572 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{geom} =} ls_find_geometry (@var{phi}, @var{h} = 1, @var{fractol} = 1e-3)
+##
+## Interpret the level-set function @var{phi} and extract geometrical
+## properties. A rectangular grid with spacing @var{h} uniform in each
+## dimension is assumed. This function assumes the 2D situation.
+##
+## The function assumes that @var{phi} does not contain values which are
+## exact zeros. It is a good idea to use @code{ls_normalise} before
+## calling @code{ls_find_geometry}.
+## Intersection points with edges very
+## close to a grid point may be pushed away from it, governed
+## by @var{fractol}.
+##
+## The following terms will be used to denote certain elements
+## of the geometrical description returned by this function:
+##
+## @table @dfn
+## @item node
+## A point on the grid. @var{phi} is the value of the level-set function
+## at each node. They are numbered from 1 to @var{l} x @var{m} in the
+## same way as the entries in the matrix @var{phi} are numbered.
+##
+## @item element
+## One rectangular cell of the grid. Since we work in 2D,
+## each element is made up
+## of four nodes (which are the vertices of the rectangle). If the grid
+## has @var{l} x @var{m} nodes, then it contains (@var{l} - 1) x (@var{m} - 1)
+## elements.
+##
+## @item inner element
+## An element for which each node is inside the described domain.
+##
+## @item outer element
+## An element for which each node is outside of the described domain.
+##
+## @item boundary element
+## An element which has both inside and outside nodes. These elements are
+## intersected by the boundary of the domain and have intersection points
+## on some of their edges.
+##
+## It can be the case that it has two intersection
+## points (when one boundary edge intersects it) or four (if we have
+## a ``narrow pair''). The latter case is that of @emph{two} intersecting
+## (parallel) boundary edges, which happens when diagonally opposing nodes
+## of the element have the same sign of @var{phi}.
+##
+## A narrow pair is ambiguous as to which intersection points should be
+## connected together. This function is implemented such that it considers
+## the ``narrow channel'' between the parallel boundary edges to be
+## @strong{outside} of the domain. In other words, at a narrow pair, the
+## domain is considered to be disconnected instead of connected with a narrow
+## channel.
+##
+## @item intersection point
+## A point on the @emph{edge} of a boundary element where the boundary of the
+## described domain (approximately) intersects the grid (i. e., @var{phi}
+## has a zero). It can be characterised by a pair of inside/outside
+## nodes at each end of the edge it intersects and by the fraction of the
+## side that lies inside the domain.
+##
+## @item gamma
+## The boundary of the described domain. I. e., the curve given by
+## the zero level-set of @var{phi}. It may consist of several components,
+## which are each a closed curve.
+##
+## @item boundary edge
+## A single edge of the domain's boundary, which is a line connecting
+## two intersection points and cutting through a boundary element.
+## It is ordered in such a way that the domain is on the left, if the
+## grid is interpreted as coordinate system. (On the right in matrix
+## ordering.)
+## @end table
+##
+## The returned variable @var{geom} will be a struct with these
+## fields:
+##
+## @table @code
+## @item dim
+## The dimension of the grid, equal to @code{size (@var{phi})}.
+##
+## @item elem
+## Itself a struct describing the elements of the grid. It contains:
+##
+## @table @code
+## @item n
+## Number of elements on the grid. Equal to (@var{l} - 1) x (@var{m} - 1)
+## if @var{phi} is @var{l} x @var{m}.
+##
+## @item nodelist
+## @code{n} x 4 matrix containing in its rows the indices of nodes
+## making up each element. According to the ordering in the matrix @var{phi},
+## the nodes are stored in the order SE, SW, NW, NE. Interpreted in a
+## (x, y) grid constructed via @code{meshgrid}, the order is instead
+## NE, NW, SW, SE matching the usual numbering of quadrants in the plane.
+## (This is the case because the y axis points down when numbering rows
+## of a matrix, but up in the usual coordinate system.)
+##
+## @item type
+## A vector giving the type of each element. It is -1, 0 and 1
+## for inner, boundary and outer elements, respectively. (This convention
+## mimics the sign of @var{phi} somewhat.)
+##
+## @item index
+## Itself a struct with the fields @code{inner}, @code{bdry}
+## and @code{outer}, containing a list of indices for inner, boundary
+## and outer elements.
+##
+## These equal the result of @code{find (@var{geom}.elem.type == @var{x})}
+## with @var{x} being -1, 0 and 1.
+## @end table
+##
+## @item bdryel
+## A struct with information about the boundary elements. They have their
+## own index here, which can be mapped to the global element index
+## via @code{@var{geom}.elem.index.bdry}. Its fields are:
+##
+## @table @code
+## @item n
+## Number of boundary elements.
+##
+## @item edges
+## @code{n} x 4 matrix, whose columns correspond to the four edges of
+## the boundary elements. For each edge where an intersection point lies,
+## the respective matrix entry is set to the intersection point's index
+## into @code{@var{geom}.ispts}.
+## If there is no intersection point, the value is @code{NA}.
+##
+## The edges are ordered similarly to the order in
+## @code{@var{geom}.elem.nodelist}. In matrix interpretation, the
+## edges are in the columns as S, W, N, E. Interpreted as a coordinate
+## grid, the order is N, W, S, E.
+## @end table
+##
+## @item ispts
+## Struct containing details about the intersection points with the fields:
+##
+## @table @code
+## @item n
+## Number of intersection points.
+##
+## @item inout
+## @code{n} x 2 matrix containing the node indices of the inner and
+## outer nodes neighbouring each intersection point (in this order).
+##
+## @item frac
+## Fraction of the edge that is inside the domain (connecting
+## the intersection point to the inner neighbour node). This value
+## is always in the range 0--1, independent of @var{h}.
+##
+## @item incoord
+## Coordinates of the intersection points relative to the neighbouring
+## inner nodes as a @code{n} x 2 matrix. This takes @var{h} into account.
+##
+## @item onedge
+## @code{n} x 2 x 3 array that indicates on which edges of which
+## boundary elements this intersection point occurs. The first dimension
+## is indexed by intersection point number, the second dimension numbers
+## the (two) boundary elements each intersection point is part of, and
+## the third dimension contains the tuple (boundary element index, edge index,
+## boundary edge index) in the ranges 1-- at code{@var{geom}.bdryel.n}, 1--4
+## and 1-- at code{@var{geom}.bedges.n}.
+##
+## The occurances (second dimension) are ordered such that the first
+## one is where the boundary edge ``enters'' the boundary element,
+## while the second one is where it ``leaves'' a boundary element.
+##
+## @item gammachain
+## @code{n} x 2 matrix that contains for each intersection point
+## the next and previous intersection point indices when
+## following gamma. See also @code{@var{geom}.gamma.ispts}.
+## @end table
+##
+## @item gamma
+## Struct with information about the components of the domain's boundary.
+## It contains these fields:
+##
+## @table @code
+## @item n
+## Number of gamma components.
+##
+## @item ispts
+## Cell-array with @code{n} elements. Each element is a list of indices
+## of intersection points (i. e., indices into @code{@var{geom}.ispts}).
+## In the order given, they can be joined together to form a closed curve
+## (when also the last one is connected back to the first).
+## @end table
+##
+## @item bedges
+## Information about the boundary edges:
+##
+## @table @code
+## @item n
+## Number of boundary edges in total.
+##
+## @item comp
+## A vector giving the gamma component this edge is part of. The
+## components are in the range 1-- at code{@var{geom}.gamma.n}.
+##
+## @item ispts
+## @code{n} x 2 matrix containing the intersection point indices that
+## make up each boundary edge.
+## @end table
+##
+## @item internal
+## Data field with additional information for internal use, in particular
+## in @code{ls_build_mesh}. The precise format may be subject to change
+## and should not be used.
+## @end table
+##
+## @seealso{ls_absolute_geom, ls_normalise, ls_build_mesh, ls_inside}
+## @end deftypefn
+
+% Currently contained information in "internal":
+%
+% bdryelSegments:
+% Information about the (one or two) "segments" of each boundary element
+% that belong to the domain. This is the polygon that will be divided
+% into triangles during mesh building, and it is a by-product of
+% locating the gamma components. Thus it makes sense to collect the
+% information already here instead of redoing everything later.
+%
+% It is a cell-array indexed by boundary element number. Each entry
+% is itself a cell-array with one or two struct entries (for each single
+% segment). These structs contain the fields:
+% startEdge: edge index (1--4) where the startPt intersects.
+% endEdge: edge index (1--4) where the endPt lies.
+% startPt: ispt index of the "start" of the intersecting edge.
+% endPt: ispt index of the "end" point.
+% inners: Node indices of inner points "enclosed" (one to three).
+% The corresponding gamma component piece will be [startPt, endPt],
+% and when using [startPt, inners, endPt, startPt], one gets an enumeration
+% of the polygon's vertices *in clockwise order* (with coordinate grid
+% interpretation of the matrices).
+
+% Additional information that could be added as needed:
+% - more about nodes (elements that contain a node, neighbours)
+% - connected components of the domain / exterior
+
+function geom = ls_find_geometry (phi, h = 1, fractol = 1e-3)
+ if (nargin () < 1 || nargin () > 3)
+ print_usage ();
+ endif
+
+ sz = size (phi);
+ if (length (sz) ~= 2 || any (sz < 2))
+ error ("ls_find_geometry is only implemented for 2D");
+ endif
+
+ if (!all (abs (sign (phi(:))) == 1))
+ error ("PHI is not normalised and contains exact zeros or NaN's");
+ endif
+
+ % Use internal routines.
+ elem = __levelset_geomElements (phi);
+ [ispts, edges] = __levelset_geomBoundary (phi, h, fractol, ...
+ elem.index.bdry, elem.nodelist);
+ bdryel = struct ("n", length (elem.index.bdry), "edges", edges);
+ [gammaComp, ispts.onedge, bdryelSeg] ...
+ = __levelset_geomGamma (phi, elem.nodelist, elem.index.bdry, ...
+ edges, ispts.inout);
+ gamma = struct ("n", length (gammaComp), "ispts", {gammaComp});
+ internal = struct ("bdryelSegments", {bdryelSeg});
+
+ % Build the bedges struct and ispts.gammachain from gammaComp.
+ bedges = struct ("n", 0, "comp", [], "ispts", []);
+ ispts.gammachain = NA (ispts.n, 2);
+ for i = 1 : gamma.n
+ comp = gamma.ispts{i};
+ nCurPts = length (gamma.ispts{i});
+ comp(end + 1) = comp(1);
+
+ bedges.n += nCurPts;
+ bedges.comp = [bedges.comp, repmat(i, 1, nCurPts)];
+
+ block = [comp(1 : end - 1), comp(2 : end)];
+ assert (size (block), [nCurPts, 2]);
+ bedges.ispts = [bedges.ispts; block];
+
+ ispts.gammachain(comp(1 : end - 1), 1) = comp(2 : end);
+ ispts.gammachain(comp(2 : end), 2) = comp(1 : end - 1);
+ endfor
+ assert (length (bedges.comp), bedges.n);
+ assert (size (bedges.ispts), [bedges.n, 2]);
+ assert (all (!isna (ispts.gammachain)));
+
+ % Fill in onedge(:, :, 3) from bedges.
+ ispts.onedge(bedges.ispts(:, 1), 1, 3) = 1 : bedges.n;
+ ispts.onedge(bedges.ispts(:, 2), 2, 3) = 1 : bedges.n;
+ assert (all (!isna (ispts.onedge(:))));
+
+ % Construct result.
+ geom = struct ("dim", sz, "elem", elem, ...
+ "bdryel", bdryel, "ispts", ispts, ...
+ "gamma", gamma, "bedges", bedges, ...
+ "internal", internal);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_find_geometry ()
+%!error <Invalid call to>
+%! ls_find_geometry (1, 2, 3, 4)
+%!error <ls_find_geometry is only implemented for 2D>
+%! ls_find_geometry (resize (zeros (8, 1), [2, 2, 2]))
+%!error <ls_find_geometry is only implemented for 2D>
+%! ls_find_geometry ([1, -1, 1])
+%!error <PHI is not normalised>
+%! ls_find_geometry ([0, 1; -1, -1])
+%!error <PHI is not normalised>
+%! ls_find_geometry ([-1, NA; -1, -1])
+
+% A function to verify certain expected properties / postconditions
+% for the resulting geometry structure.
+%!function verifyGeometry (phi, geom)
+%! sz = size (phi);
+%! nNode = prod (sz);
+%! L = sz(1);
+%! M = sz(2);
+%! nElem = (L - 1) * (M - 1);
+%! assert (geom.dim, sz);
+%!
+%! % Basic properties of the elem field.
+%! assert (geom.elem.n, nElem);
+%! assert (size (geom.elem.nodelist), [nElem, 4]);
+%! assert (all (geom.elem.type(geom.elem.index.inner) == -1));
+%! assert (all ((geom.elem.type(geom.elem.index.bdry) == 0)));
+%! assert (all ((geom.elem.type(geom.elem.index.outer) == 1)));
+%! % Order of the nodes in nodelist.
+%! assert (geom.elem.nodelist(1, :), [L+2, 2, 1, L+1]);
+%! % Correct classification of inner/outer/bdry elements.
+%! assert (all (phi(geom.elem.nodelist(geom.elem.index.inner, :))(:) < 0));
+%! assert (all (phi(geom.elem.nodelist(geom.elem.index.outer, :))(:) > 0));
+%! bdryPhis = phi(geom.elem.nodelist(geom.elem.index.bdry, :));
+%! bdryPhis = sort (bdryPhis, 2);
+%! assert (all (bdryPhis(:, 1) < 0) && all (bdryPhis(:, end) > 0));
+%!
+%! % Each boundary element should have two or four intersected edges.
+%! % Also verify that the start/endEdge fields of the internal information
+%! % match up to the points in question.
+%! for i = 1 : geom.bdryel.n
+%! empty = length (find (isna (geom.bdryel.edges(i, :))));
+%! assert (empty == 2 || empty == 0);
+%! for j = 1 : length (geom.internal.bdryelSegments{i})
+%! s = geom.internal.bdryelSegments{i}{j};
+%! assert (geom.bdryel.edges(i, s.startEdge) == s.startPt);
+%! assert (geom.bdryel.edges(i, s.endEdge) == s.endPt);
+%! endfor
+%! endfor
+%!
+%! % Run through gamma components and verify that geom.ispts.onedge matches.
+%! for i = 1 : geom.gamma.n
+%! c = geom.gamma.ispts{i};
+%! c(end + 1) = c(1);
+%! for j = 1 : length (c) - 1
+%! cur = c(j);
+%! next = c(j + 1);
+%! bdryel = geom.ispts.onedge(cur, 1, 1);
+%! assert (geom.ispts.onedge(next, 2, 1), bdryel);
+%! assert (geom.bdryel.edges(bdryel, geom.ispts.onedge(cur, 1, 2)), cur);
+%! assert (geom.bdryel.edges(bdryel, geom.ispts.onedge(next, 2, 2)), next);
+%!
+%! edgeForward = geom.ispts.onedge(cur, 1, 3);
+%! assert (geom.bedges.ispts(edgeForward, 1), cur);
+%! assert (geom.bedges.ispts(edgeForward, 2), next);
+%!
+%! assert (geom.ispts.gammachain(cur, 1), next);
+%! assert (geom.ispts.gammachain(next, 2), cur);
+%! endfor
+%! endfor
+%!
+%! % Verify that the ordering of points in bedges matches the onedge
+%! % information about start / end points in each boundary element.
+%! for i = 1 : geom.bedges.n
+%! assert (geom.ispts.onedge(geom.bedges.ispts(i, 1), 1, 1), ...
+%! geom.ispts.onedge(geom.bedges.ispts(i, 2), 2, 1));
+%! assert (geom.ispts.onedge(geom.bedges.ispts(i, 1), 1, 3), i);
+%! assert (geom.ispts.onedge(geom.bedges.ispts(i, 2), 2, 3), i);
+%! endfor
+%!endfunction
+
+% Test the postconditions against all test phis.
+%!test
+%! phis = ls_get_tests ();
+%! hs = [0.1, 0.5, 1, 1.5];
+%! for i = 1 : length (phis)
+%! for h = hs
+%! for s = [-1, 1]
+%! cur = phis{i} * s;
+%! phi = ls_normalise (cur, h);
+%! geom = ls_find_geometry (phi, h);
+%! verifyGeometry (phi, geom);
+%! endfor
+%! endfor
+%! endfor
+
+% Test pushing away of intersection points from nodes and the
+% fraction calculation in general. The constructed example
+% is a narrow-pair with four intersection points.
+%!test
+%! phi = ones (4, 4);
+%! phi(2 : 3, 2 : 3) = [Inf, -1; -Inf, 2];
+%! g = ls_find_geometry (phi, 0.1, 0.01);
+%!
+%! ind = find (g.elem.index.bdry == 5);
+%! assert (size (ind), [1, 1]);
+%! edges = g.bdryel.edges(ind, :);
+%! assert (all (~isna (edges)));
+%!
+%! assert (g.ispts.inout(edges, :), [7, 11; 7, 6; 10, 6; 10, 11]);
+%! assert (g.ispts.frac(edges), [0.99; 0.5; 0.01; 1/3], sqrt (eps));
+%! assert (g.ispts.incoord(edges, :), ...
+%! [0.099, 0; 0, -0.05; -0.001, 0; 0, 1/30], sqrt (eps));
+
+% Test that cutting through the hold-all domain is correctly identified.
+%!error <boundary cuts through hold-all domain>
+%! phi = [-1, -1; 1, 1];
+%! ls_find_geometry (phi);
+
+% Very simple test for gamma components.
+%!test
+%! phi = ones (3, 3);
+%! phi(2, 2) = -1;
+%! geom = ls_find_geometry (phi);
+%!
+%! % Permute the component array such that it starts with the north point
+%! % and then compare coordinates. Note that the "north" point is "south"
+%! % in matrix interpretation and thus has "outindex" of 6.
+%! assert (geom.gamma.n, 1);
+%! c = geom.gamma.ispts{1}';
+%! while (geom.ispts.inout(c(1), 2) != 6)
+%! c = [c(2 : end), c(1)];
+%! endwhile
+%! assert (geom.ispts.incoord(c, :), ...
+%! [0, 1/2; -1/2, 0; 0, -1/2; 1/2, 0], sqrt (eps));
+
+% Helper function to check that the given coordinates all have the
+% given direction with respect to the origin. +1 means counter-clockwise
+% and -1 is clockwise.
+%!function checkBoundaryDirection (pts, dir)
+%! pts(end + 1, :) = pts(1, :);
+%! for i = 1 : length (pts) - 1
+%! a = [pts(i, :), 0];
+%! b = [pts(i + 1, :), 0];
+%! z = cross (a, b);
+%! assert (z(1 : 2), [0, 0]);
+%! assert (sign (z(3)), dir);
+%! endfor
+%!endfunction
+
+% Check that both "outside" and "inside" boundaries get the correct
+% ordering in gamma components. The test case here is a ring, where we
+% identify the larger (in terms of count of ispts) gamma component with
+% the outer and smaller component with the inner boundary. We expect
+% that the coordiantes of the outer and inner intersection points
+% have (counter-)clockwise ordering with respect to the origin, respectively.
+%!test
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_setdiff (ls_genbasic (XX, YY, "sphere", [0, 0], 8), ...
+%! ls_genbasic (XX, YY, "sphere", [0, 0], 4));
+%! phi = ls_normalise (phi, h);
+%! geom = ls_find_geometry (phi, h);
+%! geom = ls_absolute_geom (geom, XX, YY);
+%!
+%! assert (geom.gamma.n, 2);
+%! len1 = length (geom.gamma.ispts{1});
+%! len2 = length (geom.gamma.ispts{2});
+%! if (len1 < len2)
+%! outer = geom.gamma.ispts{2};
+%! inner = geom.gamma.ispts{1};
+%! else
+%! assert (len1 > len2);
+%! outer = geom.gamma.ispts{1};
+%! inner = geom.gamma.ispts{2};
+%! endif
+%!
+%! checkBoundaryDirection (geom.ispts.coord(outer, :), 1);
+%! checkBoundaryDirection (geom.ispts.coord(inner, :), -1);
+
+% Test that narrow bands are interpreted in the documented way.
+%!test
+%! phi = ones (4, 4);
+%! phi(2, 2) = -1;
+%! phi(3, 3) = -1;
+%!
+%! geom = ls_find_geometry (phi);
+%! assert (geom.gamma.n, 2);
+%! geom = ls_find_geometry (-phi);
+%! assert (geom.gamma.n, 1);
+%!
+%! % Now also try with other "kind" of narrow pair.
+%! phi = phi(:, end : -1 : 1);
+%! geom = ls_find_geometry (phi);
+%! assert (geom.gamma.n, 2);
+%! geom = ls_find_geometry (-phi);
+%! assert (geom.gamma.n, 1);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+% Plot a grid, colour the cells according to the element type and
+% mark the intersection points.
+%!demo
+%! n = 15;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! r1 = sqrt (2) * 2.9;
+%! r2 = 3;
+%! phi = ls_union (ls_genbasic (XX, YY, "sphere", [-3, -3], r1), ...
+%! ls_genbasic (XX, YY, "sphere", [3, 3], r1), ...
+%! ls_genbasic (XX, YY, "sphere", [-5, 5], r2), ...
+%! ls_genbasic (XX, YY, "sphere", [5, -5], r2));
+%!
+%! phi = ls_normalise (phi, h);
+%! g = ls_find_geometry (phi, h, 0.2);
+%! g = ls_absolute_geom (g, XX, YY);
+%!
+%! figure ();
+%! hold ("on");
+%!
+%! for i = 1 : size (g.elem.nodelist, 1)
+%! nodes = g.elem.nodelist(i, :);
+%! switch (g.elem.type(i))
+%! case -1
+%! colour = [0.5, 0.5, 1];
+%! case 0
+%! colour = [1, 0.5, 0.5];
+%! case 1
+%! colour = [0.8, 0.8, 0.8];
+%! endswitch
+%! patch (XX(nodes), YY(nodes), colour);
+%! endfor
+%!
+%! plot (g.ispts.coord(:, 1), g.ispts.coord(:, 2), "k.", "MarkerSize", 8);
+%! for i = 1 : g.gamma.n
+%! pts = g.gamma.ispts{i};
+%! pts(end + 1) = pts(1);
+%! plot (g.ispts.coord(pts, 1), g.ispts.coord(pts, 2), "k-", "LineWidth", 2);
+%! endfor
+%!
+%! contour (XX, YY, phi, [0, 0], "g");
+%!
+%! axis ([min(x), max(x), min(x), max(x)]);
+%! axis ("equal");
+%! grid ("on");
+%! set (gca (), "xtick", x, "ytick", x, "xticklabel", "", "yticklabel", "");
+%! hold ("off");
diff --git a/inst/ls_genbasic.m b/inst/ls_genbasic.m
new file mode 100644
index 0000000..a7fa628
--- /dev/null
+++ b/inst/ls_genbasic.m
@@ -0,0 +1,309 @@
+## Copyright (C) 2014-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_genbasic (@var{XX}, @var{YY}, @var{shape}, ...)
+## @deftypefnx {Function File} {@var{phi} =} ls_genbasic (@var{XX}, ..., @var{shape}, ...)
+## @deftypefnx {Function File} {@var{phi} =} ls_genbasic (..., @qcode{"sphere"}, @var{c}, @var{r})
+## @deftypefnx {Function File} {@var{phi} =} ls_genbasic (..., @qcode{"box"}, @var{a}, @var{b})
+## @deftypefnx {Function File} {@var{phi} =} ls_genbasic (..., @qcode{"half"}, @var{p}, @var{n})
+##
+## Construct the level-set function for a basic geometric shape. The arguments
+## @var{XX} and @var{YY} should be the coordinates where the level-set function
+## should be evaluated. For a case that is not 2D, the respective
+## number of arguments must be given.
+##
+## @var{shape} specifies what shape should be constructed, the arguments
+## following are shape-dependent.
+## Possible shapes:
+##
+## @table @asis
+## @item @qcode{"sphere"}
+## Construct a sphere or ellipsoid with centre @var{c} and radius @var{r}.
+## Optionally, @var{r} can also be a vector giving the radii in each
+## coordinate direction separately.
+##
+## @item @qcode{"box"}
+## Construct a box spanned by the given two vertex coordinates.
+##
+## @item @qcode{"half"}
+## Construct a half-space at one side of a hyperplane. The plane
+## is specified by some point @var{p} on it, and the normal vector
+## @var{n} pointing @emph{into} the domain.
+## @end table
+##
+## @seealso{ls_union, ls_intersect, ls_setdiff, meshgrid, ndgrid}
+## @end deftypefn
+
+function phi = ls_genbasic (varargin)
+
+ % Read in the coordinate arguments until we hit a string (should be the
+ % shape). Check that they have matching dimension.
+ coords = {};
+ argpos = 1;
+ while (true)
+ if (argpos > length (varargin))
+ print_usage ();
+ endif
+
+ cur = varargin{argpos++};
+ if (strcmp (typeinfo (cur), "string"))
+ shape = cur;
+ break;
+ endif
+
+ if (length (coords) == 0)
+ sz = size (cur);
+ else
+ if (~all (size (cur) == sz))
+ error ("coordinate grid size mismatch");
+ endif
+ endif
+ coords{end + 1} = cur;
+ endwhile
+ if (length (coords) == 0)
+ print_usage ();
+ endif
+
+ % For ease-of-use later, extract the shape-dependent arguments now.
+ shargs = cell (1, length (varargin) - argpos + 1);
+ for i = 1 : length (shargs)
+ shargs{i} = varargin{argpos + i - 1};
+ endfor
+
+ % Handle the actual shapes themselves.
+ switch (shape)
+ case "sphere"
+ phi = construct_sphere (coords, shargs);
+ case "box"
+ phi = construct_box (coords, shargs);
+ case "half"
+ phi = construct_half (coords, shargs);
+ otherwise
+ error ("invalid value '%s' for SHAPE argument", shape);
+ endswitch
+endfunction
+
+% Construct a sphere.
+function phi = construct_sphere (coords, shargs)
+ if (length (shargs) ~= 2)
+ print_usage ("ls_genbasic");
+ endif
+ c = shargs{1};
+ r = shargs{2};
+ dims = length (coords);
+ if (length (c) ~= dims)
+ error ("wrong number of dimensions for centre");
+ endif
+ if (length (r) == 1)
+ r = repmat (r, 1, dims);
+ endif
+ if (length (r) ~= dims)
+ error ("wrong number of dimensions for radii");
+ endif
+
+ % The level-set function will simply use the standard equation of
+ % an ellipse:
+ % X^2 / r1^2 + Y^2 / r2^2 = 1^2
+
+ rsq = zeros (size (coords{1}));
+ for i = 1 : dims
+ rsq += ((coords{i} - c(i)) / r(i)).^2;
+ endfor
+
+ phi = rsq - 1;
+endfunction
+
+% Construct a box.
+function phi = construct_box (coords, shargs)
+ if (length (shargs) ~= 2)
+ print_usage ("ls_genbasic");
+ endif
+ a = shargs{1};
+ b = shargs{2};
+ dims = length (coords);
+ if (length (a) ~= dims || length (b) ~= dims)
+ error ("wrong number of dimensions for box vertex");
+ endif
+
+ % Start with the full domain. For each dimension, construct
+ % a "slice" that checks the box constraint for this dimension only,
+ % and intersect all those.
+
+ phi = -ones (size (coords{1}));
+ for i = 1 : dims
+ m = (a(i) + b(i)) / 2;
+ r = abs (a(i) - b(i)) / 2;
+ inner = abs (coords{i} - m) - r;
+
+ phi = ls_intersect (phi, inner);
+ endfor
+endfunction
+
+% Construct a half-space.
+function phi = construct_half (coords, shargs)
+ if (length (shargs) ~= 2)
+ print_usage ("ls_genbasic");
+ endif
+ p = shargs{1};
+ n = shargs{2};
+ dims = length (coords);
+ if (length (p) ~= dims)
+ error ("wrong number of dimensions for plane point");
+ endif
+ if (length (n) ~= dims)
+ error ("wrong number of dimensions for normal vector");
+ endif
+
+ % We use the plane equation in normal form. That is, for a point x to
+ % be in the domain, the inner product between (x - p) and n must be
+ % positive. Use its negative as level-set function.
+
+ phi = zeros (size (coords{1}));
+ for i = 1 : dims
+ phi -= n(i) * (coords{i} - p(i));
+ endfor
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Some basic variables.
+%!shared x, y, XX, YY, XXX, YYY, ZZZ
+%! n = 121;
+%! x = linspace (-5, 7, n);
+%! y = linspace (-7, 5, n);
+%! [XX, YY] = meshgrid (x, y);
+%! [XXX, YYY, ZZZ] = ndgrid (x, y, x);
+
+% Test for basic (common) argument parsing.
+%!error <Invalid call to>
+%! ls_genbasic ();
+%!error <Invalid call to>
+%! ls_genbasic (1, 2);
+%!error <Invalid call to>
+%! ls_genbasic ("sphere");
+%!error <invalid value 'unknown' for SHAPE argument>
+%! ls_genbasic (XX, YY, "unknown");
+%!test
+%! ls_genbasic (x, "sphere", 0, 1);
+%! ls_genbasic (x', "sphere", 0, 1);
+%! ls_genbasic (XX, YY, "sphere", [0, 0], 1);
+%! ls_genbasic (0, 1, "sphere", [0, 0], 1);
+
+% Test sphere construction.
+%!error <Invalid call to>
+%! ls_genbasic (XX, YY, "sphere");
+%!error <Invalid call to>
+%! ls_genbasic (XX, YY, "sphere", 1);
+%!error <Invalid call to>
+%! ls_genbasic (XX, YY, "sphere", 1, 2, 3);
+%!error <wrong number of dimensions for centre>
+%! ls_genbasic (XX, YY, "sphere", [1, 2, 3], 1);
+%!error <wrong number of dimensions for radii>
+%! ls_genbasic (XX, YY, "sphere", [1, 2], [1, 2, 3]);
+%!test
+%! assert (ls_genbasic (XX, YY, "sphere", [1, -1], 1), ...
+%! ls_genbasic (XX, YY, "sphere", [1, -1], [1, 1]));
+%!test
+%! c = [1; -1; 2];
+%! r = [1; 2; 0.6];
+%! tol = 0.4;
+%! phi1 = ls_genbasic (XXX, YYY, ZZZ, "sphere", c, r);
+%! phi2 = ls_genbasic (XXX, YYY, ZZZ, "sphere", c, min (r) - tol);
+%! assert (ls_issubset (phi2, phi1));
+%! coords = {XXX, YYY, ZZZ};
+%! for i = 1 : length (c)
+%! assert (ls_disjoint (phi1, ...
+%! coords{i} - (c(i) - r(i)), ...
+%! c(i) + r(i) - coords{i}));
+%! assert (~ls_disjoint (phi1 - tol, coords{i} - (c(i) - r(i))));
+%! assert (~ls_disjoint (phi1 - tol, c(i) + r(i) - coords{i}));
+%! endfor
+
+% Test box construction.
+%!error <Invalid call to>
+%! ls_genbasic (XX, YY, "box", 1);
+%!error <Invalid call to>
+%! ls_genbasic (XX, YY, "box", 1, 2, 3);
+%!error <wrong number of dimensions for box vertex>
+%! ls_genbasic (XX, YY, "box", [1, 2, 3], [1, 2]);
+%!error <wrong number of dimensions for box vertex>
+%! ls_genbasic (XX, YY, "box", [1; 2], [1, 2, 3]);
+%!test
+%! assert (ls_equal (ls_genbasic (x, "box", -1, 2), abs (x - 0.5) - 1.5));
+%! assert (ls_equal (ls_genbasic (x, "box", 2, -1), abs (x - 0.5) - 1.5));
+%!test
+%! a = [2; -3; 1];
+%! b = [0; 2; -1];
+%! tol = 0.2;
+%! phi1 = ls_genbasic (XXX, YYY, ZZZ, "box", a, b);
+%! phi2 = ls_genbasic (XXX, YYY, ZZZ, "box", b, a);
+%! assert (phi1, phi2);
+%! coords = {XXX, YYY, ZZZ};
+%! for i = 1 : length (coords)
+%! assert (ls_disjoint (phi1, ...
+%! coords{i} - min (a(i), b(i)), ...
+%! max (a(i), b(i)) - coords{i}));
+%! assert (~ls_disjoint (phi1 - tol, coords{i} - min (a(i), b(i))));
+%! assert (~ls_disjoint (phi1 - tol, max (a(i), b(i)) - coords{i}));
+%! endfor
+
+% Test half-space construction.
+%!error <Invalid call to>
+%! ls_genbasic (XX, YY, "half", 1);
+%!error <Invalid call to>
+%! ls_genbasic (XX, YY, "half", 1, 2, 3);
+%!error <wrong number of dimensions for plane point>
+%! ls_genbasic (XX, YY, "half", [1, 2, 3], [1, 2]);
+%!error <wrong number of dimensions for normal vector>
+%! ls_genbasic (XX, YY, "half", [1; 2], [1, 2, 3]);
+%!test
+%! p = [0, 2, 1];
+%!
+%! phi = ls_genbasic (XXX, YYY, ZZZ, "half", p, [0; 0; 1]);
+%! assert (ls_equal (phi, 1 - ZZZ));
+%!
+%! normal = [1; 2; -0.5];
+%! phi1 = ls_genbasic (XXX, YYY, ZZZ, "half", p, normal);
+%! phi2 = ls_genbasic (XXX, YYY, ZZZ, "half", p, 2 * normal);
+%! assert (ls_equal (phi1, phi2));
+%!
+%! phi = ls_genbasic (XX, YY, "half", [0, 0], [1, -1]);
+%! assert (ls_equal (phi, YY - XX));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+%!demo
+%! n = 200;
+%! x = linspace (-7, 7, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = ls_genbasic (XX, YY, "box", [-5, -3], [3, 6]);
+%! phi2 = ls_genbasic (XX, YY, "sphere", [-1; 1], 2);
+%! phi3 = ls_genbasic (XX, YY, "sphere", [0; 0], [6, 1]);
+%! phi4 = ls_genbasic (XX, YY, "half", [3, -3], [1, -2]) / 8;
+%!
+%! phi = ls_union (ls_setdiff (phi1, phi2), phi3, phi4);
+%! figure ();
+%! hold ("on");
+%! imagesc (x, x, phi);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ();
+%! contour (XX, YY, phi, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
diff --git a/inst/ls_get_tests.m b/inst/ls_get_tests.m
new file mode 100644
index 0000000..d02599a
--- /dev/null
+++ b/inst/ls_get_tests.m
@@ -0,0 +1,80 @@
+## Copyright (C) 2013-2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phis} =} ls_get_tests ()
+##
+## Return a couple of level-set functions for certain 2D cases in the
+## cell-array @var{phis}. They can be used in tests.
+##
+## Use @code{demo ls_get_tests} to get an overview of the situations included.
+##
+## @end deftypefn
+
+function phis = ls_get_tests ()
+ phis = {};
+
+ % The most basic situation.
+ phi = ones (3, 3);
+ phi(2, 2) = -1;
+ phis{end + 1} = phi;
+
+ % The same but now with infinite values.
+ phi = Inf (3, 3);
+ phi(2, 2) = -Inf;
+ phis{end + 1} = phi;
+
+ % Include a fat zero level-set.
+ phi = ones (6, 6);
+ phi(2 : 5, 2 : 5) = 0;
+ phi(3, 4) = -1;
+ phis{end + 1} = phi;
+
+ % Two connected components.
+ x = linspace (-10, 10, 100);
+ y = linspace (-10, 10, 80);
+ [XX, YY] = meshgrid (x, y);
+ phi = ls_union ((XX - 2).^2 + (YY - 5).^2 - 3^2, ...
+ (XX + 2).^2 + (YY - 5).^2 - 3^2, ...
+ XX.^2 + (YY + 3).^2 - 1.5^2);
+ phis{end + 1} = phi;
+
+ % Include a "narrow pair".
+ phi = ones (6, 6);
+ phi(2 : 3, 4 : 5) = -1;
+ phi(4 : 5, 2 : 3) = -1;
+ phis{end + 1} = phi;
+
+ % Very small value which may be rounded to zero during
+ % operations like ls_signed_distance.
+ phi = ones (4, 4);
+ phi(2 : 3, 2 : 3) = -1;
+ phi(2, 2) = -1e-18;
+ phis{end + 1} = phi;
+endfunction
+
+% Demo function that plots all the tests.
+%!demo
+%! phis = ls_get_tests ();
+%! for i = 1 : length (phis)
+%! figure ();
+%! hold ("on");
+%! imagesc (sign (phis{i}));
+%! caxis ([-1, 1]);
+%! hold ("off");
+%! titleFmt = "Situation #%d. Blue: Interior, Green: Zero, Red: Outside.";
+%! title (sprintf (titleFmt, i));
+%! endfor
diff --git a/inst/ls_hausdorff_dist.m b/inst/ls_hausdorff_dist.m
new file mode 100644
index 0000000..9c922c9
--- /dev/null
+++ b/inst/ls_hausdorff_dist.m
@@ -0,0 +1,132 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{dh} =} ls_hausdorff_dist (@var{phi1}, @var{phi2}, @var{h} = 1)
+## @deftypefnx {Function File} {@var{dh} =} ls_hausdorff_dist (@var{sd1}, @var{sd2}, @qcode{"sd"})
+##
+## Approximate the Hausdorff distance between two sets. The sets are given
+## by their level-set functions @var{phi1} and @var{phi2}. The Hausdorff
+## distance is calculated as the maximum difference between their distance
+## functions. (Note that it is the ordinary distance function here, not
+## the @emph{signed} distance function!)
+##
+## If we neglect possible approximation errors in the distance function,
+## the result @var{dh} is guaranteed to be a lower bound of the exact
+## Hausdorff distance. It is within the real distance by, roughly, @var{h}.
+##
+## The second call form assumes that the level-set functions of the
+## domains are actually already signed distance functions @var{sd1} and
+## @var{sd2}. In this case, the grid spacing @var{h} is not necessary.
+## Since there is no need to call @code{ls_distance_fcn}, the calculation
+## can be performed faster in this case.
+##
+## @seealso{ls_equal, ls_distance_fcn, ls_signed_distance}
+## @end deftypefn
+
+function dh = ls_hausdorff_dist (phi1, phi2, h = 1)
+ if (nargin () < 2 || nargin () > 3)
+ print_usage ();
+ endif
+
+ sdMode = false;
+ if (isnumeric (h))
+ if (!isscalar (h))
+ error ("H must be scalar");
+ endif
+ else
+ if (!strcmp (h, "sd"))
+ error ("invalid option, expected H or \"sd\"");
+ endif
+ sdMode = true;
+ endif
+
+ if (!all (size (phi1) == size (phi2)))
+ error ("PHI1 and PHI2 must be of the same size");
+ endif
+
+ if (sdMode)
+ d1 = max (phi1, 0);
+ d2 = max (phi2, 0);
+ else
+ d1 = ls_distance_fcn (phi1, h);
+ d2 = ls_distance_fcn (phi2, h);
+ endif
+ assert (all (d1(:)) >= 0);
+ assert (all (d2(:)) >= 0);
+
+ dh = norm (d1(:) - d2(:), inf);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_hausdorff_dist (1)
+%!error <Invalid call to>
+%! ls_hausdorff_dist (1, 2, 3, 4)
+%!error <H must be scalar>
+%! ls_hausdorff_dist (1, 2, [3, 3])
+%!error <invalid option, expected H or "sd">
+%! ls_hausdorff_dist (1, 2, "foo")
+
+% Basic test with various circle combinations.
+% Also exercise the "sd" variant.
+%!test
+%! n = 50;
+%! x = linspace (-5, 5, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = ls_genbasic (XX, YY, "sphere", [3, 0], 1);
+%! phi2 = ls_genbasic (XX, YY, "sphere", [-2, 0], 2);
+%! assert (ls_hausdorff_dist (phi1, phi2, h), 6, h);
+%!
+%! % Make sure that phi isn't a signed-distance fcn already.
+%! % ls_genbasic should return the same, but make it explicit.
+%! rSq = XX.^2 + YY.^2;
+%! d = ls_hausdorff_dist (rSq - 4^2, rSq - 2^2, h);
+%! assert (d, 2, h);
+%! dWrong = ls_hausdorff_dist (rSq - 4^2, rSq - 2^2, "sd");
+%! assert (abs (d - dWrong) > 10 * h);
+%! dRight = ls_hausdorff_dist (sqrt (rSq) - 4, sqrt (rSq) - 2, "sd");
+%! assert (dRight, d, h);
+
+% Test the case where the difference of the *signed* distance
+% functions gives the wrong answer.
+%!test
+%! n = 200;
+%! x = linspace (-5, 5, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phiA = ls_genbasic (XX, YY, "sphere", [0, 0], 4);
+%! phiInner = ls_genbasic (XX, YY, "sphere", [0, 0], 2);
+%! phiB = ls_setdiff (phiA, phiInner);
+%!
+%! d1 = ls_hausdorff_dist (phiA, phiB, h);
+%! d2 = ls_hausdorff_dist (phiB, phiA, h);
+%! assert (d1, d2);
+%! assert (d1, 2, 2 * h);
+%! dc = ls_hausdorff_dist (ls_complement (phiA), ...
+%! ls_complement (phiB), h);
+%! assert (dc, 4, 2 * h);
+%!
+%! sdA = ls_signed_distance (phiA, h);
+%! sdB = ls_signed_distance (phiB, h);
+%! sdDiff = norm (sdA(:) - sdB(:), inf);
+%! assert (abs (sdDiff - d1) > 10 * h);
diff --git a/inst/ls_init_narrowband.m b/inst/ls_init_narrowband.m
new file mode 100644
index 0000000..9ab5ea7
--- /dev/null
+++ b/inst/ls_init_narrowband.m
@@ -0,0 +1,154 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{d} =} ls_init_narrowband (@var{phi}, @var{h} = 1)
+##
+## Calculate the estimated distances of grid points in the ``narrow band''
+## to the zero level-set of @var{phi}. The points considered are those
+## which have neighbours with different sign of @var{phi}. The calculated
+## distances are returned in @var{d}, where values for points not in the
+## narrow band are set to @code{NA}.
+## If the optional argument @var{h} is given, it is used as the grid spacing
+## instead of the default value of 1.
+##
+## The distances will be positive or negative depending on the sign
+## of @var{phi}. This means that @var{d} gives the signed
+## distance function of the level-set domain for narrow-band points.
+##
+## We assume a linear model for @var{phi}, meaning that the approximate
+## intersection points on grid edges are calculated using linear interpolation.
+## The distances at narrow-band points are calculated using the quadratic
+## update equation of the Fast-Marching Method using these approximated
+## intersection points.
+##
+## Note that this method does not take an arbitrary speed field into account.
+## It assumes a uniform speed of 1 everywhere. For different speeds, the
+## resulting distances must be scaled as required.
+##
+## It may be a good idea to use @code{ls_normalise} on the level-set function
+## before using this method, to prevent almost-zero values from underflowing
+## due to the performed calculations.
+##
+## @seealso{ls_signed_distance, ls_nb_from_geom, ls_normalise}
+## @end deftypefn
+
+function d = ls_init_narrowband (phi, h = 1)
+ if (nargin () < 1 || nargin () > 2)
+ print_usage ();
+ endif
+
+ d = __levelset_internal_init_narrowband (phi, h);
+ d = ls_copy_sign (d, phi);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_init_narrowband ()
+%!error <Invalid call to>
+%! ls_init_narrowband (1, 2, 3)
+
+% Check 0D case.
+%!test
+%! assert (ls_init_narrowband ([]), []);
+
+% Check 1D case, for which the expected result is trivial to calculate
+% and should be almost exact.
+%!test
+%! n = 10;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! phi = abs (x) - 1;
+%!
+%! d = ls_init_narrowband (phi, h);
+%! where = ~isna (d);
+%! assert (d(where), phi(where), sqrt (eps));
+
+% Test with circular region in 3D.
+%!test
+%! n = 50;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! [XX, YY, ZZ] = ndgrid (x);
+%! RRsq = XX.^2 + YY.^2 + ZZ.^2;
+%! phi = RRsq - 1;
+%!
+%! d = ls_init_narrowband (phi, h);
+%! where = ~isna (d);
+%! assert (d(where), sqrt (RRsq(where)) - 1, h / 3);
+
+% Test cases of infinities in level-set function.
+%!test
+%! tol = sqrt (eps);
+%! assert (ls_init_narrowband ([Inf, -Inf, Inf]), [0.5, -0.5, 0.5], tol);
+%! assert (ls_init_narrowband ([Inf, -1, Inf]), [1, 0, 1], tol);
+%! %assert (ls_init_narrowband ([1, -Inf, 1]), [0, 1, 0], tol);
+
+% Compare two contour lines as per contourc for their Hausdorff distance.
+% This is used to check that the contour lines found for d and phi itself
+% are (almost) the same.
+%
+%!function dh = distToSegment (x, a, b)
+%! dir = b - a;
+%! dir /= norm (dir);
+%! proj = dot (x - a, dir);
+%! if (proj < 0)
+%! dh = norm (x - a);
+%! elseif (proj > 1)
+%! dh = norm (x - b);
+%! else
+%! dh = norm (x - (a + proj * dir));
+%! endif
+%!endfunction
+%
+%!function dh = distToLine (x, c)
+%! assert (c(1, 1), 0);
+%! dh = Inf;
+%! for i = 2 : size (c, 2) - 1
+%! dh = min (dh, distToSegment (x, c(:, i), c(:, i + 1)));
+%! endfor
+%!endfunction
+%
+%!function dh = distOfContoursAsym (c1, c2)
+%! dh = 0;
+%! for i = 2 : size (c1, 2)
+%! dh = max (dh, distToLine (c1(:, i), c2));
+%! endfor
+%!endfunction
+%
+%!function dh = distOfContours (c1, c2)
+%! dh = max (distOfContoursAsym (c1, c2), distOfContoursAsym (c2, c1));
+%!endfunction
+
+% Compare distance of contour lines for the test phis.
+%!test
+%! phis = ls_get_tests ();
+%! printf ("Comparing contours for %d cases...\n", length (phis));
+%! for i = 1 : length (phis)
+%! d = ls_init_narrowband (phis{i});
+%! c1 = contourc (d, [0, 0]);
+%! if (size (c1, 2) > 0)
+%! c2 = contourc (phis{i}, [0, 0]);
+%! dh = distOfContours (c1, c2);
+%! printf ("i = %d: dist = %f\n", i, dh);
+%! if (isfinite (dh))
+%! assert (dh, 0, 1e-1);
+%! endif
+%! endif
+%! endfor
diff --git a/inst/ls_inside.m b/inst/ls_inside.m
new file mode 100644
index 0000000..bae53f5
--- /dev/null
+++ b/inst/ls_inside.m
@@ -0,0 +1,60 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{where} =} ls_inside (@var{phi})
+##
+## Return in a logical array which points are inside the domain
+## described by the level-set function @var{phi}. These are the points
+## where @var{phi} is negative. If the @code{signbit} function is available
+## (on newer versions of GNU Octave), also negative zeros are considered
+## to be part of the domain.
+##
+## @seealso{ls_check, ls_issubset, ls_normalise}
+## @end deftypefn
+
+function res = ls_inside (phi)
+ if (nargin () ~= 1)
+ print_usage ();
+ endif
+
+ if (exist ("signbit") == 5)
+ res = signbit (phi);
+ else
+ res = (phi < 0);
+ endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_inside ()
+%!error <Invalid call to>
+%! ls_inside (1, 2)
+
+% Basic test.
+%!test
+%! assert (ls_inside ([1, 0, -1, 0, 1]), [false, false, true, false, false]);
+
+% Test negative zeros.
+%!test
+%! if (exist ("signbit") == 5)
+%! assert (ls_inside ([-0, 0]), [true, false]);
+%! else
+%! warning ("'signbit' function not available, skipping test.");
+%! endif
diff --git a/inst/ls_intersect.m b/inst/ls_intersect.m
new file mode 100644
index 0000000..d594582
--- /dev/null
+++ b/inst/ls_intersect.m
@@ -0,0 +1,90 @@
+## Copyright (C) 2014-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_intersect (@var{phi1}, @var{phi2})
+## @deftypefnx {Function File} {@var{phi} =} ls_intersect (@var{phi}, ...)
+##
+## Calculate a level-set function for the intersection of the sets described
+## by the argument level-set functions.
+##
+## @seealso{ls_complement, ls_union, ls_setdiff, ls_setxor, intersect}
+## @end deftypefn
+
+function res = ls_intersect (varargin)
+ res = ls_union_intersect (@max, varargin, "ls_intersect");
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_intersect ()
+%!error <size mismatch in the arguments>
+%! ls_intersect (1, -2, [1, 2])
+
+% Test that the intersection is part of all pieces.
+%!test
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 2).^2 + (YY - 2).^2 - 2^2;
+%! phi2 = (XX + 2).^2 + (YY + 2).^2 - 2^2;
+%! phi3 = XX.^2 + YY.^2 - 2^2;
+%!
+%! assert (ls_isempty (ls_intersect (phi1, phi2)));
+%! assert (ls_intersect (phi3), phi3);
+%!
+%! phi = ls_intersect (phi1, phi3);
+%! assert (~ls_isempty (phi));
+%! assert (ls_issubset (phi, phi1) && ls_issubset (phi, phi3));
+%!
+%! phi = ls_intersect (phi2, phi3);
+%! assert (~ls_isempty (phi));
+%! assert (ls_issubset (phi, phi2) && ls_issubset (phi, phi3));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%! n = 100;
+%! x = linspace (-7, 7, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 2 * cos (7/6 * pi)).^2 + (YY - 2 * sin (7/6 * pi)).^2 - 3^2;
+%! phi2 = (XX - 2 * cos (11/6 * pi)).^2 + (YY - 2 * sin (11/6 * pi)).^2 - 3^2;
+%! phi3 = XX.^2 + (YY - 2).^2 - 3^2;
+%! phi = ls_intersect (phi1, phi2, phi3);
+%!
+%! figure ();
+%! subplot (1, 2, 1);
+%! hold ("on");
+%! contour (XX, YY, phi1, [0, 0], "k");
+%! contour (XX, YY, phi2, [0, 0], "k");
+%! contour (XX, YY, phi3, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
+%!
+%! subplot (1, 2, 2);
+%! hold ("on");
+%! imagesc (x, x, phi);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ();
+%! contour (XX, YY, phi, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
diff --git a/inst/ls_isempty.m b/inst/ls_isempty.m
new file mode 100644
index 0000000..d809f6e
--- /dev/null
+++ b/inst/ls_isempty.m
@@ -0,0 +1,54 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{res} =} ls_isempty (@var{phi})
+##
+## Check if the set described by @var{phi} is the empty set.
+##
+## @seealso{ls_inside}
+## @end deftypefn
+
+function res = ls_isempty (phi)
+ if (nargin () ~= 1)
+ print_usage ();
+ endif
+
+ inside = ls_inside (phi);
+ res = all (~inside(:));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_isempty ()
+%!error <Invalid call to>
+%! ls_isempty (1, 2)
+
+% Basic tests for the function.
+%!test
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 2).^2 + (YY - 2).^2 - 2^2;
+%! phi2 = (XX + 2).^2 + (YY + 2).^2 - 2^2;
+%!
+%! assert (~ls_isempty (phi1) && ~ls_isempty (phi2));
+%! assert (ls_isempty (ls_intersect (phi1, phi2)));
+%! assert (~ls_isempty (ls_union (phi1, phi2)));
diff --git a/inst/ls_issubset.m b/inst/ls_issubset.m
new file mode 100644
index 0000000..fefa898
--- /dev/null
+++ b/inst/ls_issubset.m
@@ -0,0 +1,61 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{res} =} ls_issubset (@var{phi1}, @var{phi2})
+##
+## Check if the set described by @var{phi1} is a subset of @var{phi2}.
+##
+## @seealso{ls_inside, ls_equal, ls_disjoint, ls_check}
+## @end deftypefn
+
+function res = ls_issubset (phi1, phi2)
+ if (nargin () ~= 2)
+ print_usage ();
+ endif
+
+ if (~all (size (phi1) == size (phi2)))
+ error ("PHI1 and PHI2 must be of the same size");
+ endif
+
+ res = ls_check (phi2, "contain", ls_inside (phi1));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_issubset (1)
+%!error <Invalid call to>
+%! ls_issubset (1, 2, 3)
+%!error <PHI1 and PHI2 must be of the same size>
+%! ls_issubset (1, [1, 2])
+
+% Basic tests for the cases.
+%!test
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = XX.^2 + YY.^2 - 1^2;
+%! phi2 = XX.^2 + YY.^2 - 5^2;
+%! phi3 = XX.^2 + YY.^2 - 8^2;
+%! phi4 = (XX - 3).^2 + YY.^2 - 3^2;
+%!
+%! assert (ls_issubset (phi1, phi2));
+%! assert (ls_issubset (phi4, phi3));
+%! assert (~ls_issubset (phi4, phi2));
diff --git a/inst/ls_nb_from_geom.m b/inst/ls_nb_from_geom.m
new file mode 100644
index 0000000..69c7485
--- /dev/null
+++ b/inst/ls_nb_from_geom.m
@@ -0,0 +1,174 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{d} =} ls_nb_from_geom (@var{geom}, @var{phi})
+## @deftypefnx {Function File} {[@var{d}, @var{g}] =} ls_nb_from_geom (@var{geom}, @var{phi}, @var{g0})
+##
+## Initialise the narrow-band values utilising information about the
+## geometry. @var{geom} should be the geometry according to
+## @code{ls_find_geometry}, and it must contain absolute coordinates
+## as per @code{ls_absolute_geom} in addition. Thus it is unnecessary
+## to pass the grid spacing, as this information is already
+## contained in the absolute coordinates.
+## This is an alternative routine to the standard
+## @code{ls_init_narrowband} for 2D situations.
+##
+## This function sets the distance at @emph{each} node of a boundary
+## element to the shortest distance to any boundary edge
+## according to the geometry information in @var{geom}. In contrast
+## to @code{ls_init_narrowband}, this also initialises values on the
+## ``far diagonally away'' point. This makes the result more accurate.
+##
+## In the second form, @var{g0} is expected to contain the values of some
+## function defined on each boundary edge (ordered in the same way as
+## @code{@var{geom}.bedges}). These function values will be
+## extended onto narrow-band points as well, and returned in @var{g}.
+##
+## @seealso{ls_init_narrowband, ls_find_geometry, ls_absolute_geom,
+## ls_solve_stationary, fastmarching}
+## @end deftypefn
+
+function [d, g] = ls_nb_from_geom (geom, phi, g0 = NA)
+ if (nargin () < 2 || nargin () > 3)
+ print_usage ();
+ endif
+
+ hasG = (nargin () == 3);
+ if (hasG)
+
+ % Turn row vector into column.
+ if (size (g0, 1) == 1)
+ g0 = g0';
+ endif
+
+ if (size (g0, 2) != 1)
+ error ("G0 should be a vector");
+ endif
+ if (length (g0) != geom.bedges.n)
+ error ("G0 has wrong size");
+ endif
+ else
+ if (nargout () > 1)
+ print_usage ();
+ endif
+ endif
+
+ if (!isfield (geom.ispts, "coord") || !isfield (geom, "nodes"))
+ error ("GEOM misses absolute coordinates, use ls_absolute_geom first");
+ endif
+
+ [d, gSum, gCnt] = __levelset_nbFromGeom (geom.bedges.ispts, phi, hasG, g0, ...
+ geom.elem.nodelist, ...
+ geom.elem.index.bdry, ...
+ geom.ispts.onedge(:, :, 1), ...
+ geom.nodes.coord, geom.ispts.coord);
+
+ d = ls_copy_sign (d, phi);
+ if (hasG)
+ g = gSum ./ gCnt;
+ endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_nb_from_geom (1)
+%!error <Invalid call to>
+%! ls_nb_from_geom (1, 2, 3, 4)
+%!error <Invalid call to>
+%! [a, b] = ls_nb_from_geom (1, 2);
+%!error <G0 should be a vector>
+%! ls_nb_from_geom (1, 2, rand (2, 2));
+
+% Test for missing absolute coordinates.
+%!error <GEOM misses absolute coordinates, use ls_absolute_geom first>
+%! x = linspace (-10, 10, 10);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], 5);
+%! phi = ls_normalise (phi, h);
+%! geom = ls_find_geometry (phi, h);
+%! ls_nb_from_geom (geom, phi);
+
+% Test for size check on g0.
+%!error <G0 has wrong size>
+%! x = linspace (-10, 10, 10);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], 5);
+%! phi = ls_normalise (phi, h);
+%! geom = ls_find_geometry (phi, h);
+%! ls_nb_from_geom (geom, phi, [1, 2, 3]);
+
+% Compare (with some tolerance) to ls_init_narrowband.
+%!test
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_setdiff (ls_genbasic (XX, YY, "sphere", [0, 0], 8), ...
+%! ls_genbasic (XX, YY, "sphere", [1, 1], 5));
+%! phi = ls_normalise (phi, h);
+%! geom = ls_find_geometry (phi, h);
+%! geom = ls_absolute_geom (geom, XX, YY);
+%!
+%! dOld = ls_init_narrowband (phi, h);
+%! dNew = ls_nb_from_geom (geom, phi);
+%!
+%! where = (!isna (dOld));
+%! assert (all (!isna (dNew(where))));
+%! assert (dNew(where), dOld(where), h / 4);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% TODO: Demo comparing different initialisations.
+
+% Try a basic extension of function values.
+%!demo
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
+%! phi = ls_normalise (phi, h);
+%! geom = ls_find_geometry (phi, h);
+%! geom = ls_absolute_geom (geom, XX, YY);
+%!
+%! g0 = NA (1, geom.bedges.n);
+%! for i = 1 : geom.bedges.n
+%! a = geom.ispts.coord(geom.bedges.ispts(i, 1), :);
+%! b = geom.ispts.coord(geom.bedges.ispts(i, 2), :);
+%! m = (a + b) / 2;
+%! angle = abs (atan2 (m(2), m(1)));
+%! g0(i) = min (angle, 2 * pi - angle);
+%! endfor
+%!
+%! [d, g] = ls_nb_from_geom (geom, phi, g0);
+%! [d, g] = fastmarching (d, g, h * ones (size (phi)));
+%!
+%! figure ();
+%! hold ("on");
+%! imagesc (x, x, g);
+%! set (gca (), "ydir", "normal");
+%! contour (XX, YY, phi, [0, 0], "k");
+%! hold ("off");
+%! colorbar ();
diff --git a/inst/ls_normalise.m b/inst/ls_normalise.m
new file mode 100644
index 0000000..a71a7e7
--- /dev/null
+++ b/inst/ls_normalise.m
@@ -0,0 +1,74 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_normalise (@var{phi}, @var{h} = 1, @var{zerotol} = 1e-3)
+##
+## Normalise the level-set function @var{phi}. This gets rid of values that
+## are exactly zero, by ensuring that each entry of the changed level-set
+## function has a magnitude of at least @var{h} * @var{zerotol}. The
+## actual level-set geometry is not changed, except possibly the approximated
+## intersections between the zero level-set and grid edges due to slight
+## variations in the actual values of @var{phi}.
+##
+## Exactly zero values are interpreted according to their sign bit if the
+## @code{signbit} function is available (on newer versions of GNU Octave).
+## If the function is not available, then zeros are assumed to be
+## @strong{not} part of the level-set domain.
+##
+## @seealso{ls_inside}
+## @end deftypefn
+
+function phi = ls_normalise (phi, h = 1, zerotol = 1e-3)
+ if (nargin () < 1 || nargin () > 3)
+ print_usage ();
+ endif
+
+ zerotol *= h;
+ exz = (phi == 0);
+ if (exist ("signbit") == 5)
+ phi(exz) = zerotol * (1 - 2 * double (signbit (phi(exz))));
+ else
+ phi(exz) = zerotol;
+ endif
+ zeroPts = (abs (phi) < zerotol);
+ phi(zeroPts) = zerotol * sign (phi(zeroPts));
+
+ assert (all (abs (phi(:)) >= zerotol));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_normalise ()
+%!error <Invalid call to>
+%! ls_normalise (1, 2, 3, 4)
+
+%!test
+%! h = 0.1;
+%! zeroTol = 0.2;
+%! phi = ones (4, 4);
+%! phiEx = phi;
+%! phi(2 : 3, 2 : 3) = [-eps, -0; 0, eps];
+%! phiEx(2 : 3, 2 : 3) = [-zeroTol*h, -zeroTol*h; zeroTol*h, zeroTol*h];
+%! phin = ls_normalise (phi, h, zeroTol);
+%! if (exist ("signbit") == 5)
+%! assert (phin, phiEx);
+%! else
+%! warning ("'signbit' function not available, skipping test.");
+%! endif
diff --git a/inst/ls_setdiff.m b/inst/ls_setdiff.m
new file mode 100644
index 0000000..2c0f5de
--- /dev/null
+++ b/inst/ls_setdiff.m
@@ -0,0 +1,122 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_setdiff (@var{phi1}, @var{phi2})
+##
+## Calculate a level-set function for the set difference @var{phi1}
+## minus @var{phi2}.
+##
+## @seealso{ls_complement, ls_union, ls_intersect, ls_setxor, setdiff}
+## @end deftypefn
+
+function phi = ls_setdiff (phi1, phi2)
+ if (nargin () ~= 2)
+ print_usage ();
+ endif
+
+ if (~all (size (phi1) == size (phi2)))
+ error ("PHI1 and PHI2 must be of the same size");
+ endif
+
+ phi = ls_intersect (phi1, ls_complement (phi2));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_setdiff (1)
+%!error <Invalid call to>
+%! ls_setdiff (1, 2, 3)
+%!error <PHI1 and PHI2 must be of the same size>
+%! ls_setdiff (1, [1, 2])
+
+% Basic test.
+%!test
+%! assert (ls_equal (ls_setdiff ([-1, -1, 1], [1, -1, -1]), [-1, 1, 1]));
+
+% 2D test with circles.
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = XX.^2 + YY.^2 - 8^2;
+%! phi2 = XX.^2 + YY.^2 - 5^2;
+%!
+%! phi = ls_setdiff (phi1, phi2);
+%! assert (ls_issubset (phi, phi1));
+%! assert (ls_disjoint (phi, phi2));
+%!
+%! phi = ls_setdiff (phi2, phi1);
+%! assert (ls_isempty (phi));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%! n = 100;
+%! x = linspace (-7, 7, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 2).^2 + YY.^2 - 3^2;
+%! phi2 = (XX + 2).^2 + YY.^2 - 3^2;
+%! phi = ls_setdiff (phi1, phi2);
+%!
+%! figure ();
+%! subplot (1, 2, 1);
+%! hold ("on");
+%! contour (XX, YY, phi1, [0, 0], "k");
+%! contour (XX, YY, phi2, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
+%!
+%! subplot (1, 2, 2);
+%! hold ("on");
+%! imagesc (x, x, phi);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ();
+%! contour (XX, YY, phi, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
+
+%!demo
+%! n = 100;
+%! x = linspace (-7, 7, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = XX.^2 + YY.^2 - 6^2;
+%! phi2 = XX.^2 + YY.^2 - 3^2;
+%! phi = ls_setdiff (phi1, phi2);
+%!
+%! figure ();
+%! subplot (1, 2, 1);
+%! hold ("on");
+%! contour (XX, YY, phi1, [0, 0], "k");
+%! contour (XX, YY, phi2, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
+%!
+%! subplot (1, 2, 2);
+%! hold ("on");
+%! imagesc (x, x, phi);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ();
+%! contour (XX, YY, phi, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
diff --git a/inst/ls_setxor.m b/inst/ls_setxor.m
new file mode 100644
index 0000000..e897d73
--- /dev/null
+++ b/inst/ls_setxor.m
@@ -0,0 +1,95 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_setxor (@var{phi1}, @var{phi2})
+##
+## Calculate a level-set function for the set ``xor'' of the
+## two domains given via @var{phi1} and @var{phi2}. In other words,
+## returned is a domain that contains all points that are in precisely
+## @emph{one} of the sets but not in both.
+##
+## @seealso{ls_complement, ls_union, ls_intersect, ls_setdiff, setxor}
+## @end deftypefn
+
+function phi = ls_setxor (phi1, phi2)
+ if (nargin () ~= 2)
+ print_usage ();
+ endif
+
+ if (~all (size (phi1) == size (phi2)))
+ error ("PHI1 and PHI2 must be of the same size");
+ endif
+
+ total = ls_union (phi1, phi2);
+ both = ls_intersect (phi1, phi2);
+ phi = ls_setdiff (total, both);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_setxor (1)
+%!error <Invalid call to>
+%! ls_setxor (1, 2, 3)
+%!error <PHI1 and PHI2 must be of the same size>
+%! ls_setxor (1, [1, 2])
+
+% Basic test.
+%!test
+%! assert (ls_equal (ls_setxor ([-1, -1, Inf], [1, -1, -Inf]), [-1, 1, -1]));
+
+% 2D test with circles.
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = XX.^2 + YY.^2 - 8^2;
+%! phi2 = XX.^2 + YY.^2 - 5^2;
+%!
+%! assert (ls_equal (ls_setdiff (phi1, phi2), ls_setxor (phi1, phi2)));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%! n = 100;
+%! x = linspace (-7, 7, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 2).^2 + YY.^2 - 3^2;
+%! phi2 = (XX + 2).^2 + YY.^2 - 3^2;
+%! phi = ls_setxor (phi1, phi2);
+%!
+%! figure ();
+%! subplot (1, 2, 1);
+%! hold ("on");
+%! contour (XX, YY, phi1, [0, 0], "k");
+%! contour (XX, YY, phi2, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
+%!
+%! subplot (1, 2, 2);
+%! hold ("on");
+%! imagesc (x, x, phi);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ();
+%! contour (XX, YY, phi, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
diff --git a/inst/ls_sign_colourmap.m b/inst/ls_sign_colourmap.m
new file mode 100644
index 0000000..b8bbab5
--- /dev/null
+++ b/inst/ls_sign_colourmap.m
@@ -0,0 +1,244 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} ls_sign_colourmap (@var{type} = @qcode{"sign"})
+## @deftypefnx {Function File} ls_sign_colourmap (@var{colours})
+## @deftypefnx {Function File} {@var{cmap} =} ls_sign_colourmap (@var{ax}, @var{type} = @qcode{"sign"})
+## @deftypefnx {Function File} {@var{cmap} =} ls_sign_colourmap (@var{ax}, @var{colours})
+##
+## Construct a colour map that can visibly distinguish between
+## positive and negative values.
+## These colour maps are especially suited to show the distinction between
+## above-zero and below-zero of a level-set function or for speed fields
+## between moving outward and inward.
+##
+## The colour axis range for which it is used should be given in @var{ax}.
+## There will always be 1024 entries in the constructed colour map.
+##
+## The map is ``defined'' by four colours, with a colour gradient between
+## the first two for positive values and a gradient between the third and
+## fourth for negative values. These colours can be explicitly given
+## in @var{colours} as a 4 x 3 matrix. Predefined ``types'' of maps
+## can be loaded with a string as @var{type} argument. It can be:
+##
+## @table @qcode
+## @item "sign"
+## The default value. Values above zero will be yellow--red, and values
+## below zero cyan--blue. There is a visible discontinuity in colour
+## at the zero level-set, with yellow and cyan on both ``sides'' of the
+## transition.
+##
+## @item "highlight"
+## Show zero as white (independent of the sign). Positive and negative
+## values are marked as red and blue, respectively. This is useful to
+## show speed fields and just highlight where they are most active.
+## (Like a heat map.)
+## @end table
+##
+## The forms without @var{ax} and output arguments use @code{caxis ()} of the
+## current figure and set the figure's colour map to the result, instead of
+## returning the constructed colour map.
+##
+## Use @code{demo ls_sign_colourmap} to get an overview of how the
+## predefined maps look like.
+##
+## @seealso{colormap, colorbar}
+## @end deftypefn
+
+function cmap = ls_sign_colourmap (varargin)
+
+ % Start by determining which form of the function was called and doing
+ % some usage checks, setting default arguments and all that.
+
+ n = 1024;
+ type = "sign";
+
+ if (length (varargin) == 0)
+ % No arguments at all: First form, keep defaults.
+ setFig = true;
+
+ elseif (strcmp (typeinfo (varargin{1}), "string"))
+ % First form with type given as string.
+ if (length (varargin) != 1)
+ print_usage ();
+ endif
+ setFig = true;
+ type = varargin{1};
+
+ elseif (min (size (varargin{1})) > 1)
+ % First form with colours given.
+ if (length (varargin) != 1)
+ print_usage ();
+ endif
+ setFig = true;
+
+ type = "custom";
+ colours = varargin{1};
+
+ else
+ % Should be second form.
+ setFig = false;
+ ax = varargin{1};
+
+ if (length (varargin) > 1)
+ if (length (varargin) != 2)
+ print_usage ();
+ endif
+
+ if (strcmp (typeinfo (varargin{2}), "string"))
+ type = varargin{2};
+ else
+ type = "custom";
+ colours = varargin{2};
+ endif
+ endif
+ endif
+
+ if (setFig && nargout () > 0)
+ print_usage ();
+ endif
+ if (!setFig && (min (size (ax)) != 1 || length (ax) != 2))
+ print_usage ();
+ endif
+
+ switch (type)
+ case "sign"
+ colours = [1, 0, 0; 1, 1, 0; 0, 1, 1; 0, 0, 1];
+ case "highlight"
+ colours = [1, 0, 0; 1, 1, 1; 1, 1, 1; 0, 0, 1];
+ case "custom"
+ if (size (colours, 1) != 4 || size (colours, 2) != 3)
+ print_usage ();
+ endif
+ otherwise
+ error ("unknown type of colour map: '%s'", type);
+ endswitch
+
+ if (setFig)
+ ax = caxis ();
+ endif
+
+ % Now comes the real calculation of the map.
+
+ cmap = NA (n, 3);
+
+ if (ax(1) >= 0)
+ nBelow = 0;
+ nAbove = n;
+ elseif (ax(2) <= 0)
+ nBelow = n;
+ nAbove = 0;
+ else
+ assert (ax(1) < 0 && ax(2) > 0);
+ nBelow = round (n * -ax(1) / (ax(2) - ax(1)));
+ nAbove = n - nBelow;
+ assert (nAbove, round (n * ax(2) / (ax(2) - ax(1))), 1);
+ endif
+
+ for i = 1 : nBelow
+ theta = (i - 1) / (nBelow - 1);
+ cmap(i, :) = theta * colours(3, :) + (1 - theta) * colours(4, :);
+ endfor
+
+ for i = 1 : nAbove
+ theta = (i - 1) / (nAbove - 1);
+ cmap(i + nBelow, :) = theta * colours(1, :) + (1 - theta) * colours(2, :);
+ endfor
+
+ if (setFig)
+ colormap (cmap);
+ endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% The tests check the various usage patterns, but not really
+% the functionality. This can be best checked by a visual inspection
+% of the demos.
+
+%!shared colours
+%! colours = [1, 1, 1; 0, 0, 0; 1, 1, 1; 0, 0, 0];
+
+% Test usage of the "first form".
+%!error <Invalid call to>
+%! ls_sign_colourmap (1, 2);
+%!error <Invalid call to>
+%! ls_sign_colourmap (1);
+%!error <Invalid call to>
+%! cmap = ls_sign_colourmap ();
+%!error <unknown type of colour map>
+%! ls_sign_colourmap ("foo");
+%!test
+%! figure ();
+%! ls_sign_colourmap ();
+%! ls_sign_colourmap ("highlight");
+%! ls_sign_colourmap (colours);
+%! close ();
+
+% Test usage of the "second form".
+%!error <Invalid call to>
+%! ls_sign_colourmap (1, 2, 3);
+%!error <Invalid call to>
+%! ls_sign_colourmap ([1, 2, 3]);
+%!error <Invalid call to>
+%! ls_sign_colourmap ([1, 2], 1);
+%!error <unknown type of colour map>
+%! ls_sign_colourmap ([1, 2], "foo");
+%!test
+%! cmap = ls_sign_colourmap ([1, 2]);
+%! cmap = ls_sign_colourmap ([1; 2], "highlight");
+%! cmap = ls_sign_colourmap ([1; 2], colours);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Demo function for the "sign" map.
+%!demo
+%! n = 100;
+%! x = linspace (-2, 2, n);
+%! y = linspace (-1, 3, n);
+%! [XX, YY] = meshgrid (x, y);
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 1], 1);
+%!
+%! figure ();
+%! imagesc (x, y, phi);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ();
+%! colorbar ();
+%! title ("Type 'sign'");
+
+% Demo function for the "highlight" version.
+%!demo
+%! n = 100;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi = ls_genbasic (XX, YY, "sphere", [0, 0], 1);
+%! d = ls_signed_distance (phi, h);
+%! F = YY .* exp (-10 * d.^2);
+%!
+%! figure ();
+%! hold ("on");
+%! imagesc (x, x, F);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ("highlight");
+%! colorbar ();
+%! contour (XX, YY, phi, [0, 0], "k", "LineWidth", 2);
+%! hold ("off");
+%! title ("Type 'highlight'");
diff --git a/inst/ls_signed_distance.m b/inst/ls_signed_distance.m
new file mode 100644
index 0000000..6559216
--- /dev/null
+++ b/inst/ls_signed_distance.m
@@ -0,0 +1,144 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{d} =} ls_signed_distance (@var{phi}, @var{h} = 1)
+##
+## Calculate the signed distance function of a set described by its level-set
+## function. The array @var{phi} must contain the values of the level-set
+## function on a rectangular grid with spacing @var{h}.
+##
+## The initial distances are approximated using @code{ls_init_narrowband},
+## and then @code{fastmarching} is used to propagate them to all other points
+## on the grid.
+##
+## It may be a good idea to use @code{ls_normalise} on the level-set function
+## before using this method, to prevent almost-zero values from underflowing
+## due to the performed calculations.
+##
+## @seealso{fastmarching, ls_distance_fcn, ls_solve_stationary, ls_normalise}
+## @end deftypefn
+
+function d = ls_signed_distance (phi, h = 1)
+ if (nargin () < 1 || nargin () > 2)
+ print_usage ();
+ endif
+
+ % We work with all positive distances for the beginning. This does not
+ % hurt, as points in the interior can only be reached by points on the
+ % inner side of the boundary, and vice versa.
+ d = __levelset_internal_init_narrowband (phi, h);
+ f = ones (size (phi));
+ d = fastmarching (d, h * f);
+
+ % Fix the sign.
+ d = ls_copy_sign (d, phi);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_signed_distance ()
+%!error <Invalid call to>
+%! ls_signed_distance (1, 2, 3)
+
+% Check 0D case.
+%!test
+%! assert (ls_signed_distance ([]), []);
+
+% Check 1D case, for which the expected result is trivial to calculate
+% and should be almost exact.
+%!test
+%! n = 10;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! phi = abs (x) - 1;
+%!
+%! d = ls_signed_distance (phi, h);
+%! assert (d, phi, sqrt (eps));
+
+% Test with circular region in 3D.
+%!test
+%! n = 50;
+%! x = linspace (-2, 2, n);
+%! h = x(2) - x(1);
+%! [XX, YY, ZZ] = ndgrid (x);
+%! RRsq = XX.^2 + YY.^2 + ZZ.^2;
+%! phi = RRsq - 1;
+%!
+%! d = ls_signed_distance (phi, h);
+%! assert (d, sqrt (RRsq) - 1, h);
+
+% Ensure that a previous error with numeric underflow for very small but
+% non-zero phi values is now fixed.
+%!test
+%! phi = [5e-6, 1.7e-154, -1.3e-2];
+%! for h = [0.02, 0.01, 0.005]
+%! d = ls_signed_distance (phi, h);
+%! assert (all (isfinite (d)));
+%! assert (d(2), 0);
+%! endfor
+
+% Check that ls_signed_distance together with phi normalisation
+% produces a level-set domain equivalent to the original one.
+%!test
+%! phis = ls_get_tests ();
+%! for i = 1 : length (phis)
+%! phi = ls_normalise (phis{i});
+%! d = ls_signed_distance (phi);
+%! assert (ls_equal (d, phi));
+%! endfor
+
+% Check for proper handling of signed zeros.
+%!test
+%! if (exist ("signbit") == 5)
+%! phi = [-0, 0];
+%! d = ls_signed_distance (phi);
+%! assert (ls_equal (phi, d));
+%! else
+%! warning ("'signbit' function not available, skipping test.");
+%! endif
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Use ls_signed_distance to "normalise" a very irregularly scaled
+% initial level-set function.
+%!demo
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi0 = atan (ls_union ((XX.^2 + (YY - 5).^2) - 20, ...
+%! (XX.^2 + (YY + 5).^2) - 20));
+%! %phi0 = XX.^2 / 1.3^2 + YY.^2 - 1;
+%! d = ls_signed_distance (phi0, h);
+%!
+%! figure ();
+%! subplot (1, 2, 1);
+%! contour (XX, YY, phi0);
+%! title ("Initial");
+%! colorbar ();
+%! axis ("equal");
+%!
+%! subplot (1, 2, 2);
+%! contour (XX, YY, d);
+%! title ("Signed Distance");
+%! colorbar ();
+%! axis ("equal");
diff --git a/inst/ls_solve_stationary.m b/inst/ls_solve_stationary.m
new file mode 100644
index 0000000..8218952
--- /dev/null
+++ b/inst/ls_solve_stationary.m
@@ -0,0 +1,299 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{d} =} ls_solve_stationary (@var{phi}, @var{f}, @var{h} = 1)
+## @deftypefnx {Function File} {@var{d} =} ls_solve_stationary (@var{phi}, @var{f}, @var{h} = 1, @var{nb})
+##
+## Solve a generalised Eikonal equation with speeds of arbitrary
+## signs. The equation solved is
+## @tex
+## \begin{equation*}
+## f \left| \nabla d \right| = 1
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## f | grad d | = 1
+## @end example
+##
+## @end ifnottex
+## with d = 0 on the boundary. The domain is described by the level-set
+## function @var{phi} on a rectangular grid. @var{f} should contain the values
+## of the speed field on the grid points. @var{h} can be given as the
+## grid spacing. In the second form, where the optional @var{nb} is given,
+## it is used to initialise the narrow band with a manual calculation.
+## By default, the result of @code{ls_init_narrowband} is used. Values which
+## are not fixed by the narrow band should be set to @code{NA}.
+##
+## Note that in comparison to @code{fastmarching}, the speed need not be
+## positive. It is the reciprocal of @var{f} in @code{fastmarching}.
+##
+## This is a preparation step, and afterwards, the evolved geometry according
+## to the level-set equation
+## @tex
+## \begin{equation*}
+## \phi_t + f \left| \nabla \phi \right| = 0
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## d/dt phi + f | grad phi | = 0
+## @end example
+##
+## @end ifnottex
+## can be extracted from @var{d} at arbitrary positive times using
+## the supplemental function @code{ls_extract_solution}.
+##
+## At points where @var{f} is exactly zero, the output will be set
+## to @code{NA}. This case is handled separately in @code{ls_extract_solution}.
+## In the narrow band, the returned distances may actually be negative
+## even for positive @var{f} and vice-versa. This helps to avoid
+## unnecessary errors introduced into the level-set function due to
+## a finite grid-size when the time step is chosen small.
+##
+## @seealso{ls_extract_solution, ls_signed_distance, ls_nb_from_geom}
+## @end deftypefn
+
+function d = ls_solve_stationary (phi, f, h = 1, nb)
+ if (nargin () < 2 || nargin () > 4)
+ print_usage ();
+ endif
+
+ sz = size (phi);
+ if (~all (sz == size (f)))
+ error ("PHI and F must be of the same size");
+ endif
+
+ if (nargin () < 4)
+ nb = ls_init_narrowband (phi, h);
+ elseif (~all (sz == size (nb)))
+ error ("PHI and NB must be of the same size");
+ endif
+
+ % Find regions of signs of f. Below for the main calculations,
+ % we will always ignore f = 0 regions and will mostly work with
+ % the absolute value of f, fixing the sign of d at the end.
+ fp = (f > 0);
+ fz = (f == 0);
+ fn = (f < 0);
+ fabs = abs (f);
+
+ % Find regions of the domain.
+ inner = (phi < 0);
+ outer = (phi > 0);
+
+ % Initialise narrow-band distances. Scale them (roughly) according
+ % to our f.
+ d0 = nb;
+ d0(~fz) ./= fabs(~fz);
+
+ % Handle points where the front moves away (depending on the signs of
+ % phi and f). In theory, these distances should be zero according to
+ % the definition in the theoretical paper. Set them to Inf or -Inf
+ % in the numerics for points deeply inside, and leave the narrow-band
+ % values for points near the boundary. Since fastmarching does not
+ % allow -Inf, choose the signs such that the points will be passed with
+ % positive Inf to fastmarching. The sign will be reverted later
+ % on to get the value matching the distance semantics (-Inf inside with
+ % front moving away and Inf outside).
+ % For phi = 0, nothing special needs to be done as d0 is set to zero there
+ % already by internal_init_narrowband.
+ d0(isna (d0) & inner & fp) = Inf;
+ d0(isna (d0) & outer & fn) = -Inf;
+ assert (all (isna (d0(:)) | ~isnan(d0(:))));
+
+ % Solve for both parts. Invert the negative one's distances. Also for
+ % the "islands" with -Inf there, this is fine, as islands mean that the
+ % respective points are never reached by the front, thus never taken
+ % out of the domain, and thus D < -t should always be true for them
+ % and all arbitrarily large times t.
+ dp = solvePart (d0, fp, h, fabs);
+ % Be careful with -NA ~= NA!
+ invD0 = -d0;
+ invD0(isna (d0)) = NA;
+ dn = -solvePart (invD0, fn, h, fabs);
+
+ % Build everything together.
+ d = NA (size (phi));
+ d(fp) = dp(fp);
+ d(fn) = dn(fn);
+endfunction
+
+% D = solvePart (D0, REGION, H, FABS)
+%
+% Utility function that performs one of the two solves required for the
+% regions with positive and negative f. D0 should be the initial distances
+% (all positive), REGION a boolean flag of the region we process (f > 0
+% or f < 0), H the grid spacing and FABS the absolute value of f.
+%
+% We solve in the given REGION using fast marching for the distances
+% of points that are eventually (but not initially) reached by the front,
+% and also handle the case of never-reached "islands".
+
+function d = solvePart (d0, region, h, fabs)
+ d = d0;
+ d(~region) = Inf;
+ fval = NA (size (fabs));
+ fval(region) = h ./ fabs(region);
+ d = fastmarching (d, fval);
+
+ % Set distances from which the front moved away from Inf to -Inf.
+ d(d == Inf & region) = -Inf;
+
+ % If a region is not reached by the fast marching (i. e., NA), this means
+ % that it is separated from the boundary by a sign change of f. This also
+ % means that it will actually never be reached, and thus we can set its
+ % distance to Inf.
+ d(isna (d)) = Inf;
+
+ assert (all (~isnan (d(:))));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_solve_stationary (1)
+%!error <Invalid call to>
+%! ls_solve_stationary (1, 2, 3, 4, 5)
+%!error <PHI and F must be of the same size>
+%! ls_solve_stationary ([1, 2], [3; 4]);
+%!error <PHI and NB must be of the same size>
+%! ls_solve_stationary ([1, 2], [1, 2], :, [3; 4]);
+
+% Check 0D case.
+%!test
+%! assert (ls_solve_stationary ([], []), []);
+
+% Check invariants expected for the output in terms
+% of finite/infinite values and signs.
+%!function checkOutput (phi, f, d)
+%! assert (all (isna (d(f == 0))));
+%! where = (f ~= 0);
+%! assert (all (isfinite (d(where)) | isinf (d(where))));
+%! assert (sign (d(where)), sign (phi(where)));
+%!endfunction
+
+% For the test phi's, just check that solving works
+% without any errors.
+%!test
+%! phis = ls_get_tests ();
+%! for i = 1 : length (phis)
+%! f = ones (size (phis{i}));
+%! for j = -1 : 1
+%! phi = ls_normalise (phis{i});
+%! d = ls_solve_stationary (phi, j * f);
+%! checkOutput (phi, j * f, d);
+%! endfor
+%! endfor
+
+% Test for island handling and the general output conditions
+% (that phi = 0 leads to D = NA and otherwise we only ever get
+% some ordinary values with signs depending on the sign of f
+% or infinities with appropriate signs).
+%
+% The constructed example contains in each half-plane (left / right of the
+% y-axis) an initial circle as domain, and a "ring" around the domain boundary
+% where f is positive or negative. This ensures we have a sign change of f
+% both in the interior and exterior of the initial domain. Depending on the
+% sign of f (and thus the half-plane), one of them will be an "island".
+%
+%!test
+%! n = 101;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! r1 = (XX - 5).^2 + YY.^2;
+%! r2 = (XX + 5).^2 + YY.^2;
+%!
+%! phi = ls_union (r1 - 2^2, r2 - 2^2);
+%! f1 = max (r1 - 3^2, 1^2 - r1);
+%! f2 = max (r2 - 3^2, 1^2 - r2);
+%! f = min (f1, f2) .* sign (XX);
+%! assert (~all (f(:) ~= 0));
+%!
+%! d = ls_solve_stationary (phi, f, h);
+%!
+%! checkOutput (phi, f, d);
+%! where = (f ~= 0);
+%! assert (any (isinf (d(where))));
+%!
+%! %{
+%! contour (phi, [0, 0]);
+%! colorbar ();
+%! figure ();
+%! imagesc (f);
+%! colorbar ();
+%! figure ();
+%! imagesc (d);
+%! colorbar ();
+%! %}
+
+% Test manual NB initialisation.
+%!test
+%! phi = [3, 1, -1, 1, 3];
+%! f = ones (size (phi));
+%! dists1 = ls_solve_stationary (phi, f);
+%! dists2 = ls_solve_stationary (phi, f, :, [NA, NA, -0.1, NA, NA]);
+%!
+%! assert (dists1, [1.5, 0.5, -0.5, 0.5, 1.5], sqrt (eps));
+%! assert (dists2, [1.9, 0.9, -0.1, 0.9, 1.9]);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Visualise refraction and diffraction of a wave front. If we only
+% use positive speeds, then the different times can be plotted nicely
+% just with 'contour'.
+%
+%!demo
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi0 = YY - XX + 15;
+%! f = sign (XX) + 2;
+%!
+%! d = ls_solve_stationary (phi0, f, h);
+%! figure ();
+%! contour (XX, YY, d);
+%! line ([0, 0], [-10, 10]);
+%! title ("Refraction");
+%
+%!demo
+%! n = 101;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi0 = XX + 9;
+%! f = ones (size (phi0));
+%! f(XX == 0 & abs (YY) > 2) = 0;
+%!
+%! d = ls_solve_stationary (phi0, f, h);
+%! figure ();
+%! contour (XX, YY, d);
+%! line ([0, 0], [-10, -2]);
+%! line ([0, 0], [2, 10]);
+%! title ("Diffraction");
+
+% XXX: Can we use this for ray-tracing as a nice example???
diff --git a/inst/ls_time_step.m b/inst/ls_time_step.m
new file mode 100644
index 0000000..4b5d10f
--- /dev/null
+++ b/inst/ls_time_step.m
@@ -0,0 +1,253 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phit} =} ls_time_step (@var{t}, @var{phi0}, @var{f}, @var{h} = 1)
+## @deftypefnx {Function File} {@var{phit} =} ls_time_step (@var{t}, @var{c}, @var{phi0}, @var{f}, @var{h} = 1)
+##
+## Evolve the level-set equation with time stepping. Perform explicit time
+## stepping on the equation
+## @tex
+## \begin{equation*}
+## \phi_t + f \left| \nabla \phi \right| = 0.
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## d/dt phi + f | grad phi | = 0.
+## @end example
+##
+## @end ifnottex
+## The initial value is given by @var{phi0}. @var{phi0} and @var{f} must
+## be of the same size. If @var{h} is present, it sets the spatial grid size.
+## The time stepping uses @code{upwind_gradient_norm} for the evaluation
+## of the gradient norm at each step.
+##
+## @var{t} is the time (or vector of times) at which the solution should
+## be returned. If more than one time is given, the result @var{phit}
+## will be a cell array of the evolved level-set functions at each of
+## the requested time points. If @var{t} is a scalar, @var{phit} is
+## returned as array of the same size of @var{phi0}.
+##
+## The time step is chosen to satisfy the Courant-Friedrichs-Lewy condition
+## @tex
+## \begin{equation*}
+## \Delta t = c \frac{h}{F_m n}.
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## dt = c h / (Fm * n).
+## @end example
+##
+## @end ifnottex
+## Here, Fm is the maximum absolute value in @var{f}, and n is the number
+## of space dimensions. The desired ratio c is by default one, but can be
+## passed explicitly with the second usage form.
+##
+## @seealso{ls_extract_solution, upwind_gradient_norm}
+## @end deftypefn
+
+function phit = ls_time_step (t, second, third, fourth, fifth)
+ c = 1;
+ h = 1;
+ switch nargin ()
+ case 3
+ phi0 = second;
+ F = third;
+ case 4
+ if (isscalar (second))
+ c = second;
+ phi0 = third;
+ F = fourth;
+ else
+ phi0 = second;
+ F = third;
+ h = fourth;
+ endif
+ case 5
+ c = second;
+ phi0 = third;
+ F = fourth;
+ h = fifth;
+ otherwise
+ print_usage ();
+ endswitch
+
+ if (!isscalar (h))
+ print_usage ();
+ endif
+
+ if (~all (size (phi0) == size (F)))
+ error ("PHI0 and F must be of the same size");
+ endif
+
+ if (!isvector (t))
+ error ("T must be a vector or scalar");
+ endif
+
+ Fm = norm (F(:), Inf);
+ n = sum (size (phi0) > 1);
+ dt = c * h / (Fm * n);
+
+ phit = cell (length (t), 1);
+
+ phi = phi0;
+ curT = 0;
+ nextTind = 1;
+
+ while (nextTind <= length (t))
+ gradNorm = upwind_gradient_norm (phi, F, h);
+
+ dNextT = t(nextTind) - curT;
+ if (dNextT < 0)
+ error ("T must be ascending");
+ endif
+
+ if (dNextT <= dt)
+ phi -= dNextT * F .* gradNorm;
+ curT += dNextT;
+ assert (curT, t(nextTind));
+ phit{nextTind} = phi;
+ ++nextTind;
+ else
+ phi -= dt * F .* gradNorm;
+ curT += dt;
+ endif
+ endwhile
+
+ if (length (phit) == 1)
+ phit = phit{1};
+ endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_time_step (1, 2)
+%!error <Invalid call to>
+%! ls_time_step (1, 2, 3, 4, 5, 6)
+%!error <Invalid call to>
+%! ls_time_step (1, 2, 3, 4, [5, 5])
+%!error <PHI0 and F must be of the same size>
+%! ls_time_step ([1; 1], [2, 2], [3, 3, 3])
+%!error <PHI0 and F must be of the same size>
+%! ls_time_step ([1, 1], 1, [2, 2], [3, 3, 3])
+%!error <T must be a vector or scalar>
+%! ls_time_step (ones (2, 2), 1, 1)
+
+% Test a monotone profile is shifted when we have constant speed.
+% Also test basic usage.
+%!test
+%! n = 100;
+%! x = linspace (0, 1, n);
+%! h = x(2) - x(1);
+%!
+%! phi0 = sin (x);
+%! fPos = ones (size (phi0));
+%!
+%! % Single time step, moving exactly one grid step.
+%! phit = ls_time_step (1, phi0, fPos);
+%! assert (phit(2 : end), phi0(1 : end - 1));
+%!
+%! % t = 1 should correspond to the previous result, and t = 2
+%! % shifts one more time step. However, due to boundary effects,
+%! % we have to exclude some elements in the comparisons.
+%! phitArr = ls_time_step ([1, 2], 0.5, phi0, fPos);
+%! assert (size (phitArr), [2, 1]);
+%! assert (phitArr{1}(3 : end), phit(3 : end), 1e-4);
+%! assert (phitArr{2}(5 : end), phi0(3 : end - 2), 1e-4);
+%!
+%! % Try out movement in the other direction.
+%! phit = ls_time_step (0.5, 0.1, phi0, -fPos, 0.5);
+%! assert (phit(1 : end - 4), phi0(2 : end - 3), 1e-4);
+
+% Compare evolution to the fast marching method.
+%!function compareFastMarching (t, phi0, F, h, tol)
+%! phit = ls_time_step (t, phi0, F, h);
+%! d = ls_solve_stationary (phi0, F, h);
+%! phit_fm = ls_extract_solution (t, d, phi0, F);
+%!
+%! sd1 = ls_signed_distance (phit, h);
+%! sd2 = ls_signed_distance (phit_fm, h);
+%! assert (sd1, sd2, tol);
+%!endfunction
+
+% Run comparison tests between fast marching and time stepping.
+%!test
+%! warning ("off", "level-set:fast-marching:too-far-alive");
+%!
+%! n = 100;
+%! x = linspace (-5, 5, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], [3, 1.5]);
+%! F = ones (size (phi0));
+%! compareFastMarching (0.5, phi0, F, h, h / 2);
+%! compareFastMarching (1.5, phi0, F, h, h / 2);
+%!
+%! phi0 = ls_genbasic (XX, YY, "box", [-3, -3], [3, 3]);
+%! F = YY / 5;
+%! compareFastMarching (1, phi0, F, h, h);
+%!
+%! F = ones (size (phi0));
+%! compareFastMarching (1, phi0, F, h, h / 2);
+%!
+%! phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
+%! F = sin (XX .* YY);
+%! compareFastMarching (1, phi0, F, h, h);
+
+% Try also a 3D problem.
+%!test
+%! warning ("off", "level-set:fast-marching:increased-distance");
+%!
+%! n = 50;
+%! x = linspace (-5, 5, n);
+%! h = x(2) - x(1);
+%! [XX, YY, ZZ] = ndgrid (x, x, x);
+%!
+%! phi0 = ls_genbasic (XX, YY, ZZ, "sphere", [0, 0, 0], 3);
+%! F = ones (size (phi0));
+%! compareFastMarching (1, phi0, F, h, h / 2);
+%! compareFastMarching (1, phi0, -F, h, h / 2);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+%!demo
+%! n = 500;
+%! x = linspace (-5, 5, n);
+%! h = x(2) - x(1);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! F = sin (XX .* YY);
+%! phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
+%! phit = ls_time_step ([0.5, 1], phi0, F, h);
+%!
+%! figure ();
+%! hold ("on");
+%! imagesc (x, x, F);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ();
+%! contour (XX, YY, phi0, [0, 0], "k", "LineWidth", 2);
+%! contour (XX, YY, phit{1}, [0, 0], "k", "LineWidth", 2);
+%! contour (XX, YY, phit{2}, [0, 0], "k", "LineWidth", 2);
+%! hold ("off");
diff --git a/inst/ls_union.m b/inst/ls_union.m
new file mode 100644
index 0000000..18b334b
--- /dev/null
+++ b/inst/ls_union.m
@@ -0,0 +1,86 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_union (@var{phi1}, @var{phi2})
+## @deftypefnx {Function File} {@var{phi} =} ls_union (@var{phi}, ...)
+##
+## Calculate a level-set function for the union of the sets described
+## by the argument level-set functions.
+##
+## @seealso{ls_complement, ls_intersect, ls_setdiff, ls_setxor, union}
+## @end deftypefn
+
+function res = ls_union (varargin)
+ res = ls_union_intersect (@min, varargin, "ls_union");
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! ls_union ()
+%!error <size mismatch in the arguments>
+%! ls_union (1, -2, [1, 2])
+
+% Test that the union creates something which contains all pieces.
+%!test
+%! n = 50;
+%! x = linspace (-10, 10, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 3).^2 + (YY - 3).^2 - 2^2;
+%! phi2 = (XX + 3).^2 + (YY + 3).^2 - 2^2;
+%! phi3 = XX.^2 + YY.^2 - 2^2;
+%!
+%! phi = ls_union (phi1, phi2, phi3);
+%! assert (ls_issubset (phi1, phi));
+%! assert (ls_issubset (phi2, phi));
+%! assert (ls_issubset (phi3, phi));
+%!
+%! assert (ls_union (phi3), phi3);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%! n = 100;
+%! x = linspace (-7, 7, n);
+%! [XX, YY] = meshgrid (x, x);
+%!
+%! phi1 = (XX - 2 * cos (7/6 * pi)).^2 + (YY - 2 * sin (7/6 * pi)).^2 - 3^2;
+%! phi2 = (XX - 2 * cos (11/6 * pi)).^2 + (YY - 2 * sin (11/6 * pi)).^2 - 3^2;
+%! phi3 = XX.^2 + (YY - 2).^2 - 3^2;
+%! phi = ls_union (phi1, phi2, phi3);
+%!
+%! figure ();
+%! subplot (1, 2, 1);
+%! hold ("on");
+%! contour (XX, YY, phi1, [0, 0], "k");
+%! contour (XX, YY, phi2, [0, 0], "k");
+%! contour (XX, YY, phi3, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
+%!
+%! subplot (1, 2, 2);
+%! hold ("on");
+%! imagesc (x, x, phi);
+%! set (gca (), "ydir", "normal");
+%! ls_sign_colourmap ();
+%! contour (XX, YY, phi, [0, 0], "k");
+%! hold ("off");
+%! axis ("equal");
diff --git a/inst/maze.png b/inst/maze.png
new file mode 100644
index 0000000..8f64570
Binary files /dev/null and b/inst/maze.png differ
diff --git a/inst/private/ls_copy_sign.m b/inst/private/ls_copy_sign.m
new file mode 100644
index 0000000..17d1092
--- /dev/null
+++ b/inst/private/ls_copy_sign.m
@@ -0,0 +1,81 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{d} =} ls_copy_sign (@var{d}, @var{phi})
+##
+## Internal routine for taking over the sign of @var{phi} onto @var{d}. This
+## routine assumes that @var{d} is positive initially. It copies over the
+## sign of @var{phi} to @var{d}, taking signed zeros into account.
+##
+## @seealso{ls_signed_distance, ls_init_narrowband, ls_nb_from_geom, sign}
+## @end deftypefn
+
+function d = ls_copy_sign (d, phi)
+ if (nargin () != 2)
+ print_usage ();
+ endif
+
+ sz = size (phi);
+ if (!all (size (d) == sz))
+ error ("size mismatch in the arguments");
+ endif
+
+ if (!all (d(:) >= 0 | isna (d(:))))
+ error ("D should initially be positive");
+ endif
+
+ if (exist ("signbit") == 5)
+ where = signbit (phi);
+ else
+ where = (phi < 0);
+ endif
+ d(where & !isna (d)) *= -1;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for errors.
+%!error <Invalid call to>
+%! ls_copy_sign (1);
+%!error <Invalid call to>
+%! ls_copy_sign (1, 2, 3);
+%!error <size mismatch in the arguments>
+%! ls_copy_sign (1, [2, 3]);
+%!error <D should initially be positive>
+%! ls_copy_sign (-1, 5);
+
+% Test basic functionality.
+%!test
+%! d = [0, 1, 2, eps, Inf];
+%! zeros = (d == 0);
+%! phi = [-1, 1, 1, -1, -1];
+%! dp = ls_copy_sign (d, phi);
+%! assert (abs (dp), d);
+%! assert (sign (dp(!zeros)), sign (phi(!zeros)));
+
+% Test signed zeros.
+%!test
+%! if (exist ("signbit") == 5)
+%! d = [0, 0, 1, 1];
+%! phi = [-0, 0, -0, 0];
+%! dp = ls_copy_sign (d, phi);
+%! assert (signbit (dp), signbit (phi));
+%! assert (dp, [-0, 0, -1, 1]);
+%! else
+%! warning ("'signbit' function not available, skipping test.");
+%! endif
diff --git a/inst/private/ls_union_intersect.m b/inst/private/ls_union_intersect.m
new file mode 100644
index 0000000..3f5cfba
--- /dev/null
+++ b/inst/private/ls_union_intersect.m
@@ -0,0 +1,43 @@
+## Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{phi} =} ls_union_intersect (@var{fcn}, @var{phis}, @var{fcnName})
+##
+## Internal routine to calculate intersection or union. @var{fcn} should be
+## a handle to either @code{min} or @code{max}. @var{phis} is a cell-array
+## of level-set functions.
+##
+## @seealso{ls_union, ls_intersect}
+## @end deftypefn
+
+function res = ls_union_intersect (fcn, phis, fcnName)
+ if (length (phis) < 1)
+ print_usage (fcnName);
+ endif
+
+ sz = size (phis{1});
+ for i = 2 : length (phis)
+ if (!all (size (phis{i}) == sz))
+ error ("size mismatch in the arguments");
+ endif
+ endfor
+
+ res = phis{1};
+ for i = 2 : length (phis)
+ res = fcn (res, phis{i});
+ endfor
+endfunction
diff --git a/inst/private/so_load_compressed.m b/inst/private/so_load_compressed.m
new file mode 100644
index 0000000..7786d43
--- /dev/null
+++ b/inst/private/so_load_compressed.m
@@ -0,0 +1,35 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{x} =} so_load_compressed (@var{fd}, @var{field}, @var{data})
+##
+## Internal routine that loads and optionally ``uncompresses'' data
+## from a descent log. This is used by both @code{so_replay_descent}
+## and @code{so_explore_descent} and thus shared here.
+##
+## @seealso{so_replay_descent, so_explore_descent}
+## @end deftypefn
+
+function x = so_load_compressed (fd, field, data)
+ x = fload (fd);
+
+ if (isfield (data, "compress") && isfield (data.compress, "load")
+ && isfield (data.compress.load, field))
+ fcn = getfield (data.compress.load, field);
+ x = fcn (x, data);
+ endif
+endfunction
diff --git a/inst/so_example_problem.m b/inst/so_example_problem.m
new file mode 100644
index 0000000..2586edd
--- /dev/null
+++ b/inst/so_example_problem.m
@@ -0,0 +1,309 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{data} =} so_example_problem (@var{data})
+##
+## Construct an example problem for shape optimisation. The example problem
+## is one-dimensional. Roughly speaking, the cost is chosen such that
+## the optimal solution should be symmetric around the origin and
+## have a determined volume.
+##
+## The parameters in @code{@var{data}.p} as well as the basic grid in
+## @code{@var{data}.g} should already be set. This routine fills in
+## the necessary callbacks to compute the cost and to find a descent direction.
+##
+## These parameters in @code{@var{data}.p} are used to refine the
+## example problem:
+##
+## @table @code
+## @code vol
+## The desired volume. Deviations from this volume will be penalised in the
+## cost functional.
+##
+## @code weight
+## Weight of the volume penalisation term. This is relative to the term
+## that penalises non-symmetry and should be relatively large.
+## @end table
+##
+## Note that these routines assume (for simplicity) that the level-set domain
+## will always be a single interval. Thus, the initial domain should
+## fulfil this assumption. (It will then automatically stay that way
+## even when the shape is evolved.)
+##
+## In addition to the callbacks, also utility routines will be returned
+## that can be used in handlers for plotting. They will be set
+## in @code{@var{data}.util}:
+##
+## @table @code
+## @item [@var{a}, @var{b}] = bounds (@var{phi}, @var{data})
+## Find the left and right bounds of the (assumed) interval that the
+## level-set function @var{phi} describes. This makes use of the
+## grid in @code{@var{data}.g}. The bounds will also be filled into
+## the state struct as @code{@var{s}.a} and @code{@var{s}.b}.
+## @end table
+##
+## @seealso{so_run_descent}
+## @end deftypefn
+
+function data = so_example_problem (data)
+ if (nargin () != 1)
+ print_usage ();
+ endif
+ if (!isstruct (data))
+ error ("DATA should be a struct");
+ endif
+
+ if (!isfield (data, "figs"))
+ data.figs = struct ();
+ endif
+
+ data.util = struct ("bounds", @bounds);
+ data.cb = struct ("update_state", @updateState, ...
+ "get_direction", @getDirection);
+ data.handler = struct ("direction", @plotSpeed);
+
+ % Set also an "onFirst" handler for so_explore_descent.
+ % This handler plots all (already known) costs as we go.
+ if (isfield (data.figs, "exploreCosts"))
+ data.onFirst = struct ("before_step", @plotCosts);
+ endif
+
+ holdall = true (size (data.g.x));
+ holdall([1, end]) = false;
+ data.g.constraints = struct ("holdall", holdall);
+endfunction
+
+% Cost function of the example problem.
+function s = updateState (phi, data)
+ s = struct ("cost", 0);
+
+ % Disallow empty domain.
+ if (ls_isempty (phi))
+ s.cost = Inf;
+ return;
+ endif
+
+ % Penalise deviations from the desired volume.
+ [s.a, s.b] = bounds (phi, data);
+ s.vol = s.b - s.a;
+ assert (s.vol > 0);
+ s.cost += data.p.weight * (s.vol - data.p.vol)^2;
+
+ % Penalise non-symmetry. This is done by integrating over x.
+ where = ls_inside (phi);
+ s.integ = data.g.h * sum (data.g.x(where));
+ s.cost += s.integ^2;
+endfunction
+
+% Compute shape derivative and gradient.
+function [f, dJ] = getDirection (data)
+
+ % Find derivatives at the end points (assuming F = 1).
+ bdry = [data.s.a, data.s.b];
+ deriv = zeros (size (bdry));
+ for i = 1 : length (bdry)
+ deriv(i) += 2 * data.p.weight * (data.s.vol - data.p.vol);
+ deriv(i) += 2 * data.s.integ * bdry(i);
+ endfor
+
+ % Use derivatives at the end points to compute dJ. Furthermore,
+ % build the descent direction as a linear interpolation of the
+ % end-point derivatives.
+
+ f = NA (size (data.g.x));
+
+ left = (data.g.x < data.s.a);
+ fact = (data.g.x(left) - data.g.x(1)) ./ (data.s.a - data.g.x(1));
+ f(left) = -deriv(1) * fact;
+
+ mid = (data.g.x >= data.s.a & data.g.x <= data.s.b);
+ fact = (data.g.x(mid) - data.s.a) ./ (data.s.b - data.s.a);
+ f(mid) = -deriv(2) * fact - (1 - fact) * deriv(1);
+
+ right = (data.g.x > data.s.b);
+ fact = (data.g.x(end) - data.g.x(right)) ./ (data.g.x(end) - data.s.b);
+ f(right) = -deriv(2) * fact;
+
+ assert (all (isfinite (f)));
+ dJ = -dot (deriv, deriv);
+endfunction
+
+% Handler to plot speed and the current situation.
+function log = plotSpeed (k, f, dJ, data)
+ fmin = min (f);
+ fmax = max (f);
+
+ if (!isfield (data.figs, "speed"))
+ figure ();
+ else
+ figure (data.figs.speed);
+ clf ();
+ endif
+ hold ("on");
+ plot (data.g.x, f, "b");
+ plot ([-5, -5], [fmin, fmax], "k");
+ plot ([data.s.a, data.s.a], [fmin, fmax], "r");
+ plot ([5, 5], [fmin, fmax], "k");
+ plot ([data.s.b, data.s.b], [fmin, fmax], "r");
+ hold ("off");
+ axis ([data.g.x(1), data.g.x(end), fmin, fmax]);
+ legend ("Speed", "Target", "Current");
+ title (sprintf ("Step %d", k));
+
+ log = data.log;
+endfunction
+
+% Plot the currently known costs. This is used for so_explore_descent.
+function log = plotCosts (k, data)
+ assert (k <= data.log.steps);
+
+ figure (data.figs.exploreCosts);
+ clf ();
+ costs = postpad (data.log.costs, data.p.nSteps + 1, NA);
+ semilogy (0 : data.p.nSteps, costs, "o");
+ axis ([0, data.p.nSteps]);
+ title (sprintf ("Costs up to step %d", data.log.steps));
+
+ log = data.log;
+endfunction
+
+% Bounds utility function.
+function [a, b] = bounds (phi, data)
+ if (ls_isempty (phi))
+ error ("PHI describes empty domain");
+ endif
+ if (!all (size (phi) == size (data.g.x)))
+ error ("PHI and DATA.g.x size mismatch");
+ endif
+
+ % Hack around infinite phi values. This could be done
+ % nicer by checking for infinite values directly (as it
+ % is done in ls_find_geometry, for instance), but just
+ % do it like this for simplicity.
+ whereInf = !isfinite (phi);
+ phi(whereInf) = sign (phi(whereInf)) * max (abs (phi(!whereInf)));
+
+ insideInd = find (ls_inside (phi));
+ minInd = min (insideInd);
+ maxInd = max (insideInd);
+
+ convexZero = @(phiA, phiB, xA, xB) ...
+ xA + (xB - xA) * abs (phiA) / (abs (phiA) + abs (phiB));
+
+ if (minInd == 1)
+ a = data.g.x(1);
+ else
+ a = convexZero (phi(minInd - 1), phi(minInd), ...
+ data.g.x(minInd - 1), data.g.x(minInd));
+ endif
+
+ if (maxInd == length (phi))
+ b = data.g.x(end);
+ else
+ b = convexZero (phi(maxInd), phi(maxInd + 1), ...
+ data.g.x(maxInd), data.g.x(maxInd + 1));
+ endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Build basic data with example grid.
+%!shared basicData
+%! basicData = struct ();
+%! basicData.p = so_init_params (true);
+%! basicData.p.vol = 4;
+%! basicData.p.weight = 10;
+%!
+%! n = 50;
+%! x = linspace (-4, 5, n);
+%! h = x(2) - x(1);
+%! basicData.g = struct ("x", x, "h", h);
+
+% Test for errors.
+%!error <Invalid call to>
+%! so_example_problem ();
+%!error <Invalid call to>
+%! so_example_problem (1, 2);
+%!error <DATA should be a struct>
+%! so_example_problem (1);
+
+% Test the bounds routine created.
+%!test
+%! data = so_example_problem (basicData);
+%! phi = ls_genbasic (data.g.x, "box", -2.5, 1.3);
+%!
+%! [a, b] = data.util.bounds (phi, data);
+%! assert (a, -2.5, sqrt (eps));
+%! assert (b, 1.3, sqrt (eps));
+
+% Test that the cost works qualitatively as it should.
+%!test
+%! data = so_example_problem (basicData);
+%!
+%! phi = ls_genbasic (data.g.x, "box", -2, 2);
+%! s0 = data.cb.update_state (phi, data);
+%!
+%! phi = ls_genbasic (data.g.x, "box", -3, 3);
+%! s1 = data.cb.update_state (phi, data);
+%!
+%! phi = ls_genbasic (data.g.x, "box", -1, 1);
+%! s2 = data.cb.update_state (phi, data);
+%!
+%! phi = ls_genbasic (data.g.x, "box", -1, 3);
+%! s3 = data.cb.update_state (phi, data);
+%!
+%! phi = ls_genbasic (data.g.x, "box", -3, 1);
+%! s4 = data.cb.update_state (phi, data);
+%!
+%! assert (all ([s0.cost, s1.cost, s2.cost, s3.cost, s4.cost] >= 0));
+%! assert (s0.cost, 0, 1e-1);
+%! assert (s1.cost > s3.cost);
+%! assert (s1.cost > s4.cost);
+%! assert (s2.cost > s3.cost);
+%! assert (s2.cost > s4.cost);
+%! assert (s3.cost > s0.cost);
+%! assert (s4.cost > s0.cost);
+
+% Do a rough finite-difference test for the derivative and ensure
+% that we have a descent direction.
+%
+%!function testDeriv (a, b, data)
+%! phi = ls_genbasic (data.g.x, "box", a, b);
+%! data.s = data.cb.update_state (phi, data);
+%! [f, dJ] = data.cb.get_direction (data);
+%! assert (dJ < 0);
+%!
+%! dt = 5e-3;
+%! dists = ls_solve_stationary (phi, f, data.g.h);
+%! phip = ls_extract_solution (dt, dists, phi, f);
+%! sp = data.cb.update_state (phip, data);
+%!
+%! assert (sp.cost < data.s.cost);
+%! relErr = abs (sp.cost - (data.s.cost + dt * dJ)) / data.s.cost;
+%! assert (relErr < 0.1);
+%!endfunction
+%
+%!test
+%! data = so_example_problem (basicData);
+%!
+%! testDeriv (-1, 1, data);
+%! testDeriv (-3, 3, data);
+%! testDeriv (-1, 3, data);
+%! testDeriv (-3, 1, data);
+
+% This routine is also "tested" by the tests and demos in so_run_descent.
diff --git a/inst/so_explore_descent.m b/inst/so_explore_descent.m
new file mode 100644
index 0000000..66cae46
--- /dev/null
+++ b/inst/so_explore_descent.m
@@ -0,0 +1,362 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} so_explore_descent (@var{file}, @var{init})
+##
+## Interactively explore a descent file saved by @code{so_save_descent}.
+## This opens a shell where the user can navigate through the replay.
+##
+## The arguments are the same as for @code{so_replay_descent}. In order
+## to allow backwards navigation, @var{file} must either be a file name
+## or a file descriptor that allows seeking.
+##
+## On the initial call, the @code{initialised} handler is called.
+## Then, @code{before_step}, @code{direction} and @code{after_step}
+## will be called for the currently active data at each navigation
+## of the user. Note that the descent steps on which they are called may be
+## out-of-order and may skip intermediate iterations. The @code{finished}
+## handler will be called with the data that is active when the user closes
+## the interactive session.
+##
+## Note that @code{@var{data}.log.steps} and @code{@var{data}.log.costs} will
+## always contain @emph{all} steps and costs that have been loaded during
+## the replay so far, which may not correspond to the currently active step.
+## These fields will be present in all calls to handlers except
+## @code{initialised}, not just for the call to @code{finished}.
+##
+## In addition to the default handlers in @code{handler} of the
+## @var{data} struct, also extra handlers can be defined in
+## @code{@var{data}.onFirst}. They will be called @emph{in order} and
+## @emph{on each} iteration when it is first loaded from the descent log.
+## The @code{finished} handler will be called on the last iteration data
+## that has been loaded before the session is terminated. This may not
+## be the active data at that point in time, if the user has navigated
+## backwards. The @code{onFirst} handlers (if present) will be called before
+## the corresponding @code{handler} functions.
+##
+## No on-the-fly computation of extra steps (as it is done by
+## @code{so_replay_descent} when not enough steps are saved in the log) is
+## supported by this routine. Instead, it is simply not possible to step
+## beyond EOF of the log file.
+##
+## This routine requires @code{fload} from the @code{parallel} package
+## to be available.
+##
+## @seealso{so_replay_descent, so_run_descent, so_save_descent, fload}
+## @end deftypefn
+
+function so_explore_descent (file, init)
+ if (nargin () != 2)
+ print_usage ();
+ endif
+
+ openFile = ischar (file);
+ if (!openFile && (!isnumeric (file) || !isscalar (file)))
+ error ("FILE must be a string or file ID");
+ endif
+
+ hasFload = exist ("fload");
+ if (hasFload != 2 && hasFload != 3)
+ error ("'fload' is not available");
+ endif
+
+ % Open file and read header.
+ if (openFile)
+ fd = fopen (file, "rb");
+ if (fd == -1)
+ error ("failed to open file '%s' for reading the descent log", file);
+ endif
+ else
+ fd = file;
+ endif
+ header = fload (fd);
+
+ % Call init method on the header.
+ data = init (header);
+
+ % Add some (possibly) missing stuff with defaults.
+ if (!isfield (data, "log"))
+ data.log = struct ();
+ endif
+ if (!isfield (data, "handler"))
+ data.handler = struct ();
+ endif
+ if (!isfield (data, "onFirst"))
+ data.onFirst = struct ();
+ endif
+
+ % Load initial state. During the entire run, we keep track of fseek
+ % positions for the state *before* each step's begin. This is used
+ % to later quickly jump to it. For the first step, this is now.
+ filePos = ftell (fd);
+ data.s = so_load_compressed (fd, "state", data);
+
+ % Call "initialised" handler and save log stuff.
+ data.log.s0 = data.s;
+ if (isfield (data.onFirst, "initialised"))
+ data.log = data.onFirst.initialised (data);
+ endif
+ if (isfield (data.handler, "initialised"))
+ data.log = data.handler.initialised (data);
+ endif
+ data.log.costs = [data.s.cost];
+ data.log.steps = 0;
+
+ % Start the shell loop.
+ k = 0;
+ target = 1;
+ lastN = 1;
+ s = data.s;
+ while (true)
+ if (ferror (fd))
+ error ("error reading descent log");
+ endif
+
+ % Read in sequentially until we reach the target. Seeking to the
+ % correct previous position as well as reading the previous state
+ % has already been performed -- either above for the initial
+ % step or at the end of the loop after a navigation command.
+ assert (k < target);
+ while (k < target)
+ try
+ f = fload (fd);
+ catch
+ if (feof (fd))
+ break;
+ endif
+ error ("error reading descent log");
+ end_try_catch
+
+ % Now update data.s. This ensures that data.s is always
+ % set to the correct value (i. e., the old state) when calling
+ % any of the onFirst or handler handlers. The update should
+ % be done after the EOF check above and before overwriting s with
+ % the newly loaded state below.
+ data.s = s;
+
+ dJ = fload (fd);
+ step = fload (fd);
+ pos = ftell (fd);
+ s = so_load_compressed (fd, "state", data);
+ if (ferror (fd))
+ error ("error reading descent log");
+ endif
+
+ ++k;
+ assert (k <= length (filePos));
+ if (k == length (filePos))
+ assert (data.log.steps == k - 1);
+ data.log.costs(k + 1) = s.cost;
+ data.log.steps = k;
+ assert (data.log.steps == length (filePos));
+ assert (data.log.steps + 1 == length (data.log.costs));
+ filePos(k + 1) = pos;
+
+ if (isfield (data.onFirst, "before_step"))
+ data.log = data.onFirst.before_step (k, data);
+ endif
+ if (isfield (data.onFirst, "direction"))
+ data.log = data.onFirst.direction (k, f, dJ, data);
+ endif
+ if (isfield (data.onFirst, "after_step"))
+ data.log = data.onFirst.after_step (k, step, s, data);
+ endif
+ else
+ assert (filePos(k + 1) == pos);
+ endif
+ endwhile
+
+ % Handle EOF if we have it.
+ assert (k <= target);
+ if (k < target)
+ assert (feof (fd));
+ if (k == 0)
+ error ("no data in descent log");
+ else
+ fprintf (stderr (), "no more steps present in the descent log\n");
+ endif
+ endif
+
+ % Call the actual handlers.
+ if (isfield (data.handler, "before_step"))
+ data.log = data.handler.before_step (k, data);
+ endif
+ if (isfield (data.handler, "direction"))
+ data.log = data.handler.direction (k, f, dJ, data);
+ endif
+ if (isfield (data.handler, "after_step"))
+ data.log = data.handler.after_step (k, step, s, data);
+ endif
+
+ % Prompt for user input.
+ fullExit = false;
+ do
+ try
+ cmd = input (sprintf ("@ step %d> ", k), "s");
+ catch
+ cmd = "exit";
+ end_try_catch
+ if (strcmp (cmd, "exit") || strcmp (cmd, "quit"))
+ fullExit = true;
+ break;
+ endif
+
+ haveValidN = true;
+ relative = true;
+ if (strcmp (cmd, ""))
+ n = lastN;
+ elseif (strcmp (cmd, "start"))
+ n = -k;
+ elseif (strcmp (cmd, "end"))
+ n = Inf;
+ elseif (strcmp (cmd, "help"))
+ fprintf (stderr (), "\nCommands for descent log explorer:\n\n");
+ fprintf (stderr (), "exit Close the explorer session.\n");
+ fprintf (stderr (), "quit Close the explorer session.\n");
+ fprintf (stderr (), "help Display this help message.\n");
+ fprintf (stderr (), "kb Open Octave shell with 'data' set.\n");
+ fprintf (stderr (), "start Go to the very first iteration.\n");
+ fprintf (stderr (), "end Go to the last iteration.\n");
+ fprintf (stderr (), "go N Go to iteration N.\n");
+ fprintf (stderr (), "N Go N steps forward.\n");
+ fprintf (stderr (), "-N Go N steps backward.\n");
+ fprintf (stderr (), "\nIf no command is given, the last navigation");
+ fprintf (stderr (), " command is repeated.\n");
+ haveValidN = false;
+ elseif (strcmp (cmd, "kb"))
+ openKbShell (data);
+ haveValidN = false;
+ else
+ if (length (cmd) >= 4 && strcmp (substr (cmd, 1, 3), "go "))
+ relative = false;
+ cmd = substr (cmd, 4);
+ endif
+
+ n = str2double (cmd);
+ if (isnan (n) || n != fix (n))
+ fprintf (stderr (), "invalid command given\n");
+ haveValidN = false;
+ elseif (relative)
+ lastN = n;
+ endif
+ endif
+ until (haveValidN);
+ if (fullExit)
+ break;
+ endif
+
+ % Find target step number and fix out-of-bounds backward steps.
+ assert (haveValidN);
+ if (relative)
+ target = k + n;
+ else
+ target = n;
+ endif
+ if (target < 1)
+ fprintf (stderr (), "stepping to beginning\n");
+ target = 1;
+ endif
+
+ % If we have to go back, seek to the correct position.
+ if (target <= k)
+ assert (length (filePos) >= target);
+ fseek (fd, filePos(target));
+ k = target - 1;
+ s = fload (fd);
+ endif
+ endwhile
+
+ % Close the file now.
+ if (openFile)
+ fclose (fd);
+ endif
+
+ % Call the finished handlers.
+ if (isfield (data.onFirst, "finished"))
+ data.log = data.onFirst.finished (data);
+ endif
+ if (isfield (data.handler, "finished"))
+ data.log = data.handler.finished (data);
+ endif
+endfunction
+
+% Helper function for the "kb" command. It creates a new scope
+% where the user can safely play around with data without
+% messing up anything else.
+function openKbShell (data)
+ fprintf (stderr (), "You can now interactively explore the current data.\n");
+ fprintf (stderr (), "Use the variable 'data' to access all information.\n\n");
+ fprintf (stderr (), "Type 'return' to go back to the explore prompt.\n\n");
+ keyboard ("kb> ");
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for errors.
+%!error <Invalid call to>
+%! so_explore_descent (1);
+%!error <Invalid call to>
+%! so_explore_descent (1, 2, 3);
+%!error <FILE must be a string or file ID>
+%! so_explore_descent (struct (), 2);
+%!error <FILE must be a string or file ID>
+%! so_explore_descent ([2, 3], 2);
+%!error <'fload' is not available>
+%! pkg unload parallel;
+%! so_explore_descent ("foo", 2);
+
+% No functional tests, since they require user interaction.
+% Use the demo instead for basic testing.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%! pkg load parallel;
+%!
+%! data = struct ();
+%! data.p = so_init_params (false);
+%! data.p.vol = 10;
+%! data.p.weight = 50;
+%!
+%! data.p.nSteps = 10;
+%! data.figs = struct ();
+%! data.figs.speed = figure ();
+%! data.figs.exploreCosts = figure ();
+%!
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! data.g = struct ("x", x, "h", h);
+%!
+%! data = so_example_problem (data);
+%! phi0 = ls_genbasic (data.g.x, "box", -3, 7);
+%!
+%! printf ("Computing descent...\n");
+%! f = tmpfile ();
+%! d = data;
+%! d.handler = struct ();
+%! d = so_save_descent (f, struct (), d);
+%! s = so_run_descent (data.p.nSteps, phi0, d);
+%! printf ("Final cost: %.6d\n", s.cost);
+%!
+%! printf ("\nNow replaying...\n");
+%! init = @() data;
+%! frewind (f);
+%! so_explore_descent (f, init);
+%! fclose (f);
diff --git a/inst/so_init_params.m b/inst/so_init_params.m
new file mode 100644
index 0000000..257a6a2
--- /dev/null
+++ b/inst/so_init_params.m
@@ -0,0 +1,82 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{p} =} so_init_params (@var{verbose}, @var{nProc} = 1)
+##
+## Initialise the parameter structure for the shape optimisation routines.
+## Choose default parameters, and set whether or not ``verbose'' log chatter
+## should be printed.
+##
+## @var{nProc} specifies the number of processes to use for some
+## steps that can be done in parallel (like the line search with
+## @code{so_step_armijo}). If this number is larger than one,
+## the function @code{pararrayfun} from the @code{parallel} package
+## must be available.
+##
+## The result of this function call can be used as a basis for defining
+## @code{@var{data}.p}. Values can be overwritten after the call where desired.
+##
+## @seealso{so_run_descent, so_step_armijo, pararrayfun}
+## @end deftypefn
+
+function p = so_init_params (verbose, nProc = 1)
+ if (nargin () < 1 || nargin () > 2)
+ print_usage ();
+ endif
+
+ if (nProc < 1 || nProc != round (nProc))
+ error ("invalid number of processors");
+ endif
+ hasParallel = exist ("pararrayfun");
+ if (nProc > 1 && hasParallel != 2 && hasParallel != 3)
+ error ("multi-threading requested but 'pararrayfun' is not available");
+ endif
+
+ p = struct ("verbose", verbose, "nProc", nProc);
+
+ p.lineSearch = struct ("relaxation", 0.1, "backtrack", 0.8, ...
+ "initial", 2, "minStep", 1e-6);
+
+ p.descent = struct ("initialStep", 1);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! so_init_params ()
+%!error <Invalid call to>
+%! so_init_params (true, 1, 2)
+%!error <invalid number of processors>
+%! so_init_params (true, 0);
+%!error <invalid number of processors>
+%! so_init_params (true, 1.5);
+
+% Check the test for loaded pararrayfun.
+%!test
+%! p = so_init_params (true);
+%!error <'pararrayfun' is not available>
+%! pkg unload parallel;
+%! p = so_init_params (true, 2);
+%!test
+%! pkg load parallel;
+%! p = so_init_params (true, 2);
+
+% Since so_init_params is used in the tests of the other
+% routines, they already verify that the parameters
+% are accepted by them.
diff --git a/inst/so_replay_descent.m b/inst/so_replay_descent.m
new file mode 100644
index 0000000..c10b5ac
--- /dev/null
+++ b/inst/so_replay_descent.m
@@ -0,0 +1,479 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {[@var{s}, @var{log}] =} so_replay_descent (@var{file}, @var{init})
+## @deftypefnx {Function File} {[@var{s}, @var{log}] =} so_replay_descent (@var{file}, @var{init}, @var{nSteps})
+##
+## Replay a descent file saved by @code{so_save_descent}. @var{file} should
+## be either an already open file descriptor or a file name to be opened
+## in @qcode{"rb"} mode.
+##
+## @var{init} must be a function that can be called with the ``header''
+## saved in the descent log. It should return the @var{data} structure
+## to use for the descent run. The handlers defined in
+## @code{@var{data}.handler} will be called in the same way as they
+## would by @code{so_run_descent}. Also similar log chatter will be printed
+## if @code{@var{data}.p.verbose} is @code{true}.
+##
+## If the state structs were compressed when saving with @code{so_save_descent},
+## a routine should be provided to ``uncompress'' them as necessary. This
+## function should be defined in @code{@var{data}.compress.load.state}
+## and should return the uncompressed state struct when called
+## with the arguments @code{(@var{s}, @var{data})} where @var{s}
+## is the compressed struct.
+##
+## If @var{nSteps} is not given, the file is read until EOF is encountered.
+## If @var{nSteps} is present, the descent will be run until either
+## this number is reached or @code{@var{data}.cb.check_stop} returns true.
+## If the descent log does not contain enough data for this condition to be
+## fulfilled, more steps will be performed according to @code{so_run_descent}.
+## In this case, the callbacks in @code{@var{data}.cb} must be set in the
+## same way as for @code{so_run_descent}.
+##
+## Returned are the final state and the produced log structure,
+## in the same way as by @code{so_run_descent}.
+##
+## This routine requires @code{fload} from the @code{parallel} package
+## to be available.
+##
+## @seealso{so_explore_descent, so_run_descent, so_save_descent, fload}
+## @end deftypefn
+
+function [s, descentLog] = so_replay_descent (file, init, nSteps)
+ if (nargin () < 2 || nargin () > 3)
+ print_usage ();
+ endif
+
+ if (nargin () == 3)
+ forceSteps = true;
+ if (!isscalar (nSteps) || nSteps != round (nSteps) || nSteps < 1)
+ print_usage ();
+ endif
+ else
+ forceSteps = false;
+ endif
+
+ openFile = ischar (file);
+ if (!openFile && (!isnumeric (file) || !isscalar (file)))
+ error ("FILE must be a string or file ID");
+ endif
+
+ hasFload = exist ("fload");
+ if (hasFload != 2 && hasFload != 3)
+ error ("'fload' is not available");
+ endif
+
+ % Open file and read header.
+ if (openFile)
+ fd = fopen (file, "rb");
+ if (fd == -1)
+ error ("failed to open file '%s' for reading the descent log", file);
+ endif
+ else
+ fd = file;
+ endif
+ header = fload (fd);
+
+ % Call init method and load initial state.
+ data = init (header);
+ data.s = so_load_compressed (fd, "state", data);
+
+ % Add some (possibly) missing stuff with defaults.
+ if (!isfield (data, "log"))
+ data.log = struct ();
+ endif
+ if (!isfield (data, "handler"))
+ data.handler = struct ();
+ endif
+ if (!isfield (data, "cb"))
+ data.cb = struct ();
+ endif
+ if (!isfield (data.cb, "check_stop"))
+ data.cb.check_stop = @() false;
+ endif
+
+ % Call "initialised" handler and save initial state struct.
+ data.log.s0 = data.s;
+ if (isfield (data.handler, "initialised"))
+ data.log = data.handler.initialised (data);
+ endif
+
+ % Do the iteration.
+ costs = [data.s.cost];
+ k = 0;
+ while (true)
+ if (ferror (fd))
+ error ("error reading descent log");
+ endif
+
+ % Honour stopping condition if nSteps is given.
+ if (forceSteps && (k >= nSteps || data.cb.check_stop (data.s)))
+ break;
+ endif
+
+ % Read next data and check for EOF.
+ try
+ f = fload (fd);
+ catch
+ if (feof (fd))
+ break;
+ endif
+ error ("error reading descent log");
+ end_try_catch
+ dJ = fload (fd);
+ step = fload (fd);
+ newS = so_load_compressed (fd, "state", data);
+ if (ferror (fd))
+ error ("error reading descent log");
+ endif
+
+ % Call handlers.
+ ++k;
+ if (data.p.verbose)
+ printf ("\nDescent iteration %d...\n", k);
+ printf ("Starting cost: %.6f\n", data.s.cost);
+ printf ("Directional derivative: %.6f\n", dJ);
+ printf ("Armijo step %.6f: cost = %.6f\n", step, newS.cost);
+ endif
+ if (isfield (data.handler, "before_step"))
+ data.log = data.handler.before_step (k, data);
+ endif
+ if (isfield (data.handler, "direction"))
+ data.log = data.handler.direction (k, f, dJ, data);
+ endif
+ if (isfield (data.handler, "after_step"))
+ data.log = data.handler.after_step (k, step, newS, data);
+ endif
+
+ % Update cost tracking and switch to new state.
+ costs(k + 1) = newS.cost;
+ data.s = newS;
+ endwhile
+
+ % Make sure to close the file now. Record EOF condition
+ % before we do so.
+ haveEOF = feof (fd);
+ if (openFile)
+ fclose (fd);
+ endif
+
+ % If we have an EOF condition, see if we need more steps.
+ if (haveEOF && forceSteps)
+ assert (k < nSteps && !data.cb.check_stop (data.s));
+
+ if (k == 0)
+ error ("no data in descent log");
+ endif
+
+ % Call through to so_run_descent, in a special "continuation"
+ % mode (defined internally for this purpose).
+ data._descentContinuation = struct ("k", k, "step", step, "costs", costs);
+ [s, descentLog] = so_run_descent (nSteps, [], data);
+ return;
+ endif
+
+ % Finish everything.
+ data.log.steps = k;
+ assert (length (costs) == k + 1);
+ data.log.costs = costs;
+ if (isfield (data.handler, "finished"))
+ data.log = data.handler.finished (data);
+ endif
+
+ % Fill return values.
+ s = data.s;
+ descentLog = data.log;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for errors.
+%!error <Invalid call to>
+%! so_replay_descent (1);
+%!error <Invalid call to>
+%! so_replay_descent (1, 2, 3, 4);
+%!error <Invalid call to>
+%! so_replay_descent (1, 2, 0);
+%!error <Invalid call to>
+%! so_replay_descent (1, 2, 1.5);
+%!error <Invalid call to>
+%! so_replay_descent (1, 2, [2, 3]);
+%!error <FILE must be a string or file ID>
+%! so_replay_descent (struct (), 2);
+%!error <FILE must be a string or file ID>
+%! so_replay_descent ([2, 3], 2);
+%!error <'fload' is not available>
+%! pkg unload parallel;
+%! so_replay_descent ("foo", 2);
+
+% Define compression / uncompression functions. They do not really
+% "compress", but instead manipulate the data in ways such that it
+% is later possible to check that they have been called correctly.
+%
+%!function x = compressionCheckStruct (s, data)
+%! x = struct ("weight", data.p.weight, "cost", s.cost);
+%!endfunction
+%
+%!function checkCompression (s, data, field)
+%! assert (isfield (s, field));
+%! assert (getfield (s, field), compressionCheckStruct (s, data));
+%!endfunction
+%
+%!function s = compressState (s, data)
+%! s.compressed = compressionCheckStruct (s, data);
+%!endfunction
+%
+%!function s = uncompressState (s, data)
+%! checkCompression (s, data, "compressed");
+%! s.uncompressed = compressionCheckStruct (s, data);
+%!endfunction
+
+% Sample handlers that just build up "some" array from all the data
+% they are passed. This is used to compare replayed and computed runs.
+%
+%!function log = initialised (data)
+%! log = data.log;
+%! log.mashUp = [data.s.a, data.s.b];
+%!endfunction
+%
+%!function log = beforeStep (k, data)
+%! log = data.log;
+%! log.mashUp = [log.mashUp, k, data.s.vol];
+%!
+%! if (data.checkUncompression)
+%! checkCompression (data.s, data, "uncompressed");
+%! endif
+%!endfunction
+%
+%!function log = direction (k, f, dJ, data)
+%! log = data.log;
+%! log.mashUp = [log.mashUp, k, mean(f), sum(f), dJ, data.s.integ];
+%!endfunction
+%
+%!function log = afterStep (k, t, s, data)
+%! log = data.log;
+%! log.mashUp = [log.mashUp, k, t, s.a, s.b, s.cost, data.s.cost];
+%!
+%! if (data.checkUncompression)
+%! checkCompression (s, data, "uncompressed");
+%! endif
+%!endfunction
+%
+%!function log = finished (data)
+%! log = data.log;
+%! log.mashUp = [log.mashUp, data.s.cost, log.steps, log.costs];
+%!endfunction
+
+% Define a function that initialises the problem data.
+%!function data = getData ()
+%! data = struct ();
+%! data.p = so_init_params (false);
+%! data.p.vol = 10;
+%! data.p.weight = 50;
+%!
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! data.g = struct ("x", x, "h", h);
+%!
+%! data = so_example_problem (data);
+%! data.handler = struct ("initialised", @initialised, ...
+%! "before_step", @beforeStep, ...
+%! "direction", @direction, ...
+%! "after_step", @afterStep, ...
+%! "finished", @finished);
+%!
+%! % Unless explicitly set, do not expect compressed states.
+%! data.checkUncompression = false;
+%!
+%! data.phi0 = ls_genbasic (data.g.x, "box", -3, 5);
+%!endfunction
+
+% Get data and enable compression.
+%!function data = getDataForCompression (uncompress)
+%! data = getData ();
+%! data.compress = struct ();
+%! data.compress.save = struct ("state", @compressState);
+%! data.compress.load = struct ("state", @uncompressState);
+%! data.checkUncompression = uncompress;
+%!endfunction
+
+% Compare results (s, log) two runs and check if the mash-ups match.
+% We cannot fully compare the log structs, though, since they contain
+% private data used for saving.
+%!function compareResults (s1, l1, s2, l2)
+%! assert (s2, s1);
+%! assert (l2.mashUp, l1.mashUp);
+%!endfunction
+
+% Create a basic log and replay it without forcing steps. This tests
+% both saving to a file name and file descriptor.
+%!test
+%! pkg load parallel;
+%!
+%! nSteps = 10;
+%! baseData = getData ();
+%!
+%! f = tempname ();
+%! data = so_save_descent (f, struct (), baseData);
+%! [s1, l1] = so_run_descent (nSteps, data.phi0, data);
+%! [s2, l2] = so_replay_descent (f, @getData);
+%! unlink (f);
+%! compareResults (s1, l1, s2, l2);
+%!
+%! f = tmpfile ();
+%! data = so_save_descent (f, struct (), baseData);
+%! [s1, l1] = so_run_descent (nSteps, data.phi0, data);
+%! frewind (f);
+%! [s2, l2] = so_replay_descent (f, @getData);
+%! fclose (f);
+%! compareResults (s1, l1, s2, l2);
+%!
+%! [pr, pw] = pipe ();
+%! data = so_save_descent (pw, struct (), baseData);
+%! [s1, l1] = so_run_descent (nSteps, data.phi0, data);
+%! fclose (pw);
+%! [s2, l2] = so_replay_descent (pr, @getData);
+%! fclose (pr);
+%! compareResults (s1, l1, s2, l2);
+
+% Check for "no data" error if we need to compute more steps
+% but *none* are saved. Also check that it *is* ok to have
+% zero steps if the stopping criterion stops immediately.
+%
+%!test
+%! pkg load parallel;
+%!
+%! baseData = getData ();
+%! [pr, pw] = pipe ();
+%! data = so_save_descent (pw, struct (), baseData);
+%! data.cb.check_stop = @() true;
+%! [s1, l1] = so_run_descent (1, data.phi0, data);
+%! fclose (pw);
+%! assert (l1.steps, 0);
+%! [s2, l2] = so_replay_descent (pr, @getData);
+%! fclose (pr);
+%! compareResults (s1, l1, s2, l2);
+%!
+%! baseData = getData ();
+%! [pr, pw] = pipe ();
+%! data = so_save_descent (pw, struct (), baseData);
+%! [s1, l1] = so_run_descent (1, data.phi0, data);
+%! fclose (pw);
+%! [s2, l2] = so_replay_descent (pr, @getData, 1);
+%! fclose (pr);
+%! compareResults (s1, l1, s2, l2);
+%
+%!error <no data in descent log>
+%! pkg load parallel;
+%!
+%! baseData = getData ();
+%! [pr, pw] = pipe ();
+%! data = so_save_descent (pw, struct (), baseData);
+%! data.cb.check_stop = @() true;
+%! [s1, l1] = so_run_descent (1, data.phi0, data);
+%! fclose (pw);
+%! [s2, l2] = so_replay_descent (pr, @getData, 1);
+%! fclose (pr);
+%! compareResults (s1, l1, s2, l2);
+
+% Check forcing the steps to be less or to require computation.
+%!test
+%! pkg load parallel;
+%!
+%! nStepsShort = 5;
+%! nStepsLong = 10;
+%!
+%! baseData = getData ();
+%! [prl, pwl] = pipe ();
+%! data = so_save_descent (pwl, struct (), baseData);
+%! [sl, ll] = so_run_descent (nStepsLong, data.phi0, data);
+%! fclose (pwl);
+%!
+%! baseData = getData ();
+%! [prs, pws] = pipe ();
+%! data = so_save_descent (pws, struct (), baseData);
+%! [ss, ls] = so_run_descent (nStepsShort, data.phi0, data);
+%! fclose (pws);
+%!
+%! [s, l] = so_replay_descent (prl, @getData, nStepsShort);
+%! fclose (prl);
+%! compareResults (s, l, ss, ls);
+%!
+%! [s, l] = so_replay_descent (prs, @getData, nStepsLong);
+%! fclose (prs);
+%! compareResults (s, l, sl, ll);
+
+% Check compression / uncompression feature.
+%!test
+%! pkg load parallel;
+%!
+%! nSteps = 10;
+%! [pr, pw] = pipe ();
+%!
+%! data = getDataForCompression (false);
+%! data = so_save_descent (pw, struct (), data);
+%! [s1, l1] = so_run_descent (nSteps, data.phi0, data);
+%! fclose (pw);
+%!
+%! init = @(header) getDataForCompression (true);
+%! [s2, l2] = so_replay_descent (pr, init);
+%! fclose (pr);
+%!
+%! % No result comparison here, since the compress / uncompress
+%! % functions introduced changes to the state structs.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+% This is the "same" demo as for so_run_descent. The difference is just
+% that the descent is first saved and only later replayed to produce
+% the actual plots.
+%
+%!demo
+%! pkg load parallel;
+%!
+%! data = struct ();
+%! data.p = so_init_params (false);
+%! data.p.vol = 10;
+%! data.p.weight = 50;
+%!
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! data.g = struct ("x", x, "h", h);
+%!
+%! data = so_example_problem (data);
+%! phi0 = ls_genbasic (data.g.x, "box", -3, 7);
+%!
+%! printf ("Computing descent...\n");
+%! [pr, pw] = pipe ();
+%! d = data;
+%! d.handler = struct ();
+%! d = so_save_descent (pw, struct (), d);
+%! s = so_run_descent (5, phi0, d);
+%! fclose (pw);
+%! printf ("Final cost: %.6d\n", s.cost);
+%!
+%! printf ("\nNow replaying...\n");
+%! d = data;
+%! d.p.verbose = true;
+%! init = @() d;
+%! s = so_replay_descent (pr, init);
+%! fclose (pr);
+%! printf ("Final cost: %.6d\n", s.cost);
diff --git a/inst/so_run_descent.m b/inst/so_run_descent.m
new file mode 100644
index 0000000..cc5ebe0
--- /dev/null
+++ b/inst/so_run_descent.m
@@ -0,0 +1,458 @@
+## Copyright (C) 2013-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {[@var{s}, @var{log}] =} so_run_descent (@var{nSteps}, @var{phi0}, @var{data})
+##
+## Run a descent method for shape optimisation. This routine is quite
+## general, since the actual ``descent direction'' used must be computed
+## by a callback (see below). It is thus not just for steepest descent.
+## At each step, a line search according to @code{so_step_armijo} is employed.
+##
+## @var{nSteps} defines the maximal number of descent steps to take. After
+## this number, the method returns no matter whether or not the stopping
+## criterion is fulfilled. @var{phi0} is the initial geometry to use.
+## Returned are the final state and the descent log structs (see below).
+##
+## @var{data} contains all the information about the actual problem.
+## This includes various parameters that influence the behaviour of this
+## and other routines. Furthermore, also some callback functions must
+## be defined to evaluate the cost functional and to determine the
+## next descent direction. This struct is also updated with the current
+## state at each optimisation step and passed to the callback functions.
+##
+## Most content of @var{data} is read-only, with @code{@var{data}.s} and
+## @code{@var{data}.log} being the only exceptions. They are updated
+## accordingly from the results of the callback functions and the
+## optimisation descent. These two structs will be created and filled in
+## by @code{so_run_descent}. @code{@var{data}.log} is preserved if it is
+## already present. @code{@var{data}.s} will always be overwritten
+## with the initial result of the @code{update_state} callback.
+##
+## The struct @code{@var{data}.p} contains parameters. They can contain
+## problem-dependent parameters that are handled by the callback routines
+## as well as parameters for the line search with @code{so_step_armijo}.
+## This routine is influenced by the following parameters directly:
+##
+## @table @code
+## @item verbose
+## Whether or not log chatter should be printed.
+##
+## @item descent.initialStep
+## Initial trial step length for the line search in the first iteration.
+##
+## @item descent.projectSpeed
+## If set, project the speed field (in L2, discontinuously) to enforce
+## geometric constraints (see below). If not set or missing, the
+## geometry is projected instead.
+## @end table
+##
+## The routine @code{so_init_params} can be used to initialise the parameter
+## structure with some defaults.
+##
+## Information about the used grid and grid-dependent data should be in
+## @code{@var{data}.g}. Generally defined are the following fields:
+##
+## @table @code
+## @item h
+## The grid spacing.
+##
+## @item constraints
+## Optional, can be used to specify the hold-all domain and contained
+## regions as shape constraints. If specified, the shape or the speed
+## field will be ``projected'' (depending on
+## @code{@var{data}.p.descent.projectSpeed}) to satisfy the constraints.
+## The callback computing the descent direction is also a good place
+## to take the constraints into account in a problem-specific way.
+##
+## The following fields (as logical arrays with the grid size) can
+## be used to specify the constraints:
+##
+## @table @code
+## @item holdall
+## The hold-all domain. The current shape must always be part of it.
+## By excluding interior parts of the grid, forbidden regions can be defined.
+##
+## @item contained
+## A part of the hold-all domain that should always be part of the shape.
+## @end table
+## @end table
+##
+## The specific problem is handled by @emph{callback routines} to compute
+## the cost and find descent directions. They must be set in
+## @code{@var{data}.cb}, with these interfaces:
+##
+## @table @code
+## @item @var{s} = update_state (@var{phi}, @var{data})
+## Compute the state (see below) for the geometry described by @var{phi}.
+## The main work done by this callback is evaluating the cost functional.
+## It does not need to update @code{@var{s}.phi} to @var{phi}, this will
+## be done automatically after the call.
+##
+## @item [@var{f}, @var{dJ}] = get_direction (@var{data})
+## Compute the speed field @var{f} and its associated directional
+## shape derivative @var{dJ} to be used as the next descent direction.
+## The current cost and geometry can be extracted from @code{@var{data}.s}.
+##
+## @item @var{exit} = check_stop (@var{data})
+## Determine whether or not to stop the iteration. Should return
+## @code{true} if the state in @var{data} satisfies the problem-specific
+## stopping criterion. If not present, no stopping criterion
+## is used and the iteration done for precisely @var{nSteps} iterations.
+##
+## @item @var{d} = solve_stationary (@var{f}, @var{data})
+## Solve the stationary Eikonal equation for the speed field.
+## If not present, @code{ls_solve_stationary} is used. It can be
+## set explicitly to give a more appropriate routine. For instance,
+## @code{ls_nb_from_geom} could be used if geometry information is computed.
+## @end table
+##
+## Additional callback functions can be defined optionally
+## in @code{@var{data}.handler}. They can keep problem-specific
+## logs of the descent, plot figures, print output or something else.
+## They should always return a (possibly) updated log struct, which
+## will be updated in @code{@var{data}.log} after the call.
+## This struct can also be used to persist information between
+## calls to the handlers. These handlers can be defined:
+##
+## @table @code
+## @item @var{log} = initialised (@var{data})
+## Called before the iteration is started. @code{@var{data}.s}
+## contains the initial geometry and computed state data for it.
+##
+## @item @var{log} = before_step (@var{k}, @var{data})
+## Called when starting a new step, before any action for the
+## step is taken. @var{k} is the number of the iteration and
+## @code{@var{data}.s} contains the current state.
+##
+## @item @var{log} = direction (@var{k}, @var{f}, @var{dJ}, @var{data})
+## Called when the descent direction has been determined.
+## @var{f} and @var{dJ} are the results of @code{@var{data}.cb.get_direction}.
+##
+## @item @var{log} = after_step (@var{k}, @var{t}, @var{s}, @var{data})
+## Called after a step has been found. @var{t} is the step length
+## found by the line search. @var{s} is the state struct after the step
+## has been taken. The previous state can still be found in
+## @code{@var{data}.s}.
+##
+## @item @var{log} = finished (@var{data})
+## Called after the descent run. @code{@var{data}.log} is already filled
+## in with the standard data (number of steps and costs at each step).
+## @end table
+##
+## Note that information about the speed field @var{f} is only
+## passed to the @code{direction} handler and @emph{not again}
+## to @code{after_step}. If it is required by the latter,
+## it should be saved somehow in the log struct. This way, the handlers
+## really only receive new information and no redundant arguments.
+##
+## Finally, we come to the state struct @code{@var{data}.s}. It must
+## contain at least the fields below, but can also contain problem-specific
+## information that is, for instance, computed in the @code{update_state}
+## handler and reused for computing the descent direction.
+##
+## @table @code
+## @item phi
+## The level-set function of the current geometry. This is automatically
+## updated after the @code{update_state} handler has been called.
+##
+## @item cost
+## Value of the cost functional for the geometry in @code{@var{data}.s.phi}.
+## @end table
+##
+## The log struct can contain mostly user-defined data, but
+## @code{so_run_descent} will also fill in some data by itself:
+##
+## @table @code
+## @item s0
+## The state struct corresponding to the initial geometry defined
+## by @var{phi0}.
+##
+## @item steps
+## Total number of steps done. This is useful if the stopping criterion
+## was satisfied before @var{nSteps} iterations.
+##
+## @item costs
+## Cost values for all steps. This will be a vector of length
+## @code{@var{data}.log.steps + 1}, since it contains both the initial
+## and the final cost value in addition to all intermediate ones.
+## @end table
+##
+## @seealso{so_init_params, so_save_descent, so_replay_descent,
+## so_explore_descent, so_step_armijo}
+## @end deftypefn
+
+function [s, descentLog] = so_run_descent (nSteps, phi0, data)
+ if (nargin () != 3)
+ print_usage ();
+ endif
+
+ if (!isscalar (nSteps) || nSteps != round (nSteps) || nSteps < 1)
+ print_usage ();
+ endif
+
+ if (data.p.descent.initialStep <= 0)
+ error ("invalid initial step length");
+ endif
+
+ % Construct empty structs for optional fields. This simplifies the
+ % process later on.
+ if (!isfield (data.g, "constraints"))
+ data.g.constraints = struct ();
+ endif
+ if (!isfield (data, "handler"))
+ data.handler = struct ();
+ endif
+
+ % Augment the update_state and get_direction callbacks.
+ data._descent = struct ();
+ data._descent.userUpdateState = data.cb.update_state;
+ data._descent.userGetDirection = data.cb.get_direction;
+ data.cb.update_state = @augmentedUpdateState;
+ data.cb.get_direction = @augmentedGetDirection;
+
+ % Create 'null' check_stop callback if none is present.
+ if (!isfield (data.cb, "check_stop"))
+ data.cb.check_stop = @() false;
+ endif
+
+ % Create default 'solve_stationary' callback.
+ if (!isfield (data.cb, "solve_stationary"))
+ data.cb.solve_stationary ...
+ = @(f, data) ls_solve_stationary (data.s.phi, f, data.g.h);
+ endif
+
+ % Do some initialisations, but only if we're not in "continuation" mode.
+ % In continuation mode, the "initialised" handler is already called
+ % and the data to use is explicitly given by the caller.
+ if (!isfield (data, "_descentContinuation"))
+ data.s = data.cb.update_state (phi0, data);
+ data.s.phi = phi0;
+ if (!isfield (data, "log"))
+ data.log = struct ();
+ endif
+
+ lastStep = data.p.descent.initialStep;
+ costs = [data.s.cost];
+ k = 0;
+
+ data.log.s0 = data.s;
+ if (isfield (data.handler, "initialised"))
+ data.log = data.handler.initialised (data);
+ endif
+ else
+ lastStep = data._descentContinuation.step;
+ costs = data._descentContinuation.costs;
+ k = data._descentContinuation.k;
+ endif
+
+ % Do the descent iteration.
+ while (k < nSteps && !data.cb.check_stop (data.s))
+ ++k;
+ if (data.p.verbose)
+ printf ("\nDescent iteration %d...\n", k);
+ printf ("Starting cost: %.6f\n", data.s.cost);
+ ticId = tic ();
+ endif
+
+ % Call "before_step" handler if present.
+ if (isfield (data.handler, "before_step"))
+ data.log = data.handler.before_step (k, data);
+ endif
+
+ % Check the constraints. Since we enforce them, they
+ % should always be satisfied.
+ if (isfield (data.g.constraints, "holdall"))
+ assert (ls_check (data.s.phi, "inside", data.g.constraints.holdall));
+ endif
+ if (isfield (data.g.constraints, "contained"))
+ assert (ls_check (data.s.phi, "contain", data.g.constraints.contained));
+ endif
+
+ % Get the descent direction.
+ [f, dJ] = data.cb.get_direction (data);
+ if (dJ >= 0)
+ error ("no descent direction found");
+ endif
+
+ % Print log and call "direction" handler if present.
+ if (data.p.verbose)
+ printf ("Directional derivative: %.6f\n", dJ);
+ endif
+ if (isfield (data.handler, "direction"))
+ data.log = data.handler.direction (k, f, dJ, data);
+ endif
+
+ % Calculate level-set stationary distances and perform Armijo step.
+ dists = data.cb.solve_stationary (f, data);
+ [newS, lastStep] = so_step_armijo (lastStep, dists, f, dJ, data);
+
+ % Call "after_step" handler.
+ if (isfield (data.handler, "after_step"))
+ data.log = data.handler.after_step (k, lastStep, newS, data);
+ endif
+
+ % Update cost tracking and switch over to new state.
+ costs(k + 1) = newS.cost;
+ data.s = newS;
+
+ % Finish timing for verbose output.
+ if (data.p.verbose)
+ toc (ticId);
+ endif
+ endwhile
+
+ % Fill in the final results.
+ data.log.steps = k;
+ assert (length (costs) == k + 1);
+ data.log.costs = costs;
+
+ % Call "finished" handler as the very last action.
+ if (isfield (data.handler, "finished"))
+ data.log = data.handler.finished (data);
+ endif
+
+ % Fill return values.
+ s = data.s;
+ descentLog = data.log;
+endfunction
+
+% Function that augments the 'update_state' handler with general stuff
+% that it should do. In particular, we enforce the constraints
+% before doing anything (so that they are definitely not violated
+% before calling the user code). Also, update data.s.phi after the call.
+function s = augmentedUpdateState (phi, data)
+ phi = ls_normalise (phi, data.g.h);
+ if (isfield (data.g.constraints, "holdall"))
+ phi = ls_enforce (phi, "inside", data.g.constraints.holdall);
+ endif
+ if (isfield (data.g.constraints, "contained"))
+ phi = ls_enforce (phi, "contain", data.g.constraints.contained);
+ endif
+
+ s = data._descent.userUpdateState (phi, data);
+ s.phi = phi;
+endfunction
+
+% Function that augments the 'get_direction' handler. It hooks the
+% speed field projection onto it when requested by the parameters.
+function [F, DJ] = augmentedGetDirection (data)
+ [F, DJ] = data._descent.userGetDirection (data);
+
+ % Note that the projection below does not take care of adapting DJ
+ % accordingly! Using this feature makes the descent method potentially
+ % "inconsistent". The same is true if the geometry is projected
+ % after a step, though.
+
+ if (isfield (data.p.descent, "projectSpeed") && data.p.descent.projectSpeed)
+ if (isfield (data.g.constraints, "holdall"))
+ F = ls_enforce_speed (F, "inside", data.g.constraints.holdall);
+ endif
+ if (isfield (data.g.constraints, "contained"))
+ F = ls_enforce_speed (F, "contain", data.g.constraints.contained);
+ endif
+ endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Construct basic example problem for testing.
+%!shared data, phi0
+%! data = struct ();
+%! data.p = so_init_params (false);
+%! data.p.vol = 10;
+%! data.p.weight = 50;
+%!
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! data.g = struct ("x", x, "h", h);
+%!
+%! data = so_example_problem (data);
+%! data.handler = struct ();
+%!
+%! phi0 = ls_genbasic (data.g.x, "box", -3, 5);
+
+% Test for errors.
+%!error <Invalid call to>
+%! so_run_descent (1, 2);
+%!error <Invalid call to>
+%! so_run_descent (1, 2, 3, 4);
+%!error <Invalid call to>
+%! so_run_descent (0, phi0, data);
+%!error <Invalid call to>
+%! so_run_descent (1.5, phi0, data);
+%!error <Invalid call to>
+%! so_run_descent ([2, 3], phi0, data);
+
+% Validate parameters.
+%!error <invalid initial step length>
+%! d = data;
+%! d.p.descent.initialStep = 0;
+%! so_run_descent (1, phi0, d);
+
+% Test optimisation of the example problem.
+%!test
+%! nSteps = 12;
+%!
+%! [s, descentLog] = so_run_descent (nSteps, phi0, data);
+%! assert (s.cost, 0, 1e-3);
+%! assert (length (descentLog.costs), nSteps + 1);
+%! assert (descentLog.costs(end), s.cost);
+%! %semilogy (descentLog.costs, "o");
+
+% Test stopping criterion.
+%!test
+%! tol = 5e-2;
+%! nSteps = 100;
+%! d = data;
+%! d.cb.check_stop = @(data) (data.cost < tol);
+%!
+%! [s, descentLog] = so_run_descent (nSteps, phi0, d);
+%! assert (s.cost < tol);
+%! printf ("Steps needed: %d\n", descentLog.steps);
+%! assert (descentLog.steps < nSteps);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+% Use so_example_problem to define a demo.
+%
+%!demo
+%! data = struct ();
+%! data.p = so_init_params (true);
+%! data.p.vol = 10;
+%! data.p.weight = 50;
+%!
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! data.g = struct ("x", x, "h", h);
+%!
+%! % Look at so_example_problem to see how the callbacks
+%! % and the plotting handler is defined.
+%! data = so_example_problem (data);
+%!
+%! phi0 = ls_genbasic (data.g.x, "box", -3, 7);
+%! [s, descentLog] = so_run_descent (5, phi0, data);
+%!
+%! figure ();
+%! semilogy (0 : descentLog.steps, descentLog.costs, "o");
+%! title ("Cost Descrease");
+%!
+%! printf ("\nFinal interval: [%.6d, %.6d]\n", s.a, s.b);
+%! printf ("Final cost: %.6d\n", s.cost);
diff --git a/inst/so_save_descent.m b/inst/so_save_descent.m
new file mode 100644
index 0000000..240ef1c
--- /dev/null
+++ b/inst/so_save_descent.m
@@ -0,0 +1,190 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{data} =} so_save_descent (@var{file}, @var{header}, @var{data})
+##
+## Update handlers to save the descent run to a file. The existing handlers
+## in @var{data} will be kept, but additional logic will be added to them
+## that saves all data computed during the descent run to a file. This
+## file can then be used with @code{so_replay_descent}.
+##
+## @var{file} can either be a file ID (integer) or file name (string).
+## In the latter case, the file will be created in @qcode{"wb"} mode
+## and closed after the descent run. @var{header} should be some data
+## (possibly a struct) that will be written as the ``file header''.
+## It can contain data that identifies the problem solved and all
+## parameter values. It must be possible to reproduce a problem's @var{data}
+## structure from this header when replaying the descent.
+##
+## The state struct can be optionally ``compressed'' before it is written,
+## in case it contains excessive amounts of data that is not necessary
+## for the replay (for instance, because it is cheap to re-compute).
+## If this feature should be used, a function must be defined that translates
+## the state struct accordingly. It should be stored in
+## @code{@var{data}.compress.save.state}, and will be called
+## with arguments @code{(@var{s}, @var{data})}. It should return
+## the state struct in the form to save it.
+##
+## This routine requires @code{fsave} from the @code{parallel} package
+## to be available. Note that the files are not guaranteed to be interoperable
+## between machines or package versions.
+##
+## @seealso{so_run_descent, so_replay_descent, so_explore_descent, fsave}
+## @end deftypefn
+
+% Format of the descent log (all variables saved with 'fsave'):
+%
+% header
+% initial state struct
+% for each step:
+% speed field f
+% dJ for f
+% step length taken
+% new state struct
+%
+% This format, in particular that the new state is the very last
+% item and the initial state struct the last part of the "header",
+% ensures that each actual step data is preceded by the "old" state.
+% Thus, if we start reading actually there, we get information for each state
+% that includes both the old and the new state.
+
+function data = so_save_descent (file, header, data)
+ if (nargin () != 3)
+ print_usage ();
+ endif
+ if (!isstruct (data))
+ error ("DATA should be a struct");
+ endif
+
+ openFile = ischar (file);
+ if (!openFile && (!isnumeric (file) || !isscalar (file)))
+ error ("FILE must be a string or file ID");
+ endif
+
+ hasFsave = exist ("fsave");
+ if (hasFsave != 2 && hasFsave != 3)
+ error ("'fsave' is not available");
+ endif
+
+ % Retain old handlers (if present).
+ if (!isfield (data, "handler"))
+ data.handler = struct ();
+ endif
+ oldHandlers = data.handler;
+
+ % Set defaults for old handlers.
+ if (!isfield (oldHandlers, "initialised"))
+ oldHandlers.initialised = @(data) data.log;
+ endif
+ if (!isfield (oldHandlers, "direction"))
+ oldHandlers.direction = @(k, f, dJ, data) data.log;
+ endif
+ if (!isfield (oldHandlers, "after_step"))
+ oldHandlers.after_step = @(k, t, s, data) data.log;
+ endif
+ if (!isfield (oldHandlers, "finished"))
+ oldHandlers.finished = @(data) data.log;
+ endif
+
+ % Set new handlers.
+ args = struct ("openFile", openFile, "file", file, ...
+ "header", header, "oldHandlers", oldHandlers);
+ data.handler.initialised = @(data) initialised (args, data);
+ data.handler.direction = @(k, f, dJ, data) direction (args, k, f, dJ, data);
+ data.handler.after_step = @(k, t, s, data) afterStep (args, k, t, s, data);
+ data.handler.finished = @(data) finished (args, data);
+endfunction
+
+% Save the given state struct, compressing it before if necessary.
+function saveStateStruct (fd, s, data)
+ if (isfield (data, "compress") && isfield (data.compress, "save")
+ && isfield (data.compress.save, "state"))
+ s = data.compress.save.state (s, data);
+ endif
+
+ fsave (fd, s);
+endfunction
+
+% Initialisation handler. This is used to open the file (if required)
+% and to write the header. It also saves the initial state struct.
+function log = initialised (args, data)
+ if (args.openFile)
+ fd = fopen (args.file, "wb");
+ if (fd == -1)
+ error ("failed to open file '%s' for saving the descent log", args.file);
+ endif
+ else
+ fd = args.file;
+ endif
+
+ fsave (fd, args.header);
+ saveStateStruct (fd, data.s, data);
+ data.log._saveDescentFd = fd;
+
+ log = args.oldHandlers.initialised (data);
+endfunction
+
+% Save direction information when known.
+function log = direction (args, k, f, dJ, data)
+ fd = data.log._saveDescentFd;
+ fsave (fd, f);
+ fsave (fd, dJ);
+ log = args.oldHandlers.direction (k, f, dJ, data);
+endfunction
+
+% Save new state and step length. The new state should be the very
+% last information saved for a step.
+function log = afterStep (args, k, t, s, data)
+ fd = data.log._saveDescentFd;
+ fsave (fd, t);
+ saveStateStruct (fd, s, data);
+ log = args.oldHandlers.after_step (k, t, s, data);
+endfunction
+
+% Finished handler, closes the file (if applicable).
+function log = finished (args, data)
+ if (args.openFile)
+ fclose (data.log._saveDescentFd);
+ endif
+ log = args.oldHandlers.finished (data);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for errors.
+%!error <Invalid call to>
+%! so_save_descent (1, 2);
+%!error <Invalid call to>
+%! so_save_descent (1, 2, 3, 4);
+%!error <DATA should be a struct>
+%! so_save_descent (1, 2, 3);
+%!error <FILE must be a string or file ID>
+%! so_save_descent (struct (), 2, struct ());
+%!error <FILE must be a string or file ID>
+%! so_save_descent ([2, 3], 2, struct ());
+%!error <'fsave' is not available>
+%! pkg unload parallel;
+%! so_save_descent ("foo", 2, struct ());
+
+% Working calls.
+%!test
+%! pkg load parallel;
+%! so_save_descent ("foo", 2, struct ());
+%! so_save_descent (5, 2, struct ());
+
+% Funtional tests and a demo are in so_replay_descent.
diff --git a/inst/so_step_armijo.m b/inst/so_step_armijo.m
new file mode 100644
index 0000000..99d51b4
--- /dev/null
+++ b/inst/so_step_armijo.m
@@ -0,0 +1,311 @@
+## Copyright (C) 2013-2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {[@var{s}, @var{t}] =} so_step_armijo (@var{t0}, @var{d}, @var{f}, @var{dJ}, @var{data})
+##
+## Perform a line search according to the Armijo rule. A backtracking
+## strategy is employed, such that the returned step length @var{t}
+## satisfies
+## @tex
+## \begin{equation*}
+## J(t) \le J(0) + t \tau \cdot dJ.
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## J(t) <= J(0) + t * tau * dJ.
+## @end example
+##
+## @end ifnottex
+##
+## @var{t0} defines the initial step length (see below). It can be set to
+## some suitable constant or to the last successful step length.
+## @var{d} should be the result of @code{ls_solve_stationary} for
+## the current geometry, and @var{f} the matching speed field on the grid.
+## @var{dJ} must be the directional shape derivative of the cost functional
+## in direction @var{f}. This value must be negative.
+##
+## Data defining the problem, the current state and parameters
+## should be passed in @var{data}. See @code{so_run_descent} for a
+## description of this struct.
+##
+## Returned is the final state struct in @var{s} and the corresponding
+## step length taken in @var{t}.
+##
+## The following parameters must be set in @code{@var{data}.p.lineSearch}
+## to determine how the Armijo line search is done:
+##
+## @table @code
+## @item relaxation
+## The parameter tau in the Armijo rule.
+##
+## @item backtrack
+## Backtracking factor. Should be less than one.
+##
+## @item initial
+## Factor for increasing @var{t0} to get the initial guess. This should
+## be larger than one in order to allow increasing of the step
+## length when this is possible.
+##
+## @item minStep
+## Minimum step length. If backtracking goes below this value, use
+## @code{minStep} and disregard the condition.
+## @end table
+##
+## Furthermore, the following general parameters in @code{@var{data}.p}
+## also influence the behaviour of the line search:
+##
+## @table @code
+## @item verbose
+## Whether or not log chatter should be printed.
+##
+## @item nProc
+## Number of parallel threads to use for the line search. If this
+## is larger than one, multiple trial steps are computed in parallel to
+## speed up the computation. For this to work, @code{pararrayfun}
+## of the @code{parallel} package must be available.
+## @end table
+##
+## The routine @code{so_init_params} can be used to initialise the parameter
+## structure with default values.
+##
+## @seealso{so_init_params, so_run_descent, ls_solve_stationary}
+## @end deftypefn
+
+function [s, t] = so_step_armijo (t0, d, f, dJ, data)
+ if (nargin () != 5)
+ print_usage ();
+ endif
+
+ if (!isscalar (t0) || !isscalar (dJ) || !isstruct (data))
+ print_usage ();
+ endif
+
+ if (dJ >= 0)
+ error ("DJ should be negative");
+ endif
+
+ p = data.p.lineSearch;
+ if (p.relaxation <= 0 || p.relaxation >= 1)
+ error ("invalid Armijo relaxation parameter");
+ endif
+ if (p.backtrack <= 0 || p.backtrack >= 1)
+ error ("invalid backtracking factor");
+ endif
+ if (p.initial <= 1)
+ error ("invalid initial increase factor");
+ endif
+ if (p.minStep < 0)
+ error ("invalid minimum step length");
+ endif
+
+ sz = size (d);
+ if (!all (sz == size (f)))
+ error ("sizes mismatch between D and F");
+ endif
+ if (!all (sz == size (data.s.phi)))
+ error ("sizes mismatch between D and PHI");
+ endif
+
+ t = t0 * p.initial;
+ while (true)
+
+ % Use parcellfun to check a couple of steps in parallel.
+ parTs = t * p.backtrack.^(0 : data.p.nProc - 1);
+ errorHandler = @(err, curT) deal (err, NA);
+ handler = @(curT) getCostForStep (curT, d, f, data);
+
+ if (length (parTs) == 1)
+ [costps, sps] = handler (parTs);
+ costps = {costps};
+ sps = {sps};
+ else
+ [costps, sps] = pararrayfun (data.p.nProc, handler, parTs, ...
+ "UniformOutput", false, ...
+ "ErrorHandler", errorHandler);
+ endif
+
+ % Process the results looking for a step that is ok.
+ for i = 1 : length (parTs)
+ if (isstruct (costps{i}))
+ error ("Subprocess error: %s", costps{i}.message);
+ endif
+ if (data.p.verbose)
+ printf ("Armijo step %.6f: cost = %.6f\n", parTs(i), costps{i});
+ endif
+
+ % Check for minimum step length. Note that it may happen
+ % that even this step is "too long", eliminating the domain.
+ % In that case, error out.
+ if (parTs(i) < p.minStep)
+ warning ("level-set:shape-optimisation:no-armijo-step", ...
+ "no step found in line search until minimum %.6f", p.minStep);
+
+ t = p.minStep;
+ [cost, s] = getCostForStep (p.minStep, d, f, data);
+ if (isinf (cost))
+ error ("minimum step length results in invalid state");
+ endif
+
+ return;
+ endif
+
+ if (costps{i} <= data.s.cost + parTs(i) * p.relaxation * dJ)
+ t = parTs(i);
+ s = sps{i};
+ return;
+ endif
+ endfor
+
+ % Decrease step length.
+ t *= p.backtrack .^ data.p.nProc;
+ endwhile
+endfunction
+
+% Helper routine to calculate costs given a step length.
+function [costp, sp] = getCostForStep (t, d, f, data)
+ phip = ls_extract_solution (t, d, data.s.phi, f);
+
+ % Do not allow steps that eliminate the domain!
+ if (ls_isempty (phip))
+ costp = Inf;
+ sp = NA;
+ return;
+ endif
+
+ sp = data.cb.update_state (phip, data);
+ costp = sp.cost;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Cost function for our example problem.
+%!function s = updateState (phi, data)
+%! inside = ls_inside (phi);
+%! vol = data.g.h * length (find (inside));
+%! cost = 1 / vol;
+%!
+%! % Throw if the volume is too large. This is used
+%! % to check the error handling code.
+%! if (vol > 15)
+%! error ("volume too large");
+%! endif
+%!
+%! s = struct ("phi", phi, "cost", cost);
+%!endfunction
+
+% Define default parameters and phi used in the test later on.
+%!shared n, phi, baseData, d, f, dJ
+%! n = 100;
+%! x = linspace (-10, 10, n);
+%! h = x(2) - x(1);
+%! a = 0;
+%! b = 5;
+%! phi = ls_genbasic (x, "box", a, b);
+%!
+%! baseData = struct ();
+%! baseData.p = so_init_params (false);
+%! baseData.g = struct ("x", x, "h", h);
+%! baseData.cb = struct ("update_state", @updateState);
+%! baseData.s = updateState (phi, baseData);
+%! baseData.s.phi = phi;
+%!
+%! f = ones (size (phi));
+%! d = ls_solve_stationary (phi, f, h);
+%!
+%! curVol = b - a;
+%! dJ = -2 / curVol^2;
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! so_step_armijo (1, 2, 3, 4)
+%!error <Invalid call to>
+%! so_step_armijo (1, 2, 3, 4, 5, 6)
+%!error <Invalid call to>
+%! so_step_armijo ([1, 1], 2, 3, 4, struct ())
+%!error <Invalid call to>
+%! so_step_armijo (1, 2, 3, [4, 4], struct ())
+%!error <Invalid call to>
+%! so_step_armijo (1, 2, 3, 4, 5)
+%!error <DJ should be negative>
+%! so_step_armijo (1, d, f, 4, baseData)
+%!error <sizes mismatch between D and F>
+%! so_step_armijo (1, 2, f, dJ, baseData)
+%!error <sizes mismatch between D and PHI>
+%! so_step_armijo (1, 2, 3, dJ, baseData)
+
+% Test parameter validation.
+%!error <invalid Armijo relaxation parameter>
+%! data = baseData;
+%! data.p.lineSearch.relaxation = 0;
+%! so_step_armijo (1, d, f, dJ, data);
+%!error <invalid Armijo relaxation parameter>
+%! data = baseData;
+%! data.p.lineSearch.relaxation = 1;
+%! so_step_armijo (1, d, f, dJ, data);
+%!error <invalid backtracking factor>
+%! data = baseData;
+%! data.p.lineSearch.backtrack = 0;
+%! so_step_armijo (1, d, f, dJ, data);
+%!error <invalid backtracking factor>
+%! data = baseData;
+%! data.p.lineSearch.backtrack = 1;
+%! so_step_armijo (1, d, f, dJ, data);
+%!error <invalid initial increase factor>
+%! data = baseData;
+%! data.p.lineSearch.initial = 1;
+%! so_step_armijo (1, d, f, dJ, data);
+%!error <invalid minimum step length>
+%! data = baseData;
+%! data.p.lineSearch.minStep = -1;
+%! so_step_armijo (1, d, f, dJ, data);
+%!test
+%! data = baseData;
+%! data.p.lineSearch.minStep = 0;
+%! so_step_armijo (1, d, f, dJ, data);
+
+% Test that backtracking works. Set the parameters such that
+% backtracking is actually required.
+%!test
+%! data = baseData;
+%! data.p.verbose = true;
+%! data.p.lineSearch.relaxation = 0.9;
+%! [~, t] = so_step_armijo (1, d, f, dJ, data);
+%! assert (t < 0.5);
+
+% Test error handling. With a large step, we trigger
+% the "volume too large" error above. Verify that the
+% error is handled both with and without multi-threading.
+%
+% For evolution time t, the resulting volume will be 5 + 4 t.
+% Thus the volume constraint is hit at t = 2.5.
+%
+% Note that we don't get 'Subprocess error' prefixed. This is
+% presumably due to how Octave handles the error checks in
+% combination with the forks induced by multi-threading.
+%
+%!test
+%! so_step_armijo (2, d, f, dJ, baseData);
+%!error <volume too large>
+%! so_step_armijo (3, d, f, dJ, baseData);
+%!error <volume too large>
+%! pkg load parallel;
+%! data = baseData;
+%! data.p.nProc = 2;
+%! so_step_armijo (3, d, f, dJ, data);
diff --git a/inst/upwind_gradient_norm.m b/inst/upwind_gradient_norm.m
new file mode 100644
index 0000000..29de853
--- /dev/null
+++ b/inst/upwind_gradient_norm.m
@@ -0,0 +1,140 @@
+## Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+## GNU Octave level-set package.
+##
+## 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 program 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{gnorm} =} upwind_gradient_norm (@var{phi}, @var{h} = 1)
+## @deftypefnx {Function File} {@var{gnorm} =} upwind_gradient_norm (@var{phi}, @var{f}, @var{h} = 1)
+##
+## Approximate the gradient norm of @var{phi} using an upwind scheme.
+## The scheme chosen is appropriate to propagate the level-set equation
+## @tex
+## \begin{equation*}
+## \phi_t + f \left| \nabla \phi \right| = 0
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## d/dt phi + f | grad phi | = 0
+## @end example
+##
+## @end ifnottex
+## in time. If the argument @var{f} is given, its sign is used to
+## find the correct upwind direction. If it is not present, positive
+## sign is assumed throughout the domain. @var{h} gives the grid
+## spacing to use for the difference approximation.
+##
+## @var{phi} can be an array of arbitrary dimension, and @var{gnorm}
+## is always of the same size as @var{phi}. If @var{f} is given, it
+## must also be of the same size as @var{phi}.
+##
+## The gradient is approximated with finite differences, either the
+## forward or backward difference quotient. The direction chosen at
+## each grid point depends on the sign of f and the gradient of phi
+## at that point. The approximation used is from section 6.4 of
+##
+## J. A. Sethian: Level Set Methods and Fast Marching Methods, second edition,
+## 1999. Cambridge Monographs on Applied and Computational Mathematics,
+## Cambridge University Press.
+##
+## @seealso{ls_time_step}
+## @end deftypefn
+
+function gnorm = upwind_gradient_norm (phi, second, third)
+ switch nargin ()
+ case 1
+ F = ones (size (phi));
+ h = 1;
+ case 2
+ if (isscalar (second))
+ F = ones (size (phi));
+ h = second;
+ else
+ F = second;
+ h = 1;
+ endif
+ case 3
+ F = second;
+ h = third;
+ otherwise
+ print_usage ();
+ endswitch
+
+ if (!isscalar (h))
+ print_usage ();
+ endif
+
+ if (~all (size (phi) == size (F)))
+ error ("PHI and F must be of the same size");
+ endif
+
+ gnorm = __levelset_upwindGrad (phi, F, h);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%! upwind_gradient_norm ()
+%!error <Invalid call to>
+%! upwind_gradient_norm (1, 2, 3, 4)
+%!error <Invalid call to>
+%! upwind_gradient_norm ([1, 1], [2, 2], [3, 3])
+%!error <PHI and F must be of the same size>
+%! upwind_gradient_norm ([1, 1], [2, 2, 2])
+%!error <PHI and F must be of the same size>
+%! upwind_gradient_norm ([1, 1], [2, 2, 2], 5)
+
+% Basic test in 1D, check that the correct direction is chosen.
+%!test
+%! phi = [0, 2, 3];
+%! fPos = ones (size (phi));
+%!
+%! % Propagate the original phi to the right. This should result
+%! % in the *backward* difference to be used in the middle.
+%! g = upwind_gradient_norm (phi);
+%! assert (g(2), 2);
+%! gp = upwind_gradient_norm (phi, fPos);
+%! assert (gp, g);
+%! gp = upwind_gradient_norm (phi, 0.5);
+%! assert (gp, 2 * g);
+%! gp = upwind_gradient_norm (phi, fPos, 0.5);
+%! assert (gp, 2 * g);
+%!
+%! % Propagate to the left for the reverse effect.
+%! g = upwind_gradient_norm (phi, -fPos);
+%! assert (g(2), 1);
+%!
+%! % Propagate -phi, which again turns everything around.
+%! g = upwind_gradient_norm (-phi, fPos);
+%! assert (g(2), 1);
+%! g = upwind_gradient_norm (-phi, -fPos);
+%! assert (g(2), 2);
+
+% Test in 3D with a quadratic function, where we know the resulting
+% expected gradient norm.
+%!test
+%! n = 50;
+%! x = linspace (-1, 1, n);
+%! h = x(2) - x(1);
+%! [XX, YY, ZZ] = ndgrid (x, x, x);
+%!
+%! phi = XX.^2 + YY.^2 + ZZ.^2;
+%! g = upwind_gradient_norm (phi, h);
+%!
+%! r = sqrt (phi);
+%! assert (g, 2 * r, 0.1);
diff --git a/src/FastMarching.cpp b/src/FastMarching.cpp
new file mode 100644
index 0000000..22ef9d2
--- /dev/null
+++ b/src/FastMarching.cpp
@@ -0,0 +1,417 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2013-2015 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+#include "FastMarching.hpp"
+
+#include <algorithm>
+#include <cmath>
+#include <memory>
+#include <stdexcept>
+
+namespace fastMarching
+{
+
+/* Tolerance for safety checks. */
+static const realT ASSERT_TOL = 1.0e-9;
+
+/* ************************************************************************** */
+/* Update equation class. */
+
+/**
+ * Destruct and free all memory held in the entries map.
+ */
+UpdateEquation::~UpdateEquation ()
+{
+ for (auto e : entries)
+ delete e.second;
+}
+
+/**
+ * Add a new entry together with g.
+ * @param d Dimension along which the point neighbours the centre.
+ * @param h Grid spacing to the neighbour.
+ * @param u Value of the distance function at the neighbour.
+ * @param g Value of the extended function at the neighbour.
+ */
+void
+UpdateEquation::add (dimensionT d, realT h, realT u, realT g)
+{
+ std::unique_ptr<EqnEntry> newEntry(new EqnEntry (h, u, g));
+
+ auto iter = entries.find (d);
+ if (iter == entries.end ())
+ {
+ entries.insert (std::make_pair (d, newEntry.release ()));
+ return;
+ }
+
+ if (iter->second->getEffDistance () > newEntry->getEffDistance ())
+ {
+ delete iter->second;
+ iter->second = newEntry.release ();
+ }
+}
+
+/**
+ * Calculate the new distance value solving the quadratic equation.
+ * @param f Speed value at the current cell.
+ * @return Distance value at the centre cell.
+ */
+realT
+UpdateEquation::solve (realT f) const
+{
+ sortedEntries.clear ();
+
+ /* First, sort the entries by distance. */
+ for (const auto& e : entries)
+ sortedEntries.push_back (e.second);
+ std::sort (sortedEntries.begin (), sortedEntries.end (), &EqnEntry::compare);
+
+ /*
+ The equation we solve is:
+
+ (X - N_1)^2 / h_1^2 + (X - N_2)^2 / h_2^2 + ... = f^2,
+
+ where N_i are the smaller, alive neighbours in each direction. During
+ the loop, we build up the coefficients accordingly.
+ */
+
+ realT a, b, c;
+ realT d;
+
+ while (true)
+ {
+ assert (!sortedEntries.empty ());
+
+ a = 0.0;
+ b = 0.0;
+ c = -std::pow (f, 2);
+
+ for (const auto cur : sortedEntries)
+ {
+ const realT invH2 = std::pow (cur->h, -2);
+ a += invH2;
+ b -= 2.0 * cur->u * invH2;
+ c += std::pow (cur->u, 2) * invH2;
+ }
+
+ d = std::pow (b, 2) - 4.0 * a * c;
+ if (d >= 0.0)
+ break;
+
+ issueWarning ("level-set:fast-marching:too-far-alive",
+ "found too far alive point in fast marching, d = %f", d);
+ sortedEntries.pop_back ();
+ }
+
+ /* Now the equation is solvable, actually do it and take the larger
+ of the solutions. */
+ u = (-b + sqrt (d)) / (2.0 * a);
+
+ return u;
+}
+
+/**
+ * Calculate the value of the extended function at the centre. This
+ * can only be used if all g values were given for all entries, and
+ * if solve() was already called.
+ * @return The value of the extended function at the centre.
+ * @throws std::logic_error if the equation was built without all g's.
+ * @throws std::logic_error if solve() was not yet called.
+ */
+realT
+UpdateEquation::getExtendedFunction () const
+{
+ if (gMissing)
+ throw std::logic_error ("not all values of g given in update equation");
+ if (sortedEntries.empty ())
+ throw std::logic_error ("update equation is not yet solved");
+
+ /*
+ For the extended function g, the equation to solve is:
+
+ (G - G_1) (U - N_1) / h_1^2 + (G - G_2) (U - N_2) / h_2^2 + ... = 0,
+
+ which is even linear in G since we know U already. We solve this equation
+ in the form "a G = b".
+ */
+
+ realT a = 0.0;
+ realT b = 0.0;
+ for (const auto cur : sortedEntries)
+ {
+ const realT invH2 = std::pow (cur->h, -2);
+ const realT grad = u - cur->u;
+ a += grad * invH2;
+ b += cur->g * grad * invH2;
+ }
+
+ assert (a > 0.0);
+ return b / a;
+}
+
+/* ************************************************************************** */
+/* Single cell entry. */
+
+/**
+ * Set state to alive.
+ * @see setNarrowBand
+ */
+void
+Entry::setAlive ()
+{
+ /* Note that this routine is only used to set narrow_band -> alive, since
+ far_away -> narrow_band must be done with setNarrowBand. */
+ assert (state == NARROW_BAND);
+ state = ALIVE;
+
+ assert (heapEntry);
+ heapEntry = nullptr;
+}
+
+/**
+ * Set state to narrow band, adding also the heap entry.
+ * @param e Heap entry.
+ * @see setState
+ */
+void
+Entry::setNarrowBand (const heapT::Entry& e)
+{
+ assert (state == FAR_AWAY);
+ state = NARROW_BAND;
+
+ assert (!heapEntry);
+ heapEntry = e;
+}
+
+/**
+ * Update distance value from the neighbours. They are found
+ * using our coordinates and the given grid. This may raise assertion
+ * failures under certain conditions.
+ * @param grid The grid to use to access neighbours.
+ */
+void
+Entry::update (const Grid& grid)
+{
+ assert (state == NARROW_BAND);
+
+ /*
+ Go over all neighbours and remember in each dimension the smaller of
+ the neighbour values. We only use alive values as suggested in the
+ technical report of J. A. Baerentzen.
+ */
+
+ UpdateEquation eqn;
+ const auto buildEqn
+ = [&eqn, &grid] (const IndexTuple& neighbour, dimensionT d, indexT)
+ {
+ const Entry* e = grid.get (neighbour);
+ if (e && e->state == ALIVE && e->u < LARGE_DIST)
+ eqn.add (d, 1.0, e->u, e->g);
+ };
+ grid.iterateNeighbours (coord, buildEqn);
+
+ /* Solve the equation. */
+ const realT newU = eqn.solve (f);
+ g = eqn.getExtendedFunction ();
+
+ /* Update. Usually the distance should be smaller, but in certain
+ circumstances we want to accomodate also for increases correcting
+ prior inaccuracies (and inaccuracies in the initialisation possibly). */
+ bool bubbleDown = false;
+ if (newU > u)
+ {
+ if (newU > u + ASSERT_TOL)
+ issueWarning ("level-set:fast-marching:increased-distance",
+ "increased distance in fast marching %f to %f", u, newU);
+ bubbleDown = true;
+ }
+ u = newU;
+
+ /* Bubble up the heap. */
+ assert (heapEntry);
+ if (bubbleDown)
+ heapEntry->bubbleDown ();
+ else
+ heapEntry->bubbleUp ();
+}
+
+/* ************************************************************************** */
+/* Full grid. */
+
+/**
+ * Calculate index into entries for given coordinates.
+ * @param c Coordinates in indices.
+ * @return Single index into entries or -1 if c is out of range.
+ */
+indexT
+Grid::lineariseIndex (const IndexTuple& c) const
+{
+ assert (size.size () == c.size ());
+
+ indexT res = 0;
+ for (dimensionT dim = 0; dim < size.size (); ++dim)
+ {
+ if (c[dim] < 0 || c[dim] >= size[dim])
+ return -1;
+ res *= size[dim];
+ res += c[dim];
+ }
+
+ return res;
+}
+
+/**
+ * Perform a single update step on the currently best next narrow-band
+ * entry. It is removed and set alive, and its neighbours are then
+ * added into the narrow-band if not yet there and updated.
+ */
+void
+Grid::update ()
+{
+ assert (!narrowBand.empty ());
+
+ Entry* top = **narrowBand.top ();
+ narrowBand.pop ();
+
+ top->setAlive ();
+ recalculateNeighbours (*top);
+}
+
+/**
+ * Recalculate neighbours of a given entry. This is used both for
+ * update and initialisation of narrow band.
+ * @param center Recalculate this entry's neighbours.
+ */
+void
+Grid::recalculateNeighbours (const Entry& center)
+{
+ IndexTuple neighbour = center.getCoordinate ();
+ const auto updateIt
+ = [this] (const IndexTuple& neighbour, dimensionT, indexT)
+ {
+ Entry* e = get (neighbour);
+ if (!e)
+ return;
+
+ switch (e->getState ())
+ {
+ case Entry::FAR_AWAY:
+ /* Note: The heap push below inserts first at the very
+ bottom of the heap and needs no bubbling up, since the
+ distance is currently LARGE_DIST. It will be bubbled
+ only in the update below. */
+ {
+ const auto heapEntry = narrowBand.push (e);
+ e->setNarrowBand (heapEntry);
+ }
+ /* Fall through. */
+ case Entry::NARROW_BAND:
+ e->update (*this);
+ break;
+
+ default:
+ /* Nothing to do. */
+ break;
+ }
+ };
+ iterateNeighbours (center.getCoordinate (), updateIt);
+}
+
+/**
+ * Construct the grid with a given size. The entries will be empty
+ * and will still have to be filled in.
+ * @param s Size to use.
+ */
+Grid::Grid (const IndexTuple& s)
+ : size(s), entries(), narrowBand(&Entry::compareForHeap)
+{
+ indexT fullSize = 1;
+ for (auto i : s)
+ fullSize *= i;
+
+ entries.resize (fullSize);
+ for (auto& p : entries)
+ p = nullptr;
+
+ assert (narrowBand.empty ());
+}
+
+/**
+ * Destructor, freeing all memory.
+ */
+Grid::~Grid ()
+{
+ /* Narrow band heap only holds pointers, memory not owned there! */
+
+ /* Free memory in entries. */
+ for (auto p : entries)
+ if (p)
+ delete p;
+ entries.clear ();
+}
+
+/**
+ * Initialise a given grid cell by the given entry. The coordinate
+ * where to put the entry is taken from the object itself, and an
+ * error is raised if that entry is already filled in. The entries
+ * state must be either "alive" or "far away", narrow band is
+ * automatically initialised later on.
+ * @param e The new entry. Ownership is taken over.
+ * @throws std::invalid_argument if something is wrong with e.
+ */
+void
+Grid::setInitial (Entry* e)
+{
+ if (!e)
+ throw std::invalid_argument ("entry is NULL");
+ if (e->getState () != Entry::ALIVE && e->getState () != Entry::FAR_AWAY)
+ throw std::invalid_argument ("only alive and far-away entries can be"
+ " set initially");
+
+ const IndexTuple& indTuple = e->getCoordinate ();
+ const indexT ind = lineariseIndex (indTuple);
+
+ if (ind < 0)
+ throw std::invalid_argument ("invalid coordinate of entry");
+ if (entries[ind])
+ throw std::invalid_argument ("duplicate entry set on grid");
+
+ entries[ind] = e;
+}
+
+/**
+ * Perform the calculation, updating all narrow-band entries until
+ * all of them are alive. It is necessary to have narrow-band already
+ * filled in, meaning that far-away entries not reached by the propagation
+ * from any narrow-band will not be updated!
+ */
+void
+Grid::perform ()
+{
+ for (const auto center : entries)
+ if (center && center->getState () == Entry::ALIVE)
+ recalculateNeighbours (*center);
+
+ while (!narrowBand.empty ())
+ update ();
+}
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
diff --git a/src/FastMarching.hpp b/src/FastMarching.hpp
new file mode 100644
index 0000000..fef8e96
--- /dev/null
+++ b/src/FastMarching.hpp
@@ -0,0 +1,616 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2013-2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+#ifndef FASTMARCHING_HPP
+#define FASTMARCHING_HPP
+
+/*
+
+Implementation of the fast marching method to numerically solve the
+Eikonal equation:
+
+ | grad u | = f
+
+f is assumed to be positive, and also assumed to include the grid spacing
+already (so that 1 is used as unit distance between grid points).
+For solving problems with changing sign of f,
+the domain has to be split into parts with constant sign of f, and if f
+is negative, a corresponding transformation on f and u has to be performed
+to make f positive.
+
+The base grid is rectangular and assumed to "hold all" of the domain,
+but it is not necessary to set F on the full grid to meaningful values.
+Only for cells which are not initially marked as "alive" are the
+neighbours actually ever accessed, and points outside the domain of
+interest can be set NULL. Those will be assumed to be always
+too far away to influence interior points, so in particular if f -> infinity
+towards the boundary we can really ignore those points.
+
+Each grid cell is represented by a pointed-to object, so that we can easily
+store pointers to those objects in the grid to access them by indices and
+find their neighbours, and also in the heap structure used to find the
+next "narrow band" point to operate on.
+
+Additionally, it is possible to extend a function g known on the domain boundary
+"along" the normal direction, such that
+
+ grad u * grad g = 0.
+
+*/
+
+#include "Heap.hpp"
+
+#include <cassert>
+#include <limits>
+#include <map>
+#include <vector>
+
+namespace fastMarching
+{
+
+class Entry;
+class Grid;
+
+/** Type used for real numbers. */
+typedef double realT;
+
+/** Define value used to represent "very large" values. */
+static const realT LARGE_DIST = std::numeric_limits<realT>::max ();
+
+/**
+ * Type used to number entries. Signed values are allowed in order to allow
+ * -1 to be a "valid" index. The grid takes care of out-of-range values.
+ */
+typedef int indexT;
+
+/**
+ * Full set of indices. To be precise, this contains N indexT grouped
+ * together, where N is the space dimension we solve the equation in.
+ */
+typedef std::vector<indexT> IndexTuple;
+
+/** Type used to number the dimensions. */
+typedef IndexTuple::size_type dimensionT;
+
+/** Type used for the heap. */
+typedef Heap<Entry*, bool (*)(const Entry*, const Entry*)> heapT;
+
+/**
+ * Method to issue warnings. This is not implemented in FastMarching.cpp
+ * but must be provided by the caller. That way, it can use Octave's
+ * warning infrastructure but still make the FastMarching code itself
+ * independent of Octave.
+ * @param id An ID for the warning.
+ * @param fmt The (printf-like) message template.
+ * @param ... Other arguments for printf-like formatting.
+ */
+void issueWarning (const std::string& id, const std::string& fmt, ...);
+
+/* ************************************************************************** */
+/* Update equation class. */
+
+/**
+ * Represent and solve the quadratic equation necessary in the fast marching
+ * step for updating the distance of a point from given neighbours. This
+ * is also useful for initialising the narrow-band entries from the level-set
+ * function, and thus available as global class.
+ */
+class UpdateEquation
+{
+
+private:
+
+ class EqnEntry;
+
+ /**
+ * Map space dimensions to the currently best entry along the given
+ * dimension. It may be null if there's not yet an acceptable entry
+ * there, and there may be multiple entries added for the same dimension
+ * (on both sides of our point). In this case, only the closer one is
+ * relevant, and will be kept in the map.
+ */
+ typedef std::map<dimensionT, EqnEntry*> entryMapT;
+
+ /** The actual map. */
+ entryMapT entries;
+
+ /**
+ * Remember whether one of the g values was being absent. If that's
+ * the case, we can't calculate g from the equation. This is enforced.
+ */
+ bool gMissing;
+
+ /**
+ * When solving, keep track of the actually used and sorted neighbours
+ * for calculating g later on.
+ */
+ mutable std::vector<const EqnEntry*> sortedEntries;
+
+ /** Store calculated distance value here. */
+ mutable realT u;
+
+public:
+
+ /**
+ * Construct it empty.
+ */
+ inline UpdateEquation ()
+ : entries (), gMissing(false), sortedEntries(), u(0.0)
+ {}
+
+ /**
+ * Destruct and free all memory held in the entries map.
+ */
+ ~UpdateEquation ();
+
+ // No copying.
+ UpdateEquation (const UpdateEquation&) = delete;
+ UpdateEquation& operator= (const UpdateEquation&) = delete;
+
+ /**
+ * Add a new entry. The entry is characterised by the dimension along
+ * which it neighbours the cell being calculated, the distance (grid spacing)
+ * to it and the distance value at the neighbour. Using this function
+ * without a parameter for g flags the equation as missing g values,
+ * which means that getExtendedFunction does not work.
+ * @param d Dimension along which the point neighbours the centre.
+ * @param h Grid spacing to the neighbour.
+ * @param u Value of the distance function at the neighbour.
+ */
+ inline void
+ add (dimensionT d, realT h, realT u)
+ {
+ add (d, h, u, 0.0);
+ gMissing = true;
+ }
+
+ /**
+ * Add a new entry together with g.
+ * @param d Dimension along which the point neighbours the centre.
+ * @param h Grid spacing to the neighbour.
+ * @param u Value of the distance function at the neighbour.
+ * @param g Value of the extended function at the neighbour.
+ */
+ void add (dimensionT d, realT h, realT u, realT g);
+
+ /**
+ * Calculate the new distance value solving the quadratic equation.
+ * @param f Speed value at the current cell.
+ * @return Distance value at the centre cell.
+ */
+ realT solve (realT f) const;
+
+ /**
+ * Calculate the value of the extended function at the centre. This
+ * can only be used if all g values were given for all entries, and
+ * if solve() was already called.
+ * @return The value of the extended function at the centre.
+ * @throws std::logic_error if the equation was built without all g's.
+ * @throws std::logic_error if solve() was not yet called.
+ */
+ realT getExtendedFunction () const;
+
+};
+
+/**
+ * The equation entry class. This simply holds some data.
+ */
+class UpdateEquation::EqnEntry
+{
+
+public:
+
+ /** The neighbour's distance on the grid. */
+ realT h;
+
+ /** The neighbour's distance value. */
+ realT u;
+
+ /** The neighbour's extended function value. */
+ realT g;
+
+ /* Note that we don't need the dimension, since this is already part
+ of the map in UpdateEquation anyway. */
+
+ /**
+ * Construct it given all the data.
+ * @param mh Grid distance.
+ * @param mu Distance value.
+ * @param mg Extended function value.
+ */
+ inline EqnEntry (realT mh, realT mu, realT mg)
+ : h(mh), u(mu), g(mg)
+ {}
+
+ // No copying or default constructor, since it is not needed.
+ EqnEntry () = delete;
+ EqnEntry (const EqnEntry&) = delete;
+ EqnEntry& operator= (const EqnEntry&) = delete;
+
+ /**
+ * Get the "effective distance" that this point would imply for the
+ * centre, which is the distance if only this point would be there. It
+ * is used for deciding which one of two neighbours along a single dimension
+ * is the one to use, and also for sorting later on.
+ * @return The effective distance, which is u + h.
+ */
+ inline realT
+ getEffDistance () const
+ {
+ /* Note: The roles of u and h in the quadratic equation being solved
+ are not entirely the same, so it is not clear whether this
+ "effective distance" is really the right criterion to decide upon which
+ point to use. However, in the cases actually used, either h is the
+ same along a dimension (grid spacing) or u is zero everywhere (initial
+ distances from the level-set function). For those, this concept should
+ work at least. */
+
+ return u + h;
+ }
+
+ /**
+ * Compare two entry pointers for sorting by distance. If the distances
+ * are equal, we compare the pointers just to get a criterion to avoid
+ * false classification as being equal (which might confuse the sorting
+ * algorithm).
+ * @param a First pointer.
+ * @param b Second pointer.
+ * @return True iff a < b.
+ */
+ static inline bool
+ compare (const EqnEntry* a, const EqnEntry* b)
+ {
+ const realT diff = a->getEffDistance () - b->getEffDistance ();
+ if (diff != 0.0)
+ return (diff < 0.0);
+
+ return (a < b);
+ }
+
+};
+
+/* ************************************************************************** */
+/* Single cell entry. */
+
+/**
+ * This class represents all data stored for a single grid point. It also
+ * has the ability to do cell-related calculations, and update itself solving
+ * the respective quadratic equation.
+ */
+class Entry
+{
+
+public:
+
+ /** Possible states of a cell during processing. */
+ enum State
+ {
+ ALIVE,
+ NARROW_BAND,
+ FAR_AWAY
+ };
+
+private:
+
+ /** Value of f at the given point. */
+ const realT f;
+
+ /** Current value of u. */
+ realT u;
+ /** Current value of the extended function g. */
+ realT g;
+
+ /** Current state. */
+ State state;
+
+ /**
+ * Index of this grid cell. This is used to find the neighbours
+ * in the updating calculation.
+ */
+ const IndexTuple coord;
+
+ /** Entry into the heap in case we're in narrow band. */
+ heapT::Entry heapEntry;
+
+ /**
+ * Construct the cell entry given the data.
+ * @param s Initial state.
+ * @param c Coordinate in the grid.
+ * @param mf Value of f at this position.
+ * @param mu Distance value.
+ * @param mg Value for extended function.
+ */
+ inline Entry (State s, const IndexTuple& c, realT mf, realT mu, realT mg)
+ : f(mf), u(mu), g(mg), state(s), coord(c), heapEntry(nullptr)
+ {
+ assert (s == ALIVE || s == FAR_AWAY);
+ }
+
+public:
+
+ /* No copying or default constructor. */
+ Entry () = delete;
+ Entry (const Entry& o) = delete;
+ Entry& operator= (const Entry& o) = delete;
+
+ /**
+ * Construct the entry for an initially alive cell.
+ * @param c Coordinate for it.
+ * @param u Initial distance.
+ * @param g Initial function value.
+ */
+ static inline
+ Entry* newAlive (const IndexTuple& c, realT u, realT g)
+ {
+ /* f is not necessary, set to dummy value. */
+ return new Entry (ALIVE, c, 0.0, u, g);
+ }
+
+ /**
+ * Construct the entry for an initially far away cell.
+ * @param c Coordinate for it.
+ * @param f Value of f.
+ */
+ static inline
+ Entry* newFarAway (const IndexTuple& c, realT f)
+ {
+ /* g0 is not necessary (will be overwritten), set to dummy value. */
+ return new Entry (FAR_AWAY, c, f, LARGE_DIST, 0.0);
+ }
+
+ /**
+ * Get current value of u.
+ * @return Current value of u for this cell.
+ */
+ inline realT
+ getDistance () const
+ {
+ return u;
+ }
+
+ /**
+ * Get value of extended function g.
+ * @return Current value of g for this cell.
+ */
+ inline realT
+ getFunction () const
+ {
+ return g;
+ }
+
+ /**
+ * Get index coordinate. This is used by the grid to put the cell into
+ * the correct place when initialising.
+ * @return This entry's coordinate.
+ */
+ inline const IndexTuple&
+ getCoordinate () const
+ {
+ return coord;
+ }
+
+ /**
+ * Get the current state.
+ * @return The current state.
+ */
+ inline State
+ getState () const
+ {
+ return state;
+ }
+
+ /**
+ * Set state to alive.
+ * @see setNarrowBand
+ */
+ void setAlive ();
+
+ /**
+ * Set state to narrow band, adding also the heap entry.
+ * @param e Heap entry.
+ * @see setState
+ */
+ void setNarrowBand (const heapT::Entry& e);
+
+ /**
+ * Update distance value from the neighbours. They are found
+ * using our coordinates and the given grid, and the heap is
+ * automatically bubbled for the change to take place.
+ * This may raise assertion failures under certain conditions.
+ * @param grid The grid to use to access neighbours.
+ */
+ void update (const Grid& grid);
+
+ /**
+ * Compare two elements as they should be compared for the heap
+ * data structure. Since the heap has the largest element first and
+ * that should correspond to smallest distance, return ordering
+ * induced by ">" on u. Because the heap
+ * will be based on pointers to entries, this function also works on them.
+ * @param a First entry.
+ * @param b Second entry.
+ * @return True iff a should be accessed later than b, which is equivalent
+ * to a.u > b.u.
+ */
+ static inline bool
+ compareForHeap (const Entry* a, const Entry* b)
+ {
+ assert (a && b);
+ return a->u > b->u;
+ }
+
+};
+
+/* ************************************************************************** */
+/* Full grid. */
+
+/**
+ * This class represents a full grid, which also keeps track of the high-level
+ * data structures during the solve.
+ */
+class Grid
+{
+
+private:
+
+ /** Size of the grid. */
+ const IndexTuple size;
+
+ /**
+ * Store pointers to the grid entries "by index" here. This is the
+ * multi-dimensional array mapped to a single dimension. Some entries
+ * may be NULL, which means that those are outside of the domain
+ * we're interested in.
+ */
+ std::vector<Entry*> entries;
+
+ /** Keep track of points currently in "narrow band" state. */
+ heapT narrowBand;
+
+ /**
+ * Calculate index into entries for given coordinates.
+ * @param c Coordinates in indices.
+ * @return Single index into entries or -1 if c is out of range.
+ */
+ indexT lineariseIndex (const IndexTuple& c) const;
+
+ /**
+ * Perform a single update step on the currently best next narrow-band
+ * entry. It is removed and set alive, and its neighbours are then
+ * added into the narrow-band if not yet there and updated.
+ */
+ void update ();
+
+ /**
+ * Recalculate neighbours of a given entry. This is used both for
+ * update and initialisation of narrow band.
+ * @param center Recalculate this entry's neighbours.
+ */
+ void recalculateNeighbours (const Entry& center);
+
+ /**
+ * Get grid cell by index, mutable version for internal use.
+ * @param c Index coordinate tuple.
+ * @return The corresponding entry pointer.
+ * @see get (const IndexTuple&)
+ */
+ inline Entry*
+ get (const IndexTuple& c)
+ {
+ const indexT ind = lineariseIndex (c);
+ if (ind < 0)
+ return nullptr;
+ return entries[ind];
+ }
+
+ /**
+ * Internal routine used for iterate(), that is called recursively
+ * to perform the required number of nested loops.
+ * @param cur Current index tuple, will be built up recursively.
+ * @param depth Current depth in the recursion.
+ * @param f The call-back to call for each cell index.
+ */
+ template<typename F>
+ void iterate (IndexTuple& cur, dimensionT depth, const F& f) const;
+
+public:
+
+ /* No copying or default constructor. */
+ Grid () = delete;
+ Grid (const Grid& o) = delete;
+ Grid& operator= (const Grid& o) = delete;
+
+ /**
+ * Construct the grid with a given size. The entries will be empty
+ * and will still have to be filled in.
+ * @param s Size to use.
+ */
+ Grid (const IndexTuple& s);
+
+ /**
+ * Destructor, freeing all memory.
+ */
+ ~Grid ();
+
+ /**
+ * Initialise a given grid cell by the given entry. The coordinate
+ * where to put the entry is taken from the object itself, and an
+ * error is raised if that entry is already filled in. The entries
+ * state must be either "alive" or "far away", narrow band is
+ * automatically initialised later on.
+ * @param e The new entry. Ownership is taken over.
+ * @throws std::invalid_argument if something is wrong with e.
+ */
+ void setInitial (Entry* e);
+
+ /**
+ * Get grid cell by index. Returned is the pointer, which may be NULL
+ * in case this cell is outside the domain or even the index range given
+ * outside the size.
+ * @param c Index coordinate tuple.
+ * @return The corresponding entry pointer.
+ */
+ inline const Entry*
+ get (const IndexTuple& c) const
+ {
+ const indexT ind = lineariseIndex (c);
+ if (ind < 0)
+ return nullptr;
+ return entries[ind];
+ }
+
+ /**
+ * Perform the calculation, updating all narrow-band entries until
+ * all of them are alive. It is necessary to have narrow-band already
+ * filled in, meaning that far-away entries not reached by the propagation
+ * from any narrow-band will not be updated!
+ */
+ void perform ();
+
+ /**
+ * Iterate over all cells of the given grid and call the provided
+ * call-back routine with the coordinate as IndexTuple.
+ * @param f The call-back to call for each cell index.
+ */
+ template<typename F>
+ inline void
+ iterate (const F& f) const
+ {
+ IndexTuple cur(size.size ());
+ iterate (cur, 0, f);
+ }
+
+ /**
+ * Iterate over all neighbours of a given index. For each neighbour that
+ * is still on the grid, call the provided function with its index.
+ * @param c The centre coordinate.
+ * @param f The call-back to call for each neighbour.
+ */
+ template<typename F>
+ void iterateNeighbours (const IndexTuple& c, const F& f) const;
+
+};
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
+
+/* Include template implementations. */
+#include "FastMarching.tpp"
+
+#endif /* Header guard. */
diff --git a/src/FastMarching.tpp b/src/FastMarching.tpp
new file mode 100644
index 0000000..f917ce8
--- /dev/null
+++ b/src/FastMarching.tpp
@@ -0,0 +1,82 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Template implementations of FastMarching.hpp. */
+
+namespace fastMarching
+{
+
+/* ************************************************************************** */
+/* Full grid. */
+
+/**
+ * Internal routine used for iterate(), that is called recursively
+ * to perform the required number of nested loops.
+ * @param cur Current index tuple, will be built up recursively.
+ * @param depth Current depth in the recursion.
+ * @param f The call-back to call for each cell index.
+ */
+template<typename F>
+ void
+ Grid::iterate (IndexTuple& cur, dimensionT depth, const F& f) const
+{
+ const dimensionT D = size.size ();
+
+ assert (depth <= D);
+ if (depth == D)
+ {
+ f (cur);
+ return;
+ }
+
+ for (indexT i = 0; i < size[depth]; ++i)
+ {
+ cur[depth] = i;
+ iterate (cur, depth + 1, f);
+ }
+}
+
+/**
+ * Iterate over all neighbours of a given index. For each neighbour that
+ * is still on the grid, call the provided function with its index,
+ * the dimension index along which they are neighbours, and the
+ * offset along this dimension (+1 or -1).
+ * @param c The centre coordinate.
+ * @param f The call-back to call for each neighbour.
+ */
+template<typename F>
+ void
+ Grid::iterateNeighbours (const IndexTuple& c, const F& f) const
+{
+ IndexTuple neighbour = c;
+ for (dimensionT d = 0; d < neighbour.size (); ++d)
+ {
+ const auto oldValue = neighbour[d];
+ for (neighbour[d] -= 1; neighbour[d] <= oldValue + 2;
+ neighbour[d] += 2)
+ {
+ if (lineariseIndex (neighbour) >= 0)
+ f (neighbour, d, neighbour[d] - oldValue);
+ }
+ neighbour[d] = oldValue;
+ }
+}
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
diff --git a/src/Heap.hpp b/src/Heap.hpp
new file mode 100644
index 0000000..0fb80ff
--- /dev/null
+++ b/src/Heap.hpp
@@ -0,0 +1,262 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2013-2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+#ifndef HEAP_HPP
+#define HEAP_HPP
+
+/* Manual implementation of a binary heap, which is used by the fast marching
+ algorithm. Unfortunately I can't use the STL provided stuff directly,
+ since we need "bubbling up" of values that were already in the heap and
+ that changed to a lesser distance. Hence I implement my own.
+
+ Mapping from the linear array physically holding the elements to the
+ tree structure and back is performed by the Entry class. It also
+ adds an additional layer of redirection so that we can store "permanent"
+ references to heap elements (pointers to the Entry objects) which are
+ not invalidated and keep pointing to the same element and also its physical
+ position inside the heap even when the heap is bubbled in some way. */
+
+#include <cassert>
+#include <stdexcept>
+#include <vector>
+
+namespace fastMarching
+{
+
+/* ************************************************************************** */
+/* Heap class itself. */
+
+/**
+ * This class represents the binary heap, with all the basic operations
+ * necessary for it. As underlying store we use a std::vector for simplicity.
+ */
+template<typename T, typename C>
+ class Heap
+{
+
+ private:
+
+ class EntryData;
+
+ /**
+ * Type of array holding physical data and the "master" pointers to
+ * the memory allocated to entries.
+ */
+ typedef std::vector<EntryData*> entryArr;
+
+ /** Type indexing a physical entry. */
+ typedef typename entryArr::size_type indexT;
+
+ /** Hold the elements. */
+ entryArr data;
+
+ /** Our comparator functor. */
+ const C compare;
+
+ public:
+
+ /** Pointer to an entry as it can be used from the outside. */
+ typedef EntryData* Entry;
+
+ /* No copying for simplicity. The default implementations should work,
+ but it is not needed. */
+ Heap () = delete;
+ Heap (const Heap& o) = delete;
+ Heap& operator= (const Heap& o) = delete;
+
+ /**
+ * Construct an empty heap with the given comparator.
+ * @param c Comparator object to use.
+ */
+ explicit inline Heap (const C& c)
+ : data(), compare(c)
+ {}
+
+ /**
+ * Destruct freeing all memory.
+ */
+ ~Heap ();
+
+ /**
+ * Get entry at the top of the heap.
+ * @return Entry reference to top of heap.
+ * @throws std::out_of_range if the heap is empty.
+ */
+ inline Entry
+ top ()
+ {
+ if (empty ())
+ throw std::out_of_range ("top() for empty heap");
+
+ return data.front ();
+ }
+
+ /**
+ * Pop off and remove the top.
+ * @throws std::out_of_range if the heap is empty.
+ */
+ void pop ();
+
+ /**
+ * Push a new element and return its entry.
+ * @param e The new element.
+ * @return Entry reference to it after finding the correct position.
+ */
+ Entry push (const T& e);
+
+ /**
+ * Bubble an element pointed to by the entry up to the correct position.
+ * @param e Entry to bubble up.
+ * @throws std::invalid_argument if the element belongs not to this heap.
+ */
+ void bubbleUp (Entry e);
+
+ /**
+ * Bubble an element pointed to by the entry down to the correct position.
+ * @param e Entry to bubble down.
+ * @throws std::invalid_argument if the element belongs not to this heap.
+ */
+ void bubbleDown (Entry e);
+
+ /**
+ * Query whether the heap is empty or not.
+ * @return True iff no elements are present.
+ */
+ inline bool
+ empty () const
+ {
+ return data.empty ();
+ }
+
+};
+
+/* ************************************************************************** */
+/* Heap entry class. */
+
+/**
+ * Represent an entry in the heap. This holds both the actual data and
+ * also the position in the heap vector. It allows accessing parent nodes
+ * and children in the binary tree.
+ */
+template<typename T, typename C>
+ class Heap<T, C>::EntryData
+{
+
+ private:
+
+ friend class Heap;
+
+ /** Type of parent heap. */
+ typedef Heap<T, C> parentT;
+
+ /** The heap we're part of. */
+ parentT& parent;
+
+ /** Piece of data hold. */
+ const T data;
+
+ /** Current index into the heap array. */
+ parentT::indexT index;
+
+ /**
+ * Construct it, given the parent heap, data entry and index.
+ * @param h Parent heap.
+ * @param d Data entry.
+ * @param i Index.
+ */
+ inline EntryData (parentT& h, const T& d, parentT::indexT i)
+ : parent(h), data(d), index(i)
+ {}
+
+ /**
+ * Verify that the subtree belonging to this entry is in fact really
+ * a heap satisfying the invariant. An assertion failure is thrown if not.
+ */
+ void verifySubtree ();
+
+ /**
+ * Swap two entries with each other in the vector. This is a utility
+ * routine, which updates the vector holding the entries as well
+ * as updating the indices. Of course, they must belong to the same
+ * parent heap.
+ * @param a First entry.
+ * @param b Second entry.
+ */
+ static void swap (EntryData& a, EntryData& b);
+
+ public:
+
+ /* No copying, users should use pointers to this one. */
+ EntryData () = delete;
+ EntryData (const EntryData& o) = delete;
+ EntryData& operator= (const EntryData& o) = delete;
+
+ /* No destructor necessary. */
+
+ /**
+ * Access data element.
+ * @return Data element.
+ */
+ inline const T&
+ operator* () const
+ {
+ return data;
+ }
+
+ /**
+ * Get pointer to entry of parent in the tree.
+ * @return Parent in the tree as Entry or NULL if we're on top.
+ */
+ Entry up () const;
+
+ /**
+ * Get pointer to one of the childs in the tree.
+ * @param c Which child to get (0 or 1).
+ * @return Selected child as Entry or NULL if there's no such child.
+ * @throws std::invalid_argument if c is not 0 or 1.
+ */
+ Entry down (parentT::indexT c) const;
+
+ /**
+ * Bubble up this entry in the containing heap.
+ */
+ inline void
+ bubbleUp ()
+ {
+ parent.bubbleUp (this);
+ }
+
+ /**
+ * Bubble down this entry in the containing heap.
+ */
+ inline void
+ bubbleDown ()
+ {
+ parent.bubbleDown (this);
+ }
+
+};
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
+
+/* Include template implementations. */
+#include "Heap.tpp"
+
+#endif /* Header guard. */
diff --git a/src/Heap.tpp b/src/Heap.tpp
new file mode 100644
index 0000000..2dd3e5b
--- /dev/null
+++ b/src/Heap.tpp
@@ -0,0 +1,238 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2013-2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Template implementations of Heap.hpp. */
+
+#define AGGRESSIVE_CHECKS 0
+
+namespace fastMarching
+{
+
+/* ************************************************************************** */
+/* Heap class itself. */
+
+/**
+ * Destruct freeing all memory.
+ */
+template<typename T, typename C>
+ Heap<T, C>::~Heap ()
+{
+ for (auto e : data)
+ delete e;
+}
+
+/**
+ * Pop off and remove the top.
+ * @throws std::out_of_range if the heap is empty.
+ */
+template<typename T, typename C>
+ void
+ Heap<T, C>::pop ()
+{
+ if (empty ())
+ throw std::out_of_range ("pop() for empty heap");
+
+ if (data.front () == data.back ())
+ {
+ assert (data.size () == 1);
+ delete data.back ();
+ data.pop_back ();
+ return;
+ }
+
+ assert (data.size () > 1);
+ EntryData::swap (*data.front (), *data.back ());
+
+ delete data.back ();
+ data.pop_back ();
+
+ bubbleDown (top ());
+
+#if AGGRESSIVE_CHECKS
+ top ()->verifySubtree ();
+#endif
+}
+
+/**
+ * Push a new element and return its entry.
+ * @param e The new element.
+ * @return Entry reference to it after finding the correct position.
+ */
+template<typename T, typename C>
+ typename Heap<T, C>::Entry
+ Heap<T, C>::push (const T& e)
+{
+ Entry newEntry = new EntryData (*this, e, data.size ());
+ data.push_back (newEntry);
+ bubbleUp (newEntry);
+
+#if AGGRESSIVE_CHECKS
+ top ()->verifySubtree ();
+#endif
+
+ return newEntry;
+}
+
+/**
+ * Bubble an element pointed to by the entry up to the correct position.
+ * @param e Entry to bubble up.
+ * @throws std::invalid_argument if the element belongs not to this heap.
+ */
+template<typename T, typename C>
+ void
+ Heap<T, C>::bubbleUp (Entry e)
+{
+ if (&e->parent != this)
+ throw std::invalid_argument ("element belongs to a different heap");
+
+ const Entry p = e->up ();
+ if (!p)
+ return;
+
+ if (!compare (**p, **e))
+ return;
+
+ EntryData::swap (*p, *e);
+#if AGGRESSIVE_CHECKS
+ e->verifySubtree ();
+#endif
+
+ bubbleUp (e);
+}
+
+/**
+ * Bubble an element pointed to by the entry down to the correct position.
+ * @param e Entry to bubble down.
+ * @throws std::invalid_argument if the element belongs not to this heap.
+ */
+template<typename T, typename C>
+ void
+ Heap<T, C>::bubbleDown (Entry e)
+{
+ if (&e->parent != this)
+ throw std::invalid_argument ("element belongs to a different heap");
+
+ const Entry c1 = e->down (0);
+ const Entry c2 = e->down (1);
+
+ if (!c1)
+ {
+ assert (!c2);
+ return;
+ }
+
+ Entry largerC;
+ if (!c2 || compare (**c2, **c1))
+ largerC = c1;
+ else
+ largerC = c2;
+ assert (largerC);
+
+ if (!compare (**e, **largerC))
+ return;
+
+ EntryData::swap (*largerC, *e);
+ /* Here, we can't yet check the tree since it is not finished and the
+ subtrees not yet satisfy the heap condition. */
+
+ bubbleDown (e);
+}
+
+/* ************************************************************************** */
+/* Heap entry class. */
+
+/**
+ * Verify that the subtree belonging to this entry is in fact really
+ * a heap satisfying the invariant. An assertion failure is thrown if not.
+ */
+template<typename T, typename C>
+ void
+ Heap<T, C>::EntryData::verifySubtree ()
+{
+ for (parentT::indexT i = 0; i <= 1; ++i)
+ {
+ const auto child = down (i);
+ if (child)
+ {
+ assert (!parent.compare (**this, **child));
+ child->verifySubtree ();
+ }
+ }
+}
+
+/**
+ * Swap two entries with each other in the vector. This is a utility
+ * routine, which updates the vector holding the entries as well
+ * as updating the indices. Of course, they must belong to the same
+ * parent heap.
+ * @param a First entry.
+ * @param b Second entry.
+ */
+template<typename T, typename C>
+ void
+ Heap<T, C>::EntryData::swap (EntryData& a, EntryData& b)
+{
+ assert (&a.parent == &b.parent);
+
+ const auto indexA = a.index;
+ const auto indexB = b.index;
+
+ a.parent.data[indexA] = &b;
+ a.parent.data[indexB] = &a;
+
+ a.index = indexB;
+ b.index = indexA;
+}
+
+/**
+ * Get pointer to entry of parent in the tree.
+ * @return Parent in the tree as Entry or NULL if we're on top.
+ */
+template<typename T, typename C>
+ typename Heap<T, C>::Entry
+ Heap<T, C>::EntryData::up () const
+{
+ if (index == 0)
+ return nullptr;
+
+ return parent.data[(index - 1) / 2];
+}
+
+/**
+ * Get pointer to one of the childs in the tree.
+ * @param c Which child to get (0 or 1).
+ * @return Selected child as Entry or NULL if there's no such child.
+ * @throws std::invalid_argument if c is not 0 or 1.
+ */
+template<typename T, typename C>
+ typename Heap<T, C>::Entry
+ Heap<T, C>::EntryData::down (parentT::indexT c) const
+{
+ if (c != 0 && c != 1)
+ throw std::invalid_argument ("down() only accepts 0 or 1");
+ const auto ind = 2 * index + c + 1;
+
+ if (ind >= parent.data.size ())
+ return nullptr;
+
+ return parent.data[ind];
+}
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..32af9f7
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,63 @@
+# GNU Octave level-set package.
+# Copyright (C) 2013-2015 Daniel Kraft <d at domob.eu>
+#
+# 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 program 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/>.
+
+# Makefile to build the test files in C++ and the package's .oct files.
+
+CXX ?= @CXX@
+MKOCTFILE ?= mkoctfile
+
+CXXFLAGS ?= -Wall -Wextra
+FULL_CXXFLAGS = @CXXFLAGS@ $(CXXFLAGS)
+EXTRA_FLAGS = -Weffc++ -pedantic -fPIC
+
+MKOCT = CXXFLAGS="$(FULL_CXXFLAGS)" $(MKOCTFILE)
+
+HEADERS = $(wildcard:*.hpp *.tpp)
+
+ALGO_SOURCES = FastMarching.cpp
+ALGO_OBJECTS = $(ALGO_SOURCES:%.cpp=%.o)
+
+OCT_SOURCES = Utils.cpp
+OCT_OBJECTS = $(OCT_SOURCES:%.cpp=%.o)
+
+TESTS = heapsort marching
+
+PREFIX = __levelset_
+OCTS = internal_fastmarching internal_init_narrowband \
+ geomElements geomBoundary geomGamma nbFromGeom \
+ internal_mesh upwindGrad
+OCT_FILES = $(OCTS:%=$(PREFIX)%.oct)
+
+.PHONY: clean oct tests
+
+oct: $(OCT_FILES)
+
+tests: $(TESTS)
+
+clean:
+ rm -f *.o *.oct $(TESTS)
+
+$(ALGO_OBJECTS): %.o: %.cpp $(HEADERS)
+ $(CXX) $(FULL_CXXFLAGS) $(EXTRA_FLAGS) -c $< -o $@
+
+$(OCT_OBJECTS): %.o: %.cpp $(HEADERS)
+ $(MKOCT) -c $< -o $@
+
+$(OCT_FILES): $(PREFIX)%.oct: %.cpp $(HEADERS) $(ALGO_OBJECTS) $(OCT_OBJECTS)
+ $(MKOCT) $< $(ALGO_OBJECTS) $(OCT_OBJECTS) -o $@
+
+$(TESTS): %: %.cpp $(HEADERS) $(ALGO_OBJECTS)
+ $(CXX) $(FULL_CXXFLAGS) $(EXTRA_FLAGS) $< $(ALGO_OBJECTS) -o $@
diff --git a/src/Utils.cpp b/src/Utils.cpp
new file mode 100644
index 0000000..d5d4bfa
--- /dev/null
+++ b/src/Utils.cpp
@@ -0,0 +1,82 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+#include "Utils.hpp"
+
+#include <octave/lo-ieee.h>
+
+#include <algorithm>
+#include <cstdarg>
+
+using namespace fastMarching;
+
+namespace fastMarching
+{
+
+/* Provice an Octave-based implementation of issueWarning here. */
+void
+issueWarning (const std::string& id, const std::string& fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ vwarning_with_id (id.c_str (), fmt.c_str (), args);
+ va_end (args);
+}
+
+} // namespace fastMarching
+
+/**
+ * Convert an IndexTuple to an index suitable to Octave as the
+ * used type Array<octave_idx_type>.
+ * @param ind IndexTuple representing the coordinate.
+ * @return The same index suitable for Octave.
+ */
+Array<octave_idx_type>
+getOctaveIdx (const fastMarching::IndexTuple& ind)
+{
+ const dimensionT D = ind.size ();
+
+ Array<octave_idx_type> idx(dim_vector (D, 1));
+ std::copy (ind.begin (), ind.end (), idx.fortran_vec ());
+
+ return idx;
+}
+
+/**
+ * Given two phi values with differing signs, calculate the approximate
+ * fraction along the edge connecting both where the zero lies. This
+ * function in particular also handles infinities well.
+ * @param my Phi value at the "current point" (corresponding to fraction 0).
+ * @param other Other phi value, correspondig to fraction 1.
+ * @return Fraction of the zero level-set intersection.
+ */
+double
+getZeroFraction (double my, double other)
+{
+ const bool myInf = lo_ieee_isinf (my);
+ const bool otherInf = lo_ieee_isinf (other);
+
+ if (myInf && otherInf)
+ return 0.5;
+ if (myInf)
+ return 1;
+ if (otherInf)
+ return 0;
+
+ return std::abs (my / (my - other));
+}
diff --git a/src/Utils.hpp b/src/Utils.hpp
new file mode 100644
index 0000000..0a7be2c
--- /dev/null
+++ b/src/Utils.hpp
@@ -0,0 +1,78 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014-2015 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+#ifndef UTILS_HPP
+#define UTILS_HPP
+
+/* Some basic utility routines shared between the .oct files. */
+
+#include "FastMarching.hpp"
+
+#include <octave/oct.h>
+
+#include <cassert>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+/**
+ * Check and possibly return dimensions of an array. If d1 and d2 are
+ * both given as positive numbers, check that the array has the given size.
+ * If one of them is negative (-1), return the dimension along this
+ * dimension and check only the other one. Throw if mismatch.
+ * @param var The array to check.
+ * @param d1 Row dimension.
+ * @param d2 Column dimension.
+ * @return Possibly the -1 dimension size.
+ */
+#define getDimension(var, d1, d2) \
+ getDimensionReal (var, #var, d1, d2)
+template<typename Arr>
+ unsigned getDimensionReal (const Arr& var, const std::string& name,
+ int d1, int d2);
+
+/**
+ * Convert an IndexTuple to an index suitable to Octave as the
+ * used type Array<octave_idx_type>.
+ * @param ind IndexTuple representing the coordinate.
+ * @return The same index suitable for Octave.
+ */
+Array<octave_idx_type> getOctaveIdx (const fastMarching::IndexTuple& ind);
+
+/**
+ * Convert a generic container to an Octave ColumnVector.
+ * @param cont Generic container (e. g., std::vector<double>).
+ * @return ColumnVector with the container's content.
+ */
+template<typename C>
+ ColumnVector convertToColumnVector (const C& cont);
+
+/**
+ * Given two phi values with differing signs, calculate the approximate
+ * fraction along the edge connecting both where the zero lies. This
+ * function in particular also handles infinities well.
+ * @param my Phi value at the "current point" (corresponding to fraction 0).
+ * @param other Other phi value, correspondig to fraction 1.
+ * @return Fraction of the zero level-set intersection.
+ */
+double getZeroFraction (double my, double other);
+
+/* Include template implementations. */
+#include "Utils.tpp"
+
+#endif /* Header guard. */
diff --git a/src/Utils.tpp b/src/Utils.tpp
new file mode 100644
index 0000000..713badd
--- /dev/null
+++ b/src/Utils.tpp
@@ -0,0 +1,83 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Template implementations of Utils.hpp. */
+
+/**
+ * Check and possibly return dimensions of an array. If d1 and d2 are
+ * both given as positive numbers, check that the array has the given size.
+ * If one of them is negative (-1), return the dimension along this
+ * dimension and check only the other one. Throw if mismatch.
+ * @param var The array to check.
+ * @param d1 Row dimension.
+ * @param d2 Column dimension.
+ * @return Possibly the -1 dimension size.
+ */
+template<typename Arr>
+ unsigned
+ getDimensionReal (const Arr& var, const std::string& name,
+ int d1, int d2)
+{
+ const dim_vector size = var.dims ();
+ if (size.length () > 2)
+ throw std::runtime_error ("Expected at most two dimensions for "
+ + name + ".");
+ assert (size.length () == 2);
+
+ const int expected[2] = {d1, d2};
+ unsigned res = 0;
+ bool foundRes = false;
+ for (unsigned i = 0; i < 2; ++i)
+ if (expected[i] < 0)
+ {
+ if (foundRes)
+ throw std::runtime_error ("Two dimensions unknown for " + name + ".");
+
+ foundRes = true;
+ res = size(i);
+ }
+ else if (expected[i] != size(i))
+ {
+ std::ostringstream msg;
+ msg << "Dimension mismatch for " << name
+ << " in dimension " << (i + 1) << ": ";
+ msg << "expected " << expected[i] << ", got " << size(i);
+ throw std::runtime_error (msg.str ());
+ }
+
+ return res;
+}
+
+/**
+ * Convert a generic container to an Octave ColumnVector.
+ * @param cont Generic container (e. g., std::vector<double>).
+ * @return ColumnVector with the container's content.
+ */
+template<typename C>
+ ColumnVector
+ convertToColumnVector (const C& cont)
+{
+ ColumnVector res(cont.size ());
+
+ unsigned ind = 0;
+ for (const auto& el : cont)
+ res(ind++) = el;
+ assert (ind == cont.size ());
+
+ return res;
+}
diff --git a/src/configure b/src/configure
new file mode 100755
index 0000000..80188d6
--- /dev/null
+++ b/src/configure
@@ -0,0 +1,3584 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for Octave-Forge level-set package 0.2.0.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf at gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='Octave-Forge level-set package'
+PACKAGE_TARNAME='octave-forge-level-set-package'
+PACKAGE_VERSION='0.2.0'
+PACKAGE_STRING='Octave-Forge level-set package 0.2.0'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+HAVE_CXX11
+OBJEXT
+EXEEXT
+ac_ct_CXX
+CPPFLAGS
+LDFLAGS
+CXXFLAGS
+CXX
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CXX
+CXXFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CCC'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures Octave-Forge level-set package 0.2.0 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root
+ [DATAROOTDIR/doc/octave-forge-level-set-package]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of Octave-Forge level-set package 0.2.0:";;
+ esac
+ cat <<\_ACEOF
+
+Some influential environment variables:
+ CXX C++ compiler command
+ CXXFLAGS C++ compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+Octave-Forge level-set package configure 0.2.0
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by Octave-Forge level-set package $as_me 0.2.0, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+ if test -n "$CCC"; then
+ CXX=$CCC
+ else
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CXX" && break
+ done
+fi
+if test -z "$CXX"; then
+ ac_ct_CXX=$CXX
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CXX"; then
+ ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CXX="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CXX" && break
+done
+
+ if test "x$ac_ct_CXX" = x; then
+ CXX="g++"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CXX=$ac_ct_CXX
+ fi
+fi
+
+ fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5
+$as_echo_n "checking whether the C++ compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C++ compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5
+$as_echo_n "checking for C++ compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GXX=yes
+else
+ GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+ ac_cxx_werror_flag=yes
+ ac_cv_prog_cxx_g=no
+ CXXFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_cv_prog_cxx_g=yes
+else
+ CXXFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+ CXXFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+ CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+ if test "$GXX" = yes; then
+ CXXFLAGS="-g -O2"
+ else
+ CXXFLAGS="-g"
+ fi
+else
+ if test "$GXX" = yes; then
+ CXXFLAGS="-O2"
+ else
+ CXXFLAGS=
+ fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+## autoconf-archive (Debian package name) must be installed
+
+ ax_cxx_compile_cxx11_required=true
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+ ac_success=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5
+$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; }
+if ${ax_cv_cxx_compile_cxx11+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ struct Base {
+ virtual void f() {}
+ };
+ struct Child : public Base {
+ virtual void f() override {}
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);
+
+ auto d = a;
+ auto l = [](){};
+
+ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this
+ namespace test_template_alias_sfinae {
+ struct foo {};
+
+ template<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() {
+ func<foo>(0);
+ }
+ }
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ax_cv_cxx_compile_cxx11=yes
+else
+ ax_cv_cxx_compile_cxx11=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5
+$as_echo "$ax_cv_cxx_compile_cxx11" >&6; }
+ if test x$ax_cv_cxx_compile_cxx11 = xyes; then
+ ac_success=yes
+ fi
+
+
+
+ if test x$ac_success = xno; then
+ for switch in -std=c++11 -std=c++0x; do
+ cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh`
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5
+$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; }
+if eval \${$cachevar+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS $switch"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ struct Base {
+ virtual void f() {}
+ };
+ struct Child : public Base {
+ virtual void f() override {}
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);
+
+ auto d = a;
+ auto l = [](){};
+
+ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this
+ namespace test_template_alias_sfinae {
+ struct foo {};
+
+ template<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() {
+ func<foo>(0);
+ }
+ }
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ eval $cachevar=yes
+else
+ eval $cachevar=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CXXFLAGS="$ac_save_CXXFLAGS"
+fi
+eval ac_res=\$$cachevar
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ if eval test x\$$cachevar = xyes; then
+ CXXFLAGS="$CXXFLAGS $switch"
+ ac_success=yes
+ break
+ fi
+ done
+ fi
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+ if test x$ax_cxx_compile_cxx11_required = xtrue; then
+ if test x$ac_success = xno; then
+ as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5
+ fi
+ else
+ if test x$ac_success = xno; then
+ HAVE_CXX11=0
+ { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5
+$as_echo "$as_me: No compiler with C++11 support was found" >&6;}
+ else
+ HAVE_CXX11=1
+
+$as_echo "#define HAVE_CXX11 1" >>confdefs.h
+
+ fi
+
+
+ fi
+
+
+ac_config_files="$ac_config_files Makefile"
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+:mline
+/\\$/{
+ N
+ s,\\\n,,
+ b mline
+}
+t clear
+:clear
+s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+ g
+ s/^\n//
+ s/\n/ /g
+ p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by Octave-Forge level-set package $as_me 0.2.0, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+Octave-Forge level-set package config.status 0.2.0
+configured by $0, generated by GNU Autoconf 2.69,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h | --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+
+eval set X " :F $CONFIG_FILES "
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+
+
+
+ esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/src/configure.ac b/src/configure.ac
new file mode 100644
index 0000000..98b6bc2
--- /dev/null
+++ b/src/configure.ac
@@ -0,0 +1,31 @@
+# GNU Octave level-set package.
+# Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+#
+# 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 program 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/>.
+
+# autoconf script for the level-set package.
+# This is based on configure.ac of the image package.
+
+AC_PREREQ([2.67])
+AC_INIT([Octave-Forge level-set package], [0.2.0])
+
+AC_PROG_CXX
+AC_LANG(C++)
+
+## autoconf-archive (Debian package name) must be installed
+AX_CXX_COMPILE_STDCXX_11(noext)
+
+AC_CONFIG_FILES([Makefile])
+
+AC_OUTPUT
diff --git a/src/geomBoundary.cpp b/src/geomBoundary.cpp
new file mode 100644
index 0000000..c5c970f
--- /dev/null
+++ b/src/geomBoundary.cpp
@@ -0,0 +1,376 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Calculate boundary and intersection-point related stuff of the information
+ returned by ls_find_geometry. */
+
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/ov-struct.h>
+#include <octave/lo-ieee.h>
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <stdexcept>
+#include <utility>
+
+/* ************************************************************************** */
+/* IntersectionPoint class. */
+
+/**
+ * Encapsulate an intersection point.
+ */
+class IntersectionPoint
+{
+
+public:
+
+ /** Type used as index into map to uniquely identify the point. */
+ typedef std::pair<unsigned, unsigned> keyT;
+
+ /**
+ * Type of this intersection point in terms of how the edge it lies on
+ * is oriented. This is used internally for some decisions.
+ */
+ enum Type
+ {
+ WESTIN_EASTOUT,
+ EASTIN_WESTOUT,
+ SOUTHIN_NORTHOUT,
+ NORTHIN_SOUTHOUT
+ };
+
+private:
+
+ /** Index of defining inside point. */
+ unsigned inInd;
+ /** Index of defining outside point. */
+ unsigned outInd;
+
+ /** Type to describe a boundary element edge this lies on. */
+ typedef std::pair<unsigned, unsigned> edgeT;
+ /** Edges this point lies on (should be two in the end). */
+ std::vector<edgeT> edges;
+
+ /** Type of the intersection point. */
+ Type type;
+
+ /** Edge fraction value. */
+ double frac;
+
+ /** x coordinate wrt inner point. */
+ double coordX;
+ /** y coordinate wrt inner point. */
+ double coordY;
+
+ /**
+ * Store index of this intersection point in the ordering we want to
+ * use in the end.
+ */
+ int index;
+
+public:
+
+ /**
+ * Construct the intersection point, given inside- and outside point
+ * defining it as well as the other parameters.
+ * @param in Inside point index.
+ * @param out Outside point index.
+ * @param L Rows of grid.
+ * @param M Columns of grid.
+ * @param phi Level-set function.
+ * @param h Grid spacing.
+ * @param fracTol Tolerance for pushing points away from nodes.
+ */
+ IntersectionPoint (unsigned in, unsigned out, unsigned L, unsigned M,
+ const Matrix& phi, double h, double fracTol);
+
+ // No default constructor, copying is ok.
+ IntersectionPoint () = delete;
+ IntersectionPoint (const IntersectionPoint&) = default;
+ IntersectionPoint& operator= (const IntersectionPoint&) = default;
+
+ /**
+ * Retrieve the map key for this point.
+ * @return Key uniquely identifying this point.
+ */
+ inline keyT
+ getKey () const
+ {
+ return std::make_pair (inInd, outInd);
+ }
+
+ /**
+ * Add an edge this point lies on to the list.
+ * @param el The bdryelement index.
+ * @param ed The edge number.
+ */
+ inline void
+ addEdge (unsigned el, unsigned ed)
+ {
+ assert (ed <= 3);
+ edges.push_back (edgeT (el, ed));
+ }
+
+ /**
+ * Get type of this intersection point.
+ * @return This intersection point's type.
+ */
+ inline Type
+ getType () const
+ {
+ return type;
+ }
+
+ /**
+ * Set this intersection point's index.
+ * @param ind New index.
+ */
+ inline void
+ setIndex (unsigned ind)
+ {
+ assert (index == -1);
+ index = ind;
+ }
+
+ /**
+ * Fill in the bdryel.edges row corresponding to this point.
+ * @param mat Matrix to fill into.
+ */
+ inline void
+ fillEdges (Matrix& mat) const
+ {
+ if (edges.size () != 2)
+ throw std::runtime_error ("boundary cuts through hold-all domain");
+
+ assert (index >= 0);
+ for (const auto& e : edges)
+ {
+ assert (lo_ieee_is_NA (mat(e.first, e.second)));
+ mat(e.first, e.second) = index + 1;
+ }
+ }
+
+ /**
+ * Fill in the inout matrix row.
+ * @param mat Matrix to fill in.
+ */
+ inline void
+ fillInOut (Matrix& mat) const
+ {
+ assert (index >= 0);
+ mat(index, 0) = inInd + 1;
+ mat(index, 1) = outInd + 1;
+ }
+
+ /**
+ * Fill in the frac entry.
+ * @param vec Vector to fill in.
+ */
+ inline void
+ fillFrac (ColumnVector& vec) const
+ {
+ assert (index >= 0);
+ vec(index) = frac;
+ }
+
+ /**
+ * Fill in the coord_in_orig matrix row.
+ * @param mat Matrix to fill in.
+ */
+ inline void
+ fillInCoord (Matrix& mat) const
+ {
+ assert (index >= 0);
+ mat(index, 0) = coordX;
+ mat(index, 1) = coordY;
+ }
+
+};
+
+/* Define constructor. */
+IntersectionPoint::IntersectionPoint (unsigned in, unsigned out,
+ unsigned L, unsigned,
+ const Matrix& phi,
+ double h, double fracTol)
+ : inInd(in), outInd(out), edges(), index(-1)
+{
+ assert (phi(inInd) < 0.0 && phi(outInd) > 0.0);
+
+ const unsigned diff
+ = std::abs (static_cast<int> (in) - static_cast<int> (out));
+ if (diff == L)
+ {
+ if (in < out)
+ type = WESTIN_EASTOUT;
+ else
+ type = EASTIN_WESTOUT;
+ }
+ else
+ {
+ assert (diff == 1);
+ if (in < out)
+ type = NORTHIN_SOUTHOUT;
+ else
+ type = SOUTHIN_NORTHOUT;
+ }
+
+ /* Calculate intersection position on the boundary edge. Make sure to
+ avoid a total collapse of the triangles if phi takes infinite values
+ at some points. */
+ frac = getZeroFraction (phi(inInd), phi(outInd));
+ assert (frac >= 0.0 && frac <= 1.0);
+ if (frac < fracTol)
+ frac = fracTol;
+ else if (frac > 1.0 - fracTol)
+ frac = 1.0 - fracTol;
+
+ switch (type)
+ {
+ case WESTIN_EASTOUT:
+ coordX = frac * h;
+ coordY = 0.0;
+ break;
+
+ case EASTIN_WESTOUT:
+ coordX = -frac * h;
+ coordY = 0.0;
+ break;
+
+ case NORTHIN_SOUTHOUT:
+ coordX = 0.0;
+ coordY = frac * h;
+ break;
+
+ case SOUTHIN_NORTHOUT:
+ coordX = 0.0;
+ coordY = -frac * h;
+ break;
+
+ default:
+ assert (false);
+ }
+}
+
+/* ************************************************************************** */
+
+/** Map of intersection points. */
+typedef std::map<IntersectionPoint::keyT, IntersectionPoint> isptMap;
+
+/* C++ implementation. */
+DEFUN_DLD (__levelset_geomBoundary, args, nargout,
+ " [ISPTS, EDGES] = geomBoundary (PHI, H, FRACTOL, BDRYINDX, NODELIST)\n\n"
+ "Calculate geometrical properties of the boundary for the geometry\n"
+ "described by PHI and with grid-width H. BDRYINDX should be a vector\n"
+ "of indices of boundary elements, as per elem.index.bdry, which must\n"
+ "already be known. NODELIST should be elem.nodelist.\n\n"
+ "ISPTS returns the intersectpts structure of the geometry, and EDGES\n"
+ "contains bdryel.edges, which can be calculated as a by-product.\n")
+{
+ try
+ {
+ if (args.length () != 5 || nargout > 2)
+ throw std::runtime_error ("invalid argument counts");
+
+ /* Get argument and retrieve dimensions. */
+ const Matrix phi = args(0).matrix_value ();
+ const double h = args(1).double_value ();
+ const double fracTol = args(2).double_value ();
+ const ColumnVector bdryindx = args(3).column_vector_value ();
+ const Matrix nodelist = args(4).matrix_value ();
+
+ /* Check all the dimensions. */
+ const dim_vector dims = phi.dims ();
+ const unsigned L = dims(0);
+ const unsigned M = dims(1);
+ const unsigned nBdry = getDimension (bdryindx, -1, 1);
+ getDimension (nodelist, -1, 4);
+
+ /* Build up the list of intersection points. */
+ isptMap ispts;
+ unsigned nextInd = 0;
+ for (unsigned i = 0; i < nBdry; ++i)
+ {
+ const unsigned idx = bdryindx(i) - 1;
+ static const unsigned EDGES[4][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}};
+ for (unsigned j = 0; j < 4; ++j)
+ {
+ unsigned pt1 = nodelist(idx, EDGES[j][0]) - 1;
+ unsigned pt2 = nodelist(idx, EDGES[j][1]) - 1;
+ if (phi(pt1) > phi(pt2))
+ std::swap (pt1, pt2);
+
+ if (phi(pt1) * phi(pt2) < 0.0)
+ {
+ IntersectionPoint p(pt1, pt2, L, M, phi, h, fracTol);
+ auto iter = ispts.find (p.getKey ());
+ if (iter == ispts.end ())
+ {
+ p.setIndex (nextInd++);
+ const auto pair = std::make_pair (p.getKey (), p);
+ const auto res = ispts.insert (pair);
+ assert (res.second);
+ iter = res.first;
+ }
+
+ iter->second.addEdge (i, j);
+ }
+ else
+ assert ((phi(pt1) > 0.0 && phi(pt2) > 0.0)
+ || (phi(pt1) < 0.0 && phi(pt2) < 0.0));
+ }
+ }
+ assert (nextInd == ispts.size ());
+
+ /* Build Octave matrices from the intersection points. */
+ Matrix edges(nBdry, 4);
+ edges.fill (octave_NA);
+ const unsigned nIspts = ispts.size ();
+ Matrix inout(nIspts, 2);
+ ColumnVector frac(nIspts);
+ Matrix incoord(nIspts, 2);
+ for (const auto& p : ispts)
+ {
+ p.second.fillEdges (edges);
+ p.second.fillInOut (inout);
+ p.second.fillFrac (frac);
+ p.second.fillInCoord (incoord);
+ }
+
+ /* Build result structure. */
+ octave_scalar_map st;
+ st.assign ("n", nIspts);
+ st.assign ("inout", inout);
+ st.assign ("frac", frac);
+ st.assign ("incoord", incoord);
+
+ /* Return. */
+ octave_value_list res;
+ res(0) = st;
+ res(1) = edges;
+
+ return res;
+ }
+ catch (const std::runtime_error& exc)
+ {
+ error (exc.what ());
+ return octave_value_list ();
+ }
+}
diff --git a/src/geomElements.cpp b/src/geomElements.cpp
new file mode 100644
index 0000000..7c8a1f4
--- /dev/null
+++ b/src/geomElements.cpp
@@ -0,0 +1,130 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Calculate the elements-related things of the information returned
+ by ls_find_geometry. */
+
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/ov-struct.h>
+
+#include <cassert>
+#include <vector>
+#include <stdexcept>
+
+/** Vector of indices used for the *indx fields. */
+typedef std::vector<unsigned> indexArr;
+
+/* C++ implementation. */
+DEFUN_DLD (__levelset_geomElements, args, nargout,
+ " ELEMENTS = geomElements (PHI)\n\n"
+ "Construct ELEM structure as per ls_find_geometry for the given level-set\n"
+ "function PHI. PHI must not contain values which are exactly zero.\n")
+{
+ try
+ {
+ if (args.length () != 1 || nargout > 1)
+ throw std::runtime_error ("invalid argument counts");
+
+ /* Get argument. */
+ const Matrix phi = args(0).matrix_value ();
+
+ /* Extract the dimensions. */
+ const dim_vector dims = phi.dims ();
+ const unsigned L = dims(0);
+ const unsigned M = dims(1);
+
+ /* Go over all elements to build the nodelist. */
+ const unsigned nElem = (L - 1) * (M - 1);
+ Matrix nodelist(nElem, 4);
+ for (unsigned row = 0; row < L - 1; ++row)
+ for (unsigned col = 0; col < M - 1; ++col)
+ {
+ const unsigned elInd = col * (L - 1) + row;
+
+ /* The element nodes are stored in order SE, SW, NW, NE. */
+#define STORE_NODELIST(num, dr, dc) \
+ nodelist(elInd, num) = (col + (dc)) * L + (row + (dr)) + 1
+ STORE_NODELIST(0, 1, 1);
+ STORE_NODELIST(1, 1, 0);
+ STORE_NODELIST(2, 0, 0);
+ STORE_NODELIST(3, 0, 1);
+#undef STORE_NODELIST
+ }
+
+ /* Find outer/inner/bdry elements. */
+ indexArr outindx, inindx, bdryindx;
+ ColumnVector type(nElem);
+ for (unsigned i = 0; i < (L - 1) * (M - 1); ++i)
+ {
+ bool hasOut = false;
+ bool hasIn = false;
+ for (unsigned j = 0; j < 4; ++j)
+ {
+ const unsigned nodeInd = nodelist(i, j) - 1;
+ if (phi(nodeInd) > 0.0)
+ hasOut = true;
+ else
+ {
+ assert (phi(nodeInd) < 0.0);
+ hasIn = true;
+ }
+ }
+
+ if (hasOut && hasIn)
+ {
+ type(i) = 0.0;
+ bdryindx.push_back (i + 1);
+ }
+ else if (hasOut)
+ {
+ type(i) = 1.0;
+ outindx.push_back (i + 1);
+ }
+ else
+ {
+ assert (hasIn);
+ type(i) = -1.0;
+ inindx.push_back (i + 1);
+ }
+ }
+
+ /* Build result structure. */
+ octave_scalar_map indx;
+ indx.assign ("inner", convertToColumnVector (inindx));
+ indx.assign ("outer", convertToColumnVector (outindx));
+ indx.assign ("bdry", convertToColumnVector (bdryindx));
+ octave_scalar_map st;
+ st.assign ("n", nElem);
+ st.assign ("nodelist", nodelist);
+ st.assign ("type", type);
+ st.assign ("index", indx);
+
+ /* Return. */
+ octave_value_list res;
+ res(0) = st;
+
+ return res;
+ }
+ catch (const std::runtime_error& exc)
+ {
+ error (exc.what ());
+ return octave_value_list ();
+ }
+}
diff --git a/src/geomGamma.cpp b/src/geomGamma.cpp
new file mode 100644
index 0000000..032f717
--- /dev/null
+++ b/src/geomGamma.cpp
@@ -0,0 +1,378 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Calculate the gamma chaining information returned by ls_find_geometry. */
+
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/Cell.h>
+#include <octave/lo-ieee.h>
+#include <octave/ov-struct.h>
+
+#include <cassert>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+/* Type used for building up intersection-point chains. */
+typedef std::vector<unsigned> IndexChain;
+/* Type for indexing a (boundary element, edge) value. */
+typedef std::pair<unsigned, unsigned> EdgeIndex;
+
+/* ************************************************************************** */
+
+/**
+ * Simple utility class for the information collected with the boundary
+ * element segments. This class contains all data relevant for one segment,
+ * and can convert it to the Octave struct in the end.
+ */
+class BdryelSegmentEntry
+{
+
+private:
+
+ /* Start point and edge. Already 1-based. */
+ unsigned startPt, startEdge;
+ /* End point and edge. Already 1-based. */
+ unsigned endPt, endEdge;
+
+ /** The enclosed inner points. Already 1-based indices. */
+ IndexChain inners;
+
+public:
+
+ /**
+ * Construct it with set "empty" values. This is necessary because
+ * we want to use this class later in a vector for collecting the
+ * different segments of a single boundary element.
+ */
+ inline BdryelSegmentEntry ()
+ : startPt(0), startEdge(0), endPt(0), endEdge(0), inners()
+ {}
+
+ // Copying and assignment per default.
+ BdryelSegmentEntry (const BdryelSegmentEntry&) = default;
+ BdryelSegmentEntry& operator= (const BdryelSegmentEntry&) = default;
+
+ // Initialise start and end points (p and e are 0-based).
+
+ inline void
+ setStart (unsigned p, unsigned e)
+ {
+ assert (startPt == 0 && startEdge == 0);
+ assert (e < 4);
+ startPt = p + 1;
+ startEdge = e + 1;
+ }
+
+ inline void
+ setEnd (unsigned p, unsigned e)
+ {
+ assert (endPt == 0 && endEdge == 0);
+ assert (e < 4);
+ endPt = p + 1;
+
+ /* Note that the edge stored for a point is always the one in the
+ boundary element in which the point *starts* an edge. Thus for
+ the end point, we have to take the opposite edge, since we are
+ interested in the edge relative to the *other* boundary element
+ (where p is the end point). */
+ endEdge = (e + 2) % 4 + 1;
+ }
+
+ /**
+ * Add a new inner point.
+ */
+ inline void
+ addInner (unsigned p)
+ {
+ inners.push_back (p + 1);
+ }
+
+ /**
+ * Fill in the ispts.onedge structure entries represented by this segment.
+ * @param onedge The onedge array to fill in.
+ * @param bdryel The boundary element index this segment is part of.
+ */
+ void fillOnEdge (NDArray& onedge, unsigned bdryel) const;
+
+ /**
+ * Finalise and convert to Octave structure.
+ * @return Resulting Octave structure value.
+ */
+ octave_scalar_map toStruct () const;
+
+};
+
+/**
+ * Fill in the ispts.onedge structure entries represented by this segment.
+ * @param onedge The onedge array to fill in.
+ * @param bdryel The boundary element index this segment is part of.
+ */
+void
+BdryelSegmentEntry::fillOnEdge (NDArray& onedge, unsigned bdryel) const
+{
+ assert (startPt > 0 && endPt > 0);
+ assert (startEdge >= 1 && startEdge <= 4);
+ assert (endEdge >= 1 && endEdge <= 4);
+
+#define FILL_CHECK_NA(lhs, rhs) \
+ if (!lo_ieee_is_NA (lhs)) \
+ throw std::runtime_error ("onedge already filled in"); \
+ lhs = (rhs);
+
+ FILL_CHECK_NA (onedge(startPt - 1, 0, 0), bdryel + 1)
+ FILL_CHECK_NA (onedge(startPt - 1, 0, 1), startEdge)
+ FILL_CHECK_NA (onedge(endPt - 1, 1, 0), bdryel + 1)
+ FILL_CHECK_NA (onedge(endPt - 1, 1, 1), endEdge)
+
+#undef FILL_CHECK_NA
+}
+
+/**
+ * Finalise and convert to Octave structure.
+ * @return Resulting Octave structure value.
+ */
+octave_scalar_map
+BdryelSegmentEntry::toStruct () const
+{
+ assert (startPt > 0 && endPt > 0);
+ assert (startEdge >= 1 && startEdge <= 4);
+ assert (endEdge >= 1 && endEdge <= 4);
+ assert (inners.size () >= 1 && inners.size () <= 3);
+
+ octave_scalar_map res;
+ res.assign ("startPt", startPt);
+ res.assign ("startEdge", startEdge);
+ res.assign ("endPt", endPt);
+ res.assign ("endEdge", endEdge);
+ res.assign ("inners", convertToColumnVector (inners));
+
+ return res;
+}
+
+/* ************************************************************************** */
+
+/* C++ implementation. */
+DEFUN_DLD (__levelset_geomGamma, args, nargout,
+ " [COMPS, ISPTONEDGE, BDRLELSG]\n"
+ " = geomGamma (PHI, NODELIST, BDRYIND, EDGES, INOUT)\n\n"
+ "Internal routine to construct the gamma component chains for the\n"
+ "given information. The arguments should already be calculated by\n"
+ "the other geometry routines.\n\n"
+ "Returned are the components cell array, the intersection points' onedge\n"
+ "array as well as the structure to be kept internally for mesh-building.\n")
+{
+ try
+ {
+ if (args.length () != 5 || nargout > 3)
+ throw std::runtime_error ("invalid argument counts");
+
+ /* Get arguments. */
+ const Matrix phi = args(0).matrix_value ();
+ const Matrix nodelist = args(1).matrix_value ();
+ const ColumnVector bdryInd = args(2).column_vector_value ();
+ const Matrix edges = args(3).matrix_value ();
+ const Matrix inout = args(4).matrix_value ();
+
+ /* Extract and check the dimensions. */
+ const unsigned nNodes = phi.nelem ();
+ const unsigned nElem = getDimension (nodelist, -1, 4);
+ const unsigned nBdryEl = getDimension (bdryInd, -1, 1);
+ getDimension (edges, nBdryEl, 4);
+ const unsigned nIspts = getDimension (inout, -1, 2);
+
+ /* Each intersection point should occur in the "edges" array of exactly
+ two boundary elements. One of these can be used to "continue"
+ following the chain, in order to satisfy our ordering requirement
+ (interior of the domain to the left in coordinate interpretation).
+
+ For each intersection point, find the corresponding boundary element
+ and edge index for which this is the case and store this information
+ in a map. It is a map and not a vector, since we will be removing
+ intersection points as we add them to chains. */
+
+ std::map<unsigned, EdgeIndex> isptStartElem;
+ for (unsigned i = 0; i < nBdryEl; ++i)
+ for (unsigned j = 0; j < 4; ++j)
+ if (!lo_ieee_is_NA (edges(i, j)))
+ {
+ if (edges(i, j) < 1 || edges(i, j) > nIspts)
+ throw std::out_of_range ("edges entry out of bounds");
+ const unsigned ispt = edges(i, j) - 1;
+ assert (ispt < nIspts);
+
+ if (bdryInd(i) < 1 || bdryInd(i) > nElem)
+ throw std::out_of_range ("element index out of bounds");
+ const unsigned elem = bdryInd(i) - 1;
+ assert (elem < nElem);
+
+ /* See if the intersection point's inner node matches the
+ element's nodelist entry at the correct side of the edge.
+ For a N edge intersected, we have a valid "starting point"
+ if the inner node is at NE (in coordinate grid interpretation).
+ Given the orderings of nodelist and edges, this means
+ that the "correct" point is at the same index in nodelist
+ as the edge is in edges. */
+ const unsigned inpt = inout(ispt, 0);
+ if (inpt == nodelist(elem, j))
+ {
+ if (isptStartElem.find (ispt) != isptStartElem.end ())
+ throw std::runtime_error ("multiple starts found for ispt");
+
+ const EdgeIndex entry(i, j);
+ isptStartElem.insert (std::make_pair (ispt, entry));
+ }
+ }
+ if (isptStartElem.size () != nIspts)
+ throw std::runtime_error ("wrong number of starting points found");
+
+ /* Now, build up the result. We start at an arbitrary unused
+ intersection point, and follow the chain until we're back at
+ it. This is done until no more intersection points are available. */
+
+ std::vector<IndexChain> chains;
+ std::vector<std::vector<BdryelSegmentEntry>> bdryelSegs(nBdryEl);
+
+ while (!isptStartElem.empty ())
+ {
+ const auto startIter = isptStartElem.begin ();
+ const auto startEntry = *startIter;
+ isptStartElem.erase (startIter);
+
+ IndexChain curChain;
+ std::pair<unsigned, EdgeIndex> curEntry = startEntry;
+ while (true)
+ {
+ /* Add to chain. Take care of 1-based indices for Octave. */
+ curChain.push_back (curEntry.first + 1);
+
+ /* Find the next edge with an intersection point. We get the
+ correct handling of narrow pairs by trying to make the
+ enclosed area "as small as possible". I. e., we turn as much
+ "left" as possible, until we find an intersection point. This
+ means that we have to traverse the edges in *reverse*
+ direction (since increasing indices are counter-clockwise
+ in the coordinate grid interpretation).
+
+ With this choice of ordering, it should also always
+ be the case that points over which we step are inner
+ points only. */
+
+ const unsigned bdryEl = curEntry.second.first;
+ const unsigned fullEl = bdryInd(bdryEl) - 1;
+ const unsigned edge = curEntry.second.second;
+ assert (bdryEl < nBdryEl && edge < 4);
+ assert (edges(bdryEl, edge) == curEntry.first + 1);
+
+ BdryelSegmentEntry seg;
+ seg.setStart (curEntry.first, edge);
+
+ unsigned nextPt = nIspts;
+ for (unsigned i = edge + 3; i != edge; --i)
+ {
+ const unsigned innerPtInd = (i + 1) % 4;
+ const unsigned innerPt = nodelist(fullEl, innerPtInd) - 1;
+ if (innerPt >= nNodes)
+ throw std::runtime_error ("nodelist entry out-of-bounds");
+ if (phi(innerPt) >= 0.0)
+ throw std::runtime_error ("innerPt is not actually inside");
+ assert (i != edge + 3
+ || innerPt == inout(curEntry.first) - 1);
+ seg.addInner (innerPt);
+
+ if (!lo_ieee_is_NA (edges(bdryEl, i % 4)))
+ {
+ nextPt = edges(bdryEl, i % 4) - 1;
+ break;
+ }
+ }
+ assert (nextPt <= nIspts);
+ if (nextPt == nIspts)
+ throw std::runtime_error ("no next ispt found");
+
+ /* If we have a closed loop, finish it. We have to finish
+ the boundary element segment, too. */
+ if (nextPt == startEntry.first)
+ {
+ seg.setEnd (nextPt, startEntry.second.second);
+ bdryelSegs[bdryEl].push_back (seg);
+ break;
+ }
+
+ /* Do some book keeping before adding it to the chain at
+ the beginning of the next iteration. */
+ const auto nextIter = isptStartElem.find (nextPt);
+ if (nextIter == isptStartElem.end ())
+ throw std::runtime_error ("next point is not available");
+ curEntry = *nextIter;
+ isptStartElem.erase (nextIter);
+
+ /* Finish the boundary element segment. */
+ seg.setEnd (nextPt, curEntry.second.second);
+ bdryelSegs[bdryEl].push_back (seg);
+ }
+
+ if (curChain.size () < 4)
+ throw std::runtime_error ("too short gamma chain found");
+ chains.push_back (curChain);
+ }
+
+ /* Build up the return value as a cell-array. */
+ Cell c(chains.size (), 1);
+ for (unsigned i = 0; i < chains.size (); ++i)
+ c(i) = convertToColumnVector (chains[i]);
+
+ /* Build the intersection points' "onedge" array. */
+ dim_vector dims{static_cast<octave_idx_type> (nIspts), 2, 3};
+ NDArray onedge(dims);
+ onedge.fill (octave_NA);
+ assert (bdryelSegs.size () == nBdryEl);
+ for (unsigned i = 0; i < nBdryEl; ++i)
+ for (const auto& s : bdryelSegs[i])
+ s.fillOnEdge (onedge, i);
+
+ /* Build boundary element segments as cell-array. */
+ Cell bs(nBdryEl, 1);
+ for (unsigned i = 0; i < nBdryEl; ++i)
+ {
+ const unsigned nSeg = bdryelSegs[i].size ();
+ assert (nSeg == 1 || nSeg == 2);
+
+ Cell e(nSeg, 1);
+ for (unsigned j = 0; j < nSeg; ++j)
+ e(j) = bdryelSegs[i][j].toStruct ();
+
+ bs(i) = e;
+ }
+
+ /* Return. */
+ octave_value_list res;
+ res(0) = c;
+ res(1) = onedge;
+ res(2) = bs;
+
+ return res;
+ }
+ catch (const std::runtime_error& exc)
+ {
+ error (exc.what ());
+ return octave_value_list ();
+ }
+}
diff --git a/src/heapsort.cpp b/src/heapsort.cpp
new file mode 100644
index 0000000..ebebf19
--- /dev/null
+++ b/src/heapsort.cpp
@@ -0,0 +1,87 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2013-2015 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Test program for heap, doing a simple heap sort of random numbers. */
+
+#include "Heap.hpp"
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+using namespace fastMarching;
+
+namespace fastMarching
+{
+
+/* Provide printf-based implementation of issueWarning. */
+void
+issueWarning (const std::string&, const std::string& fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ vprintf (fmt.c_str (), args);
+ va_end (args);
+}
+
+} // namespace fastMarching
+
+/**
+ * Comparison routine used. Simply compare doubles.
+ * @param a First value.
+ * @param b Second value.
+ */
+inline bool
+compare (double a, double b)
+{
+ return a < b;
+}
+
+/** Type of heap. */
+typedef Heap<double, decltype (&compare)> heapT;
+
+/**
+ * Main routine.
+ * @return Exit code 0.
+ */
+int
+main ()
+{
+ heapT heap(&compare);
+
+ for (int i = 0; i < 40000; ++i)
+ {
+ const double cur = static_cast<double> (std::rand ());
+ heap.push (cur / RAND_MAX);
+ }
+
+ double last = **heap.top ();
+ heap.pop ();
+
+ while (!heap.empty ())
+ {
+ const double cur = **heap.top ();
+ heap.pop ();
+
+ if (cur > last)
+ printf ("%f vs %f!\n", cur, last);
+ last = cur;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/internal_fastmarching.cpp b/src/internal_fastmarching.cpp
new file mode 100644
index 0000000..543e6dd
--- /dev/null
+++ b/src/internal_fastmarching.cpp
@@ -0,0 +1,131 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2013-2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Interfacing the C++ fast marching code to Octave. This is not the actual
+ Octave interface, but the routine performing the work. The real Octave
+ function is written as .m file and performs some more preparation work
+ on the arguments before calling in here. */
+
+#include "FastMarching.hpp"
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+
+#include <cassert>
+
+using namespace fastMarching;
+
+/* Octave routine interfacing to fast marching code. */
+DEFUN_DLD (__levelset_internal_fastmarching, args, nargout,
+ "[U, G, REACHED] = internal_fastmarching (DOMAIN, U0, G0, ALIVE, F)\n\n"
+ "Internal routine for fastmarching. DOMAIN, U0, ALIVE and F must all be\n"
+ "arrays of the same dimension and size, specifying the grid. The solution\n"
+ "is returned in the array U, giving the final distance at each grid point.\n"
+ "REACHED is boolean array storing for each point whether it is actually\n"
+ "reached by the method in finite distance from some point that is\n"
+ "initially alive.\n\n"
+ "DOMAIN and ALIVE are boolean arrays defining whether some point belongs\n"
+ "to the domain (holes in the domain are allowed and treated as absolutely\n"
+ "unreachable and untraversable regions), and whether the point belongs to\n"
+ "the initially alive set. For those points, U0 is assumed to contain\n"
+ "the initialisation distance.\n")
+{
+ try
+ {
+ if (args.length () != 5 || nargout != 3)
+ throw std::invalid_argument ("invalid argument counts");
+
+ const boolNDArray domain = args(0).bool_array_value ();
+ const NDArray u0 = args(1).array_value ();
+ const NDArray g0 = args(2).array_value ();
+ const boolNDArray alive = args(3).bool_array_value ();
+ const NDArray f = args(4).array_value ();
+
+ const dim_vector size = domain.dims ();
+ const dimensionT D = size.length ();
+
+ if (size != u0.dims () || size != g0.dims ()
+ || size != alive.dims () || size != f.dims ())
+ throw std::invalid_argument ("argument dimensions mismatch");
+
+ IndexTuple gridSize(D);
+ for (dimensionT i = 0; i < D; ++i)
+ gridSize[i] = size(i);
+
+ Grid grid(gridSize);
+
+ const auto fillIn
+ = [D, &domain, &u0, &g0, &alive, &f, &grid] (const IndexTuple& c)
+ {
+ const Array<octave_idx_type> idx = getOctaveIdx (c);
+ assert (c.size () == D
+ && static_cast<dimensionT> (idx.length ()) == D);
+
+ if (domain(idx))
+ {
+ Entry* e;
+ if (alive(idx))
+ e = Entry::newAlive (c, u0(idx), g0(idx));
+ else
+ e = Entry::newFarAway (c, f(idx));
+
+ grid.setInitial (e);
+ }
+ };
+ grid.iterate (fillIn);
+
+ grid.perform ();
+
+ NDArray u(size);
+ NDArray g(size);
+ boolNDArray reached(size);
+ const auto getOut
+ = [D, &grid, &u, &g, &reached] (const IndexTuple& c)
+ {
+ const Array<octave_idx_type> idx = getOctaveIdx (c);
+ assert (c.size () == D
+ && static_cast<dimensionT> (idx.length ()) == D);
+
+ const Grid& constGrid(grid);
+ const Entry* e = constGrid.get (c);
+
+ if (e)
+ {
+ const realT dist = e->getDistance ();
+ reached(idx) = (dist != LARGE_DIST);
+ u(idx) = dist;
+ g(idx) = e->getFunction ();
+ }
+ else
+ reached(idx) = false;
+ };
+ grid.iterate (getOut);
+
+ octave_value_list res;
+ res(0) = u;
+ res(1) = g;
+ res(2) = reached;
+
+ return res;
+ }
+ catch (const std::exception& exc)
+ {
+ error (exc.what ());
+ return octave_value_list ();
+ }
+}
diff --git a/src/internal_init_narrowband.cpp b/src/internal_init_narrowband.cpp
new file mode 100644
index 0000000..e103e47
--- /dev/null
+++ b/src/internal_init_narrowband.cpp
@@ -0,0 +1,145 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Given a level-set function, initialise the narrow-band distances. The
+ code here does the internal C++ implementation for better performance,
+ and is used from the "public" .m file including some more error and
+ argument checks as well as test and demo routines. */
+
+#include "FastMarching.hpp"
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/lo-ieee.h>
+
+#include <cmath>
+
+using namespace fastMarching;
+
+/* Fraction below which we consider a point exactly on the boundary. This
+ is necessary to prevent underflows from happening for the h's passed
+ to the update equation solver.
+
+ Should this be configurable? But note that this value is just necessary
+ to prevent underflows. The fracTol in the geometry routine, on the other
+ hand, influences the resulting geometry and further constructed triangle
+ meshes. (In particular, how distorted triangles may get.) Thus it makes
+ sense to allow configuration there. */
+static const double MIN_FRAC = 1e-16;
+
+/* Octave routine interfacing to .m file code. */
+DEFUN_DLD (__levelset_internal_init_narrowband, args, nargout,
+ " D = internal_init_narrowband (PHI, H)\n\n"
+ "Internal routine for ls_init_narrowband. Given the level-set function\n"
+ "PHI on some array with grid-spacing H, calculate the distances from the\n"
+ "zero level-set of all grid-points next to it ('narrow band') and return\n"
+ "them in D. All distances are positive, so the sign of PHI is not\n"
+ "considered to differentiate between inside and outside. Entries not\n"
+ "in the narrow band will be set to NA.\n")
+{
+ try
+ {
+ if (args.length () != 2 || nargout != 1)
+ throw std::invalid_argument ("invalid argument counts");
+
+ const NDArray phi = args(0).array_value ();
+ const double h = args(1).double_value ();
+
+ const dim_vector size = phi.dims ();
+ const dimensionT D = size.length ();
+
+ IndexTuple gridSize(D);
+ for (dimensionT i = 0; i < D; ++i)
+ gridSize[i] = size(i);
+
+ NDArray dists(size);
+ dists.fill (octave_NA);
+
+ /* Now iterate over the grid. For each cell, check all links to
+ neighbours that are still on the grid. If phi changes sign
+ along one of those links, we are in the narrow band. In this
+ case, build up a fast marching update equation from all of those
+ links, and solve it to get our distance. We assume a linear
+ model for phi in order to find the "exact" point on the
+ boundary along the intersected edge. */
+
+ Grid grid(gridSize);
+ const auto doIt
+ = [h, &phi, &dists, &grid] (const IndexTuple& coord)
+ {
+ const Array<octave_idx_type> idx = getOctaveIdx (coord);
+ const double myPhi = phi(idx);
+
+ /* Handle the special case of phi exactly zero here. */
+ if (myPhi == 0.0)
+ {
+ dists(idx) = 0.0;
+ return;
+ }
+
+ UpdateEquation eqn;
+ bool isNarrowBand = false;
+
+ const auto checkLink
+ = [h, myPhi, &eqn, &isNarrowBand, &phi, &dists, &idx]
+ (const IndexTuple& neighbour, dimensionT d, indexT)
+ {
+ const double nPhi = phi(getOctaveIdx (neighbour));
+
+ /* The special case of phi exactly zero at the neighbour
+ is handled as if there were no intersection at all. */
+ if (nPhi == 0.0)
+ return;
+
+ if (nPhi * myPhi < 0.0)
+ {
+ const double frac = getZeroFraction (myPhi, nPhi);
+ assert (lo_ieee_finite (frac));
+
+ /* If the neighbour value is infinite leading to a frac
+ of zero, this point is exactly on the boundary. Thus
+ set distance to zero and we're done. */
+ if (std::abs (frac) < MIN_FRAC)
+ {
+ isNarrowBand = false;
+ dists(idx) = 0.0;
+ return;
+ }
+
+ isNarrowBand = true;
+ eqn.add (d, frac * h, 0.0);
+ }
+ };
+ grid.iterateNeighbours (coord, checkLink);
+
+ if (isNarrowBand)
+ dists(idx) = eqn.solve (1.0);
+ };
+ grid.iterate (doIt);
+
+ octave_value_list res;
+ res(0) = dists;
+
+ return res;
+ }
+ catch (const std::exception& exc)
+ {
+ error (exc.what ());
+ return octave_value_list ();
+ }
+}
diff --git a/src/internal_mesh.cpp b/src/internal_mesh.cpp
new file mode 100644
index 0000000..b772615
--- /dev/null
+++ b/src/internal_mesh.cpp
@@ -0,0 +1,525 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2013-2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Internal C++ implementation for mesh building. Inside and outside elements
+ are simply split in two triangles, while boundary elements are split
+ depending on how they are intersected by the boundary. For them, the
+ geom.internal data about "boundary segments" is used. */
+
+#include <octave/oct.h>
+#include <octave/Cell.h>
+#include <octave/ov-struct.h>
+
+#include <stdexcept>
+#include <vector>
+
+#include "Utils.hpp"
+
+/**
+ * Type used to hold maps of indices. It needs to allow negative values, too,
+ * since they are used as "indices" of intersection points in the geometry.
+ * Furthermore, -1 is used to denote not-yet-set indices (since NA is not
+ * available as an integer value).
+ */
+typedef std::vector<int> indexArr;
+
+/** Type used for coordinates in space. */
+typedef double realT;
+
+/* ************************************************************************** */
+/* Containers for the datas of the mesh. */
+
+/**
+ * Represent a vertex in the list of vertices of a mesh (the mesh.p result).
+ */
+class Vertex
+{
+
+private:
+
+ /** This vertex' x coordinate in space. */
+ realT x;
+ /** This vertex' y coordinate in space. */
+ realT y;
+
+public:
+
+ /**
+ * Construct with given coordinates.
+ * @param mx x coordinate.
+ * @param my y coordinate.
+ */
+ inline Vertex (const realT mx, const realT my)
+ : x(mx), y(my)
+ {
+ // Nothing else to do.
+ }
+
+ /* Default copying. */
+ Vertex () = default;
+ Vertex (const Vertex& o) = default;
+ Vertex& operator= (const Vertex& o) = default;
+
+ /**
+ * Convert to row in the vertex matrix to be returned to Octave.
+ * @param i Index for the row to write in.
+ * @param m Matrix to write to.
+ */
+ inline void
+ toMatrix (const unsigned i, Matrix& m) const
+ {
+ m(0, i) = x;
+ m(1, i) = y;
+ }
+
+};
+
+/**
+ * Represent a triangle in the list of triangles of a mesh (mesh.t field).
+ */
+class Triangle
+{
+
+private:
+
+ /** Indices of the vertices. */
+ unsigned vertices[3];
+
+public:
+
+ /**
+ * Construct with given vertices.
+ * @param a First vertex.
+ * @param b Second vertex.
+ * @param c Third vertex.
+ */
+ inline Triangle (const unsigned a, const unsigned b, const unsigned c)
+ : vertices{a, b, c}
+ {
+ // Nothing else to do.
+ }
+
+ /* Default copying. */
+ Triangle () = default;
+ Triangle (const Triangle& o) = default;
+ Triangle& operator= (const Triangle& o) = default;
+
+ /**
+ * Convert to row in the triangle matrix to be returned to Octave.
+ * @param i Index for the row to write in.
+ * @param m Matrix to write to.
+ */
+ inline void
+ toMatrix (const unsigned i, Matrix& m) const
+ {
+ for (unsigned j = 0; j < 3; ++j)
+ m(j, i) = vertices[j];
+ }
+
+};
+
+/* ************************************************************************** */
+/* Mesh class itself. */
+
+/**
+ * This class represents the mesh that is being built up. It keeps track of
+ * the vertices and triangles as they are added. In addition,
+ * in order to do that, it also keeps a map between original vertex indices
+ * and indices into the already set-up list of vertices.
+ */
+class Mesh
+{
+
+private:
+
+ /** The list of vertices. */
+ std::vector<Vertex> vertices;
+ /** The list of triangles. */
+ std::vector<Triangle> triangles;
+
+ /** Map ordinary grid point index to that in list of vertices. */
+ indexArr innerVertexInd;
+ /** Map intersection point index to that in list of vertices. */
+ indexArr bdryVertexInd;
+ /**
+ * Map index of vertices on the mesh to the vertex code on the geometry.
+ * I. e., may be positive or negative depending on grid point or ispt.
+ */
+ indexArr meshVertexInd;
+
+public:
+
+ /**
+ * Construct the yet empty mesh.
+ * @param n Number of inner grid points.
+ * @param nBdry Number of intersection points.
+ */
+ Mesh (const unsigned n, const unsigned nBdry);
+
+ /* No copying. */
+ Mesh () = delete;
+ Mesh (const Mesh& o) = delete;
+ Mesh& operator= (const Mesh& o) = delete;
+
+ /**
+ * Get internal vertex index for a given grid point. Insert it into
+ * the list of vertices if necessary.
+ * @param ind Grid point index.
+ * @param coord Full coordinate array, so we know the coordinates.
+ * @return Index into internal vertex list.
+ */
+ unsigned getInnerVertex (const unsigned ind, const Matrix& coord);
+
+ /**
+ * Get internal vertex index for a given intersection point. Insert it into
+ * the list of vertices if necessary.
+ * @param ind Grid point index.
+ * @param coord Array of coordinates of boundary points.
+ * @return Index into internal vertex list.
+ */
+ unsigned getBoundaryVertex (const unsigned ind, const Matrix& coord);
+
+ /**
+ * Add a triangle with the given vertices.
+ * @param a First vertex.
+ * @param b Second vertex.
+ * @param c Third vertex.
+ */
+ inline void
+ addTriangle (const unsigned a, const unsigned b, const unsigned c)
+ {
+ triangles.push_back (Triangle(a, b, c));
+ }
+
+ /**
+ * Wrap the mesh's content up into the struct expected by Octave.
+ * @param res Store the msh structure here.
+ * @param vertexMap Store the vertex map here.
+ */
+ void toStruct (octave_scalar_map& res, octave_scalar_map& vertexMap) const;
+
+};
+
+Mesh::Mesh (const unsigned n, const unsigned nBdry)
+ : vertices(), triangles(),
+ innerVertexInd(n, -1), bdryVertexInd(nBdry, -1), meshVertexInd()
+{
+ // Nothing else to do.
+}
+
+unsigned
+Mesh::getInnerVertex (const unsigned ind, const Matrix& coord)
+{
+ if (innerVertexInd[ind] < 0)
+ {
+ const unsigned next = vertices.size ();
+ assert (next == meshVertexInd.size ());
+
+ vertices.push_back (Vertex(coord(ind, 0), coord(ind, 1)));
+ meshVertexInd.push_back (ind + 1);
+
+ innerVertexInd[ind] = next + 1;
+ }
+
+ assert (innerVertexInd[ind] >= 0);
+ return innerVertexInd[ind];
+}
+
+unsigned
+Mesh::getBoundaryVertex (const unsigned ind, const Matrix& coord)
+{
+ if (bdryVertexInd[ind] < 0)
+ {
+ const unsigned next = vertices.size ();
+ assert (next == meshVertexInd.size ());
+
+ vertices.push_back (Vertex(coord(ind, 0), coord(ind, 1)));
+ /* FIXME: Can get rid of cast? */
+ meshVertexInd.push_back (-static_cast<int> (ind) - 1);
+
+ bdryVertexInd[ind] = next + 1;
+ }
+
+ assert (bdryVertexInd[ind] >= 0);
+ return bdryVertexInd[ind];
+}
+
+void
+Mesh::toStruct (octave_scalar_map& res, octave_scalar_map& vertexMap) const
+{
+ Matrix vert(2, vertices.size ());
+ Matrix tri(3, triangles.size ());
+
+ for (unsigned i = 0; i < vertices.size (); ++i)
+ vertices[i].toMatrix (i, vert);
+ for (unsigned i = 0; i < triangles.size (); ++i)
+ triangles[i].toMatrix (i, tri);
+
+ vertexMap = octave_scalar_map ();
+ vertexMap.assign ("grid", convertToColumnVector (innerVertexInd));
+ vertexMap.assign ("ispt", convertToColumnVector (bdryVertexInd));
+ vertexMap.assign ("mesh", convertToColumnVector (meshVertexInd));
+
+ res = octave_scalar_map ();
+ res.assign ("p", vert);
+ res.assign ("t", tri);
+ /* e is filled out from geom.bedges in the m-file itself. */
+}
+
+/* ************************************************************************** */
+
+/**
+ * Extract the relevant information for building up the triangles from
+ * the boundary element segments information provided by ls_find_geometry.
+ * It basically just extracts everything of interest and reorders the points.
+ * @param segs Boundary element segment info from ls_find_geometry.
+ * @param bdryPoints Return boundary points here.
+ * @param innerPts Add inner points here.
+ */
+void
+getInnerSegment (const octave_scalar_map& segs,
+ unsigned bdryPts[2], indexArr& innerPts)
+{
+ bdryPts[0] = segs.contents ("endPt").int_value () - 1;
+ bdryPts[1] = segs.contents ("startPt").int_value () - 1;
+
+ assert (innerPts.empty ());
+ const ColumnVector inners = segs.contents ("inners").column_vector_value ();
+ const unsigned nInners = inners.nelem ();
+ for (unsigned i = 0; i < nInners; ++i)
+ innerPts.push_back (inners(nInners - i - 1) - 1);
+}
+
+/* Octave routine for mesh building. */
+DEFUN_DLD (__levelset_internal_mesh, args, nargout,
+ " [MESH, VMAP] = internal_mesh (L, M, INSIDE, COORDS, ISPTCOORDS,\n"
+ " NODELIST, INELEMS, OUTELEMS,\n"
+ " BDRYELEMS, BDRYELSEGS, GLOBAL)\n\n"
+ "Internal, fast implementation of mesh-building. Returned are already\n"
+ "the mesh structure (except the 'e' field) and vertex map returned\n"
+ "from ls_build_mesh, but the arguments are already picked apart by the\n"
+ "wrapper routine.\n\n"
+ "L, M are the size of the grid, COORDS give the coordinates of grid points\n"
+ "in space and INSIDE flags which of those points are inside the domain.\n"
+ "ISPTCOORDS are the coordinates of all intersection points indexed by\n"
+ "intersection point number. GLOBAL signals that we want a global mesh\n"
+ "instead of a list of meshes for only the inner parts.\n\n"
+ "The remaining arguments contain the relevant information from the\n"
+ "geometry struct picked apart.\n")
+{
+ try
+ {
+ if (args.length () != 11 || nargout > 2)
+ throw std::runtime_error ("invalid argument counts");
+
+ /* Get arguments in. */
+ const unsigned L = args(0).int_value ();
+ const unsigned M = args(1).int_value ();
+ const boolNDArray inside = args(2).bool_array_value ();
+ const Matrix coords = args(3).matrix_value ();
+ const Matrix isptCoords = args(4).matrix_value ();
+ const Matrix nodelist = args(5).matrix_value ();
+ const ColumnVector inElems = args(6).column_vector_value ();
+ const ColumnVector outElems = args(7).column_vector_value ();
+ const ColumnVector bdryElems = args(8).column_vector_value ();
+ const Cell bdryelSegs = args(9).cell_value ();
+ const bool global = args(10).bool_value ();
+
+ /* Check and extract dimensions. */
+ getDimension (inside, L * M, 1);
+ getDimension (coords, L * M, 2);
+ const unsigned numIntsec = getDimension (isptCoords, -1, 2);
+ getDimension (nodelist, (L - 1) * (M - 1), 4);
+ const unsigned numInElems = getDimension (inElems, -1, 1);
+ const unsigned numOutElems = getDimension (outElems, -1, 1);
+ const unsigned numBdryElems = getDimension (bdryElems, -1, 1);
+
+ /* Construct empty mesh. */
+ Mesh mesh(L * M, numIntsec);
+
+ /* Construct triangles for inner elements. */
+ for (unsigned i = 0; i < numInElems; ++i)
+ {
+ const unsigned cur = inElems(i) - 1;
+
+ unsigned vertInds[4];
+ for (unsigned j = 0; j < 4; ++j)
+ vertInds[j] = mesh.getInnerVertex (nodelist(cur, j) - 1, coords);
+
+ mesh.addTriangle (vertInds[0], vertInds[1], vertInds[2]);
+ mesh.addTriangle (vertInds[0], vertInds[2], vertInds[3]);
+ }
+
+ /* In global mode, do that also for outer elements. */
+ if (global)
+ for (unsigned i = 0; i < numOutElems; ++i)
+ {
+ const unsigned cur = outElems(i) - 1;
+
+ unsigned vertInds[4];
+ for (unsigned j = 0; j < 4; ++j)
+ vertInds[j] = mesh.getInnerVertex (nodelist(cur, j) - 1, coords);
+
+ mesh.addTriangle (vertInds[0], vertInds[1], vertInds[2]);
+ mesh.addTriangle (vertInds[0], vertInds[2], vertInds[3]);
+ }
+
+ /* Go over boundary elements and handle them. We use the information
+ provided already by ls_find_geometry internally about the segments
+ of each boundary element that belong to the domain. These need
+ to be split into triangles. */
+ for (unsigned i = 0; i < numBdryElems; ++i)
+ {
+ const unsigned cur = bdryElems(i) - 1;
+ const Cell cellSegs = bdryelSegs(i).cell_value ();
+ const unsigned nSegs = cellSegs.nelem ();
+
+ std::vector<octave_scalar_map> segs;
+ indexArr endEdges;
+ for (unsigned j = 0; j < nSegs; ++j)
+ {
+ segs.push_back (cellSegs(j).scalar_map_value ());
+ int edge = segs.back ().contents ("endEdge").int_value () - 1;
+ endEdges.push_back (edge);
+ }
+
+ /* Special case is that of *two* starting points, which
+ indicates that we have a narrow pair. */
+ if (segs.size () == 2)
+ {
+ assert ((endEdges[0] + 2) % 4 == endEdges[1]);
+
+ unsigned bdryPts[4];
+ indexArr innerPts[2];
+ for (unsigned j = 0; j < 2; ++j)
+ {
+ getInnerSegment (segs[j], bdryPts + 2 * j, innerPts[j]);
+ assert (innerPts[j].size () == 1);
+
+ for (unsigned k = 0; k < 2; ++k)
+ {
+ const unsigned ind = 2 * j + k;
+ bdryPts[ind] = mesh.getBoundaryVertex (bdryPts[ind],
+ isptCoords);
+ }
+ for (auto& p : innerPts[j])
+ p = mesh.getInnerVertex (p, coords);
+
+ mesh.addTriangle (bdryPts[2 * j], innerPts[j][0],
+ bdryPts[2 * j + 1]);
+ }
+
+ if (global)
+ {
+ int outerPts[] = {endEdges[0], endEdges[1]};
+ for (auto& p : outerPts)
+ {
+ p = nodelist(cur, p) - 1;
+ assert (!inside (p));
+ p = mesh.getInnerVertex (p, coords);
+ }
+
+ mesh.addTriangle (bdryPts[1], outerPts[1], bdryPts[2]);
+ mesh.addTriangle (bdryPts[0], bdryPts[1], bdryPts[2]);
+ mesh.addTriangle (bdryPts[0], bdryPts[2], bdryPts[3]);
+ mesh.addTriangle (bdryPts[0], bdryPts[3], outerPts[0]);
+ }
+ }
+
+ /* "Ordinary" case. */
+ else
+ {
+ assert (segs.size () == 1);
+
+ /* Find whole list of points forming the segment. */
+ unsigned bdryPts[2];
+ indexArr innerPts;
+ getInnerSegment (segs[0], bdryPts, innerPts);
+ assert (!innerPts.empty ());
+
+ indexArr outerPts;
+ for (unsigned p = endEdges[0] % 4; ; p = (p + 3) % 4)
+ {
+ const unsigned pInd = nodelist (cur, p) - 1;
+ if (static_cast<int> (pInd) == innerPts.back ())
+ break;
+ assert (!inside (pInd));
+ outerPts.push_back (pInd);
+ }
+ assert (innerPts.size () + outerPts.size () == 4);
+
+ /* Add to list of vertices in the mesh and overwrite the
+ indices on the way. */
+ for (auto& p : bdryPts)
+ p = mesh.getBoundaryVertex (p, isptCoords);
+ for (auto& p : innerPts)
+ p = mesh.getInnerVertex (p, coords);
+ if (global)
+ for (auto& p : outerPts)
+ p = mesh.getInnerVertex (p, coords);
+
+ /* Build the triangles accordingly and also the list of
+ boundary edges in this case. */
+ switch (innerPts.size ())
+ {
+ case 1:
+ mesh.addTriangle (bdryPts[0], innerPts[0], bdryPts[1]);
+ if (global)
+ {
+ mesh.addTriangle (bdryPts[0], outerPts[1], outerPts[0]);
+ mesh.addTriangle (bdryPts[0], bdryPts[1], outerPts[1]);
+ mesh.addTriangle (bdryPts[1], outerPts[2], outerPts[1]);
+ }
+ break;
+ case 2:
+ mesh.addTriangle (bdryPts[0], innerPts[0], innerPts[1]);
+ mesh.addTriangle (innerPts[1], bdryPts[1], bdryPts[0]);
+ if (global)
+ {
+ mesh.addTriangle (bdryPts[1], outerPts[1], outerPts[0]);
+ mesh.addTriangle (outerPts[0], bdryPts[0], bdryPts[1]);
+ }
+ break;
+ case 3:
+ mesh.addTriangle (bdryPts[0], innerPts[0], innerPts[1]);
+ mesh.addTriangle (innerPts[1], bdryPts[1], bdryPts[0]);
+ mesh.addTriangle (innerPts[1], innerPts[2], bdryPts[1]);
+ if (global)
+ mesh.addTriangle (bdryPts[1], outerPts[0], bdryPts[0]);
+ break;
+ default:
+ assert (false);
+ }
+ }
+ }
+
+ /* Build return results. */
+ octave_scalar_map msh, vertexMap;
+ mesh.toStruct (msh, vertexMap);
+ octave_value_list res;
+ res(0) = msh;
+ res(1) = vertexMap;
+
+ return res;
+ }
+ catch (const std::runtime_error& exc)
+ {
+ error (exc.what ());
+ return octave_value_list ();
+ }
+}
diff --git a/src/marching.cpp b/src/marching.cpp
new file mode 100644
index 0000000..b59c0e6
--- /dev/null
+++ b/src/marching.cpp
@@ -0,0 +1,103 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2013-2015 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Test program for fast marching. */
+
+#include "FastMarching.hpp"
+
+#include <cmath>
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+using namespace fastMarching;
+
+namespace fastMarching
+{
+
+/* Provide printf-based implementation of issueWarning. */
+void
+issueWarning (const std::string&, const std::string& fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ vprintf (fmt.c_str (), args);
+ va_end (args);
+}
+
+} // namespace fastMarching
+
+/**
+ * Main routine.
+ * @return Exit code 0.
+ */
+int
+main ()
+{
+ static const unsigned N = 100;
+
+ IndexTuple size(2);
+ size[0] = N;
+ size[1] = N;
+
+ Grid grid(size);
+ for (unsigned i = 0; i < N; ++i)
+ for (unsigned j = 0; j < N; ++j)
+ {
+ size[0] = i;
+ size[1] = j;
+
+ if (i == N/2 && j == N/2)
+ grid.setInitial (Entry::newAlive (size, 0.0, 0.0));
+ else
+ {
+ realT f = 0.0;
+ f += std::pow (static_cast<double> (i) - N/2, 2);
+ f += std::pow (static_cast<double> (j) - N/2, 2);
+ f = 1.0 - std::sqrt (f) / N * std::sqrt (1.8);
+ f = 1.0 / f;
+ grid.setInitial (Entry::newFarAway (size, f));
+ }
+ }
+
+ grid.perform ();
+
+ printf ("data = [ ...\n");
+ for (unsigned i = 0; i < N; ++i)
+ {
+ for (unsigned j = 0; j < N; ++j)
+ {
+ size[0] = i;
+ size[1] = j;
+ const Grid& constGrid(grid);
+ const Entry& cur = *constGrid.get (size);
+
+ printf (" %f", cur.getDistance ());
+ if (j + 1 < N)
+ printf (",");
+ }
+
+ if (i + 1 < N)
+ printf ("; ...\n");
+ else
+ printf (" ...\n");
+ }
+ printf ("];\n");
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/nbFromGeom.cpp b/src/nbFromGeom.cpp
new file mode 100644
index 0000000..ad28275
--- /dev/null
+++ b/src/nbFromGeom.cpp
@@ -0,0 +1,166 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2014 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Internal routine for ls_nb_from_geom. */
+
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/lo-ieee.h>
+#include <octave/oct-norm.h>
+
+#include <cassert>
+#include <stdexcept>
+
+/* Tolerance for considering distances equal when averaging over multiple
+ g values. */
+static const double avgTol = 1.0e-9;
+
+/* C++ implementation. */
+DEFUN_DLD (__levelset_nbFromGeom, args, nargout,
+ " [D, GSUM, GCNT] = nbFromGeom (BEDGES, PHI, HASG, G0,\n"
+ " NODELIST, BDRYINDEX, ONEDGE,\n"
+ " NODECOORD, ISPTCOORD)\n\n"
+ "Internal routine to calculate the narrow-band distances from the geometry\n"
+ "information in the respective arguments (corresponding to ls_find_geometry\n"
+ "struct fields).\n")
+{
+ try
+ {
+ if (args.length () != 9 || nargout > 3)
+ throw std::runtime_error ("invalid argument counts");
+
+ /* Get arguments. */
+ const Matrix bedges = args(0).matrix_value ();
+ const Matrix phi = args(1).matrix_value ();
+ const bool hasG = args(2).bool_value ();
+ const ColumnVector g0 = args(3).column_vector_value ();
+ const Matrix nodelist = args(4).matrix_value ();
+ const ColumnVector bdryIndex = args(5).column_vector_value ();
+ const Matrix onEdge = args(6).matrix_value ();
+ const Matrix nodeCoord = args(7).matrix_value ();
+ const Matrix isptCoord = args(8).matrix_value ();
+
+ /* Extract the dimensions. */
+
+ const unsigned nEdges = getDimension (bedges, -1, 2);
+ if (hasG)
+ getDimension (g0, nEdges, 1);
+
+ const dim_vector dims = phi.dims ();
+ const unsigned L = dims(0);
+ const unsigned M = dims(1);
+ const unsigned nNodes = L * M;
+ const unsigned nElems = (L - 1) * (M - 1);
+
+ getDimension (nodelist, nElems, 4);
+ const unsigned nBdryEl = getDimension (bdryIndex, -1, 1);
+ const unsigned nIspts = getDimension (onEdge, -1, 2);
+ getDimension (nodeCoord, nNodes, 2);
+ getDimension (isptCoord, nIspts, 2);
+
+ /* Loop over each boundary segment and perform the update to d. */
+
+ Matrix d(L, M), gSum(L, M), gCnt(L, M);
+ d.fill (octave_NA);
+ if (hasG)
+ {
+ gSum.fill (octave_NA);
+ gCnt.fill (octave_NA);
+ }
+
+ for (unsigned i = 0; i < nEdges; ++i)
+ {
+ const unsigned indA = bedges(i, 0) - 1;
+ const unsigned indB = bedges(i, 1) - 1;
+ const unsigned bdryEl = onEdge(indA, 0) - 1;
+ assert (bdryEl == onEdge(indB, 1) - 1);
+ assert (bdryEl < nBdryEl);
+ const unsigned elem = bdryIndex(bdryEl) - 1;
+
+#define EXTRACT_VERTEX(varname, coordlist, ind) \
+ ColumnVector varname(2); \
+ varname(0) = coordlist((ind), 0); \
+ varname(1) = coordlist((ind), 1);
+
+ /* Get information about the line segment. */
+
+ EXTRACT_VERTEX (a, isptCoord, indA)
+ EXTRACT_VERTEX (b, isptCoord, indB)
+
+ ColumnVector dir = b - a;
+ const double len = xnorm (dir);
+ dir /= len;
+
+ /* Try to update all boundary element nodes. */
+ for (unsigned j = 0; j < 4; ++j)
+ {
+ const unsigned node = nodelist(elem, j) - 1;
+ assert (node < nNodes);
+
+ EXTRACT_VERTEX (x, nodeCoord, node)
+
+ /* Project onto the side. If the projection is negative,
+ x lies "beyond" the edge on the side of a. In this case,
+ its distance is the distance to a. The same is true (with b)
+ if the projection is too large. Otherwise, we
+ use the distance to the line spanned by a and b as usual. */
+ const double proj = dir.transpose () * (x - a);
+ double cur;
+ if (proj < 0.0)
+ cur = xnorm (x - a);
+ else if (proj > len)
+ cur = xnorm (x - b);
+ else
+ cur = xnorm (x - (a + proj * dir));
+
+ if (hasG)
+ {
+ if (lo_ieee_is_NA (d(node)) || d(node) > cur + avgTol)
+ {
+ gSum(node) = g0(i);
+ gCnt(node) = 1;
+ }
+ else if (std::abs (d(node) - cur) < avgTol)
+ {
+ gSum(node) += g0(i);
+ ++gCnt(node);
+ }
+ }
+
+ if (lo_ieee_is_NA (d(node)) || d(node) > cur)
+ d(node) = cur;
+ }
+
+#undef EXTRACT_VERTEX
+ }
+
+ /* Return. */
+ octave_value_list res;
+ res(0) = d;
+ res(1) = gSum;
+ res(2) = gCnt;
+
+ return res;
+ }
+ catch (const std::runtime_error& exc)
+ {
+ error (exc.what ());
+ return octave_value_list ();
+ }
+}
diff --git a/src/upwindGrad.cpp b/src/upwindGrad.cpp
new file mode 100644
index 0000000..7418dc0
--- /dev/null
+++ b/src/upwindGrad.cpp
@@ -0,0 +1,152 @@
+/*
+ GNU Octave level-set package.
+ Copyright (C) 2015 Daniel Kraft <d at domob.eu>
+
+ 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 program 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/>.
+*/
+
+/* Implement an approximation to |grad phi| using an appropriate
+ upwind scheme. The scheme implemented is basically (6.17)
+ from Sethian's book "Level Set Methods and Fast Marching Methods",
+ Cambridge University Press, second edition, 1999.
+
+ The discretisation could probably be discretised also in Octave code
+ with proper vector operations, but this would to harder-to-read code.
+ The direct, point-wise C++ formulation is closer to the formulas
+ that are derived in the book. */
+
+#include <octave/oct.h>
+#include <octave/lo-ieee.h>
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <stdexcept>
+
+/** Type used to index n-dimensional Octave arrays. */
+typedef Array<octave_idx_type> GridIndex;
+/** Type used to denote number of dimensions. */
+typedef size_t dimensionT;
+
+/**
+ * Calculate the gradient norm in a given grid point.
+ * @param idx The current point's index.
+ * @param phi The function whose gradient is requested.
+ * @param F The sign gives the upwind direction.
+ * @param size The total domain size.
+ * @param D The number of dimensions.
+ * @return The approximate gradient norm in the point, not yet divided by h.
+ */
+static double
+gradientNormInPoint (const GridIndex& idx, const NDArray& phi, double F,
+ const dim_vector& size, dimensionT D)
+{
+ double res = 0.0;
+ for (dimensionT d = 0; d < D; ++d)
+ {
+ GridIndex neighbour(idx);
+ double forward = 0.0;
+ double backward = 0.0;
+
+ if (idx(d) > 0)
+ {
+ neighbour(d) = idx(d) - 1;
+ backward = phi(idx) - phi(neighbour);
+ }
+ if (idx(d) + 1 < size(d))
+ {
+ neighbour(d) = idx(d) + 1;
+ forward = phi(neighbour) - phi(idx);
+ }
+
+ if (F < 0.0)
+ std::swap (forward, backward);
+
+ if (backward > 0.0)
+ res += std::pow (backward, 2);
+ if (forward < 0.0)
+ res += std::pow (forward, 2);
+ }
+
+ return std::sqrt (res);
+}
+
+/**
+ * Recursively (since the number of dimensions and thus required loops
+ * is not known) iterate the whole array, calling gradientNormInPoint
+ * at each grid point.
+ * @param idx The current index.
+ * @param depth The current depth in the recursion.
+ * @param phi The function whose gradient is requested.
+ * @param F The sign defines the upwind direction.
+ * @param size Size of the array.
+ * @param D Number of dimensions.
+ * @param grad Return the gradient norm here.
+ */
+static void
+iterate (GridIndex& idx, dimensionT depth, const NDArray& phi, const NDArray& F,
+ const dim_vector& size, dimensionT D, NDArray& grad)
+{
+ if (depth == D)
+ {
+ grad(idx) = gradientNormInPoint (idx, phi, F(idx), size, D);
+ return;
+ }
+
+ for (idx(depth) = 0; idx(depth) < size(depth); ++idx(depth))
+ iterate (idx, depth + 1, phi, F, size, D, grad);
+}
+
+/* Octave routine interfacing to fast marching code. */
+DEFUN_DLD (__levelset_upwindGrad, args, nargout,
+ "GNORM = upwindGrad (PHI, F, H)\n\n"
+ "Internal routine to calculate the upwind-discretised gradient norm\n"
+ "of PHI. F is the speed field, and only the sign of F is used to decide\n"
+ "about the upwind direction. H is the grid size as usual.\n\n"
+ "Returned is an array GNORM of the same size as PHI, with |grad phi| in\n"
+ "the entries.\n")
+{
+ try
+ {
+ if (args.length () != 3 || nargout != 1)
+ throw std::invalid_argument ("invalid argument counts");
+
+ const NDArray phi = args(0).array_value ();
+ const NDArray F = args(1).array_value ();
+ const double h = args(2).double_value ();
+
+ const dim_vector size = phi.dims ();
+ const dimensionT D = size.length ();
+
+ if (size != F.dims ())
+ throw std::invalid_argument ("argument dimensions mismatch");
+
+ NDArray grad(size);
+ grad.fill (octave_NA);
+
+ GridIndex idx(dim_vector (D, 1));
+ iterate (idx, 0, phi, F, size, D, grad);
+ grad /= h;
+
+ octave_value_list res;
+ res(0) = grad;
+
+ return res;
+ }
+ catch (const std::exception& exc)
+ {
+ error (exc.what ());
+ return octave_value_list ();
+ }
+}
--
Alioth's /home/groups/pkg-octave/bin/git-commit-notice on /srv/git.debian.org/git/pkg-octave/octave-level-set.git
More information about the Pkg-octave-commit
mailing list