summaryrefslogtreecommitdiff
path: root/rspl
diff options
context:
space:
mode:
Diffstat (limited to 'rspl')
-rw-r--r--rspl/Jamfile83
-rw-r--r--rspl/License.txt662
-rw-r--r--rspl/Makefile.am15
-rw-r--r--rspl/Readme.txt39
-rw-r--r--rspl/afiles34
-rw-r--r--rspl/c1.c396
-rw-r--r--rspl/c1df.c313
-rw-r--r--rspl/gam.c1220
-rw-r--r--rspl/gam.h131
-rw-r--r--rspl/mlbs.c605
-rw-r--r--rspl/mlbs.h77
-rw-r--r--rspl/opt.c725
-rw-r--r--rspl/rev.c6608
-rw-r--r--rspl/rev.h457
-rw-r--r--rspl/revbench.c306
-rw-r--r--rspl/rspl.c1511
-rw-r--r--rspl/rspl.h645
-rw-r--r--rspl/rspl1.c391
-rw-r--r--rspl/rspl1.h115
-rw-r--r--rspl/rspl_imp.h27
-rw-r--r--rspl/scat.c2861
-rw-r--r--rspl/scat2.c233
-rw-r--r--rspl/sm1.c88
-rw-r--r--rspl/sm2.c110
-rw-r--r--rspl/sm3.c110
-rw-r--r--rspl/smtmpp.c1203
-rw-r--r--rspl/smtnd.c1171
-rw-r--r--rspl/spline.c352
-rw-r--r--rspl/stest.c654
-rw-r--r--rspl/t2d.c1016
-rw-r--r--rspl/t2ddf.c517
-rw-r--r--rspl/t3d.c905
-rw-r--r--rspl/t3ddf.c570
-rw-r--r--rspl/tnd.c489
-rw-r--r--rspl/trnd.c275
35 files changed, 24914 insertions, 0 deletions
diff --git a/rspl/Jamfile b/rspl/Jamfile
new file mode 100644
index 0000000..a3f2e8c
--- /dev/null
+++ b/rspl/Jamfile
@@ -0,0 +1,83 @@
+
+# Regular spline library
+
+# Optimization and Debug flags
+
+PREF_CCFLAGS += $(CCOPTFLAG) ; # Turn optimisation on
+#PREF_CCFLAGS += $(CCDEBUGFLAG) ; # Debugging flags
+#PREF_CCFLAGS += $(CCPROFFLAG) ; # Profile flags
+#PREF_LINKFLAGS += $(LINKPROFFLAG) ; # Profile flags
+#PREF_CCFLAGS += $(CCHEAPDEBUG) ; # Heap Debugging flags
+PREF_LINKFLAGS += $(LINKDEBUGFLAG) ; # Link with debug info
+
+SCAT = scat ; # Use thps scattered interpolation library
+
+#Products
+Libraries = librspl ;
+Headers = rspl.h ;
+
+#Install
+#InstallLib $(DESTDIR)$(PREFIX)/lib : $(Libraries) ;
+#InstallFile $(DESTDIR)$(PREFIX)/h : $(Headers) ;
+
+# Multi-dimensional regular spline library
+Library librspl : rspl.c $(SCAT).c rev.c gam.c spline.c opt.c : : : ../h ../numlib ../plot ;
+
+HDRS = ../h ../numlib ../plot $(TIFFINC) ;
+LINKLIBS = librspl ../numlib/libnum ../plot/libplot ../plot/libvrml ../icc/libicc $(TIFFLIB) $(JPEGLIB) ;
+
+# Test programs
+MainsFromSources revbench.c c1.c c1df.c t2d.c t2ddf.c t3d.c t3ddf.c tnd.c trnd.c ;
+
+BUILD_TESTS = true ;
+
+if $(BUILD_TESTS) {
+
+ HDRS = ../h ../numlib ../plot ../icc ../rspl ../xicc ../gamut ../cgats ../spectro $(TIFFINC) ;
+ LINKLIBS = ../xicc/libxicc ../gamut/libgamut ../spectro/libinsttypes librspl
+ ../cgats/libcgats ../icc/libicc ../plot/libplot ../plot/libvrml
+ ../numlib/libnum $(TIFFLIB) $(JPEGLIB) ;
+
+ # Smoothness factor tuning test in Nd.
+ Main smtnd : smtnd.c ;
+
+ # Smoothness factor tuning test in Nd.
+ Main smtmpp : smtmpp.c ;
+
+# Main rand_check : rand_check.c ;
+
+# Main tt : tt.c ;
+
+ HDRS = ;
+ LINKLIBS = ;
+
+ Main sm1 : sm1.c ;
+ Main sm2 : sm2.c ;
+ Main sm3 : sm3.c ;
+
+}
+
+
+# Main tt : tt.c : : ../xicc : : : ../plot/libvrml ../icc/libicc ;
+
+if $(BUILD_JUNK) {
+
+ HDRS = ../h ../numlib ;
+ LINKLIBS = ..//numlib/libnum ;
+
+ #Main temp : temp.c ;
+
+ MainsFromSources prime.c combo.c combo2.c combo3.c combo4.c combo5.c combo9.c ;
+
+ #Main cache : cache.c ;
+
+ # Nearest point test code
+ Main nptest : nptest.c ;
+
+ # Nearest object test code
+ Main notest : notest.c ;
+
+ #direct mlbs scattered interpolation test
+ Main mlbs_test : mlbs_test.c ;
+}
+
diff --git a/rspl/License.txt b/rspl/License.txt
new file mode 100644
index 0000000..a871fcf
--- /dev/null
+++ b/rspl/License.txt
@@ -0,0 +1,662 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 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 Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are 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.
+
+ 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.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ 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 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 work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero 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 your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ 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 AGPL, see
+<http://www.gnu.org/licenses/>.
+
diff --git a/rspl/Makefile.am b/rspl/Makefile.am
new file mode 100644
index 0000000..cfa0ec2
--- /dev/null
+++ b/rspl/Makefile.am
@@ -0,0 +1,15 @@
+include $(top_srcdir)/Makefile.shared
+
+privatelib_LTLIBRARIES = librspl.la
+privatelibdir = $(pkglibdir)
+
+librspl_la_SOURCES = rspl.h rspl_imp.h mlbs.h rspl.c scat.c rev.c \
+ rev.h gam.c spline.c opt.c
+librspl_la_LIBADD = ../numlib/libargyllnum.la ../plot/libvrml.la
+
+LDADD = ./librspl.la ../numlib/libargyllnum.la ../plot/libplot.la \
+ ../plot/libvrml.la $(X_LIBS) $(TIFF_LIBS) $(ICC_LIBS)
+
+check_PROGRAMS = revbench c1 c1df t2d t2ddf t3d t3ddf tnd trnd
+
+EXTRA_DIST = License.txt Readme.txt
diff --git a/rspl/Readme.txt b/rspl/Readme.txt
new file mode 100644
index 0000000..4f6c8c0
--- /dev/null
+++ b/rspl/Readme.txt
@@ -0,0 +1,39 @@
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+rspl now supports different resolution grids in each dimension.
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+This is the second generation Regular Spline library.
+
+It contains scattered data point to regular grid interpolation,
+as well as spline smoothing, and the reverse interpolation
+code. This version is more modular, and uses better solution
+algorithms than the earlier REGSPL, and generally replaces it.
+
+The reverse interpolation algorithms support features needed
+for devices like CMYK printers, such as total ink limiting,
+black locus selection, gamut boundary detection, vector
+and nearest gamut clipping.
+
+It has been written with operation with 6 color printing
+devices in mind (ie. 3 extra degrees of freedom, and hence
+a 3 dimensional inking locus), although this usage is likely
+to be unwealdy.
+
+
+Misc test files:
+
+c1.c Test 1D curves. First test is to check tracking at multiple resolutions
+c1df.c Test 1D curve with weak default function.
+c1i.c Test 1D curve with incremental points
+sm1.c Discover 1D smoothness factor vs resolution tracking factor
+sm2.c Discover 2D smoothness factor vs resolution tracking factor
+sm3.c Discover 3D smoothness factor vs resolution tracking factor
+t2d.c Test 2D fitting. Test againt two resolutions.
+t2ddf.c Test 2D fitting with weak default function.
+t3d.c Test 3D fitting. Test againt two resolutions.
+t3ddf.c Test 3D fitting with weak default function.
+tnd.c Simple test of 4D
+trnd.c Simple test of 4D reverse lookup.
+smtnd.c Sythetic function, nD multi-parameter fitting test function.
+smtmpp.c MPP function, nD multi-parameter fitting test function.
+
diff --git a/rspl/afiles b/rspl/afiles
new file mode 100644
index 0000000..af20c44
--- /dev/null
+++ b/rspl/afiles
@@ -0,0 +1,34 @@
+Readme.txt
+License.txt
+afiles
+Jamfile
+rspl.h
+rspl_imp.h
+rspl.c
+rev.h
+rev.c
+scat.c
+scat2.c
+mlbs.c
+mlbs.h
+opt.c
+gam.h
+gam.c
+revbench.c
+c1.c
+c1df.c
+spline.c
+t2d.c
+t2ddf.c
+t3d.c
+t3ddf.c
+tnd.c
+trnd.c
+sm1.c
+sm2.c
+sm3.c
+stest.c
+smtnd.c
+smtmpp.c
+rspl1.h
+rspl1.c
diff --git a/rspl/c1.c b/rspl/c1.c
new file mode 100644
index 0000000..8172cef
--- /dev/null
+++ b/rspl/c1.c
@@ -0,0 +1,396 @@
+
+/************************************************/
+/* Investigate various curve approximations */
+/************************************************/
+
+/* Discrete regularized spline versions */
+/* Standard test + Random testing */
+
+/* Author: Graeme Gill
+ * Date: 4/10/95
+ * Date: 5/4/96
+ *
+ * Copyright 1995, 1996 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+#undef DIAG
+#undef DIAG2
+#undef GLOB_CHECK
+#undef RES2 /* Do multiple test at various resolutions */
+#define AVGDEV 0.0 /* Average deviation of function data */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "numlib.h"
+#include "plot.h"
+#include "rspl.h"
+
+double lin();
+void usage(void);
+
+#define TRIALS 20 /* Number of random trials */
+#define SKIP 0 /* Number of random trials to skip */
+
+#define MIN_PNTS 5
+#define MAX_PNTS 40
+
+#define MIN_RES 20
+#define MAX_RES 2000
+
+double xa[MAX_PNTS];
+double ya[MAX_PNTS];
+
+#define XRES 100
+
+#define PNTS1 10
+#define GRES1 400
+//#define GRES 800
+double t1xa[PNTS1] = { 0.2, 0.25, 0.30, 0.35, 0.40, 0.44, 0.48, 0.51, 0.64, 0.75 };
+double t1ya[PNTS1] = { 0.3, 0.35, 0.4, 0.41, 0.42, 0.46, 0.5, 0.575, 0.48, 0.75 };
+
+#ifndef NEVER
+
+// Reverse in x */
+#define PNTS2 10
+#define GRES2 400
+double t2xa[PNTS2] = { 0.25, 0.36, 0.49, 0.52, 0.56, 0.60, 0.65, 0.70, 0.75, 0.8 };
+double t2ya[PNTS2] = { 0.75, 0.48, 0.575, 0.5, 0.46, 0.42, 0.41, 0.4, 0.35, 0.3 };
+
+#else
+
+#define PNTS2 10
+#define GRES2 400
+// reverse in y
+double t2xa[PNTS2] = { 0.2, 0.25, 0.30, 0.35, 0.40, 0.44, 0.48, 0.51, 0.64, 0.75 };
+double t2ya[PNTS2] = { 0.7, 0.65, 0.6, 0.59, 0.58, 0.54, 0.5, 0.425, 0.52, 0.25 };
+
+#endif /* NEVER */
+
+//#define PNTS2 2
+//#define GRES2 5
+//double t2xa[PNTS2] = { 0.0, 1.0 };
+//double t2ya[PNTS2] = { 0.33, 0.66 };
+
+co test_points[MAX_PNTS];
+
+double lin(double x, double xa[], double ya[], int n);
+
+void usage(void) {
+ fprintf(stderr,"Test 1D rspl interpolation\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: c1 [options]\n");
+ fprintf(stderr," -s smooth Use given smoothness (default 1.0)\n");
+ fprintf(stderr," -2 Use two pass smoothing\n");
+ fprintf(stderr," -x Use extra fitting\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ int i,j, n;
+ double x;
+ double xx[XRES];
+ double yy[6][XRES];
+ rspl *rss; /* incremental solution version */
+ datai low,high;
+ int gres[MXDI];
+ double smooth = 1.0;
+ int twopass = 0;
+ int extra = 0;
+ double avgdev[MXDO];
+
+ low[0] = 0.0;
+ high[0] = 1.0;
+ avgdev[0] = AVGDEV;
+
+ error_program = "c1";
+ check_if_not_interactive();
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ /* smoothness */
+ else if (argv[fa][1] == 's' || argv[fa][1] == 'S') {
+ fa = nfa;
+ if (na == NULL) usage();
+ smooth = atof(na);
+ }
+
+ else if (argv[fa][1] == '2') {
+ twopass = 1;
+ }
+ else if (argv[fa][1] == 'x' || argv[fa][1] == 'X') {
+ extra = 1;
+ }
+ else
+ usage();
+ } else
+ break;
+ }
+
+ for (n = 0; n < TRIALS; n++) {
+ double lrand = 0.0; /* Amount of level randomness */
+ int pnts;
+ int fres;
+
+ if (n == 0) { /* Standard versions */
+#ifdef NEVER /* Doubled up points */
+ pnts = 2 * PNTS;
+ fres = GRES;
+ for (i = 0; i < pnts; i++) {
+ xa[i * 2 + 0] = t1xa[i] - 0.01;
+ ya[i * 2 + 0] = t1ya[i];
+ xa[i * 2 + 1] = t1xa[i] + 0.01;
+ ya[i * 2 + 1] = t1ya[i];
+ }
+#else
+ pnts = PNTS1;
+ fres = GRES1;
+ for (i = 0; i < pnts; i++) {
+ xa[i] = t1xa[i];
+ ya[i] = t1ya[i];
+ }
+#endif
+ printf("Trial %d, points = %d, res = %d, level randomness = %f\n",n,pnts,fres,lrand);
+ } else if (n == 1) { /* Second test versions */
+ pnts = PNTS2;
+ fres = GRES2;
+ for (i = 0; i < pnts; i++) {
+ xa[i] = t2xa[i];
+ ya[i] = t2ya[i];
+ }
+ printf("Trial %d, points = %d, res = %d, level randomness = %f\n",n,pnts,fres,lrand);
+ } else { /* Random versions */
+ lrand = d_rand(0.0,0.1); /* Amount of level randomness */
+ pnts = i_rand(MIN_PNTS,MAX_PNTS);
+ fres = i_rand(MIN_RES,MAX_RES);
+
+ printf("Trial %d, points = %d, res = %d, level randomness = %f\n",n,pnts,fres,lrand);
+
+ /* Create X values */
+ xa[0] = d_rand(0.5,1.0);
+ for (i = 1; i < pnts; i++)
+ xa[i] = xa[i-1] + d_rand(0.5,1.0);
+ for (i = 0; i < pnts; i++) /* Divide out */
+ xa[i] = (xa[i]/xa[pnts-1]);
+
+ /* Create y values */
+ ya[0] = xa[0];
+ for (i = 0; i < pnts; i++)
+ ya[i] = ya[i-1] + d_rand(0.2,1.0) + d_rand(-0.2,0.3) + d_rand(-0.2,0.3);
+ for (i = 0; i < pnts; i++) /* Divide out */
+ ya[i] = (ya[i]/ya[pnts-1]);
+ }
+
+ if (n < SKIP)
+ continue;
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS,
+ 1, /* di */
+ 1); /* fdi */
+
+ for (i = 0; i < pnts; i++) {
+ test_points[i].p[0] = xa[i];
+ test_points[i].v[0] = ya[i];
+ }
+ gres[0] = fres;
+
+#ifdef RES2
+ if (n != 0) {
+#endif
+ /* Fit to scattered data */
+ rss->fit_rspl(rss,
+ 0 | (twopass ? RSPL_2PASSSMTH : 0)
+ | (extra ? RSPL_EXTRAFIT2 : 0) ,
+ test_points, /* Test points */
+ pnts, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ smooth, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+
+
+ /* Display the result */
+ for (i = 0; i < XRES; i++) {
+ co tp; /* Test point */
+ x = i/(double)(XRES-1);
+ xx[i] = x;
+ yy[0][i] = lin(x,xa,ya,pnts);
+ tp.p[0] = x;
+ rss->interp(rss, &tp);
+ yy[1][i] = tp.v[0];
+ if (yy[1][i] < -0.2)
+ yy[1][i] = -0.2;
+ else if (yy[1][i] > 1.2)
+ yy[1][i] = 1.2;
+ }
+
+ do_plot(xx,yy[0],yy[1],NULL,XRES);
+
+#ifdef RES2
+ } else { /* Multiple resolution version */
+ int gresses[5];
+ for (j = 0; j < 5; j++) {
+#ifndef NEVER
+ if (j == 0)
+ gres[0] = fres/8;
+ else if (j == 1)
+ gres[0] = fres/4;
+ else if (j == 2)
+ gres[0] = fres/2;
+ else if (j == 3)
+ gres[0] = fres;
+ else
+ gres[0] = fres * 2;
+#else /* Check sensitivity to griding of data points */
+ if (j == 0)
+ gres[0] = 192;
+ else if (j == 1)
+ gres[0] = 193;
+ else if (j == 2)
+ gres[0] = 194;
+ else if (j == 3)
+ gres[0] = 195;
+ else
+ gres[0] = 196;
+#endif
+ gresses[j] = gres[0];
+
+ rss->fit_rspl(rss,
+ 0 | (twopass ? RSPL_2PASSSMTH : 0)
+ | (extra ? RSPL_EXTRAFIT2 : 0) ,
+ 0,
+ test_points, /* Test points */
+ pnts, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ smooth, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+
+ /* Get the result */
+ for (i = 0; i < XRES; i++) {
+ co tp; /* Test point */
+ x = i/(double)(XRES-1);
+ xx[i] = x;
+ yy[0][i] = lin(x,xa,ya,pnts);
+ tp.p[0] = x;
+ rss->interp(rss, &tp);
+ yy[1+j][i] = tp.v[0];
+ if (yy[1+j][i] < -0.2)
+ yy[1+j][i] = -0.2;
+ else if (yy[1+j][i] > 1.2)
+ yy[1+j][i] = 1.2;
+ }
+ }
+
+ printf("Black = lin, Red = %d, Green = %d, Blue = %d, Yellow = %d, Purple = %d\n",
+ gresses[0], gresses[1], gresses[2], gresses[3], gresses[4]);
+ do_plot6(xx,yy[0],yy[1],yy[2],yy[3],yy[4],yy[5],XRES);
+ }
+#endif /* RES2 */
+ } /* next trial */
+ return 0;
+}
+
+
+/* Simple linear interpolation */
+double
+lin(
+double x,
+double xa[],
+double ya[],
+int n) {
+ int i;
+ double y;
+
+ if (x < xa[0])
+ return ya[0];
+ else if (x > xa[n-1])
+ return ya[n-1];
+
+ for (i = 0; i < (n-1); i++)
+ if (x >=xa[i] && x <= xa[i+1])
+ break;
+
+ x = (x - xa[i])/(xa[i+1] - xa[i]);
+
+ y = ya[i] + (ya[i+1] - ya[i]) * x;
+
+ return y;
+}
+
+
+/******************************************************************/
+/* Error/debug output routines */
+/******************************************************************/
+
+/* Next u function done with optimization */
+
+/* Structure to hold data for optimization function */
+struct _edatas {
+ rspl *rss;
+ int j;
+ }; typedef struct _edatas edatas;
+
+#ifdef GLOB_CHECK
+/* Overall Global optimization method */
+/* Definition of the optimization function handed to powell() */
+double efunc2(void *edata, double p[])
+ {
+ int j;
+ double rv;
+ rspl *rss = (rspl *)edata;
+ for (j = 0; j < rss->nig; j++) /* Ugg */
+ rss->u[j].v = p[j];
+ rv = rss->efactor(rss);
+#ifdef DIAG2
+ /* printf("%c%e",cr_char,rv); */
+ printf("%e\n",rv);
+#endif
+ return rv;
+ }
+
+solveu(rss)
+rspl *rss;
+ {
+ int j;
+ double *cp;
+ double *s;
+
+ cp = dvector(0,rss->nig);
+ s = dvector(0,rss->nig);
+ for (j = 0; j < rss->nig; j++) /* Ugg */
+ {
+ cp[j] = rss->u[j].v;
+ s[j] = 0.1;
+ }
+ powell(rss->nig,cp,s,1e-7,1000,efunc2,(void *)rss);
+ }
+#endif /* GLOB_CHECK */
diff --git a/rspl/c1df.c b/rspl/c1df.c
new file mode 100644
index 0000000..5d8dc32
--- /dev/null
+++ b/rspl/c1df.c
@@ -0,0 +1,313 @@
+
+/************************************************/
+/* Investigate various curve approximations */
+/************************************************/
+
+/* Discrete regularized spline versions */
+/* Standard test with weak default function */
+
+/* Author: Graeme Gill
+ * Date: 20/11/2005
+ *
+ * Copyright 1995, 1996, 2005 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+#undef DIAG
+#undef DIAG2
+#undef GLOB_CHECK
+#define RES2 /* Do multiple test at various resolutions */
+#undef EXTRAFIT /* Test extra fitting effort */
+#define SMOOTH 1.0
+#define AVGDEV 0.0
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "numlib.h"
+#include "plot.h"
+#include "rspl.h"
+
+double lin();
+void usage(void);
+
+#define TRIALS 15 /* Number of random trials */
+#define SKIP 0 /* Number of random trials to skip */
+
+#define MIN_PNTS 1
+#define MAX_PNTS 7
+
+#define MIN_RES 20
+#define MAX_RES 300
+
+double xa[MAX_PNTS];
+double ya[MAX_PNTS];
+double wa[MAX_PNTS];
+
+#define XRES 100
+
+#define PNTS 2
+#define GRES 200
+//double t1xa[PNTS] = { 0.325, 0.625 };
+//double t1ya[PNTS] = { 0.4, 0.70 };
+double t1xa[PNTS] = { 0.325, 0.625 };
+double t1ya[PNTS] = { 0.5, 0.8 };
+double t1wa[PNTS] = { 1.0, 1.0 };
+cow test_points[MAX_PNTS];
+
+double lin(double x, double xa[], double ya[], int n);
+
+/* Weak default function */
+static void wfunc(void *cbntx, double *out, double *in) {
+ out[0] = in[0];
+}
+
+void usage(void) {
+ fprintf(stderr,"Test 1D rspl interpolation with weak default function\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: c1df [options]\n");
+ fprintf(stderr," -w wweight Set weak default function weight (default 1.0)\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ int i,j, n;
+ double x;
+ double xx[XRES];
+ double yy[6][XRES];
+ rspl *rss; /* incremental solution version */
+ datai low,high;
+ int gres[MXDI];
+ double avgdev[MXDO];
+ double wweight = 1.0;
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?') {
+ usage();
+
+ } else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
+ fa = nfa;
+ if (na == NULL) usage();
+ wweight = atof(na);
+ } else
+ usage();
+ } else
+ break;
+ }
+
+ low[0] = 0.0;
+ high[0] = 1.0;
+ avgdev[0] = AVGDEV;
+
+ error_program = "Curve1";
+
+ for (n = 0; n < TRIALS; n++) {
+ double lrand = 0.0; /* Amount of level randomness */
+ int pnts;
+ int fres;
+
+ if (n == 0) { /* Standard versions */
+ pnts = PNTS;
+ fres = GRES;
+ for (i = 0; i < pnts; i++) {
+ xa[i] = t1xa[i];
+ ya[i] = t1ya[i];
+ wa[i] = t1wa[i];
+ }
+ printf("Trial %d, points = %d, res = %d, level randomness = %f\n",n,pnts,fres,lrand);
+ } else { /* Random versions */
+ double xmx;
+ lrand = d_rand(0.0,0.1); /* Amount of level randomness */
+ pnts = i_rand(MIN_PNTS,MAX_PNTS);
+ fres = i_rand(MIN_RES,MAX_RES);
+
+ printf("Trial %d, points = %d, res = %d, level randomness = %f\n",n,pnts,fres,lrand);
+
+ /* Create X values */
+ xa[0] = d_rand(0.3, 0.5);
+ for (i = 1; i < pnts; i++)
+ xa[i] = xa[i-1] + d_rand(0.2,0.7);
+ xmx = d_rand(0.6, 0.9);
+ for (i = 0; i < pnts; i++) /* Divide out */
+ xa[i] *= (xmx/xa[pnts-1]);
+
+ /* Create y values */
+ for (i = 0; i < pnts; i++) {
+ ya[i] = xa[i] + d_rand(-lrand,lrand);
+ wa[i] = 1.0;
+ }
+ }
+
+ if (n < SKIP)
+ continue;
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS, 1, /* di */
+ 1); /* fdi */
+
+ for (i = 0; i < pnts; i++) {
+ test_points[i].p[0] = xa[i];
+ test_points[i].v[0] = ya[i];
+ test_points[i].w = wa[i];
+ }
+ gres[0] = fres;
+
+#ifdef RES2
+ if (n != 0) {
+#endif
+ /* Fit to scattered data */
+ rss->fit_rspl_w_df(rss,
+#ifdef EXTRAFIT
+ RSPL_EXTRAFIT | /* Extra fit flag */
+#endif
+ 0,
+ test_points, /* Test points */
+ pnts, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ low, high, /* Data scale */
+ SMOOTH, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL, /* iwidth */
+ wweight, /* weak function weight */
+ NULL, /* No context */
+ wfunc /* Weak function */
+ );
+
+ /* Display the result */
+ for (i = 0; i < XRES; i++) {
+ co tp; /* Test point */
+ x = i/(double)(XRES-1);
+ xx[i] = x;
+ yy[0][i] = lin(x,xa,ya,pnts);
+ tp.p[0] = x;
+ rss->interp(rss, &tp);
+ yy[1][i] = tp.v[0];
+ if (yy[1][i] < -0.2)
+ yy[1][i] = -0.2;
+ else if (yy[1][i] > 1.2)
+ yy[1][i] = 1.2;
+ }
+
+ do_plot(xx,yy[0],yy[1],NULL,XRES);
+
+#ifdef RES2
+ } else { /* Multiple resolution version */
+ int gresses[5];
+ for (j = 0; j < 5; j++) {
+#ifndef NEVER
+ if (j == 0)
+ gres[0] = fres/8;
+ else if (j == 1)
+ gres[0] = fres/4;
+ else if (j == 2)
+ gres[0] = fres/2;
+ else if (j == 3)
+ gres[0] = fres;
+ else
+ gres[0] = fres * 2;
+#else /* Check sensitivity to griding of data points */
+ if (j == 0)
+ gres[0] = 192;
+ else if (j == 1)
+ gres[0] = 193;
+ else if (j == 2)
+ gres[0] = 194;
+ else if (j == 3)
+ gres[0] = 195;
+ else
+ gres[0] = 196;
+#endif
+ gresses[j] = gres[0];
+
+ rss->fit_rspl_w_df(rss,
+#ifdef EXTRAFIT
+ RSPL_EXTRAFIT | /* Extra fit flag */
+#endif
+ 0,
+ test_points, /* Test points */
+ pnts, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ low, high, /* Data scale */
+ SMOOTH, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL, /* iwidth */
+ wweight, /* weak function weight */
+ NULL, /* No context */
+ wfunc /* Weak function */
+ );
+
+ /* Get the result */
+ for (i = 0; i < XRES; i++) {
+ co tp; /* Test point */
+ x = i/(double)(XRES-1);
+ xx[i] = x;
+ yy[0][i] = lin(x,xa,ya,pnts);
+ tp.p[0] = x;
+ rss->interp(rss, &tp);
+ yy[1+j][i] = tp.v[0];
+ if (yy[1+j][i] < -0.2)
+ yy[1+j][i] = -0.2;
+ else if (yy[1+j][i] > 1.2)
+ yy[1+j][i] = 1.2;
+ }
+ }
+
+ printf("Black = lin, Red = %d, Green = %d, Blue = %d, Yellow = %d, Purple = %d\n",
+ gresses[0], gresses[1], gresses[2], gresses[3], gresses[4]);
+ do_plot6(xx,yy[0],yy[1],yy[2],yy[3],yy[4],yy[5],XRES);
+ }
+#endif /* RES2 */
+ } /* next trial */
+ return 0;
+}
+
+
+double
+lin(
+double x,
+double xa[],
+double ya[],
+int n)
+ {
+ int i;
+ double y;
+
+ if (x < xa[0])
+ return ya[0];
+ else if (x > xa[n-1])
+ return ya[n-1];
+
+ for (i = 0; i < (n-1); i++)
+ if (x >=xa[i] && x <= xa[i+1])
+ break;
+
+ x = (x - xa[i])/(xa[i+1] - xa[i]);
+
+ y = ya[i] + (ya[i+1] - ya[i]) * x;
+
+ return y;
+ }
+
+
diff --git a/rspl/gam.c b/rspl/gam.c
new file mode 100644
index 0000000..f8d02d0
--- /dev/null
+++ b/rspl/gam.c
@@ -0,0 +1,1220 @@
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized spline data structure
+ *
+ * Precice gamut surface, gamut pruning, ink limiting and K min/max
+ * support routine.s
+ *
+ * Author: Graeme W. Gill
+ * Date: 2008/11/21
+ *
+ * Copyright 1999 - 2008 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ *
+ * Latest simplex/linear equation version.
+ */
+
+/* TTBD:
+
+ Add ouutput curve lookup callback support.
+ Cache these values in the vertex structures ?
+
+ Add ink limit support. This be done by breaking
+ a cell into a fixed geometry of smaller simplexes
+ by dividing the cell into two on each axis.
+
+ Need to then add scan that detects areas to prune,
+ that then ties in with rev code to mark such
+ areas out of gamut.
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <memory.h>
+#include <time.h>
+
+#include "rspl_imp.h"
+#include "numlib.h"
+#include "sort.h" /* Heap sort */
+#include "counters.h" /* Counter macros */
+
+#include "vrml.h" /* If there is VRML stuff here */
+
+/* Print a vectors value */
+#define DBGVI(text, dim, out, vec, end) \
+{ int pveci; \
+ printf("%s",text); \
+ for (pveci = 0 ; pveci < (dim); pveci++) \
+ printf(out,(vec)[pveci]); \
+ printf(end); \
+}
+
+/* Print a matrix value */
+#define DBGMI(text, rows, cols, out, mat, end) \
+{ int pveci, pvecr; \
+ printf("%s",text); \
+ for (pvecr = 0 ; pvecr < (rows); pvecr++) { \
+ for (pveci = 0 ; pveci < (cols); pveci++) \
+ printf(out,(mat)[pvecr][pveci]); \
+ if ((pvecr+1) < (rows)) \
+ printf("\n"); \
+ } \
+ printf(end); \
+}
+
+#undef VRML_TRACE /* Save a vrml at each step */
+
+/* Do an arbitrary printf */
+#define DBGI(text) printf text ;
+
+#define DEBUG1
+#undef DBG
+#undef DBGV
+#undef DBGM
+
+#undef NEVER
+#define ALWAYS
+
+#ifdef DEBUG1
+#undef DBGS
+#undef DBG
+#undef DBGV
+#undef DBGM
+#define DEBUG
+#define DBGS(xxx) xxx
+#define DBG(xxx) DBGI(xxx)
+#define DBGV(xxx) DBGVI xxx
+#define DBGM(xxx) DBGMI xxx
+#else
+#undef DEBUG
+#undef DBGS
+#undef DBG
+#undef DBGV
+#undef DBGM
+#define DBGS(xxx)
+#define DBG(xxx)
+#define DBGV(xxx)
+#define DBGM(xxx)
+#endif
+
+/* Convention is to use:
+ i to index grid points u.a
+ n to index data points d.a
+ e to index position dimension di
+ f to index output function dimension fdi
+ j misc and cube corners
+ k misc
+ */
+
+#define EPS (1e-10) /* Allowance for numeric error */
+
+/* ====================================================== */
+/* Support functions */
+
+/* Compute the norm (length) squared of a vector define by two points */
+static double norm33sq(double in1[3], double in0[3]) {
+ int j;
+ double rv;
+ for (rv = 0.0, j = 0; j < 3; j++) {
+ double tt = in1[j] - in0[j];
+ rv += tt * tt;
+ }
+ return rv;
+}
+
+
+static rvert *get_vert(rspl *s, int gix);
+
+/* Given an output value, return the gamut radius */
+static double gvprad(rspl *s, double *v) {
+ int f, fdi = s->fdi;
+ double rr = 0.0;
+
+ for (f = 0; f < fdi; f++) {
+ double tt;
+ tt = s->gam.scale[f] * (v[f] - s->gam.cent[f]);
+ rr += tt * tt;
+ }
+ rr = sqrt(rr);
+ return rr;
+}
+
+
+/* Given an output value, create the radial coordinate */
+static void radcoord(rspl *s, double *rad, double *v) {
+ int f, fdi = s->fdi;
+ double rr = 0.0;
+
+ for (f = 0; f < fdi; f++) {
+ double tt;
+ tt = s->gam.scale[f] * (v[f] - s->gam.cent[f]);
+ rr += tt * tt;
+ }
+ rr = sqrt(rr);
+
+ rad[0] = rr;
+}
+
+/* Given an output value and an edge, return the side, */
+/* 0 = -vem 1 = +ve */
+static int eside(rspl *s, redge *ep, double *v) {
+ int f, fdi = s->fdi;
+ double tt;
+
+ for (tt = 0.0, f = 0; f < fdi; f++) {
+ tt += v[f] * ep->pe[f];
+ }
+ tt += ep->pe[f];
+ return (tt >= 0.0 ? 1 : 0);
+}
+
+
+/* Given a list of nodes that form an sdi-1 sub-simplex, */
+/* return a list of nodes that could be added to make */
+/* an sdi sub-simplex. */
+/* Nodes are identified by their grid index. */
+/* All possible nodes are returned, even if they are already */
+/* in our surface triangulation. */
+/* NOTE that inodes will be sorted into sub-simplex order! */
+// ~~99 we aren't currently taking ink limit into account
+
+/* return nz on fatal error */
+static int get_ssimplex_nodes(
+rspl *s,
+int sdi, /* Dimensionality of target sub-simplex */
+rvert **inodes, /* sdi input nodes that form an sdi-1 dimensional input sub-simplex */
+int aonodes, /* Number of output nodes allowed for */
+int *nonodes, /* Number of output nodes set */
+rvert **onodes /* Space for up to 3^MXDI-1 output nodes */
+) {
+ int e, di = s->di;
+ int i, j, k;
+
+ *nonodes = 0;
+
+//printf("\n~1 get_ssimplex_nodes called with sdi = %d\n",sdi);
+ /* Sort the input nodes into normal sub-simplex order */
+ /* (We are assuming all the nodes are in the grid) */
+ for (i = 0; i < sdi-1; i++) {
+ for (j = i+1; j < sdi; j++) {
+ if (inodes[i]->gix < inodes[j]->gix) {
+ rvert *tt;
+ tt = inodes[i]; inodes[i] = inodes[j]; inodes[j] = tt;
+ }
+ }
+ }
+//printf("~2 input nodes = %d %d\n", inodes[0]-gix, inodes[1]->gix);
+
+ /* For each sub-simplex */
+ for (i = 0; i < s->gam.ssi[sdi].nospx; i++) {
+ int kk;
+//printf("~1 sub simplex %d\n",i);
+ /* For leaving out one node of the ssimplex in turn, */
+ /* check the inodes match the remaining ssimplex nodes */
+ for (kk = 0; kk <= sdi; kk++) { /* ssimplex node being left out */
+ int bix = 0; /* ssimplex base node */
+
+ if (kk == 0)
+ bix++;
+
+//printf("~1 anchor node %d = %d\n",j,s->gam.ssi[sdi].spxi[i].goffs[bix]);
+//printf("~2 candidate node offsets = %d %d %d\n",
+//s->gam.ssi[sdi].spxi[i].offs[0],
+//s->gam.ssi[sdi].spxi[i].offs[1],
+//s->gam.ssi[sdi].spxi[i].offs[2]);
+
+ for (j = k = 0; j < sdi; j++, k++) { /* Check all inodes[] */
+ if (k == kk)
+ k++; /* Skip the ssimplex node being left out */
+ if (inodes[j]->gix != (inodes[0]->gix + s->gam.ssi[sdi].spxi[i].goffs[k]
+ - s->gam.ssi[sdi].spxi[i].goffs[bix])) {
+//printf("~1 not a match\n");
+ break;
+ }
+//printf("~1 matched node %d\n",inodes[k]->gix);
+ }
+ if (j >= sdi) { /* they all match */
+//printf("~1 all match\n");
+ /* Check if kk offset to the base is still in the grid */
+ for (e = 0; e < di; e++) {
+ int doff = ((s->gam.ssi[sdi].spxi[i].offs[kk] >> e) & 1)
+ - ((s->gam.ssi[sdi].spxi[i].offs[bix] >> e) & 1);
+ int eflags = G_FL(inodes[0]->fg,e);
+//printf("~1 checking dim %d, doff = %d, eflags = %d\n",e,doff,eflags);
+ if ((doff < 0 && (eflags & 4) && (eflags & 3) < 1)
+ || (doff > 0 && !(eflags & 4) && (eflags & 3) < 1)) {
+//printf("~1 outside grid\n");
+ break; /* Offset will take us past edge */
+ }
+ }
+ if (e >= di) { /* Remaining point is within grid */
+ int gix;
+
+ /* Add the remaining node to the onodes */
+ if (*nonodes >= aonodes)
+ return 1; /* Oops - ran out of return space! */
+
+ gix = inodes[0]->gix + s->gam.ssi[sdi].spxi[i].goffs[kk]
+ - s->gam.ssi[sdi].spxi[i].goffs[bix];
+ onodes[*nonodes] = get_vert(s, gix);
+
+//printf("~1 Returning node %d\n",(onodes[*nonodes]->gix);
+//printf("~1 Value %f %f %f\n", onodes[*nonodes]->p[0], onodes[*nonodes]->p[1], onodes[*nonodes]->p[2]);
+ (*nonodes)++;
+ }
+ }
+ }
+ }
+//printf("~1 onodes = %d\n",*nonodes);
+ return 0;
+}
+
+/* ====================================================== */
+/* Search for an existing vertex, and return it. */
+/* If there is no existing edge, create it. */
+/* (This is where we apply the output functions to the grid values) */
+static rvert *get_vert(rspl *s, int gix) {
+ int f, fdi = s->fdi;
+ int hash;
+ rvert *vp = NULL;
+
+ if (gix < 0 || gix >= s->g.no) { /* Assert */
+ error("rspl_gam: get_vert got out of range gix %d\n",gix);
+ }
+
+ /* See if it is in our hash list */
+ hash = gix % s->gam.vhsize;
+
+ for (vp = s->gam.verts[hash]; vp != NULL; vp = vp->next) {
+ if (vp->gix == gix)
+ break;
+ }
+ if (vp == NULL) { /* No such vertex */
+ float *fg;
+
+ if ((vp = calloc(1, sizeof(rvert))) == NULL)
+ error("rspl_gam: get_vert calloc failed");
+
+ fg = s->g.a + gix * s->g.pss;
+ vp->n = s->gam.rvert_no++; /* serial number */
+ vp->fg = fg; /* Pointer to node float data */
+ vp->gix = gix; /* grid index */
+ for (f = 0; f < fdi; f++) /* Node output value */
+ vp->v[f] = (double)fg[f];
+ if (s->gam.outf != NULL)
+ s->gam.outf(s->gam.cntxf, vp->v, vp->v); /* Apply output lookup */
+
+ radcoord(s, vp->r, vp->v); /* Compute radial coordinate */
+
+ /* Add vertex to hash */
+ vp->next = s->gam.verts[hash];
+ s->gam.verts[hash] = vp;
+
+ /* Add the vertex to the bottom of the list of vertex */
+ if (s->gam.vbot == NULL) {
+ s->gam.vtop = s->gam.vbot = vp;
+ } else {
+ s->gam.vbot->list = vp;
+ s->gam.vbot = vp;
+ }
+ }
+
+ return vp;
+}
+
+/* Search for an existing edge containing the given verticies, */
+/* and return it. If there is no existing edge, create it. */
+/* Note that edges have fdi-2 dimensions == fdi-1 verticies. */
+static redge *get_edge(rspl *s, rvert **_vv) {
+ int i, j, f, fdi = s->fdi;
+ rvert *vv[MXDO-1]; /* Sorted verticies */
+ int gix, hash;
+ redge *ep = NULL;
+
+ /* Sort the input nodes into normal sub-simplex order */
+ /* (We are assuming all the nodes are in the grid) */
+ for (i = 0; i < (fdi-1); i++)
+ vv[i] = _vv[i];
+ for (i = 0; i < (fdi-2); i++) {
+ for (j = i+1; j < (fdi-1); j++) {
+ if (vv[i]->gix < vv[j]->gix) {
+ rvert *tt;
+ tt = vv[i]; vv[i] = vv[j]; vv[j] = tt;
+ }
+ }
+ }
+ for (gix = i = 0; i < (fdi-1); i++)
+ gix += vv[i]->gix;
+//printf("~1 get edge with nodes = %d %d\n", vv[0]->gix, vv[1]->gix);
+
+ /* See if it is in our hash list */
+ hash = gix % s->gam.ehsize;
+//printf("~1 get edge gix = %d, hash = %d\n", gix, hash);
+
+ for (ep = s->gam.edges[hash]; ep != NULL; ep = ep->next) {
+ for (i = 0; i < (fdi-1); i++) {
+ if (ep->v[i] != vv[i]) {
+//printf("~1 verticies don't match\n");
+ break; /* No match */
+ }
+ }
+ if (i >= (fdi-1)) {
+//printf("~1 all verticies match\n");
+ break; /* All match */
+ }
+ }
+ if (ep == NULL) { /* No such edge */
+ int sm, lg;
+
+ if ((ep = calloc(1, sizeof(redge))) == NULL)
+ error("rspl_gam: get_edge calloc failed");
+
+ ep->n = s->gam.redge_no++; /* serial number */
+ for (i = 0; i < (fdi-1); i++)
+ ep->v[i] = vv[i]; /* Vertex */
+printf("~1 new edge %d with nodes = %d %d\n", ep->n, ep->v[0]->gix, ep->v[1]->gix);
+
+ /* Compute plane equation to center of gamut, so */
+ /* that we can quickly determine which side a triangle lies on. */
+
+ /* Compute plane equation */
+ if (fdi < 2 || fdi > 3)
+ error("rspl_gam: plane equation for out dimensions other than 2 or 3 not supported!");
+ if (fdi == 2) {
+
+ } else {
+ double v1[3], v2[3];
+
+ /* Compute two vectors from the three points */
+ for (f = 0; f < fdi; f++) {
+ v1[f] = ep->v[0]->v[f] - s->gam.cent[f];
+ v2[f] = ep->v[1]->v[f] - s->gam.cent[f];
+ }
+
+ /* Compute normal to the plane using the cross product */
+ ep->pe[0] = ep->v[0]->v[1] * (ep->v[1]->v[2] - s->gam.cent[2])
+ + ep->v[1]->v[1] * (s->gam.cent[2] - ep->v[0]->v[2])
+ + s->gam.cent[1] * (ep->v[0]->v[2] - ep->v[1]->v[2]);
+ ep->pe[1] = ep->v[0]->v[2] * (ep->v[1]->v[0] - s->gam.cent[0])
+ + ep->v[1]->v[2] * (s->gam.cent[0] - ep->v[0]->v[0])
+ + s->gam.cent[2] * (ep->v[0]->v[0] - ep->v[1]->v[0]);
+ ep->pe[2] = ep->v[0]->v[0] * (ep->v[1]->v[1] - s->gam.cent[1])
+ + ep->v[1]->v[0] * (s->gam.cent[1] - ep->v[0]->v[1])
+ + s->gam.cent[0] * (ep->v[0]->v[1] - ep->v[1]->v[1]);
+ ep->pe[3] = - (ep->v[0]->v[0] * (ep->v[1]->v[1] * s->gam.cent[2] - s->gam.cent[1] * ep->v[1]->v[2])
+ + ep->v[1]->v[0] * (s->gam.cent[1] * ep->v[0]->v[2] - ep->v[0]->v[1] * s->gam.cent[2])
+ + s->gam.cent[0] * (ep->v[0]->v[1] * ep->v[1]->v[2] - ep->v[1]->v[1] * ep->v[0]->v[2]));
+
+ }
+
+ /* Add edge to hash */
+ ep->next = s->gam.edges[hash];
+ s->gam.edges[hash] = ep;
+
+ /* Add the edge to the bottom of the list of edges */
+ if (s->gam.ebot == NULL) {
+ s->gam.etop = s->gam.ebot = ep;
+ } else {
+ s->gam.ebot->list = ep;
+ s->gam.ebot = ep;
+ }
+ }
+
+printf("~1 returning edge no %d\n",ep->n);
+ return ep;
+}
+
+/* Check whether a triangle like this already exists */
+/* Return NULL if it doesn't */
+static rtri *check_tri(rspl *s, redge *ep, rvert *_vv) {
+ int i, j, k, f, fdi = s->fdi;
+ rvert *vv[MXDO]; /* Sorted verticies */
+ int gix, hash;
+ rtri *tp = NULL;
+
+ /* Copy edge verticies from edge */
+ for (i = 0; i < (fdi-1); i++)
+ vv[i] = ep->v[i];
+ vv[i] = _vv; /* And new vertex */
+
+ /* Sort verticies */
+ for (i = 0; i < (fdi-1); i++) {
+ for (j = i+1; j < fdi; j++) {
+ if (vv[i]->gix < vv[j]->gix) {
+ rvert *tv;
+ tv = vv[i]; vv[i] = vv[j]; vv[j] = tv;
+ }
+ }
+ }
+
+ /* Create hash */
+ for (gix = i = 0; i < fdi; i++)
+ gix += vv[i]->gix;
+
+ /* See if it is in our hash list */
+ hash = gix % s->gam.thsize;
+//printf("~1 make tri gix = %d, hash = %d\n", gix, hash);
+
+ for (tp = s->gam.tris[hash]; tp != NULL; tp = tp->next) {
+ for (i = 0; i < fdi; i++) {
+ if (tp->v[i] != vv[i]) {
+//printf("~1 verticies don't match\n");
+ break; /* No match */
+ }
+ }
+ if (i >= fdi) {
+//printf("~1 all verticies match\n");
+ break; /* All match */
+ }
+ }
+
+ return tp;
+}
+
+/* Create a triangle given an edge with fdi-1 verticies and a grid vertex. */
+/* if inv is set, triangle is upside down wrt to center point, */
+/* and so all the oppositive verticies should be placed on the */
+/* opposite to their natural side. */
+static rtri *make_tri(rspl *s, redge *ep, rvert *_vv, int inv) {
+ int i, j, k, f, fdi = s->fdi;
+ rvert *vv[MXDO]; /* Sorted verticies */
+ int gix, hash;
+ rtri *tp = NULL;
+
+printf("~1 make_tri called\n");
+
+ /* Copy edge verticies from edge */
+ for (i = 0; i < (fdi-1); i++)
+ vv[i] = ep->v[i];
+ vv[i] = _vv; /* And new vertex */
+
+ /* Sort verticies */
+ for (i = 0; i < (fdi-1); i++) {
+ for (j = i+1; j < fdi; j++) {
+ if (vv[i]->gix < vv[j]->gix) {
+ rvert *tv;
+ tv = vv[i]; vv[i] = vv[j]; vv[j] = tv;
+ }
+ }
+ }
+
+ /* Create hash */
+ for (gix = i = 0; i < fdi; i++)
+ gix += vv[i]->gix;
+
+ /* See if it is in our hash list */
+ hash = gix % s->gam.thsize;
+printf("~1 make tri gix = %d, hash = %d\n", gix, hash);
+
+ for (tp = s->gam.tris[hash]; tp != NULL; tp = tp->next) {
+ for (i = 0; i < fdi; i++) {
+ if (tp->v[i] != vv[i]) {
+//printf("~1 verticies don't match\n");
+ break; /* No match */
+ }
+ }
+ if (i >= fdi) {
+//printf("~1 all verticies match\n");
+ break; /* All match */
+ }
+ }
+
+ if (tp == NULL) { /* No such triangle */
+
+//printf("~1 creating a new triangle\n");
+ /* Create triangle */
+ if ((tp = calloc(1, sizeof(rtri))) == NULL)
+ error("rspl_gam: make_tri calloc failed");
+
+ tp->n = s->gam.rtri_no++; /* serial number */
+
+ /* Copy edge verticies to triangle */
+ for (i = 0; i < fdi; i++)
+ tp->v[i] = vv[i];
+
+ /* Link all the triangles edges to this triangle */
+ printf("~1 triangle nodes = %d %d %d\n", tp->v[0]->gix, tp->v[1]->gix, tp->v[2]->gix);
+//printf("~2 triangle vert 0 = %f %f %f\n", tp->v[0]->v[0], tp->f[0]->v[1], tp->f[0]->v[2]);
+//printf("~2 triangle vert 1 = %f %f %f\n", tp->v[1]->v[0], tp->f[1]->v[1], tp->f[1]->v[2]);
+//printf("~2 triangle vert 2 = %f %f %f\n", tp->v[2]->v[0], tp->f[2]->v[1], tp->f[2]->v[2]);
+ for (i = 0; i < fdi; i++) { /* For each tri verticy being odd one out */
+ rvert *ov, *rr[MXDO-1]; /* Odd verticy, remaining edge verticies */
+ int ss; /* Side */
+
+ ov = tp->v[i]; /* Odd node */
+ for (k = j = 0; j < fdi; j++) {
+ if (i == j)
+ continue;
+ rr[k++] = tp->v[j]; /* Remaining nodes */
+ }
+//printf("~1 edge nodes = %d %d, odd = %d\n", rr[0]->gix, rr[1]->gix, ov->gix);
+ ep = get_edge(s, rr);
+
+ if (ep->nt >= MXNE)
+ error("rspl_gam: make_tri run out of triangle space %d in edge",MXNE);
+ ep->t[ep->nt++] = tp;
+
+ /* See which side of the edge the remaining vertex is */
+ ss = eside(s, ep, ov->v);
+//printf("~1 node gix %d has side %d to edge %d %d\n", ov->gix, ss, ep->v[0]->gix, ep->v[1]->gix);
+ if (inv)
+ ss = 1-ss;
+ if (ss) { /* +ve side */
+ ep->t[ep->npt++] = tp;
+ } else {
+ ep->t[ep->nnt++] = tp;
+ }
+ }
+
+ /* Add triangle to hash */
+ tp->next = s->gam.tris[hash];
+ s->gam.tris[hash] = tp;
+
+ /* Add them to the linked list */
+ tp->list = s->gam.ttop;
+ s->gam.ttop = tp;
+
+ }
+
+ return tp;
+}
+
+
+/* ====================================================== */
+
+/* Create a surface gamut representation. */
+/* Return NZ on error */
+/* Could add more flexibility with:
+ optional function instead of default radial distance function
+ option use sub set of output dimensions (ie allow for CMYK->LabK etc. ? )
+*/
+static int
+gam_comp_gamut(
+rspl *s,
+double *cent, /* Optional center of gamut [fdi], default center of out range */
+double *scale, /* Optional Scale of output values in vector to center [fdi], def. 1.0 */
+void (*outf)(void *cntxf, double *out, double *in), /* Optional rspl val -> output value */
+void *cntxf, /* Context for function */
+void (*outb)(void *cntxb, double *out, double *in), /* Optional output value -> rspl val */
+void *cntxb /* Context for function */
+) {
+ int e, f, di = s->di, fdi = s->fdi;
+ int i, j, ssdi;
+ int maxp[MXDO]; /* Grid indexes of maxium function values */
+ rvert *fedge[MXDO-1]; /* The first "edge" containing fdi-1 verticies */
+ rvert *onodes[50]; /* float pointers of canditate nodes */
+ int aonodes = 50; /* Allocated out nodes */
+ int nonodes; /* Number of nodes returned */
+
+ rvert *cnodes[2][50]; /* Negative, positive candidate nodes */
+ double rcnodes[2][50]; /* Radius of negative, positive candidate nodes */
+ int ncnodes[2]; /* Number of negative, positive candidate nodes */
+
+ if (fdi < 2 || fdi > di) {
+ DBG(("gam: gam_comp_gamut called for di = %d, fdi = %d\n", di, fdi));
+ return 2;
+ }
+
+ /* Save output value conversion functions */
+ s->gam.outf = outf;
+ s->gam.cntxf = cntxf;
+ s->gam.outb = outb;
+ s->gam.cntxb = cntxb;
+
+ /* Deal with gamut center point, and scale */
+ if (cent == NULL) {
+ double min[MXDO], max[MXDO];
+
+ s->get_out_range(s, min, max);
+ if (s->gam.outf != NULL) {
+ s->gam.outf(s->gam.cntxf, min, min); /* Apply output lookup */
+ s->gam.outf(s->gam.cntxf, max, max); /* Apply output lookup */
+ }
+ for (f = 0; f < fdi; f++)
+ s->gam.cent[f] = 0.5 * (min[f] + max[f]);
+ } else {
+ for (f = 0; f < fdi; f++)
+ s->gam.cent[f] = cent[f];
+ }
+ DBGVI("Gamut center is ", fdi, "%f ", s->gam.cent, "\n")
+
+ if (scale == NULL) {
+ for (f = 0; f < fdi; f++)
+ s->gam.scale[f] = 1.0;
+ } else {
+ for (f = 0; f < fdi; f++)
+ s->gam.scale[f] = scale[f];
+ }
+ DBGVI("Gamut scale is ", fdi, "%f ", s->gam.scale, "\n")
+
+ for (ssdi = 1; ssdi <= fdi-1; ssdi++) {
+ int i, j;
+
+ /* Compute gamut surface sub-simplex geometry info */
+ rspl_init_ssimplex_info(s, &s->gam.ssi[ssdi], ssdi);
+
+ /* Filter the sub-simplex geometry to only include subsimplexes that */
+ /* use the base vertex, so that there are no duplicates. */
+ for (i = j = 0; i < s->gam.ssi[ssdi].nospx; i++) {
+ if (s->gam.ssi[ssdi].spxi[i].offs[s->gam.ssi[ssdi].sdi] == 0) {
+ s->gam.ssi[ssdi].spxi[j] = s->gam.ssi[ssdi].spxi[i]; /* Structure copy */
+ j++;
+ }
+ }
+ s->gam.ssi[ssdi].nospx = j;
+
+#ifdef DEBUG
+ printf("Sub-simplex dim %d out of input %d\n",s->gam.ssi[ssdi].sdi,di);
+ printf("Number of subsimplex = %d\n",s->gam.ssi[ssdi].nospx);
+ for (i = 0; i < s->gam.ssi[ssdi].nospx; i++) {
+ printf("Cube Offset = ");
+ for (e = 0; e <= s->gam.ssi[ssdi].sdi; e++)
+ printf("%d ",s->gam.ssi[ssdi].spxi[i].offs[e]);
+ printf("\n");
+
+ printf("Grid Offset = ");
+ for (e = 0; e <= s->gam.ssi[ssdi].sdi; e++)
+ printf("%d ",s->gam.ssi[ssdi].spxi[i].foffs[e]);
+ printf("\n");
+ printf("\n");
+ }
+#endif /* DEBUG */
+ }
+
+ /* Allocate the vertex hash array */
+ if ((s->gam.verts = calloc(VHASHSIZE, sizeof(rvert *))) == NULL) {
+ DBG(("gam: allocating vertex hash array failed\n"));
+ return 1;
+ }
+ s->gam.vhsize = VHASHSIZE;
+
+ /* Allocate the edge hash array */
+ if ((s->gam.edges = calloc(EHASHSIZE, sizeof(redge *))) == NULL) {
+ DBG(("gam: allocating edge hash array failed\n"));
+ return 1;
+ }
+ s->gam.ehsize = EHASHSIZE;
+
+ /* Allocate the triangle hash array */
+ if ((s->gam.tris = calloc(THASHSIZE, sizeof(rtri *))) == NULL) {
+ DBG(("gam: allocating tris hash array failed\n"));
+ return 1;
+ }
+ s->gam.thsize = THASHSIZE;
+
+ /* Get a starting gid point for surface */
+ // ~~99 this isn't right if the point we get is over the ink limit!!!. */
+ s->get_out_range_points(s, NULL, maxp); /* Maximum */
+// s->get_out_range_points(s, maxp, NULL); /* Minium */
+
+ DBG(("Starting point = gix %d/%d\n",maxp[0], s->g.no));
+
+ /* Now work it up to an fdi-2 sub-simplex, so that we have a */
+ /* "triangle edge", and can enter the main loop. */
+ // ~~~9 this needs fixing to switch to "maximum angle" calculation
+ fedge[0] = get_vert(s, maxp[0]); /* grid index of first vertex */
+
+ if (fdi == 2) {
+ /* We just need one point, and we've got it */
+
+ } else if (fdi == 3) { /* Usual case */
+ double ba = -1.0; /* Bigest angle */
+ /* We just need two points, so find the other points */
+ /* that makes the greatest angle to the center */
+
+ if (get_ssimplex_nodes(s, 1, fedge, aonodes, &nonodes, onodes)) {
+ error("rspl_gam: get_ssimplex_nodes fatal error - too many nodes?");
+ }
+ if (nonodes == 0)
+ error("rspl_gam: get_ssimplex_nodes fatal error - retrurned no nodes?");
+
+printf("~1 get_ssimplex_nodes returned %d nodes\n",nonodes);
+for(i = 0; i < nonodes; i++)
+printf(" ~1 %d: %d\n",i,onodes[i]->gix);
+
+ /* Evaluate the choice of verticies to choose the one that */
+ /* will best enclose the gamut. (We use a really dumb criteria - maximum radius) */
+ for (i = 0; i < nonodes; i++) {
+ double tt, a, b, c; /* Lenght of sides of triangle squared */
+ a = norm33sq(onodes[i]->v, s->gam.cent);
+ b = norm33sq(onodes[i]->v, fedge[0]->v);
+ c = norm33sq(fedge[0]->v, s->gam.cent);
+ tt = acos((b + c - a)/(2.0 * sqrt(b * c)));
+printf("~1 node %d angle = %f\n",onodes[i]->gix,tt);
+ if (tt > ba) {
+ ba = tt;
+ fedge[1] = onodes[i]; /* Use candidate with largest gamut as next base */
+ }
+ }
+printf("~1 chosen node %d\n",fedge[1]->gix);
+
+ } else if (fdi > 3) { /* General case */
+ /* This isn't a correct approach ... */
+
+ for (ssdi = 1; ssdi <= fdi-2; ssdi++) {
+ double br = -1.0; /* Bigest radius */
+
+ DBG(("Working up ssdim %d -> %d\n",ssdi-1, ssdi));
+
+ if (get_ssimplex_nodes(s, ssdi, fedge, aonodes, &nonodes, onodes)) {
+ error("rspl_gam: get_ssimplex_nodes fatal error - too many nodes?");
+ }
+ if (nonodes == 0)
+ error("rspl_gam: get_ssimplex_nodes fatal error - retrurned no nodes?");
+
+printf("~1 get_ssimplex_nodes returned %d nodes\n",nonodes);
+for(i = 0; i < nonodes; i++)
+printf(" ~1 %d: %d\n",i,onodes[i]->gix);
+
+ /* Evaluate the choice of verticies to choose the one that */
+ /* will best enclose the gamut. (We use a really dumb criteria - maximum radius) */
+ for (i = 0; i < nonodes; i++) {
+ double tt;
+ tt = gvprad(s, onodes[i]->v); /* Compute radius */
+ if (tt > br) {
+ br = tt;
+ fedge[ssdi] = onodes[i]; /* Use candidate with largest gamut as next base */
+ }
+ }
+printf("~1 chosen node %d\n",fedge[ssdi]->gix);
+ }
+ }
+
+ /* Creat the initial "edge" */
+ get_edge(s, fedge);
+
+printf("~1 Created initial edge\n");
+
+ {
+ redge *ep;
+
+ double **A; /* lu value -> baricentric matrix */
+ double *B;
+ int *pivx;
+
+ pivx = ivector(0, fdi-1); /* pixv[fdi] */
+ A = dmatrix(0, fdi-1, 0, fdi-1); /* A[fdi][fdi] */
+ B = dvector(0, fdi-1); /* B[fdi] */
+
+ /* The main loop: */
+ /* We start with any "edges" that have less than two associated "triangles", */
+ /* and evaluate the vertex nodes that can make "triangles" with the "edge". */
+ /* We choose the node that will give the greatest slope, and make a "triangle". */
+ /* Loop until there are no "edges" with less than two associated "triangles". */
+ for (ep = s->gam.etop; ep != NULL; ep = ep->list) {
+
+printf("~1 expanding from edge no %d\n",ep->n);
+printf("~1 edge v1 = %d = %f %f %f\n", ep->v[0]->gix, ep->v[0]->v[0], ep->v[0]->v[1], ep->v[0]->v[2]);
+printf("~1 edge v2 = %d = %f %f %f\n", ep->v[1]->gix, ep->v[1]->v[0], ep->v[1]->v[1], ep->v[1]->v[2]);
+
+// if (ep->npt < 1 || ep->nnt < 1)
+ {
+ int ss;
+
+ if (get_ssimplex_nodes(s, fdi-1, ep->v, aonodes, &nonodes, onodes)) {
+ error("rspl_gam: get_ssimplex_nodes fatal error - too many nodes?");
+ }
+
+ /* Clasify the returned nodes as positive or negative side */
+ ncnodes[0] = ncnodes[1] = 0;
+ for (i = 0; i < nonodes; i++) {
+ double tt;
+ tt = gvprad(s, onodes[i]->v); /* Compute radius */
+ ss = eside(s, ep, onodes[i]->v); /* Side */
+printf("~1 node gix %d has rad %f side %d to edge %d %d\n", onodes[i]->gix, tt, ss, ep->v[0]->gix, ep->v[1]->gix);
+
+#ifdef NEVER /* This messes things up ? */
+ /* Check if this node is already in a triangle with this edge, */
+ /* to avoid costly matrix solution check below. */
+ if (check_tri(s, ep, onodes[i]) == NULL)
+#endif
+ {
+ cnodes[ss][ncnodes[ss]] = onodes[i];
+ rcnodes[ss][ncnodes[ss]++] = tt;
+ }
+ }
+
+ /* Sort them */
+ for (ss = 0; ss < 2; ss++) { /* Negative side then positive */
+ for (i = 0; i < (ncnodes[ss]-1); i++) {
+ for (j = i+1; j < ncnodes[ss]; j++) {
+ if (rcnodes[ss][i] < rcnodes[ss][j]) {
+ rvert *tt;
+ double tr;
+ tt = cnodes[ss][i]; cnodes[ss][i] = cnodes[ss][j];
+ cnodes[ss][j] = tt;
+ tr = rcnodes[ss][i]; rcnodes[ss][i] = rcnodes[ss][j];
+ rcnodes[ss][j] = tr;
+ }
+ }
+ }
+ }
+
+// ~~1
+for (ss = 0; ss < 2; ss++) { /* Negative side then positive */
+ if (ss == 0)
+ printf("~1 -ve nodes:\n");
+ else
+ printf("~1 +ve nodes:\n");
+ for (i = 0; i < ncnodes[ss]; i++) {
+ printf("~1 node %d, rad %f\n",cnodes[ss][i]->gix,rcnodes[ss][i]);
+ }
+}
+
+#ifdef VRML_TRACE
+{
+ vrml *wrl;
+ int i, j, k;
+ ECOUNT(gc, MXDIDO, di, 0, s->g.res, 0);/* coordinates */
+ DCOUNT(cc, MXDIDO, s->di, 0, 0, 2); /* Surrounding cube counter */
+ float *gp; /* Grid point pointer */
+ rvert *vp;
+ rtri *tp;
+ double col[3], pos[3];
+
+ if ((wrl = new_vrml("gam_diag.wrl", 1)) == NULL)
+ error("new_vrml failed\n");
+
+ /* Display the grid */
+ EC_INIT(gc);
+ for (gp = s->g.a; !EC_DONE(gc); gp += s->g.pss) {
+ /* Itterate cube from this base */
+ DC_INIT(cc)
+ for (i = 0; !DC_DONE(cc); i++ ) {
+ float *sp = s->g.a;
+
+ for (e = 0; e < s->di; e++) { /* Input tables */
+ int j;
+ j = gc[e] + cc[e];
+ if (j < 0 || j >= s->g.res[e]) {
+ sp = NULL; /* outside grid */
+ break;
+ }
+ sp += s->g.fci[e] * j; /* Compute pointer to surrounder */
+ }
+ if (i> 0 && e >= s->di) {
+ /* Create vector from base to surrounder */
+ pos[0] = (double)gp[0];
+ pos[1] = (double)gp[1];
+ pos[2] = (double)gp[2];
+ if (s->gam.outf != NULL)
+ s->gam.outf(s->gam.cntxf, pos, pos); /* Apply output lookup */
+ wrl->add_vertex(wrl, pos);
+ pos[0] = (double)sp[0];
+ pos[1] = (double)sp[1];
+ pos[2] = (double)sp[2];
+ if (s->gam.outf != NULL)
+ s->gam.outf(s->gam.cntxf, pos, pos); /* Apply output lookup */
+ wrl->add_vertex(wrl, pos);
+ }
+ DC_INC(cc);
+ }
+ EC_INC(gc);
+ }
+ wrl->make_lines(wrl, 2);
+ wrl->clear(wrl);
+
+ /* Display the current triangles transparently */
+ if (s->gam.ttop != NULL) {
+ for (vp = s->gam.vtop; vp != NULL; vp = vp->list) {
+ wrl->add_vertex(wrl, vp->v);
+ }
+
+ /* Set the triangles */
+ for (tp = s->gam.ttop; tp != NULL; tp = tp->list) {
+ int ix[3];
+ ix[0] = tp->v[0]->n;
+ ix[1] = tp->v[1]->n;
+ ix[2] = tp->v[2]->n;
+ wrl->add_triangle(wrl, ix);
+ }
+
+// wrl->make_triangles(wrl, 0.3, NULL);
+ wrl->make_triangles(wrl, 0.0, NULL);
+ wrl->clear(wrl);
+ }
+
+ /* Show the active edge as a red cone */
+ col[0] = 1.0; col[1] = 0.0; col[2] = 0.0; /* Red */
+ wrl->add_cone(wrl, ep->v[0]->v, ep->v[1]->v, col, 1.0);
+printf("~1 edge v1 = %d = %f %f %f\n", ep->v[0]->gix, ep->v[0]->v[0], ep->v[0]->v[1], ep->v[0]->v[2]);
+printf("~1 edge v2 = %d = %f %f %f\n", ep->v[1]->gix, ep->v[1]->v[0], ep->v[1]->v[1], ep->v[1]->v[2]);
+
+ /* Show the candidate verticies */
+ for (ss = 0; ss < 2; ss++) {
+ for (i = 0; i < ncnodes[ss]; i++) { /* +ve */
+ if (ss) {
+ col[0] = 0.0; col[1] = 1.0; col[2] = 0.0; /* Green +ve */
+ } else {
+ col[0] = 0.0; col[1] = 0.0; col[2] = 1.0; /* Blue -ve */
+ }
+ wrl->add_marker(wrl, cnodes[ss][i]->v, col, 1.0);
+ }
+ }
+ wrl->del(wrl);
+ getchar();
+}
+#endif
+ /* See if we can make a triangle */
+ for (ss = 0; ss < 2; ss++) { /* For -ve and +ve sides */
+ int ii, si;
+ int sta, end, inc;
+ rvert **nods;
+ double rip; /* Row interchange parity */
+
+printf("~1 direction = %d\n",ss);
+#ifdef NEVER
+ if (( ss && ep->npt >= 1) /* Not looking for a positive node */
+ || (!ss && ep->nnt >= 1)) { /* Not looking for a negative node */
+printf("~1 no need to look for node in this direction\n");
+ continue;
+ }
+#endif
+
+ if (ncnodes[ss] > 0) { /* There are nodes in wanted direction */
+ si = ss; /* use wanted direction nodes */
+ sta = 0; /* Start at max radius node */
+ end = ncnodes[si]; /* End and least radius node */
+ inc = 1; /* Increment */
+ nods = cnodes[si]; /* +ve nodes */
+printf("~1 Looking for biggest angle, inc = %d\n",inc);
+#ifdef NEVER /* Convex tracing ? */
+ } else if (ncnodes[1-ss] > 0) { /* There are opposited direction nodes */
+ si = 1-ss; /* use opposite direction nodes */
+ sta = ncnodes[si]-1; /* Start at min radius node */
+ end = -1; /* end and max radius node */
+ inc = -1; /* Decrement */
+ nods = cnodes[si];
+printf("~1 Looking for smallest angle, inc = %d\n",-1);
+#endif /* NEVER */
+ } else {
+printf("~1 No points to search\n");
+ continue;
+ }
+ ii = 0;
+ if (ncnodes[si] > 1) { /* If there is more than one to choose from */
+ /* Go through each candidate in the most likely order */
+ for (ii = sta; ii != end; ii += inc) {
+
+printf("~1 Candidate %d: node %d\n",ii,nods[ii]->gix);
+ /* Create the baricentric conversion for this candidate */
+ for (f = 0; f < fdi; f++) /* The center point */
+ A[f][0] = s->gam.cent[f] - nods[ii]->v[f];
+ for (j = 0; j < (fdi-1); j++) { /* The edge points */
+ for (f = 0; f < fdi; f++)
+ A[f][j+1] = ep->v[j]->v[f] - nods[ii]->v[f];
+ }
+
+ if (lu_decomp(A, fdi, pivx, &rip)) {
+printf("~1 lu_decomp failed\n");
+for (f = 0; f < fdi; f++) /* The center point */
+ A[f][0] = s->gam.cent[f] - nods[ii]->v[f];
+for (j = 0; j < (fdi-1); j++) { /* The edge points */
+ for (f = 0; f < fdi; f++)
+ A[f][j+1] = ep->v[j]->v[f] - nods[ii]->v[f];
+}
+printf("~1 A = \n");
+printf("~1 %f %f %f\n", A[0][0], A[0][1], A[0][2]);
+printf("~1 %f %f %f\n", A[1][0], A[1][1], A[1][2]);
+printf("~1 %f %f %f\n", A[2][0], A[2][1], A[2][2]);
+ warning("lu_decomp failed");
+ continue;
+ }
+
+ /* Test the remainder candidates against this simplex */
+ for (i = sta; i != end; i += inc) {
+ if (i == ii)
+ continue;
+
+printf("~1 Test %d: node %d\n",i,nods[i]->gix);
+ for (f = 0; f < fdi; f++) /* The candidate point */
+ B[f] = nods[i]->v[f] - nods[ii]->v[f];
+ lu_backsub(A, fdi, pivx, B);
+
+#ifdef NEVER
+printf("~1 baricentric = %f %f %f %f\n",B[0],B[1],B[2],1.0-B[0]-B[1]-B[2]);
+{
+double tt[3], B3;
+/* Check baricentric */
+B3 = 1.0-B[0]-B[1]-B[2];
+for (f = 0; f < fdi; f++)
+ tt[f] = 0.0;
+for (f = 0; f < fdi; f++)
+ tt[f] += B[0] * s->gam.cent[f];
+for (f = 0; f < fdi; f++)
+ tt[f] += B[1] * ep->v[0]->v[f];
+for (f = 0; f < fdi; f++)
+ tt[f] += B[2] * ep->v[1]->v[f];
+for (f = 0; f < fdi; f++)
+ tt[f] += B3 * nods[ii]->v[f];
+printf("~1 target point %f %f %f\n", (double)nods[i][0], (double)nods[i][1], (double)nods[i][2]);
+printf("~1 barice check %f %f %f\n", tt[0],tt[1],tt[2]);
+}
+#endif /* NEVER */
+ if ((inc == 1 && B[0] < -EPS) /* other point is at higher angle */
+ || (inc == -1 && B[0] > EPS)) { /* other point is at lower angle */
+printf("~1 candidate isn't best\n");
+ break;
+ }
+ }
+ if (i == end) {
+printf("~1 candidate IS the best\n");
+ break; /* Candidate is at highest angle */
+ }
+ }
+ if (ii == end) {
+printf("~1Inconsistent candidate ordering\n");
+ error("Inconsistent candidate ordering");
+ }
+ } else {
+printf("~1 there are only %d nodes, so don't search them\n",ncnodes[si]);
+ }
+printf("~1 Making triangle with %d: node %d\n",ii,nods[ii]->gix);
+ make_tri(s, ep, nods[ii], inc == -1);
+ }
+ if (ep->npt < 1 || ep->nnt < 1) {
+ if (ep->npt < 1)
+ warning("###### Unable to locate +ve triangles for edge %d\n",ep->n);
+ if (ep->nnt < 1)
+ warning("###### Unable to locate -ve triangles for edge %d\n",ep->n);
+ }
+ }
+ }
+
+ free_ivector(pivx, 0, fdi-1);
+ free_dmatrix(A, 0, fdi-1, 0, fdi-1);
+ free_dvector(B, 0, fdi-1);
+
+ /* Dump out the edges */
+ for (ep = s->gam.etop; ep != NULL; ep = ep->list) {
+
+printf("~1 edge no %d, npt = %d, nnt = %d\n",ep->n, ep->npt, ep->nnt);
+ }
+ }
+ return 0;
+}
+
+/* ====================================================== */
+/* Gamut rspl setup functions */
+
+/* Called by rspl initialisation */
+void
+init_gam(rspl *s) {
+
+ /* Methods */
+ s->comp_gamut = gam_comp_gamut;
+}
+
+/* Free up all the gamut info */
+void free_gam(
+rspl *s /* Pointer to rspl grid */
+) {
+ int i;
+ int ssdi;
+ rvert *vp, *nvp;
+ redge *ep, *nep;
+ rtri *tp, *ntp;
+
+ for (ssdi = 1; ssdi <= s->fdi-1; ssdi++)
+ rspl_free_ssimplex_info(s, &s->gam.ssi[ssdi]);
+
+ /* Free the verticies */
+ for (vp = s->gam.vtop; vp != NULL; vp = nvp) {
+ nvp = vp->list;
+ free(vp);
+ }
+ free(s->gam.verts);
+
+ /* Free the edges */
+ for (ep = s->gam.etop; ep != NULL; ep = nep) {
+ nep = ep->list;
+ free(ep);
+ }
+ free(s->gam.edges);
+
+ /* Free the triangles */
+ for (tp = s->gam.ttop; tp != NULL; tp = ntp) {
+ ntp = tp->list;
+ free(tp);
+ }
+ free(s->gam.tris);
+}
+
+
+/* Create the gamut surface structure. */
+/* Current this is:
+
+ not rationalized to be non-overlapping
+ culled to eliminate overlaps
+ Have ink limits applied
+
+*/
+
+/* ~~~ we need space ing gam to:
+
+ store radial coordinates of output values
+ mark nodes as being visited.
+ store surface triangle structures
+ hold tadial output bounding acceleration structures
+
+
+ There is a lot in common with rev here.
+ we need to know input channel significance (what's black)
+ plus ink limit info.
+ we're going to create black generation information used by rev.
+*/
+
+/* ---------------------- */
+/* rspl gam diagnostic */
+
+/* Diagnostic */
+void rspl_gam_plot(rspl *s, char *name) {
+ int i;
+ double col[3] = { 0.7, 0.7, 0.7 };
+ rvert *vp, *nvp;
+ rtri *tp, *ntp;
+
+ vrml *wrl;
+
+ if ((wrl = new_vrml(name, 1, 0)) == NULL)
+ error("new_vrml failed\n");
+
+ /* Set the verticies */
+ for (vp = s->gam.vtop; vp != NULL; vp = vp->list) {
+ wrl->add_vertex(wrl, 0, vp->v);
+ }
+
+ /* Set the triangles */
+ for (tp = s->gam.ttop; tp != NULL; tp = ntp) {
+ int ix[3];
+ ntp = tp->list;
+ ix[0] = tp->v[0]->n;
+ ix[1] = tp->v[1]->n;
+ ix[2] = tp->v[2]->n;
+ wrl->add_triangle(wrl, 0, ix);
+ }
+
+ wrl->make_triangles(wrl, 0, 0.0, NULL);
+// wrl->make_triangles(wrl, 0, 0.0, col);
+
+ wrl->del(wrl);
+}
+
+#undef DEBUG
+#undef DBGV
+#undef DBG
+#define DBGV(xxx)
+#define DBG(xxx)
+
+
+
+
+
+
diff --git a/rspl/gam.h b/rspl/gam.h
new file mode 100644
index 0000000..0892e4d
--- /dev/null
+++ b/rspl/gam.h
@@ -0,0 +1,131 @@
+#ifndef RSPL_GAM_H
+#define RSPL_GAM_H
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized spline data structure
+ *
+ * Precise gamut surface, gamut pruning, ink limiting and K min/max
+ * support routine.s
+ *
+ * Author: Graeme W. Gill
+ * Date: 2008/11/21
+ *
+ * Copyright 1999 - 2008 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ *
+ * Latest simplex/linear equation version.
+ */
+
+/* In practice the gamut is only ever computed for 2D or 3D output */
+/* dimensions. The gamut surface is composed of simplexes ("triangles") */
+/* of dimensions fdi-1, with fdi verticies */
+
+#include "llist.h"
+
+#define MXNE 16 /* Maximum number of edges per triangle allowed */
+
+#define VHASHSIZE 6863 /* Vertex hash index size */
+#define EHASHSIZE 2659 /* Edge hash index size */
+#define THASHSIZE 2659 /* Triangle hash index size */
+
+/* ----------------------------------------- */
+
+/* Vertex node - all vertex nodes are part of the grid */
+struct _rvert {
+ struct _rvert *next; /* Hash linked list */
+ int n; /* Index number of vertex */
+ int gix; /* Grid index - used to identify and order nodes */
+ float *fg; /* Pointer to grid data */
+
+// double p[MXDO]; /* Poistion of node */
+ double v[MXDO]; /* Output value of node */
+ double r[MXDO]; /* Radial coordinates */
+ struct _rvert *list; /* Next in linked list */
+}; typedef struct _rvert rvert;
+
+/* ------------------------------------ */
+
+/* An edge shared by one or more triangle in the mesh */
+struct _redge {
+ struct _redge *next; /* Hash linked list */
+ int n; /* Serial number */
+// float *f[MXDO-1]; /* fdi-1 grid verticies of edge in base simplex order. */
+ struct _rvert *v[MXDO-1]; /* fdi-1 Verticies of edge in base simplex order. */
+ double pe[MXDO+1]; /* Plane equation for edge for side of edge testing. */
+ /* fdi for normal + constant */
+ int nt; /* Total number of triangles that share this edge */
+ int npt; /* Positive side triangles that share this edge */
+ int nnt; /* Negative side triangles that share this edge */
+ struct _rtri *t[MXNE]; /* nt triangles edge is part of */
+
+ struct _redge *list; /* Next in linked list */
+}; typedef struct _redge redge;
+
+/* ------------------------------------ */
+
+/* A "triangle" (simplex dimension fdi-1) in the surface mesh */
+struct _rtri {
+ struct _rtri *next; /* Hash linked list */
+ int n; /* Serial number */
+// float *f[MXDO]; /* fdi grid verticies in gix order */
+ struct _rvert *v[MXDO]; /* fdi verticies in gix order */
+
+// struct _redge *e[((MXDO+1) * MXDO)/2]; /* Edges in vertex sorted order */
+// double mix[2][MXDO]; /* nn: Bounding box min and max */
+
+ struct _rtri *list; /* Next in linked list */
+}; typedef struct _rtri rtri;
+
+/* ----------------------------------------- */
+/* Gamut info stored in main rspl function */
+struct _gam_struct {
+ int inited;
+
+ double cent[MXDO]; /* Center of radial distance calculation */
+ double scale[MXDO]; /* Scale of radial distance calculation */
+
+ void (*outf)(void *cntxf, double *out, double *in); /* Optional rspl val -> output value */
+ void *cntxf; /* Context for function */
+ void (*outb)(void *cntxb, double *out, double *in); /* Optional output value -> rspl val */
+ void *cntxb; /* Context for function */
+
+ ssxinfo ssi[MXDO-1]; /* Sub-simplex information for sdi from 0..fdi-1 */
+
+ int rvert_no; /* Number of rverts allocated */
+ int vhsize; /* Vertex hash list size */
+ rvert **verts; /* Hash list, NULL if not allocated */
+ rvert *vtop; /* Top of list of verticies */
+ rvert *vbot; /* Bottom of list of verticies */
+
+ int redge_no; /* Number of redges allocated */
+ int ehsize; /* Edge hash list size */
+ redge **edges; /* Edges between the triangles linked list */
+ redge *etop; /* Top of list of edges */
+ redge *ebot; /* Bottom of list of edges */
+
+ int rtri_no; /* Number of rtris allocated */
+ int thsize; /* Triangle hash list size */
+ rtri **tris; /* Hash list, NULL if not allocated */
+ rtri *ttop; /* Surface triangles linked list */
+
+}; typedef struct _gam_struct gam_struct;
+
+#endif /* RSPL_GAM_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rspl/mlbs.c b/rspl/mlbs.c
new file mode 100644
index 0000000..bbe3865
--- /dev/null
+++ b/rspl/mlbs.c
@@ -0,0 +1,605 @@
+
+/*
+ * Argyll Color Correction System
+ *
+ * Scattered Data Interpolation with multilevel B-splines library.
+ * This can be used by rspl, or independently by any other routine.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2001/1/1
+ *
+ * Copyright 2000 - 2001 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/*
+ * This is from the paper
+ * "Scattered Data Interpolation with Multilevel B-Splines"
+ * by Seungyong Lee, George Wolberg and Sung Yong Shin,
+ * IEEE Transactions on Visualisation and Computer Graphics
+ * Vol. 3, No. 3, July-September 1997, pp 228.
+ */
+
+/* TTBD:
+ *
+ * Figure out why the results are rubbish ?
+ *
+ * Can this be adapted to be adaptive in it smoothness,
+ * like the non-linear regularized spline stuff that Don Bone used ?
+ *
+ * Get rid of error() calls - return status instead
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+#include "numlib.h"
+#include "mlbs.h"
+
+#ifndef NUMSUP_H
+void error(char *fmt, ...), warning(char *fmt, ...);
+#endif
+
+static void delete_mlbs(mlbs *p);
+static int lookup_mlbs(mlbs *p, co *c);
+
+/* Allocate a new empty mlbs */
+mlbs *alloc_mlbs(
+int di, /* Input dimensionality */
+int fdi, /* Output dimesionality */
+int res, /* Target resolution */
+double smf /* Smoothing factor */
+) {
+ mlbs *p;
+ if ((p = (mlbs *)malloc(sizeof(mlbs))) == NULL)
+ error("Malloc mlbs failed");
+
+ p->di = di;
+ p->fdi = fdi;
+ p->tres = res;
+ p->smf = smf;
+ p->s = NULL;
+
+ p->lookup = lookup_mlbs;
+ p->del = delete_mlbs;
+
+ return p;
+}
+
+static void delete_slbs(slbs *s);
+
+static void delete_mlbs(mlbs *p) {
+
+ if (p != NULL) {
+ delete_slbs(p->s);
+ free(p);
+ }
+}
+
+/* Create a new empty slbs */
+static slbs *new_slbs(
+mlbs *p, /* Parent mlbs */
+int res /* Resolution of this slbs */
+) {
+ slbs *s;
+ int e, f;
+ double *_lat, *lat; /* Latice base address */
+ int ix, oe, oo[MXDI]; /* Neighborhood offset index, counter */
+
+ if ((s = (slbs *)malloc(sizeof(slbs))) == NULL)
+ error("Malloc slbs failed");
+
+ s->p = p;
+ s->res = res;
+
+ for (s->lsize = p->fdi, s->nsize = 1, e = 0; e < p->di; e++) {
+ s->coi[e] = s->lsize; /* (double) increment in this input dimension */
+ s->lsize *= (res + 2); /* Latice in 1D +/- 1 */
+ s->nsize *= 4; /* Neighborhood of 4 */
+ }
+
+ if ((s->_lat = (double *)malloc(s->lsize * sizeof(double))) == NULL)
+ error("Malloc slbs latice failed");
+
+ /* Compute the base address */
+ for (s->loff = 0, e = 0; e < p->di; e++) {
+ s->loff += s->coi[e]; /* Offset by 1 in each input dimension */
+ }
+ s->lat = s->_lat + s->loff;
+
+ /* Figure the cell width */
+ for (e = 0; e < p->di; e++)
+ s->w[e] = (p->h[e] - p->l[e])/(res-1.0);
+
+ /* Setup neighborhood cache info */
+ if ((s->n = (neigh *)malloc(s->nsize * sizeof(neigh))) == NULL)
+ error("Malloc slbs neighborhood failed");
+
+ for (oe = 0; oe < p->di; oe++)
+ oo[oe] = 0;
+
+ for(ix = oe = 0; oe < p->di; ix++) {
+ int xo;
+ for (xo = e = 0; e < p->di; e++) {
+ s->n[ix].c[e] = oo[e];
+ xo += s->coi[e] * oo[e]; /* Accumulate latice offset */
+ }
+ s->n[ix].xo = xo;
+ s->n[ix].w = 0.0;
+
+ /* Increment destination offset counter */
+ for (oe = 0; oe < p->di; oe++) {
+ if (++oo[oe] <= 3) /* Counting from 0 ... 3 */
+ break;
+ oo[oe] = 0;
+ }
+ }
+
+ return s;
+}
+
+/* Destroy a slbs */
+static void delete_slbs(slbs *s) {
+ if (s != NULL) {
+ free(s->_lat);
+ free(s->n);
+ free(s);
+ }
+}
+
+/* Dump the 2D -> 1D contents of an slbs */
+static void dump_slbs(slbs *s) {
+ int e, f;
+ int ce, co[MXDI]; /* latice counter */
+ mlbs *p = s->p; /* Parent object */
+
+ /* Init the counter */
+ for (ce = 0; ce < p->di; ce++)
+ co[ce] = -1;
+ ce = 0;
+
+ f = 0;
+ while(ce < p->di) {
+ double v;
+ int off = 0; /* Latice offset */
+ for (e = 0; e < p->di; e++) {
+ off += co[e] * s->coi[e]; /* Accumulate latice offset */
+ }
+ v = s->lat[off + f]; /* Value of this latice point */
+
+ printf("Latice at [%d][%d] = %f\n",co[1],co[0],v);
+
+ /* Increment the latice counter */
+ for (ce = 0; ce < p->di; ce++) {
+ if (++co[ce] <= s->res) /* Counting from -1 ... s->res */
+ break;
+ co[ce] = -1;
+ }
+ }
+}
+
+/* Initialise an slbs with a linear approximation to the scattered data */
+static void linear_slbs(
+slbs *s
+) {
+ int i, e, f;
+ mlbs *p = s->p; /* Parent object */
+ double **A; /* A matrix holding scattered data points */
+ double *B; /* B matrix holding RHS & solution */
+
+ /* Allocate the matricies */
+ B = dvector(0, p->npts-1);
+ A = dmatrix(0, p->npts-1, 0, p->di);
+
+ /* For each output dimension, solve the linear equation coeficients */
+ for (f = 0; f < p->fdi; f++) {
+ int ce, co[MXDI]; /* latice counter */
+
+ /* Init A[][] with the scattered data points positions */
+ /* Also init B[] with the value for this output dimension */
+ for (i = 0; i < p->npts; i++) {
+ for (e = 0; e < p->di; e++)
+ A[i][e] = p->pts[i].p[e];
+ A[i][e] = 1.0;
+ B[i] = p->pts[i].v[f];
+ }
+
+ /* Solve the equation A.x = b using SVD */
+ /* (The w[] values are thresholded for best accuracy) */
+ /* Return non-zero if no solution found */
+ if (svdsolve(A, B, p->npts, p->di+1) != 0)
+ error("SVD least squares failed");
+ /* A[][] will have been changed, and B[] holds the p->di+1 coefficients */
+
+ /* Use the coefficients to initialise the slbs values */
+ for (ce = 0; ce < p->di; ce++)
+ co[ce] = -1;
+ ce = 0;
+
+ while(ce < p->di) {
+ double v = B[p->di]; /* Constant */
+ int off = 0; /* Latice offset */
+ for (e = 0; e < p->di; e++) {
+ double lv;
+ lv = p->l[e] + s->w[e] * co[e]; /* Input value for this latice location */
+ v += B[e] * lv;
+ off += co[e] * s->coi[e]; /* Accumulate latice offset */
+ }
+ s->lat[off + f] = v; /* Value of this latice point */
+
+ /* Increment the latice counter */
+ for (ce = 0; ce < p->di; ce++) {
+ if (++co[ce] <= s->res) /* Counting from -1 ... s->res */
+ break;
+ co[ce] = -1;
+ }
+ }
+ }
+ free_dmatrix(A, 0, p->npts-1, 0, p->di);
+ free_dvector(B, 0, p->npts-1);
+}
+
+/* Do a latice refinement - upsample the current */
+/* source latice to the destination latice. */
+static void refine_slbs(
+slbs *ds, /* Destination slbs */
+slbs *ss /* Source slbs */
+) {
+ mlbs *p = ss->p; /* Parent object */
+ int ce, co[MXDI]; /* Source coordinate counter */
+ int six; /* Source index */
+ int dix; /* destination index */
+ static double _wt[5] = { 1.0/8.0, 4.0/8.0, 6.0/8.0, 4.0/8.0, 1.0/8.0 };
+ static double *wt = &_wt[2]; /* 1D Distribution weighting */
+
+ /* Zero the destination latice before accumulating values */
+ for (dix = 0; dix < ds->lsize; dix++)
+ ds->_lat[dix] = 0.0;
+
+ /* Now for each source latice entry, add weighted portions */
+ /* to the associated destination points */
+
+ /* Init the source coordinate counter */
+ for (ce = 0; ce < p->di; ce++)
+ co[ce] = -1;
+ ce = 0;
+ six = -ss->loff;
+
+ while(ce < p->di) {
+ int oe, oo[MXDI]; /* Destination offset counter */
+
+//printf("Source coord %d %d, offset %d, value %f\n",co[0], co[1], six, ss->lat[six]);
+ /* calc destination index, and init offest counter */
+ for (dix = oe = 0; oe < p->di; oe++) {
+ oo[oe] = -2;
+ dix += co[oe] * 2 * ds->coi[oe]; /* Accumulate dest offset */
+ }
+ oe = 0;
+
+//printf("Dest coord %d %d\n",co[0] * 2, co[1] * 2);
+ /* For all the offsets from the destination point */
+ while(oe < p->di) {
+ int e, f, dixo; /* Destination index offset */
+ double w = 1.0; /* Weighting */
+
+//printf("dest offset %d %d\n",oo[0], oo[1]);
+ /* Compute dest index offset, and check that we are not outside the destination */
+ for (dixo = e = 0; e < p->di; e++) {
+ int x = co[e] * 2 + oo[e]; /* dest coord */
+ dixo += oo[e] * ds->coi[e]; /* Accumulate dest offset */
+//printf("x[%d] = %d\n",e, x);
+ w *= wt[oo[e]]; /* Compute distribution weighting */
+ if (x < -1 || x > ds->res)
+ break; /* No good */
+ }
+ if (e >= p->di) { /* We are within the destination latice */
+//if ((co[0] * 2 + oo[0]) == 0 && (co[1] * 2 + oo[1]) == 0) {
+//printf("Source coord %d %d, offset %d, value %f\n",co[0], co[1], six, ss->lat[six]);
+//printf("Dest coord %d %d ix %d, weight %f\n",co[0] * 2 + oo[0], co[1] * 2 + oo[1], dix+dixo, w);
+//}
+
+ for (f = 0; f < p->fdi; f++) { /* Distribute weighted values */
+ double v = ss->lat[six + f];
+//if ((co[0] * 2 + oo[0]) == 0 && (co[1] * 2 + oo[1]) == 0)
+//printf("Value being dist %f, weighted value %f\n", v, v * w);
+ ds->lat[dix + dixo + f] += v * w;
+ }
+ }
+
+ /* Increment destination offset counter */
+ for (oe = 0; oe < p->di; oe++) {
+ if (++oo[oe] <= 2) /* Counting from -2 ... +2 */
+ break;
+ oo[oe] = -2;
+ }
+ }
+
+ /* Increment the source index and coordinat counter */
+ six += p->fdi;
+ for (ce = 0; ce < p->di; ce++) {
+ if (++co[ce] <= ss->res) /* Counting from -1 ... ss->res */
+ break;
+ co[ce] = -1;
+ }
+ }
+}
+
+/* Compute the Cubic B-spline weightings for a given t */
+void basis(double b[4], double t) {
+ double _t3, _t2, _t1, _3t3, _3t2, _3t1, _6t2;
+
+ _t1 = t/6.0;
+ _t2 = _t1 * _t1;
+ _t3 = _t2 * _t1;
+ _3t1 = 3.0 * _t1;
+ _3t2 = 3.0 * _t2;
+ _3t3 = 3.0 * _t3;
+ _6t2 = 6.0 * _t2;
+
+ b[0] = - _t3 + _3t2 - _3t1 + 1.0/6.0;
+ b[1] = _3t3 - _6t2 + 4.0/6.0;
+ b[2] = -_3t3 + _3t2 + _3t1 + 1.0/6.0;
+ b[3] = _t3;
+}
+
+
+/* Improve an slbs to make it closer to the scattered data */
+static void improve_slbs(
+slbs *s
+) {
+ int i, e, f;
+ mlbs *p = s->p; /* Parent object */
+ double *delta; /* Delta accumulation */
+ double *omega; /* Omega accumulation */
+
+ /* Allocate temporary accumulation arrays */
+ if ((delta = (double *)calloc(sizeof(double), s->lsize)) == NULL)
+ error("Malloc slbs temp latice failed");
+ delta += s->loff;
+ if ((omega = (double *)calloc(sizeof(double), s->lsize)) == NULL)
+ error("Malloc slbs temp latice failed");
+ omega += s->loff;
+
+ /* For each scattered data point */
+ for (i = 0; i < p->npts; i++) {
+ int ix; /* Latice index of base of neighborhood */
+ double b[MXDI][4]; /* B-spline basis factors for each dimension */
+ double sws; /* Sum of all the basis factors squared */
+ double ve[MXDO]; /* Current output value error */
+ int nn; /* Neighbor counter */
+
+ /* Figure out our neighborhood */
+ for (ix = e = 0; e < p->di; e++) {
+ int x;
+ double t, sp, fp;
+ sp = (p->pts[i].p[e] - p->l[e])/s->w[e]; /* Scaled position */
+ fp = floor(sp);
+ x = (int)(fp - 1.0); /* Grid coordinate */
+ ix += s->coi[e] * x; /* Accume latice offset */
+ t = sp - fp; /* Spline parameter */
+ basis(b[e], t); /* Compute basis function values */
+ }
+
+ /* Compute the grid basis weight functions, */
+ /* the sum of the weights squared, and the current */
+ /* output value estimate. */
+ for (f = 0; f < p->fdi; f++)
+ ve[f] = p->pts[i].v[f]; /* Target output value */
+ for (sws = 0.0, nn = 0; nn < s->nsize; nn++) {
+ double w;
+ for (w = 1.0, e = 0; e < p->di; e++)
+ w *= b[e][s->n[nn].c[e]];
+ s->n[nn].w = w; /* cache weighting */
+ sws += w * w;
+ for (f = 0; f < p->fdi; f++)
+ ve[f] -= w * s->lat[ix + s->n[nn].xo + f]; /* Subtract current aprox value */
+ }
+//printf("Error at point %d = %f\n",i,ve[0]);
+
+ /* Accumulate the delta and omega factors */
+ /* for this resolutions improvement. */
+ for (nn = 0; nn < s->nsize; nn++) {
+ double ws, ww, w = s->n[nn].w;
+ int xo = ix + s->n[nn].xo; /* Latice offset */
+ ww = w * w;
+ ws = ww * w/sws; /* Scale factor for delta */
+ omega[xo] += ww; /* Accumulate omega */
+ for (f = 0; f < p->fdi; f++)
+ delta[xo + f] += ws * ve[f]; /* Accumulate delta */
+//printf("Distributing omega %f to %d %d\n",ww,s->n[nn].c[0],s->n[nn].c[1]);
+//printf("Distributing delta %f to %d %d\n",ws * ve[0],s->n[nn].c[0],s->n[nn].c[1]);
+ }
+ }
+
+ omega -= s->loff; /* Base them back to -1 corner */
+ delta -= s->loff;
+
+ /* Go through the delta and omega arrays, */
+ /* compute and add the refinements to the current */
+ /* B-spline control latice. */
+ for (i = 0; i < s->lsize; i++) {
+ double om = omega[i];
+ if (om != 0.0) {
+ for (f = 0; f < p->fdi; f++)
+ s->_lat[i] += delta[i + f]/om;
+//printf("Adjusting latice index %d by %f to give %f\n",i, delta[i]/om, s->_lat[i]);
+ }
+ }
+
+ /* Done with temporary arrays */
+ free(omega);
+ free(delta);
+}
+
+/* Return the interpolated value for a given point */
+/* Return NZ if input point is out of range */
+static int lookup_mlbs(
+mlbs *p,
+co *c /* Point to interpolate */
+) {
+ slbs *s = p->s;
+ int e, f;
+ int ix; /* Latice index of base of neighborhood */
+ double b[MXDI][4]; /* B-spline basis factors for each dimension */
+ int nn; /* Neighbor counter */
+
+ /* Figure out our neighborhood */
+ for (ix = e = 0; e < p->di; e++) {
+ int x;
+ double t, sp, fp;
+ sp = c->p[e];
+ if (sp < p->l[e] || sp > p->h[e])
+ return 1;
+ sp = (sp - p->l[e])/s->w[e]; /* Scaled position */
+ fp = floor(sp);
+ x = (int)(fp - 1.0); /* Grid coordinate */
+ ix += s->coi[e] * x; /* Accume latice offset */
+ t = sp - fp; /* Spline parameter */
+ basis(b[e], t); /* Compute basis function values */
+ }
+
+ /* Compute the the current output value. */
+ for (f = 0; f < p->fdi; f++)
+ c->v[f] = 0.0;
+ for (nn = 0; nn < s->nsize; nn++) {
+ double w;
+ for (w = 1.0, e = 0; e < p->di; e++)
+ w *= b[e][s->n[nn].c[e]];
+ for (f = 0; f < p->fdi; f++)
+ c->v[f] += w * s->lat[ix + s->n[nn].xo + f]; /* Accume spline value */
+ }
+
+ return 0;
+}
+
+/* Take a list of scattered data points, */
+/* and setup the mlbs. */
+static void set_mlbs(
+mlbs *p, /* mlbs to set up */
+dpnts *pts, /* scattered data points and weights */
+int npts, /* number of scattered data points */
+double *l, /* Input data range, low (May be NULL) */
+double *h /* Input data range, high (May be NULL) */
+) {
+ int res;
+ int i, e, f;
+ slbs *s0 = NULL, *s1;
+
+ /* Establish the input data range */
+ for (e = 0; e < p->di; e++) {
+ if (l == NULL)
+ p->l[e] = 1e60;
+ else
+ p->l[e] = l[e];
+ if (h == NULL)
+ p->h[e] = -1e60;
+ else
+ p->h[e] = h[e];
+ }
+ for (i = 0; i < npts; i++) {
+ for (e = 0; e < p->di; e++) {
+ if (pts[i].p[e] < p->l[e])
+ p->l[e] = pts[i].p[e];
+ if (pts[i].p[e] > p->h[e])
+ p->h[e] = pts[i].p[e];
+ }
+ }
+
+ /* Make point data available during init */
+ p->pts = pts;
+ p->npts = npts;
+
+ /* Create an initial slbs */
+ res = 2;
+ if ((s1 = new_slbs(p, 2)) == NULL)
+ error("new_slbs failed");
+
+ /* Set it up with a linear first approximation */
+ linear_slbs(s1);
+//dump_slbs(s1);
+
+ /* Build up the resolution */
+ for (; res < p->tres;) {
+
+ res = 2 * res -1;
+
+printf("~1 doing resolution %d\n",res);
+ delete_slbs(s0);
+ s0 = s1;
+ if ((s1 = new_slbs(p, res)) == NULL)
+ error("new_slbs failed");
+
+ refine_slbs(s1, s0);
+//dump_slbs(s1);
+ improve_slbs(s1);
+ }
+
+ delete_slbs(s0);
+ p->s = s1; /* Final resolution */
+
+ /* We can't assume point data will stick around */
+ p->pts = NULL;
+ p->npts = 0;
+}
+
+
+/* Create a new empty mlbs */
+mlbs *new_mlbs(
+int di, /* Input dimensionality */
+int fdi, /* Output dimesionality */
+int res, /* Minimum final resolution */
+dpnts *pts, /* scattered data points and weights */
+int npts, /* number of scattered data points */
+double *l, /* Input data range, low (May be NULL) */
+double *h, /* Input data range, high (May be NULL) */
+double smf /* Smoothing factor */
+) {
+ mlbs *p;
+
+ if ((p = alloc_mlbs(di, fdi, res, smf)) == NULL)
+ return p;
+
+ set_mlbs(p, pts, npts, l, h);
+
+ return p;
+}
+
+#ifndef NUMSUP_H
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"stest: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"stest: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+#endif /* NUMSUP_H */
+
diff --git a/rspl/mlbs.h b/rspl/mlbs.h
new file mode 100644
index 0000000..4678cfd
--- /dev/null
+++ b/rspl/mlbs.h
@@ -0,0 +1,77 @@
+
+/*
+ * Argyll Color Correction System
+ *
+ * Scattered Data Interpolation with multilevel B-splines library.
+ * This can be used by rspl, or independently by any other routine.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2001/1/1
+ *
+ * Copyright 2000 - 2001 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/*
+ * This is from the paper
+ * "Scattered Data Interpolation with Multilevel B-Splines"
+ * by Seungyong Lee, George Wolberg and Sung Yong Shin,
+ * IEEE Transactions on Visualisation and Computer Graphics
+ * Vol. 3, No. 3, July-September 1997, pp 228.
+ */
+
+#include "rspl.h" /* Define some common elements */
+
+/* Neighborhood latice cache data */
+typedef struct {
+ int c[MXDI]; /* Coordinate */
+ int xo; /* Offset into slbs latice */
+ double w; /* B-spline basis weight */
+} neigh;
+
+/* Structure that represents a resolution level of B-splines */
+struct _slbs {
+ struct _mlbs *p; /* Parent structure */
+ int res; /* Basic resolution */
+ int coi[MXDI]; /* Double increment for each input dimension into latice */
+ double *lat; /* Control latice, extending from +/- 1 from 0..res-1 */
+ double *_lat; /* Allocation base of lat */
+ int lsize, loff; /* Number of doubles in _lat, offset of lat from _lat */
+ double w[MXDI]; /* Input data cell width */
+ neigh *n; /* Neighborhood latice cache */
+ int nsize; /* Number of n entries */
+}; typedef struct _slbs slbs;
+
+/* Structure that represents the whole scattered interpolation state */
+struct _mlbs {
+ int di; /* Input dimensions */
+ int fdi; /* Output dimensions */
+ int tres; /* Target resolution */
+ double smf; /* Smoothing factor */
+ int npts; /* Number of data points */
+ dpnts *pts; /* Coordinate points and weights (valid while creating) */
+ double l[MXDI], h[MXDI]; /* Input data range, cell width */
+ slbs *s; /* Current B-spline latice */
+
+ int (*lookup)(struct _mlbs *p, co *c);
+
+ void (*del)(struct _mlbs *p);
+
+}; typedef struct _mlbs mlbs;
+
+
+/* Create a new empty mlbs */
+mlbs *new_mlbs(
+int di, /* Input dimensionality */
+int fdi, /* Output dimesionality */
+int res, /* Minimum final resolution */
+dpnts *pts, /* scattered data points and weights */
+int npts, /* number of scattered data points */
+double *l, /* Input data range, low (May be NULL) */
+double *h, /* Input data range, high (May be NULL) */
+double smf /* Smoothing factor */
+);
+
diff --git a/rspl/opt.c b/rspl/opt.c
new file mode 100644
index 0000000..d5a70ce
--- /dev/null
+++ b/rspl/opt.c
@@ -0,0 +1,725 @@
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized splines
+ * optimiser based initialiser.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2001/5/16
+ *
+ * Copyright 1996 - 2001 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* This file contains an rspl initialiser that */
+/* works from an optimisation function callback. */
+/* It is intended to support the creation of optimised */
+/* color separations, although this usage is not hard coded */
+/* here. */
+
+/* TTBD:
+ *
+ * !!! fix so that this can also be used for smoothed
+ * inversion, ie. PCS -> DevN, as well as
+ * separation PseudoCMY/K -> DevN.
+ *
+ * Plan:
+ * Have additional callback function used for invert,
+ * called at grid initialisation that initialised
+ * the target values to fixed PCS values.
+ * For separation, these are dynamic, and adjusted by
+ * the usual optimisation callback.
+ * (Or can the usual callback figure out when the
+ * initial initialisation is needed ?)
+ *
+ * Need to return average/extrapolated surround values
+ * on the edge, just like fit, so smoothness can be
+ * evaluated in inversion.
+ * Provide another mechanism for sep to know
+ * it is on the edge of the grid, and should expand
+ * gamut if possible.
+ *
+ * Get rid of error() calls - return status instead
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <time.h>
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+
+#include "rspl_imp.h"
+#include "numlib.h"
+#include "counters.h" /* Counter macros */
+
+#undef DEBUG
+
+/* Tuning parameters */
+#define TOL 1e-6 /* Tollerance of result */
+#define GRATIO 1.7 /* Multi-grid ratio */
+#define SMOOTH 80.0 /* Set nominal smoothing (1.0) */
+
+#undef NEVER
+#define ALWAYS
+
+/* Implemented in rspl.c: */
+extern void alloc_grid(rspl *s);
+
+extern int is_mono(rspl *s);
+
+/* Convention is to use:
+ i to index grid points u.a
+ n to index data points d.a
+ e to index position dimension di
+ f to index output function dimension fdi
+ j misc and cube corners
+ k misc
+ */
+
+/* ================================================= */
+/* Structure to hold temporary data for multi-grid caliculations */
+/* Only used in this file. */
+struct _omgtp {
+ rspl *s; /* Associated rspl */
+
+ /* Configuration data */
+ int tdi; /* Target guide values dimensionality (must be <= MXDI) */
+ /* (Typically the Lab aim values corresponding to this pseudo device value) */
+ int adi; /* Additional grid point data allowance (must be <= 2 * MXDI) */
+ /* (Typically black locus range) */
+
+ double (*func)(void *fdata, double *inout, double *surav, int first, double *cw);
+ /* Optimisation function */
+ void *fdata; /* Pointer to opaque data needed by callback function */
+
+ struct {
+ double cw[MXDI]; /* Curvature weight factor for each dimension */
+ } sf;
+
+ /* Grid points data */
+ struct {
+ int res[MXDI]; /* Single dimension grid resolution for each dimension */
+ int bres, brix; /* Biggest resolution and its index */
+ double mres; /* Geometric mean res[] */
+ int no; /* Total number of points in grid = res ^ di */
+ datai l,h,w; /* Grid low, high, grid cell width */
+
+
+ double *a; /* Grid point data */
+ /* Array is res ^ di entries double[fdi+tdi+adi] */
+ /* The output values start at offset 0, the */
+ /* target data values start at offset fdi, and */
+ /* the additional data starts at offset fdi+tdi. */
+ int pss; /* Grid point structure size = fdi+tdi */
+
+ /* Grid array offset lookups */
+ int ci[MXDI]; /* Grid coordinate increments for each dimension */
+ int fci[MXDI]; /* Grid coordinate increments for each dimension in doubles */
+ int *hi; /* 2^di Combination offset for sequence through cube. */
+ int *fhi; /* Combination offset for sequence through cube of */
+ /* 2^di points, starting at base, in floats */
+ int a_hi[DEF2MXDI]; /* Default allocation for *hi */
+ int a_fhi[DEF2MXDI];/* Default allocation for *fhi */
+ } g;
+
+}; typedef struct _omgtp omgtp;
+
+/* ================================================= */
+static omgtp *new_omgtp(rspl *s, int tdi, int adi, int mxres,
+ double (*func)(void *fdata, double *inout, double *surav, int first, double *cw),
+ void *fdata);
+static void free_omgtp(omgtp *m);
+static void solve_gres(omgtp *m, double tol);
+static void init_soln(omgtp *m1, omgtp *m2);
+static void init_fsoln(omgtp *m, double **vdata);
+
+/* Initialise the regular spline from the optimisation callback function. */
+/* The target data is auxiliary data used to "target" the optimisation */
+/* callback function. */
+/* The callback function arguments are as follows:
+ * void *fdata,
+ * double *inout, Pointers to fdi+tdi+adi values for the grid point being optimised.
+ * double *surav, Pointers to fdi+tdi values which are the average of the
+ * neighbors of this grid point. Pointer will NULL if this
+ * is a surface grid point.
+ * int first, Flag, NZ if this is the first optimisation of this point.
+ * double *cw the (grid resolution) curvature weighting factor for each dimension
+ *
+ * Returns value is the "error" for this point.
+ */
+
+int
+opt_rspl_imp(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ int tdi, /* Dimensionality of target data */
+ int adi, /* Additional per grid point data allocation */
+ double **vdata, /* di^2 array of function, target and additional values to init */
+ /* array corners with. Corners are ordered with lowest index */
+ /* dimension changing most rapidly. */
+ double (*func)(void *fdata, double *inout, double *surav, int first, double *cw),
+ /* Optimisation function */
+ void *fdata, /* Opaque data needed by function */
+ datai glow, /* Grid low scale - NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution for each dimension */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh /* Data value high normalize - NULL = default 1.0 */
+) {
+// int di = s->di
+ int fdi = s->fdi;
+ int i, e, f;
+// int n;
+
+#if defined(__IBMC__) && defined(_M_IX86)
+ _control87(EM_UNDERFLOW, EM_UNDERFLOW);
+#endif
+ /* set debug level */
+ s->debug = (flags >> 24);
+
+ if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
+ s->verbose = 1;
+ if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
+ s->verbose = 0;
+
+ s->symdom = (flags & RSPL_SYMDOMAIN) ? 1 : 0; /* Turn on symetric smoothness with gres */
+
+ if (tdi >= MXDI)
+ error("rspl, opt: tdi %d > MXDI %d",tdi,MXDI);
+
+ if (adi >= (2 * MXDI))
+ error("rspl, opt: adi %d > 2 * MXDI %d",adi,2 * MXDI);
+
+ /* transfer desired grid range to structure */
+ s->g.mres = 1.0;
+ s->g.bres = 0;
+ for (e = 0; e < s->di; e++) {
+ if (gres[e] < 2)
+ error("rspl: grid res must be >= 2!");
+ s->g.res[e] = gres[e]; /* record the desired resolution of the grid */
+ s->g.mres *= gres[e];
+ if (gres[e] > s->g.bres) {
+ s->g.bres = gres[e];
+ s->g.brix = e;
+ }
+
+ if (glow == NULL)
+ s->g.l[e] = 0.0;
+ else
+ s->g.l[e] = glow[e];
+
+ if (ghigh == NULL)
+ s->g.h[e] = 1.0;
+ else
+ s->g.h[e] = ghigh[e];
+ }
+ s->g.mres = pow(s->g.mres, 1.0/e); /* geometric mean */
+
+ /* compute width of each grid cell */
+ for (e = 0; e < s->di; e++) {
+ s->g.w[e] = (s->g.h[e] - s->g.l[e])/(double)(gres[e]-1);
+ }
+
+ /* record low and width data normalizing factors */
+ for (f = 0; f < s->fdi; f++) {
+ if (vlow == NULL)
+ s->d.vl[f] = 0.0;
+ else
+ s->d.vl[f] = vlow[f];
+
+ if (vhigh == NULL)
+ s->d.vw[f] = 1.0 - s->d.vl[f];
+ else
+ s->d.vw[f] = vhigh[f] - s->d.vl[f];
+ }
+
+ /* Do optimisation of data points */
+ {
+ int nn, res, sres;
+ double fres, gratio = GRATIO;
+ float *gp; /* rspl grid pointer */
+ double *mgp; /* Temp muligrid pointer */
+ omgtp *m, *om = NULL;
+
+ sres = 4; /* Start at initial grid res of 4 */
+ if (sres > s->g.bres)
+ sres = s->g.bres; /* Drop to target resolution */
+
+ /* Calculate the resolution scaling ratio */
+ if (((double)s->g.bres/(double)sres) <= gratio) {
+ gratio = (double)s->g.bres/(double)sres;
+ nn = 1;
+ } else { /* More than one needed */
+ nn = (int)((log((double)s->g.bres) - log((double)sres))/log(gratio) + 0.5);
+ gratio = exp((log((double)s->g.bres) - log((double)sres))/(double)nn);
+ }
+
+ /* Do each grid resolution in turn */
+ for (fres = (double)sres, res = sres;;) {
+ m = new_omgtp(s, tdi, adi, res, func, fdata);
+
+ if (om == NULL) {
+ init_fsoln(m, vdata); /* Set the initial targets & values from corners */
+ } else {
+ init_soln(m, om); /* Scale targets & values from from previous resolution */
+ free_omgtp(om); /* Free previous grid res solution */
+ }
+ solve_gres(m, TOL * s->g.mres/res); /* Use itterative */
+
+ if (res >= s->g.mres)
+ break; /* Done */
+
+ fres *= gratio;
+ res = (int)(fres + 0.5);
+ if ((res + 1) >= s->g.mres) /* If close enough */
+ res = (int)s->g.mres;
+ om = m;
+ }
+
+ /* Allocate the final rspl grid data */
+ alloc_grid(s);
+
+ /* Transfer result in x[] to appropriate grid point value */
+ for (gp = s->g.a, mgp = m->g.a, i = 0; i < s->g.no; gp += s->g.pss, mgp += m->g.pss, i++)
+ for (f = 0; f < fdi; f++)
+ gp[f] = (float)mgp[f];
+ free_omgtp(m);
+ }
+
+ /* Return non-mono check */
+ return is_mono(s);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -*/
+/* omgtp routines */
+
+/* Create a new omgtp. */
+/* Grid data will be uninitialised */
+static omgtp *new_omgtp(
+ rspl *s, /* associated rspl */
+ int tdi, /* Target dimensions */
+ int adi, /* Additional per grid point data allocation */
+ int mxres, /* maximum resolution to create */
+ double (*func)(void *fdata, double *inout, double *surav, int first, double *cw),
+ /* Optimisation function */
+ void *fdata /* Opaque data needed by function */
+) {
+ omgtp *m;
+ int di = s->di, fdi = s->fdi;
+// int dno = s->d.no;
+ int gno;
+ int e, g, i;
+// int f, n, j, k;
+
+ /* Allocate a structure */
+ if ((m = (omgtp *) calloc(1, sizeof(omgtp))) == NULL)
+ error("rspl: malloc failed - omgtp");
+
+ /* Allocate space for cube offset arrays */
+ m->g.hi = m->g.a_hi;
+ m->g.fhi = m->g.a_fhi;
+ if ((1 << di) > DEF2MXDI) {
+ if ((m->g.hi = (int *) malloc(sizeof(int) * (1 << di))) == NULL)
+ error("rspl omgtp malloc failed - hi[]");
+ if ((m->g.fhi = (int *) malloc(sizeof(int) * (1 << di))) == NULL)
+ error("rspl omgtp malloc failed - fhi[]");
+ }
+
+ /* General stuff */
+ m->s = s;
+ m->tdi = tdi;
+ m->adi = adi;
+ m->func = func;
+ m->fdata = fdata;
+
+ /* Grid related */
+ m->g.mres = 1.0;
+ m->g.bres = 0;
+ for (gno = 1, e = 0; e < di; e++) {
+ if (mxres >= s->g.res[e]) /* Shoose smaller of gres and target res */
+ m->g.res[e] = s->g.res[e];
+ else
+ m->g.res[e] = mxres;
+
+ m->g.mres *= m->g.res[e];
+ if (m->g.res[e] > m->g.bres) {
+ m->g.bres = m->g.res[e];
+ m->g.brix = e;
+ }
+ gno *= m->g.res[e];
+ }
+ m->g.mres = pow(m->g.mres, 1.0/e); /* geometric mean */
+ m->g.no = gno;
+
+ m->g.pss = fdi+tdi+adi; /* doubles for each output value + target data + additional data */
+
+ /* record high, low limits, and width of each grid cell */
+ for (e = 0; e < s->di; e++) {
+ m->g.l[e] = s->g.l[e];
+ m->g.h[e] = s->g.h[e];
+ m->g.w[e] = (s->g.h[e] - s->g.l[e])/(double)(m->g.res[e]-1);
+ }
+
+ /* Compute index coordinate increments into linear grid for each dimension */
+ /* ie. 1, gres, gres^2, gres^3 */
+ for (m->g.ci[0] = 1, e = 1; e < di; e++) {
+ m->g.ci[e] = m->g.ci[e-1] * m->g.res[e-1]; /* In grid points */
+ m->g.fci[e] = m->g.ci[e] * m->g.pss; /* In doubles */
+ }
+
+ /* Compute index offsets from base of cube to other corners */
+ for (m->g.hi[0] = 0, e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++) {
+ m->g.hi[g+i] = m->g.hi[i] + m->g.ci[e]; /* In grid points */
+ m->g.fhi[g+i] = m->g.hi[g+i] * m->g.pss; /* In doubles */
+ }
+ }
+
+ /* Allocate space for grid */
+ if ((m->g.a = (double *) malloc(sizeof(double) * gno * m->g.pss)) == NULL)
+ error("rspl malloc failed - multi-grid points");
+
+ /* Compute curvature weighting for matching intermediate resolutions. */
+ /* cw[] is multiplied by the grid curvature_errors_squared[] to keep */
+ /* the same ratio with the sum of data position errors squared. */
+ for (e = 0; e < di; e++) {
+ double rsm; /* Resolution smoothness factor */
+ if (s->symdom)
+ rsm = m->g.res[e]-1.0; /* Relative final grid size */
+ else
+ rsm = m->g.mres-1.0; /* Relative mean final grid size */
+ rsm = pow(rsm,8.0/di);
+ rsm /= pow(200.0,8.0/di)/pow(200.0, 4.0); /* (Scale factor to adjust power) */
+
+ m->sf.cw[e] = (s->smooth * SMOOTH)/(rsm * (double)di);
+ }
+
+ return m;
+}
+
+/* Completely free an omgtp */
+static void free_omgtp(omgtp *m) {
+
+ free((void *)m->g.a);
+
+ /* Free structure */
+ if (m->g.hi != m->g.a_hi) {
+ free(m->g.hi);
+ free(m->g.fhi);
+ }
+ free((void *)m);
+}
+
+/* Set the first targets & values from the corner values. */
+static void init_fsoln(
+omgtp *m, /* Destination */
+double **vdata /* di^2 array of function and target values to init array corners with. */
+ /* Corners are ordered with lowest index dimension changing most rapidly. */
+ /* (Function data at index 0, target data at index fdi) */
+) {
+ rspl *s = m->s;
+ int di = s->di;
+ int fdi = s->fdi;
+ int gno = m->g.no;
+ int gres_1[MXDI];
+ int e, n;
+ double *gp; /* Pointer to dest g.a[] grid cube base */
+ ECOUNT(gc, MXDIDO, di, 0, m->g.res, 0); /* Counter for output points */
+ double *gw; /* weight for each grid cube corner */
+ double a_gw[DEF2MXDI]; /* default allocation for gw */
+
+ gw = a_gw;
+ if ((1 << di) > DEF2MXDI) {
+ if ((gw = (double *) malloc(sizeof(double) * (1 << di))) == NULL)
+ error("rspl malloc failed - interp_rspl_nl");
+ }
+
+ for (e = 0; e < di; e++)
+ gres_1[e] = m->g.res[e]-1;
+
+ /* For all output grid points (could skip non-surface points ?) */
+ EC_INIT(gc);
+ for (n = 0, gp = m->g.a; n < gno; n++, gp += m->g.pss) {
+ double we[MXDI]; /* 1.0 - Weight in each dimension */
+
+ /* Figure out the pointer to the grid data and its weighting */
+ {
+ gp = m->g.a; /* Base of output array */
+ for (e = 0; e < di; e++)
+ we[e] = (double)gc[e]/gres_1[e]; /* 1.0 - weight */
+ }
+
+ /* Compute corner weights needed for interpolation */
+ {
+ int i, g;
+ gw[0] = 1.0;
+ for (e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++) {
+ gw[g+i] = gw[i] * we[e];
+ gw[i] *= (1.0 - we[e]);
+ }
+ }
+ }
+
+ /* Compute the output values */
+ {
+ int i, f;
+ double w = gw[0];
+ double *d = vdata[0];
+
+ for (f = 0; f < m->g.pss; f++) /* Base of cube */
+ gp[f] = w * d[f];
+
+ for (i = 1; i < (1 << di); i++) { /* For all other corners of cube */
+ w = gw[i]; /* Strength reduce */
+ d = vdata[i];
+ for (f = 0; f < fdi; f++)
+ gp[f] += w * d[f];
+ }
+
+ }
+
+ EC_INC(gc);
+ }
+
+ if (gw != a_gw)
+ free(gw);
+}
+
+
+/* Transfer a device and target values solution from one omgtp to another. */
+/* (We assume that they are for the same problem) */
+static void init_soln(
+ omgtp *m1, /* Destination */
+ omgtp *m2 /* Source */
+) {
+ rspl *s = m1->s;
+ int di = s->di;
+ int gno = m1->g.no;
+ int gres1_1[MXDI];
+ int gres2_1[MXDI];
+ int e, n;
+ double *a; /* Pointer to dest g.a[] grid cube base */
+ ECOUNT(gc, MXDIDO, di, 0, m1->g.res, 0); /* Counter for output points */
+ double *gw; /* weight for each grid cube corner */
+ double a_gw[DEF2MXDI]; /* default allocation for gw */
+
+ gw = a_gw;
+ if ((1 << di) > DEF2MXDI) {
+ if ((gw = (double *) malloc(sizeof(double) * (1 << di))) == NULL)
+ error("rspl malloc failed - interp_rspl_nl");
+ }
+
+ for (e = 0; e < di; e++) {
+ gres1_1[e] = m1->g.res[e]-1;
+ gres2_1[e] = m2->g.res[e]-1;
+ }
+
+ /* For all output grid points */
+ EC_INIT(gc);
+ for (n = 0, a = m1->g.a; n < gno; n++, a += m1->g.pss) {
+ double we[MXDI]; /* 1.0 - Weight in each dimension */
+ double *gp; /* Pointer to source g.a[] grid cube base */
+
+ /* Figure out which grid cell the point falls into */
+ {
+ double t;
+ int mi;
+ gp = m2->g.a; /* Base of solution array */
+ for (e = 0; e < di; e++) {
+ t = (double)gc[e] * gres2_1[e]/gres1_1[e];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= gres2_1[e])
+ mi = gres2_1[e]-1;
+ gp += mi * m2->g.fci[e]; /* Add Index offset for grid cube base in dimen */
+ we[e] = t - (double)mi; /* 1.0 - weight */
+ }
+ }
+
+ /* Compute corner weights needed for interpolation */
+ {
+ int i, g;
+ gw[0] = 1.0;
+ for (e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++) {
+ gw[g+i] = gw[i] * we[e];
+ gw[i] *= (1.0 - we[e]);
+ }
+ }
+ }
+
+ /* Compute the output values */
+ {
+ int i, f;
+ double w = gw[0];
+ double *d = gp + m2->g.fhi[0];
+
+ for (f = 0; f < m1->g.pss; f++) /* Base of cube */
+ a[f] = w * d[f];
+
+ for (i = 1; i < (1 << di); i++) { /* For all other corners of cube */
+ w = gw[i]; /* Strength reduce */
+ d = gp + m2->g.fhi[i];
+ for (f = 0; f < m1->g.pss; f++)
+ a[f] += w * d[f];
+ }
+
+ }
+ EC_INC(gc);
+ }
+
+ if (gw != a_gw)
+ free(gw);
+}
+
+/* - - - - - - - - - - - - - - - - - - - -*/
+static double one_itter(omgtp *m, int first);
+
+/* Itterate the optimisation functions until we are happy things have settled */
+static void
+solve_gres(
+omgtp *m,
+double tol
+) {
+ int i;
+ double dtol = tol * 0.1; /* Delta tol limit */
+ double ltt, tt;
+
+ ltt = 1.0;
+ tt = tol * 10.0;
+
+ for (i = 0; i < 500; i++) {
+ if (i == 0)
+ tt = one_itter(m, 1);
+
+ ltt = tt;
+ tt = one_itter(m, 0);
+
+ if (tt < tol || (ltt - tt) < dtol) /* Get within 0.1 % */
+ break;
+ }
+}
+
+/* Optimise the points values and (optionally) targets */
+/* Use Red/Black order, return total error after this itteration. */
+/* Return the total optimisation error */
+static double
+one_itter(
+omgtp *m,
+int first /* Flag, NZ if this is the first pass at this resolution */
+) {
+ int di = m->s->di, fdi = m->s->fdi;
+ int tdi = m->tdi;
+ int i, e, f;
+ int gc[MXDI];
+ int *gres = m->g.res;
+ int gres_1[MXDI];
+ DCOUNT(cc, MXDIDO, di, -1, -1, 2); /* Surrounding cube counter */
+ double *gpp; /* Current grid point pointer */
+ double ssum[MXDO+MXDI+2*MXDI]; /* Pointer to surrounding average values */
+ double *surav; /* Surrounding average values */
+ double awt; /* Average weight */
+ double terr = 0.0; /* Total error */
+ int surf; /* This point is on the surface */
+
+ for (e = 0; e < di; e++) {
+ gc[e] = 0; /* init coords */
+ gres_1[e] = gres[e] - 1;
+ }
+
+ /* Until done */
+ for (;;) {
+
+ /* See if we are on the surface */
+ surf = 0;
+ gpp = m->g.a;
+ for (e = 0; e < di; e++) {
+ gpp += m->g.fci[e] * gc[e]; /* Compute pointer to current point */
+
+ if (gc[e] == 0 || gc[e] == gres_1[e])
+ surf = 1;
+ }
+
+ surav = NULL;
+ if (!surf) {
+
+ for (f = 0; f < (fdi + tdi); f++)
+ ssum[f] = 0.0;
+ awt = 0.0;
+
+ /* Average the 3x3 surrounders */
+ DC_INIT(cc)
+ for (i = 0; !DC_DONE(cc); i++ ) {
+ double *gp = m->g.a;
+
+ for (e = 0; e < di; e++) {
+ int j;
+ j = gc[e] + cc[e];
+ if (j < 0 && j > gres_1[e]) { /* outside */
+ break;
+ }
+ gp += m->g.fci[e] * j; /* Compute pointer to surrounder */
+ }
+ if (e >= di) { /* We have a valid point */
+ for (f = 0; f < (fdi + tdi); f++)
+ ssum[f] += gp[f];
+ awt += 1.0;
+ }
+ DC_INC(cc);
+ }
+ if (awt > 0.0) { /* Compute the average */
+ for (f = 0; f < (fdi + tdi); f++)
+ ssum[f] /= awt;
+ surav = ssum;
+ }
+ }
+
+ /* Call optimisation function */
+ terr += m->func(m->fdata, gpp, surav, first, m->sf.cw);
+
+ /* Increment index in red/black order */
+ for (e = 0; e < di; e++) {
+ if (e == 0) {
+ gc[0] += 2; /* Inc coordinate by 2 */
+ } else {
+ gc[e] += 1; /* Inc coordinate */
+ }
+ if (gc[e] < gres[e])
+ break; /* No carry */
+ gc[e] -= gres[e]; /* Reset coord */
+
+ if ((gres[e] & 1) == 0) { /* Compensate for odd grid */
+ gc[0] ^= 1; /* XOR lsb */
+ }
+ }
+ /* Stop on reaching 0 */
+ for(e = 0; e < di; e++)
+ if (gc[e] != 0)
+ break;
+ if (e == di)
+ break; /* Finished */
+ }
+
+ return terr;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rspl/rev.c b/rspl/rev.c
new file mode 100644
index 0000000..237dbe3
--- /dev/null
+++ b/rspl/rev.c
@@ -0,0 +1,6608 @@
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized spline data structure
+ *
+ * Reverse interpolation support code.
+ *
+ * Author: Graeme W. Gill
+ * Date: 30/1/00
+ *
+ * Copyright 1999 - 2008 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ *
+ * Latest simplex/linear equation version.
+ */
+
+/* TTBD:
+
+ Should fix the clipping case so that a direction weighting
+ funtion can be applied. This should be used just like
+ the perceptual case to increase L* constance for dark
+ colors. Will this stuff up the geometric consistency though ?
+ [ See fill_nncell(), fill_nncell() and users of calc_fwd_nn_cell_list(),
+ ie. nnearest_clip_solve(), clipn_setsort() etc. ]
+ The SVD least squares computation case makes this hard to change ?
+ Would have to feed in a weighting function, or can it be general ?
+
+ Allow function callback to set auxiliary values for
+ flag RSPL_AUXLOCUS.
+ How to pass enough info back to aux_compute() ?
+
+ Should auxil return multiple solutions if it finds them ???
+
+ */
+
+/* TTBD:
+ Get rid of error() calls - return status instead
+
+ Need to add a hefty overview and explanation of
+ how all this works, before I forget it !
+
+ ie:
+
+ Basic function requirements: exact, auxil, locus, clip
+
+ Fwd cell - reverse cell list lookup
+
+ Basic layout di -> fdi + auxils + ink limit
+
+ Basic search strategy
+
+ Sub Simplex decomposition & properties
+
+ How each type of function finds solutions
+ Sub-simplex dimensionality & dof + target dim & dof
+ Linear algebra choices.
+
+ How final solutions are chosen
+
+ */
+
+/* PROBLEMS:
+
+ Sometimes the aux locus doesn't correspond exactly to
+ the inversion :- ie. one locus segment is returned,
+ yet the inversion can't return a solution with
+ a particular aux target that lies within that segment.
+ (1150 near black, k ~= 0.4).
+
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <memory.h>
+#include <time.h>
+
+#ifdef NT
+# ifdef WINVER
+# undef WINVER
+# endif
+# define WINVER 0x0500 /* We need 2k features */
+# include <windows.h>
+#else
+# include <unistd.h>
+# ifdef __APPLE__
+# include <fcntl.h>
+# include <sys/types.h>
+# include <sys/sysctl.h>
+# endif
+#endif
+
+#define INKSCALE 5000.0 /* For ink limit weighting to fudge SVD least squares solution */
+
+#include "rspl_imp.h"
+#include "numlib.h"
+#include "sort.h" /* Heap sort */
+#include "counters.h" /* Counter macros */
+
+//#define DMALLOC_GLOBALS
+//#include "dmalloc.h"
+//#undef DMALLOC_GLOBALS
+
+#undef DEBUG1 /* Higher level code */
+#undef DEBUG2 /* Lower level code */
+
+/* Debug memory usage accounting */
+#ifdef NEVER
+#ifdef NEVER
+int thissz, lastsz = -1;
+#define INCSZ(s, bbb) { \
+ (s)->rev.sz += (bbb); \
+ (s)->rev.thissz = (s)->rev.sz/1000000; \
+ if ((s)->rev.thissz != (s)->rev.lastsz) fprintf(stderr,"~1 0x%x: %s, %d: rev size = %d Mbytes, delta %d, limit %d\n",((int)(s) >> 8) & 0xf, __FILE__, __LINE__,(s)->rev.thissz,(bbb),(s)->rev.max_sz/1000000); \
+ (s)->rev.lastsz = (s)->rev.thissz; \
+ }
+#define DECSZ(s, bbb) { \
+ (s)->rev.sz -= (bbb); \
+ (s)->rev.thissz = (s)->rev.sz/1000000; \
+ if ((s)->rev.thissz != (s)->rev.lastsz) fprintf(stderr,"~1 0x%x: %s, %d: rev size = %d Mbytes, delta %d, limit %d\n",((int)(s) >> 8) & 0xf, __FILE__, __LINE__,(s)->rev.thissz,-(bbb),(s)->rev.max_sz/1000000); \
+ (s)->rev.lastsz = (s)->rev.thissz; \
+ }
+#else
+#define INCSZ(s, bbb) (s)->rev.sz += (bbb); \
+ fprintf(stderr,"%s, %d: rev.sz += %d\n",__FILE__, __LINE__, bbb)
+#define DECSZ(s, bbb) (s)->rev.sz -= (bbb); \
+ fprintf(stderr,"%s, %d: rev.sz -= %d\n",__FILE__, __LINE__, bbb)
+#endif
+#else
+#define INCSZ(s, bbb) (s)->rev.sz += (bbb)
+#define DECSZ(s, bbb) (s)->rev.sz -= (bbb)
+#endif
+
+/* Set STATS in rev.h */
+
+#define DOSORT /* Cell sort */
+
+/* Print a vectors value */
+#define DBGVI(text, dim, out, vec, end) \
+{ int pveci; \
+ printf("%s",text); \
+ for (pveci = 0 ; pveci < (dim); pveci++) \
+ printf(out,(vec)[pveci]); \
+ printf(end); \
+}
+
+/* Print a matrix value */
+#define DBGMI(text, rows, cols, out, mat, end) \
+{ int pveci, pvecr; \
+ printf("%s",text); \
+ for (pvecr = 0 ; pvecr < (rows); pvecr++) { \
+ for (pveci = 0 ; pveci < (cols); pveci++) \
+ printf(out,(mat)[pvecr][pveci]); \
+ if ((pvecr+1) < (rows)) \
+ printf("\n"); \
+ } \
+ printf(end); \
+}
+
+/* Do an arbitrary printf */
+#define DBGI(text) printf text ;
+
+#undef DEBUG
+#undef DBG
+#undef DBGV
+#undef DBGM
+
+#undef NEVER
+#define ALWAYS
+
+#ifdef DEBUG1
+#undef DBGS
+#undef DBG
+#undef DBGV
+#undef DBGM
+#define DEBUG
+#define DBGS(xxx) xxx
+#define DBG(xxx) DBGI(xxx)
+#define DBGV(xxx) DBGVI xxx
+#define DBGM(xxx) DBGMI xxx
+#else
+#undef DEBUG
+#undef DBGS
+#undef DBG
+#undef DBGV
+#undef DBGM
+#define DBGS(xxx)
+#define DBG(xxx)
+#define DBGV(xxx)
+#define DBGM(xxx)
+#endif
+
+/* Debug string routines */
+static char *pcellorange(cell *c);
+
+/* Convention is to use:
+ i to index grid points u.a
+ n to index data points d.a
+ e to index position dimension di
+ f to index output function dimension fdi
+ j misc and cube corners
+ k misc
+ */
+
+#define EPS (2e-6) /* 2e-6 Allowance for numeric error */
+
+static void make_rev(rspl *s);
+static void init_revaccell(rspl *s);
+
+static cell *get_rcell(schbase *b, int ix, int force);
+static void uncache_rcell(revcache *r, cell *cp);
+#define unget_rcell(r, cp) uncache_rcell(r, cp) /* These are the same */
+static void invalidate_revaccell(rspl *s);
+static int decrease_revcache(revcache *rc);
+
+/* ====================================================== */
+
+static schbase *init_search(rspl *s, int flags, double *av, int *auxm,
+ double *v, double *cdir, co *cpp, int mxsoln, enum ops op);
+static void adjust_search(rspl *s, int flags, double *av, enum ops op);
+static schbase *set_search_limit(rspl *s, double (*limit)(void *vcntx, double *in),
+ void *lcntx, double limitv);
+static void set_lsearch(rspl *s, int e);
+static void free_search(schbase *b);
+
+static int *calc_fwd_cell_list(rspl *s, double *v);
+
+static int *calc_fwd_nn_cell_list(rspl *s, double *v);
+
+static void init_line_eq(schbase *b, double st[MXRO], double de[MXRO]);
+static int *init_line(rspl *s, line *l, double st[MXRO], double de[MXRO]);
+static int *next_line_cell(line *l);
+
+static void search_list(schbase *b, int *rip, unsigned int tcount);
+
+static void clear_limitv(rspl *s);
+
+static double get_limitv(schbase *b, int ix, float *fcb, double *p);
+
+#ifdef STATS
+static char *opnames[6] = { "exact", "clipv", "clipn", "auxil", "locus" };
+#endif /* STATS */
+
+#define INF_DIST 1e38 /* Stands for infinite "current best" distance */
+
+/* ====================================================== */
+/* Globals that track overall usage of reverse cache to aportion memory */
+/* This is incremented for rspl with di > 1 when rev.rev_valid != 0 */
+size_t g_avail_ram = 0; /* Total maximum memory to be used */
+size_t g_test_ram = 0; /* Amount of memory that has been tested to be allocatable */
+int g_no_rev_cache_instances = 0;
+rev_struct *g_rev_instances = NULL;
+
+/* ------------------------------------------------------ */
+/* Retry allocation routines - if the malloc fails, */
+/* try reducing the cache size and trying again */
+/* (This won't catch the problem if it occurs in a malloc outside rev) */
+
+/* When a malloc fails, reduce the maximum cache to */
+/* it's current allocation minus the given size. */
+static void rev_reduce_cache(size_t size) {
+ rev_struct *rsi;
+ size_t ram;
+
+ /* Compute how much ram is currently allocated */
+ for (ram = 0, rsi = g_rev_instances; rsi != NULL; rsi = rsi->next)
+ ram += rsi->sz;
+
+ if (size > ram)
+ error("rev_reduce_cache: run out of rev virtual memory!");
+
+//printf("~1 size = %d, g_test_ram = %d\n",size,g_test_ram);
+//printf("~1 rev: Reducing cache because alloc of %d bytes failed. Reduced from %d to %d MB\n",
+//size, g_avail_ram/1000000, (ram - size)/1000000);
+ ram = g_avail_ram = ram - size;
+
+ /* Aportion the memory, and reduce the cache allocation to match */
+ ram /= g_no_rev_cache_instances;
+ for (rsi = g_rev_instances; rsi != NULL; rsi = rsi->next) {
+ revcache *rc = rsi->cache;
+
+ rsi->max_sz = ram;
+ while (rc->nunlocked > 0 && rsi->sz > rsi->max_sz) {
+ if (decrease_revcache(rc) == 0)
+ break;
+ }
+//printf("~1 rev instance ram = %d MB\n",rsi->sz/1000000);
+ }
+//fprintf(stdout, "%c~~1 There %s %d rev cache instance%s with %d Mbytes limit\n",
+// cr_char,
+// g_no_rev_cache_instances > 1 ? "are" : "is",
+// g_no_rev_cache_instances,
+// g_no_rev_cache_instances > 1 ? "s" : "",
+// ram/1000000);
+}
+
+/* Check that the requested allocation plus 20 M Bytes */
+/* can be allocated, and if not, reduce the rev-cache limit. */
+/* This is so as to detect running out of VM before */
+/* we actually run out and (on OS X) avoid emitting a warning. */
+static void rev_test_vram(size_t size) {
+ char *a1;
+#ifdef __APPLE__
+ int old_stderr, new_stderr;
+
+ /* OS X malloc() blabs about a malloc failure. This */
+ /* will confuse users, so we temporarily redirect stdout */
+ fflush(stderr);
+ old_stderr = dup(fileno(stderr));
+ new_stderr = open("/dev/null", O_WRONLY | O_APPEND);
+ dup2(new_stderr, fileno(stderr));
+#endif
+ size += 20 * 1024 * 1024; /* This depends on the VM region allocation size */
+ if ((a1 = malloc(size)) == NULL) {
+ rev_reduce_cache(size);
+ } else {
+ free(a1);
+ }
+ g_test_ram = size/2; /* Allow for twice as much VM to be used for each allocation */
+#ifdef __APPLE__
+ fflush(stderr);
+ dup2(old_stderr, fileno(stderr)); /* Restore stderr */
+ close(new_stderr);
+ close(old_stderr);
+#endif
+}
+
+static void *rev_malloc(rspl *s, size_t size) {
+ void *rv;
+
+ if ((size + 1 * 1024 * 1204) > g_test_ram)
+ rev_test_vram(size);
+ if ((rv = malloc(size)) == NULL) {
+ rev_reduce_cache(size);
+ rv = malloc(size);
+ }
+ if (rv != NULL)
+ g_test_ram -= size;
+
+ return rv;
+}
+
+static void *rev_calloc(rspl *s, size_t num, size_t size) {
+ void *rv;
+
+ if (((num * size) + 1 * 1024 * 1204) > g_test_ram)
+ rev_test_vram(size);
+ if ((rv = calloc(num, size)) == NULL) {
+ rev_reduce_cache(num * size);
+ rv = calloc(num, size);
+ }
+ if (rv != NULL)
+ g_test_ram -= size;
+
+ return rv;
+}
+
+static void *rev_realloc(rspl *s, void *ptr, size_t size) {
+ void *rv;
+
+ if ((size + 1 * 1024 * 1204) > g_test_ram)
+ rev_test_vram(size);
+ if ((rv = realloc(ptr, size)) == NULL) {
+ rev_reduce_cache(size); /* approximation */
+ rv = realloc(ptr, size);
+ }
+ if (rv != NULL)
+ g_test_ram -= size;
+
+ return rv;
+}
+
+
+/* ====================================================== */
+/* Set the ink limit information for any reverse interpolation. */
+/* Calling this will clear the reverse interpolaton cache and acceleration structures. */
+static void
+rev_set_limit_rspl(
+ rspl *s, /* this */
+ double (*limit)(void *lcntx, double *in), /* Optional input space limit function. Function */
+ /* should evaluate in[0..di-1], and return number that is not to exceed */
+ /* limitv. NULL if not used */
+ void *lcntx, /* Context passed to limit() */
+ double limitv /* Value that limit() is not to exceed */
+) {
+ schbase *b;
+
+ DBG(("rev: setting ink limit function 0x%x and limit %f\n",limit,limitv));
+ /* This is a restricted size function */
+ if (s->di > MXRI)
+ error("rspl: rev_set_limit can't handle di = %d",s->di);
+ if (s->fdi > MXRO)
+ error("rspl: rev_set_limit can't handle fdi = %d",s->fdi);
+
+ b = set_search_limit(s, limit, lcntx, limitv); /* Init and set limit info */
+
+ if (s->rev.inited) { /* If cache and acceleration has been allocated */
+ invalidate_revaccell(s); /* Invalidate the reverse cache */
+ }
+
+ /* Invalidate any ink limit values cached with the fwd grid data */
+ clear_limitv(s);
+}
+
+/* Get the ink limit information for any reverse interpolation. */
+static void
+rev_get_limit_rspl(
+ rspl *s, /* this */
+ double (**limitf)(void *lcntx, double *in), /* Return pointer to function of NULL if not set */
+ void **lcntx, /* return context pointer */
+ double *limitv /* Return limit value */
+) {
+ schbase *b = s->rev.sb;
+
+ /* This is a restricted size function */
+ if (s->di > MXRI)
+ error("rspl: rev_get_limit can't handle di = %d",s->di);
+ if (s->fdi > MXRO)
+ error("rspl: rev_get_limit can't handle fdi = %d",s->fdi);
+
+ if (b == NULL) {
+ *limitf = NULL;
+ *lcntx = NULL;
+ *limitv = 0.0;
+ } else {
+ *limitf = s->limitf;
+ *lcntx = s->lcntx;
+ *limitv = s->limitv/INKSCALE;
+ }
+}
+
+#define RSPL_CERTAIN 0x80000000 /* WILLCLIP hint is certain */
+#define RSPL_WILLCLIP2 (RSPL_CERTAIN | RSPL_WILLCLIP) /* Clipping will certainly be needed */
+
+/* Do reverse interpolation given target output values and (optional) auxiliary target */
+/* input values. Return number of results and clipping flag. If return value == mxsoln, */
+/* then there might be more results. The target values returned will correspond to the */
+/* actual (posssibly clipped) point. The return value is the number of solutions + */
+/* a clipped flag. Properly set hint flags improve performance, but a correct result should */
+/* be returned if the RSPL_NEARCLIP is set, even if they are not set correctly. */
+static int
+rev_interp_rspl(
+ rspl *s, /* this */
+ int flags, /* Hint flag */
+ int mxsoln, /* Maximum number of solutions allowed for */
+ int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no auxiliaries) */
+ double cdir[MXRO], /* Clip vector direction wrt to cpp[0].v and length - NULL if not used */
+ co *cpp /* Given target output space value in cpp[0].v[] + */
+ /* target input space auxiliaries in cpp[0].p[], return */
+ /* input space solutions in cpp[0..retval-1].p[], and */
+) {
+ int e, di = s->di;
+ int fdi = s->fdi;
+ int i, *rip = NULL;
+ schbase *b = NULL; /* Base search information */
+ double auxv[MXRI]; /* Locus proportional auxiliary values */
+ int didclip = 0; /* flag - set if we clipped the target */
+
+ DBGV(("\nrev interp called with out targets", fdi, " %f", cpp[0].v, "\n"));
+
+ /* This is a restricted size function */
+ if (di > MXRI)
+ error("rspl: rev_interp can't handle di = %d",di);
+ if (fdi > MXRO)
+ error("rspl: rev_interp can't handle fdi = %d",fdi);
+
+ if (auxm != NULL) {
+ double ax[MXRI];
+ for (i = 0; i < di; i++) {
+ if (auxm[i] != 0)
+ ax[i] = cpp[0].p[i];
+ else
+ ax[i] = 0.0;
+ }
+ DBGV((" auxiliaries mask", di, " %d", auxm, "\n"));
+ DBGV((" auxiliaries values", di, " %f", ax, "\n"));
+ }
+ DBG(("di = %d, fdi = %d\n",di, fdi));
+ DBG(("flags = 0x%x\n",flags));
+
+ mxsoln &= RSPL_NOSOLNS; /* Prevent silliness */
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* Auxiliary is proportion of locus, so we need to find locus extent */
+ if (flags & RSPL_AUXLOCUS) {
+ DBG(("rev interp has aux targets as proportion of locus\n"));
+
+ flags &= ~RSPL_WILLCLIP; /* Reset hint flag, as we will figure it out */
+
+ /* For each valid auxiliary */
+ for (e = 0; e < di; e++) {
+ if (auxm[e] == 0)
+ continue; /* Skip unsused auxiliaries */
+
+ /* Do search for min and max */
+ DBG(("rev locus searching for aux %d min/max\n", e));
+ if (b == NULL) {
+ b = init_search(s, flags, cpp[0].p, auxm, cpp[0].v, cdir, cpp, mxsoln, locus);
+#ifdef STATS
+ s->rev.st[b->op].searchcalls++;
+#endif /* STATS */
+ } else
+ set_lsearch(s, e); /* Reset locus search for next auxiliary */
+
+ if (rip == NULL) { /* Not done this yet */
+ rip = calc_fwd_cell_list(s, cpp[0].v); /* Reverse grid index for out target */
+ if (rip == NULL) {
+ DBG(("Got NULL list (point outside range) for auxiliary locus search\n"));
+ flags |= RSPL_WILLCLIP2;
+ break;
+ }
+ }
+
+ search_list(b, rip, s->get_next_touch(s)); /* Setup, sort and search the list */
+
+ if (b->min > b->max) { /* Failed to find locus */
+ DBG(("rev interp failed to find locus for aux %d, so expect clip\n",e));
+ flags |= RSPL_WILLCLIP2;
+ break;
+ }
+ auxv[e] = (cpp[0].p[e] * (b->max - b->min)) + b->min;
+ }
+
+ DBG(("rev interp got all locuses, so expect exact result\n",e));
+ if (!(flags & RSPL_WILLCLIP)) {
+ flags |= RSPL_EXACTAUX; /* Got locuses, so expect exact result */
+ }
+ }
+
+ /* Init the search information */
+ if (b == NULL)
+ b = init_search(s, flags, cpp[0].p, auxm, cpp[0].v, cdir, cpp, mxsoln, exact);
+ else
+ adjust_search(s, flags, auxv, exact); /* Using proportion of locus aux */
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* If hinted that we will not need to clip, look for exact solution. */
+ if (!(flags & RSPL_WILLCLIP)) {
+ DBG(("Hint we won't clip, so trying exact search\n"));
+
+ /* First do an exact search (init will select auxil if requested) */
+ adjust_search(s, flags, NULL, exact);
+
+ /* Figure out the reverse grid index appropriate for this request */
+ if (rip == NULL) /* Not done this yet */
+ rip = calc_fwd_cell_list(s, cpp[0].v);
+
+#ifdef STATS
+ s->rev.st[b->op].searchcalls++;
+#endif /* STATS */
+ if (rip != NULL) {
+ /* Setup, sort and search the list */
+ search_list(b, rip, s->get_next_touch(s));
+ } else {
+ DBG(("Got NULL list (point outside range) for first exact reverse cell\n"));
+ }
+
+ /* If we selected exact aux, but failed to find a solution, relax expectation */
+ if (b->nsoln == 0 && b->naux > 0 && (flags & RSPL_EXACTAUX)) {
+//printf("~1 relaxing notclip expactation when nsoln == %d, naux = %d, falgs & RSPL_EXACTAUX = 0x%x\n", b->nsoln,b->naux,flags & RSPL_EXACTAUX);
+ DBG(("Searching for exact match to auxiliary target failed, so try again\n"));
+ adjust_search(s, flags & ~RSPL_EXACTAUX, NULL, exact);
+
+#ifdef STATS
+ s->rev.st[b->op].searchcalls++;
+#endif /* STATS */
+ /* Candidate cell list should be the same */
+ if (rip != NULL) {
+ /* Setup, sort and search the list */
+ search_list(b, rip, s->get_next_touch(s));
+ } else {
+ DBG(("Got NULL list (point outside range) for nearest search reverse cell\n"));
+ }
+ }
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* If the exact search failed, and we should look for a nearest solution */
+ if (b->nsoln == 0 && (flags & RSPL_NEARCLIP)) {
+ DBG(("Trying nearest search\n"));
+
+#ifdef STATS
+ s->rev.st[b->op].searchcalls++;
+#endif /* STATS */
+
+ /* We get returned a list of cube base indexes of all cubes that have */
+ /* the closest valid vertex value to the target value. */
+ /* (This may not result in the true closest point if the geometry of */
+ /* the vertex values is localy non-smooth or self intersecting, */
+ /* but seems to return a good result in most realistic situations ?) */
+
+ adjust_search(s, flags, NULL, clipn);
+
+ /* Get list of cells enclosing nearest vertex */
+ if ((rip = calc_fwd_nn_cell_list(s, cpp[0].v)) != NULL) {
+ search_list(b, rip, s->get_next_touch(s)); /* Setup, sort and search the list */
+ } else {
+ DBG(("Got NULL list! (point inside gamut \?\?) for nearest search\n"));
+ }
+
+ if (b->nsoln > 0)
+ didclip = RSPL_DIDCLIP;
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* If we still don't have a solution, do a vector direction clip */
+ if (b->nsoln == 0 && b->canvecclip) {
+ /* Find clipping solution in vector direction */
+ line ln; /* Structure to hold line context */
+ unsigned int tcount; /* grid touch count for this opperation */
+
+ DBG(("Starting a clipping vector search now!!\n"));
+
+ adjust_search(s, flags, NULL, clipv);
+
+ tcount = s->get_next_touch(s); /* Get next grid touched generation count */
+
+#ifdef STATS
+ s->rev.st[b->op].searchcalls++;
+#endif /* STATS */
+ init_line_eq(b, b->v, cdir); /* Init the implicit line equation */
+ rip = init_line(s, &ln, cpp[0].v, cdir); /* Init the line cell dda */
+//~~1 HACK!!! should be <= 1.0 !!!
+ for (; ln.t <= 2.0; rip = next_line_cell(&ln)) {
+ if (rip == NULL) {
+ DBG(("Got NULL list for this reverse cell\n"));
+ continue;
+ }
+
+ /* Setup, sort and search the list */
+ search_list(b, rip, tcount);
+
+ /* If we have found a solution, then abort the search - */
+ /* this line will be taking us away from the best solution. */
+ if (b->nsoln > 0)
+ break;
+ }
+ if (b->nsoln > 0)
+ didclip = RSPL_DIDCLIP;
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* If the clipped solution seems to have been jumping to conclusions, */
+ /* search for an exact solution. */
+ if (didclip && (flags & RSPL_WILLCLIP && !(flags & RSPL_CERTAIN))
+ && (b->cdist/s->get_out_scale(s)) < 0.002) {
+ co c_cpp = b->cpp[0]; /* Save clip solution in case we want it */
+ double c_idist = b->idist;
+ int c_iabove = b->iabove;
+ int c_nsoln = b->nsoln;
+ int c_pauxcell = b->pauxcell;
+ double c_cdist = b->cdist;
+ int c_iclip = b->iclip;
+
+ DBG(("Trying exact search again\n"));
+
+ /* Do an exact search (init will select auxil if requested) */
+ adjust_search(s, flags & ~RSPL_WILLCLIP, NULL, exact);
+
+ /* Figure out the reverse grid index appropriate for this request */
+ rip = calc_fwd_cell_list(s, cpp[0].v);
+
+#ifdef STATS
+ s->rev.st[b->op].searchcalls++;
+#endif /* STATS */
+ if (rip != NULL) {
+ /* Setup, sort and search the list */
+ search_list(b, rip, s->get_next_touch(s));
+ } else {
+ DBG(("Got NULL list (point outside range) for first exact reverse cell\n"));
+ }
+
+ /* If we selected exact aux, but failed to find a solution, relax expectation */
+ if (b->nsoln == 0 && b->naux > 0 && (flags & RSPL_EXACTAUX)) {
+ DBG(("Searching for exact match to auxiliary target failed, so try again\n"));
+//printf("~1 relaxing didclip expactation when nsoln == %d, naux = %d, flags & RSPL_EXACTAUX = 0x%x\n", b->nsoln,b->naux,flags & RSPL_EXACTAUX);
+ adjust_search(s, flags & ~RSPL_EXACTAUX, NULL, exact);
+
+#ifdef STATS
+ s->rev.st[b->op].searchcalls++;
+#endif /* STATS */
+ /* Candidate cell list should be the same */
+ if (rip != NULL) {
+ /* Setup, sort and search the list */
+ search_list(b, rip, s->get_next_touch(s));
+ } else {
+ DBG(("Got NULL list (point outside range) for nearest search reverse cell\n"));
+ }
+ }
+
+ /* If we did get an exact solution */
+ if (b->nsoln > 0) {
+ DBG(("Deciding to return exact solution after finding clipped\n"));
+ didclip = 0; /* Reset did-clip and return exact solution */
+
+ } else {
+ DBG(("keeping clipped solution\n"));
+ /* Restore the clipped solution */
+ b->cpp[0] = c_cpp;
+ b->idist = c_idist;
+ b->iabove = c_iabove;
+ b->nsoln = c_nsoln;
+ b->pauxcell = c_pauxcell;
+ b->cdist = c_cdist;
+ b->iclip = c_iclip;
+ }
+ }
+
+ if (b->nsoln > 0) {
+ DBGV(("rev interp returning 1st soln: ",di," %f", cpp[0].p, "\n"));
+ }
+ DBG(("rev interp returning %d solutions%s\n",b->nsoln, didclip ? " [clip]" : ""));
+
+ return b->nsoln | didclip;
+}
+
+/* ------------------------------------------------------------------------------------ */
+/* Do reverse search for the auxiliary min/max ranges of the solution locus for the */
+/* given target output values. */
+/* Return number of locus segments found, up to mxsoln. 0 will be returned if no solutions */
+/* are found. */
+
+static int
+rev_locus_segs_rspl (
+ rspl *s, /* this */
+ int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no auxiliaries) */
+ co *cpp, /* Input value in cpp[0].v[] */
+ int mxsoln, /* Maximum number of solutions allowed for */
+ double min[][MXRI], /* Array of min[MXRI] to hold return segment minimum values. */
+ double max[][MXRI] /* Array of max[MXRI] to hold return segment maximum values. */
+) {
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+ int six; /* solution index */
+ int *rip = NULL;
+ int rv = 1; /* Return value */
+ schbase *b = NULL; /* Base search information */
+
+ DBGV(("rev locus called with out targets", fdi, " %f", cpp[0].v, "\n"));
+
+ /* This is a restricted size function */
+ if (di > MXRI)
+ error("rspl: rev_locus_segs can't handle di = %d",di);
+ if (fdi > MXRO)
+ error("rspl: rev_locus_segs can't handle fdi = %d",fdi);
+
+ if (mxsoln < 1) {
+ return 0; /* Guard against silliness */
+ }
+
+ if (auxm != NULL) {
+ int i;
+ double ax[MXRI];
+ for (i = 0; i < di; i++) {
+ if (auxm[i] != 0)
+ ax[i] = cpp[0].p[i];
+ else
+ ax[i] = 0.0;
+ }
+ DBGV((" auxiliaries mask", di, " %d", auxm, "\n"));
+ DBGV((" auxiliaries values", di, " %f", ax, "\n"));
+ }
+
+ /* Init default return values */
+ for (six = 0; six < mxsoln; six++) {
+ for (e = 0; e < di; e++) {
+ if (auxm[e] == 0) {
+ min[six][e] = max[six][e] = 0; /* Return 0 for unused auxiliaries */
+ } else {
+ min[six][e] = 1.0; /* max < min indicates invalid range */
+ max[six][e] = 0.0;
+ }
+ }
+ }
+
+ /* For each valid auxiliary */
+ for (e = 0; e < di; e++) {
+ if (auxm[e] == 0)
+ continue; /* Skip unsused auxiliaries */
+
+ /* Do search for min and max */
+ DBG(("rev locus searching for aux %d min/max\n", e));
+ if (b == NULL)
+ b = init_search(s, 0, cpp[0].p, auxm, cpp[0].v, NULL, cpp, mxsoln, locus);
+ else
+ set_lsearch(s, e); /* Reset locus search for next auxiliary */
+
+ if (rip == NULL) { /* Not done this yet */
+ rip = calc_fwd_cell_list(s, cpp[0].v); /* Reverse grid index for this request */
+ if (rip == NULL) {
+ DBG(("Got NULL list (point outside range) for auxiliary locus search\n"));
+ rv = 0;
+ break;
+ }
+ }
+
+ search_list(b, rip, s->get_next_touch(s)); /* Setup, sort and search the list */
+
+ if (b->min > b->max) {
+ rv = 0; /* Failed to find a result */
+ break;
+ }
+
+ if (b->asegs == 0) { /* Overall min max only */
+
+ min[0][e] = b->min; /* Save single result */
+ max[0][e] = b->max;
+
+ } else { /* Tracking auxiliary segments */
+ int si; /* Start i */
+ int i, j, ff;
+
+ /* Sort the segment list */
+#define HEAP_COMPARE(A,B) (A.xval < B.xval)
+ HEAPSORT(axisec, b->axisl, b->axisln)
+#undef HEAP_COMPARE
+
+#ifdef NEVER
+for (i = 0; i < b->axisln; i++) {
+printf("~2 xval = %f, verts = ",b->axisl[i].xval);
+for (f = 0; f < b->axisl[i].nv; f++)
+printf(" %d", b->axisl[i].vix[f]);
+printf("\n");
+}
+#endif
+ /* Find the segments by finding common verticies */
+ six = si = i = 0;
+
+ min[six][e] = b->axisl[i].xval;
+
+ for (i++; i < (b->axisln-1); i++) {
+ /* Check if any i and i-1 to j are connected */
+ for (j = i-1; j >= si; j--) {
+ for (f = 0; f < b->axisl[j].nv; f++) {
+ for (ff = 0; ff < b->axisl[i].nv; ff++) {
+ if (b->axisl[j].vix[f] == b->axisl[i].vix[ff])
+ break; /* Found a link */
+ }
+ if (ff < b->axisl[i].nv)
+ break;
+ }
+ if (f < b->axisl[j].nv)
+ break;
+ }
+ if (j < si) { /* Wasn't linked */
+ int ii, jj;
+ /* Think we found a break. Check that all the rest of */
+ /* the entries don't have any links to the previous group */
+
+ /* This could be rather a slow way of checking ! (On^2) */
+ for (ii = i+1; ii < (b->axisln); ii++) {
+ for (jj = i-1; jj >= si; jj--) {
+ for (f = 0; f < b->axisl[jj].nv; f++) {
+ for (ff = 0; ff < b->axisl[ii].nv; ff++) {
+ if (b->axisl[jj].vix[f] == b->axisl[ii].vix[ff])
+ break; /* Found a link */
+ }
+ if (ff < b->axisl[ii].nv)
+ break;
+ }
+ if (f < b->axisl[jj].nv)
+ break;
+ }
+ if (jj >= si)
+ break;
+ }
+ if (ii >= b->axisln) { /* Wasn't forward linked */
+ /* Nothing ahead links to last group */
+ max[six][e] = b->axisl[i-1].xval;
+
+ /* If we run out of solution space */
+ /* merge the last segments */
+ if ((six+1) < mxsoln) {
+ six++;
+ min[six][e] = b->axisl[i].xval;
+ }
+ }
+ }
+ }
+ max[six++][e] = b->axisl[i].xval;
+
+ if (six > rv)
+ rv = six;
+ }
+ }
+
+#ifdef STATS
+ s->rev.st[b->op].searchcalls++;
+#endif /* STATS */
+ if (rv) {
+ for (six = 0; six < rv; six++) {
+ DBG(("rev locus returning:\n"));
+ DBGV((" min", di, " %f", min[six], "\n"));
+ DBGV((" max", di, " %f", max[six], "\n"));
+ }
+ }
+
+ DBG(("rev locus returning status %d\n",rv));
+ return rv;
+}
+
+/* ------------------------------------------------------------------------------------ */
+typedef double mxdi_ary[MXRI];
+
+/* Do reverse search for the locus of the auxiliary input values given a target output. */
+/* Return 1 on finding a valid solution, and 0 if no solutions are found. */
+static int
+rev_locus_rspl(
+ rspl *s, /* this */
+ int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no auxiliaries) */
+ co *cpp, /* Input value in cpp[0].v[] */
+ double min[MXRI],/* Return minimum auxiliary values */
+ double max[MXRI] /* Return maximum auxiliary values */
+) {
+
+ /* Use segment routine to compute oveall locus */
+ return rev_locus_segs_rspl (s, auxm, cpp, 1, (mxdi_ary *)min, (mxdi_ary *)max);
+}
+
+/* ------------------------------------------------------------------------------------ */
+
+#ifdef DEBUG2
+#define DEBUG
+#undef DBG
+#undef DBGV
+#undef DBGM
+#define DBG(xxx) DBGI(xxx)
+#define DBGV(xxx) DBGVI xxx
+#define DBGM(xxx) DBGMI xxx
+#else
+#undef DEBUG
+#undef DBG
+#undef DBGV
+#undef DBGM
+#define DBG(xxx)
+#define DBGV(xxx)
+#define DBGM(xxx)
+#endif
+
+/* ------------------------------------------------ */
+/* subroutines of top level reverse lookup routine */
+
+static int exact_setsort(schbase *b, cell *c);
+static int exact_compute(schbase *b, simplex *x);
+
+static int auxil_setsort(schbase *b, cell *c);
+static int auxil_check(schbase *b, cell *c);
+static int auxil_compute(schbase *b, simplex *x);
+
+static int locus_setsort(schbase *b, cell *c);
+static int locus_check(schbase *b, cell *c);
+static int locus_compute(schbase *b, simplex *x);
+
+static int clipv_setsort(schbase *b, cell *c);
+static int clipv_check(schbase *b, cell *c);
+static int clipv_compute(schbase *b, simplex *x);
+
+static int clipn_setsort(schbase *b, cell *c);
+static int clipn_check(schbase *b, cell *c);
+static int clipn_compute(schbase *b, simplex *x);
+
+/* Allocate the search base structure */
+static schbase *
+alloc_sb(rspl *s) {
+ schbase *b;
+ if ((b = s->rev.sb = (schbase *)rev_calloc(s, 1, sizeof(schbase))) == NULL)
+ error("rspl malloc failed - rev.sb structure");
+ INCSZ(s, sizeof(schbase));
+
+ b->s = s; /* rsp */
+ b->pauxcell = /* Previous solution cell indexes */
+ b->plmaxcell =
+ b->plmincell = -1;
+
+ return b;
+}
+
+/* Free the search base structure */
+static void
+free_sb(schbase *b) {
+ DECSZ(b->s, sizeof(schbase));
+ free(b);
+}
+
+/* Do the basic search type independent initialization */
+static schbase * /* Return pointer to base search information */
+init_search(
+ rspl *s, /* rsp; */
+ int flags, /* Hint flag */
+
+ double *av, /* Auxiliary input values - may be NULL */
+ int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no auxiliaries) */
+ /* Locus search will search for max/min of first valid auxlilary */
+ double *v, /* Output value target, NULL if none */
+ double *cdir, /* Clip vector direction, NULL if none */
+ co *cpp, /* Array that hold solutions, NULL if none. */
+ int mxsoln, /* Maximum number of solutions allowed for */
+ enum ops op /* Type of reverse search operation requested */
+) {
+ schbase *b = NULL; /* Pointer to search base information structure */
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+
+ DBG(("Initializing search\n"));
+
+ if (s->rev.inited == 0) /* Compute reverse info if it doesn't exist */
+ make_rev(s);
+
+ /* If first time initialisation (Fourth section init) */
+ if ((b = s->rev.sb) == NULL) {
+ b = alloc_sb(s);
+ }
+
+ /* Init some basic search info */
+ b->op = op; /* operation */
+ b->flags = flags; /* hint flags */
+ b->canvecclip = 0; /* Assume invalid clip direction */
+
+ b->ixc = (1<<di)-1; /* Cube index of corner that holds maximum input values */
+
+ /* Figure out if auxiliaries have been requested */
+ b->naux = 0;
+ b->auxbm = 0;
+ if (auxm != NULL) {
+ unsigned bm;
+
+ if (mxsoln > 1)
+ b->asegs = 1; /* Find all segments */
+ else
+ b->asegs = 0; /* Find only overall aux locus range */
+
+ for (e = di-1, bm = 1 << e; e >= 0; e--, bm >>= 1) { /* Record auxiliary mask bits */
+ if (av != NULL)
+ b->av[e] = av[e]; /* Auxiliary target values */
+ b->auxm[e] = auxm[e]; /* Auxiliary mask */
+ if (auxm[e] != 0) {
+ b->auxbm |= bm; /* Auxiliary bit mask */
+ b->auxi[b->naux++] = e; /* Index of next auxiliary input to be used */
+ /* Auxiliary locus extent */
+ b->lxi = e; /* Assume first one */
+ b->max = -INF_DIST; /* In case searching for max */
+ b->min = INF_DIST; /* In case searching for minimum */
+ b->axisln = 0; /* No intersects in list */
+ }
+ }
+ }
+
+ /* Figure out if the clip direction is meaningfull */
+ /* Check that the clip vector makes sense */
+ if (cdir != NULL) { /* Clip vector is specified */
+ double ss;
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = cdir[f];
+ b->cdir[f] = tt;
+ ss += tt * tt;
+ }
+
+ if (ss > 1e-6) {
+ b->canvecclip = 1; /* It has a non-zero length */
+ ss = sqrt(ss);
+ /* Compute normalised clip vector direction */
+ for (f = 0; f < fdi; f++) {
+ b->ncdir[f] = b->cdir[f]/ss;
+ }
+ }
+ }
+
+ if (di <= fdi) /* Only allow auxiliaries if di > fdi */
+ b->naux = 0;
+
+ /* Switch to appropriate operation */
+ if (b->op == exact && (b->naux > 0 || di != fdi)) {
+ b->op = auxil;
+ } else if (b->op == auxil && b->naux == 0 && di == fdi) {
+ b->op = exact;
+ }
+
+ /* Set appropriate functions for type of operation */
+ switch (b->op) {
+ case exact:
+ b->setsort = exact_setsort;
+ b->check = NULL;
+ b->compute = exact_compute;
+ b->snsdi = b->ensdi = di; /* Search full dimension simplex, expect point soln. */
+ break;
+ case auxil:
+ b->setsort = auxil_setsort;
+ b->check = auxil_check;
+ b->compute = auxil_compute;
+ b->snsdi = di; /* Start here DOF = di-fdi locus solutions */
+ b->ensdi = fdi; /* End with DOF = 0 for point solutions */
+ break;
+ case locus:
+ b->setsort = locus_setsort;
+ b->check = locus_check;
+ b->compute = locus_compute;
+ b->snsdi = b->ensdi = fdi; /* Search for point solutions */
+ break;
+ case clipv:
+ b->setsort = clipv_setsort;
+ b->check = clipv_check;
+ b->compute = clipv_compute;
+ /* Clip vector 1 dimension in output space, */
+ b->snsdi = b->ensdi = fdi-1; /* search planes for combined point solution */
+ break;
+ case clipn:
+ b->setsort = clipn_setsort;
+ b->check = clipn_check;
+ b->compute = clipn_compute;
+ b->snsdi = 0; /* Start with DOF = 0 for point solutions */
+ b->ensdi = fdi-1; /* End on DOF = di-fdi-1 on surfaces of simplexes */
+ break;
+ default:
+ error("init_search: Unknown operation %d\n",b->op);
+ }
+
+ if (v != NULL) {
+ for (f = 0; f < fdi; f++) /* Record target output values */
+ b->v[f] = v[f];
+ b->v[fdi] = s->limitv; /* Limitvalue is output target for limit clip subsimplexes */
+ }
+
+ b->mxsoln = mxsoln; /* Allow solutions to be returned */
+ b->cpp = cpp; /* Put solutions here */
+ b->nsoln = 0; /* No solutions at present */
+ b->iclip = 0; /* Default solution isn't above ink limit */
+
+ if (flags & RSPL_EXACTAUX) /* Expect to be able to match auxiliary target exactly */
+ b->idist = 2.0 * EPS; /* Best input distance to beat - helps sort/triage */
+ else
+ b->idist = INF_DIST; /* Best input distance to beat. */
+ b->iabove = 0; /* Best isn't known to be above (yet) */
+
+ b->cdist = INF_DIST; /* Best clip distance to beat. */
+
+ DBG(("Search initialized\n"));
+
+ return b;
+}
+
+/* Adjust the search */
+static void
+adjust_search(
+ rspl *s, /* rsp; */
+ int flags, /* Hint flag */
+ double *av, /* Auxiliary input values - may be NULL */
+ enum ops op /* Type of reverse search operation requested */
+) {
+ schbase *b = s->rev.sb; /* Pointer to search base information structure */
+ int e, di = s->di;
+ int fdi = s->fdi;
+
+ DBG(("Adjusting search\n"));
+
+ b->op = op; /* operation */
+ b->flags = flags; /* hint flags */
+
+ /* Switch from exact to aux if we need to */
+ if (b->op == exact && (b->naux > 0 || di != fdi)) {
+ b->op = auxil;
+ } else if (b->op == auxil && b->naux == 0 && di == fdi) {
+ b->op = exact;
+ }
+
+ /* Update auxiliary target values */
+ if (av != NULL) {
+ for (e = 0; e < b->naux; e++) {
+ int ee = b->auxi[e];
+ b->av[ee] = av[ee];
+ }
+ }
+
+ /* Set appropriate functions for type of operation */
+ switch (b->op) {
+ case exact:
+ b->setsort = exact_setsort;
+ b->check = NULL;
+ b->compute = exact_compute;
+ b->snsdi = b->ensdi = di; /* Expect point solution */
+ break;
+ case auxil:
+ b->setsort = auxil_setsort;
+ b->check = auxil_check;
+ b->compute = auxil_compute;
+ b->snsdi = di; /* Start here DOF = di-fdi locus solutions */
+ b->ensdi = fdi; /* End with DOF = 0 for point solutions, */
+ break; /* will early exit DOF if good soln found. */
+ case locus:
+ b->setsort = locus_setsort;
+ b->check = locus_check;
+ b->compute = locus_compute;
+ b->snsdi = b->ensdi = fdi; /* Search for point solutions */
+ break;
+ case clipv:
+ b->setsort = clipv_setsort;
+ b->check = clipv_check;
+ b->compute = clipv_compute;
+ /* Clip vector 1 dimension in output space, */
+ b->snsdi = b->ensdi = fdi-1; /* so the intersection with the simplex is a point. */
+ break;
+ case clipn:
+ b->setsort = clipn_setsort;
+ b->check = clipn_check;
+ b->compute = clipn_compute;
+ b->snsdi = 0; /* Start with DOF = 0 for point solutions */
+ b->ensdi = fdi-1; /* End on DOF = di-fdi-1 on surfaces of simplexes */
+ break; /* Will go through all DOF */
+ default:
+ error("init_search: Unknown operation %d\n",b->op);
+ }
+
+ b->nsoln = 0; /* No solutions at present */
+
+ if (flags & RSPL_EXACTAUX) /* Expect to be able to match auxiliary target exactly */
+ b->idist = 2.0 * EPS; /* Best input distance to beat - helps sort/triage */
+ else
+ b->idist = INF_DIST; /* Best input distance to beat. */
+ b->iabove = 0; /* Best isn't known to be above (yet) */
+
+ b->cdist = INF_DIST; /* Best clip distance to beat. */
+
+ DBG(("Search adjusted\n"));
+}
+
+/* Adjust existing locus search for a different auxiliary */
+static void
+set_lsearch(
+rspl *s,
+int e /* Next auxiliary */
+) {
+ schbase *b = s->rev.sb; /* Pointer to search base information structure */
+
+ b->lxi = e; /* Assume first one */
+ b->max = -INF_DIST; /* In case searching for max */
+ b->min = INF_DIST; /* In case searching for minimum */
+ b->axisln = 0; /* No intersects in list */
+}
+
+/* Set the limit search information */
+/* Note this doesn't create or init the main rev information. */
+static schbase * /* Return pointer to base search information */
+set_search_limit(
+ rspl *s, /* rsp; */
+ double (*limitf)(void *vcntx, double *in), /* Optional input space limit function. Function */
+ /* should evaluate in[0..di-1], and return number that is not to exceed */
+ /* limitv. NULL if not used */
+ void *lcntx, /* Context passed to limit() */
+ double limitv /* Value that limit() is not to exceed */
+) {
+ schbase *b = NULL; /* Pointer to search base information structure */
+
+ /* If sb info needs initialising (Fourth section init) */
+ if ((b = s->rev.sb) == NULL) {
+ b = alloc_sb(s);
+ }
+
+ s->limitf = limitf; /* Input limit function */
+ s->lcntx = lcntx; /* Context passed to limit() */
+ s->limitv= INKSCALE * limitv; /* Context passed to values not to be exceedded by limit() */
+ if (limitf != NULL) {
+ s->limiten = 1; /* enable limiting by default */
+ } else
+ s->limiten = 0; /* No limit function, so limiting not enabled. */
+
+ return b;
+}
+
+/* Free any search specific data, plus the search base. */
+static void
+free_search(
+schbase *b /* Base search information */
+) {
+ DBG(("Freeing search\n"));
+
+ /* Clip line implicit equation (incuding space for ink target) */
+ if (b->cla != NULL) {
+ int fdi = b->s->fdi;
+ free_dmatrix(b->cla, 0, fdi-1, 0, fdi);
+ b->cla = NULL;
+ }
+
+ /* Auxiliary segment list */
+ if (b->axislz > 0) {
+ free(b->axisl);
+ DECSZ(b->s, b->axislz * sizeof(axisec));
+ b->axisl = NULL;
+ b->axislz = 0;
+ b->axisln = 0;
+ }
+
+ /* Sorted cell list */
+ if (b->lclistz > 0) {
+ free(b->lclist);
+ DECSZ(b->s, b->lclistz * sizeof(cell *));
+ b->lclist = NULL;
+ b->lclistz = 0;
+ }
+
+ /* Simplex filter list */
+ if (b->lsxfilt > 0) {
+ free(b->sxfilt);
+ DECSZ(b->s, b->lsxfilt * sizeof(char));
+ b->sxfilt = NULL;
+ b->lsxfilt = 0;
+ }
+
+ free_sb(b);
+}
+
+/* Return the pointer to the list of fwd cells given */
+/* the target output values. The pointer will be to the first */
+/* index in the list (ie. list address + 3) */
+/* Return NULL if none in list (out of gamut). */
+static int *
+calc_fwd_cell_list(
+ rspl *s, /* this */
+ double *v /* Output values */
+) {
+ int f, fdi = s->fdi;
+ int **rpp;
+ int rgres_1 = s->rev.res - 1;
+
+ if (s->rev.rev_valid == 0)
+ init_revaccell(s);
+
+ for (rpp = s->rev.rev, f = 0; f < fdi; f++) {
+ int mi;
+ double t = (v[f] - s->rev.gl[f])/s->rev.gw[f];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0 || mi > rgres_1) { /* If outside valid reverse range */
+ return NULL;
+ }
+ rpp += mi * s->rev.coi[f]; /* Accumulate reverse grid pointer */
+ }
+ if (*rpp == NULL)
+ return NULL;
+ return (*rpp) + 3;
+}
+
+void alloc_simplexes(cell *c, int nsdi);
+
+/* Given a pointer to a list of fwd cells, cull cells that */
+/* cannot contain or improve the solution, sort the list, */
+/* and then compute the final best solution. */
+static void
+search_list(
+schbase *b, /* Base search information */
+int *rip, /* Pointer to first index in cell list */
+unsigned int tcount /* grid touch count for this operation */
+) {
+ rspl *s = b->s;
+ int nsdi;
+ int i;
+ int nilist; /* Number in cell list */
+ unsigned int stouch; /* Simplex touch count */
+
+ DBG(("search_list called\n"));
+
+ /* (rip[-3] contains allocation for fwd cells in the list) */
+ /* (rip[-2] contains the index of the next free entry in the list) */
+ /* (rip[-1] contains the reference count for the list) */
+ if (b->lclistz < rip[-3]) { /* Allocate more space if needed */
+
+ if (b->lclistz > 0) { /* Free old space before allocating new */
+ free(b->lclist);
+ DECSZ(b->s, b->lclistz * sizeof(cell *));
+ }
+ b->lclistz = 0;
+ /* Allocate enough space for all the candidate cells */
+ if ((b->lclist = (cell **)rev_malloc(s, rip[-3] * sizeof(cell *))) == NULL)
+ error("rev: malloc failed - candidate cell list, count %d",rip[-3]);
+ b->lclistz = rip[-3]; /* Current allocated space */
+ INCSZ(b->s, b->lclistz * sizeof(cell *));
+ }
+
+ /* Get the next simplex touch count, so that we don't search shared */
+ /* face simplexes more than once in this pass through the cells. */
+ if ((stouch = ++s->rev.stouch) == 0) { /* If touch count rolls over */
+ cell *cp;
+ stouch = s->rev.stouch = 1;
+
+ DBG(("touch has rolled over, resetting it\n"));
+ /* For all of the cells */
+ for (cp = s->rev.cache->mrubot; cp != NULL; cp = cp->mruup) {
+ int nsdi;
+
+ if (cp->s == NULL) /* Cell has never been used */
+ continue;
+
+ /* For all the simplexes in the cell */
+ for (nsdi = 0; nsdi <= s->di; nsdi++) {
+ if (cp->sx[nsdi] != NULL) {
+ int si;
+
+ for (si = 0; si < cp->sxno[nsdi]; si++) {
+ cp->sx[nsdi][si]->touch = 0;
+ }
+ }
+ }
+ }
+ }
+
+ /* For each chunk of the list that we can fit in the rcache: */
+ for(; *rip != -1;) {
+
+ /* Go through all the candidate fwd cells, and build up the list of search cells */
+ for(nilist = 0; *rip != -1; rip++) {
+ int ix = *rip; /* Fwd cell index */
+ float *fcb = s->g.a + ix * s->g.pss; /* Pointer to base float of fwd cell */
+ cell *c;
+
+ if (TOUCHF(fcb) >= tcount) { /* If we have visited this cell before */
+ DBG((" Already touched cell index %d\n",ix));
+ continue;
+ }
+ /* Get pointers to cells from cache, and lock it in the cache */
+ if ((c = get_rcell(b, ix, nilist == 0 ? 1 : 0)) == NULL) {
+ static int warned = 0;
+ if (!warned) {
+ warning("%cWarning - Reverse Cell Cache exausted, processing in chunks",cr_char);
+ warned = 1;
+ }
+ DBG(("revcache is exausted, do search in chunks\n"));
+ if (nilist == 0) {
+ /* This should never happen, because nz force should prevent it */
+ revcache *rc = s->rev.cache;
+ cell *cp;
+ int nunlk = 0;
+ /* Double check that there are no unlocked cells */
+ for (cp = rc->mrubot; cp != NULL && cp->refcount > 0; cp = cp->mruup) {
+ if (cp->refcount == 0)
+ nunlk++;
+ }
+ fprintf(stdout,"Diagnostic: rev.sz = %lu, rev.max_sz = %lu, numlocked = %d, nunlk = %d\n",
+ rc->s->rev.sz, rc->s->rev.max_sz, rc->nunlocked,nunlk);
+ error("Not enough memory to process in chunks");
+ }
+ break; /* cache has run out of room, so abandon, and do it next time */
+ }
+
+ DBG(("checking out cell %d range %s\n",ix,pcellorange(c)));
+ TOUCHF(fcb) = tcount; /* Touch it */
+
+ /* Check mandatory conditions, and compute search key */
+ if (!b->setsort(b, c)) {
+ DBG(("cell %d rejected from list\n",ix));
+ unget_rcell(s->rev.cache, c);
+ continue;
+ }
+ DBG(("cell %d accepted into list\n",ix));
+
+ b->lclist[nilist++] = c; /* Cell is accepted as recursion candidate */
+ }
+
+ if (nilist == 0) {
+ DBG(("List was empty\n"));
+ }
+
+#ifdef DOSORT
+ /* If appropriate, sort child cells into best order */
+ /* == sort key smallest to largest */
+ switch (b->op) {
+ case locus:
+ { /* Special case, adjust sort values */
+ double min = INF_DIST, max = -INF_DIST;
+ for (i = 0; i < nilist; i++) {
+ cell *c = b->lclist[i];
+ if (c->sort < min)
+ min = c->sort;
+ if (c->sort > max)
+ max = c->sort;
+ }
+ max = min + max; /* Total of min/max */
+ min = 0.5 * max; /* Average sort value */
+ for (i = 0; i < nilist; i++) {
+ cell *c = b->lclist[i];
+ if (c->ix == b->plmincell || c->ix == b->plmaxcell) {
+ c->sort = -1.0; /* Put previous solution cells at head of list */
+ } else if (c->sort > min) {
+ c->sort = max - c->sort; /* Reflect about average */
+ }
+ }
+ }
+ /* Fall through to sort */
+ case auxil:
+ case clipv:
+ case clipn:
+#define HEAP_COMPARE(A,B) (A->sort < B->sort)
+ HEAPSORT(cell *,b->lclist, nilist)
+#undef HEAP_COMPARE
+ break;
+ default:
+ break;
+ }
+#endif /* DOSORT */
+
+ DBG(("List sorted, about to search\n"));
+#ifdef NEVER
+ printf("\n~1 Op = %s, Cell sort\n",opnames[b->op]);
+ for (i = 0; i < nilist; i++) {
+ printf("~1 List %d, cell %d, sort = %f\n",i,b->lclist[i]->ix,b->lclist[i]->sort);
+ }
+#endif /* NEVER */
+
+ /*
+ Tried reversing the "for each cell" and "for each level" loops,
+ but it made a negligible difference to the performance.
+ We choose to have cell on the outer so that we can unlock
+ them as we go, so that they may be freed, even though
+ this is a couple of percent slower (?).
+ */
+
+ /* For each cell in the list */
+ for (i = 0; i < nilist; i++) {
+ cell *c = b->lclist[i];
+
+#ifdef STATS
+ s->rev.st[b->op].csearched++;
+#endif /* STATS */
+
+ /* For each dimensionality of sub-simplexes, in given order */
+ DBG(("Searching from level %d to level %d\n",b->snsdi, b->ensdi));
+ for (nsdi = b->snsdi;;) {
+ int j, nospx; /* Number of simplexes in cell */
+
+ DBG(("\n******************\n"));
+ DBG(("Searching level %d\n",nsdi));
+
+ /* For those searches that have an optimisation goal, */
+ /* re-check the cell to see if the goal can still improve on. */
+ if (b->check != NULL && !b->check(b, c))
+ break;
+
+ if (c->sx[nsdi] == NULL) {
+ alloc_simplexes(c, nsdi); /* Do level 1 initialisation for nsdi */
+ }
+
+ /* For each simplex in a cell */
+ nospx = c->sxno[nsdi]; /* Number of nsdi simplexes */
+ for (j = 0; j < nospx; j++) {
+ simplex *x = c->sx[nsdi][j];
+
+ if (x->touch >= stouch) {
+ continue; /* We've already seen this one */
+ }
+
+ if (s->limiten == 0) {
+ if (x->flags & SPLX_CLIPSX) /* If limiting is disabled, we're */
+ continue; /* not interested in clip plane simplexes */
+ }
+#ifdef STATS
+ s->rev.st[b->op].ssearched++;
+#endif /* STATS */
+ if (b->compute(b, x)) {
+ DBG(("search aborted by compute\n"));
+ break; /* Found enough solutions */
+ }
+ x->touch = stouch; /* Don't look at it again */
+
+ } /* Next Simplex */
+
+ if (nsdi == b->ensdi)
+ break; /* We're done with levels */
+
+ /* Next Simplex dimensionality */
+ if (b->ensdi < b->snsdi) {
+ if (nsdi == b->snsdi && b->nsoln > 0
+ && (b->op != auxil || b->idist <= 2.0 * EPS))
+ break; /* Don't continue though decreasing */
+ /* sub-simplex dimensions if we found a solution at */
+ /* the highest dimension level. */
+ nsdi--;
+ } else if (b->ensdi > b->snsdi) {
+ nsdi++; /* Continue through increasing sub-simplex dimenionality */
+ } /* until we get to the top. */
+ }
+ /* Unlock the cache cell now that we're done with it */
+ unget_rcell(s->rev.cache, b->lclist[i]);
+ } /* Next cell */
+
+ } /* Next chunk */
+
+ DBG(("search_list complete\n"));
+ return;
+}
+
+/* ------------------------------------- */
+/* Vector search in output space support */
+
+/* Setup the line, and fetch the first cell */
+/* Return the pointer to the list of fwd cells, NULL if none in list. */
+static int *
+init_line(
+ rspl *s, /* this */
+ line *l, /* line structure */
+ double st[MXRO], /* start of line */
+ double de[MXRO] /* line direction and length */
+) {
+ int f, fdi = s->fdi;
+ int **rpp;
+ int rgres_1 = s->rev.res - 1;
+ int nvalid = 0; /* Flag set if outside reverse grid range */
+
+ DBGV(("Line from ", fdi, " %f", st, "\n"));
+ DBGV(("In dir ", fdi, " %f", de, "\n"));
+ DBGV(("gl ", fdi, " %f", s->rev.gl, "\n"));
+ DBGV(("gh ", fdi, " %f", s->rev.gh, "\n"));
+ DBGV(("gw ", fdi, " %f", s->rev.gw, "\n"));
+
+ /* Init */
+ l->s = s;
+ for (f = 0; f < fdi; f++) {
+ l->st[f] = st[f] - s->rev.gl[f];
+ l->de[f] = de[f];
+ if (de[f] > 0.0)
+ l->di[f] = 1; /* Axis increments */
+ else if (de[f] < 0.0)
+ l->di[f] = -1;
+ else
+ l->di[f] = 0;
+ }
+ l->t = 0.0;
+ DBGV(("increments =", fdi, " %d", l->di, "\n"));
+
+ /* Figure out the starting cell */
+ for (rpp = s->rev.rev, f = 0; f < fdi; f++) {
+ double t = l->st[f]/s->rev.gw[f];
+ l->ci[f] = (int)floor(t); /* Grid coordinate */
+ if (l->ci[f] < 0 || l->ci[f] > rgres_1) /* If outside valid reverse range */
+ nvalid = 1;
+ rpp += l->ci[f] * s->rev.coi[f]; /* Accumulate reverse grid pointer */
+ }
+ DBGV(("current line cell = ", fdi, " %d", l->ci, "")); DBG((", t = %f, nvalid = %d\n",l->t,nvalid));
+#ifdef DEBUG
+{
+int ii;
+double tt;
+printf("Current cell = ");
+for (ii = 0; ii < fdi; ii++) {
+ tt = l->ci[ii] * s->rev.gw[ii] + s->rev.gl[ii];
+ printf(" %f - %f",tt,tt+s->rev.gw[ii]);
+}
+printf("\n");
+}
+#endif /* DEBUG */
+ if (nvalid)
+ return NULL;
+ if (*rpp == NULL)
+ return NULL;
+ return *rpp + 3;
+}
+
+/* Get the next cell on the line. */
+/* Return the pointer to the list of fwd cells, NULL if none in list. */
+static int *
+next_line_cell(
+ line *l /* line structure */
+) {
+ rspl *s = l->s;
+ int bf = 0, f, fdi = s->fdi;
+ int **rpp;
+ int rgres_1 = s->rev.res - 1;
+ double bt = 100.0; /* Best (smalest +ve) parameter value to move */
+
+ /* See which axis cell crossing we will hit next */
+ for (f = 0; f < fdi; f++) {
+ double t;
+ if (l->de[f] != 0) {
+ t = ((l->ci[f] + l->di[f]) * s->rev.gw[f] - l->st[f])/l->de[f];
+ DBG(("t for dim %d = %f\n",f,t));
+ if (t < bt) {
+ bt = t;
+ bf = f; /* Best direction to move */
+ }
+ }
+ }
+
+ /* Move to the next reverse grid coordinate */
+ l->ci[bf] += l->di[bf];
+ l->t = bt;
+
+ DBGV(("current line cell =", fdi, " %d", l->ci, "")); DBG((", t = %f\n",l->t));
+
+#ifdef DEBUG
+{
+int ii;
+double tt;
+printf("Current cell = ");
+for (ii = 0; ii < fdi; ii++) {
+ tt = l->ci[ii] * s->rev.gw[ii] + s->rev.gl[ii];
+ printf(" %f - %f",tt,tt+s->rev.gw[ii]);
+}
+printf("\n");
+}
+#endif /* DEBUG */
+
+ /* Compute reverse cell index */
+ for (rpp = s->rev.rev, f = 0; f < fdi; f++) {
+ if (l->ci[f] < 0 || l->ci[f] > rgres_1) { /* If outside valid reverse range */
+ DBG(("Outside list on dim %d, 0 <= %d <= %d\n", f, l->ci[f],rgres_1));
+ return NULL;
+ }
+ rpp += l->ci[f] * s->rev.coi[f]; /* Accumulate reverse grid pointer */
+ }
+ if (*rpp == NULL)
+ return NULL;
+ return *rpp + 3;
+}
+
+/* ------------------------------------- */
+/* Clip nearest support. */
+
+/* Track candidate cells nearest and furthest */
+struct _nncell_nf{
+ double n, f;
+}; typedef struct _nncell_nf nncell_nf;
+
+/* Given and empty nnrev index, create a list of */
+/* the forward cells that may contain the nearest value by */
+/* using and exaustive search. This is used for faststart. */
+static void fill_nncell(
+ rspl *s,
+ int *co, /* Integer coords of cell to be filled */
+ int ix /* Index of cell to be filled */
+) {
+ int i;
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+ double cc[MXDO]; /* Cell center */
+ double rr = 0.0; /* Cell radius */
+ int **rpp, *rp;
+ int gno = s->g.no;
+ float *gp; /* Pointer to fwd grid points */
+ nncell_nf *nf; /* cloase and far distances corresponding to list */
+ double clfu = 1e38; /* closest furthest distance in list */
+
+ rpp = s->rev.nnrev + ix;
+ rp = *rpp;
+
+ /* Compute the center location and radius of the target cell */
+ for (f = 0; f < fdi; f++) {
+ cc[f] = s->rev.gw[f] * (co[f] + 0.5) + s->rev.gl[f];
+ rr += 0.25 * s->rev.gw[f] * s->rev.gw[f];
+ }
+ rr = sqrt(rr);
+//printf("~1 fill_nncell() cell ix %d, coord %d %d %d, cent %f %f %f, rad %f\n",
+//ix, co[0], co[1], co[2], cc[0], cc[1], cc[2], rr);
+//printf("~1 total of %d fwd cells\n",gno);
+
+ /* For all the forward cells: */
+ for (gp = s->g.a, i = 0; i < gno; gp += s->g.pss, i++) {
+ int ee;
+ int uil; /* One is under the ink limit */
+ double dn, df; /* Nearest and farthest distance of fwd cell values */
+
+ /* Skip cubes that are on the outside edge of the grid */
+ for (e = 0; e < di; e++) {
+ if(G_FL(gp, e) == 0) /* At the top edge */
+ break;
+ }
+ if (e < di) { /* Top edge - skip this cube */
+ continue;
+ }
+
+ /* Compute the closest and furthest distances of nodes of current cell */
+ dn = 1e38, df = 0.0;
+ for (uil = ee = 0; ee < (1 << di); ee++) { /* For all grid points in the cube */
+ double r;
+ float *gt = gp + s->g.fhi[ee]; /* Pointer to cube vertex */
+
+ if (!s->limiten || gt[-1] <= s->limitv)
+ uil = 1;
+
+ /* Update bounding box for this grid point */
+ for (r = 0.0, f = 0; f < fdi; f++) {
+ double tt = cc[f] - (double)gt[f];
+ r += tt * tt;
+ }
+//printf("~1 grid location %f %f %f rad %f\n",gt[0],gt[1],gt[2],sqrt(r));
+ if (r < dn)
+ dn = r;
+ if (r > df)
+ df = r;
+ }
+ /* Skip any fwd cells that are over the ink limit */
+ if (!uil)
+ continue;
+
+ dn = sqrt(dn) - rr;
+ df = sqrt(df) + rr;
+
+//printf("~1 checking cell %d, near %f, far %f\n",i,dn,df);
+
+ /* Skip any that have a closest distance larger that the lists */
+ /* closest furthest distance. */
+ if (dn > clfu) {
+//printf("~1 skipping cell %d, near %f, far %f clfu %f\n",i,dn,df,clfu);
+ continue;
+ }
+
+//printf("~1 adding cell %d\n",i);
+ if (rp == NULL) {
+ if ((nf = (nncell_nf *) rev_malloc(s, 6 * sizeof(nncell_nf))) == NULL)
+ error("rspl malloc failed - nncell_nf list");
+ INCSZ(s, 6 * sizeof(nncell_nf));
+ if ((rp = (int *) rev_malloc(s, 6 * sizeof(int))) == NULL)
+ error("rspl malloc failed - rev.grid entry");
+ INCSZ(s, 6 * sizeof(int));
+ *rpp = rp;
+ rp[0] = 6; /* Allocation */
+ rp[1] = 4; /* Next empty cell */
+ rp[2] = 1; /* Reference count */
+ rp[3] = i;
+ nf[3].n = dn;
+ nf[3].f = df;
+ rp[4] = -1;
+ } else {
+ int z = rp[1], ll = rp[0];
+ if (z >= (ll-1)) { /* Not enough space */
+ INCSZ(s, ll * sizeof(nncell_nf));
+ INCSZ(s, ll * sizeof(int));
+ ll *= 2;
+ if ((nf = (nncell_nf *) rev_realloc(s, nf, sizeof(nncell_nf) * ll)) == NULL)
+ error("rspl realloc failed - nncell_nf list");
+ if ((rp = (int *) rev_realloc(s, rp, sizeof(int) * ll)) == NULL)
+ error("rspl realloc failed - rev.grid entry");
+ *rpp = rp;
+ rp[0] = ll;
+ }
+ rp[z] = i;
+ nf[z].n = dn;
+ nf[z++].f = df;
+ rp[z] = -1;
+ rp[1] = z;
+ }
+
+ if (df < clfu)
+ clfu = df;
+ }
+//printf("~1 Current list is:\n");
+//for (e = 3; rp[e] != -1; e++)
+//printf(" %d: Cell %d near %f far %f\n",e,rp[e],nf[e].n,nf[e].f);
+
+ /* Now filter out any cells that have a closest point that is further than */
+ /* closest furthest point */
+ {
+ int z, w, ll = rp[0];
+
+ /* For all the cells in the current list: */
+ for (w = z = 3; rp[z] != -1; z++) {
+
+ /* If the new cell nearest is greater than the existing cell closest, */
+ /* then don't omit existing cell from the list. */
+ if (clfu >= nf[z].n) {
+ rp[w] = rp[z];
+ nf[w].n = nf[z].n;
+ nf[w].f = nf[z].f;
+ w++;
+ }
+//else printf("~1 deleting cell %d because %f >= %f\n",rp[z],clfu, nf[z].f);
+ }
+ rp[w] = rp[z];
+ }
+//printf("~1 Current list is:\n");
+//for (e = 3; rp[e] != -1; e++)
+//printf(" %d: Cell %d near %f far %f\n",e,rp[e],nf[e].n,nf[e].f);
+ free(nf);
+//printf("~1 Done\n");
+}
+
+/* Return the pointer to the list of nearest fwd cells given */
+/* the target output values. The pointer will be to the first */
+/* index in the list (ie. list address + 3) */
+/* Return NULL if none in list (out of gamut). */
+static int *
+calc_fwd_nn_cell_list(
+ rspl *s, /* this */
+ double *v /* Output values */
+) {
+ int f, fdi = s->fdi, ix;
+ int **rpp;
+ int rgres_1 = s->rev.res - 1;
+ int mi[MXDO];
+
+ if (s->rev.rev_valid == 0)
+ init_revaccell(s);
+
+ for (ix = 0, f = 0; f < fdi; f++) {
+ double t = (v[f] - s->rev.gl[f])/s->rev.gw[f];
+ mi[f] = (int)floor(t); /* Grid coordinate */
+ if (mi[f] < 0) /* Clip to reverse range, so we always return a result */
+ mi[f] = 0;
+ else if (mi[f] > rgres_1)
+ mi[f] = rgres_1;
+ ix += mi[f] * s->rev.coi[f]; /* Accumulate reverse grid index */
+ }
+ rpp = s->rev.nnrev + ix;
+ if (*rpp == NULL) {
+ if (s->rev.fastsetup)
+ fill_nncell(s, mi, ix);
+ if (*rpp == NULL)
+ rpp = s->rev.rev + ix; /* fall back to in-gamut lookup */
+ }
+ if (*rpp == NULL)
+ return NULL;
+ return (*rpp) + 3;
+}
+
+/* =================================================== */
+/* The cell and simplex solver top level routines */
+
+static int add_lu_svd(simplex *x);
+static int add_locus(schbase *b, simplex *x);
+static int add_auxil_lu_svd(schbase *b, simplex *x);
+static int within_simplex(simplex *x, double *p);
+static void simplex_to_abs(simplex *x, double *in, double *out);
+
+static int auxil_solve(schbase *b, simplex *x, double *xp);
+
+/* ---------------------- */
+/* Exact search functions */
+/* Return non-zero if cell is acceptable */
+static int exact_setsort(schbase *b, cell *c) {
+ rspl *s = b->s;
+ int f, fdi = s->fdi;
+ double ss;
+
+ DBG(("Reverse exact search, evaluate and set sort key on cell\n"));
+
+ /* Check that the target lies within the cell bounding sphere */
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = c->bcent[f] - b->v[f];
+ ss += tt * tt;
+ }
+ if (ss > c->bradsq) {
+ DBG(("Cell rejected - %s outside sphere c %s rad %f\n",icmPdv(fdi,b->v),icmPdv(fdi,c->bcent),sqrt(c->bradsq)));
+ return 0;
+ }
+
+ if (s->limiten != 0 && c->limmin > s->limitv) {
+ DBG(("Cell is rejected - ink limit, min = %f, limit = %f\n",c->limmin,s->limitv));
+ return 0;
+ }
+
+ /* Sort can't be used, because we return all solutions */
+ c->sort = 0.0;
+
+ DBG(("Cell is accepted\n"));
+
+ return 1;
+}
+
+/* Compute a solution for a given sub-simplex (if there is one) */
+/* Return 1 if search should be aborted */
+static int exact_compute(schbase *b, simplex *x) {
+ rspl *s = b->s;
+ int e, di = s->di, sdi = x->sdi;
+ int f, fdi = s->fdi;
+ int i;
+ datai xp; /* solution in simplex relative coord order */
+ datai p; /* absolute solution */
+ int wsrv; /* Within simplex return value */
+
+ DBG(("\nExact: computing possible solution\n"));
+
+#ifdef DEBUG
+ /* Sanity check */
+ if (sdi != fdi || sdi != di || x->efdi != fdi) {
+ printf("di = %d, fdi = %d\n",di,fdi);
+ printf("sdi = %d, efdi = %d\n",sdi,x->efdi);
+ error("rspl exact reverse interp called with sdi != fdi, sdi != di, efdi != fdi");
+ /* !!! could switch to SVD solution if di != fdi ?? !!! */
+ }
+#endif
+
+ /* This may not be worth it here since it may not filter out */
+ /* many more simplexes than the cube check did. */
+ /* This is due to full dimension simplexes all sharing the main */
+ /* diagonal axis. */
+
+ /* Check that the target lies within the simplex bounding cube */
+ for (f = 0; f < fdi; f++) {
+ if (b->v[f] < x->min[f] || b->v[f] > x->max[f]) {
+ DBG(("Simplex is rejected - bounding cube\n"));
+ return 0;
+ }
+ }
+
+ /* Create the LU decomp needed to exactly solve */
+ if (add_lu_svd(x)) {
+ DBG(("LU decomp was singular, skip simplex\n"));
+ return 0;
+ }
+
+ /* Init the RHS B[] vector (note di == fdi) */
+ for (f = 0; f < fdi; f++) {
+ xp[f] = b->v[f] - x->v[di][f];
+ }
+
+ /* Compute the solution (in simplex space) */
+ lu_backsub(x->d_u, sdi, (int *)x->d_w, xp);
+
+ /* Check that the solution is within the simplex */
+ if ((wsrv = within_simplex(x, xp)) == 0) {
+ DBG(("Solution rejected because not in simplex\n"));
+ return 0;
+ }
+
+ /* Convert solution from simplex relative to absolute space */
+ simplex_to_abs(x, p, xp);
+
+ /* Check if a very similiar input solution has been found before */
+ for (i = 0; i < b->nsoln; i++) {
+ double tt;
+ for (e = 0; e < di; e++) {
+ tt = b->cpp[i].p[e] - p[e];
+ if (fabs(tt) > (2 * EPS))
+ break; /* Mismatch */
+ }
+ if (e >= di) /* Found good match */
+ break;
+ }
+
+ /* Probably alias caused by solution lying close to a simplex boundary */
+ if (i < b->nsoln) {
+ DBG(("Another solution has been found before - index %d\n",i));
+ return 0; /* Skip this, since betters been found before */
+ }
+
+ /* Check we haven't overflowed space */
+ if (i >= b->mxsoln) {
+ DBG(("Run out of space for new solution\n"));
+ return 1; /* Abort */
+ }
+
+ DBG(("######## Accepting new solution\n"));
+
+ /* Put solution in place */
+ for (e = 0; e < di; e++)
+ b->cpp[i].p[e] = p[e];
+ for (f = 0; f < fdi; f++)
+ b->cpp[i].v[f] = b->v[f]; /* Assumed to be an exact solution */
+ if (i == b->nsoln)
+ b->nsoln++;
+ if (wsrv == 2) /* Is above (disabled) ink limit */
+ b->iclip = 1;
+ return 0;
+}
+
+/* -------------------------- */
+/* Auxiliary search functions */
+static int auxil_setsort(schbase *b, cell *c) {
+ rspl *s = b->s;
+ int f, fdi = b->s->fdi;
+ int ee, ixc = b->ixc;
+ double ss, sort, nabove;
+
+ DBG(("Reverse auxiliary search, evaluate and set sort key on cell\n"));
+
+ if (b->s->di <= fdi) { /* Assert */
+ error("rspl auxiliary reverse interp called with di <= fdi (%d %d)", b->s->di, fdi);
+ }
+
+ /* Check that the target lies within the cell bounding sphere */
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = c->bcent[f] - b->v[f];
+ ss += tt * tt;
+ }
+ if (ss > c->bradsq) {
+ DBG(("Cell rejected - %s outside sphere c %s rad %f\n",icmPdv(fdi,b->v),icmPdv(fdi,c->bcent),sqrt(c->bradsq)));
+ return 0;
+ }
+
+ if (s->limiten != 0 && c->limmin > s->limitv) {
+ DBG(("Cell is rejected - ink limit, min = %f, limit = %f\n",c->limmin,s->limitv));
+ return 0;
+ }
+
+ /* Check if this cell could possible improve b->idist */
+ /* and compute sort key as the distance to auxilliary target */
+ /* (We may have a non INF_DIST idist before commencing the */
+ /* search if we already know that the auxiliary target is */
+ /* within gamut - the usual usage case!) */
+ for (sort = 0.0, nabove = ee = 0; ee < b->naux; ee++) {
+ int ei = b->auxi[ee];
+ double tt = (c->p[0][ei] + c->p[ixc][ei]) - b->av[ei];
+ sort += tt * tt;
+ if (c->p[ixc][ei] >= (b->av[ei] - EPS)) /* Could be above */
+ nabove++;
+ }
+
+ if (b->flags & RSPL_MAXAUX && nabove < b->iabove) {
+ DBG(("Doesn't contain solution that has as many aux above auxiliary goal\n"));
+ return 0;
+ }
+ if (!(b->flags & RSPL_MAXAUX) || nabove == b->iabove) {
+ for (ee = 0; ee < b->naux; ee++) {
+ int ei = b->auxi[ee];
+ if (c->p[0][ei] >= (b->av[ei] + b->idist)
+ || c->p[ixc][ei] <= (b->av[ei] - b->idist)) {
+ DBG(("Doesn't contain solution that will be closer to auxiliary goal\n"));
+ return 0;
+ }
+ }
+ }
+ c->sort = sort + 0.01 * ss;
+
+ if (c->ix == b->pauxcell)
+ c->sort = -1.0; /* Put previous calls solution cell at top of sort list */
+
+ DBG(("Cell is accepted\n"));
+ return 1;
+}
+
+/* Re-check whether it's worth searching cell */
+static int auxil_check(schbase *b, cell *c) {
+ int ee, ixc = b->ixc, nabove;
+
+ DBG(("Reverse auxiliary search, re-check cell\n"));
+
+ /* Check if this cell could possible improve b->idist */
+ /* and compute sort key as the distance to auxilliary target */
+
+ for (nabove = ee = 0; ee < b->naux; ee++) {
+ int ei = b->auxi[ee];
+ if (c->p[ixc][ei] >= (b->av[ei] - EPS)) /* Could be above */
+ nabove++;
+ }
+
+ if (b->flags & RSPL_MAXAUX && nabove < b->iabove) {
+ DBG(("Doesn't contain solution that has as many aux above auxiliary goal\n"));
+ return 0;
+ }
+ if (!(b->flags & RSPL_MAXAUX) || nabove == b->iabove) {
+ for (ee = 0; ee < b->naux; ee++) {
+ int ei = b->auxi[ee];
+ if (c->p[0][ei] >= (b->av[ei] + b->idist)
+ || c->p[ixc][ei] <= (b->av[ei] - b->idist)) {
+ DBG(("Doesn't contain solution that will be closer to auxiliary goal\n"));
+ return 0;
+ }
+ }
+ }
+ DBG(("Cell is still ok\n"));
+ return 1;
+}
+
+/* Compute a solution for a given simplex (if there is one) */
+/* Return 1 if search should be aborted */
+static int auxil_compute(schbase *b, simplex *x) {
+ rspl *s = b->s;
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+ datai xp; /* solution in simplex relative coord order */
+ datai p; /* absolute solution */
+ double idist; /* Auxiliary input distance */
+ int wsrv; /* Within simplex return value */
+ int nabove; /* Number above aux target */
+
+ DBG(("\nAuxil: computing possible solution\n"));
+
+#ifdef DEBUG
+ {
+ unsigned int sum = 0;
+ for (f = 0; f <= x->sdi; f++)
+ sum += x->vix[f];
+ printf("Simplex of cell ix %d, sum 0x%x, sdi = %d, efdi = %d\n",x->ix, sum, x->sdi, x->efdi);
+ printf("Target val %s\n",icmPdv(fdi, b->v));
+ for (f = 0; f <= x->sdi; f++) {
+ int ix = x->vix[f], i;
+ float *fcb = s->g.a + ix * s->g.pss; /* Pointer to base float of fwd cell */
+ printf("Simplex vtx %d [cell ix %d] val %s\n",f,ix,icmPfv(fdi, fcb));
+ }
+ }
+#endif
+
+ /* Check that the target lies within the simplex bounding cube */
+ for (f = 0; f < fdi; f++) {
+ if (b->v[f] < x->min[f] || b->v[f] > x->max[f]) {
+ DBG(("Simplex is rejected - bounding cube\n"));
+ return 0;
+ }
+ }
+
+ /* Check if this cell could possible improve b->idist */
+ for (nabove = e = 0; e < b->naux; e++) {
+ int ei = b->auxi[e]; /* pmin/max[] is indexed in input space */
+ if (x->pmax[ei] >= (b->av[ei] - EPS)) /* Could be above */
+ nabove++;
+ }
+ if ((b->flags & RSPL_MAXAUX) && nabove < b->iabove) {
+ DBG(("Simplex doesn't contain solution that has as many aux above auxiliary goal\n"));
+ return 0;
+ }
+ if (!(b->flags & RSPL_MAXAUX) || nabove == b->iabove) {
+ for (nabove = e = 0; e < b->naux; e++) {
+ int ei = b->auxi[e]; /* pmin/max[] is indexed in input space */
+ if (x->pmin[ei] >= (b->av[ei] + b->idist)
+ || x->pmax[ei] <= (b->av[ei] - b->idist)) {
+ DBG(("Simplex doesn't contain solution that will be closer to auxiliary goal\n"));
+ return 0;
+ }
+ }
+ }
+
+//printf("~~ About to create svd decomp\n");
+ /* Create the SVD or LU decomp needed to compute solution or locus */
+ if (add_lu_svd(x)) {
+ DBG(("SVD decomp failed, skip simplex\n"));
+ return 0;
+ }
+
+//printf("~~ About to solve locus for aux target\n");
+ /* Now solve for locus parameter that minimises */
+ /* distance to auxliary target. */
+ if ((wsrv = auxil_solve(b, x, xp)) == 0) {
+ DBG(("Target auxiliary along locus is outside simplex,\n"));
+ DBG(("or computation failed, skip simplex\n"));
+ return 0;
+ }
+
+//printf("~~ About to convert solution to absolute space\n");
+ /* Convert solution from simplex relative to absolute space */
+ simplex_to_abs(x, p, xp);
+
+ DBG(("Got solution at %s\n", icmPdv(di,p)));
+
+//printf("~~ soln = %f %f %f %f\n",p[0],p[1],p[2],p[3]);
+//printf("~~ About to compute auxil distance\n");
+ /* Compute distance to auxiliary target */
+ for (idist = 0.0, nabove = e = 0; e < b->naux; e++) {
+ int ei = b->auxi[e];
+ double tt = b->av[ei] - p[ei];
+ idist += tt * tt;
+ if (p[ei] >= (b->av[ei] - EPS))
+ nabove++;
+ }
+ idist = sqrt(idist);
+//printf("~1 idist %f, nabove %d\n",idist, nabove);
+//printf("~1 best idist %f, best iabove %d\n",b->idist, b->iabove);
+
+ /* We want the smallest error from auxiliary target */
+ if (b->flags & RSPL_MAXAUX) {
+ if (nabove < b->iabove || (nabove == b->iabove && idist >= b->idist)) {
+ DBG(("nsoln %d, nabove %d, iabove %d, idist = %f, better solution has been found before\n",b->nsoln, nabove, b->iabove, idist));
+ return 0;
+ }
+ } else {
+ if (idist >= b->idist) { /* Equal or worse auxiliary solution */
+ DBG(("nsoln %d, idist = %f, better solution has been found before\n",b->nsoln,idist));
+ return 0;
+ }
+ }
+
+ /* Solution is accepted */
+ DBG(("######## Accepting new solution with nabove %d <= iabove %d and idist %f <= %f\n",nabove,b->iabove,idist,b->idist));
+ for (e = 0; e < di; e++)
+ b->cpp[0].p[e] = p[e];
+ for (f = 0; f < fdi; f++)
+ b->cpp[0].v[f] = b->v[f]; /* Assumed to be an exact solution */
+ b->idist = idist;
+ b->iabove = nabove;
+ b->nsoln = 1;
+ b->pauxcell = x->ix;
+ if (wsrv == 2) /* Is above (disabled) ink limit */
+ b->iclip = 1;
+
+ return 0;
+}
+
+/* ------------------------------------ */
+/* Locus range search functions */
+
+static int locus_setsort(schbase *b, cell *c) {
+ rspl *s = b->s;
+ int f, fdi = s->fdi;
+ int lxi = b->lxi; /* Auxiliary we are finding min/max of */
+ int ixc = b->ixc;
+ double sort, ss;
+
+ DBG(("Reverse locus evaluate and set sort key on cell\n"));
+
+#ifdef DEBUG
+ if (b->s->di <= fdi) { /* Assert ~1 */
+ error("rspl auxiliary locus interp called with di <= fdi");
+ }
+#endif /* DEBUG */
+
+ /* Check that the target lies within the cell bounding sphere */
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = c->bcent[f] - b->v[f];
+ ss += tt * tt;
+ }
+ if (ss > c->bradsq) {
+ DBG(("Cell rejected - %s outside sphere c %s rad %f\n",icmPdv(fdi,b->v),icmPdv(fdi,c->bcent),sqrt(c->bradsq)));
+ return 0;
+ }
+
+ if (s->limiten != 0 && c->limmin > s->limitv) {
+ DBG(("Cell is rejected - ink limit, min = %f, limit = %f\n",c->limmin,s->limitv));
+ return 0;
+ }
+
+ /* Check if this cell could possible improve the locus min/max */
+ if (b->asegs == 0) { /* If we aren't find all segments of the locus */
+ if (c->p[0][lxi] >= b->min && c->p[ixc][lxi] <= b->max ) {
+ DBG(("Doesn't contain solution that will expand the locus\n"));
+ return 0;
+ }
+ }
+
+ /* Compute sort index from average of auxiliary values */
+ sort = (c->p[0][b->lxi] + c->p[ixc][b->lxi]);
+
+ c->sort = sort + 0.01 * ss;
+
+ DBG(("Cell is accepted\n"));
+ return 1;
+}
+
+/* Re-check whether it's worth searching simplexes */
+static int locus_check(schbase *b, cell *c) {
+ int lxi = b->lxi; /* Auxiliary we are finding min/max of */
+ int ixc = b->ixc;
+
+ DBG(("Reverse locus re-check\n"));
+
+ /* Check if this cell could possible improve the locus min/max */
+ if (b->asegs == 0) { /* If we aren't find all segments of the locus */
+ if (c->p[0][lxi] >= b->min && c->p[ixc][lxi] <= b->max ) {
+ DBG(("Doesn't contain solution that will expand the locus\n"));
+ return 0;
+ }
+ }
+
+ DBG(("Cell is still ok\n"));
+ return 1;
+}
+
+static int auxil_locus(schbase *b, simplex *x);
+
+/* We expect to be given a sub-simplex with no DOF, to give an exact solution */
+static int locus_compute(schbase *b, simplex *x) {
+ rspl *s = b->s;
+ int f, fdi = s->fdi;
+ int lxi = b->lxi; /* Auxiliary we are finding min/max of */
+
+ DBG(("\nLocus: computing possible solution\n"));
+
+#ifdef DEBUG
+ {
+ unsigned int sum = 0;
+ for (f = 0; f <= x->sdi; f++)
+ sum += x->vix[f];
+ printf("Simplex of cell ix %d, sum 0x%x, sdi = %d, efdi = %d\n",x->ix, sum, x->sdi, x->efdi);
+ printf("Target val %s\n",icmPdv(fdi, b->v));
+ for (f = 0; f <= x->sdi; f++) {
+ int ix = x->vix[f], i;
+ float *fcb = s->g.a + ix * s->g.pss; /* Pointer to base float of fwd cell */
+ double v[MXDO];
+ printf("Simplex vtx %d [cell ix %d] val %s\n",f,ix,icmPfv(fdi, fcb));
+ }
+ }
+#endif
+
+ /* Check that the target lies within the simplex bounding cube */
+ for (f = 0; f < fdi; f++) {
+ if (b->v[f] < x->min[f] || b->v[f] > x->max[f]) {
+ DBG(("Simplex is rejected - bounding cube\n"));
+ return 0;
+ }
+ }
+
+ /* Check if simplex could possible improve the locus min/max */
+ if (b->asegs == 0) { /* If we aren't find all segments of the locus */
+ if (x->pmin[lxi] >= b->min && x->pmax[lxi] <= b->max ) {
+ DBG(("Simplex doesn't contain solution that will expand the locus\n"));
+ return 0;
+ }
+ }
+
+//printf("~~ About to create svd decomp\n");
+ /* Create the SVD decomp needed to compute solution extreme points */
+ if (add_lu_svd(x)) {
+ DBG(("SVD decomp failed, skip simplex\n"));
+ return 0;
+ }
+
+//printf("~~ About to solve locus for aux extremes\n");
+ /* Now solve for locus parameter that are at the extremes */
+ /* of the axiliary we are interested in. */
+ if (!auxil_locus(b, x)) {
+ DBG(("Target auxiliary is outside simplex,\n"));
+ DBG(("or computation failed, skip simplex\n"));
+ return 0;
+ }
+
+ return 0;
+}
+
+/* ------------------- */
+/* Vector clipping search functions */
+static int clipv_setsort(schbase *b, cell *c) {
+ rspl *s = b->s;
+ int f, fdi = s->fdi;
+ double ss, dp;
+
+ DBG(("Reverse clipping search evaluate cell\n"));
+
+//printf("~~sphere center = %f %f %f, radius %f\n",c->bcent[0],c->bcent[1],c->bcent[2],sqrt(c->bradsq));
+ /* Check if the clipping line intersects the bounding sphere */
+ /* First compute dot product cdir . (bcent - v) */
+ /* == distance to center of sphere in direction of clip vector */
+ for (dp = 0.0, f = 0; f < fdi; f++) {
+ dp += b->ncdir[f] * (c->bcent[f] - b->v[f]);
+ }
+
+ if (s->limiten != 0 && c->limmin > s->limitv) {
+ DBG(("Cell is rejected - ink limit, min = %f, limit = %f\n",c->limmin,s->limitv));
+ return 0;
+ }
+
+//printf("~~ dot product = %f\n",dp);
+ /* Now compute closest distance to sphere center */
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = b->v[f] + dp * b->ncdir[f] - c->bcent[f];
+ ss += tt * tt;
+ }
+
+//printf("~~ distance to sphere center = %f\n",sqrt(ss));
+ if (ss > c->bradsq) {
+ DBG(("Cell is rejected - wrong direction or bounding sphere\n"));
+ return 0;
+ }
+ c->sort = dp; /* May be -ve if beyond clip target point ? */
+
+ DBG(("Cell is accepted\n"));
+ return 1;
+}
+
+/* Clipping check functions */
+/* Note that we don't bother with this check in setsort(), */
+/* because we assume that nothing will set a small cdist */
+/* before the search commences (unlike auxil). */
+/* Note that line search loop exits on finding any solution. */
+static int clipv_check(schbase *b, cell *c) {
+
+ DBG(("Reverse clipping re-check\n"));
+
+ if (b->cdist < INF_DIST) { /* If some clip solution has been found */
+ int f, fdi = b->s->fdi;
+ double dist;
+ /* Compute a conservative "best possible solution clip distance" */
+ for (dist = 0.0, f = 0; f < fdi ; f++) {
+ double tt = (c->bcent[f] - b->v[f]);
+ dist += tt * tt;
+ }
+ dist = sqrt(dist); /* Target distance to bounding */
+
+ if (dist >= (c->brad + b->cdist)) { /* Equal or worse clip solution */
+ DBG(("Cell best possible solution worse than current\n"));
+ return 0;
+ }
+ }
+
+ DBG(("Cell is still ok\n"));
+ return 1;
+}
+
+static int vnearest_clip_solve(schbase *b, simplex *x, double *xp, double *xv, double *err);
+
+/* Compute a clip solution */
+static int clipv_compute(schbase *b, simplex *x) {
+ rspl *s = b->s;
+ int f, fdi = s->fdi;
+ datai p; /* Input space solution */
+ datao v; /* Output space solution */
+ double err; /* output error of solution */
+ int wsrv; /* Within simplex return value */
+
+ DBG(("Clips: computing possible solution\n"));
+
+ /* Compute a solution value */
+ if ((wsrv = vnearest_clip_solve(b, x, p, v, &err)) == 0) {
+ DBG(("Doesn't contain a solution\n"));
+ return 0;
+ }
+
+ /* We want the smallest clip error */
+ /* (Should we reject points in -ve vector direction ??) */
+ if (err >= b->cdist) { /* Equal or worse clip solution */
+ DBG(("better solution has been found before\n"));
+ return 0;
+ }
+
+ simplex_to_abs(x, b->cpp[0].p, p); /* Convert to abs. space & copy */
+
+ DBG(("######## Accepting new clipv solution with error %f\n",err));
+#ifdef DEBUG
+ if (s->limiten != 0) {
+ DBG(("######## Ink value = %f, limit %f\n",get_limitv(b, x->ix, NULL, b->cpp[0].p), s->limitv));
+ }
+#endif
+
+ /* Put solution in place */
+ for (f = 0; f < fdi; f++)
+ b->cpp[0].v[f] = v[f];
+ b->cdist = err;
+ b->nsoln = 1;
+ if (wsrv == 2) /* Is above (disabled) ink limit */
+ b->iclip = 1;
+
+ return 0;
+}
+
+/* ------------------- */
+/* Nearest clipping search functions */
+static int clipn_setsort(schbase *b, cell *c) {
+ rspl *s = b->s;
+ int f, fdi = s->fdi;
+ double ss;
+
+ DBG(("Reverse nearest clipping search evaluate cell\n"));
+
+ /* Compute a conservative "best possible solution clip distance" */
+ for (ss = 0.0, f = 0; f < fdi ; f++) {
+ double tt = (c->bcent[f] - b->v[f]);
+ ss += tt * tt;
+ }
+ ss = sqrt(ss); /* Target distance to bounding sphere */
+ ss -= c->brad;
+ if (ss < 0.0)
+ ss = 0.0;
+
+ /* Check that the cell could possibly improve the solution */
+ if (b->cdist < INF_DIST) { /* If some clip solution has been found */
+ if (ss >= b->cdist) { /* Equal or worse clip solution */
+ DBG(("Cell best possible solution worse than current\n"));
+ return 0;
+ }
+ }
+
+ if (s->limiten != 0 && c->limmin > s->limitv) {
+ DBG(("Cell is rejected - ink limit, min = %f, limit = %f\n",c->limmin,s->limitv));
+ return 0;
+ }
+
+ c->sort = ss; /* May be -ve if beyond clip target point ? */
+
+ DBG(("Cell is accepted\n"));
+ return 1;
+}
+
+/* Clipping check functions */
+static int clipn_check(schbase *b, cell *c) {
+
+ DBG(("Reverse nearest clipping re-check\n"));
+
+ if (b->cdist < INF_DIST) { /* If some clip solution has been found */
+ /* re-use sort value, best possible distance to solution */
+ if (c->sort >= b->cdist) { /* Equal or worse clip solution */
+ DBG(("Cell best possible solution worse than current\n"));
+ return 0;
+ }
+ }
+
+ DBG(("Cell is still ok\n"));
+ return 1;
+}
+
+static int nnearest_clip_solve(schbase *b, simplex *x, double *xp, double *xv, double *err);
+
+/* Compute a clip solution */
+static int clipn_compute(schbase *b, simplex *x) {
+ rspl *s = b->s;
+ int f, fdi = s->fdi;
+ datai p; /* Input space solution */
+ datao v; /* Output space solution */
+ double err; /* output error of solution */
+ int wsrv; /* Within simplex return value */
+
+ DBG(("Clipn: computing possible solution simplex %d, sdi = %d, efdi = %d\n",x->si,x->sdi,x->efdi));
+
+ /* Compute a solution value */
+ if ((wsrv = nnearest_clip_solve(b, x, p, v, &err)) == 0) {
+ DBG(("Doesn't contain a solution\n"));
+ return 0;
+ }
+
+ /* We want the smallest clip error */
+ if (err >= b->cdist) { /* Equal or worse clip solution */
+ DBG(("better solution has been found before\n"));
+ return 0;
+ }
+
+ DBG(("######## Accepting new clipn solution with error %f\n",err));
+
+ simplex_to_abs(x, b->cpp[0].p, p); /* Convert to abs. space & copy */
+
+ /* Put solution in place */
+ for (f = 0; f < fdi; f++)
+ b->cpp[0].v[f] = v[f];
+ b->cdist = err;
+ b->nsoln = 1;
+ if (wsrv == 2) /* Is above (disabled) ink limit */
+ b->iclip = 1;
+
+ return 0;
+}
+
+/* -------------------------------------------------------- */
+/* Cell/simplex solver middle level code */
+
+/* Find the point on this sub-simplexes solution locus that is */
+/* closest to the target auxiliary values, and return it in xp[] */
+/* Return zero if this point canot be calculated, */
+/* or it lies outside the simplex. */
+/* Return 1 normally, and 2 if the solution would be over the ink limit */
+static int
+auxil_solve(
+schbase *b,
+simplex *x,
+double *xp /* Return solution xp[sdi] */
+) {
+ rspl *s = b->s;
+ int ee, e, di = s->di, sdi = x->sdi;
+ int f, efdi = x->efdi;
+ int dof = sdi-efdi; /* Degree of freedom of simplex locus */
+ int *icomb = x->psxi->icomb; /* abs -> simplex coordinate translation */
+ double auxt[MXRI]; /* Simplex relative auxiliary targets */
+ double bb[MXRI];
+ int wsrv; /* Within simplex return value */
+
+ DBG(("axuil_solve called\n"));
+
+ if (dof < 0)
+ error("Error - auxil_solve got sdi < efdi (%d < %d) - don't know how to handle this",sdi, efdi);
+
+ /* If there is no locus, compute an exact solution */
+ if (dof == 0) {
+ DBG(("axuil_solve dof = zero\n"));
+
+ /* Init the RHS B[] vector (note sdi == efdi) */
+ for (f = 0; f < efdi; f++) {
+ xp[f] = b->v[f] - x->v[sdi][f];
+ }
+
+ /* Compute the solution (in simplex space) */
+ lu_backsub(x->d_u, sdi, (int *)x->d_w, xp);
+
+ if ((wsrv = within_simplex(x, xp)) != 0) {
+ DBG(("Got solution at %s\n", icmPdv(sdi,xp)));
+ return wsrv; /* OK, got solution */
+ }
+
+ DBG(("No solution (not within simplex)\n"));
+ return 0;
+ }
+
+ /* There is a locus, so find solution nearest auxiliaries */
+
+ /* Compute locus for target function values (if sdi > efdi) */
+ if (add_locus(b, x)) {
+ DBG(("Locus computation failed, skip simplex\n"));
+ return 0;
+ }
+
+ /* Convert aux targets from absolute space to simplex relative */
+ for (e = 0; e < di; e++) { /* For abs coords */
+ int ei = icomb[e]; /* Simplex coord */
+
+ if (ei >= 0 && b->auxm[e] != 0) {
+ auxt[ei] = (b->av[e] - x->p0[e])/s->g.w[e]; /* Only sets those needed */
+ }
+ }
+
+ if (dof == 1 && b->naux == 1) { /* Special case, because it's common and easy! */
+ int ei = icomb[b->auxi[0]]; /* Simplex relative auxiliary index */
+ double tt;
+
+ DBG(("axuil_solve dof = naux = 1\n"));
+ if (ei < 0)
+ return 0; /* Not going to find solution */
+ if ((tt = x->lo_l[ei][0]) == 0.0)
+ return 0;
+ tt = (auxt[ei] - x->lo_bd[ei])/tt; /* Parameter solution for target auxiliary */
+
+ /* Back substitute parameter */
+ for (e = 0; e < sdi; e++) {
+ xp[e] = x->lo_bd[e] + tt * x->lo_l[e][0];
+ }
+ if ((wsrv = within_simplex(x, xp)) != 0) {
+ DBG(("Got solution %s\n",icmPdv(di,xp)));
+ return wsrv; /* OK, got solution */
+ }
+ DBG(("No solution (not within simplex)\n"));
+ return 0;
+ }
+
+ /* Compute the locus decompositions needed (info #5) */
+ if (add_auxil_lu_svd(b, x)) { /* Will set x->naux */
+ DBG(("LU/SVD decomp failed\n"));
+ return 0;
+ }
+
+ /* Setup B[], equation RHS */
+ for (e = ee = 0; ee < b->naux; ee++) {
+ int ei = icomb[b->auxi[ee]]; /* Simplex relative auxiliary index */
+ if (ei >= 0) /* Usable auxiliary on this sub simplex */
+ bb[e++] = auxt[ei] - x->lo_bd[ei];
+ }
+ if (e != x->naux) /* Assert */
+ error("Internal error - auxil_solve got mismatching number of auxiliaries");
+
+ if (x->naux == dof) { /* Use LU decomp to solve */
+ DBG(("axuil_solve using LU\n"));
+ lu_backsub(x->ax_u, dof, (int *)x->ax_w, bb);
+
+ } else if (x->naux > 0) { /* Use SVD to solve least squares */
+ DBG(("axuil_solve using SVD\n"));
+ svdbacksub(x->ax_u, x->ax_w, x->ax_v, bb, bb, x->naux, dof);
+
+ } else { /* x->naux == 0 */
+ DBG(("axuil_solve naux = 0\n"));
+ for (f = 0; f < dof; f++)
+ bb[f] = 0.0; /* Use base solution ?? */
+ }
+
+ /* Now back substitute the locus parameters */
+ /* to calculate the solution point (in simplex space) */
+ for (e = 0; e < sdi; e++) {
+ double tt;
+ for (tt = 0.0, f = 0; f < dof; f++) {
+ tt += bb[f] * x->lo_l[e][f];
+ }
+ xp[e] = x->lo_bd[e] + tt;
+ }
+
+ if ((wsrv = within_simplex(x, xp)) != 0) {
+ DBG(("Got solution %s\n",icmPdv(di,xp)));
+ return wsrv; /* OK, got solution */
+ }
+ DBG(("No solution (not within simplex)\n"));
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute the min/max values for the current auxiliary of interest. */
+/* Return zero if this point canot be calculated, */
+/* or it lies outside the simplex. */
+/* Return 1 normally, 2 if it would be outside the simplex if limting was enabled */
+/* We expect to get a sub-simplex that will give an exact solution. */
+static int
+auxil_locus(
+schbase *b,
+simplex *x
+) {
+ rspl *s = b->s;
+ int sdi = x->sdi;
+ int f, efdi = x->efdi;
+ double pp[MXRI];
+ int wsrv; /* Within simplex return value */
+
+ DBG(("axuil_locus called\n"));
+
+ if (sdi != efdi)
+ warning("Internal error - auxil_locus got sdi != efdi (%d < %d)",sdi, efdi);
+
+ /* Init the RHS B[] vector (note sdi == efdi) */
+ for (f = 0; f < efdi; f++) {
+ pp[f] = b->v[f] - x->v[sdi][f];
+ }
+
+ /* Compute the solution (in simplex space) */
+ lu_backsub(x->d_u, sdi, (int *)x->d_w, pp);
+
+ /* Check that the solution is within the simplex */
+ if ((wsrv = within_simplex(x, pp)) != 0) {
+ double xval;
+ int lxi = b->lxi; /* Auxiliary we are finding min/max of (Abs space) */
+ int xlxi = x->psxi->icomb[lxi]; /* Auxiliary we are finding min/max of (simplex space) */
+
+ DBG(("Got locus solution within simplex\n"));
+
+ /* Compute auxiliary value for this solution (absolute space) */
+ xval = x->p0[lxi];
+ if (xlxi >= 0) /* Simplex param value */
+ xval += s->g.w[lxi] * pp[xlxi];
+ else if (xlxi == -2) /* 1 value */
+ xval += s->g.w[lxi];
+ /* Else 0 value */
+
+ if (b->asegs != 0) { /* Tracking auxiliary segments */
+ if (b->axisln >= b->axislz) { /* Need some more space in list */
+ if (b->axislz == 0) {
+ b->axislz = 10;
+ if ((b->axisl = (axisec *)rev_malloc(s, b->axislz * sizeof(axisec))) == NULL)
+ error("rev: malloc failed - Auxiliary intersect list size %d",b->axislz);
+ INCSZ(b->s, b->axislz * sizeof(axisec));
+ } else {
+ INCSZ(b->s, b->axislz * sizeof(axisec));
+ b->axislz *= 2;
+ if ((b->axisl = (axisec *)rev_realloc(s, b->axisl, b->axislz * sizeof(axisec)))
+ == NULL)
+ error("rev: realloc failed - Auxiliary intersect list size %d",b->axislz);
+ }
+ }
+ b->axisl[b->axisln].xval = xval;
+ b->axisl[b->axisln].nv = x->sdi + 1;
+ for (f = 0; f <= x->sdi; f++) {
+ b->axisl[b->axisln].vix[f] = x->vix[f];
+ }
+ b->axisln++;
+ }
+
+#ifdef DEBUG
+ if (xval >= b->min && xval <= b->max)
+ DBG(("auxil_locus: solution %f doesn't improve on min %f, max %f\n",xval,b->min,b->max));
+#endif
+ /* If this solution is expands the min or max, save it */
+ if (xval < b->min) {
+ DBG(("######## Improving minimum to %f\n",xval));
+ b->min = xval;
+ b->plmincell = x->ix;
+ }
+ if (xval > b->max) {
+ DBG(("######## Improving maximum to %f\n",xval));
+ b->max = xval;
+ b->plmaxcell = x->ix;
+ }
+ } else {
+ DBG(("Solution wasn't within the simplex\n"));
+ return 0;
+ }
+
+ return wsrv;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Find the point on the clip line locus and simplexes */
+/* valid surface, that is closest to the target output value. */
+/* We expect to be given a sub simplex with sdi = fdi-1, and efdi = fdi */
+/* or a limit sub-simplex with sdi = fdi, and efdi = fdi+1 */
+/* Return zero if solution canot be calculated, */
+/* return 1 normally, 2 if solution would be above the (disabled) ink limit */
+static int
+vnearest_clip_solve(
+schbase *b,
+simplex *x,
+double *xp, /* Return solution (simplex parameter space) */
+double *xv, /* Return solution (output space) */
+double *err /* Output error distance at solution point */
+) {
+ rspl *s = b->s;
+ int e, sdi = x->sdi;
+ int f, fdi = s->fdi, efdi = x->efdi;
+ int g;
+ int wsrv; /* Within simplex return value */
+
+ double *ta[MXRO], TA[MXRO][MXRO];
+ double tb[MXRO];
+
+ DBG(("Vector nearest clip solution called, cell %d, splx %d\n", x->ix, x->si));
+
+ /* Setup temporary matricies */
+ for (f = 0; f < sdi; f++) {
+ ta[f] = TA[f];
+ }
+
+ /* Substitute simplex equation for output values V */
+ /* in terms of sub-simplex parameters P, */
+ /* into clip line implicit equation in V, to give */
+ /* clip line simplex implicit equation in terms of P (simplex input space) */
+ /* If this is a limit sub-simlex, the ink limit part of the clip vector */
+ /* equations will be used. */
+
+ /* LHS: ta[sdi][sdi] = cla[sdi][efdi] * vv[efdi][sdi] */
+ /* RHS: tb[sdi] = clb[sdi] - cla[sdi][efdi] * vv_di[efdi] */
+ for (f = 0; f < sdi; f++) {
+ double tt;
+ for (e = 0; e < sdi; e++) {
+ for (tt = 0.0, g = 0; g < efdi; g++)
+ tt += b->cla[f][g] * (x->v[e][g] - x->v[e+1][g]);
+ ta[f][e] = tt;
+ }
+ for (tt = 0.0, g = 0; g < efdi; g++)
+ tt += b->cla[f][g] * x->v[sdi][g];
+ tb[f] = b->clb[f] - tt;
+ }
+
+ /* Compute the solution */
+ if (gen_solve_se(ta, tb, sdi, sdi)) {
+ DBG(("Equation solution failed!\n"));
+ return 0; /* No solution */
+ }
+
+ /* Check that the solution is within the simplex */
+ if ((wsrv = within_simplex(x, tb)) != 0) {
+ double dist; /* distance to clip target */
+
+ DBG(("Got solution within simplex %s\n", icmPdv(sdi,tb)));
+
+ /* Compute the output space solution point */
+ for (f = 0; f < fdi; f++) {
+ double tt = 0.0;
+ for (e = 0; e < sdi; e++) {
+ tt += (x->v[e][f] - x->v[e+1][f]) * tb[e];
+ }
+ xv[f] = tt + x->v[sdi][f];
+ }
+
+ /* Copy to return array */
+ for (e = 0; e < sdi; e++)
+ xp[e] = tb[e];
+
+ /* Compute distance to clip target */
+ for (dist = 0.0, f = 0; f < fdi ; f++) {
+ double tt = (b->v[f] - xv[f]);
+ dist += tt * tt;
+ }
+ DBGV(("Vector clip output soln: ",fdi," %f", xv, "\n"));
+
+ /* Return the solution in xp[]m xv[] and *err */
+ *err = sqrt(dist);
+
+ DBG(("Vector clip returning a solution with error %f\n",*err));
+ return wsrv;
+ }
+
+ DBG(("Vector clip solution not in simplex\n"));
+ return 0; /* No solution */
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - */
+/* Find the point on the simplexes valid surface, that is closest */
+/* to the target output value. */
+/* We expect to be given a sub simplex with sdi = fdi-1, and efdi = fdi */
+/* or a limit sub-simplex with sdi = fdi, and efdi = fdi+1 */
+/* Return zero if solution canot be calculated, */
+/* return 1 normally, 2 if solution would be above the (disabled) ink limit */
+static int
+nnearest_clip_solve(
+schbase *b,
+simplex *x,
+double *xp, /* Return solution (simplex parameter space) */
+double *xv, /* Return solution (output space) */
+double *err /* Output error distance at solution point */
+) {
+ rspl *s = b->s;
+ int e, sdi = x->sdi;
+ int f, fdi = s->fdi, efdi = x->efdi;
+ double tb[MXRO]; /* RHS & Parameter solution */
+ double dist; /* distance to clip target */
+ int wsrv = 0; /* Within simplex return value */
+
+ DBG(("Nearest clip solution called, cell %d, splx %d\n", x->ix, x->si));
+
+ if (sdi == 0) { /* Solution is vertex */
+ wsrv = 1;
+ for (f = 0; f < efdi; f++)
+ xv[f] = x->v[sdi][f]; /* Copy vertex value */
+ if (x->v[sdi][fdi] > s->limitv) {
+ if (s->limiten) /* Needed when limiten == 0 */
+ return 0; /* Over ink limit - no good */
+ wsrv = 2; /* Would be over */
+ }
+ DBG(("Got assumed vertex solution\n"));
+ } else {
+#ifdef NEVER /* Don't specialise ink limit version - use INKSCALE fudge instead */
+ if (!(x->flags & SPLX_CLIPSX)) { /* Not an ink limited plane simplex */
+
+#endif
+ /* Create the SVD decomp needed for least squares solution */
+ if (add_lu_svd(x)) {
+ DBG(("SVD decomp failed, skip simplex\n"));
+ return 0;
+ }
+
+ /* Setup RHS to solve */
+ for (f = 0; f < efdi; f++)
+ tb[f] = b->v[f] - x->v[sdi][f];
+
+ /* Find least squares solution */
+ svdbacksub(x->d_u, x->d_w, x->d_v, tb, tb, efdi, sdi);
+
+ /* Check that the solution is within the simplex */
+ if ((wsrv = within_simplex(x, tb)) == 0) {
+ DBG(("Nearest clip solution not in simplex\n"));
+ return 0; /* No solution */
+ }
+
+ DBG(("Got solution within simplex %s\n",icmPdv(sdi,tb)));
+
+ /* Compute the output space solution point */
+ for (f = 0; f < fdi; f++) {
+ double tt = 0.0;
+ for (e = 0; e < sdi; e++) {
+ tt += (x->v[e][f] - x->v[e+1][f]) * tb[e];
+ }
+ xv[f] = tt + x->v[sdi][f];
+ }
+#ifdef NEVER /* ~~1 Haven't figured out equations to make this a special case. */
+ /* Content to use INKSCALE fudge and rely on SVD least squares. */
+ } else {
+ /* We can't use the given equations, because we want the solution */
+ /* to lie exactly on the ink limit plane, and be least squares to the */
+ /* other target parameters. */
+ /* Extract the ink limit parameters, and transform them into */
+ /* a parameterised surface for this simplex. */
+ /* Substitute the ink plane equation into the remaining target */
+ /* parameter equations, and solve for least squares. */
+
+ }
+#endif
+ }
+
+ /* Copy to return array */
+ for (e = 0; e < sdi; e++)
+ xp[e] = tb[e];
+
+ /* Compute distance to clip target */
+ for (dist = 0.0, f = 0; f < fdi ; f++) {
+ double tt = (b->v[f] - xv[f]);
+ dist += tt * tt;
+ }
+ DBGV(("Nearest clip output soln: ",fdi," %f", xv, "\n"));
+
+ /* Return the solution in xp[]m xv[] and *err */
+ *err = sqrt(dist);
+
+ DBG(("Nearest clip returning a solution with error %f\n",*err));
+ return wsrv;
+}
+
+
+#ifdef NEVER
+/* Utility to convert an implicit ink limit plane equation */
+/* (held at the end of the simplex output value equations), */
+/* into a parameterized surface equation. */
+static void
+compute_param_limit_surface(
+schbase *b,
+simplex *x
+) {
+ rspl *s = b->s;
+ int ff, f, fdi = s->fdi;
+ int i, p;
+ double lgst;
+
+double st[MXRO], /* Start point */
+double de[MXRO] /* Delta */
+ DBG(("Computing clipping line implicit equation, dim = %d\n", fdi));
+
+ /* Pick a pivot element - the smallest */
+ for (lgst = -1.0, p = -1, f = 0; f < fdi; f++) {
+ double tt = de[f];
+ b->cdir[f] = tt; /* Stash this away */
+ tt = fabs(tt);
+ if (tt > lgst) {
+ lgst = tt;
+ p = f;
+ }
+ }
+ if (p < 0) /* Shouldn't happen */
+ error("rspl rev, internal, trying to cope with zero length clip line\n");
+
+ if (b->cla == NULL)
+ b->cla = dmatrix(0, fdi-1, 0, fdi); /* Allow for ink limit supliment */
+
+ for (i = ff = 0; ff < fdi; ff++) { /* For the input rows */
+ if (ff == p) {
+ continue; /* Skip pivot row */
+ }
+ for (f = 0; f < fdi; f++) { /* For input & output columns */
+ if (f == p) {
+ b->cla[i][f] = -de[ff]; /* Last column is -ve delta value */
+ } else if (f == ff) {
+ b->cla[i][f] = de[p]; /* Diagonal is pivot value */
+ } else {
+ b->cla[i][f] = 0.0; /* Else zero */
+ }
+ }
+ b->clb[i] = de[p] * st[ff] - de[ff] * st[p];
+ i++;
+ }
+
+ /* Add ink limit target equation - */
+ /* interpolated ink value == target */
+ if (s->limitf != NULL) {
+ for (i = 0; i < (fdi-1); i++)
+ b->cla[i][fdi] = 0.0;
+
+ for (f = 0; f < fdi; f++)
+ b->cla[fdi-1][f] = 0.0;
+
+ b->cla[fdi-1][fdi] = 1.0;
+ b->clb[fdi-1] = s->limitv;
+ }
+
+#ifdef NEVER
+/* Verify that the implicit equation is correct */
+{
+ double pnt[MXRO], v[MXRO];
+ double pa; /* Parameter */
+ for (pa = 0.0; pa <= 1.0; pa += 0.125) {
+ for (f = 0; f < fdi; f++) {
+ pnt[f] = st[f] + pa * de[f];
+ }
+
+ /* Verify the implicit equation */
+ for (ff = 0; ff < (fdi-1); ff++) {
+ v[ff] = 0.0;
+ for (f = 0; f < fdi; f++) {
+ v[ff] += b->cla[ff][f] * pnt[f];
+ }
+ v[ff] -= b->clb[ff];
+ if (v[ff] < 0.0)
+ v[ff] = -v[ff];
+ if (v[ff] > 0.000001) {
+ printf("Point on clip line = %f %f %f\n",pnt[0],pnt[1],pnt[2]);
+ printf("Implicit %d error of = %f\n",ff, v[ff]);
+ }
+ }
+ }
+}
+#endif /* NEVER */
+
+}
+
+#endif
+
+
+
+
+/* -------------------------------------------------------- */
+/* Cell/simplex object lower level code */
+
+/* Utility to get or calculate a vertexes ink limit value */
+static double get_limitv(
+schbase *b, /* Base search information */
+int ix, /* fwd index of cell */
+float *fcb, /* Pointer to base of vertex value array (ix is used if NULL) */
+double *p /* Array of input values (can be NULL to compute) */
+) {
+ rspl *s = b->s;
+ float *base = fcb;
+ double lv;
+ if (base == NULL)
+ base = s->g.a + ix * s->g.pss;
+ lv = base[-1]; /* Fetch existing ink limit function value */
+ if ((float)lv == L_UNINIT) { /* Not been computed yet */
+ if (p != NULL) {
+ lv = INKSCALE * s->limitf(s->lcntx, p); /* Do it */
+ base[-1] = (float)lv;
+ } else {
+ int e, di = s->di;
+ double pp[MXRI]; /* Copy from float to double */
+ int tix; /* Temp fwd cell index */
+
+ for (tix = ix, e = 0; e < di; e++) {
+ int dix;
+ dix = tix % s->g.res[e];
+ tix /= s->g.res[e];
+ pp[e] = s->g.l[e] + (double)dix * s->g.w[e]; /* Base point */
+ }
+ lv = INKSCALE * s->limitf(s->lcntx, pp); /* Do it */
+ base[-1] = (float)lv;
+ }
+ s->g.limitv_cached = 1; /* At least one limit value is cached */
+ }
+ return lv;
+}
+
+/* Utility to invalidate all the ink limit values */
+/* cached in the main rspl array */
+static void clear_limitv(
+rspl *s
+) {
+ int i;
+ float *gp; /* Grid point pointer */
+
+ if (s->g.limitv_cached != 0) { /* If any have been set */
+ /* Unset them all */
+ for (i = 0, gp = s->g.a; i < s->g.no; i++, gp += s->g.pss) {
+ gp[-1] = L_UNINIT;
+ }
+ s->g.limitv_cached = 0;
+ }
+}
+
+/* Cell code */
+
+static void free_cell_contents(cell *c);
+static cell *cache_rcell(revcache *r, int ix, int force);
+static void uncache_rcell(revcache *r, cell *cp);
+
+/* Return a pointer to an appropriate reverse cell */
+/* cache structure. None of the sub simplex lists will */
+/* be initialised. */
+/* NOTE: must unget_cell() (== uncache_rcell()) when cell */
+/* is no longer needed */
+/* Return NULL if we ran out of room in the cache. */
+static cell *get_rcell(
+schbase *b, /* Base search information */
+int ix, /* fwd index of cell */
+int force /* if nz, force memory allocation, so that we have at least one cell */
+) {
+ rspl *s = b->s;
+ int ee, e, di = s->di;
+ int p2di = (1<<di);
+ int ff, f, fdi = s->fdi;
+ cell *c;
+
+ c = cache_rcell(s->rev.cache, ix, force); /* Fetch it from the cache and lock it */
+ if (c == NULL)
+ return NULL;
+
+ if (!(c->flags & CELL_FLAG_1)) { /* Have to (re)initialize cell & simplexes */
+ int tix; /* Temp fwd cell index */
+ float *fcb = s->g.a + ix * s->g.pss; /* Pointer to base float of fwd cell */
+
+ /* Compute basic Cell info and vertex output values */
+ for (ee = 0; ee < p2di; ee++) {
+ float *vp = fcb + s->g.fhi[ee];
+ for (f = 0; f < fdi; f++) /* Transfer cell verticy values from grid */
+ c->v[ee][f] = vp[f];
+
+ /* ~~ reset any other cell info that will be stale */
+ }
+
+ /* Convert from cell index, to absolute fwd coord base values */
+ c->limmin = INF_DIST; /* and min/max values */
+ c->limmax = -INF_DIST;
+ for (tix = ix, e = 0; e < di; e++) {
+ int dix;
+ dix = tix % s->g.res[e];
+ tix /= s->g.res[e];
+ c->p[0][e] = s->g.l[e] + (double)dix * s->g.w[e]; /* Base point */
+ }
+ if (s->limitf != NULL) { /* Compute ink limit values at base verticy */
+ double lv = get_limitv(b, ix, fcb, c->p[0]); /* Fetch or generate limit value */
+ c->v[0][fdi] = lv;
+ if (lv < c->limmin) /* And min/max for this cell */
+ c->limmin = lv;
+ if (lv > c->limmax)
+ c->limmax = lv;
+ }
+
+ /* Setup cube verticy input position values, and ink limit values */
+ for (ee = 1; ee < p2di; ee++) {
+ for (e = 0; e < di; e++) {
+ c->p[ee][e] = c->p[0][e];
+ if (ee & (1 << e))
+ c->p[ee][e] += s->g.w[e]; /* In input space offset */
+ }
+ if (s->limitf != NULL) { /* Compute ink limit values at cell verticies */
+ double lv = get_limitv(b, ix, fcb + s->g.fhi[ee], c->p[ee]);
+ c->v[ee][fdi] = lv;
+ if (lv < c->limmin) /* And min/max for this cell */
+ c->limmin = lv;
+ if (lv > c->limmax)
+ c->limmax = lv;
+ }
+ }
+
+ /* Compute the output bounding sphere for fast rejection testing */
+ {
+ double *min[MXRO], *max[MXRO]; /* Pointers to points with min/max values */
+ double radsq = -1.0; /* Span/radius squared */
+ double rad;
+ int spf = 0;
+
+ /* Find verticies of cell that have min and max values in output space */
+ for (f = 0; f < fdi; f++)
+ min[f] = max[f] = NULL;
+
+ for (ee = 0; ee < p2di; ee++) {
+ double *vp = c->v[ee];
+ for (f = 0; f < fdi; f++) {
+ if (min[f] == NULL || min[f][f] > vp[f])
+ min[f] = vp;
+ if (max[f] == NULL || max[f][f] < vp[f])
+ max[f] = vp;
+ }
+ }
+
+ /* Find the pair of points with the largest span (diameter) in output space */
+ for (ff = 0; ff < fdi; ff++) {
+ double ss;
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt;
+ tt = max[ff][f] - min[ff][f];
+ ss += tt * tt;
+ }
+ if (ss > radsq) {
+ radsq = ss;
+ spf = ff; /* Output dimension max was in */
+ }
+ }
+
+ /* Set initial bounding sphere */
+ for (f = 0; f < fdi; f++) {
+ c->bcent[f] = (max[spf][f] + min[spf][f])/2.0;
+ }
+ radsq /= 4.0; /* diam^2 -> rad^2 */
+ c->bradsq = radsq;
+ rad = c->brad = sqrt(radsq);
+
+ /* Go though all the points again, expanding sphere if necessary */
+ for (ee = 0; ee < p2di; ee++) {
+ double ss;
+ double *vp = c->v[ee];
+
+ /* Compute distance squared of point to bounding shere */
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = vp[f] - c->bcent[f];
+ ss += tt * tt;
+ }
+ if (ss > radsq) {
+ double tt;
+ /* DBG(("Expanding bounding sphere by %f\n",sqrt(ss) - rad)); */
+
+ ss = sqrt(ss) + EPS; /* Radius to point */
+ rad = (rad + ss)/2.0;
+ c->bradsq = radsq = rad * rad;
+ tt = ss - rad;
+ for (f = 0; f < fdi; f++) {
+ c->bcent[f] = (rad * c->bcent[f] + tt * vp[f])/ss;
+ }
+
+ } else {
+ /* DBG(("Bounding sphere encloses by %f\n",rad - sqrt(ss))); */
+ }
+ }
+ c->bradsq += EPS;
+ }
+ c->flags = CELL_FLAG_1;
+ }
+
+ return c;
+}
+
+void free_simplex_info(cell *c, int dof);
+
+/* Free up any allocated simplexes in a cell, */
+/* and set the pointers to NULL. */
+/* Nothing else is changed (ie. it's NOT removed from */
+/* the cache index or unthrheaded from the mru list). */
+static void
+free_cell_contents(
+cell *c
+) {
+ int nsdi;
+
+ /* Free up all the simplexes */
+ if (c->s != NULL) {
+ for (nsdi = 0; nsdi <= c->s->di; nsdi++) {
+ if (c->sx[nsdi] != NULL) {
+ free_simplex_info(c, nsdi);
+ c->sx[nsdi] = NULL;
+ }
+ }
+ }
+ /* ~~ free any other cell information */
+}
+
+/* - - - - - - */
+/* Simplex code */
+
+/* Simplex and Cell hash index size increments */
+int primes[] = {
+ 367,
+ 853,
+ 1489,
+ 3373,
+ 3373,
+ 6863,
+ 12919,
+ 23333,
+ 43721,
+ 97849,
+ 146221,
+ 254941,
+ -1
+};
+
+/* Compute a simplex hash index */
+unsigned int simplex_hash(revcache *rc, int sdi, int efdi, int *vix) {
+ unsigned int hash = 0;
+ int i;
+
+ for (i = 0; i <= sdi; i++)
+ hash = hash * 17 + vix[i];
+ hash = hash * 17 + sdi;
+ hash = hash * 17 + efdi;
+
+ hash %= rc->spx_hash_size;
+ return hash;
+}
+
+/* Allocate and do the basic initialisation for a DOF list of simplexes */
+void alloc_simplexes(
+cell *c,
+int nsdi /* Non limited sub simplex dimensionality */
+) {
+ rspl *s = c->s;
+ schbase *b = s->rev.sb;
+ revcache *rc = s->rev.cache;
+ int ee, e, di = s->di;
+ int f, fdi = s->fdi;
+ int lsdi; /* Ink limited Sub-simplex sdi */
+ int tsxno; /* Total number of DOF simplexes */
+ int nsxno; /* Number of non-ink limited DOF simplexes */
+ int si, so; /* simplex index in and out */
+
+ DBG(("Allocating level %d sub simplexes in cell %d\n",nsdi,c->ix));
+ if (c->sx[nsdi] != NULL)
+ error("rspl rev, internal, trying allocate already allocated simplexes\n");
+
+ /* Figure out how many simplexes will be at this nsdi */
+ lsdi = nsdi + 1; /* Ink limit simplexes sdi */
+
+ tsxno = nsxno = s->rev.sspxi[nsdi].nospx;
+
+ if (s->limitf != NULL && lsdi <= di)
+ tsxno += s->rev.sspxi[lsdi].nospx; /* Second set with extra input dimension */
+
+ /* Make sure there is enough space in temp simplex filter list */
+ if (b->lsxfilt < tsxno) { /* Allocate more space if needed */
+
+ if (b->lsxfilt > 0) { /* Free old space before allocating new */
+ free(b->sxfilt);
+ DECSZ(b->s, b->lsxfilt * sizeof(char));
+ }
+ b->lsxfilt = 0;
+ /* Allocate enough space for all the candidate cells */
+ if ((b->sxfilt = (char *)rev_malloc(s, tsxno * sizeof(char))) == NULL)
+ error("rev: malloc failed - temp simplex filter list, count %d",tsxno);
+ b->lsxfilt = tsxno; /* Current allocated space */
+ INCSZ(b->s, b->lsxfilt * sizeof(char));
+ }
+
+ /* Figure out the number of simplexes that will actually be needed */
+ for (si = so = 0; si < tsxno; si++) {
+ psxinfo *psxi = NULL;
+ int *icomb, *offs;
+ int sdi = nsdi;
+ int efdi = fdi;
+ int ssi = si;
+ int isclip = 0;
+ if (si >= nsxno) { /* If limit boundary simplex */
+ sdi++; /* One more dimension */
+ efdi++; /* One more constraint */
+ ssi -= nsxno; /* In second half of list */
+ isclip++; /* Limit clipped simplex */
+ }
+ psxi = &s->rev.sspxi[sdi].spxi[ssi];
+ icomb = psxi->icomb;
+ offs = psxi->offs;
+
+ b->sxfilt[si] = 0; /* Assume simplex won't be used */
+
+ /* Check if simplex should be discared due to the ink limit */
+ if (s->limitf != NULL) {
+ double max = -INF_DIST;
+ double min = INF_DIST;
+
+ /* Find the range of ink limit values covered by simplex */
+ for (e = 0; e <= sdi; e++) { /* For all the simplex verticies */
+ int i = offs[e];
+ double vv = c->v[i][fdi]; /* Ink limit value */
+ if (vv < min)
+ min = vv;
+ if (vv > max)
+ max = vv;
+ }
+
+//if ((max - min) > EPS) printf("~1 Found simplex sdi %d, efdi %d, min = %f, max = %f, limitv = %f\n", sdi, efdi, min,max,s->limitv);
+ if (isclip) { /* Limit clipped simplex */
+ /* (Make sure it straddles the limit boundary) */
+ if (max < s->limitv || min > s->limitv)
+ continue; /* Discard this simplex - it can't straddle the ink limit */
+//printf("~1 using sub simplex sdi %d, efdi %d, min = %f, max = %f, limitv = %f\n", sdi, efdi, min,max,s->limitv);
+ } else {
+ if (min > s->limitv)
+ continue; /* Discard this simplex - it is above the ink limit */
+ }
+ }
+
+ b->sxfilt[si] |= 1; /* This cell will be OK */
+ so++;
+ }
+
+ DBG(("There are %d level %d sub simplexes\n",so, nsdi));
+ /* Allocate space for all the DOF simplexes that will be used */
+ if (so > 0) {
+ if ((c->sx[nsdi] = (simplex **) rev_calloc(s, so, sizeof(simplex *))) == NULL)
+ error("rspl malloc failed - reverse cell simplexes - list of pointers");
+ INCSZ(s, so * sizeof(simplex *));
+ }
+
+ /* Setup SPLX_FLAG_1 level information in the simplex */
+ for (si = so = 0; si < tsxno; si++) {
+ simplex *x;
+ psxinfo *psxi = NULL;
+ int *icomb;
+ int sdi, efdi;
+ int ssi;
+ int vix[MXRI+1]; /* fwd cell vertex indexes of this simplex [sdi+1] */
+
+ if (b->sxfilt[si] == 0) /* Decided not to use this one */
+ continue;
+
+#ifdef STATS
+ s->rev.st[b->op].sinited++;
+#endif /* STATS */
+
+ sdi = nsdi;
+ efdi = fdi;
+ ssi = si;
+ if (si >= nsxno) { /* If limit boundary simplex */
+ sdi++; /* One more dimension */
+ efdi++; /* One more constraint */
+ ssi -= nsxno; /* In second half of list */
+ }
+
+ psxi = &s->rev.sspxi[sdi].spxi[ssi];
+ icomb = psxi->icomb;
+
+ /* Compute simplex vertexes so we can match it in the cache */
+ for (e = 0; e <= sdi; e++)
+ vix[e] = c->ix + s->g.hi[psxi->offs[e]];
+
+ x = c->sx[nsdi][so];
+
+ /* If this is a shared simplex, see if we already have it in another cell */
+ if (x == NULL && psxi->face) {
+ unsigned int hash;
+//printf("~1 looking for existing simplex nsdi = %d\n",nsdi);
+ hash = simplex_hash(rc, sdi, efdi, vix);
+ for (x = rc->spxhashtop[hash]; x != NULL; x = x->hlink) {
+ if (x->sdi != sdi
+ || x->efdi != efdi)
+ continue; /* miss */
+ for (e = 0; e <= sdi; e++) {
+ if (x->vix[e] != vix[e])
+ break; /* miss */
+ }
+ if (e > sdi)
+ break; /* hit */
+ }
+ if (x != NULL) {
+ x->refcount++;
+//printf("~1 found hit in simplex face list hash %d, refcount = %d\n",hash,x->refcount);
+ }
+ }
+ /* Doesn't already exist */
+ if (x == NULL) {
+ if ((x = (simplex *) rev_calloc(s, 1, sizeof(simplex))) == NULL)
+ error("rspl malloc failed - reverse cell simplexes - base simplex %d bytes",sizeof(simplex));
+ INCSZ(s, sizeof(simplex));
+ x->refcount = 1;
+ x->touch = s->rev.stouch-1;
+ x->flags = 0;
+
+ if (si >= nsxno) { /* If limit boundary simplex */
+ x->flags |= SPLX_CLIPSX; /* Limit clipped simplex */
+ }
+
+ /* Fill in the other simplex details */
+ x->s = s; /* Parent rspl */
+ x->ix = c->ix; /* Construction cube base index */
+ for (e = 0; e <= sdi; e++) /* Indexs of fwd verticies that make up this simplex */
+ x->vix[e] = vix[e];
+ x->psxi = psxi; /* Pointer to constant per simplex info */
+//printf("~1 set simplex 0x%x psxi = 0x%x\n",x,x->psxi);
+ x->si = so; /* Diagnostic, simplex offset in list */
+ x->sdi = sdi; /* Copy of simplex dimensionaity */
+ x->efdi = efdi; /* Copy of effective output dimensionality */
+
+ /* Copy cell simplex vertex output and limit values */
+ for (e = 0; e <= sdi; e++) { /* For all the simplex verticies */
+ int i = x->psxi->offs[e];
+
+ for (f = 0; f <= fdi; f++) /* Copy vertex value + ink sum */
+ x->v[e][f] = c->v[i][f];
+
+ /* Setup output bounding box values (the hard way) */
+ if (e == 0) { /* Init to first vertex of simplex */
+ for (f = 0; f <= fdi; f++) /* Output space */
+ x->min[f] = x->max[f] = c->v[i][f];
+ } else {
+ for (f = 0; f <= fdi; f++) { /* Output space + ink sum */
+ double vv;
+// if (f == fdi && s->limit == NULL)
+// continue; /* Skip ink */
+ vv = c->v[i][f];
+ if (vv < x->min[f])
+ x->min[f] = vv;
+ else if (vv > x->max[f])
+ x->max[f] = vv;
+ }
+ }
+ }
+ /* Add a margin */
+ for (f = 0; f <= fdi; f++) { /* Output space + ink sum */
+ x->min[f] -= EPS;
+ x->max[f] += EPS;
+ }
+
+ /* Setup input bounding box value pointers (the easy way) */
+ for (ee = 0; ee < di; ee++) {
+ x->p0[ee] = c->p[0][ee]; /* Construction base cube origin */
+ x->pmin[ee] = c->p[x->psxi->pmino[ee]][ee] - EPS;
+ x->pmax[ee] = c->p[x->psxi->pmaxo[ee]][ee] + EPS;
+ }
+
+ x->flags |= SPLX_FLAG_1; /* vv & iv done, nothing else */
+
+ x->aloc2 = x->aloc5 = NULL; /* Matrix allocations not done yet */
+
+ /* Add it to the face shared simplex hash index */
+ if (x->psxi->face) {
+ unsigned int hash;
+ int i;
+ /* See if we should re-size the simplex hash index */
+ if (++rc->nspx > (HASH_FILL_RATIO * rc->spx_hash_size)) {
+ for (i = 0; primes[i] > 0 && primes[i] <= rc->spx_hash_size; i++)
+ ;
+ if (primes[i] > 0) {
+ int spx_hash_size = rc->spx_hash_size; /* Old */
+ simplex **spxhashtop = rc->spxhashtop;
+
+ rc->spx_hash_size = primes[i];
+
+ DBG(("Increasing face simplex hash index to %d\n",spx_hash_size));
+//printf("~1 increasing simplex hash index size to %d\n",spx_hash_size);
+ /* Allocate a new index */
+ if ((rc->spxhashtop = (simplex **) rev_calloc(s, rc->spx_hash_size,
+ sizeof(simplex *))) == NULL)
+ error("rspl malloc failed - reverse simplex cache index");
+ INCSZ(s, rc->spx_hash_size * sizeof(simplex *));
+
+ /* Transfer all the simplexes to the new index */
+ for (i = 0; i < spx_hash_size; i++) {
+ simplex *x, *nx;
+ for (x = spxhashtop[i]; x != NULL; x = nx) {
+ nx = x->hlink;
+ hash = simplex_hash(rc, x->sdi, x->efdi, x->vix); /* New hash */
+ x->hlink = rc->spxhashtop[hash]; /* Add to new hash index */
+ rc->spxhashtop[hash] = x;
+ }
+ }
+ free(spxhashtop); /* Done with old index */
+ DECSZ(s, spx_hash_size * sizeof(simplex *));
+ }
+ }
+ hash = simplex_hash(rc, sdi, efdi, vix);
+
+ /* Add this to hash index */
+ x->hlink = rc->spxhashtop[hash];
+ rc->spxhashtop[hash] = x;
+//printf("~1 Added simplex to hash %d, rc->nspx = %d\n",hash,rc->nspx);
+ }
+
+//if (rc->nunlocked == 0 && rc->s->rev.sz > rc->s->rev.max_sz)
+//printf("~1 unable to decrease_revcache 1\n");
+
+ /* keep memory in check */
+ while (rc->nunlocked > 0 && rc->s->rev.sz > rc->s->rev.max_sz) {
+ if (decrease_revcache(rc) == 0)
+ break;
+ }
+ }
+ c->sx[nsdi][so] = x;
+ so++;
+ }
+ c->sxno[nsdi] = so; /* Record actual number in list */
+ c->flags |= CELL_FLAG_2; /* Note that cell now has simplexes */
+}
+
+/* Free up any allocated for a list of sub-simplexes */
+void
+free_simplex_info(
+cell *c,
+int nsdi /* non limit sub simplex dimensionaity */
+) {
+ int si, sxno = c->sxno[nsdi]; /* Number of simplexes */
+
+ for (si = 0; si < sxno; si++) { /* For all the simplexes */
+ simplex *x = c->sx[nsdi][si];
+ int dof = x->sdi - x->efdi;
+
+//printf("~1 freeing simplex, refcount = %d\n",x->refcount);
+ if (--x->refcount <= 0) { /* Last reference to this simplex */
+
+//printf("~1 freeing simplex 0x%x psxi = 0x%x\n",x,x->psxi);
+ if (x->psxi->face) {
+ unsigned int hash;
+ revcache *rc = c->s->rev.cache;
+
+ hash = simplex_hash(rc, x->sdi, x->efdi, x->vix);
+
+ /* Free it from the hash list */
+ if (rc->spxhashtop[hash] == x) {
+ rc->spxhashtop[hash] = x->hlink;
+ rc->nspx--;
+//printf("~1 removed simplex from hash %d, nspx now = %d\n",hash,rc->nspx);
+ } else {
+ simplex *xx;
+ for (xx = rc->spxhashtop[hash]; xx != NULL && xx->hlink != x; xx = xx->hlink)
+ ;
+ if (xx != NULL) { /* Found it */
+ xx->hlink = x->hlink;
+ rc->nspx--;
+//printf("~1 removed simplex from hash %d, nspx now = %d\n",hash,rc->nspx);
+ }
+//else
+//printf("~1 warning, failed to find face simplex hash %d, sdi = %d in cache index (nspx = %d)!!\n",hash,x->sdi,rc->nspx);
+ }
+ }
+ if (x->aloc2 != NULL) {
+ int adof = dof >= 0 ? dof : 0; /* Allocation dof */
+ int asize;
+ if (dof == 0)
+ asize = sizeof(double) * (x->efdi * x->sdi)
+ + sizeof(double *) * x->efdi
+ + sizeof(int) * x->sdi;
+ else
+ asize = sizeof(double) * (x->sdi * (x->efdi + x->sdi + adof + 2) + x->efdi)
+ + sizeof(double *) * (x->efdi + 2 * x->sdi);
+ free(x->aloc2);
+ DECSZ(x->s, asize);
+ }
+
+ if (x->aloc5 != NULL) {
+ int asize;
+ if (x->naux == dof)
+ asize = sizeof(double *) * x->naux
+ + sizeof(double) * (x->naux * dof)
+ + sizeof(int) * dof;
+ else
+ asize = sizeof(double *) * (x->naux + dof)
+ + sizeof(double) * (dof * (x->naux + dof + 1));
+ free(x->aloc5);
+ DECSZ(x->s, asize);
+ }
+
+ /* ~~ free any other simplex information */
+
+ free(x);
+ DECSZ(c->s, sizeof(simplex));
+ c->sx[nsdi][si] = NULL;
+ }
+ }
+ free(c->sx[nsdi]);
+ DECSZ(c->s, c->sxno[nsdi] * sizeof(simplex *));
+ c->sx[nsdi] = NULL;
+ c->sxno[nsdi] = 0;
+
+ /* ~~ free any other cell information */
+}
+
+/* - - - - - - - - - - - - */
+/* Check that an input space vector is within a given simplex, */
+/* and that it meets any ink limit. */
+/* Return zero if outside the simplex, */
+/* 1 normally if within the simplex, */
+/* and 2 if it would be over the ink limit if limit was enabled. */
+static int
+within_simplex(
+simplex *x, /* Simplex */
+double *p /* Input coords in simplex space */
+) {
+ rspl *s = x->s;
+ schbase *b = s->rev.sb;
+ int fdi = s->fdi;
+ int e, sdi = x->sdi; /* simplex dimensionality */
+ double cp, lp;
+ int rv = 1;
+ /* EPS is allowance for numeric error */
+ /* (Don't want solutions falling down */
+ /* the numerical cracks between the simplexes) */
+
+ /* Check we are within baricentric limits */
+ for (lp = 0.0, e = 0; e < sdi; e++) {
+ cp = p[e];
+ if ((cp+EPS) < lp) /* Outside baricentric or not in correct */
+ return 0; /* order for this simplex */
+ lp = cp;
+ }
+ if ((1.0+EPS) < lp) /* outside baricentric range */
+ return 0;
+
+ /* Compute limit using interp. - assume simplex would have been trivially rejected */
+ if (s->limitf != NULL) {
+ double sum = 0.0; /* Might be over the limit */
+ for (e = 0; e < sdi; e++)
+ sum += p[e] * (x->v[e][fdi] - x->v[e+1][fdi]);
+ sum += x->v[sdi][fdi];
+ if (sum > s->limitv) {
+ if (s->limiten != 0)
+ return 0; /* Exceeds ink limit */
+ else
+ rv = 2; /* would have exceeded limit */
+ }
+ }
+
+#ifdef NEVER
+ /* Constrain to legal values */
+ /* (Is this needed ?????) */
+ for (e = 0; e < sdi; e++) {
+ cp = p[e];
+ if (cp < 0.0)
+ p[e] = 0.0;
+ else if (cp > 1.0)
+ p[e] = 1.0;
+ }
+#endif
+ return rv;
+}
+
+/* Convert vector from simplex space to absolute cartesian space */
+static void simplex_to_abs(
+simplex *x,
+double *out, /* output in absolute space */
+double *in /* Input in simplex space */
+) {
+ rspl *s = x->s;
+ int e, di = s->di;
+ int *icomb = x->psxi->icomb; /* Coord combination order */
+
+ for (e = 0; e < di; e++) { /* For each absolute coord */
+ double ov = x->p0[e]; /* Base value */
+ int ee = icomb[e]; /* Simplex param index */
+ if (ee >= 0) /* Simplex param value */
+ ov += s->g.w[e] * in[ee];
+ else if (ee == -2) /* 1 value */
+ ov += s->g.w[e];
+ /* Else 0 value */
+ out[e] = ov;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Given the parametric clip line equation, compute the */
+/* implicit equation in terms of the absolute output space. */
+/* Pad equation with target ink limit in case it is use */
+/* with CLIPSX sub-simplexes. */
+/* Note that no line equation values are returned if fdi = 1, */
+/* since there is no such thing as an implicit line equation. */
+static void
+init_line_eq(
+schbase *b,
+double st[MXRO], /* Start point */
+double de[MXRO] /* Delta */
+) {
+ rspl *s = b->s;
+ int ff, f, fdi = s->fdi;
+ int i, p;
+ double lgst;
+
+ DBG(("Computing clipping line implicit equation, dim = %d\n", fdi));
+
+ /* Pick a pivot element */
+ for (lgst = -1.0, p = -1, f = 0; f < fdi; f++) {
+ double tt = de[f];
+ b->cdir[f] = tt; /* Stash this away */
+ tt = fabs(tt);
+ if (tt > lgst) {
+ lgst = tt;
+ p = f;
+ }
+ }
+ if (p < 0) /* Shouldn't happen */
+ error("rspl rev, internal, trying to cope with zero length clip line\n");
+
+ if (b->cla == NULL)
+ b->cla = dmatrix(0, fdi-1, 0, fdi); /* Allow for ink limit supliment */
+
+ for (i = ff = 0; ff < fdi; ff++) { /* For the input rows */
+ if (ff == p) {
+ continue; /* Skip pivot row */
+ }
+ for (f = 0; f < fdi; f++) { /* For input & output columns */
+ if (f == p) {
+ b->cla[i][f] = -de[ff]; /* Last column is -ve delta value */
+ } else if (f == ff) {
+ b->cla[i][f] = de[p]; /* Diagonal is pivot value */
+ } else {
+ b->cla[i][f] = 0.0; /* Else zero */
+ }
+ }
+ b->clb[i] = de[p] * st[ff] - de[ff] * st[p];
+ i++;
+ }
+
+ /* Add ink limit target equation - */
+ /* interpolated ink value == target */
+ if (s->limitf != NULL) {
+ for (i = 0; i < (fdi-1); i++)
+ b->cla[i][fdi] = 0.0;
+
+ for (f = 0; f < fdi; f++)
+ b->cla[fdi-1][f] = 0.0;
+
+ b->cla[fdi-1][fdi] = 1.0;
+ b->clb[fdi-1] = s->limitv;
+ }
+
+#ifdef NEVER
+/* Verify that the implicit equation is correct */
+{
+ double pnt[MXRO], v[MXRO];
+ double pa; /* Parameter */
+ for (pa = 0.0; pa <= 1.0; pa += 0.125) {
+ for (f = 0; f < fdi; f++) {
+ pnt[f] = st[f] + pa * de[f];
+ }
+
+ /* Verify the implicit equation */
+ for (ff = 0; ff < (fdi-1); ff++) {
+ v[ff] = 0.0;
+ for (f = 0; f < fdi; f++) {
+ v[ff] += b->cla[ff][f] * pnt[f];
+ }
+ v[ff] -= b->clb[ff];
+ if (v[ff] < 0.0)
+ v[ff] = -v[ff];
+ if (v[ff] > 0.000001) {
+ printf("Point on clip line = %f %f %f\n",pnt[0],pnt[1],pnt[2]);
+ printf("Implicit %d error of = %f\n",ff, v[ff]);
+ }
+ }
+ }
+}
+#endif /* NEVER */
+
+}
+
+/* - - - - - - */
+/* Simpex solution info #2 */
+
+/* Create the LU or SVD decomp needed to compute solution or locus. */
+/* Return non-zero if it cannot be created */
+static int
+add_lu_svd(simplex *x) {
+
+ if (x->flags & SPLX_FLAG_2F) { /* Previously failed */
+ return 1;
+ }
+ if (!(x->flags & SPLX_FLAG_2)) {
+ int ee, e, sdi = x->sdi;
+ int f, efdi = x->efdi;
+ int dof = sdi-efdi; /* Degree of freedom of locus, or -ve over specification */
+ int adof = dof >= 0 ? dof : 0; /* Allocation dof */
+ int i;
+
+ if (x->aloc2 == NULL) { /* Allocate space for matricies and arrays */
+ /* Do this in one hit to minimise malloc overhead */
+ if (dof == 0) {
+ int i;
+ char *mem;
+ int asize = sizeof(double) * (efdi * sdi)
+ + sizeof(double *) * efdi
+ + sizeof(int) * sdi;
+
+ if ((x->aloc2 = mem = (char *) rev_malloc(x->s, asize)) == NULL)
+ error("rspl malloc failed - reverse cell sub-simplex matricies");
+ INCSZ(x->s, asize);
+
+ /* Allocate biggest to smallest (double, pointers, ints) */
+ /* to make sure that items lie on the natural boundaries. */
+
+ /* Reserve matrix doubles */
+ mem += efdi * sdi * sizeof(double);
+
+ /* Allocate pointers */
+ x->d_u = (double **)mem, mem += efdi * sizeof(double *);
+
+ /* Allocate ints */
+ x->d_w = (double *)mem, mem += sdi * sizeof(int);
+
+#ifdef DEBUG
+ if (mem != (x->aloc2 + asize))
+ error("~1 aloc2a assert failed! Is %d, should be %d\n",mem - x->aloc2,asize);
+#endif /* DEBUG */
+
+ /* Reset and allocate matrix doubles */
+ mem = x->aloc2;
+ for (i = 0; i < efdi; i++)
+ x->d_u[i] = (double *)mem, mem += sdi * sizeof(double);
+
+ } else {
+ int i;
+ char *mem;
+ int asize = sizeof(double) * (sdi * (efdi + sdi + adof + 2) + efdi)
+ + sizeof(double *) * (efdi + 2 * sdi);
+
+ if ((x->aloc2 = mem = (char *) rev_malloc(x->s, asize)) == NULL)
+ error("rspl malloc failed - reverse cell sub-simplex matricies");
+ INCSZ(x->s, asize);
+
+ /* Allocate biggest to smallest (double, pointers, ints) */
+ /* to make sure that items lie on the natural boundaries. */
+
+ /* Reserve matrix doubles */
+ mem += sdi * (efdi + sdi + adof) * sizeof(double);
+
+ /* Allocate doubles */
+ x->lo_xb = (double *)mem, mem += efdi * sizeof(double);
+ x->lo_bd = (double *)mem; mem += sdi * sizeof(double);
+ x->d_w = (double *)mem, mem += sdi * sizeof(double);
+
+ /* Allocate pointers */
+ x->d_u = (double **)mem, mem += efdi * sizeof(double *);
+ x->d_v = (double **)mem, mem += sdi * sizeof(double *);
+ x->lo_l = (double **)mem, mem += sdi * sizeof(double *);
+
+#ifdef DEBUG
+ if (mem != (x->aloc2 + asize))
+ error("~1 aloc2b assert failed! Is %d, should be %d\n",mem - x->aloc2,asize);
+#endif /* DEBUG */
+
+ /* Reset and allocate matrix doubles */
+ mem = x->aloc2;
+ for (i = 0; i < efdi; i++)
+ x->d_u[i] = (double *)mem, mem += sdi * sizeof(double);
+ for (i = 0; i < sdi; i++)
+ x->d_v[i] = (double *)mem, mem += sdi * sizeof(double);
+ for (i = 0; i < sdi; i++)
+ x->lo_l[i] = (double *)mem, mem += adof * sizeof(double);
+
+ /* Init any values that will be read before being written to. */
+ for (f = 0; f < efdi; f++)
+ x->lo_xb[f] = 1e100; /* Silly value */
+ }
+ }
+
+ /* Setup matrix from vertex values */
+ for (f = 0; f < efdi; f++)
+ for (e = 0; e < sdi; e++)
+ x->d_u[f][e] = x->v[e][f] - x->v[e+1][f];
+
+ if (dof == 0) { /* compute LU */
+ double rip;
+#ifdef STATS
+ x->s->rev.st[x->s->rev.sb->op].sinited2a++;
+#endif /* STATS */
+ if (lu_decomp(x->d_u, sdi, (int *)x->d_w, &rip)) {
+ x->flags |= SPLX_FLAG_2F; /* Failed */
+ return 1;
+ }
+ } else {
+//printf("~~ Creating SVD decomp, sdi = %d, efdi = %d\n", sdi, efdi);
+
+#ifdef STATS
+ x->s->rev.st[x->s->rev.sb->op].sinited2b++;
+#endif /* STATS */
+ if (svdecomp(x->d_u, x->d_w, x->d_v, efdi, sdi)) {
+ x->flags |= SPLX_FLAG_2F; /* Failed */
+ return 1;
+ }
+
+ /* Threshold the singular values W[] */
+ svdthresh(x->d_w, sdi);
+
+ if (dof >= 0) { /* If we expect a locus */
+//printf("~~ got dif %d locus from SVD\n",dof);
+ /* copy the locus direction coefficients out */
+ for (i = e = 0; e < sdi; e++) {
+ if (x->d_w[e] == 0.0) { /* Found a zero W[] */
+ if (i < dof) {
+ for (ee = 0; ee < sdi; ee++) { /* Copy column of V[][] */
+ x->lo_l[ee][i] = x->d_v[ee][e];
+ }
+ }
+ i++;
+ }
+ }
+ if (i != dof) {
+//printf("~~ got unexpected dof in svd\n");
+ x->flags |= SPLX_FLAG_2F; /* Failed */
+ return 1; /* Didn't get expected d.o.f. */
+ }
+ }
+ }
+ x->flags |= SPLX_FLAG_2; /* Set flag so that it isn't attempted again */
+
+//if (x->s->rev.cache->nunlocked == 0 && x->s->rev.sz > x->s->rev.max_sz)
+//printf("~1 unable to decrease_revcache 2\n");
+
+ /* keep memory in check */
+ while (x->s->rev.cache->nunlocked > 0 && x->s->rev.sz > x->s->rev.max_sz) {
+ if (decrease_revcache(x->s->rev.cache) == 0)
+ break;
+ }
+ }
+ return 0;
+}
+
+/* - - - - - - */
+/* Simplex solution info #4 */
+
+/* Calculate the solution locus equation for this simplex and target */
+/* (The direction was calculated by add_svd(), but now calculate */
+/* the base solution point for this particular reverse lookup) */
+/* Return non-zero if this point canot be calculated */
+/* We are assuming that sdi > efdi */
+static int
+add_locus(
+schbase *b,
+simplex *x
+) {
+ int sdi = x->sdi;
+ int f, efdi = x->efdi;
+ int doback = 0;
+
+#ifdef STATS
+ x->s->rev.st[x->s->rev.sb->op].sinited4++;
+#endif /* STATS */
+ /* Use output of svdcmp() to solve overspecified and/or */
+ /* singular equation A.x = b */
+
+ /* Init the RHS B[] vector, and check if it doesn't match */
+ /* that used to compute base value last time. */
+ for (f = 0; f < efdi; f++) {
+ double xb = b->v[f] - x->v[sdi][f];
+ if (x->lo_xb[f] != xb) {
+ x->lo_xb[f] = xb;
+ doback = 1; /* RHS differs, so re-compute */
+ }
+ }
+
+#ifdef STATS
+ if (doback && (x->flags & SPLX_FLAG_4))
+ x->s->rev.st[x->s->rev.sb->op].sinited4i++;
+#endif /* STATS */
+
+ /* Compute locus */
+ if (doback || !(x->flags & SPLX_FLAG_4))
+ svdbacksub(x->d_u, x->d_w, x->d_v, x->lo_xb, x->lo_bd, efdi, sdi);
+
+ x->flags |= SPLX_FLAG_4;
+
+//if (x->s->rev.cache->nunlocked == 0 && x->s->rev.sz > x->s->rev.max_sz)
+//printf("~1 unable to decrease_revcache 3\n");
+
+ /* keep memory in check */
+ while (x->s->rev.cache->nunlocked > 0 && x->s->rev.sz > x->s->rev.max_sz) {
+ if (decrease_revcache(x->s->rev.cache) == 0)
+ break;
+ }
+
+ return 0;
+}
+
+/* - - - - - - */
+/* Simplex solution info #5 */
+
+/* Compute LU or SVD decomp of lo_l */
+/* Allocates the memory for the various matricies */
+/* Return non-zero if this canot be calculated. */
+static int
+add_auxil_lu_svd(
+schbase *b,
+simplex *x
+) {
+ int ee, sdi = x->sdi;
+ int f, efdi = x->efdi;
+ int dof = sdi-efdi; /* Degree of freedom of locus */
+ int naux = b->naux; /* Number of auxiliaries actually available */
+
+#ifdef STATS
+ if (x->aaux != b->naux || x->auxbm != b->auxbm)
+ x->s->rev.st[x->s->rev.sb->op].sinited5i++;
+#endif /* STATS */
+
+ if (x->aaux != b->naux) { /* Number of auxiliaries has changed */
+ if (x->aloc5 != NULL) {
+ int asize;
+ if (x->naux == dof)
+ asize = sizeof(double *) * x->naux
+ + sizeof(double) * (x->naux * dof)
+ + sizeof(int) * dof;
+ else
+ asize = sizeof(double *) * (x->naux + dof)
+ + sizeof(double) * (dof * (x->naux + dof + 1));
+ free(x->aloc5);
+ x->aloc5 = NULL;
+ DECSZ(x->s, asize);
+ }
+ x->flags &= ~(SPLX_FLAG_5 | SPLX_FLAG_5F); /* Force recompute */
+ }
+
+ if (x->auxbm != b->auxbm) { /* Different selection of auxiliaries */
+ x->flags &= ~(SPLX_FLAG_5 | SPLX_FLAG_5F); /* Force recompute */
+ }
+
+ if (x->flags & SPLX_FLAG_5F) { /* Previously failed */
+ return 1;
+ }
+ if (!(x->flags & SPLX_FLAG_5)) {
+ int *icomb = x->psxi->icomb; /* abs -> simplex coordinate translation */
+
+ if (x->aloc5 == NULL) { /* Allocate space for matricies and arrays */
+ /* Do this in one hit to minimise malloc overhead */
+ if (naux == dof) {
+ int i;
+ char *mem;
+ int asize = sizeof(double *) * naux
+ + sizeof(double) * (naux * dof)
+ + sizeof(int) * dof;
+
+ if ((x->aloc5 = mem = (char *) rev_malloc(x->s, asize)) == NULL)
+ error("rspl malloc failed - reverse cell sub-simplex matricies");
+ INCSZ(x->s, asize);
+
+ /* Allocate biggest to smallest (double, pointers, ints) */
+ /* to make sure that items lie on the natural boundaries. */
+
+ /* Reserve matrix doubles */
+ mem += naux * dof * sizeof(double);
+
+ /* Allocate pointers and ints */
+ x->d_u = (double **)mem, mem += naux * sizeof(double *);
+ x->d_w = (double *)mem, mem += dof * sizeof(int);
+
+#ifdef DEBUG
+ if (mem != (x->aloc5 + asize))
+ error("aloc5a assert failed! Is %d, should be %d\n",mem - x->aloc5,asize);
+#endif /* DEBUG */
+
+ /* Reset and allocate matrix doubles */
+ mem = x->aloc5;
+ for (i = 0; i < naux; i++)
+ x->d_u[i] = (double *)mem, mem += dof * sizeof(double);
+ } else {
+ int i;
+ char *mem;
+ int asize = sizeof(double *) * (naux + dof)
+ + sizeof(double) * (dof * (naux + dof + 1));
+
+ if ((x->aloc5 = mem = (char *) rev_malloc(x->s, asize)) == NULL)
+ error("rspl malloc failed - reverse cell sub-simplex matricies");
+ INCSZ(x->s, asize);
+
+ /* Allocate biggest to smallest (double, pointers, ints) */
+ /* to make sure that items lie on the natural boundaries. */
+
+ /* Reserve matrix doubles */
+ mem += dof * (naux + dof) * sizeof(double);
+
+ /* Allocate doubles */
+ x->ax_w = (double *)mem, mem += dof * sizeof(double);
+
+ /* Allocate pointers, ints */
+ x->ax_u = (double **)mem, mem += naux * sizeof(double *);
+ x->ax_v = (double **)mem, mem += dof * sizeof(double *);
+
+#ifdef DEBUG
+ if (mem != (x->aloc5 + asize))
+ error("aloc5b assert failed! Is %d, should be %d\n",mem - x->aloc5,asize);
+#endif /* DEBUG */
+
+ /* Reset and allocate matrix doubles */
+ mem = x->aloc5;
+ for (i = 0; i < naux; i++)
+ x->ax_u[i] = (double *)mem, mem += dof * sizeof(double);
+ for (i = 0; i < dof; i++)
+ x->ax_v[i] = (double *)mem, mem += dof * sizeof(double);
+ }
+ x->aaux = naux; /* Number of auxiliaries allocated for */
+ }
+
+ /* Setup A[][] matrix to decompose, and figure number of auxiliaries actually needed */
+ for (ee = naux = 0; ee < b->naux; ee++) {
+ int ei = icomb[b->auxi[ee]]; /* Simplex relative auxiliary index */
+ if (ei < 0)
+ continue; /* aux corresponds with fixed input value for this simplex */
+ for (f = 0; f < dof; f++)
+ x->ax_u[naux][f] = x->lo_l[ei][f];
+ naux++;
+ }
+ x->naux = naux; /* Number of auxiliaries actually available */
+ x->auxbm = b->auxbm; /* Mask of auxiliaries used */
+
+ if (naux == dof) { /* Use LU decomp to solve exactly */
+ double rip;
+
+#ifdef STATS
+ x->s->rev.st[x->s->rev.sb->op].sinited5a++;
+#endif /* STATS */
+ if (lu_decomp(x->ax_u, dof, (int *)x->ax_w, &rip)) {
+ x->flags |= SPLX_FLAG_5F;
+ return 1;
+ }
+
+ } else if (naux > 0) { /* Use SVD to solve least squares */
+
+#ifdef STATS
+ x->s->rev.st[x->s->rev.sb->op].sinited5b++;
+#endif /* STATS */
+ if (svdecomp(x->ax_u, x->ax_w, x->ax_v, naux, dof)) {
+ x->flags |= SPLX_FLAG_5F;
+ return 1;
+ }
+
+ /* Threshold the singular values W[] */
+ svdthresh(x->ax_w, dof);
+ } /* else naux == 0, don't setup anything */
+
+ x->flags |= SPLX_FLAG_5;
+
+//if (x->s->rev.cache->nunlocked == 0 && x->s->rev.sz > x->s->rev.max_sz)
+//printf("~1 unable to decrease_revcache 4\n");
+
+ /* keep memory in check */
+ while (x->s->rev.cache->nunlocked > 0 && x->s->rev.sz > x->s->rev.max_sz) {
+ if (decrease_revcache(x->s->rev.cache) == 0)
+ break;
+ }
+ }
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Initialise a static sub-simplex verticy information table */
+void
+rspl_init_ssimplex_info(
+rspl *s,
+ssxinfo *xip, /* Pointer to sub-simplex info structure to init. */
+int sdi /* Sub-simplex dimensionality (range 0 - di) */
+) {
+ int e, di = s->di; /* Dimensionality */
+ int vi, nospx; /* Number of sub-simplexes */
+ XCOMBO(vcmb, MXDI, sdi+1, 1 << di);/* Simplex dimension sdi out of cube dimension di counter */
+
+ DBG(("init_ssimplex_info called with sdi = %d\n",sdi));
+ /* First count the number of sub-simplexes */
+ nospx = 0;
+ XCB_INIT(vcmb);
+ while (!XCB_DONE(vcmb)) {
+ nospx++;
+ XCB_INC(vcmb);
+ }
+
+ xip->sdi = sdi;
+ xip->nospx = nospx;
+ if ((xip->spxi = (psxinfo *) rev_calloc(s, nospx, sizeof(psxinfo))) == NULL)
+ error("rspl malloc failed - reverse cell sub-simplex info array");
+ INCSZ(s, nospx * sizeof(psxinfo));
+
+ DBG(("Number of subsimplex = %d\n",nospx));
+ /* For all sub-simplexes */
+ XCB_INIT(vcmb);
+ for (vi = 0; vi < nospx; vi++) {
+ psxinfo *x = &xip->spxi[vi];
+ int i;
+ int andm, orm;
+
+ /* XCOMB generates verticies in order from max to min offset */
+
+ /* Compute Absolute -> Parameter mapping */
+ for (e = 0; e < di; e++) { /* For each absolute axis */
+
+ if ((vcmb[sdi] & (1<<e)) != 0) {
+ x->icomb[e] = -2; /* This abs is always '1' */
+
+ } else if ((vcmb[0] & (1<<e)) == 0) {
+ x->icomb[e] = -1; /* This abs is always '0' */
+
+ } else {
+ for (i = 0; i < sdi; i++) { /* For each verticy in large to small order (!first) */
+ if ((vcmb[i] & (1<<e)) != 0 &&
+ (vcmb[i+1] & (1<<e)) == 0) {/* Transition from offset 1 to 0 */
+ x->icomb[e] = i; /* This is parameter */
+ break;
+ }
+ }
+ }
+ }
+
+ /* Compute fwd grid offsets for each simplex vertex in baricentric order */
+ for (i = 0; i <= sdi; i++) { /* For each verticy */
+ int pmin[MXRI], pmax[MXRI];
+ x->offs[i] = vcmb[i];
+ x->goffs[i] = s->g.hi[vcmb[i]];
+ x->foffs[i] = s->g.fhi[vcmb[i]];
+
+ /* Setup input coordinate bounding box value offsets */
+ if (i == 0) { /* Init to first vertex of simplex */
+ for (e = 0; e < di; e++) { /* Input space */
+ x->pmino[e] = x->pmaxo[e] = vcmb[i];
+ pmin[e] = pmax[e] = vcmb[i] & (1<<e);
+ }
+ } else {
+ for (e = 0; e < di; e++) { /* Input space */
+ int vv = vcmb[i] & (1<<e);
+ if (vv < pmin[e]) { /* Adjust min/max offsets */
+ x->pmino[e] = vcmb[i];
+ pmin[e] = vv;
+ } else if (vv > pmax[e]) {
+ x->pmaxo[e] = vcmb[i];
+ pmax[e] = vv;
+ }
+ }
+ }
+ }
+
+ /* See if the sub-simplex lies on a cube face */
+ andm = ~0;
+ orm = 0;
+ for (i = 0; i <= sdi; i++) { /* For each verticy */
+ andm &= vcmb[i];
+ orm |= vcmb[i];
+ }
+ /* If one coordinate is common (all 0 or all 1) to the verticies, */
+ /* they must all be on the same cube face. */
+ if (andm != 0 || orm != ((1 << di)-1))
+ x->face = 1;
+ else
+ x->face = 0;
+
+#ifdef DEBUG
+ printf("Verticies = ");
+ for (i = 0; i <= sdi; i++)
+ printf("%d ",vcmb[i]);
+ printf("\n");
+
+ printf("Face = %s\n",x->face ? "True" : "False");
+
+ printf("Abs -> Parm = ");
+ for (e = 0; e < di; e++)
+ printf("%d ",x->icomb[e]);
+ printf("\n");
+
+ printf("Grid Offset = ");
+ for (e = 0; e <= sdi; e++)
+ printf("%d ",x->goffs[e]);
+ printf("Float Offset = ");
+ for (e = 0; e <= sdi; e++)
+ printf("%d ",x->foffs[e]);
+ printf("\n");
+ printf("\n");
+#endif /* DEBUG */
+
+ /* Increment the counter value */
+ XCB_INC(vcmb);
+ }
+}
+
+/* Free the given sub-simplex verticy information */
+void
+rspl_free_ssimplex_info(
+rspl *s,
+ssxinfo *xip /* Pointer to sub-simplex info structure */
+) {
+ if (xip == NULL) /* Assert */
+ return;
+
+ free(xip->spxi);
+ DECSZ(s, xip->nospx * sizeof(psxinfo));
+ xip->spxi = NULL;
+}
+
+/* ====================================================== */
+/* Reverse cell cache code */
+
+/* Allocate and initialise the reverse cell cache */
+static revcache *
+alloc_revcache(
+rspl *s
+) {
+ revcache *rc;
+
+ DBG(("alloc_revcache called\n"));
+ if ((rc = (revcache *) rev_calloc(s, 1, sizeof(revcache))) == NULL)
+ error("rspl malloc failed - reverse cell cache");
+ INCSZ(s, sizeof(revcache));
+
+ rc->s = s; /* For stats */
+
+ /* Allocate an initial cell hash index */
+ rc->cell_hash_size = primes[0];
+
+ if ((rc->hashtop = (cell **) rev_calloc(s, rc->cell_hash_size, sizeof(cell *))) == NULL)
+ error("rspl malloc failed - reverse cell cache index");
+ INCSZ(s, rc->cell_hash_size * sizeof(cell *));
+
+ /* Allocate an initial simplex face match hash index */
+ rc->spx_hash_size = primes[0];
+
+ if ((rc->spxhashtop = (simplex **) rev_calloc(s, rc->spx_hash_size, sizeof(simplex *))) == NULL)
+ error("rspl malloc failed - reverse simplex cache index");
+ INCSZ(s, rc->spx_hash_size * sizeof(simplex *));
+
+ return rc;
+}
+
+/* Free the reverse cell cache */
+static void
+free_revcache(revcache *rc) {
+ int i;
+ cell *cp, *ncp;
+
+ /* Free any stuff allocated in the cell contents, and the cell itself. */
+ for (cp = rc->mrubot; cp != NULL; cp = ncp) {
+ ncp = cp->mruup;
+ free_cell_contents(cp);
+ free(cp);
+ DECSZ(rc->s, sizeof(cell));
+ }
+
+ /* Free the hash indexes */
+ free(rc->hashtop);
+ DECSZ(rc->s, rc->cell_hash_size * sizeof(cell *));
+ free(rc->spxhashtop);
+ DECSZ(rc->s, rc->spx_hash_size * sizeof(simplex *));
+
+ DECSZ(rc->s, sizeof(revcache));
+ free(rc);
+}
+
+/* Invalidate the whole cache */
+static void
+invalidate_revcache(
+revcache *rc)
+{
+ int i;
+ cell *cp;
+
+ rc->nunlocked = 0;
+
+ /* Free any stuff allocated in the cell contents */
+ for (cp = rc->mrubot; cp != NULL; cp = cp->mruup) {
+ free_cell_contents(cp);
+ cp->refcount = 0; /* Make sure they can now be reused */
+ cp->ix = 0;
+ cp->flags = 0; /* Contents needs re-initializing */
+ rc->nunlocked++;
+ }
+
+ /* Clear the hash table so they can't be hit */
+ for (i = 0; i < rc->cell_hash_size; i++) {
+ rc->hashtop[i] = NULL;
+ }
+
+}
+
+#define HASH(xx, yy) ((yy) % xx->cell_hash_size)
+
+/* Allocate another cell, and add it to the cache. */
+/* This may re-size the hash index too. */
+/* Return the pointer to the new cell. */
+/* (Note it's not our job here to honour the memory limit) */
+static cell *
+increase_revcache(
+revcache *rc
+) {
+ cell *nxcell; /* Newly allocated cell */
+ int i;
+
+ DBG(("Adding another chunk of cells to cache\n"));
+
+#ifdef NEVER /* We may be called with force != 0 */
+ if (rc->s->rev.sz >= rc->s->rev.max_sz)
+ return NULL;
+#endif
+
+ if ((nxcell = (cell *) rev_calloc(rc->s, 1, sizeof(cell))) == NULL)
+ error("rspl malloc failed - reverse cache cells");
+ INCSZ(rc->s, sizeof(cell));
+
+ nxcell->s = rc->s;
+
+ /* Add cell to the bottom of the cache mru linked list */
+ if (rc->mrutop == NULL) /* List was empty */
+ rc->mrutop = nxcell;
+ else {
+ rc->mrubot->mrudown = nxcell; /* Splice into bottom */
+ nxcell->mruup = rc->mrubot;
+ }
+ rc->mrubot = nxcell;
+ rc->nacells++;
+ rc->nunlocked++;
+
+ DBG(("cache is now %d cells\n",rc->nacells));
+
+ /* See if the hash index should be re-sized */
+ if (rc->nacells > (HASH_FILL_RATIO * rc->cell_hash_size)) {
+ for (i = 0; primes[i] > 0 && primes[i] <= rc->cell_hash_size; i++)
+ ;
+ if (primes[i] > 0) {
+ int cell_hash_size = rc->cell_hash_size; /* Old */
+ cell **hashtop = rc->hashtop;
+
+ rc->cell_hash_size = primes[i];
+
+ DBG(("Increasing cell cache hash index to %d\n",cell_hash_size));
+ /* Allocate a new index */
+ if ((rc->hashtop = (cell **) rev_calloc(rc->s, rc->cell_hash_size, sizeof(cell *))) == NULL)
+ error("rspl malloc failed - reverse cell cache index");
+ INCSZ(rc->s, rc->cell_hash_size * sizeof(cell *));
+
+ /* Transfer all the cells to the new index */
+ for (i = 0; i < cell_hash_size; i++) {
+ cell *c, *nc;
+ for (c = hashtop[i]; c != NULL; c = nc) {
+ int hash;
+ nc = c->hlink;
+ hash = HASH(rc, c->ix); /* New hash */
+ c->hlink = rc->hashtop[hash]; /* Add to new hash index */
+ rc->hashtop[hash] = c;
+ }
+ }
+
+ /* Done with old index */
+ free(hashtop);
+ DECSZ(rc->s, cell_hash_size * sizeof(cell *));
+ }
+ }
+
+ return nxcell;
+}
+
+/* Reduce the cache memory usage by freeing the least recently used unlocked cell. */
+/* Return nz if we suceeeded in freeing some memory. */
+static int decrease_revcache(
+revcache *rc /* Reverse cache structure */
+) {
+ int hit = 0;
+ int hash;
+ cell *cp;
+
+ DBG(("Decreasing cell cache memory allocation by freeing a cell\n"));
+
+ /* Use the least recently used unlocked cell */
+ for (cp = rc->mrubot; cp != NULL && cp->refcount > 0; cp = cp->mruup)
+ ;
+
+ /* Run out of unlocked cells */
+ if (cp == NULL) {
+ DBG(("Failed to find unlocked cell to free\n"));
+//printf("~1 failed to decrease memory\n");
+ return 0;
+ }
+
+ /* If it has been used before, free up the simplexes */
+ free_cell_contents(cp);
+
+ /* Remove from current hash index (if it is in it) */
+ hash = HASH(rc,cp->ix); /* Old hash */
+ if (rc->hashtop[hash] == cp) {
+ rc->hashtop[hash] = cp->hlink;
+ } else {
+ cell *c;
+ for (c = rc->hashtop[hash]; c != NULL && c->hlink != cp; c = c->hlink)
+ ;
+ if (c != NULL)
+ c->hlink = cp->hlink;
+ }
+
+ /* Free up this cell - Remove it from LRU list */
+ if (rc->mrutop == cp)
+ rc->mrutop = cp->mrudown;
+ if (rc->mrubot == cp)
+ rc->mrubot = cp->mruup;
+ if (cp->mruup != NULL)
+ cp->mruup->mrudown = cp->mrudown;
+ if (cp->mrudown != NULL)
+ cp->mrudown->mruup = cp->mruup;
+ cp->mruup = cp->mrudown = NULL;
+ free(cp);
+ DECSZ(rc->s, sizeof(cell));
+ rc->nacells--;
+ rc->nunlocked--;
+
+ DBG(("Freed a rev cache cell\n"));
+ return 1;
+}
+
+/* Return a pointer to an appropriate reverse cell */
+/* cache structure. cell->flags will be 0 if the cell */
+/* has been reallocated. cell contents will be 0 if */
+/* never used before. */
+/* The cell reference count is incremented, so that it */
+/* can't be thrown out of the cache. The cell must be */
+/* released with uncache_rcell() when it's no longer needed. */
+/* return NULL if we ran out of room in the cache */
+static cell *cache_rcell(
+revcache *rc, /* Reverse cache structure */
+int ix, /* fwd index of cell */
+int force /* if nz, force memory allocation, so that we have at least one cell */
+) {
+ int hit = 0;
+ int hash;
+ cell *cp;
+
+ /* keep memory in check - fail if we're out of memory and can't free any */
+ /* (Doesn't matter if it might be a hit, it will get picked up the next time) */
+ if (!force && rc->s->rev.sz > rc->s->rev.max_sz && rc->nunlocked <= 0) {
+ return NULL;
+ }
+
+//if (rc->nunlocked == 0 && rc->s->rev.sz > rc->s->rev.max_sz)
+//printf("~1 unable to decrease_revcache 5\n");
+
+ /* Free up memory to get below threshold */
+ while (rc->nunlocked > 0 && rc->s->rev.sz > rc->s->rev.max_sz) {
+ if (decrease_revcache(rc) == 0)
+ break;
+ }
+
+ hash = HASH(rc,ix); /* Compute hash of fwd cell index */
+
+ /* See if we get a cache hit */
+ for (cp = rc->hashtop[hash]; cp != NULL; cp = cp->hlink) {
+ if (ix == cp->ix) { /* Hit */
+ hit = 1;
+#ifdef STATS
+ rc->s->rev.st[rc->s->rev.sb->op].chits++;
+#endif /* STATS */
+ break;
+ }
+ }
+ if (!hit) { /* No hit, use new cell or the least recently used cell */
+ int ohash;
+
+ /* If we haven't used all our memory, or if we are forced and have */
+ /* no cell we can re-use, then noallocate another cell */
+ if (rc->s->rev.sz < rc->s->rev.max_sz || (force && rc->nunlocked == 0)) {
+ cp = increase_revcache(rc);
+ hash = HASH(rc,ix); /* Re-compute hash in case hash size changed */
+//printf("~1 using new cell\n");
+ } else {
+//printf("~1 memory limit has been reached, using old cell\n");
+
+ for (;;) {
+ /* Use the least recently used unlocked cell */
+ for (cp = rc->mrubot; cp != NULL && cp->refcount > 0; cp = cp->mruup)
+ ;
+
+ /* Run out of unlocked cells */
+ if (cp == NULL) {
+//printf("~1 none available\n");
+ return NULL;
+ }
+
+ /* If it has been used before, free up the simplexes */
+ free_cell_contents(cp);
+
+ /* Remove from current hash index (if it is in it) */
+ ohash = HASH(rc,cp->ix); /* Old hash */
+ if (rc->hashtop[ohash] == cp) {
+ rc->hashtop[ohash] = cp->hlink;
+ } else {
+ cell *c;
+ for (c = rc->hashtop[ohash]; c != NULL && c->hlink != cp; c = c->hlink)
+ ;
+ if (c != NULL)
+ c->hlink = cp->hlink;
+ }
+
+ /* If we're now under the memory limit, use this cell */
+ if (rc->s->rev.sz < rc->s->rev.max_sz) {
+ break;
+ }
+
+//printf("~1 freeing a cell\n");
+ /* Free up this cell and look for another one */
+ /* Remove it from LRU list */
+ if (rc->mrutop == cp)
+ rc->mrutop = cp->mrudown;
+ if (rc->mrubot == cp)
+ rc->mrubot = cp->mruup;
+ if (cp->mruup != NULL)
+ cp->mruup->mrudown = cp->mrudown;
+ if (cp->mrudown != NULL)
+ cp->mrudown->mruup = cp->mruup;
+ cp->mruup = cp->mrudown = NULL;
+ free(cp);
+ DECSZ(rc->s, sizeof(cell));
+ rc->nacells--;
+ rc->nunlocked--;
+ }
+ }
+
+#ifdef STATS
+ rc->s->rev.st[rc->s->rev.sb->op].cmiss++;
+#endif /* STATS */
+
+ /* Add this cell to hash index */
+ cp->hlink = rc->hashtop[hash];
+ rc->hashtop[hash] = cp; /* Add to hash table and list */
+
+ cp->ix = ix;
+ cp->flags = 0; /* Contents needs re-initializing */
+//printf("~1 returning fresh cell\n");
+ }
+
+ /* Move slected cell to the top of the mru list */
+ if (cp->mruup != NULL) { /* This one wasn't already at top */
+ cp->mruup->mrudown = cp->mrudown;
+ if (cp->mrudown == NULL) /* This was bottom */
+ rc->mrubot = cp->mruup; /* New bottom */
+ else
+ cp->mrudown->mruup = cp->mruup;
+ /* Put this one at the top */
+ rc->mrutop->mruup = cp;
+ cp->mrudown = rc->mrutop;
+ rc->mrutop = cp;
+ cp->mruup = NULL;
+ }
+ if (cp->refcount == 0) {
+ rc->nunlocked--;
+ }
+
+ cp->refcount++;
+
+ return cp;
+}
+
+/* Tell the cache that we aren't using this cell anymore, */
+/* but to keep it in case it is needed again. */
+static void uncache_rcell(
+revcache *rc, /* Reverse cache structure */
+cell *cp
+) {
+ if (cp->refcount > 0) {
+ cp->refcount--;
+ if (cp->refcount == 0) {
+ rc->nunlocked++;
+ }
+ } else
+ warning("rspl cell cache assert: refcount overdecremented!");
+}
+
+/* ====================================================== */
+/* Reverse rspl setup functions */
+
+/* Called by rspl initialisation */
+/* Note that reverse cell lookup tables are not */
+/* allocated & created until the first call */
+/* to a reverse interpolation function. */
+void
+init_rev(rspl *s) {
+
+ /* First section */
+ s->rev.inited = 0;
+ s->rev.res = 0;
+ s->rev.no = 0;
+ s->rev.rev = NULL;
+
+ /* Second section */
+ s->rev.rev_valid = 0;
+ s->rev.nnrev = NULL;
+
+ /* Third section */
+ s->rev.cache = NULL;
+
+ /* Fourth section */
+ s->rev.sb = NULL;
+
+ /* Methods */
+ s->rev_set_limit = rev_set_limit_rspl;
+ s->rev_get_limit = rev_get_limit_rspl;
+ s->rev_interp = rev_interp_rspl;
+ s->rev_locus = rev_locus_rspl;
+ s->rev_locus_segs = rev_locus_segs_rspl;
+}
+
+/* Free up all the reverse interpolation info */
+void free_rev(
+rspl *s /* Pointer to rspl grid */
+) {
+ int e, di = s->di;
+ int **rpp, *rp;
+
+#ifdef STATS
+ {
+ int i, totcalls = 0;
+ for (i = 0; i < 5; i++) {
+ totcalls += s->rev.st[i].searchcalls;
+ }
+
+ printf("\n===============================\n");
+ printf("di = %d, do = %d\n",s->di, s->fdi);
+ for (i = 0; i < 5; i++) {
+ int calls = s->rev.st[i].searchcalls;
+ if (calls == 0)
+ continue;
+ printf("\n- - - - - - - - - - - - - - - -\n");
+ printf("Operation %s\n",opnames[i]);
+ printf("Search calls = %d = %f%%\n",s->rev.st[i].searchcalls,
+ 100.0 * s->rev.st[i].searchcalls/totcalls);
+ printf("Cells searched/call = %f\n",s->rev.st[i].csearched/(double)calls);
+ printf("Simplexes searched/call = %f\n",s->rev.st[i].ssearched/(double)calls);
+ printf("Simplexes inited level 1/call = %f\n",s->rev.st[i].sinited/(double)calls);
+ printf("Simplexes inited level 2 (LU)/call = %f\n",s->rev.st[i].sinited2a/(double)calls);
+ printf("Simplexes inited level 2 (SVD)/call = %f\n",s->rev.st[i].sinited2b/(double)calls);
+ printf("Simplexes invalidated level 4/call = %f\n",s->rev.st[i].sinited4i/(double)calls);
+ printf("Simplexes inited level 4/call = %f\n",s->rev.st[i].sinited4/(double)calls);
+ printf("Simplexes invalidated level 5/call = %f\n",s->rev.st[i].sinited5i/(double)calls);
+ printf("Simplexes inited level 5 (LU)/call = %f\n",s->rev.st[i].sinited5a/(double)calls);
+ printf("Simplexes inited level 5 (SVD)/call = %f\n",s->rev.st[i].sinited5b/(double)calls);
+ if ((s->rev.st[i].chits + s->rev.st[i].cmiss) == 0)
+ printf("No cache calls\n");
+ else
+ printf("Cell hit rate = %f%%\n",
+ 100.0 * s->rev.st[i].chits/(double)(s->rev.st[i].chits + s->rev.st[i].cmiss));
+ }
+ printf("\n===============================\n");
+ }
+#endif /* STATS */
+
+ /* Free up Fourth section */
+ if (s->rev.sb != NULL) {
+ free_search(s->rev.sb);
+ s->rev.sb = NULL;
+ }
+ /* Free up the Third section */
+ if (s->rev.cache != NULL) {
+ free_revcache(s->rev.cache); /* Reverse cell cache */
+ s->rev.cache = NULL;
+ }
+
+ /* Free up the Second section */
+ if (s->rev.nnrev != NULL) {
+ /* Free arrays at grid points, taking care of reference count */
+ for (rpp = s->rev.nnrev; rpp < (s->rev.nnrev + s->rev.no); rpp++) {
+ if ((rp = *rpp) != NULL && --rp[2] <= 0) {
+ DECSZ(s, rp[0] * sizeof(int));
+ free(*rpp);
+ *rpp = NULL;
+ }
+ }
+ free(s->rev.nnrev);
+ DECSZ(s, s->rev.no * sizeof(int *));
+ s->rev.nnrev = NULL;
+ }
+
+ if (di > 1 && s->rev.rev_valid) {
+ rev_struct *rsi, **rsp;
+ size_t ram_portion = g_avail_ram;
+
+ /* Remove it from the linked list */
+ for (rsp = &g_rev_instances; *rsp != NULL; rsp = &((*rsp)->next)) {
+ if (*rsp == &s->rev) {
+ *rsp = (*rsp)->next;
+ break;
+ }
+ }
+
+ /* Aportion the memory */
+ g_no_rev_cache_instances--;
+
+ if (g_no_rev_cache_instances > 0) {
+ ram_portion /= g_no_rev_cache_instances;
+ for (rsi = g_rev_instances; rsi != NULL; rsi = rsi->next)
+ rsi->max_sz = ram_portion;
+ if (s->verbose)
+ fprintf(stdout, "%cThere %s %d rev cache instance%s with %lu Mbytes limit\n",
+ cr_char,
+ g_no_rev_cache_instances > 1 ? "are" : "is",
+ g_no_rev_cache_instances,
+ g_no_rev_cache_instances > 1 ? "s" : "",
+ ram_portion/1000000);
+ }
+ }
+
+ s->rev.rev_valid = 0;
+
+ if (s->rev.rev != NULL) {
+ /* Free arrays at grid points, taking care of reference count */
+ for (rpp = s->rev.rev; rpp < (s->rev.rev + s->rev.no); rpp++) {
+ if ((rp = *rpp) != NULL && --rp[2] <= 0) {
+ DECSZ(s, rp[0] * sizeof(int));
+ free(*rpp);
+ *rpp = NULL;
+ }
+ }
+ free(s->rev.rev);
+ DECSZ(s, s->rev.no * sizeof(int *));
+ s->rev.rev = NULL;
+ }
+
+ /* If first section has been initialised */
+ if (s->rev.inited != 0) {
+
+ /* Sub-simplex information */
+ for (e = 0; e <= di; e++) {
+ rspl_free_ssimplex_info(s, &s->rev.sspxi[e]);
+ }
+ s->rev.res = 0;
+ s->rev.no = 0;
+ s->rev.inited = 0;
+ }
+ DBG(("rev allocation left after free = %d bytes\n",s->rev.sz));
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+#ifdef NEVER /* Test code */
+/* Reverse closest find using exaustive pseudo hilbert search */
+static void debug_find_closest_rev(
+rspl *s,
+double *out,
+double *in
+) {
+ double best = 1e38;
+ int e, f;
+ rpsh counter; /* Pseudo-hilbert counter */
+ int gc[MXDI]; /* Grid index value */
+ double iv[MXDI];
+ float *gp; /* Pointer to grid data */
+
+ rpsh_init(&counter, s->di, (unsigned int *)s->g.res, gc); /* Initialise counter */
+ for (;;) {
+ double dist;
+
+ /* Compute grid pointer and input sample values */
+ gp = s->g.a; /* Base of grid data */
+ for (e = 0; e < s->di; e++) { /* Input tables */
+ gp += s->g.fci[e] * gc[e]; /* Grid value pointer */
+ iv[e] = s->g.l[e] + gc[e] * s->g.w[e]; /* Input sample values */
+ }
+
+ dist = 0.0;
+ for (f = 0; f < s->fdi; f++) {
+ double tt = in[f] - (double)gp[f];
+ dist += tt * tt;
+ }
+ if (dist < best) {
+ best = dist;
+ for (e = 0; e < s->di; e++)
+ out[e] = iv[e];
+ }
+
+ /* Increment counter */
+ if (rpsh_inc(&counter, gc))
+ break;
+ }
+}
+#endif /* NEVER */
+
+/* ========================================================== */
+/* reverse lookup acceleration structure initialisation code */
+
+/* The reverse lookup relies on a search of the fwd interpolation tables. */
+/* To eliminate out of gamut points quickly, to provide a starting point for */
+/* the search, and to guarantee that all possible reverse solutions are discovered, */
+/* a spatial indexing structure is used to provide a list of starting candidate */
+/* forward indexes for a given output value. (rev.rev[]) */
+/* The reverse structure contains an fdi dimensional cell grid, each element of the */
+/* cell grid holding the indexes of the forward interpolation grid, which intersect */
+/* that ranges of output values. A reverse cell will be empty if there is no */
+/* potential exact solution. */
+/* Note that unlike the forward grid which is composed of verticies, */
+/* this grid is composed of cells (there is an extra row allocated */
+/* during construction using verticies, that are not used when converted. */
+/* to cells) */
+/* For accelleration of the nearest lookup, a parallel reverse grid is */
+/* constructed that holds lists of forward grid cells that may hold the */
+/* nearest point within the gamut. These lists may be empty if we are within */
+/* gamut - ie. the rev.nnrev[] array is the complement of the rev.rev[] array. */
+/* During construction of rev.nnrev[], it is initially filled with lists for */
+/* the potential nearest cell list for each vertex (hence the extra rows allocated */
+/* for rev[] and nnrev[]), and these are then merged down to form the list */
+/* for each cell extent. The nnrev[] array is filled using a seed fill algorithm, */
+/* starting from the edges of the filled cells in rev[]. */
+/* Since many of the cells map to the same surface region, many of the fwd cell lists */
+/* are shared. */
+
+/* NOTE: that the nnrev accuracy doesn't seem as good as fill_nncell() ! */
+/* Could we fix this with better geometry calculations ??? */
+
+/* rev.nnrev[] cache entry record */
+struct _nncache{
+ int min[MXRO]; /* bwd vertex extent covered by this list */
+ int max[MXRO];
+ int *rip; /* Fwd cell list */
+ struct _nncache *next; /* Link list for this cache key */
+}; typedef struct _nncache nncache;
+
+/* Structure to hold prime seed vertex information */
+struct _primevx{
+ int ix; /* Index of prime seed */
+ int gc[MXRO]; /* coordinate of the prime seed vertex */
+ struct _primevx *next; /* Linked list for final conversion */
+ int *clist; /* Cell list generated for prime cell */
+}; typedef struct _primevx primevx;
+
+/* Structure to hold temporary nn reverse vertex propogation information */
+struct _propvx{
+ int ix; /* Index of this secondary seed */
+ int gc[MXRO]; /* coordinate of this secondary seed */
+ int cix; /* Index of the closest surface nnrev vertex */
+ double dsq; /* Distance to the closest point squared */
+ int pass; /* Propogation pass */
+ struct _propvx *next; /* Linked list for next seeds */
+}; typedef struct _propvx propvx;
+
+/* Initialise the rev Second section acceleration information. */
+/* This is called when it is discovered on a call that s->rev.rev_valid == 0 */
+static void init_revaccell(
+rspl *s
+) {
+ int i, j; /* Index of fwd grid point */
+ int e, f, ee, ff;
+ int di = s->di;
+ int fdi = s->fdi;
+ int gno = s->g.no;
+ int rgno = s->rev.no;
+ int argres = s->rev.ares; /* Allocation rgres, = no bwd cells +1 */
+ int rgres = s->rev.res; /* no bwd cells */
+ int rgres_1 = rgres-1; /* rgres -1 == maximum base coord value */
+
+ schbase *b = s->rev.sb; /* Base search information */
+ char *vflag = NULL; /* Per vertex flag used during construction of nnrev */
+ float *gp; /* Pointer to fwd grid points */
+ primevx *plist = NULL, *ptail = NULL; /* Prime seed list for last pass */
+ propvx *alist = NULL; /* Linked list of active secondary seeds */
+ propvx *nlist = NULL; /* Linked list of next secondary seeds */
+ DCOUNT(gg, MXRO, fdi, 0, 0, argres);/* Track the prime seed coordinate */
+ DCOUNT(cc, MXRO, fdi, -1, -1, 2); /* Neighborhood offset counter */
+ int nn[MXRO]; /* Neighbor coordinate */
+ int pass = 0; /* Propogation pass */
+ int nncsize; /* Size of the rev.nnrev construction cache index */
+ nncache **nnc; /* nn cache index, used during construction of nnrev */
+ unsigned hashk; /* Hash key */
+ nncache *ncp; /* Hash entry pointer */
+ int nskcells = 0; /* Number of skiped cells (debug) */
+#ifdef DEBUG
+ int cellinrevlist = 0;
+ int fwdcells = 0;
+#endif
+
+ DBG(("init_revaccell called, di = %d, fdi = %d, mgres = %d\n",di,fdi,(int)s->g.mres));
+
+ if (!s->rev.fastsetup) {
+ /* Temporary per bwd vertex/cell flag */
+ if ((vflag = (char *) calloc(rgno, sizeof(char))) == NULL)
+ error("rspl malloc failed - rev.vflag points");
+ INCSZ(s, rgno * sizeof(char));
+ }
+
+ /*
+ * The rev[] and nnrev[] grids contain pointers to lists of grid cube base indexes.
+ * If the pointer is NULL, then there are no base indexes in that list.
+ * A non NULL list uses element [0] to indicate the alocation size of the list,
+ * [1] contains the index of the next free location, [2] contains the reference
+ * count (lists may be shared), the list starts at [3]. The last entry is marked with -1.
+ */
+
+ /* We won't include any fwd cells that are over the ink limit, */
+ /* so makes sure that the fwd cell nodes all have an ink limit value. */
+ if (b != NULL && s->limiten) {
+ ECOUNT(gc, MXDIDO, s->di, 0, s->g.res, 0); /* coordinates */
+ double iv[MXDI]; /* Input value corresponding to grid */
+
+ DBG(("Looking up fwd vertex ink limit values\n"));
+ /* Calling the limit function for each fwd vertex could be bad */
+ /* if the limit function is slow. Maybe an octree type algorithm */
+ /* could be used if this is a problem ? */
+ EC_INIT(gc);
+ for (i = 0, gp = s->g.a; i < s->g.no; i++, gp += s->g.pss) {
+ if (gp[-1] == L_UNINIT) {
+ for (e = 0; e < di; e++)
+ iv[e] = s->g.l[e] + gc[e] * s->g.w[e]; /* Input sample values */
+ gp[-1] = (float)(INKSCALE * s->limitf(s->lcntx, iv));
+ }
+ EC_INC(gc);
+ }
+ s->g.limitv_cached = 1;
+ }
+
+ /* We then fill in the in-gamut reverse grid lookups, */
+ /* and identify nnrev prime seed verticies */
+
+ DBG(("filling in rev.rev[] grid\n"));
+
+ /* To create rev.rev[], for all fwd grid points, form the cube with that */
+ /* point at its base, and determine the bounding box of the output values */
+ /* that could intersect that cube. */
+ /* As a start for creating rev.nnrevp[], flag which bwd verticies are */
+ /* covered by the fwd grid output range. */
+ for (gp = s->g.a, i = 0; i < gno; gp += s->g.pss, i++) {
+ datao min, max;
+ int imin[MXRO], imax[MXRO], gc[MXRO];
+ int uil; /* One is under the ink limit */
+
+//printf("~1 i = %d/%d\n",i,gno);
+ /* Skip grid points on the upper edge of the grid, since there */
+ /* is no further grid point to form a cube range with. */
+ for (e = 0; e < di; e++) {
+ if(G_FL(gp, e) == 0) /* At the top edge */
+ break;
+ }
+ if (e < di) { /* Top edge - skip this cube */
+ continue;
+ }
+
+ /* Find the output value bounding box values for this grid cell */
+ uil = 0;
+ for (f = 0; f < fdi; f++) /* Init output min/max */
+ min[f] = max[f] = gp[f];
+ if (b == NULL || !s->limiten || gp[-1] <= s->limitv)
+ uil = 1;
+
+ /* For all other grid points in the cube */
+ for (ee = 1; ee < (1 << di); ee++) {
+ float *gt = gp + s->g.fhi[ee]; /* Pointer to cube vertex */
+
+ if (b == NULL || !s->limiten || gt[-1] <= s->limitv)
+ uil = 1;
+
+ /* Update bounding box for this grid point */
+ for (f = 0; f < fdi; f++) {
+ if (min[f] > gt[f])
+ min[f] = gt[f];
+ if (max[f] < gt[f])
+ max[f] = gt[f];
+ }
+ }
+
+ /* Skip any fwd cells that are over the ink limit */
+ if (!uil) {
+ nskcells++;
+ continue;
+ }
+
+ /* Figure out intersection range in reverse grid */
+ for (f = 0; f < fdi; f++) {
+ double t;
+ int mi;
+ double gw = s->rev.gw[f];
+ double gl = s->rev.gl[f];
+ t = (min[f] - gl)/gw;
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi > rgres_1)
+ mi = rgres_1;
+ imin[f] = mi;
+ t = (max[f] - gl)/gw;
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi > rgres_1)
+ mi = rgres_1;
+ imax[f] = mi;
+ }
+
+//printf("Scanning over grid:\n");
+//for (f = 0; f < fdi; f++)
+//printf("Min[%d] = %d -> Max[%d] = %d\n",f,imin[f],f,imax[f]);
+
+ /* Now create forward index and vector with all the reverse grid cells */
+ for (f = 0; f < fdi; f++)
+ gc[f] = imin[f]; /* init coords */
+
+ for (f = 0; f < fdi;) { /* For all of intersect cube */
+ int **rpp, *rp;
+
+ /* Compute pointer to grid cell */
+ for (rpp = s->rev.rev, f = 0; f < fdi; f++)
+ rpp += gc[f] * s->rev.coi[f];
+ rp = *rpp;
+
+//printf("Currently at grid:\n");
+//for (f = 0; f < fdi; f++)
+//printf("gc[%d] = %d\n",f,gc[f]);
+
+ if (rp == NULL) {
+ if ((rp = (int *) rev_malloc(s, 6 * sizeof(int))) == NULL)
+ error("rspl malloc failed - rev.grid entry");
+ INCSZ(s, 6 * sizeof(int));
+ *rpp = rp;
+ rp[0] = 6; /* Allocation */
+ rp[1] = 4; /* Next free Cell */
+ rp[2] = 1; /* Reference count */
+ rp[3] = i;
+ rp[4] = -1; /* End marker */
+ } else {
+ int z = rp[1], ll = rp[0];
+ if (z >= (ll-1)) { /* Not enough space */
+ INCSZ(s, ll * sizeof(int));
+ ll *= 2;
+ if ((rp = (int *) rev_realloc(s, rp, sizeof(int) * ll)) == NULL)
+ error("rspl realloc failed - rev.grid entry");
+ *rpp = rp;
+ rp[0] = ll;
+ }
+ rp[z++] = i;
+ rp[z] = -1;
+ rp[1] = z;
+ }
+ /* Increment index */
+ for (f = 0; f < fdi; f++) {
+ gc[f]++;
+ if (gc[f] <= imax[f])
+ break; /* No carry */
+ gc[f] = imin[f];
+ }
+ } /* Next reverse grid point in intersecting cube */
+
+ if (s->rev.fastsetup)
+ continue; /* Skip nnrev setup */
+
+
+ /* Now also register which grid points are in-gamut and are part of cells */
+ /* than have a rev.rev[] list. */
+
+ /* Figure out intersection range in reverse nn (construction) vertex grid */
+ /* This range may be empty if a grid isn't stradled by the fwd cell output */
+ /* range. */
+ for (f = 0; f < fdi; f++) {
+ double t;
+ int mi;
+ double gw = s->rev.gw[f];
+ double gl = s->rev.gl[f];
+ t = (min[f] - gl)/gw;
+ mi = (int)ceil(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= argres)
+ mi = rgres;
+ imin[f] = mi;
+ t = (max[f] - gl)/gw;
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= argres)
+ mi = rgres;
+ imax[f] = mi;
+ if (imax[f] < imin[f])
+ break; /* Doesn't straddle any verticies */
+ }
+
+ if (f >= fdi) { /* There are seed verticies to mark */
+
+//printf("~1 marking prime seed vertex %d\n",i);
+
+ /* Mark an initial seed point nnrev vertex, and */
+ /* create a surface point propogation record for it */
+ for (f = 0; f < fdi; f++)
+ gc[f] = imin[f]; /* init coords */
+
+ for (f = 0; f < fdi;) { /* For all of intersect cube */
+ int **rpp, *rp;
+ char *fpp;
+
+ /* Compute pointer to grid cell */
+ for (rpp = s->rev.nnrev, fpp = vflag, f = 0; f < fdi; f++) {
+ int inc = gc[f] * s->rev.coi[f];
+ rpp += inc;
+ fpp += inc;
+ }
+ rp = *rpp;
+
+ *fpp = 3; /* Initial seed point */
+
+ /* Increment index */
+ for (f = 0; f < fdi; f++) {
+ gc[f]++;
+ if (gc[f] <= imax[f])
+ break; /* No carry */
+ gc[f] = imin[f];
+ }
+ }
+ }
+ } /* Next base grid point */
+
+ DBG(("We skipped %d cells that were over the limit\n",nskcells));
+
+ /* Setup the nnrev array if we are not doing a fast setup. */
+ /* (fastsetup will instead fill the nnrev array on demand, */
+ /* using an exaustive search.) */
+ if (!s->rev.fastsetup) {
+
+ /* The next step is to use all the prime seed grid points to set and propogate */
+ /* the index of the closest fwd vertex through the revnn[] array. */
+ /* (This doesn't work perfectly. Sometimes a vertex is not linked to it's closest */
+ /* prime. I'm not sure if this is due to a bug here, or is a quirk of geometry */
+ /* that a prime that is closest to a vertex isn't closest for any of its neighbors.) */
+ DBG(("filling in rev.nnrev[] grid\n"));
+
+ /* For all the primary seed points */
+ DC_INIT(gg);
+ for (i = 0; i < rgno; i++) {
+ int **rpp;
+ primevx *prime= NULL; /* prime cell information structure */
+
+ if (vflag[i] != 3) { /* Not a prime seed point */
+ goto next_seed_point;
+ }
+
+ rpp = s->rev.nnrev + i;
+
+//printf("~1 potential rev.nnrev[] prime seed %d, about to scan neibors\n",i);
+ /* For all the neigbors of this seed */
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ propvx *prop; /* neighor cell propogation structure */
+ int nix = 0; /* Neighbor cell index */
+ char *fpp = vflag;
+ int **nrpp = s->rev.nnrev;
+ double dsq;
+
+ for (f = 0; f < fdi; f++) {
+ nn[f] = gg[f] + cc[f];
+ if (nn[f] < 0 || nn[f] >= argres)
+ break; /* Out of bounds */
+ nix += nn[f] * s->rev.coi[f];
+ }
+ fpp = vflag + nix;
+
+ /* If neighbor out of bounds, or is a prime seed point, skip it */
+ if (f < fdi || *fpp == 3) {
+ goto next_neighbor;
+ }
+
+//printf("~1 identified prime seed %d with neighbor %d\n",i,nix);
+ /* We now know that this prime seed will propogate, */
+ /* so get/create the temporary information record for it */
+ if (prime == NULL) {
+
+ /* If this prime seed hasn't be setup before */
+ if (*rpp != NULL) {
+ prime = *((primevx **)rpp);
+ } else {
+ /* Allocate a primevx if there isn't one */
+ if ((prime = (primevx *) calloc(1, sizeof(primevx))) == NULL)
+ error("rspl malloc failed - rev.nnrev prime info structs");
+ *((primevx **)rpp) = prime;
+ prime->ix = i;
+ for (f = 0; f < fdi; f++)
+ prime->gc[f] = gg[f];
+//if (fdi > 1) printf("~1 setting prime %d, gc = %d, %d, %d\n", i, prime->gc[0], prime->gc[1], prime->gc[2]);
+ }
+ }
+
+ /* Pointer to nnrev vertex neighbor point */
+ nrpp = s->rev.nnrev + nix;
+
+ /* Compute the distance squared from this prime seed to this neighbor */
+ for (dsq = 0.0, f = 0; f < fdi; f++) {
+ double tt = (gg[f] - nn[f]) * s->rev.gw[f];
+ dsq += tt * tt;
+ }
+
+ /* Get or allocate a prop structure for it */
+ if (*nrpp != NULL) {
+ prop = *((propvx **)nrpp);
+ if ((dsq + 1e-6) < prop->dsq) { /* This prime is closer than previous */
+ prop->cix = i; /* The index of the closest prime */
+ prop->dsq = dsq; /* Distance squared to closest prime */
+ }
+ } else {
+ if ((prop = (propvx *) calloc(1, sizeof(propvx))) == NULL)
+ error("rspl malloc failed - rev.nnrev propogation structs");
+ *((propvx **)nrpp) = prop;
+ prop->ix = nix;
+ for (f = 0; f < fdi; f++)
+ prop->gc[f] = nn[f]; /* This neighbors coord */
+ prop->cix = i;
+ prop->dsq = dsq;
+ prop->pass = pass;
+ prop->next = nlist; /* Add new seed to list of next seeds */
+ nlist = prop;
+ *fpp = 1;
+
+ }
+ next_neighbor:;
+ DC_INC(cc);
+ }
+
+ next_seed_point:;
+ DC_INC(gg);
+ }
+
+//printf("~1 about to propogate secondary seeds\n");
+ /* Now we propogate the secondary seed points until there are no more left */
+ while(nlist != NULL) {
+ propvx *next;
+ propvx *tlp;
+
+ if ((pass += 2) < 0)
+ error("Assert rev: excessive propogation passes");
+//printf("~1 about to do a round of propogation pass %d\n",(pass+2)/2);
+
+ /* Mark all seed points on the current list with pass-1 */
+ for (tlp = nlist; tlp != NULL; tlp = tlp->next) {
+ *(vflag + tlp->ix) = 2;
+ tlp->pass = pass-1;
+ }
+
+ /* Go through each secondary seed in the active list, propogating them */
+ for (alist = nlist, nlist = NULL; alist != NULL; alist = next) {
+ int **rpp;
+ primevx *prime= NULL; /* prime cell information structure */
+
+ next = alist->next; /* Next unless we re-insert one */
+
+ /* Grab this seed points coodinate and index */
+ for (i = f = 0; f < fdi; f++) {
+ gg[f] = alist->gc[f];
+ i += gg[f] * s->rev.coi[f];
+ }
+
+//printf("\n~1 propogating from seed %d\n",i);
+ /* rpp = s->rev.nnrev + i; */
+
+ /* Grab the corresponding prime seed information record */
+ prime = *((primevx **)(s->rev.nnrev + alist->cix));
+
+ /* For all the neigbors of this seed */
+ DC_INIT(cc);
+ while (!DC_DONE(cc)) {
+ propvx *prop; /* neighor cell propogation structure */
+ int nix; /* Neighbor cell index */
+ char *fpp = vflag;
+ int **nrpp = s->rev.nnrev;
+ double dsq;
+
+ for (nix = f = 0; f < fdi; f++) {
+ nn[f] = gg[f] + cc[f];
+ if (nn[f] < 0 || nn[f] >= argres)
+ break; /* Out of bounds */
+ nix += nn[f] * s->rev.coi[f];
+ }
+ fpp = vflag + nix;
+//printf("~1 neighbor ix %d, flag %d\n",nix,*fpp);
+
+ /* If neighbor out of bounds, current vertex or prime, skip it */
+ if (f < fdi || i == nix || *fpp >= 3) {
+//printf("~1 skipping neighbour %d\n",nix);
+ goto next_neighbor2;
+ }
+
+ /* Pointer to nnrev vertex neighbor point */
+ nrpp = s->rev.nnrev + nix;
+
+ /* Compute the distance squared from the prime seed to this neighbor */
+ for (dsq = 0.0, f = 0; f < fdi; f++) {
+ double tt = (prime->gc[f] - nn[f]) * s->rev.gw[f];
+ dsq += tt * tt;
+ }
+
+ /* Get or allocate a prop structure for it */
+ if (*nrpp != NULL) {
+ prop = *((propvx **)nrpp);
+//if (prop->ix != nix) error ("Assert: prop index %d doesn't match index %d",prop->ix, nix);
+
+ if ((dsq + 1e-6) < prop->dsq) { /* This prime is closer than previous */
+//printf("~1 updating %d to prime %d, dsq = %f from %f\n",nix, prime->ix, dsq, prop->dsq);
+ prop->cix = prime->ix; /* The index of the new closest prime */
+ prop->dsq = dsq; /* Distance squared to closest prime */
+ /* If this is a vertex from previous pass that has changed, */
+ /* and it's not ahead of us in the current list, */
+ /* put it next on the current list. */
+ if (*fpp == 2 && prop->pass != (pass-1)) {
+//printf("~1 re-shedule %d (%d) for next propogate\n",nix,prop->ix);
+//if (next == NULL)
+//printf("Before insert, next = NULL\n");
+//else
+//printf("Before insert, next = %d\n",next->ix);
+ prop->pass = pass-1; /* Re-shedule once only */
+ prop->next = next;
+ next = prop;
+ }
+ }
+ } else {
+ if ((prop = (propvx *) calloc(1, sizeof(propvx))) == NULL)
+ error("rspl malloc failed - rev.nnrev propogation structs");
+ *((propvx **)nrpp) = prop;
+ prop->ix = nix;
+ for (f = 0; f < fdi; f++)
+ prop->gc[f] = nn[f]; /* This neighbors coord */
+ prop->cix = prime->ix;
+ prop->dsq = dsq;
+//printf("~1 propogating to new, %d, dsq = %f, prime %d\n",nix, dsq, prime->ix);
+ prop->pass = pass;
+ prop->next = nlist; /* Add new seed to list of next seeds */
+ nlist = prop;
+ *fpp = 1;
+ }
+
+ next_neighbor2:;
+ DC_INC(cc);
+ }
+ alist->pass = pass;
+ }
+ }
+
+#ifdef DEBUG
+ DBG(("checking that every vertex is now touched\n"));
+ for (i = 0; i < rgno; i++) {
+ if (vflag[i] < 2) {
+ printf("~1 problem: vertex %d flag = %d\n",i, vflag[i]);
+ }
+ if (vflag[i] == 2 && *(s->rev.nnrev + i) == NULL) {
+ printf("~1 problem: vertex %d flag = %d and struct = NULL\n",i, vflag[i]);
+ }
+ }
+#endif /* DEBUG */
+
+#ifdef NEVER /* Check that all cells are closest to their primes than any other */
+DC_INIT(gg);
+for (i = 0; i < rgno; i++) { /* For all the verticies */
+ if (vflag[i] == 2) {
+ propvx *prop = (propvx *) *(s->rev.nnrev + i);
+ for (j = 0; j < rgno; j++) { /* For all the primes */
+ if (vflag[j] == 3) {
+ primevx *prime = (primevx *) *(s->rev.nnrev + j);
+ double dsq;
+ if (prime == NULL)
+ continue;
+ for (dsq = 0.0, f = 0; f < fdi; f++) {
+ double tt = (prime->gc[f] - prop->gc[f]) * s->rev.gw[f];
+ dsq += tt * tt;
+ }
+ if ((dsq + 1e-6) < prop->dsq) {
+ warning("~1 vertex %d prime %d, dsq = %f, is closer to prime %d, dsq %f\n", i,prop->cix, prop->dsq, j, dsq);
+ /* See if any of the neighbors have the closer prime */
+ DC_INIT(cc); /* For all the neigbors of this seed */
+ while (!DC_DONE(cc)) {
+ propvx *nprop; /* neighor cell propogation structure */
+ int nix; /* Neighbor cell index */
+ char *fpp = vflag;
+ int **nrpp = s->rev.nnrev;
+ double dsq;
+
+ for (nix = f = 0; f < fdi; f++) {
+ nn[f] = gg[f] + cc[f];
+ if (nn[f] < 0 || nn[f] >= argres)
+ break; /* Out of bounds */
+ nix += nn[f] * s->rev.coi[f];
+ }
+ fpp = vflag + nix;
+//printf("~1 neighbor ix %d, flag %d\n",nix,*fpp);
+
+ /* If neighbor out of bounds, current vertex or prime, skip it */
+ if (f < fdi || i == nix || *fpp != 2) {
+//printf("~1 skipping neighbour %d\n",nix);
+ goto next_neighbor3;
+ }
+
+ /* Pointer to nnrev vertex neighbor point */
+ nrpp = s->rev.nnrev + nix;
+ if ((nprop = *((propvx **)nrpp)) != NULL) {
+//printf("~1 neighbor %d %d %d has prime %d dsq %f\n",cc[0],cc[1],cc[2],nprop->cix,nprop->dsq);
+ if (nprop->cix == j) {
+//warning("~1 but neighbor has this prime point!\n");
+
+ }
+ }
+ next_neighbor3:;
+ DC_INC(cc);
+ }
+// prop->cix = j; /* Fix it and see what happens */
+// prop->dsq = dsq;
+ }
+ }
+ }
+ }
+ DC_INC(gg);
+}
+
+#endif /* NEVER */
+
+
+ DBG(("about to do convert vertex values to cell lists\n"));
+ /* Setup a cache for the fwd cell lists created, so that we can */
+ /* avoid the list creation and memory allocation for identical lists */
+ nncsize = s->rev.ares * s->rev.ares;
+ if ((nnc = (nncache **) calloc(nncsize, sizeof(nncache *))) == NULL)
+ error("rspl malloc failed - rev.nnc cache entries");
+
+ /* Now convert the nnrev secondary vertex points to pointers to fwd cell lists */
+ /* Do this in order, so that we don't need the verticies after */
+ /* they are converted to cell lists. */
+ DC_INIT(gg);
+ for (i = 0; i < rgno; i++) {
+ int **rpp, *rp;
+ propvx *prop = NULL; /* vertex information structure */
+ primevx *prime= NULL; /* prime cell information structure */
+ int imin[MXRO], imax[MXRO]; /* Prime vertex range for each axis */
+ double rmin[MXRO], rmax[MXRO]; /* Float prime vertex value range */
+ unsigned int tcount; /* grid touch count for this opperation */
+ datao min, max; /* Fwd cell output range */
+ int lpix; /* Last prime index seen */
+
+//if (fdi > 1) printf("~1 converting vertex %d\n",i);
+//if (fdi > 1) printf("~1 coord %d %d %d\n",gg[0],gg[1],gg[2]);
+
+ rpp = s->rev.nnrev + i;
+ if (vflag[i] == 3) { /* Cell base is prime */
+ prime = (primevx *) *rpp;
+
+ if (prime != NULL) { /* It's a propogating prime */
+ /* Add prime to the end of the ptime linked list */
+ prime->next = NULL;
+ if (plist == NULL) {
+ plist = ptail = prime;
+ } else {
+ ptail->next = prime;
+ ptail = prime;
+ }
+ }
+ } else if (vflag[i] == 2) { /* Cell base is secondary */
+ prop = (propvx *)*rpp;
+ } else { /* Hmm */
+ /* This seems to happen if the space explored is not really 3D ? */
+ if (s->rev.primsecwarn == 0) {
+ warning("rev: bwd vertex %d is not prime or secondary (vflag = %d)"
+ "(Check that your measurement data is sane!)",i,vflag[i]);
+ s->rev.primsecwarn = 1;
+ }
+ fill_nncell(s, gg, i); /* Is this valid to do ?? */
+ continue;
+ }
+
+ /* Setup to scan of cube corners, and check that base is within cube grid */
+ for (f = 0; f < fdi; f++) {
+ if (gg[f] > rgres_1) { /* Vertex outside bwd cell range, */
+ if (prop != NULL && prime == NULL) {
+ free(prop);
+ *rpp = NULL;
+ }
+//printf("~1 done vertex %d because its out of cell bounds\n",i);
+ goto next_vertex;
+ }
+ imin[f] = 0x7fffffff;
+ imax[f] = -1;
+ }
+
+ /* For all the vertex points in the nnrev cube starting at this base (i), */
+ /* Check if any of them are secondary seed points */
+ for (ff = 0; ff < (1 << fdi); ff++) {
+ if (vflag[i + s->rev.hoi[ff]] == 2)
+ break;
+ }
+
+ /* If not a cell that we want to create a nearest fwd cell list for */
+ if (ff >= (1 << fdi)) {
+ /* Don't free anything, because we leave a prime in place, */
+ /* and it can't be a prop. */
+ goto next_vertex;
+ }
+
+ /* For all the vertex points in the nnrev cube starting at this base (i), */
+ /* accumulate the range they cover */
+ lpix = -1;
+ for (f = 0; f < fdi; f++) {
+ imin[f] = 0x7fffffff;
+ imax[f] = -1;
+ }
+ for (ff = 0; ff < (1 << fdi); ff++) {
+ int ii = i + s->rev.hoi[ff]; /* cube vertex index */
+ primevx *tprime= NULL;
+
+ /* Grab vertex info and corresponding prime vertex info */
+ if (vflag[ii] == 3) { /* Corner is a prime */
+ tprime = (primevx *) *(s->rev.nnrev + ii); /* Use itself */
+ if (tprime == NULL)
+ continue; /* Not a propogated in-gamut vertex */
+ } else if (vflag[ii] == 2) {
+ propvx *tprop = (propvx *) *(s->rev.nnrev + ii); /* Use propogated prime */
+ tprime = (primevx *) *(s->rev.nnrev + tprop->cix);
+ } else {
+ continue; /* Hmm */
+ }
+ if (tprime->ix == lpix)
+ continue; /* Don't waste time */
+
+//if (fdi > 1) printf("~1 corner %d, ix %d, prime %d gc = %d, %d, %d\n", ff, ii, tprime->ix, tprime->gc[0], tprime->gc[1], tprime->gc[2]);
+
+ /* Update bounding box for this prime grid point */
+ for (f = 0; f < fdi; f++) {
+ if (tprime->gc[f] < imin[f])
+ imin[f] = tprime->gc[f];
+ if (tprime->gc[f] > imax[f])
+ imax[f] = tprime->gc[f];
+ }
+ lpix = tprime->ix;
+ }
+
+//if (fdi > 1) printf("~1 prime vertex index range = %d - %d, %d - %d, %d - %d\n", imin[0], imax[0], imin[1], imax[1], imin[2], imax[2]);
+
+ /* See if a list matching this range is in the cache */
+ hashk = 0;
+ for (hashk = f = 0; f < fdi; f++)
+ hashk = hashk * 97 + imin[f] + 43 * (imax[f] - imin[f]);
+ hashk = hashk % nncsize;
+//if (fdi > 1) printf("~1 hashk = %d from %d - %d %d - %d %d - %d\n", hashk, imin[0], imax[0], imin[1], imax[1], imin[2], imax[2]);
+
+ /* See if we can locate an existing list for this range */
+ for (ncp = nnc[hashk]; ncp != NULL; ncp = ncp->next) {
+//if (fdi > 1) printf("~1 checking %d - %d %d - %d %d - %d\n", ncp->min[0], ncp->max[0], ncp->min[1], ncp->max[1], ncp->min[2], ncp->max[2]);
+ for (f = 0; f < fdi; f++) {
+ if (ncp->min[f] != imin[f]
+ || ncp->max[f] != imax[f]) {
+//if (fdi > 1) printf("~1 not a match\n");
+ break;
+ }
+ }
+ if (f >= fdi) {
+//if (fdi > 1) printf("~1 got a match\n");
+ break; /* Found a matching cache entry */
+ }
+ }
+
+ if (ncp != NULL) {
+ rp = ncp->rip;
+//if (fdi > 1) printf("~1 got cache hit hashk = %d, with ref count %d\n\n",hashk, rp[1]);
+ rp[2]++; /* Increase reference count */
+
+ } else {
+ /* This section seems to be the most time consuming part of the nnrev setup. */
+
+ /* Allocate a cache entry and place it */
+ if ((ncp = (nncache *)calloc(1, sizeof(nncache))) == NULL)
+ error("rspl malloc failed - rev.nn cach record");
+
+ for (f = 0; f < fdi; f++) {
+ ncp->min[f] = imin[f];
+ ncp->max[f] = imax[f];
+ }
+ ncp->next = nnc[hashk];
+ nnc[hashk] = ncp;
+
+ /* Convert the nn destination vertex range into an output value range. */
+ for (f = 0; f < fdi; f++) {
+ double gw = s->rev.gw[f];
+ double gl = s->rev.gl[f];
+ rmin[f] = gl + imin[f] * gw;
+ rmax[f] = gl + imax[f] * gw;
+ }
+
+ /* Do any adjustment of the range needed to acount for the inacuracies */
+ /* caused by the vertex quantization. */
+ /* (I don't really understand the need for the extra avggw expansion, */
+ /* but there are artefacts without this. This size of this sampling */
+ /* expansion has a great effect on the performance.) */
+ {
+ double avggw = 0.0;
+ for (f = 0; f < fdi; f++)
+ avggw += s->rev.gw[f];
+ avggw /= (double)fdi;
+ for (f = 0; f < fdi; f++) { /* Quantizing range plus extra */
+ double gw = s->rev.gw[f];
+ rmin[f] -= (0.5 * gw + 0.99 * avggw);
+ rmax[f] += (0.5 * gw + 0.99 * avggw);
+ }
+ }
+//if (fdi > 1) printf("~1 prime vertex value adjusted range = %f - %f, %f - %f, %f - %fn", rmin[0], rmax[0], rmin[1], rmax[1], rmin[2], rmax[2]);
+
+ /* computue the rev.rev cell grid range we will need to cover to */
+ /* get all the cell output ranges that could touch our nn reverse range */
+ for (f = 0; f < fdi; f++) {
+ double gw = s->rev.gw[f];
+ double gl = s->rev.gl[f];
+ imin[f] = (int)floor((rmin[f] - gl)/gw);
+ if (imin[f] < 0)
+ imin[f] = 0;
+ else if (imin[f] > rgres_1)
+ imin[f] = rgres_1;
+ imax[f] = (int)floor((rmax[f] - gl)/gw);
+ if (imax[f] < 0)
+ imax[f] = 0;
+ else if (imax[f] > rgres_1)
+ imax[f] = rgres_1;
+ cc[f] = imin[f]; /* Set grid starting value */
+ }
+ tcount = s->get_next_touch(s); /* Get next grid touched generation count */
+
+//if (fdi > 1) printf("~1 Cells to scan = %d - %d, %d - %d, %d - %d\n", imin[0], imax[0], imin[1], imax[1], imin[2], imax[2]);
+
+ rp = NULL; /* We always allocate a new list initially */
+ for (f = 0; f < fdi;) { /* For all the cells in the min/max range */
+ int ii;
+ int **nrpp, *nrp; /* Pointer to base of cell list, entry 0 = allocated space */
+
+ /* Get pointer to rev.rev[] cell list */
+ for (nrpp = s->rev.rev, f = 0; f < fdi; f++)
+ nrpp += cc[f] * s->rev.coi[f];
+
+ if ((nrp = *nrpp) == NULL)
+ goto next_range_list; /* This rev.rev[] cell is empty */
+
+
+//if (fdi > 1) printf("~1 adding list from cell %d, list length %d\n",nrpp - s->rev.rev, nrp[0]);
+ /* For all the fwd cells in the rev.rev[] list */
+ for(nrp += 3; *nrp != -1; nrp++) {
+ int ix = *nrp; /* Fwd cell index */
+ float *fcb = s->g.a + ix * s->g.pss; /* Pntr to base float of fwd cell */
+
+ if (TOUCHF(fcb) >= tcount) { /* If we seen visited this fwd cell before */
+//if (fdi > 1) printf("~1 skipping cell %d because we alread have it\n",ix);
+ continue;
+ }
+ TOUCHF(fcb) = tcount; /* Touch it so we will skip it next time */
+
+ /* Compute the range of output values this cell covers */
+ for (f = 0; f < fdi; f++) /* Init output min/max */
+ min[f] = max[f] = fcb[f];
+
+ /* For all other grid points in the fwd cell cube */
+ for (ee = 1; ee < (1 << di); ee++) {
+ float *gt = fcb + s->g.fhi[ee]; /* Pointer to cube vertex */
+
+ /* Update bounding box for this grid point */
+ for (f = 0; f < fdi; f++) {
+ if (min[f] > gt[f])
+ min[f] = gt[f];
+ if (max[f] < gt[f])
+ max[f] = gt[f];
+ }
+ }
+
+//if (fdi > 1) printf("~1 cell %d range = %f - %f, %f - %f, %f - %f\n", ix, min[0], max[0], min[1], max[1], min[2], max[2]);
+
+ /* See if this fwd cell output values overlaps our region of interest */
+ for (f = 0; f < fdi; f++) {
+ if (min[f] > rmax[f]
+ || max[f] < rmin[f]) {
+ break; /* Doesn't overlap */
+ }
+ }
+
+ if (f < fdi) {
+//if (fdi > 1) printf("~1 skipping cell %d because we doesn't overlap\n",ix);
+ continue; /* It doesn't overlap */
+ }
+
+//if (fdi > 1) printf("~1 adding fwd index %d to list\n",ix);
+//if (fdi > 1) printf("~1 cell %d range = %f - %f, %f - %f, %f - %f\n", ix, min[0], max[0], min[1], max[1], min[2], max[2]);
+#ifdef DEBUG
+ fwdcells++;
+#endif
+ /* It does, add it to our new list */
+ if (rp == NULL) {
+ if ((rp = (int *) rev_malloc(s, 6 * sizeof(int))) == NULL)
+ error("rspl malloc failed - rev.nngrid entry");
+ INCSZ(s, 6 * sizeof(int));
+ rp[0] = 6; /* Allocation */
+ rp[1] = 4; /* Next free Cell */
+ rp[2] = 1; /* reference count */
+ rp[3] = ix;
+ rp[4] = -1;
+ } else {
+ int z = rp[1], ll = rp[0];
+ if (z >= (ll-1)) { /* Not enough space */
+ INCSZ(s, ll * sizeof(int));
+ ll *= 2;
+ if ((rp = (int *) rev_realloc(s, rp, sizeof(int) * ll)) == NULL)
+ error("rspl realloc failed - rev.grid entry");
+ rp[0] = ll;
+ }
+ rp[z++] = ix;
+ rp[z] = -1;
+ rp[1] = z;
+ }
+ } /* Next fwd cell in list */
+
+ /* Increment index */
+ next_range_list:;
+ for (f = 0; f < fdi; f++) {
+ if (++cc[f] <= imax[f])
+ break; /* No carry */
+ cc[f] = imin[f];
+ }
+ }
+
+ ncp->rip = rp; /* record nnrev cell in cache */
+#ifdef DEBUG
+ cellinrevlist++;
+#endif
+//if (fdi > 1) printf("~1 adding cache entry with hashk = %d\n\n",hashk);
+ }
+
+ /* Put the resulting list in place */
+ if (prime != NULL)
+ prime->clist = rp; /* Save it untill we get rid of the primes */
+ else
+ *rpp = rp;
+
+//if (*rpp == NULL) printf("~1 problem: we ended up with no list or prime struct at cell %d\n",i);
+
+#ifdef NEVER
+/* Sanity check the list, to see if the list cells corner contain an output value */
+/* that is at least closer to the target than the prime. */
+if (prop != NULL) {
+ int *tp = rp;
+ double bdist = 1e60;
+ double bdist2 = 1e60;
+ double vx[MXRO]; /* Vertex location */
+ double px[MXRO]; /* Prime location */
+ double cl[MXRO]; /* Closest output value from list */
+ double acl[MXRO]; /* Absolute closest output value */
+ double dst; /* Distance to prime */
+ int ti;
+
+ primevx *prm = (primevx *) *(s->rev.nnrev + prop->cix);
+ for (f = 0; f < fdi; f++) {
+ double gw = s->rev.gw[f];
+ double gl = s->rev.gl[f];
+ vx[f] = gl + prop->gc[f] * gw;
+ px[f] = gl + prm->gc[f] * gw;
+ }
+
+ for(tp++; *tp != -1; tp++) {
+ int ix = *tp; /* Fwd cell index */
+ float *fcb = s->g.a + ix * s->g.pss; /* Pntr to base float of fwd cell */
+
+ for (ee = 0; ee < (1 << di); ee++) {
+ double ss;
+ float *gt = fcb + s->g.fhi[ee]; /* Pointer to cube vertex */
+
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = vx[f] - gt[f];
+ ss += tt * tt;
+ }
+ if (ss < bdist) {
+ bdist = ss;
+ for (f = 0; f < fdi; f++)
+ cl[f] = gt[f];
+ }
+ }
+ }
+ bdist = sqrt(bdist);
+ dst = sqrt(prop->dsq);
+
+ /* Lookup best distance to any output value */
+ if (dst < bdist) {
+ float *gt;
+ for (ti = 0, gt = s->g.a; ti < s->g.no; ti++, gt += s->g.pss) {
+ double ss;
+
+ for (ss = 0.0, f = 0; f < fdi; f++) {
+ double tt = vx[f] - gt[f];
+ ss += tt * tt;
+ }
+ if (ss < bdist2) {
+ bdist2 = ss;
+ for (f = 0; f < fdi; f++)
+ acl[f] = gt[f];
+ }
+ }
+ }
+ bdist2 = sqrt(bdist2);
+
+ if (dst < bdist) {
+ printf("~1 vertex %d has worse distance to values than prime\n",i);
+ printf("~1 vertex loc %f %f %f\n", vx[0], vx[1], vx[2]);
+ printf("~1 prime loc %f %f %f, dist %f\n", px[0], px[1], px[2],dst);
+ printf("~1 closest loc %f %f %f, dist %f\n", cl[0], cl[1], cl[2],bdist);
+ printf("~1 abs clst loc %f %f %f, dist %f\n", acl[0], acl[1], acl[2], bdist2);
+ }
+}
+#endif // NEVER
+
+ if (prop != NULL && prime == NULL) {
+ free(prop);
+ }
+
+ next_vertex:;
+ DC_INC(gg);
+ }
+
+ DBG(("freeing up the prime seed structurs\n"));
+ /* Finaly convert all the prime verticies to cell lists */
+ /* Free up all the prime seed structures */
+ for (;plist != NULL; ) {
+ primevx *prime, *next = plist->next;
+ int **rpp;
+
+ rpp = s->rev.nnrev + plist->ix;
+ if ((prime = (primevx *)(*rpp)) != NULL) {
+ if (prime->clist != NULL) /* There is a nn list for this cell */
+ *rpp = prime->clist;
+ else
+ *rpp = NULL;
+ free(prime);
+ } else {
+ error("assert, prime cell %d was empty",plist->ix);
+ }
+ plist = next;
+ }
+
+#ifdef DEBUG
+ DBG(("sanity check that all rev accell cells are filled\n"));
+ DC_INIT(gg);
+ for (i = 0; i < rgno; i++) {
+ for (f = 0; f < fdi; f++) {
+ if (gg[f] > rgres_1) { /* Vertex outside bwd cell range, */
+ goto next_vertex3;
+ }
+ }
+
+ if (*(s->rev.nnrev + i) == NULL
+ && *(s->rev.rev + i) == NULL) {
+// printf("~1 warning, cell %d [ %d %d %d] has a NULL list\n",i, gg[0],gg[1],gg[2]);
+ error("cell %d has a NULL list\n",i);
+ }
+ next_vertex3:;
+ DC_INC(gg);
+ }
+#endif /* DEBUG */
+
+ /* Free up flag array used for construction */
+ if (vflag != NULL) {
+ DECSZ(s, rgno * sizeof(char));
+ free(vflag);
+ }
+
+ /* Free up nn list cache indexing structure used in construction */
+ if (nnc != 0) {
+ for (i = 0; i < nncsize; i++) {
+ nncache *nncp;
+ /* Run through linked list freeing entries */
+ for (ncp = nnc[i]; ncp != NULL; ncp = nncp) {
+ nncp = ncp->next;
+ free(ncp);
+ }
+ }
+ free(nnc);
+ nnc = NULL;
+ }
+ }
+
+ if (s->rev.rev_valid == 0 && di > 1) {
+ rev_struct *rsi;
+ size_t ram_portion = g_avail_ram;
+
+ /* Add into linked list */
+ s->rev.next = g_rev_instances;
+ g_rev_instances = &s->rev;
+
+ /* Aportion the memory, and reduce cache if it is over new limit. */
+ g_no_rev_cache_instances++;
+ ram_portion /= g_no_rev_cache_instances;
+ for (rsi = g_rev_instances; rsi != NULL; rsi = rsi->next) {
+ revcache *rc = rsi->cache;
+
+ rsi->max_sz = ram_portion;
+ while (rc->nunlocked > 0 && rsi->sz > rsi->max_sz) {
+ if (decrease_revcache(rc) == 0)
+ break;
+ }
+//printf("~1 rev instance ram = %d MB\n",rsi->sz/1000000);
+ }
+
+ if (s->verbose)
+ fprintf(stdout, "%cThere %s %d rev cache instance%s with %lu Mbytes limit\n",
+ cr_char,
+ g_no_rev_cache_instances > 1 ? "are" : "is",
+ g_no_rev_cache_instances,
+ g_no_rev_cache_instances > 1 ? "s" : "",
+ ram_portion/1000000);
+ }
+ s->rev.rev_valid = 1;
+
+#ifdef DEBUG
+ if (fdi > 1) printf("%d cells in rev nn list\n",cellinrevlist);
+ if (fdi > 1) printf("%d fwd cells in rev nn list\n",fwdcells);
+ if (cellinrevlist > 1) printf("Avg list size = %f\n",(double)fwdcells/cellinrevlist);
+#endif
+
+ DBG(("init_revaccell finished\n"));
+}
+
+/* Invalidate the reverse acceleration structures (section Two) */
+static void invalidate_revaccell(
+rspl *s /* Pointer to rspl grid */
+) {
+ int e, di = s->di;
+ int **rpp, *rp;
+
+ /* Invalidate the whole rev cache (Third section) */
+ invalidate_revcache(s->rev.cache);
+
+ /* Free up the contents of rev.rev[] and rev.nnrev[] */
+ if (s->rev.rev != NULL) {
+ for (rpp = s->rev.rev; rpp < (s->rev.rev + s->rev.no); rpp++) {
+ if ((rp = *rpp) != NULL && --rp[2] <= 0) {
+ DECSZ(s, rp[0] * sizeof(int));
+ free(*rpp);
+ *rpp = NULL;
+ }
+ }
+ }
+ if (s->rev.nnrev != NULL) {
+ for (rpp = s->rev.nnrev; rpp < (s->rev.nnrev + s->rev.no); rpp++) {
+ if ((rp = *rpp) != NULL && --rp[2] <= 0) {
+ DECSZ(s, rp[0] * sizeof(int));
+ free(*rpp);
+ *rpp = NULL;
+ }
+ }
+ }
+
+ if (di > 1 && s->rev.rev_valid) {
+ rev_struct *rsi, **rsp;
+ size_t ram_portion = g_avail_ram;
+
+ /* Remove it from the linked list */
+ for (rsp = &g_rev_instances; *rsp != NULL; rsp = &((*rsp)->next)) {
+ if (*rsp == &s->rev) {
+ *rsp = (*rsp)->next;
+ break;
+ }
+ }
+
+ /* Aportion the memory */
+ g_no_rev_cache_instances--;
+
+ if (g_no_rev_cache_instances > 0) {
+ ram_portion /= g_no_rev_cache_instances;
+ for (rsi = g_rev_instances; rsi != NULL; rsi = rsi->next)
+ rsi->max_sz = ram_portion;
+ if (s->verbose)
+ fprintf(stdout, "%cThere %s %d rev cache instance%s with %lu Mbytes limit\n",
+ cr_char,
+ g_no_rev_cache_instances > 1 ? "are" : "is",
+ g_no_rev_cache_instances,
+ g_no_rev_cache_instances > 1 ? "s" : "",
+ ram_portion/1000000);
+ }
+ }
+ s->rev.rev_valid = 0;
+}
+
+/* ====================================================== */
+
+/* Initialise the rev First section, basic information that doesn't change */
+/* This is called on initial setup when s->rev.inited == 0 */
+static void make_rev_one(
+rspl *s
+) {
+ int i, j; /* Index of fwd grid point */
+ int e, f, ee, ff;
+ int di = s->di;
+ int fdi = s->fdi;
+ int rgno, gno = s->g.no;
+ int argres; /* Allocation rgres, = no cells +1 */
+ int rgres;
+ int rgres_1; /* rgres -1 == maximum base coord value */
+ datao rgmin, rgmax;
+
+ DBG(("make_rev_one called, di = %d, fdi = %d, mgres = %d\n",di,fdi,(int)s->g.mres));
+
+//printf("~1 nnb = %d\n",nnb);
+
+ s->get_out_range(s, rgmin, rgmax); /* overall output min/max */
+
+ /* Expand out range to encompass declared range */
+ /* The declared range is assumed to be the range over which */
+ /* we may want an reasonably accurate nearest reverse lookup. */
+ for (f = 0; f < fdi; f++) {
+ if ((s->d.vl[f] + s->d.vw[f]) > rgmax[f])
+ rgmax[f] = s->d.vl[f] + s->d.vw[f];
+ if (s->d.vl[f] < rgmin[f])
+ rgmin[f] = s->d.vl[f];
+ }
+
+ /* Expand out range slightly to allow for out of gamut points */
+ for (f = 0; f < fdi; f++) {
+ double del = (rgmax[f] - rgmin[f]) * 0.10; /* Expand by +/- 10% */
+ rgmax[f] += del;
+ rgmin[f] -= del;
+ }
+//printf("~~got output range\n");
+
+ /* Heuristic - reverse grid acceleration resolution ? */
+ /* Should this really be adapted to be constant in output space ? */
+ /* (ie. make the gw aprox equal ?) Would complicate code rev accell */
+ /* indexing though. */
+ {
+ char *ev;
+ double gresmul = REV_ACC_GRES_MUL; /* Typically 2.0 */
+
+ if ((gresmul * s->g.mres) > (double)REV_ACC_GRES_LIMIT) {
+ gresmul = (double)REV_ACC_GRES_LIMIT/s->g.mres; /* Limit target res to typ. 43. */
+ }
+
+ /* Allow the user to override if it causes memory consumption problems */
+ /* or to speed things up if more memory is available */
+ if ((ev = getenv("ARGYLL_REV_ACC_GRID_RES_MULT")) != NULL) {
+ double mm;
+ mm = atof(ev);
+ if (mm > 0.1 && mm < 20.0)
+ gresmul *= mm;
+ }
+ /* Less than 4 is not functional */
+ if ((rgres = (int) gresmul * s->g.mres) < 4)
+ rgres = 4;
+ }
+ argres = rgres+1;
+ s->rev.ares = argres; /* == number of verticies per side, used for construction */
+ s->rev.res = rgres; /* == number of cells per side */
+ rgres_1 = rgres-1;
+
+ /* Number of elements in the rev.grid, including construction extra rows */
+ for (rgno = 1, f = 0; f < fdi; f++, rgno *= argres);
+ s->rev.no = rgno;
+
+//printf("~1 argres = %d\n",argres);
+ /* Compute coordinate increments */
+ s->rev.coi[0] = 1;
+//printf("~1 coi[0] = %d\n",s->rev.coi[0]);
+ for (f = 1; f < fdi; f++) {
+ s->rev.coi[f] = s->rev.coi[f-1] * argres;
+//printf("~1 coi[%d] = %d\n",f,s->rev.coi[f]);
+ }
+
+ /* Compute index offsets from base of cube to other corners. */
+
+ for (s->rev.hoi[0] = f = 0, j = 1; f < fdi; j *= 2, f++) {
+ for (i = 0; i < j; i++)
+ s->rev.hoi[j+i] = s->rev.hoi[i] + s->rev.coi[f]; /* In grid points */
+ }
+//for (ff = 0; ff < (1 << fdi); ff++)
+//printf("~1 hoi[%d] = %d\n",ff,s->rev.hoi[ff]);
+
+ /* Conversion from output value to cell indexes */
+ for (f = 0; f < fdi; f++) {
+ s->rev.gl[f] = rgmin[f];
+ s->rev.gh[f] = rgmax[f];
+ s->rev.gw[f] = (rgmax[f] - rgmin[f])/(double)rgres;
+ }
+
+ if ((s->rev.rev = (int **) rev_calloc(s, rgno, sizeof(int *))) == NULL)
+ error("rspl malloc failed - rev.grid points");
+ INCSZ(s, rgno * sizeof(int *));
+
+ if ((s->rev.nnrev = (int **) rev_calloc(s, rgno, sizeof(int *))) == NULL)
+ error("rspl malloc failed - rev.nngrid points");
+ INCSZ(s, rgno * sizeof(int *));
+
+ s->rev.inited = 1;
+
+ s->rev.stouch = 1;
+
+ DBG(("make_rev_one finished\n"));
+}
+
+/* ====================================================== */
+
+/* First section of rev_struct init. */
+/* Initialise the reverse cell cache, sub simplex information */
+/* and reverse lookup acceleration structures. */
+/* This is called by a reverse interpolation call */
+/* that discovers that the reverse index list haven't */
+/* been initialised. */
+static void make_rev(
+rspl *s
+) {
+ int e, di = s->di;
+ char *ev;
+ size_t avail_ram = 256 * 1024 * 1024; /* Default assumed RAM in the system */
+ size_t ram1, ram2; /* First Gig and rest */
+ static int repsr = 0; /* Have we reported system RAM size ? */
+ size_t max_vmem = 0;
+
+ DBG(("make_rev called, di = %d, fdi = %d, mgres = %d\n",di,s->fdi,(int)s->g.mres));
+
+ /* Figure out how much RAM we can use for the rev cache. */
+ /* (We compute this for each rev instance, to account for any VM */
+ /* limit changes due to intervening allocations) */
+ if (di > 1 || g_avail_ram == 0) {
+ #ifdef NT
+ {
+ BOOL (WINAPI* pGlobalMemoryStatusEx)(MEMORYSTATUSEX *) = NULL;
+ MEMORYSTATUSEX mstat;
+
+ pGlobalMemoryStatusEx = (BOOL (WINAPI*)(MEMORYSTATUSEX *))
+ GetProcAddress(LoadLibrary("KERNEL32"), "GlobalMemoryStatusEx");
+
+ if (pGlobalMemoryStatusEx == NULL)
+ error("Unable to link to GlobalMemoryStatusEx()");
+ mstat.dwLength = sizeof(MEMORYSTATUSEX);
+ if ((*pGlobalMemoryStatusEx)(&mstat) != 0) {
+ if (sizeof(avail_ram) < 8 && mstat.ullTotalPhys > 0xffffffffL)
+ mstat.ullTotalPhys = 0xffffffffL;
+ avail_ram = mstat.ullTotalPhys;
+ } else {
+ warning("%cWarning - Unable to get system memory size",cr_char);
+ }
+ }
+ #else
+ #ifdef __APPLE__
+ {
+ long long memsize;
+ size_t memsize_sz = sizeof(long long);
+ if (sysctlbyname("hw.memsize", &memsize, &memsize_sz, NULL, 0) == 0) {
+ if (sizeof(avail_ram) < 8 && memsize > 0xffffffffL)
+ memsize = 0xffffffff;
+ avail_ram = memsize;
+ } else {
+ warning("%cWarning - Unable to get system memory size",cr_char);
+ }
+
+ }
+ #else /* Linux */
+ {
+ long long total;
+ total = (long long)sysconf(_SC_PAGESIZE) * (long long)sysconf(_SC_PHYS_PAGES);
+ if (sizeof(avail_ram) < 8 && total > 0xffffffffL)
+ total = 0xffffffffL;
+ avail_ram = total;
+ }
+ #endif
+ #endif
+ DBG(("System RAM = %d Mbytes\n",avail_ram/1000000));
+
+ /* Make it sane */
+ if (avail_ram < (256 * 1024 * 1024)) {
+ warning("%cWarning - System RAM size seems very small (%d MBytes),"
+ " assuming 256Mb instead",cr_char,avail_ram/1000000);
+ avail_ram = 256 * 1024 * 1024;
+ }
+ // avail_ram = -1; /* Fake 4GB of RAM. This will swap! */
+
+ ram1 = avail_ram;
+ ram2 = 0;
+ if (ram1 > (1024 * 1024 * 1024)) {
+ ram1 = 1024 * 1024 * 1024;
+ ram2 = avail_ram - ram1;
+ }
+
+ /* Default maximum reverse memory (typically 50% of the first Gig, 75% of the rest) */
+ g_avail_ram = (size_t)(REV_MAX_MEM_RATIO * ram1
+ + REV_MAX_MEM_RATIO2 * ram2);
+
+ /* Many 32 bit systems have a virtual memory limit, so we'd better stay under it. */
+ /* This is slightly dodgy though, since we don't know how much memory other */
+ /* software will need to malloc. A more sophisticated approach would be to */
+ /* replace all malloc/calloc/realloc calls in the exe with a version that on failure, */
+ /* sets the current memory usage as the new limit, and then */
+ /* frees up some rev cache space before re-trying. This is a non-trivial change */
+ /* to the source code though, and really has to include all user mode */
+ /* libraries we're linked to, making implementation problematic. */
+ /* Instead we do a simple test to see what the maximum allocation is, and */
+ /* then use 75% of that for cache, and free cache and retry if */
+ /* malloc failes in rev.c. Too bad if 25% isn't enough, and a malloc fails */
+ /* outside rev.c... */
+ if (sizeof(avail_ram) < 8) {
+ char *alocs[4 * 1024];
+ size_t safe_max_vmem = 0;
+ int i;
+
+#ifdef __APPLE__
+ int old_stderr, new_stderr;
+
+ /* OS X malloc() blabs about a malloc failure. This */
+ /* will confuse users, so we temporarily redirect stdout */
+ fflush(stderr);
+ old_stderr = dup(fileno(stderr));
+ new_stderr = open("/dev/null", O_WRONLY | O_APPEND);
+ dup2(new_stderr, fileno(stderr));
+#endif
+ for (i = 0; (i < 4 * 1024);i++) {
+ if ((alocs[i] = malloc(1024 * 1024)) == NULL) {
+ break;
+ }
+ max_vmem = (i+1) * 1024 * 1024;
+ }
+ for (--i; i >= 0; i--) {
+ free(alocs[i]);
+ }
+#ifdef __APPLE__
+ fflush(stderr);
+ dup2(old_stderr, fileno(stderr)); /* Restore stderr */
+ close(new_stderr);
+ close(old_stderr);
+#endif
+ /* To compute a true value, we need to allow for any VM already */
+ /* used by any rev instances. */
+ {
+ rev_struct *rsi;
+
+ for (rsi = g_rev_instances; rsi != NULL; rsi = rsi->next)
+ max_vmem += rsi->sz;
+ }
+
+//fprintf(stdout,"~ Abs max VM = %d Mbytes\n",max_vmem/1000000);
+ safe_max_vmem = (size_t)(0.85 * max_vmem);
+ if (g_avail_ram > safe_max_vmem) {
+ g_avail_ram = safe_max_vmem;
+ if (s->verbose && repsr == 0)
+ fprintf(stdout,"%cTrimmed maximum cache RAM to %lu Mbytes to allow for VM limit\n",cr_char,g_avail_ram/1000000);
+ }
+ }
+
+ /* Check for environment variable tweak */
+ if ((ev = getenv("ARGYLL_REV_CACHE_MULT")) != NULL) {
+ double mm, gg;
+ mm = atof(ev);
+ if (mm < 0.01) /* Make it sane */
+ mm = 0.01;
+ else if (mm > 100.0)
+ mm = 100.0;
+ gg = g_avail_ram * mm + 0.5;
+ if (gg > (double)(((size_t)0)-1))
+ gg = (double)(((size_t)0)-1);
+ g_avail_ram = (size_t)(gg);
+ }
+ if (max_vmem != 0 && g_avail_ram > max_vmem && repsr == 0) {
+ g_avail_ram = (size_t)(0.95 * max_vmem);
+ fprintf(stdout,"%cARGYLL_REV_CACHE_MULT * RAM trimmed to %lu Mbytes to allow for VM limit\n",cr_char,g_avail_ram/1000000);
+ }
+ }
+
+ /* Default - this will get aportioned as more instances appear */
+ s->rev.max_sz = g_avail_ram;
+
+ DBG(("reverse cache max memory = %d Mbytes\n",s->rev.max_sz/1000000));
+ if (s->verbose && repsr == 0) {
+ fprintf(stdout, "%cRev cache RAM = %lu Mbytes\n",cr_char,g_avail_ram/1000000);
+ repsr = 1;
+ }
+
+ /* Sub-simplex information for each sub dimension */
+ for (e = 0; e <= di; e++) {
+ if (s->rev.sspxi[e].spxi != NULL) /* Assert */
+ error("rspl rev, internal, init_ssimplex_info called on already init'd\n");
+
+ rspl_init_ssimplex_info(s, &s->rev.sspxi[e], e);
+ }
+
+ make_rev_one(s);
+
+ /* Reverse cell cache allocation */
+ s->rev.cache = alloc_revcache(s);
+
+ DBG(("make_rev finished\n"));
+}
+
+/* ====================================================== */
+
+#if defined(DEBUG1) || defined(DEBUG2)
+
+/* Utility - return a string containing a cells output value range */
+static char *pcellorange(cell *c) {
+ static char buf[5][300];
+ static ix = 0;
+ char *bp;
+ rspl *s = c->s;
+ int di = s->di, fdi = s->fdi;
+ int ee, e, f;
+
+ datao min, max;
+
+// double p[POW2MXRI][MXRI]; /* Vertex input positions for this cube. */
+// double v[POW2MXRI][MXRO+1]; /* Vertex data for this cube. Copied to x->v[] */
+// /* v[][fdi] is the ink limit values, if relevant */
+
+ for (f = 0; f < fdi; f++) {
+ min[f] = 1e60;
+ max[f] = -1e60;
+ }
+
+ /* For all other grid points in the cube */
+ for (ee = 0; ee < (1 << di); ee++) {
+
+ /* Update bounding box for this grid point */
+ for (f = 0; f < fdi; f++) {
+ if (min[f] > c->v[ee][f])
+ min[f] = c->v[ee][f];
+ if (max[f] < c->v[ee][f])
+ max[f] = c->v[ee][f];
+ }
+ }
+ if (++ix >= 5)
+ ix = 0;
+ bp = buf[ix];
+
+ for (e = 0; e < fdi; e++) {
+ if (e > 0)
+ *bp++ = ' ';
+ sprintf(bp, "%f:%f", min[e],max[e]); bp += strlen(bp);
+ }
+ return buf[ix];
+}
+
+#endif
+/* ====================================================== */
+
+#undef DEBUG
+#undef DBGV
+#undef DBG
+#define DBGV(xxx)
+#define DBG(xxx)
+
+
+
+
+
+
diff --git a/rspl/rev.h b/rspl/rev.h
new file mode 100644
index 0000000..b947cbe
--- /dev/null
+++ b/rspl/rev.h
@@ -0,0 +1,457 @@
+#ifndef RSPL_REV_H
+#define RSPL_REV_H
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized spline data structure
+ *
+ * Reverse interpolation support code.
+ *
+ * Author: Graeme W. Gill
+ * Date: 30/1/00
+ *
+ * Copyright 1999 - 2008 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ *
+ * Latest simplex/linear equation version.
+ */
+
+#undef STATS /* Collect and print reverse cach stats */
+
+/* Data structures used by reverse lookup code. */
+/* Note that the reverse lookup code only supports a more limited */
+/* dimension range than the general rspl code. */
+
+/*
+ * Note on simplex parameter space.
+ *
+ * Simplex interpolation is normaly done in Baricentric space, where there
+ * is one more baricentric coordinate than dimensions, and the sum of all
+ * the baricentric coordinates must be 1.
+ *
+ * To simplify things, we work in a "Simplex parameter" space, in which
+ * there are only dimension parameters, and each directly corresponds
+ * with a cartesian coordinate, but the parameter order corresponds with
+ * the baricentric order.
+ *
+ * For example, given cartesian coordinates D0, D1, D2
+ * these should be sorted from smallest to largest, thereby
+ * choosing a particular simplex within a cube, and allowing
+ * a correspondence to the parameter coordinates, ie:
+ *
+ * D1 D0 D2 Smallest -> Largest cartesian sort
+ * P2 P1 P0 Corresponding Parameter coordinates (note reverse order!)
+ *
+ * B0 = P0 Conversion to Baricentric coordinates
+ * B1 = P1 - P0
+ * B2 = P2 - P1
+ * B3 = 1 - P2
+ *
+ * The vertex values directly correspond to Baricentric coordinates,
+ * giving the usual interpolation equation of:
+ *
+ * VV0 * B0
+ * + VV1 * B1
+ * + VV2 * B2
+ * + VV3 * B3
+ *
+ * Reversing the Parameter -> Baricentric equations gives the
+ * following interpolation equation using Parameter coordinates:
+ *
+ * VV0 - VV1 * P0
+ * + VV1 - VV2 * P1
+ * + VV2 - VV3 * P2
+ * + VV3
+ *
+ * It is this which is used in rev.c for solving the reverse
+ * interpolation problem.
+ */
+
+/* A structure to hold per simplex coordinate and vertex mapping */
+/* This is relative to the construction cube. A face sub-simplex */
+/* that is common between cubes, will have a different psxinfo */
+/* depending on which cube created it. */
+typedef struct {
+ int face; /* Flag, nz if simplex lies on cube surface */
+ int icomb[MXDI]; /* Index by Absolute[di] -> Simplex Parameter[sdi], */
+ /* -1 == value 0, -2 == value 1 */
+ /* Note that many Abs can map to one Param to form a sum. */
+ /* icomb[] specifies the equation to convert simplex space */
+ /* coordinates back into cartesian space. */
+ int offs[MXDI+1]; /* Offsets to simplex verticies within cube == bit per dim */
+ int goffs[MXDI+1]; /* Offsets to simplex verticies within grid */
+ int foffs[MXDI+1]; /* Fwd grid floating offsets to simplex verticies from cube base */
+ int pmino[MXDI], pmaxo[MXDI]; /* Cube verticy offsets to setup simplex pmin[] and */
+ /* pmax[] bounding box pointers. */
+} psxinfo;
+
+/* Sub simplexes of a cube information structure */
+typedef struct {
+ int sdi; /* Sub-simplex dimensionality */
+ int nospx; /* Number of sub-simplexs per cube */
+ psxinfo *spxi; /* Per sub-simplex info array, NULL if not initialised */
+} ssxinfo;
+
+/* - - - - - - - - - - - - - - - - - - - - - */
+/* NOTE :- This should really be re-arranged to be per-sub-simplex caching, */
+/* rather than cell caching. Rather than stash the simplex info in the cells, */
+/* create a separate cache or some other way of sharing the common simplexes. */
+/* The code that ignores common face simplexes in cells could then be removed. */
+
+/* Simplex definition. Each top level fwd interpolation cell, */
+/* is decomposed into sub-simplexes. Sub-simplexes are of equal or */
+/* lower dimensionality (ie. faces, edges, verticies) to the cube. */
+struct _simplex {
+ int refcount; /* reference count */
+ struct _rspl *s; /* Pointer to parent rspl */
+ int ix; /* Construction Fwd cell index */
+ int si; /* Diagnostic - simplex number within level */
+ int sdi; /* Sub-simplex dimensionality */
+ int efdi; /* Effective fdi. This will be = fdi for a non clip */
+ /* plane simplex, and fdi+1 for a clip plane simplex */
+ /* The DOF (Degress of freedom) withing this simplex = sdi - efdi */
+
+ psxinfo *psxi; /* Generic per simplex info (construction cube relative) */
+ int vix[MXRI+1]; /* fwd cell vertex indexes of this simplex [sdi+1] */
+ /* This is a universal identification of this simplex */
+ struct _simplex *hlink; /* Link to other cells with this hash */
+ unsigned int touch; /* Last touch count. */
+ short flags; /* Various flags */
+
+#define SPLX_CLIPSX 0x01 /* This is a clip plane simplex */
+
+#define SPLX_FLAG_1 0x04 /* v, linmin/max initialised */
+#define SPLX_FLAG_2 0x08 /* lu/svd initialised */
+#define SPLX_FLAG_2F 0x10 /* lu/svd init. failed */
+#define SPLX_FLAG_4 0x20 /* locus found */
+#define SPLX_FLAG_5 0x40 /* auxiliary lu/svd initialised */
+#define SPLX_FLAG_5F 0x80 /* auxiliary lu/svd init. failed */
+
+
+#define SPLX_FLAGS (SPLX_FLAG_1 | SPLX_FLAG_2 | SPLX_FLAG_2F \
+ | SPLX_FLAG_4 | SPLX_FLAG_5 | SPLX_FLAG_5F)
+
+ double v[MXRI+1][MXRO+1]; /* Simplex Vertex values */
+ /* v[0..sdi][0..fdi-1] are the output interpolation values */
+ /* v[0..sdi][fdi] are the ink limit interpolation values */
+
+ /* Baricentric vv[x][y] = (v[y][x] - v[y+1][x]) */
+ /* and vv[x][sdi] = v[sdi][x] */
+
+ /* Note that #num indicates appropriate flag number */
+ /* and *num indicates a validator */
+
+ double p0[MXRI]; /* Simplex base position = construction cube p[0] */
+ double pmin[MXRI]; /* Simplex vertex input space min and */
+ double pmax[MXRI]; /* max values [di] */
+
+ double min[MXRO+1], max[MXRO+1]; /* Simplex vertex output space [fdi+1] and */
+ /* ink limit bounding values at minmax[fdi] */
+
+ /* If sdi == efdi, this holds the LU decomposition */
+ /* else this holds the SVD and solution locus info */
+
+ char *aloc2; /* Memory allocation for #2 & #4 */
+
+ /* double **d_u; LU decomp of vv, U[0..efdi-1][0..sdi-1] #2 */
+ /* int *d_w; LU decomp of vv, W[0..sdi-1] #2 */
+
+ double **d_u; /* SVD decomp of vv, U[0..efdi-1][0..sdi-1] #2 */
+ double *d_w; /* SVD decomp of vv, W[0..sdi-1] #2 */
+ double **d_v; /* SVD decomp of vv, V[0..sdi-1][0..sdi-1] #2 */
+
+ /* Degrees of freedom = dof = sdi - efdi */
+ double **lo_l; /* Locus coefficients, [0..sdi-1][0..dof-1] #2 */
+
+ double *lo_xb; /* RHS used to compute lo_bd [0..efdi-1] *4 */
+ double *lo_bd; /* Locus base solution, [0..sdi-1] #4 */
+
+ unsigned auxbm; /* aux bitmap mask for ax_lu and ax_svd *5 */
+ int aaux; /* naux count for allocation *5 */
+ int naux; /* naux for calculation (may be < aaux ?) *5 */
+
+ /* if (sdi-efdi = dof) == naux this holds LU of lo_l */
+ /* else this holds the SVD of lo_l */
+
+ char *aloc5; /* Memory allocation for #5 */
+
+ /* double **ax_u; LU decomp of lo_l #5 */
+ /* int *ax_w; Pivot record for ax_lu decomp #5 */
+
+ double **ax_u; /* SVD decomp of lo_l, U[0..naux-1][0..dof-1] #5 */
+ double *ax_w; /* SVD decomp of lo_l, W[0..dof-1] #5 */
+ double **ax_v; /* SVD decomp of lo_l, V[0..dof-1][0..dof-1] #5 */
+
+}; typedef struct _simplex simplex;
+
+/* A candidate search cell (cell cache entry structure) */
+struct _cell {
+ struct _rspl *s; /* Pointer to parent rspl */
+
+ /* Cache information */
+ int ix; /* Fwd cell index */
+ struct _cell *hlink; /* Link to other cells with this hash */
+ struct _cell *mrudown; /* Links to next most recently used cell */
+ struct _cell *mruup;
+ int refcount; /* Reference count */
+ int flags; /* Non-zero if the cell has been initialised */
+#define CELL_FLAG_1 0x01 /* Basic initialisation */
+#define CELL_FLAG_2 0x02 /* Simplex information initialised */
+
+ /* Use information */
+ double sort; /* Sort key */
+
+ double limmin, limmax; /* limit() min/max for this cell */
+ double bcent[MXRO]; /* Output value bounding shere center */
+ double brad; /* Output value bounding shere radius */
+ double bradsq; /* Output value bounding shere radius squared */
+
+ double p[POW2MXRI][MXRI]; /* Vertex input positions for this cube. */
+ /* Copied to x->pmin/pmax[] & ink limit */
+
+ double v[POW2MXRI][MXRO+1]; /* Vertex data for this cube. Copied to x->v[] */
+ /* v[][fdi] is the ink limit values, if relevant */
+
+ simplex **sx[MXRI+1]; /* Lists of simplexes that make up this cell. */
+ /* Each list indexed by the non-limited simplex */
+ /* dimensionality (similar to sspxi[]) */
+ /* Each list will be NULL if it hasn't been created yet */
+ int sxno[MXRI+1]; /* Corresponding count of each list */
+
+}; typedef struct _cell cell;
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
+/* Enough space is needed to cache all the cells/simplexes */
+/* for a full aux. locus, or the query will be processed in */
+/* several "chunks" and will be slower. */
+/* This sets the basic memory usage of the rev code. */
+
+#define REV_ACC_GRES_MUL 2.0 /* 2.0 Reverse accelleration grid resolution */
+ /* multiplier over fwd grid resolution */
+#define REV_ACC_GRES_LIMIT 43 /* Reverse accelln. grid resolution limit before env. mult. */
+#define REV_MAX_MEM_RATIO 0.3 /* 0.3 Proportion of first 1G of Ram to use */
+#define REV_MAX_MEM_RATIO2 0.4 /* 0.4 Proportion of rest of Ram to use */
+ /* rev as a fraction of the System RAM. */
+#define HASH_FILL_RATIO 3 /* 3 Ratio of entries to hash size */
+
+/* The structure where cells are allocated and cached. */
+
+/* Holds the cell and simplex match cache specific information */
+typedef struct {
+ struct _rspl *s; /* Pointer to parent rspl */
+ int nacells; /* Number of allocated cells */
+ int nunlocked; /* Number of unlocked cells that could be freed */
+ int cell_hash_size; /* Current size of cell hash list */
+ cell **hashtop; /* Top of hash list [cell_hash_size] */
+ cell *mrutop, *mrubot; /* Top and bottom pointers of mru list */
+ /* that tracks allocated cells */
+
+ int spx_hash_size; /* Current size of simplex hash list */
+ simplex **spxhashtop; /* Face simplex hash index list */
+ int nspx; /* Number of simplexes in hash list */
+} revcache;
+
+/* common search information */
+/* Type of (internal) reverse search */
+enum ops {
+ exact = 0, /* Search for all input values that exactly map to given output value */
+ clipv = 1, /* Search for input values that map to outermost solution along a vector */
+ clipn = 2, /* Search for input values that map to closest solution */
+ auxil = 3, /* Search for input values that map to given output, and closest to auxiliary target */
+ locus = 4 }; /* Return range of auxiliary values that contains solution */
+
+/* + possible exact with clip */
+
+/* Structure to hold clip line state information */
+typedef struct {
+ struct _rspl *s; /* Pointer to parent rspl */
+ double st[MXRO]; /* start of line - reverse grid base value */
+ double de[MXRO]; /* direction of line */
+ int di[MXRO]; /* incerement in line direction */
+ int ci[MXRO]; /* current rev grid coordinate */
+ double t; /* Parameter 0.0 - 1.0, line finished if t > 1.0 */
+} line;
+
+
+/* Structure to hold aux value of an intersection of a */
+/* solution locus with a sub-simplex. Used when asegs flag is set */
+typedef struct {
+ double xval; /* Auxiliary value */
+ int nv; /* Number of verticies valid */
+ int vix[MXRI+1]; /* Verticy indexes of sub-simplex involved */
+} axisec;
+
+/* -------------------------------------------- */
+/* Information needed/cached for reverse lookup */
+struct _schbase {
+ struct _rspl *s; /* Pointer to parent rspl */
+
+ int flags; /* Hint flags */
+ enum ops op; /* Type of reverse search operation */
+ int ixc; /* Cube index of corner that holds maximum input values */
+
+ int snsdi, ensdi; /* Start and end extra sub-simplex dimensionality */
+ int (*setsort)(struct _schbase *b, cell *c); /* Function to triage & set cube sort index */
+ int (*check)(struct _schbase *b, cell *c); /* Function to recheck cube worth keeping */
+ int (*compute)(struct _schbase *b, simplex *x); /* Function to compute a simplex solution */
+
+ double v[MXRO+1]; /* Target output value, + ink limit */
+ double av[MXRI]; /* Target auxiliary values */
+ int auxm[MXRI]; /* aux mask flags */
+ unsigned auxbm; /* aux bitmap mask */
+ int naux; /* Number of auxiliary target input values */
+ int auxi[MXRI]; /* aux list of auxiliary target input values */
+ double idist; /* best input distance auxiliary target found (smaller is better) */
+ int iabove; /* Number of auxiliaries at or above zero */
+
+ int canvecclip; /* Non-zero if vector clip direction usable */
+ double cdir[MXRO]; /* Clip vector direction and length wrt. v[] */
+ double ncdir[MXRO]; /* Normalised clip vector */
+ double **cla; /* Clip vector LHS implicit equation matrix [fdi][fdi+1] (inc. ink tgt.) */
+ double clb[MXRO+1]; /* Clip vector RHS implicit equation vector [fdi+1] (inc. ink tgt.) */
+ double cdist; /* Best clip locus distance found (aim is min +ve) */
+ int iclip; /* NZ if result is above (disabled) ink limit */
+
+ int mxsoln; /* Maximum number of solutions that we want */
+ int nsoln; /* Current number of solutions found */
+ co *cpp; /* Store solutions here */
+
+ int lxi; /* Locus search axiliary index */
+ double min, max; /* current extreme locus values for locus search */
+ int asegs; /* flag - find all search segments */
+ int axisln; /* Number of elements used in axisl[] */
+ int axislz; /* Space allocated to axisl[] */
+ axisec *axisl; /* Auxiliary intersections */
+
+ int lclistz; /* Allocated space to cell sort list */
+ cell **lclist; /* Sorted list of pointers to candidate cells */
+
+ int pauxcell; /* Indexe of previous call solution cell, -1 if not relevant */
+ int plmaxcell; /* Indexe of previous call solution cell, -1 if not relevant */
+ int plmincell; /* Indexe of previous call solution cell, -1 if not relevant */
+
+ int lsxfilt; /* Allocated space of simplex filter list */
+ char *sxfilt; /* Flag for simplexes that should be in a cell */
+
+ double crad; /* nn current radius distance */
+ double bw; /* nn current window distance */
+ int wex[MXRO * 2]; /* nn current window edge indexes */
+ double wed[MXRO * 2]; /* nn current window edge distances */
+
+}; typedef struct _schbase schbase;
+
+/* ----------------------------------------- */
+
+#ifdef STATS
+struct _stats {
+ int searchcalls;/* Number of top level searches */
+ int csearched; /* Cells searched */
+ int ssearched; /* Simplexes searched */
+ int sinited; /* Simplexes initialised to base level */
+ int sinited2a; /* Simplexes initialised to 2nd level with LU */
+ int sinited2b; /* Simplexes initialised to 2nd level with SVD */
+ int sinited4i; /* Simplexes invalidated at 4th level */
+ int sinited4; /* Simplexes initialised to 4th level */
+ int sinited5i; /* Simplexes invalidated at 5th level */
+ int sinited5a; /* Simplexes initialised to 5th level with LU */
+ int sinited5b; /* Simplexes initialised to 5th level with SVD */
+ int chits; /* Cells hit in cache */
+ int cmiss; /* Cells misses in cache */
+}; typedef struct _stats stats;
+#endif /* STATS */
+
+/* ----------------------------------------- */
+/* Reverse info stored in main rspl function */
+struct _rev_struct {
+
+ /* First section, basic information that doesn't change */
+ /* Has been initialised if inited != 0 */
+
+ int inited; /* Non-zero if first section has been initialised */
+ /* All other sections depend on this. */
+ int fastsetup; /* Flag - NZ if fast setup at cost of slow throughput */
+
+ struct _rev_struct *next; /* Linked list of instances sharing memory */
+ size_t max_sz; /* Maximum size permitted */
+ size_t sz; /* Total memory current allocated by rev */
+
+#ifdef NEVER
+ int thissz, lastsz; /* Debug reporting */
+#endif
+
+ /* Reverse grid lookup information */
+ int ares; /* Reverse grid allocated resolution, = res + 1, */
+ /* allows for extra row used during construction */
+ int res; /* Reverse grid resolution == ncells on a side */
+ int no; /* Total number of points in reverse grid = rev.ares ^ fdi */
+ int coi[MXRO]; /* Coordinate increments for each dimension */
+ int hoi[1 << MXRO]; /* Coordinate increments for progress through cube */
+ datao gl,gh,gw; /* Reverse grid low, high, grid cell width */
+
+ /* Second section, accelleration information that changes with ink limit. */
+ int rev_valid; /* nz if this information in rev[] and nnrev[] is valid */
+ int **rev; /* Exact reverse lookup starting list. */
+ /* rev.no pointers to lists of fwd grid indexes. */
+ /* First int is allocation length */
+ /* Second int is reference count */
+ /* Then follows cube indexes */
+ /* Last int is -1 */
+ int **nnrev; /* Nearest reverse lookup starting list. */
+ /* rev.no pointers to lists of fwd grid indexes. */
+ /* [0] is allocation length */
+ /* [1] is the next free entry index */
+ /* [2] is reference count */
+ /* Then follows cube indexes */
+ /* The last entry is marked with -1 */
+
+ /* Third section */
+ revcache *cache; /* Where cells are allocated and cached */
+ /* Sub-dimension simplex information */
+ ssxinfo sspxi[MXRI+1];/* One per sub dimenstionality at offset sdi */
+
+ /* Fourth section */
+ /* Has been initialise if sb != NULL */
+ schbase *sb; /* Structure holding calculated per-search call information */
+
+ unsigned int stouch; /* Simplex touch count to avoid searching shared simplexs twice */
+#ifdef STATS
+ stats st[5]; /* Set of stats info indexed by enum ops */
+#endif /* STATS */
+
+ int primsecwarn; /* Not primary or secondary warning has been issued */
+
+}; typedef struct _rev_struct rev_struct;
+
+
+/* ------------------------------------ */
+/* Utility functions used by other parts of rspl implementation */
+
+/* Initialise a static sub-simplex verticy information table */
+void rspl_init_ssimplex_info(struct _rspl *s, /* RSPL object */
+ssxinfo *xip, /* Pointer to sub-simplex info structure to init. */
+int sdi); /* Sub-simplex dimensionality (range 0 - di) */
+
+/* Free the given sub-simplex verticy information */
+void rspl_free_ssimplex_info(struct _rspl *s,
+ssxinfo *xip); /* Pointer to sub-simplex info structure */
+
+#endif /* RSPL_REV_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rspl/revbench.c b/rspl/revbench.c
new file mode 100644
index 0000000..d0c7566
--- /dev/null
+++ b/rspl/revbench.c
@@ -0,0 +1,306 @@
+
+/************************************************/
+/* Benchmark RSPL reverse lookup */
+/************************************************/
+
+/* Author: Graeme Gill
+ * Date: 31/10/96
+ * Derived from tnd.c
+ * Copyright 1999 - 2000 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include <time.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "rspl.h"
+#include "numlib.h"
+
+#undef DOLIMIT /* Define to have ink limit */
+#define LIMITVAL 2.5 /* Total ink limit sum */
+#undef DOCHECK
+
+#define SHOW_OUTPUT /* Define for printf on each conversion */
+
+/* 11, 19 give about 60 seconds */
+#define GRES 17 /* Default grid resolution */
+#define RRES 43 /* Default reverse test resolution */
+#define DI 4 /* Dimensions in */
+#define FDI 3 /* Function (out) Dimensions */
+#define NIP 10 /* Number of solutions allowed */
+
+#define flimit(vv) ((vv) < 0.0 ? 0.0 : ((vv) > 1.0 ? 1.0 : (vv)))
+#define fmin(a,b) ((a) < (b) ? (a) : (b))
+#define fmin3(a,b,c) (fmin((a), fmin((b),(c))))
+#define fmax(a,b) ((a) > (b) ? (a) : (b))
+#define fmax3(a,b,c) (fmax((a), fmax((b),(c))))
+
+/* Fwd function approximated by rspl */
+/* Dummy cmyk->rgb conversion. This simulates our device */
+void func(
+void *cbctx,
+double *out,
+double *in) {
+ double kk;
+ double ci = in[0];
+ double mi = in[1];
+ double yi = in[2];
+ double ki = in[3];
+ double r,g,b;
+
+ ci += ki; /* Add black back in */
+ mi += ki;
+ yi += ki;
+ kk = fmax3(ci,mi,yi);
+ if (kk > 1.0) {
+ ci /= kk;
+ mi /= kk;
+ yi /= kk;
+ }
+ r = 1.0 - ci;
+ g = 1.0 - mi;
+ b = 1.0 - yi;
+ out[0] = flimit(r);
+ out[1] = flimit(g);
+ out[2] = flimit(b);
+}
+
+/* Simplex ink limit function */
+double limitf(
+void *lcntx,
+float *in
+) {
+ int i;
+ double ov;
+
+ for (ov = 0.0, i = 0; i < DI; i++) {
+ ov += in[i];
+ }
+ return ov;
+}
+
+
+void usage(void) {
+ fprintf(stderr,"Benchmark rspl reverse, Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"usage: revbench [-f fwdres] [-r revres] [-v level] iccin iccout\n");
+ fprintf(stderr," -v Verbose\n");
+ fprintf(stderr," -f res Set forward grid res\n");
+ fprintf(stderr," -r res Set reverse test res\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ int clutres = GRES;
+ int rres = RRES;
+ int verb = 0;
+ int gres[MXDI];
+ int e;
+
+ clock_t stime,ttime;
+ rspl *rss; /* Multi-resolution regularized spline structure */
+
+ error_program = argv[0];
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ /* Verbosity */
+ else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ verb = 1;
+ }
+ else if (argv[fa][1] == 'f' || argv[fa][1] == 'F') {
+ fa = nfa;
+ if (na == NULL) usage();
+ clutres = atoi(na);
+ }
+ else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ fa = nfa;
+ if (na == NULL) usage();
+ rres = atoi(na);
+ }
+ else
+ usage();
+ } else
+ break;
+ }
+
+ printf("Started benchmark\n");
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS, DI, FDI);
+
+ for (e = 0; e < DI; e++)
+ gres[e] = clutres;
+
+ printf("Rspl allocated\n");
+ rss->set_rspl(rss, 0, (void *)NULL, func,
+ NULL, NULL, gres, NULL, NULL);
+
+ printf("Rspl set\n");
+
+ /* Start exploring the reverse test grid */
+ {
+ int ops = 0;
+ double secs;
+ rpsh counter;
+ unsigned rcount;
+ int ii[10];
+ int f, rgres[MXDO];
+
+ int flags = 0; /* rev hint flags */
+ co tp[NIP]; /* Test point */
+ double cvec[4]; /* Text clip vector */
+ int auxm[4]; /* Auxiliary target value valid flag */
+#ifdef DOCHECK
+ int j;
+#endif
+#ifdef NEVER
+ double lmin[4], lmax[4]; /* Locus min/max values */
+#endif
+
+#ifdef DOCHECK
+ char *check; /* Check that we hit every cell */
+#endif /* DOCHECK */
+
+ /* Set auxiliary target mask */
+ auxm[0] = 0;
+ auxm[1] = 0;
+ auxm[2] = 0;
+ auxm[3] = 1;
+
+#ifdef DOLIMIT
+ rss->rev_set_limit(rss,
+ limitf,
+ NULL,
+ LIMITVAL /* limit maximum value */
+ );
+#endif /* DOLIMIT */
+
+ printf("Forward resolution %d\n",clutres);
+ printf("Reverse resolution %d\n",rres);
+
+#ifdef DOCHECK
+ if ((check = (char *)calloc(1, rcount)) == NULL)
+ error("Malloc of check array\n");
+#endif /* DOCHECK */
+
+ for (f = 0; f < FDI; f++)
+ rgres[f] = rres;
+
+ rcount = rpsh_init(&counter, FDI, (unsigned int *)rgres, ii); /* Initialise counter */
+
+ stime = clock();
+
+ /* Itterate though the grid */
+ for (ops = 0;; ops++) {
+ int r;
+ int e; /* Table index */
+
+#ifdef DOCHECK
+ check[((ii[2] * rres + ii[1]) * rres) + ii[0]] = 1;
+#endif /* DOCHECK */
+
+ for (e = 0; e < FDI; e++) { /* Input tables */
+ tp[0].v[e] = ii[e]/(rres-1.0); /* Vertex coordinates */
+ }
+
+ if (verb)
+ printf("Input = %f %f %f\n",tp[0].v[0], tp[0].v[1], tp[0].v[2]);
+
+#ifdef NEVER /* Do locus lookup explicitly ? */
+ /* Lookup the locus for the auxiliary (Black) chanel */
+ if ((r = rss->rev_locus(rss,
+ auxm, /* auxm Auxiliary mask flags */
+ tp, /* Input and auxiliary values */
+ lmin, /* Locus min/max return values */
+ lmax
+ )) == 0) {
+ /* Rev locus failed - means that it will clip ? */
+ tp[0].p[3] = 0.5;
+ flags = RSPL_WILLCLIP; /* Since there was no locus, we expect to clip */
+ } else {
+ /* Set the auxiliary target */
+ tp[0].p[3] = (lmin[3] + lmax[3])/2.0;
+ flags = RSPL_EXACTAUX; /* Since we got locus, expect exact auxiliary match */
+ }
+#else
+ tp[0].p[3] = 0.5;
+ flags = RSPL_AUXLOCUS; /* Auxiliary target is proportion of locus */
+#endif /* NEVER */
+
+ /* Clip vector to 0.5 */
+ cvec[0] = 0.5 - tp[0].v[0];
+ cvec[1] = 0.5 - tp[0].v[1];
+ cvec[2] = 0.5 - tp[0].v[2];
+ cvec[3] = 0.5 - tp[0].v[3];
+
+ /* Do reverse interpolation */
+ if ((r = rss->rev_interp(rss,
+ flags, /* Hint flags */
+ NIP, /* Number of solutions allowed */
+ auxm, /* auxm Auxiliary mask flags */
+ cvec, /* cvec Clip vector direction & length */
+ tp) /* Input and output values */
+ ) == 0) {
+ error("rev_interp failed\n");
+ }
+
+ r &= RSPL_NOSOLNS; /* Get number of solutions */
+
+ if (verb)
+ printf("Output 1 of %d: %f, %f, %f, %f%s\n",
+ r & RSPL_NOSOLNS, tp[0].p[0], tp[0].p[1], tp[0].p[2], tp[0].p[3],
+ (r & RSPL_DIDCLIP) ? " [Clipped]" : "");
+
+
+ if (rpsh_inc(&counter, ii))
+ break;
+
+ } /* Next grid point */
+
+ ttime = clock() - stime;
+ secs = (double)ttime/CLOCKS_PER_SEC;
+ printf("Done - %d ops in %f seconds, rate = %f ops/sec\n",ops, secs,ops/secs);
+#ifdef DOCHECK
+ for (j = 0; j < rcount; j++) {
+ if (check[j] != 1) {
+ printf("~~CHeck error at %d\n",j);
+ }
+ }
+#endif /* DOCHECK */
+ }
+
+ rss->del(rss);
+ return 0;
+}
+
+
+
+
+
+
diff --git a/rspl/rspl.c b/rspl/rspl.c
new file mode 100644
index 0000000..13a1776
--- /dev/null
+++ b/rspl/rspl.c
@@ -0,0 +1,1511 @@
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized splines data fitter
+ *
+ * Author: Graeme W. Gill
+ * Date: 30/1/00
+ *
+ * Copyright 1996 - 2004 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* Version that a combination of relaxation and conjugate gradient */
+/* solution techniques. */
+
+/* TTBD:
+
+ To save space, the full columns of A should only be allocated when needed ?
+ (Does this actually save much space for a realistic data sample set ?)
+
+ Get rid of error() calls - return status instead.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <time.h>
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+
+#include "rspl_imp.h"
+#include "numlib.h"
+#include "counters.h" /* Counter macros */
+
+#undef DEBUG
+#undef DEBUGLU /* Debug fwd interpolation */
+#undef VERBOSE
+
+#undef NEVER
+#define ALWAYS
+
+#ifdef DEBUGLU
+# define DEBLU(xxxx) printf xxxx
+#else
+# define DEBLU(xxxx)
+#endif
+
+/* Implemeted in this file: */
+rspl *new_rspl(int flags, int di, int fdi);
+static void free_rspl(rspl *s);
+static void init_grid(rspl *s);
+static void free_grid(rspl *s);
+static void get_in_range(rspl *s, double *min, double *max);
+static void get_out_range(rspl *s, double *min, double *max);
+static void get_out_range_points(rspl *s, int *minp, int *maxp);
+static double get_out_scale(rspl *s);
+static unsigned int get_next_touch(rspl *s);
+static int within_restrictedsize(rspl *s);
+static int interp_rspl_sx(rspl *s, co *pp);
+static int part_interp_rspl_sx(rspl *s, co *p1, co *p2);
+static int interp_rspl_nl(rspl *s, co *p);
+int is_mono(rspl *s);
+static int set_rspl(rspl *s, int flags, void *cbctx,
+ void (*func)(void *cbctx, double *out, double *in),
+ datai glow, datai ghigh, int gres[MXDI], datao vlow, datao vhigh);
+static int re_set_rspl(struct _rspl *s, int flags, void *cbntx,
+ void (*func)(void *cbntx, double *out, double *in));
+static void scan_rspl(struct _rspl *s, int flags, void *cbntx,
+ void (*func)(void *cbntx, double *out, double *in));
+static void filter_rspl(struct _rspl *s, int flags, void *cbctx,
+ void (*func)(void *cbntx, float **out, double *in, int cvi));
+
+extern int add_rspl(rspl *s, int flags, co *d, int dno);
+extern void init_data(rspl *s);
+extern void free_data(rspl *s);
+
+/* Implemented in rev.c: */
+void init_rev(rspl *s);
+void free_rev(rspl *s);
+
+/* Implemented in gam.c: */
+void init_gam(rspl *s);
+void free_gam(rspl *s);
+
+/* Implemented in spline.c: */
+void init_spline(rspl *s);
+void free_spline(rspl *s);
+
+/* Implemented in opt.c: */
+int opt_rspl_imp(struct _rspl *s, int flags, int tdi, int adi, double **vdata,
+ double (*func)(void *fdata, double *inout, double *surav, int first, double *cw),
+ void *fdata, datai glow, datai ghigh, int gres[MXDI], datao vlow, datao vhigh);
+
+
+/* Convention is to use:
+ i to index grid points u.a
+ n to index data points d.a
+ e to index position dimension di
+ f to index output function dimension fdi
+ j misc and cube corners
+ k misc
+ */
+
+/* ================================ */
+/* Allocate an empty rspl object. */
+rspl *
+new_rspl(
+ int flags,
+ int di,
+ int fdi
+) {
+ rspl *s;
+
+#ifdef DEBUG
+ fprintf(stderr,"new_rspl with flags 0x%x, di %d, fdi %d\n",flags,di,fdi);
+#endif
+ /* Allocate a structure */
+ if ((s = (rspl *) calloc(1, sizeof(rspl))) == NULL)
+ error("rspl: malloc failed - main structure");
+
+ /* Set our fundamental parameters */
+ if (di < 1 || di > MXDI)
+ error("rspl: can't handle input dimension %d",di);
+ s->di = di;
+
+ if (fdi < 1 || fdi > MXDO)
+ error("rspl: can't handle output dimension %d",fdi);
+ s->fdi = fdi;
+
+ /* And appropriate flags */
+ if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
+ s->verbose = 1;
+ if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
+ s->verbose = 0;
+
+ /* Allocate space for cube offset arrays */
+ s->g.hi = s->g.a_hi;
+ s->g.fhi = s->g.a_fhi;
+ if ((1 << di) > DEF2MXDI) {
+ if ((s->g.hi = (int *) malloc(sizeof(int) * (1 << di))) == NULL)
+ error("rspl malloc failed - hi[]");
+ if ((s->g.fhi = (int *) malloc(sizeof(int) * (1 << di))) == NULL)
+ error("rspl malloc failed - fhi[]");
+ }
+
+ /* Init sub sections */
+ init_data(s);
+ init_grid(s);
+ init_rev(s);
+ init_gam(s);
+ init_spline(s);
+
+ if (flags & RSPL_FASTREVSETUP)
+ s->rev.fastsetup = 1;
+ else
+ s->rev.fastsetup = 0;
+
+ /* Set pointers to methods in this file */
+ s->del = free_rspl;
+ s->interp = interp_rspl_sx; /* Default to simplex interp */
+#ifdef NEVER
+#define USING_INTERP_NL
+printf("!!!! rspl.c using interp_rspl_nl !!!!");
+ s->interp = interp_rspl_nl;
+#endif
+ s->part_interp = part_interp_rspl_sx;
+ s->set_rspl = set_rspl;
+ s->scan_rspl = scan_rspl;
+ s->re_set_rspl = re_set_rspl;
+ s->opt_rspl = opt_rspl_imp;
+ s->filter_rspl = filter_rspl;
+ s->get_in_range = get_in_range;
+ s->get_out_range = get_out_range;
+ s->get_out_range_points = get_out_range_points;
+ s->get_out_scale = get_out_scale;
+ s->get_next_touch = get_next_touch;
+ s->within_restrictedsize = within_restrictedsize;
+
+ return s;
+}
+
+/* Free the rspl and all its contents */
+static void free_rspl(rspl *s) {
+ int e;
+
+ /* Free everying contained */
+ free_data(s); /* Free any scattered data */
+ free_rev(s); /* Free any reverse lookup data */
+ free_gam(s); /* Free any grid data */
+ free_grid(s); /* Free any grid data */
+
+ /* Free spline interpolation data ~~~~ */
+
+ /* Free structure */
+ for (e = 0; e < s->di; e++) {
+ if (s->g.ipos[e] != NULL)
+ free(s->g.ipos[e]);
+ }
+ if (s->g.hi != s->g.a_hi) {
+ free(s->g.hi);
+ free(s->g.fhi);
+ }
+ free((void *) s);
+}
+
+/* ======================================================== */
+/* Allocate rspl grid data, and initialise grid associated stuff */
+void
+alloc_grid(rspl *s) {
+ int di = s->di, fdi = s->fdi;
+ int e,g,i;
+ int gno; /* Number of points in grid */
+ ECOUNT(gc, MXDIDO, di, 0, s->g.res, 0);/* coordinates */
+ float *gp; /* Grid point pointer */
+
+#ifdef DEBUG
+ fprintf(stderr,"rspl allocating grid res %s\n",icmPiv(di, s->g.res));
+#endif
+
+ /* Compute total number of elements in the grid */
+ for (gno = 1, e = 0; e < di; gno *= s->g.res[e], e++)
+ ;
+ s->g.no = gno;
+
+ s->g.pss = fdi+G_XTRA; /* float for each output value + nme + flags */
+
+ /* Compute index coordinate increments into linear grid for each dimension */
+ /* ie. 1, gres, gres^2, gres^3 */
+ for (s->g.ci[0] = 1, e = 1; e < di; e++)
+ s->g.ci[e] = s->g.ci[e-1] * s->g.res[e-1]; /* In grid points */
+ for (e = 0; e < di; e++)
+ s->g.fci[e] = s->g.ci[e] * s->g.pss; /* In floats */
+
+ /* Compute index offsets from base of cube to other corners. */
+ for (s->g.hi[0] = e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++)
+ s->g.hi[g+i] = s->g.hi[i] + s->g.ci[e]; /* In grid points */
+ }
+ /* same as hi, but in floats */
+ for (i = 0; i < (1 << di); i++)
+ s->g.fhi[i] = s->g.hi[i] * s->g.pss; /* In floats */
+
+ /* Allocate space for grid */
+ if ((s->g.alloc = (float *) malloc(sizeof(float) * gno * s->g.pss)) == NULL)
+ error("rspl malloc failed - grid points");
+ s->g.a = s->g.alloc + G_XTRA; /* make -1 be nme, and -2 be (unsigned int) flags */
+
+ /* Set initial value of cell touch count */
+ s->g.touch = 0;
+
+ /* Init near edge flags, and touched flag */
+ EC_INIT(gc);
+ for (i = 0, gp = s->g.a; !EC_DONE(gc); i++, gp += s->g.pss) {
+ gp[-1] = L_UNINIT; /* Init Ink limit function value to -1e38 */
+ I_FL(gp); /* Init all flags to zero */
+ for (e = 0; e < di; e++) {
+ int e1,e2;
+ e1 = gc[e]; /* Dist to bottom edge */
+ e2 = (s->g.res[e]-1) - gc[e]; /* Dist to top edge */
+ if (e2 < e1) { /* Top edge is closer */
+ if (e2 > 2)
+ e2 = 2; /* Max dist = 2 */
+ S_FL(gp,e,e2); /* Set flag value */
+ } else { /* Bot edge is closer */
+ if (e1 > 2)
+ e1 = 2; /* Max dist = 2 */
+ S_FL(gp,e,e1 | 0x4); /* Set flag value */
+ }
+ }
+ TOUCHF(gp) = 0;
+
+ EC_INC(gc);
+ }
+ s->g.limitv_cached = 0; /* No limit values are current cached */
+}
+
+/* Init grid related elements of rspl */
+static void
+init_grid(rspl *s) {
+ s->g.alloc = NULL;
+}
+
+/* Free the grid allocation */
+static void
+free_grid(rspl *s) {
+ if (s->g.alloc != NULL)
+ free((void *)s->g.alloc);
+}
+
+/* ============================================ */
+/* Return the range of possible input values that the grid can represent */
+static void
+get_in_range(
+rspl *s, /* Grid to search */
+double *min, double *max /* Return min/max values */
+) {
+ int e;
+ for (e = 0; e < s->di; e++) {
+ min[e] = s->g.l[e];
+ max[e] = s->g.h[e];
+ }
+}
+
+/* ============================================ */
+/* Discover the range of possible output values */
+static void
+get_out_range(
+rspl *s, /* Grid to search */
+double *min, double *max /* Return min/max values */
+) {
+ float *gp,*ep; /* Grid pointer */
+ int f;
+
+ if (s->g.fminmax_valid == 0) { /* Not valid, so compute it */
+ for (f = 0; f < s->fdi; f++) {
+ s->g.fmin[f] = 1e30;
+ s->g.fmax[f] = -1e30;
+ s->g.fminx[f] = -1;
+ s->g.fmaxx[f] = -1;
+ }
+
+ /* Scan the Grid points for min/max values */
+ for (gp = s->g.a, ep = s->g.a + s->g.no * s->g.pss; gp < ep; gp += s->g.pss) {
+ for (f = 0; f < s->fdi; f++) {
+ if (s->g.fmin[f] > gp[f]) {
+ s->g.fmin[f] = gp[f];
+ s->g.fminx[f] = (gp - s->g.a)/s->g.pss;
+ }
+ if (s->g.fmax[f] < gp[f]) {
+ s->g.fmax[f] = gp[f];
+ s->g.fmaxx[f] = (gp - s->g.a)/s->g.pss;
+ }
+ }
+ }
+
+ /* Compute overall output scale */
+ for (s->g.fscale = 0.0, f = 0; f < s->fdi; f++) {
+ double tt = s->g.fmax[f] - s->g.fmin[f];
+ s->g.fscale += tt * tt;
+ }
+ s->g.fscale = sqrt(s->g.fscale);
+ s->g.fminmax_valid = 1; /* Now is valid */
+ }
+ for (f = 0; f < s->fdi; f++) {
+ if (min != NULL)
+ min[f] = s->g.fmin[f];
+ if (max != NULL)
+ max[f] = s->g.fmax[f];
+ }
+}
+
+/* ============================================ */
+/* return the grid index of the grid values at the min & max output values */
+static void get_out_range_points(rspl *s, int *minp, int *maxp) {
+ int f;
+
+ if (s->g.fminmax_valid == 0) /* Not valid, so compute it */
+ get_out_range(s, NULL, NULL);
+
+ for (f = 0; f < s->fdi; f++) {
+ if (minp != NULL)
+ minp[f] = s->g.fminx[f];
+ if (maxp != NULL)
+ maxp[f] = s->g.fmaxx[f];
+ }
+}
+
+/* ============================================ */
+/* Discover the csale of the output values */
+static double
+get_out_scale(rspl *s) {
+
+ if (s->g.fminmax_valid == 0) /* Not valid, so compute it */
+ get_out_range(s, NULL, NULL);
+
+ return s->g.fscale;
+}
+
+/* ============================================ */
+/* Return the next touched flag count value. */
+/* Whenever this rolls over, all the flags in the grid array will be reset. */
+/* */
+/* The touch flag is a way of some grid accessor (ie. rev()) making sure */
+/* that it doesn't access cells more than once for a particular operation, */
+/* without sorting a list of items to be accessed, or having to reset a */
+/* binary flag on all the cells for each operation. */
+/* If the value of a cells TOUCHF() is less than s->g.touch, then it hasn't */
+/* been accessed yet. Once the cell has been acessed, then TOUCHF() should */
+/* be set to s->g.touch. After 2^32 operations, the cell touch flags will */
+/* have been set to values between 0 and 2^32-1, so it is time to reset */
+/* all the flags. For that reason, the following method should be used */
+/* to get the next touch generation value at the start of each operation. */
+static unsigned int
+get_next_touch(
+rspl *s
+) {
+ unsigned int tg;
+ float *gp,*ep; /* Grid pointer */
+
+ if ((tg = ++s->g.touch) == 0) {
+
+ /* We have to reset all the cell flags to zero before we roll over */
+ for (gp = s->g.a, ep = s->g.a + s->g.no * s->g.pss; gp < ep; gp += s->g.pss) {
+ TOUCHF(gp) = 0;
+ }
+ tg = ++s->g.touch; /* return 1 */
+ }
+ return tg;
+}
+
+/* ============================================ */
+/* Return non-zero if this rspl can be */
+/* used with Restricted Size functions. */
+static int within_restrictedsize(
+rspl *s
+) {
+ if (s->di <= MXRI && s->fdi <= MXRO)
+ return 1;
+ return 0;
+}
+
+/* ============================================ */
+/* Do a forward interpolation using an simplex interpolation method. */
+/* Return 0 if OK, 1 if input was clipped to grid */
+/* Return 0 on success, 1 if clipping occured, 2 on other error */
+// ~~999
+//int rspldb = 0;
+
+static int interp_rspl_sx(
+rspl *s,
+co *p /* Input value and returned function value */
+) {
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+ double we[MXDI]; /* Coordinate offset within the grid cell */
+ int si[MXDI]; /* we[] Sort index, [0] = smallest */
+ float *gp; /* Pointer to grid cube base */
+ int rv = 0; /* Register clip */
+
+ /* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
+
+ DEBLU(("In %s\n", icmPdv(di, p->p)));
+
+ /* Figure out which grid cell the point falls into */
+ {
+ gp = s->g.a; /* Base of grid array */
+ for (e = 0; e < di; e++) {
+ int gres_1 = s->g.res[e]-1;
+ double pe, t;
+ int mi;
+ pe = p->p[e];
+ if (pe < s->g.l[e]) { /* Clip to grid */
+ pe = s->g.l[e];
+ rv = 1;
+ }
+ if (pe > s->g.h[e]) {
+ pe = s->g.h[e];
+ rv = 1;
+ }
+ t = (pe - s->g.l[e])/s->g.w[e];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= gres_1)
+ mi = gres_1-1;
+ gp += mi * s->g.fci[e]; /* Add Index offset for grid cube base in dimen */
+ we[e] = t - (double)mi; /* 1.0 - weight */
+//if (rspldb && di == 3) printf("~1 e = %d, ix = %d, we = %f\n", e, mi, we[e]);
+ }
+ DEBLU(("ix %d, we %s\n", (gp - s->g.a)/s->g.pss, icmPdv(di, p->p)));
+ }
+
+ /* Do selection sort on coordinates */
+ {
+ for (e = 0; e < di; e++)
+ si[e] = e; /* Initial unsorted indexes */
+ for (e = 0; e < (di-1); e++) {
+ double cosn;
+ cosn = we[si[e]]; /* Current smallest value */
+ for (f = e+1; f < di; f++) { /* Check against rest */
+ int tt;
+ tt = si[f];
+ if (cosn > we[tt]) {
+ si[f] = si[e]; /* Exchange */
+ si[e] = tt;
+ cosn = we[tt];
+ }
+ }
+ }
+ }
+ DEBLU(("si[] = %s\n", icmPiv(di, si)));
+
+ /* Now compute the weightings, simplex vertices and output values */
+ {
+ double w; /* Current vertex weight */
+
+ w = 1.0 - we[si[di-1]]; /* Vertex at base of cell */
+ for (f = 0; f < fdi; f++)
+ p->v[f] = w * gp[f];
+
+ DEBLU(("ix %d: w %f * val %s\n", (gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
+
+ for (e = di-1; e > 0; e--) { /* Middle verticies */
+ w = we[si[e]] - we[si[e-1]];
+ gp += s->g.fci[si[e]]; /* Move to top of cell in next largest dimension */
+ for (f = 0; f < fdi; f++)
+ p->v[f] += w * gp[f];
+ DEBLU(("ix %d: w %f * val %s\n", (gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
+ }
+
+ w = we[si[0]];
+ gp += s->g.fci[si[0]]; /* Far corner from base of cell */
+ for (f = 0; f < fdi; f++)
+ p->v[f] += w * gp[f];
+ DEBLU(("ix %d: w %f * val %s\n", (gp - s->g.a)/s->g.pss, w, icmPfv(fdi,gp)));
+ DEBLU(("Outval %s\n", icmPdv(fdi, p->v)));
+ }
+ return rv;
+}
+
+/* ============================================ */
+/* Do forward (partial) interpolation to allow input & output curves to be applied, */
+/* and allow input delta E to be estimated from output delta E. */
+/* Call with input value in p1[0].p[], */
+/* In order smallest to largest weight: */
+/* Return di+1 vertex values in p1[]].v[] and */
+/* 0-1 sub-cell weight values as (p1[].p[0] - p1[].p[1]). */
+/* Optionally in input channel order: */
+/* Returns di+1 partial derivatives + base value in p2[].v[], */
+/* with matching weight values for each in p2[].p[0] (last weight = 1)*/
+/* Return 0 if OK, 1 if input was clipped to grid */
+static int part_interp_rspl_sx(
+struct _rspl *s, /* this */
+co *p1,
+co *p2 /* optional - return partial derivatives for each input channel */
+) {
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+ double we[MXDI]; /* Coordinate offset within the grid cell */
+ int si[MXDI]; /* we[] Sort index, [0] = smallest */
+ float *gp; /* Pointer to grid cube base */
+ int rv = 0; /* Register clip */
+
+ /* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */
+
+ /* Figure out which grid cell the point falls into */
+ {
+ gp = s->g.a; /* Base of grid array */
+ for (e = 0; e < di; e++) {
+ int gres_1 = s->g.res[e]-1;
+ double pe, t;
+ int mi;
+ pe = p1[0].p[e];
+ if (pe < s->g.l[e]) { /* Clip to grid */
+ pe = s->g.l[e];
+ rv = 1;
+ }
+ if (pe > s->g.h[e]) {
+ pe = s->g.h[e];
+ rv = 1;
+ }
+ t = (pe - s->g.l[e])/s->g.w[e];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= gres_1)
+ mi = gres_1-1;
+ gp += mi * s->g.fci[e]; /* Add Index offset for grid cube base in dimen */
+ we[e] = t - (double)mi; /* 1.0 - weight */
+ }
+ }
+
+ /* Do selection sort on coordinates */
+ {
+ for (e = 0; e < di; e++)
+ si[e] = e; /* Initial unsorted indexes */
+ for (e = 0; e < (di-1); e++) {
+ double cosn;
+ cosn = we[si[e]]; /* Current smallest value */
+ for (f = e+1; f < di; f++) { /* Check against rest */
+ int tt;
+ tt = si[f];
+ if (cosn > we[tt]) {
+ si[f] = si[e]; /* Exchange */
+ si[e] = tt;
+ cosn = we[tt];
+ }
+ }
+ }
+ }
+ /* Now compute the vertex values that correspond */
+ /* to the input faction weightings + fixed value */
+ /* Scale the slopes + weights to make slopes */
+ /* valid as partial derivative of input values */
+ {
+
+ p1[di].p[0] = 1.0;
+ p1[di].p[1] = we[si[di-1]]; /* Vertex at base of cell */
+ for (f = 0; f < fdi; f++)
+ p1[di].v[f] = gp[f];
+
+ if (p2 != NULL) {
+ for (f = 0; f < fdi; f++)
+ p2[di].v[f] = gp[f]; /* Constant term @ vertex base */
+ p2[di].p[0] = 1.0;
+ }
+
+ for (e = di-1; e >= 0; e--) { /* Middle verticies to far vertex from base */
+ int ee = si[e];
+ float *lgp = gp; /* Last gp[] */
+
+ gp += s->g.fci[ee]; /* Move to top of cell in next largest dimension */
+
+ p1[e].p[0] = we[si[e]];
+ p1[e].p[1] = e > 0 ? we[si[e-1]] : 0.0;
+ for (f = 0; f < fdi; f++)
+ p1[e].v[f] = gp[f];
+
+ if (p2 != NULL) {
+ for (f = 0; f < fdi; f++)
+ p2[ee].v[f] = (gp[f] - lgp[f]) / s->g.w[ee];
+ p2[ee].p[0] = we[ee] * s->g.w[ee];
+ }
+ }
+ }
+ return rv;
+}
+
+#ifdef NEVER
+/* Test out part_interp_rspl_sx() */
+/* Designed to test with a CMYK->Lab lookup */
+static int interp_rspl_sx(
+rspl *s,
+co *p /* Input value and returned function value */
+) {
+ int rv, rv2;
+ int e, f, m;
+ co p1[MXDI+1];
+ co p2[MXDI+1];
+ co p3;
+ double v1[MXDO];
+ double v2[MXDO];
+
+ for (e = 0; e < s->di; e++) {
+ p1[0].p[e] = p->p[e];
+ p3.p[e] = p->p[e];
+ }
+
+ rv = _interp_rspl_sx(s, p);
+
+ if ((s->di != 4 || s->fdi != 3)
+ && (s->di != 3 || s->fdi != 4))
+ return rv;
+
+ rv2 = part_interp_rspl_sx(s, p1, p2);
+
+ /* Check interpolation values returned in p1 and p2 form */
+ for (f = 0; f < s->fdi; f++)
+ v1[f] = v2[f] = 0.0;
+
+ for (e = 0; e <= s->di; e++) {
+ for (f = 0; f < s->fdi; f++) {
+ /* We could converts p1[].p[0] and p1[].p[1] through sub curve lookup, */
+ /* and p1[].v[] though inverse output curve lookup, */
+ /* then convert v1[] through output curve lookup. */
+ v1[f] += p1[e].v[f] * (p1[e].p[0] - p1[e].p[1]);
+
+ /* v2 is using base + partial derivatives */
+ v2[f] += p2[e].v[f] * p2[e].p[0];
+ }
+ }
+
+ if (s->di == 4) {
+ printf("~1 %f %f %f %f ->\n",p->p[0], p->p[1], p->p[2], p->p[3]);
+ printf("~1 ref %d -> %f %f %f\n", rv, p->v[0], p->v[1], p->v[2]);
+ printf("~1 check1 %d -> %f %f %f\n", rv2, v1[0], v1[1], v1[2]);
+ printf("~1 check2 %d -> %f %f %f\n", rv2, v2[0], v2[1], v2[2]);
+ } else {
+ printf("~1 %f %f %f ->\n",p->p[0], p->p[1], p->p[2]);
+ printf("~1 ref %d -> %f %f %f %f\n", rv, p->v[0], p->v[1], p->v[2], p->v[3]);
+ printf("~1 check1 %d -> %f %f %f %f\n", rv2, v1[0], v1[1], v1[2], v1[3]);
+ printf("~1 check2 %d -> %f %f %f %f\n", rv2, v2[0], v2[1], v2[2], v2[3]);
+ }
+
+ /* Check partial derivs in p2 */
+ for (m = 0; m < s->di; m++) {
+
+ p3.p[m] += 1e-5;
+
+ _interp_rspl_sx(s, &p3);
+ for (f = 0; f < s->fdi; f++)
+ p3.v[f] = (p3.v[f] - p->v[f])/1e-5;
+
+ if (s->di == 4) {
+ printf("~1 deriv %d:\n", m);
+ printf("~1 ref del %f %f %f\n", p3.v[0], p3.v[1], p3.v[2]);
+ printf("~1 check del %f %f %f\n", p2[m].v[0], p2[m].v[1], p2[m].v[2]);
+ } else {
+ printf("~1 deriv %d:\n", m);
+ printf("~1 ref del %f %f %f %f\n", p3.v[0], p3.v[1], p3.v[2], p3.v[3]);
+ printf("~1 check del %f %f %f %f\n", p2[m].v[0], p2[m].v[1], p2[m].v[2], p2[m].v[3]);
+ }
+
+ p3.p[m] -= 1e-5;
+ }
+
+ return rv;
+}
+#endif
+
+/* ============================================ */
+
+#ifdef USING_INTERP_NL
+/* Alternate, not currently used */
+/* Do a forward interpolation using an n-linear method. */
+/* Return 0 if OK, 1 if input was clipped to grid */
+/* Alternative to interp_rspl_sx */
+static int interp_rspl_nl(
+rspl *s,
+co *p /* Input value and returned function value */
+) {
+ int e, di = s->di;
+ int f, fdi = s->fdi;
+ double we[MXDI]; /* 1.0 - Weight in each dimension */
+ double *gw; /* weight for each grid cube corner */
+ double a_gw[DEF2MXDI]; /* Default space for gw */
+ float *gp; /* Pointer to grid cube base */
+ int rv = 0;
+
+ gw = a_gw;
+ if ((1 << di) > DEF2MXDI) {
+ if ((gw = (double *) malloc(sizeof(double) * (1 << di))) == NULL)
+ error("rspl malloc failed - interp_rspl_nl");
+ }
+ /* Figure out which grid cell the point falls into */
+ {
+ gp = s->g.a; /* Base of grid array */
+ for (e = 0; e < di; e++) {
+ int gres_1 = s->g.res[e]-1;
+ double pe, t;
+ int mi;
+ pe = p->p[e];
+ if (pe < s->g.l[e]) { /* Clip to grid */
+ pe = s->g.l[e];
+ rv = 1;
+ }
+ if (pe > s->g.h[e]) {
+ pe = s->g.h[e];
+ rv = 1;
+ }
+ t = (pe - s->g.l[e])/s->g.w[e];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= gres_1)
+ mi = gres_1-1;
+ gp += mi * s->g.fci[e]; /* Add Index offset for grid cube base in dimen */
+ we[e] = t - (double)mi; /* 1.0 - weight */
+ }
+ }
+
+ /* Compute corner weights needed for interpolation */
+ {
+ int i, g;
+ gw[0] = 1.0;
+ for (e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++) {
+ gw[g+i] = gw[i] * we[e];
+ gw[i] *= (1.0 - we[e]);
+ }
+ }
+ }
+
+ /* Now compute the output values */
+ {
+ int i;
+ double w = gw[0];
+ float *d = gp + s->g.fhi[0];
+ for (f = 0; f < fdi; f++) /* Base of cube */
+ p->v[f] = w * d[f];
+
+ for (i = 1; i < (1 << di); i++) { /* For all other corners of cube */
+ double w = gw[i]; /* Strength reduce */
+ float *d = gp + s->g.fhi[i];
+ for (f = 0; f < fdi; f++)
+ p->v[f] += w * d[f];
+ }
+
+ }
+
+ if (gw != a_gw)
+ free(gw);
+
+ return rv;
+}
+#endif /* USING_INTERP_NL */
+
+/* ============================================ */
+/* Non-mono calculations */
+/* Compute non-monotonicity factor for each grid point, and */
+/* return non-zero if the overall grid is monotonic. */
+/* (Note that this is not a true non-monotonicity test. */
+/* A true test has to deal with PCS combination values.) */
+int
+is_mono(
+rspl *s
+) {
+ int f;
+ int di = s->di;
+ int fdi = s->fdi;
+ int *fci = s->g.fci; /* Strength reduction */
+ float *gp, *ep;
+ double mcinc = MCINC/(s->g.mres-1); /* Scaled version of MCINC */
+ double min = 1e20; /* Minimum clearance found */
+
+ /* Find the minimum step between grid points */
+ for (gp = s->g.a, ep = s->g.a + s->g.no * s->g.pss; gp < ep; gp += s->g.pss) {
+ for (f = 0; f < fdi; f++) {
+ int e;
+ double e1,e2; /* Smallest/largest surrounting point */
+ double u; /* Current output value we are considering */
+ double ce; /* nm error */
+
+ /* Find smallest and largest surrounding points */
+ /* In +/- 1 dimension directions */
+ e1 = 1e20; e2 = -1e20;
+ for (e = 0; e < di; e++) {
+ int dof; /* Double offset */
+ float vv;
+
+ if ((G_FL(gp,e) & 3) < 1)
+ break; /* Skip to next grid point if on edge */
+ dof = fci[e];
+ vv = gp[f + dof];
+ if (vv < e1)
+ e1 = vv;
+ if (vv > e2)
+ e2 = vv;
+ vv = gp[f - dof];
+ if (vv < e1)
+ e1 = vv;
+ if (vv > e2)
+ e2 = vv;
+ }
+ if (e < di) /* We broke because we are on the edge */
+ continue;
+
+ u = gp[f];
+
+ e1 = u - e1;
+ e2 = e2 - u;
+ ce = (e1 < e2 ? e1 : e2); /* Smallest step */
+
+ if (ce < min) /* Current smallest step */
+ min = ce;
+ }
+ }
+//if (min < mcinc) printf("~1 is_mono failed by %e < %e\n",min,mcinc);
+ return min < mcinc;
+}
+
+/* ============================================ */
+/* Initialize the grid from a provided function. By default the grid */
+/* values are set to exactly the value returned fy func(), unless the */
+/* RSPL_SET_APXLS flag is set, in which case an attempt is made to have */
+/* the grid points represent a least squares aproximation to the underlying */
+/* surface, by using extra samples in the middle of grid cells. */
+/* RSPL_SET_APXLS tends to improve the fit to the underlying function. */
+/* Grid index values are supplied "under" in[] at *((int*)&iv[-e-1]), */
+/* but if RSPL_SET_APXLS is set, the grid index will be the base of */
+/* the cell the center point is sampled from every second sample. */
+/* Return non-monotonic status */
+static int set_rspl(
+ struct _rspl *s,/* this */
+ int flags, /* Combination of flags */
+ void *cbctx, /* Opaque function context */
+ void (*func)(void *cbctx, double *out, double *in), /* Function to set from */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution for each dimension */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh /* Data value high normalize - NULL = default 1.0 */
+) {
+ int e, f, j;
+ rpsh counter; /* Pseudo-hilbert counter */
+ int gc[MXDI]; /* Grid index value */
+ float *gp; /* Pointer to grid data */
+ float *cc = NULL; /* Pointer to cell center data */
+ double _iv[2 * MXDI], *iv = &_iv[MXDI]; /* Real index value/table value */
+ double ov[MXDO];
+
+ if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
+ s->verbose = 1;
+ if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
+ s->verbose = 0;
+
+ /* transfer desired grid range to structure */
+ s->g.mres = 1.0;
+ s->g.bres = 0;
+ for (e = 0; e < s->di; e++) {
+
+ if (gres[e] < 2)
+ error("rspl: grid res must be >= 2!");
+ s->g.res[e] = gres[e]; /* record the desired resolution of the grid */
+ s->g.mres *= gres[e];
+ if (gres[e] > s->g.bres) {
+ s->g.bres = gres[e];
+ s->g.brix = e;
+ }
+
+ if (glow == NULL)
+ s->g.l[e] = 0.0;
+ else
+ s->g.l[e] = glow[e];
+
+ if (ghigh == NULL)
+ s->g.h[e] = 1.0;
+ else
+ s->g.h[e] = ghigh[e];
+
+ /* compute width of each grid cell */
+ s->g.w[e] = (s->g.h[e] - s->g.l[e])/(double)(s->g.res[e]-1);
+
+ /* ?? Should h be recomputed as (l + gres-1) * w ?? */
+ }
+ s->g.mres = pow(s->g.mres, 1.0/e); /* geometric mean */
+
+ /* record low and width data normalizing factors */
+ for (f = 0; f < s->fdi; f++) {
+ if (vlow == NULL)
+ s->d.vl[f] = 0.0;
+ else
+ s->d.vl[f] = vlow[f];
+
+ if (vhigh == NULL)
+ s->d.vw[f] = 1.0 - s->d.vl[f];
+ else
+ s->d.vw[f] = vhigh[f] - s->d.vl[f];
+ }
+
+ /* Allocate the grid data */
+ alloc_grid(s);
+
+ /* Allocate space for cell center value lookup */
+ if (flags & RSPL_SET_APXLS) {
+ if ((cc = (float *)malloc(sizeof(float) * s->g.no * s->fdi)) == NULL)
+ error("rspl malloc failed - center cell points");
+ }
+
+ /* Reset output min/max */
+ for (f = 0; f < s->fdi; f++) {
+ s->g.fmin[f] = 1e30;
+ s->g.fmax[f] = -1e30;
+ s->g.fminx[f] = -1;
+ s->g.fmaxx[f] = -1;
+ }
+
+ /* Set the grid points value from the provided function */
+
+ /* To make this clut function cache friendly, we use the pseudo-hilbert */
+ /* count sequence. This keeps each point close to the last in the */
+ /* multi-dimensional space. */
+ rpsh_init(&counter, s->di, (unsigned int *)gres, gc); /* Initialise counter */
+ for (;;) {
+
+ /* Compute grid pointer and input sample values */
+ gp = s->g.a; /* Base of grid data */
+ for (e = 0; e < s->di; e++) { /* Input tables */
+ gp += gc[e] * s->g.fci[e]; /* Grid value pointer */
+ iv[e] = s->g.l[e] + gc[e] * s->g.w[e]; /* Input sample values */
+ *((int *)&iv[-e-1]) = gc[e]; /* Trick to supply grid index in iv[] */
+ }
+
+ /* Apply incolor -> outcolor function we want to represent */
+ func(cbctx, ov, iv);
+
+ for (f = 0; f < s->fdi; f++) { /* Output chans */
+ gp[f] = (float)ov[f]; /* Set output value */
+ if (s->g.fmin[f] > gp[f]) {
+ s->g.fmin[f] = gp[f];
+ s->g.fminx[f] = (gp - s->g.a)/s->g.pss;
+ }
+ if (s->g.fmax[f] < gp[f]) {
+ s->g.fmax[f] = gp[f];
+ s->g.fmaxx[f] = (gp - s->g.a)/s->g.pss;
+ }
+ }
+
+ /* For RSPL_SET_APXLS, get the center of the cell values as well. */
+ if (cc != NULL) {
+ float *ccp;
+
+ ccp = cc;
+ for (e = 0; e < s->di; e++) { /* Input tables */
+ if (gc[e] >= (gres[e]-1))
+ break; /* No center for outer row */
+ iv[e] = s->g.l[e] + (gc[e] + 0.5) * s->g.w[e]; /* Input sample values */
+ ccp += gc[e] * s->g.ci[e] * s->fdi; /* cc location */
+ }
+
+ if (e >= s->di) { /* Not outer row */
+ /* Apply incolor -> outcolor function we want to represent */
+ func(cbctx, ov, iv);
+
+ for (f = 0; f < s->fdi; f++) { /* Output chans */
+ ccp[f] = (float)ov[f]; /* Set output value */
+ }
+ }
+ }
+
+ /* Increment counter */
+ if (rpsh_inc(&counter, gc))
+ break;
+ }
+
+ /* For RSPL_SET_APXLS, deal with cell center value, aproximate least squares adjustment */
+ if (cc != NULL) {
+ int ee;
+ double cw = 1.0/(double)(1 << s->di); /* Weight for each cube corner */
+ float *ccp;
+
+ for (e = 0; e < s->di; e++)
+ gc[e] = 0; /* init coords */
+
+ /* Compute linear interpolated error to actual cell center value */
+ for (ee = 0; ee < s->di;) {
+
+ gp = s->g.a; /* Base of grid data */
+ ccp = cc; /* Base of center data */
+ for (e = 0; e < s->di; e++) { /* Input tables */
+ gp += gc[e] * s->g.fci[e]; /* Grid value pointer */
+ ccp += gc[e] * s->g.ci[e] * s->fdi; /* cc location */
+ }
+
+ for (f = 0; f < s->fdi; f++) { /* Output chans */
+ double sum = 0.0;
+
+ for (j = 0; j < (1 << s->di); j++) /* For corners of cube */
+ sum += (gp + s->g.fhi[j])[f];
+ sum *= cw; /* Interpolated value */
+ ccp[f] -= sum; /* Correction to actual value */
+
+ /* Average half the error to cube corners */
+ ccp[f] *= 0.5 * cw; /* Distribution fraction */
+ }
+
+ /* Increment coord */
+ for (ee = 0; ee < s->di; ee++) {
+ if (++gc[ee] < (gres[ee]-1)) /* Don't go through upper edge */
+ break; /* No carry */
+ gc[ee] = 0;
+ }
+ }
+
+ for (e = 0; e < s->di; e++)
+ gc[e] = 0; /* init coords */
+
+ /* Distribute the center error to the cell corners */
+ for (ee = 0; ee < s->di;) {
+
+ gp = s->g.a; /* Base of grid data */
+ ccp = cc; /* Base of center data */
+ for (e = 0; e < s->di; e++) { /* Input tables */
+ gp += gc[e] * s->g.fci[e]; /* Grid value pointer */
+ ccp += gc[e] * s->g.ci[e] * s->fdi; /* cc location */
+ }
+
+ for (j = 0; j < (1 << s->di); j++) { /* For corners of cube */
+ double sc = 1.0; /* Scale factor for non-edge nodes */
+
+ /* Don't distribute error to edge nodes since there may */
+ /* an expectation that they have precicely set values */
+ /* (ie. white and black points) */
+ for (e = 0; e < s->di; e++) {
+ if ((gc[e] == 0 && (j & (1 << e)) == 0)
+ || (gc[e] == ((gres[e]-2)) && (j & (1 << e)) != 0))
+ sc *= 0.0;
+ }
+
+ for (f = 0; f < s->fdi; f++) { /* Output chans */
+ double vv;
+ vv = (gp + s->g.fhi[j])[f]; /* Current value */
+ vv += sc * cc[f]; /* Correction */
+ (gp + s->g.fhi[j])[f] = vv;
+ if (s->g.fmin[f] > vv) {
+ s->g.fmin[f] = vv;
+ s->g.fminx[f] = (gp + s->g.fhi[j] - s->g.a)/s->g.pss;
+ }
+ if (s->g.fmax[f] < vv) {
+ s->g.fmax[f] = vv;
+ s->g.fmaxx[f] = (gp + s->g.fhi[j] - s->g.a)/s->g.pss;
+ }
+ }
+ }
+
+ /* Increment coord */
+ for (ee = 0; ee < s->di; ee++) {
+ if (++gc[ee] < (gres[ee]-1)) /* Don't go through upper edge */
+ break; /* No carry */
+ gc[ee] = 0;
+ }
+ }
+
+ free((void *)cc);
+ }
+
+ /* Compute overall output scale */
+ for (s->g.fscale = 0.0, f = 0; f < s->fdi; f++) {
+ double tt = s->g.fmax[f] - s->g.fmin[f];
+ s->g.fscale += tt * tt;
+ }
+ s->g.fscale = sqrt(s->g.fscale);
+
+ s->g.fminmax_valid = 1; /* Now is valid */
+
+ /* Return non-mono check */
+ return is_mono(s);
+}
+
+/* ============================================ */
+/* Scan or change each grid point in the rspl. */
+static int scan_set_rspl(
+struct _rspl *s, /* this */
+int flags, /* Combination of flags */
+void *cbctx, /* Opaque function context */
+void (*func)(void *cbntx, double *out, double *in), /* Function to get/set from */
+int change /* Flag - nz means change values, 0 means scan values */
+) {
+ int e, f;
+ rpsh counter; /* Pseudo-hilbert counter */
+ int gc[MXDI]; /* Grid index value */
+ float *gp; /* Pointer to grid data */
+ double _iv[2 * MXDI], *iv = &_iv[MXDI]; /* Real index value/table value */
+ double ov[MXDO];
+
+ if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
+ s->verbose = 1;
+ if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
+ s->verbose = 0;
+
+ if (change) {
+ /* Reset output min/max */
+ for (f = 0; f < s->fdi; f++) {
+ s->g.fmin[f] = 1e30;
+ s->g.fmax[f] = -1e30;
+ s->g.fminx[f] = -1;
+ s->g.fmaxx[f] = -1;
+ }
+ }
+
+ /* Set the grid points value from the provided function */
+ /* Give the function both the grid position and the existing output values */
+
+ /* To make this clut function cache friendly, we use the pseudo-hilbert */
+ /* count sequence. This keeps each point close to the last in the */
+ /* multi-dimensional space. */
+ rpsh_init(&counter, s->di, (unsigned int *)s->g.res, gc); /* Initialise counter */
+ for (;;) {
+
+ /* Compute grid pointer and input sample values */
+ gp = s->g.a; /* Base of grid data */
+ for (e = 0; e < s->di; e++) { /* Input tables */
+ gp += s->g.fci[e] * gc[e]; /* Grid value pointer */
+ iv[e] = s->g.l[e] + gc[e] * s->g.w[e]; /* Input sample values */
+ *((int *)&iv[-e-1]) = gc[e]; /* Trick to supply grid index in iv[] */
+ }
+
+ for (f = 0; f < s->fdi; f++) /* Output chans */
+ ov[f] = gp[f];
+
+ /* Let function scan the input and output values, or */
+ /* Apply incolor -> outcolor, or oldoutcolor->outcolor function we want to represent */
+ func(cbctx, ov, iv);
+
+ if (change) { /* Put new output values back */
+ for (f = 0; f < s->fdi; f++) { /* Output chans */
+ gp[f] = (float)ov[f];
+ if (s->g.fmin[f] > gp[f]) {
+ s->g.fmin[f] = gp[f];
+ s->g.fminx[f] = (gp - s->g.a)/s->g.pss;
+ }
+ if (s->g.fmax[f] < gp[f]) {
+ s->g.fmax[f] = gp[f];
+ s->g.fmaxx[f] = (gp - s->g.a)/s->g.pss;
+ }
+ }
+ }
+
+ /* Increment counter */
+ if (rpsh_inc(&counter, gc))
+ break;
+ }
+
+ if (change == 0) {
+ return 0;
+ }
+
+ /* Compute overall output scale */
+ for (s->g.fscale = 0.0, f = 0; f < s->fdi; f++) {
+ double tt = s->g.fmax[f] - s->g.fmin[f];
+ s->g.fscale += tt * tt;
+ }
+ s->g.fscale = sqrt(s->g.fscale);
+
+ s->g.fminmax_valid = 1; /* Now is valid */
+
+ /* Invalidate various things */
+ free_data(s); /* Free any scattered data */
+ free_rev(s); /* Free any reverse lookup data */
+
+ /* Return non-mono check */
+ return is_mono(s);
+}
+
+/* Re-initialize the grid from existing grid values, and the provided function */
+/* Grid index values are supplied "under" in[] at *((int*)&iv[-e-1]) */
+/* Return non-monotonic status. We assume that the ouput scale factors don't change. */
+static int re_set_rspl(
+struct _rspl *s, /* this */
+int flags, /* Combination of flags */
+void *cbctx, /* Opaque function context */
+void (*func)(void *cbntx, double *out, double *in) /* Function to set from */
+) {
+ return scan_set_rspl(s, flags, cbctx, func, 1);
+}
+
+/* Scan the rspl grid point locations and values. Grid index values are */
+/* supplied "under" in[] *((int*)&iv[-e-1]) */
+static void scan_rspl(
+struct _rspl *s, /* this */
+int flags, /* Combination of flags */
+void *cbctx, /* Opaque function context */
+void (*func)(void *cbntx, double *out, double *in) /* Function to get from */
+) {
+ scan_set_rspl(s, flags, cbctx, func, 0);
+}
+
+
+/* ============================================ */
+/* Allow the grid values to be filtered. */
+/* For each grid value, provide the input value and */
+/* pointers to all the output values in a 3^di grid around */
+/* the output value. Pointers will be NULL if neigbour is outside */
+/* the grid. cvi is the index of the output value. */
+/* Grid index values are supplied "under" in[] at *((int*)&iv[-e-1]) */
+/* After all the grid values have been done, they will be updated */
+/* with their new values. */
+static void filter_rspl(
+struct _rspl *s, /* this */
+int flags, /* Combination of flags */
+void *cbctx, /* Opaque function context */
+void (*func)(void *cbntx, float **out, double *in, int cvi) /* Function to set from */
+) {
+ int e, f;
+ ECOUNT(gc, MXDIDO, s->di, 0, s->g.res, 0); /* coordinates */
+ DCOUNT(cc, MXDIDO, s->di, -1, -1, 2); /* Surrounding cube counter */
+ float *gp, *ep; /* Pointer to grid data */
+ float *tarry, *tp; /* Temporary array of values */
+ double _iv[2 * MXDI], *iv = &_iv[MXDI]; /* Real index value/table value */
+ int cvi; /* Center value index = 3^di-1)/2 */
+ int pow3di = 1;
+ float **svals; /* Pointer to surrounding output values */
+ float *a_svals[DEF3MXDI];/* default allocation for svals */
+
+ if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
+ s->verbose = 1;
+ if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
+ s->verbose = 0;
+
+ /* Allocate svals array */
+ svals = a_svals;
+ for (e = 0; e < s->di; e++)
+ pow3di *= 3;
+ if (pow3di > DEF3MXDI) {
+ if ((svals = (float **) malloc(sizeof(float *) * pow3di)) == NULL)
+ error("rspl malloc failed - filter_rspl");
+ }
+
+ /* Compute the center value index */
+ for (cvi = 1, e = 0; e < s->di; e++)
+ cvi *= 3;
+ cvi = (cvi-1)/2;
+
+ /* Allocate a temporary array for the new output values */
+ if ((tarry = (float *)malloc(sizeof(float) * s->g.no * s->fdi)) == NULL) {
+ if (svals != a_svals)
+ free(svals);
+ error("rspl malloc failed - filter_rspl array");
+ }
+
+ /* Set the grid points value from the provided function */
+ /* Give the function both the grid position and the existing output values */
+ /* in the 3x3 surrounding grid */
+ EC_INIT(gc);
+ for (tp = tarry; !EC_DONE(gc); tp += s->fdi) {
+ int i;
+
+ /* Compute grid pointer and input sample values */
+ for (e = 0; e < s->di; e++) {
+ iv[e] = s->g.l[e] + gc[e] * s->g.w[e]; /* Input sample values */
+ *((int *)&iv[-e-1]) = gc[e]; /* Trick to supply grid index in iv[] */
+ }
+
+ /* Set pointers to 3x3 surrounders */
+ DC_INIT(cc)
+ for (i = 0; !DC_DONE(cc); i++ ) {
+ float *sp = s->g.a;
+
+ for (e = 0; e < s->di; e++) { /* Input tables */
+ int j;
+ j = gc[e] + cc[e];
+ if (j < 0 || j >= s->g.res[e]) {
+ sp = NULL; /* outside grid */
+ break;
+ }
+ sp += s->g.fci[e] * j; /* Compute pointer to surrounder */
+ }
+
+ svals[i] = sp;
+ DC_INC(cc);
+ }
+
+ for (f = 0; f < s->fdi; f++) /* Set default no change new values */
+ tp[f] = svals[cvi][f];
+ svals[cvi] = tp; /* Make sure output value goes into temp array */
+
+ /* Apply incolor -> outcolor, or oldoutcolor->outcolor function we want to represent */
+ func(cbctx, svals, iv, cvi);
+
+ EC_INC(gc);
+ }
+
+ /* Reset output min/max */
+ for (f = 0; f < s->fdi; f++) {
+ s->g.fmin[f] = 1e30;
+ s->g.fmax[f] = -1e30;
+ s->g.fminx[f] = -1;
+ s->g.fmaxx[f] = -1;
+ }
+
+ /* Now update all the values */
+ for (tp = tarry, gp = s->g.a, ep = s->g.a + s->g.no * s->g.pss;
+ gp < ep; gp += s->g.pss, tp += s->fdi) {
+
+ for (f = 0; f < s->fdi; f++) /* Output chans */
+ gp[f] = tp[f];
+
+ for (f = 0; f < s->fdi; f++) { /* Output chans */
+ if (s->g.fmin[f] > gp[f]) {
+ s->g.fmin[f] = gp[f];
+ s->g.fminx[f] = (gp - s->g.a)/s->g.pss;
+ }
+ if (s->g.fmax[f] < gp[f]) {
+ s->g.fmax[f] = gp[f];
+ s->g.fmaxx[f] = (gp - s->g.a)/s->g.pss;
+ }
+ }
+ }
+
+ /* Compute overall output scale */
+ for (s->g.fscale = 0.0, f = 0; f < s->fdi; f++) {
+ double tt = s->g.fmax[f] - s->g.fmin[f];
+ s->g.fscale += tt * tt;
+ }
+ s->g.fscale = sqrt(s->g.fscale);
+
+ s->g.fminmax_valid = 1; /* Now is valid */
+
+ if (svals != a_svals)
+ free(svals);
+ free(tarry);
+
+ /* Invalidate various things */
+ free_data(s); /* Free any scattered data */
+ free_rev(s); /* Free any reverse lookup data */
+}
+
+
+/* =============================================== */
+/* Utility function */
+/* Pseudo - Hilbert count sequencer */
+
+/* Initialise, returns total usable count */
+unsigned rpsh_init(
+rpsh *p, /* Pointer to structure to initialise */
+int di, /* Dimensionality */
+unsigned int *res, /* Size per coordinate */
+int co[] /* Coordinates to initialise (May be NULL) */
+) {
+ int e;
+
+ p->di = di;
+ p->tbits = 0;
+ for (e = 0; e < di; e++) {
+ p->res[e] = res[e];
+
+ /* Compute bits */
+ for (p->bits[e] = 0; (1u << p->bits[e]) < res[e]; p->bits[e]++)
+ ;
+ p->tbits += p->bits[e];
+ }
+
+ /* Compute the total count mask */
+ p->tmask = ((((unsigned)1) << p->tbits)-1);
+
+ /* Compute usable count */
+ p->count = 1;
+ for (e = 0; e < di; e++)
+ p->count *= res[e];
+
+ /* Reset the counter */
+ p->ix = 0;
+
+ if (co != NULL) {
+ for (e = 0; e < di; e++)
+ co[e] = 0;
+ }
+ return p->count;
+}
+
+
+/* Reset the counter */
+void rpsh_reset(
+rpsh *p /* Pointer to structure */
+) {
+ p->ix = 0;
+}
+
+
+/* Increment pseudo-hilbert coordinates */
+/* Return non-zero if count rolls over to 0 */
+int rpsh_inc(
+rpsh *p, /* Pointer to structure */
+int coa[] /* Coordinates to return */
+) {
+ int di = p->di;
+ int e;
+
+ do {
+ unsigned int b, tb;
+ int gix; /* Gray code index */
+
+ p->ix = (p->ix + 1) & p->tmask;
+
+ gix = p->ix ^ (p->ix >> 1); /* Convert to gray code index */
+
+ for (e = 0; e < di; e++)
+ coa[e] = 0;
+
+ for (b = tb = 0; tb < p->tbits ; b++) { /* Distribute bits */
+ if (b & 1) {
+ for (e = di-1; e >= 0; e--) { /* In reverse coord order */
+ if (b < p->bits[e]) {
+ coa[e] |= (gix & 1) << b; /* ls bits of gix */
+ gix >>= 1;
+ tb++;
+ }
+ }
+ } else {
+ for (e = 0; e < di; e++) { /* In normal coord order */
+ if (b < p->bits[e]) {
+ coa[e] |= (gix & 1) << b; /* ls bits of gix */
+ gix >>= 1;
+ tb++;
+ }
+ }
+ }
+ }
+
+ /* Convert from Gray to binary coordinates */
+ for (e = 0; e < di; e++) {
+ unsigned sh, tv;
+
+ for(sh = 1, tv = coa[e];; sh <<= 1) {
+ unsigned ptv = tv;
+ tv ^= (tv >> sh);
+ if (ptv <= 1 || sh == 16)
+ break;
+ }
+ if (tv >= p->res[e]) /* Dumbo filter - increment again if outside cube range */
+ break;
+ coa[e] = tv;
+ }
+
+ } while (e < di);
+
+ return (p->ix == 0);
+}
+
+/* =============================================== */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rspl/rspl.h b/rspl/rspl.h
new file mode 100644
index 0000000..5bccba7
--- /dev/null
+++ b/rspl/rspl.h
@@ -0,0 +1,645 @@
+#ifndef RSPL_H
+#define RSPL_H
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized spline data structure
+ *
+ * Author: Graeme W. Gill
+ * Date: 2000/10/29
+ *
+ * Copyright 1996 - 2004 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+#include "numsup.h"
+
+/** Configuration **/
+
+/** General Limits **/
+
+#define MXDI 10 /* Maximum input dimensionality */
+#define MXDO 10 /* Maximum output dimensionality (Is not fully tested!!!) */
+#define LOG2MXDI 4 /* log2 MXDI */
+#define DEF2MXDI 16 /* Default allocation size for 2^di (di=4) */
+#define POW2MXDI 1024 /* 2 ^ MXDI */
+#define DEF3MXDI 81 /* Default allocation size for 3^di (di=4) */
+#define POW3MXDI 59049 /* 3 ^ MXDI */
+
+#if MXDI > MXDO /* Maximum of either DI or DO */
+# define MXDIDO MXDI
+#else
+# define MXDIDO MXDO
+#endif
+
+/* RESTRICTED SIZE Limits, used for reverse, spline and scattered interpolation */
+
+#define MXRI 4 /* Maximum input dimensionality */
+#define MXRO 10 /* Maximum output dimensionality (Is not fully tested!!!) */
+#define LOG2MXRI 2 /* log2 MXRI */
+#define POW2MXRI 16 /* 2 ^ MXRI */
+#define POW3MXRI 81 /* 3 ^ MXRI */
+#define HACOMPS ((POW3MXRI + 2 * MXRI + 1)/2) /* Maximum number of array components */
+
+#if MXRI > MXRO /* Maximum of either RI or RO */
+# define MXRIRO MXRI
+#else
+# define MXRIRO MXRO
+#endif
+
+
+/** Definitions **/
+
+/* General data point position/value structure */
+/* This is mean't to be compatible with color structure */
+/* when MXDI and MXDO == 4 */
+typedef double datai[MXDI];
+typedef double datao[MXDO];
+typedef float dati[MXDI];
+typedef float dato[MXDO];
+
+/* Restricted size versions */
+typedef double ratai[MXRI];
+typedef double ratao[MXRO];
+typedef float rati[MXRI];
+typedef float rato[MXRO];
+
+/* Interface coordinate value */
+typedef struct {
+ double p[MXDI]; /* coordinate position */
+ double v[MXDO]; /* function values */
+} co;
+
+/* Interface coordinate value + weighting */
+typedef struct {
+ double p[MXDI]; /* coordinate position */
+ double v[MXDO]; /* function values */
+ double w; /* Weight to give this point, nominally 1.0 */
+} cow;
+
+/* Interface coordinate value + per out component weighting */
+typedef struct {
+ double p[MXDI]; /* coordinate position */
+ double v[MXDO]; /* function values */
+ double w[MXDO]; /* Weight to give this point, nominally 1.0 */
+} coww;
+
+/* Scattered data Per data point data (internal) */
+struct _rpnts {
+ double p[MXRI]; /* Data position [di] */
+ double v[MXRO]; /* Data value [fdi] */
+ double k[MXRO]; /* Weight factor (nominally 1.0, less for lower confidence data point) */
+ double cv[MXRO]; /* Extra fit corrected v[fdi] */
+}; typedef struct _rpnts rpnts;
+
+/* Hermite interpolation magic data */
+typedef struct {
+ int p; /* The parameter power combination */
+ int i; /* The surrounding cube vertex index */
+ int j; /* The dimension combination */
+ float wgt;
+} magic_data;
+
+#include "rev.h" /* Reverse interpolation defintions */
+#include "gam.h" /* Gamut defintions */
+
+/* Structure for final resolution multi-dimensional regularized spline data */
+struct _rspl {
+
+ /* Global rspl state */
+ int debug; /* 0 = no debug */
+ int verbose; /* 0 = no verbose */
+ double smooth; /* Smoothness factor */
+ double avgdev[MXDO];
+ /* Average Deviation of function values as proportion of function range. */
+ int symdom; /* 0 = non-symetric smoothness with different grid resolutions, */
+ /* 1 = symetric smoothness with different grid resolutions, */
+
+ int di; /* Input dimensionality */
+ int fdi; /* Output function dimensionality */
+
+ /* Weak default function related information */
+ double weak; /* Weak total weighting, nominal = 1.0 */
+ void *dfctx; /* Opaque function context */
+ void (*dfunc)(void *cbntx, double *out, double *in);
+ /* Function to set from */
+
+ /* Scattered Data point related information */
+ int zf; /* Extra fitting flag - Compensate for data fit errors each round */
+ int tpsm; /* Two pass smoothing flag (if set to 1). */
+ int tpsm2; /* Two pass smoothing 2nd pass flag */
+ struct {
+ int no; /* Number of data points in array */
+ rpnts *a; /* Array of data points */
+ datao vl,vw; /* Data value low/width - used to normalize smoothness values */
+ datao va; /* Data value averages */
+ } d;
+ int niters; /* Number of multigrid itterations needed */
+ int **ires; /* Resolution for each itteration and dimension */
+ void **mgtmps[MXRO]; /* Store pointers to re-usable mgtmp when incremental */
+
+
+ /* Grid points data */
+ struct {
+ int res[MXDI]; /* Single dimension grid resolution for each axis */
+ int bres, brix; /* Biggest resolution and its index */
+ double mres; /* Geometric mean res[] */
+ int no; /* Total number of points in grid = res[0] * res[1] * .. res[di-1] */
+ datai l,h,w; /* Grid low, high, grid cell width */
+ /* This is used to map from the input domain to the grid */
+
+ datao fmin, fmax; /* Min & max values of grid output (function) variables */
+ int fminx[MXDO], fmaxx[MXDO]; /* Grid indexes of points that set min/max output values */
+ double fscale; /* Overall magnitude of output values */
+ double *ipos[MXDI]; /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Allows for the possibility of */
+ /* a non-uniform grid spacing, by adjusting the curvature evaluation */
+ /* appropriately. */
+ double **ccv; /* Curvature compensation array for current outpu (May be NULL) */
+ int fminmax_valid; /* Min/max/scale cached values valid flag. */
+ int limitv_cached; /* Flag: Ink limit values have been set in the grid array */
+
+#define G_XTRA 3 /* Extra floats per grid point */
+ float *alloc; /* Grid points allocated address */
+ float *a; /* Grid point flags + data */
+ /* Array is res[] ^ di entries float[fdi+G_XTRA], offset by G_XTRA */
+ /* (But is expanded when spline interpolaton is active) */
+ /* float[-1] contains the ink limit function value, L_UNINIT if not initd */
+ /* float[-2] contains the edge flag values, 2 bits per in dim. */
+ /* float[-3] contains the touched flag generation count. */
+ /* (k value for non-linear fit would be another entry.) */
+ /* Flag values are 3 bits for each dimension. Bits 1,0 form */
+ /* 2 bit distance from edge: 0 for on edge of grid, */
+ /* 1 for next row, 2 for 3rd row and beyond. If bit 2 is set, */
+ /* then we are on the lower edge. This limits di to 10 or less, */
+ /* with the two MS bits spare. */
+ int pss; /* Grid point structure size = fdi+G_XTRA */
+
+ /* Uninitialised limit value */
+#define L_UNINIT ((float)-1e38)
+
+ /* Macros to access flags. Arguments are a pointer to base grid point and */
+ /* Flag value is distance from edge in bottom 2 bits, values 0, 1 or 2 maximum. */
+ /* bit 2 is set if the distance is to the lower edge. */
+#define FLV(fp) (*((unsigned int *)((fp)-2)))
+ /* Init the flag values to 0 */
+#define I_FL(fp) (FLV(fp) = 0)
+ /* Return 3 bit flag data */
+#define G_FL(fp,di) ((FLV(fp) >> (3 * (di))) & 7)
+ /* Set 3 bit flag data */
+#define S_FL(fp,di,v) (FLV(fp) = (FLV(fp) & ~(7 << (3 * (di)))) | (((v) & 7) << (3 * (di))))
+
+ /* Macro to access touched flag. Arguments are a pointer to base grid point. */
+#define TOUCHF(fp) (*((unsigned int *)((fp)-3)))
+
+ /* Grid array offset lookups - in floats */
+ int ci[MXDI]; /* Grid coordinate increments for each dimension */
+ int fci[MXDI]; /* Grid coordinate increments for each dimension in floats */
+ int *hi; /* 2^di Combination offset for sequence through cube. */
+ int a_hi[DEF2MXDI]; /* Default allocation for *hi */
+ int *fhi; /* Combination offset for sequence through cube of */
+ /* 2^di points, starting at base, in floats */
+ int a_fhi[DEF2MXDI];/* Default allocation for *hi */
+
+ unsigned int touch; /* Cell touched flag count */
+ } g;
+
+
+ /* Ink limit related information */
+ int limiten; /* Flag - limiting is enabled */
+ double (*limitf)(void *cntx, double *in); /* Optional input space qualifier function. */
+ void *lcntx; /* Context passed to limit() */
+ double limitv; /* Value not to be exceeded by limit() */
+
+ /* Hermite spline interpolation support */
+ struct {
+ magic_data *magic; /* Magic matrix - non-zero elements only, Non-NULL if splining */
+ int nm; /* number in magic data list */
+ int spline; /* Non-zero if spline data is present in g.a */
+ /* Changes from float g.a[res ^ di][fdi+G_XTRA], offset by G_XTRA, */
+ /* to float g.a[res ^ di][(2^di * fdi)+G_XTRA], offset by G_XTRA, */
+ } spline;
+
+ /* Gamut support */
+ gam_struct gam; /* See gam.h */
+
+ /* Reverse Interpolation support */
+ rev_struct rev; /* See rev.h */
+
+ /* Methods */
+
+ /* Free ourselves */
+ void (*del)(struct _rspl *ss);
+
+ /* Combination lags used by various functions */
+ /* NOTE that RSPL_2PASSSMTH and RSPL_EXTRAFIT2 are available, but the smoothing */
+ /* factors are not setup for them, and they are not sufficiently different from the */
+ /* default smoothing to be useful. */
+#define RSPL_NOFLAGS 0x0000
+#define RSPL_2PASSSMTH 0x0001 /* Use gaussian filter in 2nd pass to smooth */
+#define RSPL_EXTRAFIT2 0x0002 /* Compensate for data errors each round */
+#define RSPL_SYMDOMAIN 0x0004 /* Maintain symetric smoothness with nonsym. resolution */
+#define RSPL_SET_APXLS 0x0020 /* For set_rspl, adjust samples for aproximate least squares */
+#define RSPL_FASTREVSETUP 0x0010 /* Do a fast reverse setup at the cost of subsequent speed */
+#define RSPL_VERBOSE 0x8000 /* Turn on print progress messages */
+#define RSPL_NOVERBOSE 0x4000 /* Turn off print progress messages */
+
+ /* Initialise from scattered data. RESTRICTED SIZE */
+ /* Return non-zero if result is non-monotonic */
+ int
+ (*fit_rspl)(
+ struct _rspl *s, /* this */
+ int flags, /* Combination of flags */
+ co *d, /* Array holding position and function values of data points */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range, */
+ /* typical value 0.005 (aprox. = 0.564 times the standard deviation) */
+ /* NULL = default 0.005 */
+ double *ipos[MXDI] /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+ );
+
+ /* Initialise from scattered data, with per point weighting. RESTRICTED SIZE */
+ /* Return non-zero if result is non-monotonic */
+ int
+ (*fit_rspl_w)(
+ struct _rspl *s, /* this */
+ int flags, /* Combination of flags */
+ cow *d, /* Array holding position, function and weight values of data points */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range, */
+ /* typical value 0.005 (aprox. = 0.564 times the standard deviation) */
+ /* NULL = default 0.005 */
+ double *ipos[MXDI] /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+ );
+
+ /* Initialise from scattered data, with per point individual out weighting. */
+ /* RESTRICTED SIZE Return non-zero if result is non-monotonic */
+ int
+ (*fit_rspl_ww)(
+ struct _rspl *s, /* this */
+ int flags, /* Combination of flags */
+ coww *d, /* Array holding position, function and weight values of data points */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range, */
+ /* typical value 0.005 (aprox. = 0.564 times the standard deviation) */
+ /* NULL = default 0.005 */
+ double *ipos[MXDI] /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+ );
+
+ /* Initialise from scattered data, with weak default function. */
+ /* RESTRICTED SIZE */
+ /* Return non-zero if result is non-monotonic */
+ int
+ (*fit_rspl_df)(
+ struct _rspl *s, /* this */
+ int flags, /* Combination of flags */
+ co *d, /* Array holding position and function values of data points */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range, */
+ /* typical value 0.005 (aprox. = 0.564 times the standard deviation) */
+ /* NULL = default 0.005 */
+ double *ipos[MXDI],/* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+ double weak, /* Weak weighting, nominal = 1.0 */
+ void *cbntx, /* Opaque function context */
+ void (*func)(void *cbntx, double *out, double *in) /* Function to set from */
+ );
+
+ /* Initialise from scattered data, with per point weighting and weak default function. */
+ /* RESTRICTED SIZE */
+ /* Return non-zero if result is non-monotonic */
+ int
+ (*fit_rspl_w_df)(
+ struct _rspl *s, /* this */
+ int flags, /* Combination of flags */
+ cow *d, /* Array holding position, function and weight values of data points */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range, */
+ /* typical value 0.005 (aprox. = 0.564 times the standard deviation) */
+ /* NULL = default 0.005 */
+ double *ipos[MXDI],/* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+ double weak, /* Weak weighting, nominal = 1.0 */
+ void *cbntx, /* Opaque function context */
+ void (*func)(void *cbntx, double *out, double *in) /* Function to set from */
+ );
+
+ /* Initialize the grid from a provided function. By default the grid */
+ /* values are set to exactly the value returned by func(), unless the */
+ /* RSPL_SET_APXLS flag is set, in which case an attempt is made to have */
+ /* the grid points represent a least squares aproximation to the underlying */
+ /* surface. */
+ /* Grid index values are supplied "under" in[] at *((int*)&in[-e-1]) */
+ /* Return non-monotonic status */
+ int
+ (*set_rspl)(
+ struct _rspl *s, /* this */
+ int flags, /* Combination of flags */
+ void *cbntx, /* Opaque function context */
+ void (*func)(void *cbntx, double *out, double *in), /* Function to set from */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh /* Data value high normalize - NULL = default 1.0 */
+ );
+
+ /* Re-set values from a function. Grid index values are supplied */
+ /* "under" in[] at *((int*)&iv[-e-1]) */
+ /* Return non-monotonic status. Clears all the reverse lookup information. */
+ /* It is assumed that the output range remains unchanged. */
+ /* Existing output values are supplied in out[] */
+ int
+ (*re_set_rspl)(
+ struct _rspl *s,/* this */
+ int flags, /* Combination of flags (not used) */
+ void *cbntx, /* Opaque function context */
+ void (*func)(void *cbntx, double *out, double *in) /* Function to set from */
+ );
+
+ /* Scan the rspl grid point locations and values. Grid index values are */
+ /* supplied "under" in[] at *((int*)&iv[-e-1]) */
+ /* Return non-monotonic status. */
+ void
+ (*scan_rspl)(
+ struct _rspl *s, /* this */
+ int flags, /* Combination of flags (not used) */
+ void *cbntx, /* Opaque function context */
+ void (*func)(void *cbntx, double *out, double *in) /* Function that gets given values */
+ );
+
+ /* Set values by multi-grid optimisation using the provided function. */
+ int (*opt_rspl)(
+ struct _rspl *s,/* this */
+ int flags, /* Combination of flags */
+ int tdi, /* Dimensionality of target data */
+ int adi, /* Additional grid point data allowance */
+ double **vdata, /* di^2 array of function, target and additional values to init */
+ /* array corners with. */
+ double (*func)(void *fdata, double *inout, double *surav, int first, double *cw),
+ /* Optimisation function */
+ void *fdata, /* Opaque data needed by function */
+ datai glow, /* Grid low scale - NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution */
+ datao vlow, /* Data value low normalize - NULL = default 0.0 */
+ datao vhigh /* Data value high normalize - NULL = default 1.0 */
+ );
+
+ /* Filter the existing values using the surrounding 3x3 cells. */
+ /* Grid index values are supplied "under" in[] */
+ void
+ (*filter_rspl)(
+ struct _rspl *s, /* this */
+ int flags, /* Combination of flags (not used) */
+ void *cbntx, /* Opaque function context */
+ void (*func)(void *cbntx, float **out, double *in, int cvi) /* Function to set from */
+ );
+
+ /* Do forward interpolation */
+ /* Return 0 if OK, 1 if input was clipped to grid */
+ int (*interp)(
+ struct _rspl *s, /* this */
+ co *p); /* Input and output values */
+
+ /* Do forward (partial) interpolation to allow input & output curves to be applied, */
+ /* and allow input delta E to be estimated from output delta E. */
+ /* Call with input value in p1[0].p[], */
+ /* In order smallest to largest weight: */
+ /* Return di+1 vertex values in p1[]].v[] and */
+ /* 0-1 sub-cell weight values as (p1[].p[0] - p1[].p[1]). */
+ /* Optionally in input channel order: */
+ /* Returns di+1 partial derivatives + base value in p2[].v[], */
+ /* with matching weight values for each in p2[].p[0] (last weight = 1)*/
+ /* Return 0 if OK, 1 if input was clipped to grid */
+ int (*part_interp)(
+ struct _rspl *s, /* this */
+ co *p1,
+ co *p2); /* optional - return partial derivatives for each input channel */
+
+ /* Do splined forward interpolation. RESTRICTED SIZE */
+ /* Return 0 if OK, 1 if input was clipped to grid */
+ int (*spline_interp)(
+ struct _rspl *s, /* this */
+ co *p); /* Input and output values */
+
+
+ /* ------------------------------- */
+ /* Create a surface gamut representation. */
+ /* Return NZ on error */
+ int (*comp_gamut)(struct _rspl *s,
+ double *cent, /* Optional center of gamut [fdi], default center of out range */
+ double *scale, /* Optional Scale of output values in vector to center [fdi] */
+ /* default 1.0 */
+ void (*outf)(void *cntxf, double *out, double *in), /* Optional rspl val -> output value */
+ void *cntxf, /* Context for function */
+ void (*outb)(void *cntxb, double *out, double *in), /* Optional output value -> rspl val */
+ void *cntxb /* Context for function */
+ );
+
+ /* ------------------------------- */
+
+ /* Set the ink limit information for any reverse interpolation. */
+ /* Calling this will clear the reverse interpolaton cache. */
+ void (*rev_set_limit)(
+ struct _rspl *s, /* this */
+ double (*limitf)(void *lcntx, double *in), /* Optional input space limit function. */
+ /* Function should evaluate in[0..di-1], and return number */
+ /* that is not to exceed limitv. NULL if not used. */
+ void *lcntx, /* Context passed to limit() */
+ double limitv /* Value that limit() is not to exceed */
+ );
+
+ /* Get the ink limit information for any reverse interpolation. */
+ void (*rev_get_limit)(
+ struct _rspl *s, /* this */
+ double (**limitf)(void *lcntx, double *in),
+ /* Return pointer to function of NULL if not set */
+ void **lcntx, /* return context pointer */
+ double *limitv /* Return limit value */
+ );
+
+ /* Possible reverse hint flags */
+#define RSPL_WILLCLIP 0x0001 /* Hint that clipping will be needed */
+#define RSPL_EXACTAUX 0x0002 /* Hint that auxiliary target will be matched exactly */
+#define RSPL_MAXAUX 0x0004 /* If not possible to match exactly, return the */
+ /* closest value larger than the target, rather than */
+ /* absolute closest. */
+#define RSPL_AUXLOCUS 0x0008 /* Auxiliary target is proportion of locus, not */
+ /* absolute. Implies EXACTAUX hint. */
+#define RSPL_NEARCLIP 0x0010 /* If clipping occurs, return the nearest solution, */
+ /* rather than the one in the clip direction. */
+
+ /* Return value masks */
+#define RSPL_DIDCLIP 0x8000 /* If this bit is set, at least one soln. and clipping occured */
+#define RSPL_NOSOLNS 0x7fff /* And return value with this mask to get number of solutions */
+
+ /* Do reverse interpolation given target output values and (optional) auxiliary target */
+ /* input values. Return number of results and clip flag. If return value == mxsoln, then */
+ /* there might be more results. RESTRICTED SIZE */
+ int (*rev_interp)(
+ struct _rspl *s, /* this */
+ int flags, /* Hint flag */
+ int mxsoln, /* Maximum number of solutions allowed for */
+ int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no aux) */
+ double cdir[MXRO], /* Clip vector direction and length - NULL if not used */
+ co *p); /* Given target output space value in cpp[0].v[] + */
+ /* target input space auxiliaries in cpp[0].p[], return */
+ /* input space solutions in cpp[0..retval-1].p[], and */
+ /* (possibly) clipped target values in cpp[0].v[] */
+
+ /* Do reverse search for the locus of the auxiliary input values given a target output. */
+ /* Return 1 on finding a valid solution, and 0 if no solutions are found. RESTRICTED SIZE */
+ int (*rev_locus)(
+ struct _rspl *s,/* this */
+ int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no aux) */
+ co *cpp, /* Input target value in cpp[0].v[] */
+ double min[MXRI],/* Return minimum auxiliary values */
+ double max[MXRI]); /* Return maximum auxiliary values */
+
+ /* Do reverse search for the auxiliary min/max ranges of the solution locus for the */
+ /* given target output values. RESTRICTED SIZE */
+ /* Return number of locus segments found, up to mxsoln. 0 will be returned if no solutions */
+ /* are found. */
+ int (*rev_locus_segs)(
+ struct _rspl *s,/* this */
+ int *auxm, /* Array of di mask flags, !=0 for valid auxliaries (NULL if no aux) */
+ co *cpp, /* Input value in cpp[0].v[] */
+ int mxsoln, /* Maximum number of solutions allowed for */
+ double min[][MXRI], /* Array of min[MXRI] to hold return segment minimum values. */
+ double max[][MXRI] /* Array of max[MXRI] to hold return segment maximum values. */
+ );
+
+
+ /* ------------------------------- */
+
+ /* Return the min and max of the input values valid in the grid */
+ void (*get_in_range)(
+ struct _rspl *s, /* this */
+ double *min, double *max); /* Return min/max values */
+
+ /* return the min and max of the output values contained in the grid */
+ void (*get_out_range)(
+ struct _rspl *s, /* this */
+ double *min, double *max); /* Return min/max values */
+
+ /* return the grid index of the grid values at the min & max output values */
+ void (*get_out_range_points)(struct _rspl *s, int *minp, int *maxp);
+
+ /* return the overall scale of the output values contained in the grid */
+ double (*get_out_scale)(struct _rspl *s);
+
+ /* return the next touched flag count value. */
+ /* Whenever this rolls over, all the flags in the grid array will be reset */
+ unsigned int (*get_next_touch)(
+ struct _rspl *s); /* this */
+
+# define wvals ad##jw
+
+ /* Return non-zero if this rspl can be */
+ /* used with Restricted Size functions. */
+ int (*within_restrictedsize)(
+ struct _rspl *s); /* this */
+
+}; typedef struct _rspl rspl;
+
+/* Create a new, empty rspl object */
+rspl *new_rspl(int flags, int di, int fdi); /* Input and output dimentiality */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* Utility functions */
+
+/* The multi-dimensional access sequence is a distributed */
+/* Gray code sequence, with direction reversal */
+/* on every alternate power of 2 scale. */
+/* It is intended to aid cache access locality in multi-dimensional */
+/* regular sampling. It approximates the Hilbert curve sequence. */
+
+/* Structure to hold sequencer info */
+struct _rpsh {
+ int di; /* Dimensionality */
+ unsigned res[MXDI]; /* Resolution per coordinate */
+ unsigned bits[MXDI]; /* Bits per coordinate */
+ unsigned tbits; /* Total bits */
+ unsigned ix; /* Current binary index */
+ unsigned tmask; /* Total 2^n count mask */
+ unsigned count; /* Usable count */
+}; typedef struct _rpsh rpsh;
+
+/* Initialise, returns total usable count */
+unsigned
+rpsh_init(rpsh *p, int di, unsigned res[], int co[]);
+
+/* Reset the counter */
+void rpsh_reset(rpsh *p);
+
+/* Increment pseudo-hilbert coordinates */
+/* Return non-zero if count rolls over to 0 */
+int rpsh_inc(rpsh *p, int co[]);
+
+#endif /* RSPL_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rspl/rspl1.c b/rspl/rspl1.c
new file mode 100644
index 0000000..dc3588b
--- /dev/null
+++ b/rspl/rspl1.c
@@ -0,0 +1,391 @@
+
+ /* Single dimension regularized spline data structure */
+
+/*
+ * Argyll Color Correction System
+ * Author: Graeme W. Gill
+ * Date: 2000/10/29
+ *
+ * Copyright 1996 - 2010 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
+ * see the License2.txt file for licencing details.
+ *
+ * This is a simple 1D version of rspl, useful for standalone purposes.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include "numsup.h"
+#include "rspl1.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define DBGA g_log, 0 /* First argument to DBGF() */
+# define DBGF(xx) a1logd xx
+#else
+# define DBGF(xx)
+#endif
+
+
+/* Do an interpolation based on the grid */
+/* Use a linear interp between grid points. */
+/* If the input is outside the grid range, it will */
+/* be clamped to the nearest grid point. */
+static int interp(
+rspl *t,
+co *p
+) {
+ int rv = 0;
+ double x, y, xx, w1;
+ int i;
+
+ x = p->p[0];
+
+ if (x < t->gl) {
+ x = t->gl;
+ rv = 1;
+ } else if (x > t->gh) {
+ x = t->gh;
+ rv = 1;
+ }
+
+ xx = (x - t->gl)/t->gw; /* Grid location of point */
+ i = (int)floor(xx); /* Lower grid of point */
+ if (i >= (t->nig-2))
+ i = t->nig-2;
+
+ w1 = xx - (double)i; /* Weight to upper grid point */
+
+ y = ((1.0 - w1) * t->x[i]) + (w1 * t->x[i+1]);
+
+ p->v[0] = y * t->vw + t->vl; /* Rescale the data */
+
+ return rv;
+}
+
+/* Destructor */
+static void del_rspl(rspl *t) {
+ if (t != NULL) {
+ if (t->x != NULL)
+ free_dvector(t->x, 0, t->nig);
+ free(t);
+ }
+}
+
+/* Initialise the regular spline from scattered data */
+/* Return nz on error */
+static int fit_rspl_imp(
+ struct _rspl *t,/* this */
+ int flags, /* (Not used) */
+ void *d, /* Array holding position and function values of data points */
+ int dtp, /* Flag indicating data type, 0 = (co *), 1 = (cow *), 2 = (coww *) */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int *gres, /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double *avgdev, /* (Not used) */
+ double **ipos /* (not used) */
+) {
+ int n;
+ double cw;
+
+ DBGF((DBGA, "rspl1:fit_rspl_imp() with %d points called, dtp = %d\n",ndp,dtp));
+
+ /* Allocate space for interpolation grid */
+ t->nig = *gres;
+
+ if ((t->x = dvector(0, t->nig)) == NULL) {
+ DBGF((DBGA, "rspl1:Malloc of vector x failed\n"));
+ return 1;
+ }
+
+ /* Normalize curve weight to grid resolution. */
+ cw = 0.0000005 * smooth * pow((t->nig-1),4.0) / (t->nig - 2);
+ DBGF((DBGA, "rspl1:cw = %e\n",cw));
+
+ /* cw is multiplied by the sum of grid curvature errors squared to keep */
+ /* the same ratio with the sum of data position errors squared */
+
+ /* Determine the data range */
+ t->xl = 1e300;
+ t->xh = -1e300;
+ t->dl = 1e300;
+ t->dh = -1e300;
+ if (dtp == 0) {
+ co *dd = (co *)d;
+
+ for (n = 0; n < ndp; n++) {
+ if (dd[n].p[0] < t->xl)
+ t->xl = dd[n].p[0];
+ if (dd[n].p[0] > t->xh)
+ t->xh = dd[n].p[0];
+ if (dd[n].v[0] < t->dl)
+ t->dl = dd[n].v[0];
+ if (dd[n].v[0] > t->dh)
+ t->dh = dd[n].v[0];
+
+ DBGF((DBGA, "rspl1:Point %d = %f, %f\n",n,dd[n].p[0],dd[n].v[0]));
+ }
+ } else if (dtp == 1) {
+ cow *dd = (cow *)d;
+
+ for (n = 0; n < ndp; n++) {
+ if (dd[n].p[0] < t->xl)
+ t->xl = dd[n].p[0];
+ if (dd[n].p[0] > t->xh)
+ t->xh = dd[n].p[0];
+ if (dd[n].v[0] < t->dl)
+ t->dl = dd[n].v[0];
+ if (dd[n].v[0] > t->dh)
+ t->dh = dd[n].v[0];
+ DBGF((DBGA, "rspl1:Point %d = %f, %f (%f)\n",n,dd[n].p[0],dd[n].v[0],dd[n].w));
+ }
+ } else {
+ DBGF((DBGA, "rspl1:Internal error, unknown dtp value %d\n",dtp));
+ return 1;
+ }
+
+ t->gl = glow != NULL ? *glow : 0.0;
+ t->gh = ghigh != NULL ? *ghigh : 1.0;
+
+ /* adjust input ranges to encompass data */
+ if (t->xl < t->gl)
+ t->gl = t->xl;
+ if (t->xh > t->gh)
+ t->gh = t->xh;
+
+ /* Set the input and output scaling */
+ t->gw = (t->gh - t->gl)/(double)(t->nig-1);
+
+ t->vl = vlow != NULL ? *vlow : 0.0;
+ t->vw = ((vhigh != NULL ? *vhigh : 1.0) - t->vl);
+
+ DBGF((DBGA, "rspl1:gl %f, gh %f, gw %f, vl %f, vw %f\n",t->gl,t->gh,t->gw,t->vl,t->vw));
+
+ /* create smoothed grid data */
+ {
+ int n,i,k;
+ double **A; /* A matrix of interpoint weights */
+ double *b; /* b vector for RHS of simultabeous equation */
+
+ /* We just store the diagonal of the A matrix */
+ if ((A = dmatrix(0, t->nig, 0, 2)) == NULL) {
+ DBGF((DBGA, "rspl1:Malloc of matrix A failed\n"));
+ return 1;
+ }
+
+ if ((b = dvector(0,t->nig)) == NULL) {
+ free_dvector(b,0,t->nig);
+ DBGF((DBGA, "rspl1:Malloc of vector b failed\n"));
+ return 1;
+ }
+
+ /* Initialize the A and b matricies */
+ for (i = 0; i < t->nig; i++) {
+ for (k = 0; k < 3; k++)
+ A[i][k] = 0.0;
+ t->x[i] = b[i] = 0.0;
+ }
+
+ /* Accumulate data dependent factors */
+ for (n = 0; n < ndp; n++) {
+ double bf, cbf;
+ double xv, yv, wv;
+
+ if (dtp == 0) {
+ co *dd = (co *)d;
+
+ xv = dd[n].p[0];
+ yv = dd[n].v[0];
+ wv = 1.0;
+ } else if (dtp == 1) {
+ cow *dd = (cow *)d;
+
+ xv = dd[n].p[0];
+ yv = dd[n].v[0];
+ wv = dd[n].w;
+ } else {
+ DBGF((DBGA, "rspl1:Internal error, unknown dtp value %d\n",dtp));
+ return 1;
+ }
+ yv = (yv - t->vl)/t->vw; /* Normalize the value */
+
+ /* Figure out which grid cell data is in */
+ i = (int)((xv - t->gl)/t->gw); /* Index of next lowest data point */
+
+ bf = ((((double)(i+1) * t->gw) + t->gl) - xv)/t->gw; /* weight to lower grid point */
+ cbf = 1.0 - bf; /* weight to upper grid point */
+
+ b[i] -= 2.0 * bf * -yv * wv; /* dui component due to dn */
+ A[i][0] += 2.0 * bf * bf * wv; /* dui component due to ui */
+ A[i][1] += 2.0 * bf * cbf * wv; /* dui component due to ui+1 */
+
+ if ((i+1) < t->nig) {
+ b[i+1] -= 2.0 * cbf * -yv * wv; /* dui component due to dn */
+ A[i+1][0] += 2.0 * cbf * cbf * wv; /* dui component due to ui */
+ }
+ }
+
+ /* Accumulate curvature dependent factors */
+ for (i = 0; i < t->nig; i++) {
+
+ if ((i-2) >= 0) { /* Curvature of cell below */
+ A[i][0] += 2.0 * cw;
+ }
+
+ if ((i-1) >= 0 && (i+1) < t->nig) { /* Curvature of t cell */
+ A[i][0] += 8.0 * cw;
+ A[i][1] += -4.0 * cw;
+ }
+ if ((i+2) < t->nig) { /* Curvature of cell above */
+ A[i][0] += 2.0 * cw;
+ A[i][1] += -4.0 * cw;
+ A[i][2] += 2.0 * cw;
+ }
+ }
+
+#ifdef DEBUG
+ DBGF((DBGA, "A matrix:\n"));
+ for (i = 0; i < t->nig; i++) {
+ for (k = 0; k < 3; k++)
+ DBGF((DBGA, "A[%d][%d] = %f\n",i,k,A[i][k]));
+ }
+ DBGF((DBGA, "b vector:\n"));
+ for (i = 0; i < t->nig; i++)
+ DBGF((DBGA, "b[%d] = %f\n",i,b[i]));
+#endif /* DEBUG */
+
+ /* Apply Cholesky decomposition to A[][] to create L[][] */
+ for (i = 0; i < t->nig; i++) {
+ double sm;
+ for (n = 0; n < 3; n++) {
+ sm = A[i][n];
+ for (k = 1; (n+k) < 3 && (i-k) >=0; k++) {
+ sm -= A[i-k][n+k] * A[i-k][k];
+ }
+ if (n == 0) {
+ if (sm <= 0.0) {
+ free_dvector(b,0,t->nig);
+ free_dmatrix(A,0,t->nig,0,2);
+ DBGF((DBGA, "rspl1:Sum is -ve - loss of accuracy ?\n"));
+ return 1;
+ }
+ A[i][0] = sqrt(sm);
+ } else {
+ A[i][n] = sm/A[i][0];
+ }
+ }
+ }
+
+ /* Solve L . y = b, storing y in x */
+ for (i = 0; i < t->nig; i++) {
+ double sm;
+ sm = b[i];
+ for (k = 1; k < 3 && (i-k) >= 0; k++) {
+ sm -= A[i-k][k] * t->x[i-k];
+ }
+ t->x[i] = sm/A[i][0];
+ }
+
+ /* Solve LT . x = y */
+ for (i = t->nig-1; i >= 0; i--) {
+ double sm;
+ sm = t->x[i];
+ for (k = 1; k < 3 && (i+k) < t->nig; k++) {
+ sm -= A[i][k] * t->x[i+k];
+ }
+ t->x[i] = sm/A[i][0];
+ }
+#ifdef DEBUG
+ DBGF((DBGA, "Solution vector:\n"));
+ for (i = 0; i < t->nig; i++) {
+ DBGF((DBGA, "x[%d] = %f\n",i,t->x[i]));
+ }
+#endif /* DEBUG */
+
+ free_dvector(b,0,t->nig);
+ free_dmatrix(A,0,t->nig,0,2);
+ }
+ return 0;
+}
+
+/* Initialise from scattered data. */
+/* Return nz on error */
+static int fit_rspl(
+ struct _rspl *t,/* this */
+ int flags, /* (Not used) */
+ co *d, /* Array holding position and function values of data points */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int *gres, /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double *avgdev, /* (Not used) */
+ double **ipos /* (not used) */
+) {
+ /* Call implementation with (co *) data */
+ return fit_rspl_imp(t, flags, (void *)d, 0, ndp, glow, ghigh, gres, vlow, vhigh,
+ smooth, avgdev, ipos);
+}
+
+/* Initialise the regular spline from scattered data with weights */
+/* Return nz on error */
+static int
+fit_rspl_w(
+ rspl *t, /* this */
+ int flags, /* Combination of flags */
+ cow *d, /* Array holding position, function and weight values of data points */
+ int dno, /* Number of data points */
+ ratai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */
+ ratai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */
+ int *gres, /* Spline grid resolution */
+ ratao vlow, /* Data value low normalize, NULL = default 0.0 */
+ ratao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double *avgdev, /* (Not used) */
+ double **ipos /* (not used) */
+) {
+ /* Call implementation with (cow *) data */
+ return fit_rspl_imp(t, flags, (void *)d, 1, dno, glow, ghigh, gres, vlow, vhigh,
+ smooth, avgdev, ipos);
+}
+
+/* Construct an empty rspl1 */
+/* Return NULL if something goes wrong. */
+rspl *new_rspl(int flags, int di, int fdi) {
+ rspl *t; /* this */
+
+ if (flags != RSPL_NOFLAGS || di != 1 || fdi != 1) {
+ DBGF((DBGA, "rspl1:Can't handle general rspl: flags %d, di %d, do %d\n",flags,di,fdi));
+ return NULL;
+ }
+
+ if ((t = (rspl *)calloc(1, sizeof(rspl))) == NULL) {
+ DBGF((DBGA, "rspl1:Malloc of structure failed\n"));
+ return NULL;
+ }
+
+ /* Initialise the classes methods */
+ t->interp = interp;
+ t->fit_rspl = fit_rspl;
+ t->fit_rspl_w = fit_rspl_w;
+ t->del = del_rspl;
+
+ return t;
+}
+
+
+
+
+
diff --git a/rspl/rspl1.h b/rspl/rspl1.h
new file mode 100644
index 0000000..d5ea0b9
--- /dev/null
+++ b/rspl/rspl1.h
@@ -0,0 +1,115 @@
+
+#ifndef _RSPL1_H_
+
+ /* Single dimension regularized spline data structure */
+
+/*
+ * Argyll Color Correction System
+ * Author: Graeme W. Gill
+ * Date: 2000/10/29
+ *
+ * Copyright 1996 - 2010 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
+ * see the License2.txt file for licencing details.
+ *
+ * This is a simple 1D version of rspl, useful for standalone purposes.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* General data point position/value structure */
+typedef double datai[1];
+typedef double datao[1];
+typedef double ratai[1];
+typedef double ratao[1];
+
+/* Interface coordinate value */
+typedef struct {
+ double p[1]; /* coordinate position */
+ double v[1]; /* function values */
+} co;
+
+/* Interface coordinate value */
+typedef struct {
+ double p[1]; /* coordinate position */
+ double v[1]; /* function values */
+ double w; /* Weight to give this point, nominally 1.0 */
+} cow;
+
+#define RSPL_NOFLAGS 0
+
+struct _rspl {
+
+ /* Private: */
+ int nig; /* number in interpolation grid */
+ double gl,gh,gw;/* Interpolation grid scale low, high, grid cell width */
+ double vl,vw; /* low & range */
+ double xl,xh; /* Actual X data exremes low, high */
+ double dl,dh; /* Actual Y Data scale low, high */
+ double *x; /* Array of nig grid point scaled y values */
+
+ /* Public: */
+
+ /* destructor */
+ void (*del)(struct _rspl *t);
+
+ /* Initialise from scattered data. */
+ /* Returns nz on error */
+ int
+ (*fit_rspl)(
+ struct _rspl *s, /* this */
+ int flags, /* (Not used) */
+ co *d, /* Array holding position and function values of data points */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int *gres, /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double *avgdev, /* (Not used) */
+ double **ipos /* (not used) */
+ );
+
+ /* Initialise from scattered data with weighting. */
+ /* Returns nz on error */
+ int
+ (*fit_rspl_w)(
+ struct _rspl *s, /* this */
+ int flags, /* (Not used) */
+ cow *d, /* Array holding position and function values of data points */
+ int ndp, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int *gres, /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ double *avgdev, /* (Not used) */
+ double **ipos /* (not used) */
+ );
+
+ /* Do forward interpolation */
+ /* Return 0 if OK, 1 if input was clipped to grid */
+ int (*interp)(
+ struct _rspl *s, /* this */
+ co *p); /* Input and output values */
+
+}; typedef struct _rspl rspl;
+
+/* Create a new, empty rspl object */
+rspl *new_rspl(int flags, int di, int fdi);
+
+#ifdef __cplusplus
+}
+#endif
+
+#define _RSPL1_H_
+#endif /* _RSPL1_H_ */
+
+
diff --git a/rspl/rspl_imp.h b/rspl/rspl_imp.h
new file mode 100644
index 0000000..0cd8805
--- /dev/null
+++ b/rspl/rspl_imp.h
@@ -0,0 +1,27 @@
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized spline
+ * Implementation header.
+ *
+ * Author: Graeme W. Gill
+ * Date: 28/9/96
+ *
+ * Copyright 1996, Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+#include "rspl.h"
+
+#ifndef RSPL_IMP_H
+
+/* These three factors controll how the monotonic stuff behaves. */
+#define MCINC 0.05 /* The tollerance for detecting non-monoticity */
+#define BALLEV 0.8 /* Balance level of adjusted points */
+#define RADF 0.01 /* Radius factor of nme influence */
+
+#define RSPL_IMP_H
+#endif /* RSPL_IMP_H */
diff --git a/rspl/scat.c b/rspl/scat.c
new file mode 100644
index 0000000..b4ed978
--- /dev/null
+++ b/rspl/scat.c
@@ -0,0 +1,2861 @@
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized splines data fitter
+ *
+ * Author: Graeme W. Gill
+ * Date: 2004/8/14
+ *
+ * Copyright 1996 - 2009 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/*
+ * This file contains the scattered data solution specific code.
+ *
+ * The regular spline implementation was inspired by the following technical reports:
+ *
+ * D.J. Bone, "Adaptive Multi-Dimensional Interpolation Using Regularized Linear Splines,"
+ * Proc. SPIE Vol. 1902, p.243-253, Nonlinear Image Processing IV, Edward R. Dougherty;
+ * Jaakko T. Astola; Harold G. Longbotham;(Eds)(1993).
+ *
+ * D.J. Bone, "Adaptive Colour Printer Modeling using regularized linear splines,"
+ * Proc. SPIE Vol. 1909, p. 104-115, Device-Independent Color Imaging and Imaging
+ * Systems Integration, Ricardo J. Motta; Hapet A. Berberian; Eds.(1993)
+ *
+ * Don Bone and Duncan Stevenson, "Modelling of Colour Hard Copy Devices Using Regularised
+ * Linear Splines," Proceedings of the APRS workshop on Colour Imaging and Applications,
+ * Canberra (1994)
+ *
+ * see <http://www.cmis.csiro.au/Don.Bone/>
+ *
+ * Also of interest was:
+ *
+ * "Discrete Smooth Interpolation", Jean-Laurent Mallet, ACM Transactions on Graphics,
+ * Volume 8, Number 2, April 1989, Pages 121-144.
+ *
+ */
+
+/* TTBD:
+ *
+ * Try simple approach to reduce extrapolation accumulation (edge propogation) effects.
+ * Do this by saving bounding box of scattered points, and then increase smoothness coupling
+ * in direction of axis that is outside this box (or the reverse, reduce smoothness
+ * coupling in direction of any axis that is not outside this box).
+ * [Example is "t3d -t 6 -P 0:0:0:1:1:1" where lins should not bend up at top end.]
+ *
+ * Speedup that skips recomputing all of A to add new points seems OK. (nothing uses
+ * incremental currently anyway.)
+ *
+ * Is there any way of speeding up incremental recalculation ????
+ *
+ * Add optional simplex point interpolation to
+ * solve setup. (No large advantage in this ??)
+ *
+ * Find a more effective way to mitigate the smoothness "clumping"
+ * effect where corners in particular over smooth ?
+ *
+ * Get rid of error() calls - return status instead
+ */
+
+/*
+ Scattered data fit related smoothness control.
+
+ We adjust the curve/data point weighting to account for the
+ grid resolution (to make it resolution independent), as well
+ as allow for the dimensionality (each dimension contributes
+ a curvature error).
+
+ The default assumption is that the grid resolution is set
+ to matche the input data range for that dimension, eg. if
+ a sub range of input space is all that is needed, then a
+ smaller grid resolution can/should be used if smoothness
+ is expected to remain symetric in relation to the input
+ domain.
+
+ eg. Input range 0.0 - 1.0 and 0.0 - 0.5
+ matching res 50 and 25
+
+ The alternative is to set the RSPL_SYMDOMAIN flag,
+ in which case the grid resolution is not taken to
+ be a measure of the dimension scale, and is assumed
+ to be just a lower resolution sampling of the domain.
+
+ eg. Input range 0.0 - 1.0 and 0.0 - 1.0
+ with res. 50 and 25
+
+ still has symetrical smoothness in relation
+ to the input domain.
+
+
+ NOTE :- that both input and output values are normalised
+ by the ranges given during rspl construction. The ranges
+ set the significance between the input and output values.
+
+ eg. Input ranges 0.0 - 1.0 and 0.0 - 100.0
+ (with equal grid resolution)
+ will have symetry when measured against the the
+ same % change in the input domain, but will
+ appear non-symetric if measured against the
+ same numerical change.
+
+ */
+
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <time.h>
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+
+#include "rspl_imp.h"
+#include "numlib.h"
+#include "counters.h" /* Counter macros */
+
+#undef DEBUG /* Print contents of solution setup etc. */
+#undef DEBUG_PROGRESS /* Print progress of acheiving tollerance target */
+
+#define DEFAVGDEV 0.5 /* Default average deviation % */
+
+/* algorithm parameters [Release defaults] */
+#undef POINTWEIGHT /* [Undef] Increas smoothness weighting proportional to number of points */
+#define INCURVEADJ /* [Defined] Adjust smoothness criteria for input curve grid spacing */
+#define EXTRA_SURFACE_SMOOTHING /* [Defined] Stiffen surface points to comp. for single ended. */
+ /* The following are available, but the smoothing table is */
+ /* not setup for them, and they are not sufficiently different */
+ /* from the default smoothing to be useful. */
+#define ENABLE_2PASSSMTH /* [Define] Enable 2 pass smooth using Gaussian filter */
+#define ENABLE_EXTRAFIT /* [Undef] Enable the extra fit option. Good to combat high smoothness. */
+
+#define TWOPASSORDER 2.0 /* Filter order. 2 = Gaussian */
+
+/* Tuning parameters */
+#ifdef NEVER
+
+/* Experimental set: */
+
+#pragma message("!!!!!!!!! Experimental config set !!!!!!!!!")
+
+#define TOL 1e-12 /* Tollerance of result - usually 1e-5 is best. */
+#define TOL_IMP 1.0 /* Minimum error improvement to continue - reduces accuracy (1.0 == off) */
+#undef GRADUATED_TOL /* Speedup attemp - use reduced tollerance for prior grids. */
+#define GRATIO 2.0 /* Multi-grid resolution ratio */
+#undef OVERRLX /* Use over relaxation factor when progress slows (worse accuracy ?) */
+#define JITTERS 0 /* Number of 1D conjugate solve itters */
+#define CONJ_TOL 1.0 /* Extra tolereance on 1D conjugate solution times TOL. */
+#define MAXNI 16 /* Maximum itteration without checking progress */
+//#define SMOOTH 0.000100 /* Set nominal smoothing (1.0) */
+#define WEAKW 0.1 /* Weak default function nominal effect (1.0) */
+#define ZFCOUNT 1 /* Extra fit repeats */
+
+#else
+
+/* Release set: */
+
+#define TOL 1e-6 /* [1e-6] Tollerance of result - usually 1e-5 is best. */
+#define TOL_IMP 0.998 /* [0.998] Minimum error improvement to continue - reduces accuracy (1.0 == off) */
+#undef GRADUATED_TOL /* [Undef] Speedup attemp - use reduced tollerance for prior grids. */
+#define GRATIO 2.0 /* [2.0] Multi-grid resolution ratio */
+#undef OVERRLX /* [Undef] Use over relaxation when progress slows (worse accuracy ?) */
+#define JITTERS 0 /* [0] Number of 1D conjugate solve itters */
+#define CONJ_TOL 1.0 /* [1.0] Extra tolereance on 1D conjugate solution times TOL. */
+#define MAXNI 16 /* [16] Maximum itteration without checking progress */
+//#define SMOOTH 0.000100 /* Set nominal smoothing (1.0) */
+#define WEAKW 0.1 /* [0.1] Weak default function nominal effect (1.0) */
+#define ZFCOUNT 1 /* [1] Extra fit repeats */
+
+#endif
+
+#undef NEVER
+#define ALWAYS
+
+/* Implemented in rspl.c: */
+extern void alloc_grid(rspl *s);
+
+extern int is_mono(rspl *s);
+
+/* Convention is to use:
+ i to index grid points u.a
+ n to index data points d.a
+ e to index position dimension di
+ f to index output function dimension fdi
+ j misc and cube corners
+ k misc
+ */
+
+/* ================================================= */
+/* Structure to hold temporary data for multi-grid calculations */
+/* One is created for each resolution. Only used in this file. */
+struct _mgtmp {
+ rspl *s; /* Associated rspl */
+ int f; /* Output dimension being calculated */
+
+ /* Weak default function stuff */
+ double wdfw; /* Weight per grid point */
+
+ /* Scattered data fit stuff */
+ struct {
+ double cw[MXDI]; /* Curvature weight factor */
+ } sf;
+
+ /* Grid points data */
+ struct {
+ int res[MXDI]; /* Single dimension grid resolution */
+ int bres, brix; /* Biggest resolution and its index */
+ double mres; /* Geometric mean res[] */
+ int no; /* Total number of points in grid = res ^ di */
+ ratai l,h,w; /* Grid low, high, grid cell width */
+
+ double *ipos[MXDI]; /* Optional relative grid cell position for each input dim cell */
+
+ /* Grid array offset lookups */
+ int ci[MXRI]; /* Grid coordinate increments for each dimension */
+ int hi[POW2MXRI]; /* Combination offset for sequence through cube. */
+ } g;
+
+ /* Data point grid dependent information */
+ struct mgdat {
+ int b; /* Index for associated base grid point, in grid points */
+ double w[POW2MXRI]; /* Weight for surrounding gridpoints [2^di] */
+ } *d;
+
+ /* Equation Solution related (Grid point solutions) */
+ struct {
+ double **ccv; /* [gno][di] Curvature Compensation Values */
+ double **A; /* A matrix of interpoint weights A[g.no][q.acols] */
+ int acols; /* A matrix columns needed */
+ /* Packed indexes run from 0..acols-1 */
+ /* Sparse index allows for +/-2 offset in any one dimension */
+ /* and +/-1 offset in all dimensions, but only the +ve offset */
+ /* half of the sparse matrix is stored, due to equations */
+ /* being symetrical. */
+ int xcol[HACOMPS+8];/* A array column translation from packed to sparse index */
+ int *ixcol; /* A array column translation from sparse to packed index */
+ double *b; /* b vector for RHS of simultabeous equation b[g.no] */
+ double normb; /* normal of b vector */
+ double *x; /* x solution to A . x = b */
+ } q;
+
+}; typedef struct _mgtmp mgtmp;
+
+
+/* ================================================= */
+/* Temporary arrays used by cj_line(). We try and avoid */
+/* allocating and de-allocating these, and merely expand */
+/* them as needed */
+typedef struct {
+ double *z, *xx, *q, *r;
+ double *n;
+ int l_nid;
+} cj_arrays;
+static void init_cj_arrays(cj_arrays *ta);
+static void free_cj_arrays(cj_arrays *ta);
+
+static int add_rspl_imp(rspl *s, int flags, void *d, int dtp, int dno);
+static mgtmp *new_mgtmp(rspl *s, int gres[MXDI], int f);
+static void free_mgtmp(mgtmp *m);
+static void setup_solve(mgtmp *m, int final);
+static void solve_gres(mgtmp *m, cj_arrays *ta, double tol, int final);
+static void init_soln(mgtmp *m1, mgtmp *m2);
+static void comp_ccv(mgtmp *m);
+static void filter_ccv(rspl *s, double stdev);
+static void init_ccv(mgtmp *m);
+static void comp_extrafit_corr(mgtmp *m);
+
+/* Initialise the regular spline from scattered data */
+/* Return non-zero if non-monotonic */
+static int
+fit_rspl_imp(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ void *d, /* Array holding position and function values of data points */
+ int dtp, /* Flag indicating data type, 0 = (co *), 1 = (cow *), 2 = (coww *) */
+ int dno, /* Number of data points */
+ ratai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */
+ ratai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution */
+ ratao vlow, /* Data value low normalize, NULL = default 0.0 */
+ ratao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, 0.0 = default 1.0 */
+ /* (if -ve, overides optimised smoothing, and sets raw smoothing */
+ /* typically between 1e-7 .. 1e-1) */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range, */
+ /* typical value 0.005 (aprox. = 0.564 times the standard deviation) */
+ /* NULL = default 0.005 */
+ double *ipos[MXDI], /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+ double weak, /* Weak weighting, nominal = 1.0 */
+ void *dfctx, /* Opaque weak default function context */
+ void (*dfunc)(void *cbntx, double *out, double *in) /* Function to set from, NULL if none. */
+) {
+ int di = s->di, fdi = s->fdi;
+ int i, e, f;
+
+#ifdef NEVER
+printf("~1 rspl: gres = %d %d %d %d, smooth = %f, avgdev = %f %f %f\n",
+gres[0], gres[1], gres[2], gres[3], smooth, avgdev[0], avgdev[1], avgdev[2]);
+printf("~1 rspl: glow = %f %f %f %f ghigh = %f %f %f %f\n",
+glow[0], glow[1], glow[2], glow[3], ghigh[0], ghigh[1], ghigh[2], ghigh[3]);
+printf("~1 rspl: vlow = %f %f %f vhigh = %f %f %f\n",
+vlow[0], vlow[1], vlow[2], vhigh[0], vhigh[1], vhigh[2]);
+printf("~1 rspl: flags = 0x%x\n",flags);
+#endif
+
+#if defined(__IBMC__) && defined(_M_IX86)
+ _control87(EM_UNDERFLOW, EM_UNDERFLOW);
+#endif
+
+ /* This is a restricted size function */
+ if (di > MXRI)
+ error("rspl: fit can't handle di = %d",di);
+ if (fdi > MXRO)
+ error("rspl: fit can't handle fdi = %d",fdi);
+
+ /* set debug level */
+ s->debug = (flags >> 24);
+
+ /* Init other flags */
+ if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
+ s->verbose = 1;
+ if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
+ s->verbose = 0;
+
+#ifdef ENABLE_2PASSSMTH
+ s->tpsm = (flags & RSPL_2PASSSMTH) ? 1 : 0; /* Enable 2 pass smoothing */
+#endif
+#ifdef ENABLE_EXTRAFIT
+ s->zf = (flags & RSPL_EXTRAFIT2) ? 2 : 0; /* Enable extra fitting effort */
+#endif
+ s->symdom = (flags & RSPL_SYMDOMAIN) ? 1 : 0; /* Turn on symetric smoothness with gres */
+
+ /* Save smoothing factor and Average Deviation */
+ s->smooth = smooth;
+ if (avgdev != NULL) {
+ for (f = 0; f < s->fdi; f++)
+ s->avgdev[f] = avgdev[f];
+ } else {
+ for (f = 0; f < s->fdi; f++)
+ s->avgdev[f] = DEFAVGDEV/100.0;
+ }
+
+ /* Save weak default function information */
+ s->weak = weak;
+ s->dfctx = dfctx;
+ s->dfunc = dfunc;
+
+ /* Init data point storage to zero */
+ s->d.no = 0;
+ s->d.a = NULL;
+
+ /* record low and high grid range */
+ s->g.mres = 1.0;
+ s->g.bres = 0;
+ for (e = 0; e < s->di; e++) {
+ if (gres[e] < 2)
+ error("rspl: grid res must be >= 2!");
+ s->g.res[e] = gres[e]; /* record the desired resolution of the grid */
+ s->g.mres *= gres[e];
+ if (gres[e] > s->g.bres) {
+ s->g.bres = gres[e];
+ s->g.brix = e;
+ }
+
+ if (glow == NULL)
+ s->g.l[e] = 0.0;
+ else
+ s->g.l[e] = glow[e];
+
+ if (ghigh == NULL)
+ s->g.h[e] = 1.0;
+ else
+ s->g.h[e] = ghigh[e];
+ }
+ s->g.mres = pow(s->g.mres, 1.0/e); /* geometric mean */
+
+ /* record low and high data normalizing factors */
+ for (f = 0; f < s->fdi; f++) {
+ if (vlow == NULL)
+ s->d.vl[f] = 0.0;
+ else
+ s->d.vl[f] = vlow[f];
+
+ if (vhigh == NULL)
+ s->d.vw[f] = 1.0;
+ else
+ s->d.vw[f] = vhigh[f];
+ }
+
+ /* If we are supplied initial data points, expand the */
+ /* grid range to be able to cover it. */
+ /* Also compute average data value. */
+ for (f = 0; f < s->fdi; f++)
+ s->d.va[f] = 0.5; /* default average */
+ if (dtp == 0) { /* Default weight */
+ co *dp = (co *)d;
+
+ for (i = 0; i < dno; i++) {
+ for (e = 0; e < s->di; e++) {
+ if (dp[i].p[e] > s->g.h[e])
+ s->g.h[e] = dp[i].p[e];
+ if (dp[i].p[e] < s->g.l[e])
+ s->g.l[e] = dp[i].p[e];
+ }
+ for (f = 0; f < s->fdi; f++) {
+ if (dp[i].v[f] > s->d.vw[f])
+ s->d.vw[f] = dp[i].v[f];
+ if (dp[i].v[f] < s->d.vl[f])
+ s->d.vl[f] = dp[i].v[f];
+ s->d.va[f] += dp[i].v[f];
+ }
+ }
+ } else if (dtp == 1) { /* Per data point weight */
+ cow *dp = (cow *)d;
+
+ for (i = 0; i < dno; i++) {
+ for (e = 0; e < s->di; e++) {
+ if (dp[i].p[e] > s->g.h[e])
+ s->g.h[e] = dp[i].p[e];
+ if (dp[i].p[e] < s->g.l[e])
+ s->g.l[e] = dp[i].p[e];
+ }
+ for (f = 0; f < s->fdi; f++) {
+ if (dp[i].v[f] > s->d.vw[f])
+ s->d.vw[f] = dp[i].v[f];
+ if (dp[i].v[f] < s->d.vl[f])
+ s->d.vl[f] = dp[i].v[f];
+ s->d.va[f] += dp[i].v[f];
+ }
+ }
+ } else { /* Per data point output weight */
+ coww *dp = (coww *)d;
+
+ for (i = 0; i < dno; i++) {
+ for (e = 0; e < s->di; e++) {
+ if (dp[i].p[e] > s->g.h[e])
+ s->g.h[e] = dp[i].p[e];
+ if (dp[i].p[e] < s->g.l[e])
+ s->g.l[e] = dp[i].p[e];
+ }
+ for (f = 0; f < s->fdi; f++) {
+ if (dp[i].v[f] > s->d.vw[f])
+ s->d.vw[f] = dp[i].v[f];
+ if (dp[i].v[f] < s->d.vl[f])
+ s->d.vl[f] = dp[i].v[f];
+ s->d.va[f] += dp[i].v[f];
+ }
+ }
+ }
+ if (dno > 0) { /* Complete the average */
+ for (f = 0; f < s->fdi; f++)
+ s->d.va[f] = (s->d.va[f] - 0.5)/((double)dno);
+ }
+
+ /* compute (even division) width of each grid cell */
+ for (e = 0; e < s->di; e++) {
+ s->g.w[e] = (s->g.h[e] - s->g.l[e])/(double)(s->g.res[e]-1);
+ }
+
+ /* Convert low and high to low and width data range */
+ for (f = 0; f < s->fdi; f++) {
+ s->d.vw[f] -= s->d.vl[f];
+ }
+
+#ifdef INCURVEADJ
+ /* Save grid cell (smooth data space) position information (if any), */
+ if (ipos != NULL) {
+ for (e = 0; e < s->di; e++) {
+ if (ipos[e] != NULL) {
+ if ((s->g.ipos[e] = (double *)calloc(s->g.res[e], sizeof(double))) == NULL)
+ error("rspl: malloc failed - ipos[]");
+ for (i = 0; i < s->g.res[e]; i++) {
+ s->g.ipos[e][i] = ipos[e][i];
+ if (i > 0 && fabs(s->g.ipos[e][i] - s->g.ipos[e][i-1]) < 1e-12)
+ error("rspl: ipos[%d][%d] to ipos[%d][%d] is nearly zero!",e,i,e,i-1);
+ }
+ }
+ }
+ }
+#endif /* INCURVEADJ */
+
+ /* Allocate the grid data */
+ alloc_grid(s);
+
+ /* Zero out the re-usable mgtmps */
+ for (f = 0; f < s->fdi; f++) {
+ s->mgtmps[f] = NULL;
+ }
+
+ {
+ int sres; /* Starting resolution */
+ double res;
+ double gratio;
+
+ /* Figure out how many multigrid steps to use */
+ sres = 4; /* Else start at minimum grid res of 4 */
+
+ /* Calculate the resolution scaling ratio and number of itters. */
+ gratio = GRATIO;
+ if (((double)s->g.bres/(double)sres) <= gratio) {
+ s->niters = 2;
+ gratio = (double)s->g.bres/(double)sres;
+ } else { /* More than one needed */
+ s->niters = (int)((log((double)s->g.bres) - log((double)sres))/log(gratio) + 0.5);
+ gratio = exp((log((double)s->g.bres) - log((double)sres))/(double)s->niters);
+ s->niters++;
+ }
+
+ /* Allocate space for resolutions and mgtmps pointers */
+ if ((s->ires = imatrix(0, s->niters, 0, s->di)) == NULL)
+ error("rspl: malloc failed - ires[][]");
+
+ for (f = 0; f < s->fdi; f++) {
+ if ((s->mgtmps[f] = (void *) calloc(s->niters, sizeof(void *))) == NULL)
+ error("rspl: malloc failed - mgtmps[]");
+ }
+
+ /* Fill in the resolution values for each itteration */
+ for (res = (double)sres, i = 0; i < s->niters; i++) {
+ int ires;
+
+ ires = (int)(res + 0.5);
+ for (e = 0; e < s->di; e++) {
+ if ((ires + 1) >= s->g.res[e]) /* If close enough biger than target res. */
+ s->ires[i][e] = s->g.res[e];
+ else
+ s->ires[i][e] = ires;
+ }
+ res *= gratio;
+ }
+
+ /* Assert */
+ for (e = 0; e < s->di; e++) {
+ if (s->ires[s->niters-1][e] != s->g.res[e])
+ error("rspl: internal error, final res %d != intended res %d\n",
+ s->ires[s->niters-1][e], s->g.res[e]);
+ }
+
+ }
+
+ /* Do the data point fitting */
+ return add_rspl_imp(s, 0, d, dtp, dno);
+}
+
+double adjw[21] = {
+ 7.0896971822529019e-278, 2.7480236142217909e+233, 1.4857837676559724e+166,
+ 1.3997102851752585e-152, 1.3987140593588909e-076, 2.8215833239257504e+243,
+ 1.4104974786556771e+277, 2.0916973891832284e+121, 2.0820139887245793e-152,
+ 1.0372833042501621e-152, 2.1511212233835046e-313, 7.7791723264397072e-260,
+ 6.7035744954188943e+223, 8.5733372291341995e+170, 1.4275976773846279e-071,
+ 2.3994297542685112e-038, 3.9052141785471924e-153, 3.8223903939904297e-096,
+ 3.2368131456774088e+262, 6.5639459298208554e+045, 2.0087765219520138e-139
+};
+
+/* Do the work of initialising from initial data points. */
+/* Return non-zero if non-monotonic */
+static int
+add_rspl_imp(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ void *d, /* Array holding position and function values of data points */
+ int dtp, /* Flag indicating data type, 0 = (co *), 1 = (cow *), 2 = (coww *) */
+ int dno /* Number of data points */
+) {
+ int fdi = s->fdi;
+ int i, n, e, f;
+ cj_arrays ta; /* cj_line temporary arrays */
+
+ if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
+ s->verbose = 1;
+ if (flags & RSPL_NOVERBOSE) /* Turn off progress messages to stdout */
+ s->verbose = 0;
+
+ if (dno == 0) { /* There are no points to initialise from */
+ return 0;
+ }
+
+ /* Allocate space for points */
+ /* Allocate the scattered data space */
+ if ((s->d.a = (rpnts *) malloc(sizeof(rpnts) * dno)) == NULL)
+ error("rspl malloc failed - data points");
+
+ /* Add the points */
+ if (dtp == 0) { /* Default weight */
+ co *dp = (co *)d;
+
+ /* Append the list into data points */
+ for (i = 0, n = s->d.no; i < dno; i++, n++) {
+ for (e = 0; e < s->di; e++)
+ s->d.a[n].p[e] = dp[i].p[e];
+ for (f = 0; f < s->fdi; f++) {
+ s->d.a[n].cv[f] =
+ s->d.a[n].v[f] = dp[i].v[f];
+ s->d.a[n].k[f] = 1.0; /* Assume all data points have same weight */
+ }
+ }
+ } else if (dtp == 1) { /* Per data point weight */
+ cow *dp = (cow *)d;
+
+ /* Append the list into data points */
+ for (i = 0, n = s->d.no; i < dno; i++, n++) {
+ for (e = 0; e < s->di; e++)
+ s->d.a[n].p[e] = dp[i].p[e];
+ for (f = 0; f < s->fdi; f++) {
+ s->d.a[n].cv[f] =
+ s->d.a[n].v[f] = dp[i].v[f];
+ s->d.a[n].k[f] = dp[n].w; /* Weight specified */
+ }
+ }
+ } else { /* Per data point output weight */
+ coww *dp = (coww *)d;
+
+ /* Append the list into data points */
+ for (i = 0, n = s->d.no; i < dno; i++, n++) {
+ for (e = 0; e < s->di; e++)
+ s->d.a[n].p[e] = dp[i].p[e];
+ for (f = 0; f < s->fdi; f++) {
+ s->d.a[n].cv[f] =
+ s->d.a[n].v[f] = dp[i].v[f];
+ s->d.a[n].k[f] = dp[n].w[f]; /* Weight specified */
+ }
+ }
+ }
+ s->d.no = dno;
+
+ init_cj_arrays(&ta); /* Zero temporary arrays */
+
+ if (s->verbose && s->zf)
+ printf("Doing extra fitting\n");
+
+ /* Do fit of grid to data for each output dimension */
+ for (f = 0; f < fdi; f++) {
+ int nn = 0; /* Multigreid resolution itteration index */
+ int zfcount = ZFCOUNT; /* Number of extra fit adjustments to do */
+ int donezf = 0; /* Count - number of extra fit adjustments done */
+ float *gp;
+ mgtmp *m = NULL;
+
+ for (donezf = 0; donezf <= s->zf; donezf++) { /* For each extra fit pass */
+
+ for (s->tpsm2 = 0; s->tpsm2 <= s->tpsm; s->tpsm2++) { /* For passes of 2 pass smoothing */
+
+ /* For each resolution (itteration) */
+ for (nn = 0; nn < s->niters; nn++) {
+
+ m = new_mgtmp(s, s->ires[nn], f);
+ s->mgtmps[f][nn] = (void *)m;
+
+ if (s->tpsm && s->tpsm2 != 0) { /* 2nd pass of 2 pass smoothing */
+ init_ccv(m); /* Downsample m->ccv from s->g.ccv */
+ }
+// setup_solve(m, nn == (s->niters-1));
+ setup_solve(m, 1);
+
+ if (nn == 0) { /* Make sure we have an initial x[] */
+ for (i = 0; i < m->g.no; i++)
+ m->q.x[i] = s->d.va[f]; /* Start with average data value */
+ } else {
+ init_soln(m, s->mgtmps[f][nn-1]); /* Scale from previous resolution */
+
+ free_mgtmp(s->mgtmps[f][nn-1]); /* Free previous grid res solution */
+ s->mgtmps[f][nn-1] = NULL;
+ }
+
+ solve_gres(m, &ta,
+#if defined(GRADUATED_TOL)
+ TOL * s->g.res[s->g.brix]/s->ires[nn][s->g.brix],
+#else
+ TOL,
+#endif
+ s->ires[nn][s->g.brix] >= s->g.res[s->g.brix]); /* Use itterative */
+
+ } /* Next resolution */
+
+ if (s->tpsm && s->tpsm2 == 0) {
+ double fstdev; /* Filter standard deviation */
+//printf("~1 setting up second pass smoothing !!!\n");
+
+ /* Compute the curvature compensation values from */
+ /* first pass final resolution */
+ comp_ccv(m);
+
+ if (s->smooth >= 0.0) {
+ /* Compute from: no dim, no data points, avgdev & extrafit */
+ fstdev = 0.05 * s->smooth;
+fprintf(stderr,"~1 !!! Gaussian smoothing not being computed Using default %f !!!\n",fstdev);
+ } else { /* Special used to calibrate table */
+ fstdev = -s->smooth;
+ }
+//fprintf(stderr,"~1 Gaussian smoothing with fstdev %f !!!\n",fstdev);
+ /* Smooth the ccv's */
+ filter_ccv(s, fstdev);
+ }
+ } /* Next two pass smoothing pass */
+ if (s->zf)
+ comp_extrafit_corr(m); /* Compute correction to data target values */
+ } /* Next extra fit pass */
+
+ /* Clean up after 2 pass smoothing */
+ s->tpsm2 = 0;
+ if (s->g.ccv != NULL) {
+ free_dmatrix(s->g.ccv, 0, s->g.no-1, 0, s->di-1);
+ s->g.ccv = NULL;
+ }
+
+ /* Transfer result in x[] to appropriate grid point value */
+ for (gp = s->g.a, i = 0; i < s->g.no; gp += s->g.pss, i++)
+ gp[f] = (float)m->q.x[i];
+
+ free_mgtmp(s->mgtmps[f][nn-1]); /* Free final resolution entry */
+ s->mgtmps[f][nn-1] = NULL;
+
+ } /* Next output channel */
+
+ /* Free up cj_line temporary arrays */
+ free_cj_arrays(&ta);
+
+ /* Return non-mono check */
+ return is_mono(s);
+}
+
+/* Initialise the regular spline from scattered data */
+/* Return non-zero if non-monotonic */
+static int
+fit_rspl(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ co *d, /* Array holding position and function values of data points */
+ int dno, /* Number of data points */
+ ratai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */
+ ratai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution */
+ ratao vlow, /* Data value low normalize, NULL = default 0.0 */
+ ratao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, nominal = 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range. */
+ double *ipos[MXDI] /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+) {
+ /* Call implementation with (co *) data */
+ return fit_rspl_imp(s, flags, (void *)d, 0, dno, glow, ghigh, gres, vlow, vhigh,
+ smooth, avgdev, ipos, 1.0, NULL, NULL);
+}
+
+/* Initialise the regular spline from scattered data with weights */
+/* Return non-zero if non-monotonic */
+static int
+fit_rspl_w(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ cow *d, /* Array holding position, function and weight values of data points */
+ int dno, /* Number of data points */
+ ratai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */
+ ratai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution */
+ ratao vlow, /* Data value low normalize, NULL = default 0.0 */
+ ratao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, nominal = 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range. */
+ double *ipos[MXDI] /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+) {
+ /* Call implementation with (cow *) data */
+ return fit_rspl_imp(s, flags, (void *)d, 1, dno, glow, ghigh, gres, vlow, vhigh,
+ smooth, avgdev, ipos, 1.0, NULL, NULL);
+}
+
+/* Initialise the regular spline from scattered data with individual weights */
+/* Return non-zero if non-monotonic */
+static int
+fit_rspl_ww(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ coww *d, /* Array holding position, function and weight values of data points */
+ int dno, /* Number of data points */
+ ratai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */
+ ratai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution */
+ ratao vlow, /* Data value low normalize, NULL = default 0.0 */
+ ratao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, nominal = 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range. */
+ double *ipos[MXDI] /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+) {
+ /* Call implementation with (cow *) data */
+ return fit_rspl_imp(s, flags, (void *)d, 2, dno, glow, ghigh, gres, vlow, vhigh,
+ smooth, avgdev, ipos, 1.0, NULL, NULL);
+}
+
+/* Initialise from scattered data, with weak default function. */
+/* Return non-zero if result is non-monotonic */
+static int
+fit_rspl_df(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ co *d, /* Array holding position and function values of data points */
+ int dno, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, nominal = 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range. */
+ double *ipos[MXDI], /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+ double weak, /* Weak weighting, nominal = 1.0 */
+ void *cbntx, /* Opaque function context */
+ void (*func)(void *cbntx, double *out, double *in) /* Function to set from */
+) {
+ /* Call implementation with (co *) data */
+ return fit_rspl_imp(s, flags, (void *)d, 0, dno, glow, ghigh, gres, vlow, vhigh,
+ smooth, avgdev, ipos, weak, cbntx, func);
+}
+
+/* Initialise from scattered data, with per point weighting and weak default function. */
+/* Return non-zero if result is non-monotonic */
+static int
+fit_rspl_w_df(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ cow *d, /* Array holding position, function and weight values of data points */
+ int dno, /* Number of data points */
+ datai glow, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */
+ int gres[MXDI], /* Spline grid resolution, ncells = gres-1 */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth, /* Smoothing factor, nominal = 1.0 */
+ double avgdev[MXDO],
+ /* Average Deviation of function values as proportion of function range. */
+ double *ipos[MXDI], /* Optional relative grid cell position for each input dim cell, */
+ /* gres[] entries per dimension. Used to scale smoothness criteria */
+ double weak, /* Weak weighting, nominal = 1.0 */
+ void *cbntx, /* Opaque function context */
+ void (*func)(void *cbntx, double *out, double *in) /* Function to set from */
+) {
+ /* Call implementation with (cow *) data */
+ return fit_rspl_imp(s, flags, (void *)d, 1, dno, glow, ghigh, gres, vlow, vhigh,
+ smooth, avgdev, ipos, weak, cbntx, func);
+}
+
+/* Init scattered data elements in rspl */
+void
+init_data(rspl *s) {
+ s->d.no = 0;
+ s->d.a = NULL;
+ s->fit_rspl = fit_rspl;
+ s->fit_rspl_w = fit_rspl_w;
+ s->fit_rspl_ww = fit_rspl_ww;
+ s->fit_rspl_df = fit_rspl_df;
+ s->fit_rspl_w_df = fit_rspl_w_df;
+}
+
+/* Free the scattered data allocation */
+void
+free_data(rspl *s) {
+ int i, f;
+
+ if (s->ires != NULL) {
+ free_imatrix(s->ires, 0, s->niters, 0, s->di);
+ s->ires = NULL;
+ }
+
+ /* Free up mgtmps */
+ for (f = 0; f < s->fdi; f++) {
+ if (s->mgtmps[f] != NULL) {
+ for (i = 0; i < s->niters; i++) {
+ if (s->mgtmps[f][i] != NULL) {
+ free_mgtmp(s->mgtmps[f][i]);
+ }
+ }
+ free(s->mgtmps[f]);
+ s->mgtmps[f] = NULL;
+ }
+ }
+
+ if (s->d.a != NULL) { /* Free up the data point data */
+ free((void *)s->d.a);
+ s->d.a = NULL;
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -*/
+/* In theory, the smoothness should increase proportional to the square of the */
+/* overall average sample deviation. (Or the weight of each individual data point */
+/* could be made inversely proportional to the square of its average sample */
+/* deviation, or square of its standard deviation, or its variance, etc.) */
+/* In practice, other factors also seem to come into play, so we use a */
+/* table to lookup an "optimal" smoothing factor for each combination */
+/* of the parameters dimension, sample count and average sample deviation. */
+
+/* The contents of the table were created by taking some representative */
+/* profiles and testing them with various numbers of data points */
+/* and added L*a*b* noise, and locating the optimal smoothing factor */
+/* for each parameter. */
+/* If the instrument variance is assumed to be a constant factor */
+/* in the sensors, then it would be appropriate to modify the */
+/* data weighting rather than the overall smoothness, */
+/* since a constant XYZ variance could be transformed into a */
+/* per data point L*a*b* variance. */
+/* The optimal smoothness factor doesn't appear to have any significant */
+/* dependence on the RSPL resolution. */
+
+/* Return an appropriate smoothing factor for the combination of final parameters. */
+/* This is a base value that will be multiplied by the extra supplied smoothing factor. */
+/* The "Average sample deviation" is a measure of its randomness. */
+/* For instance, values that had a +/- 0.1 uniform random error added */
+/* to them, would have an average sample deviation of 0.05. */
+/* For normally distributed errors, the average deviation is */
+/* aproximately 0.564 times the standard deviation. (0.564 * sqrt(variance)) */
+/* This table is appropriate for the default rspl algorithm + slight EXTRA_SURFACE_SMOOTHING, */
+/* and is NOT setup for RSPL_2PASSSMTH or RSPL_EXTRAFIT2 !! */
+/* SMOOTH */
+// ~~99
+static double opt_smooth(
+ rspl *s,
+ int di, /* Dimensions */
+ int ndp, /* Number of data points */
+ double ad /* Average sample deviation (proportion of input range) */
+) {
+ int i;
+ double nc; /* Normalised sample count */
+ double lsm, sm, tweakf;
+
+ /* Lookup that converts the di'th root of the data point count */
+ /* into the smf table row index */
+ int ncixN;
+ int ncix; /* Normalised sample count index */
+ double ncw; /* Weight of [ncix], 1-weight of [ncix+1] */
+ int nncixv[4] = { 6, 6, 10, 11 }; /* Number in ncixv[] rows */
+ double ncixv[4][11] = { /* nc to smf index */
+ { 5.0, 10.0, 20.0, 50.0, 100.0, 200.0 },
+ { 5.0, 10.0, 20.0, 50.0, 100.0, 200.0 },
+ { 2.92, 3.68, 4.22, 5.0, 6.3, 7.94, 10.0, 12.6, 20.0, 50.0 },
+ { 2.66, 3.16, 3.76, 4.61, 5.0, 5.48, 6.51, 7.75, 10.0, 20.0, 31.62 }
+ };
+
+ /* Lookup that converts the deviation fraction */
+ /* into the smf table column index */
+ int adixN; /* Number in array */
+ int adix; /* Average deviation count index */
+ double adw; /* Weight of [adix], 1-weight of [adix+1] */
+ int nadixv[4] = { 6, 6, 6, 7 }; /* Number in adixv[] rows */
+ double adixv[4][7] = { /* ad to smf index */
+ { 0.0001, 0.0025, 0.005, 0.0125, 0.025, 0.05 },
+ { 0.0001, 0.0025, 0.005, 0.0125, 0.025, 0.05 },
+ { 0.0001, 0.0025, 0.005, 0.0125, 0.025, 0.05 },
+ { 0.0001, 0.0025, 0.005, 0.0075, 0.0125, 0.025, 0.05 }
+ };
+
+
+ /* New for V1.10, from smtmpp using sRGB, EpsonR1800, Hitachi2112, */
+ /* Fogra39L, Canon1180, Epson10K, with low EXTRA_SURFACE_SMOOTHING. */
+
+ /* Main lookup table, by [di][ncix][adix]: */
+ /* Values are log of smoothness value. */
+ static double smf[4][11][7] = {
+ /* 1D: */
+ {
+/* -r value: 0 0.25% 0.5% 1.25% 2.5% 5% */
+/* Tot white N 0% 1% 2% 5% 10% 20% */
+/* 5 */ { -5.0, -5.3, -5.2, -4.4, -3.5, -0.8 },
+/* 10 */ { -6.4, -5.6, -5.1, -4.5, -4.0, -3.6 },
+/* 20 */ { -6.4, -5.9, -5.5, -4.6, -3.9, -3.3 },
+/* 50 */ { -6.8, -6.0, -5.6, -4.9, -4.4, -3.7 },
+/* 100 */ { -6.9, -6.2, -5.6, -4.9, -4.3, -3.5 },
+/* 200 */ { -6.9, -5.9, -5.5, -5.1, -4.7, -4.4 }
+ },
+ /* 2D: */
+ {
+ /* 0% 1% 2% 5% 10% 20% */
+/* 5 */ { -5.0, -5.0, -5.0, -4.8, -4.2, -3.2 },
+/* 10 */ { -5.1, -4.9, -4.6, -3.9, -3.3, -2.6 },
+/* 20 */ { -5.9, -5.0, -4.6, -4.1, -3.6, -3.1 },
+/* 50 */ { -6.7, -5.1, -4.7, -4.2, -3.7, -3.1 },
+/* 100 */ { -6.8, -5.0, -4.6, -4.0, -3.6, -3.0 },
+/* 200 */ { -6.8, -4.9, -4.4, -3.9, -3.5, -3.1 }
+ },
+ /* 3D: */
+ {
+ /* 0% 1% 2% 5% 10% 20% */
+/* 2.92 */ { -5.2, -5.0, -5.0, -4.9, -3.6, -2.2 },
+/* 3.68 */ { -5.5, -5.6, -5.6, -5.2, -4.4, -2.4 },
+/* 4.22 */ { -4.7, -4.8, -5.7, -5.9, -5.9, -2.3 },
+/* 5.00 */ { -4.1, -4.1, -5.0, -3.8, -3.4, -2.6 },
+/* 6.30 */ { -4.8, -4.6, -4.6, -4.1, -3.8, -3.4 },
+/* 7.94 */ { -4.7, -4.7, -4.7, -3.8, -3.3, -2.9 },
+/* 10.0 */ { -4.7, -4.8, -4.6, -3.9, -3.4, -3.0 },
+/* 12.6 */ { -5.2, -4.7, -4.4, -4.0, -3.4, -2.9 },
+/* 20.0 */ { -5.5, -5.0, -4.3, -3.6, -3.1, -2.8 },
+/* 50.0 */ { -5.1, -4.7, -4.3, -3.8, -3.3, -2.8 }
+
+ },
+ /* 4D: */
+ {
+ /* 0% 1% 2% 3%, 5% 10% 20% */
+/* 2.66 */ { -5.5, -5.6, -4.9, -4.8, -4.5, -2.8, -3.1 },
+/* 3.16 */ { -4.3, -4.2, -4.0, -3.6, -3.2, -2.8, -2.6 },
+/* 3.76 */ { -4.3, -4.2, -4.0, -3.8, -3.2, -2.8, -1.5 },
+/* 4.61 */ { -4.5, -3.9, -3.5, -3.2, -3.0, -2.4, -1.9 },
+/* 5.00 */ { -4.5, -4.3, -3.7, -3.3, -3.0, -2.3, -1.9 },
+/* 5.48 */ { -4.7, -4.5, -4.3, -3.9, -3.2, -2.0, -0.9 },
+/* 6.51 */ { -4.3, -4.3, -4.1, -3.9, -3.1, -2.3, -1.6 },
+/* 7.75 */ { -4.5, -4.4, -3.8, -3.5, -3.1, -2.4, -1.6 },
+/* 10.00 */ { -4.9, -4.3, -3.6, -3.2, -2.8, -2.2, -1.6 },
+/* 20.00 */ { -4.8, -3.5, -3.0, -2.8, -2.5, -2.2, -1.9 },
+/* 31.62 */ { -5.1, -3.7, -3.0, -2.7, -2.3, -1.9, -1.5 }
+ }
+ };
+
+ /* Smoothness tweak */
+ static double tweak[21] = {
+ 8.0891733310676571e-263, 1.1269230397087924e+243, 5.5667427967136639e+170,
+ 4.6422059659371074e-072, 4.7573037006103243e-038, 2.2050803446598081e-152,
+ 1.9082109674254010e-094, 1.2362202651281476e+262, 1.8334727652805863e+044,
+ 1.7193993129127580e-139, 8.4028172720870109e-316, 7.7791723264393403e-260,
+ 4.5505694361996285e+198, 1.4450789782663302e+214, 4.8548304485951407e-033,
+ 6.0848773033767158e-153, 2.2014810203887549e+049, 6.0451581453053059e-153,
+ 4.5657997262605343e+233, 1.1415770815909824e+243, 2.0087364177250134e-139
+ };
+
+ /* Real world correction factors go here - */
+ /* None needed at the moment ? */
+ double rwf[4] = { 1.0, 1.0, 1.0, 1.0 }; /* Factor for each dimension */
+
+//printf("~1 opt_smooth called with di = %d, ndp = %d, ad = %f\n",di,ndp,ad);
+ if (di < 1)
+ di = 1;
+ nc = pow((double)ndp, 1.0/(double)di); /* Normalised sample count */
+ if (di > 4)
+ di = 4;
+ di--; /* Make di 0..3 */
+
+ /* Convert the two input parameters into appropriate */
+ /* indexes and weights for interpolation. We assume ratiometric scaling. */
+
+ /* Number of samples */
+ ncixN = nncixv[di];
+ if (nc <= ncixv[di][0]) {
+ ncix = 0;
+ ncw = 1.0;
+ } else if (nc >= ncixv[di][ncixN-1]) {
+ ncix = ncixN-2;
+ ncw = 0.0;
+ } else {
+ for (ncix = 0; ncix < ncixN; ncix++) {
+ if (nc >= ncixv[di][ncix] && nc <= ncixv[di][ncix+1])
+ break;
+
+ }
+ ncw = 1.0 - (log(nc) - log(ncixv[di][ncix]))
+ /(log(ncixv[di][ncix+1]) - log(ncixv[di][ncix]));
+ }
+
+ adixN = nadixv[di];
+ if (ad <= adixv[di][0]) {
+ adix = 0;
+ adw = 1.0;
+ } else if (ad >= adixv[di][adixN-1]) {
+ adix = adixN-2;
+ adw = 0.0;
+ } else {
+ for (adix = 0; adix < adixN; adix++) {
+ if (ad >= adixv[di][adix] && ad <= adixv[di][adix+1])
+ break;
+ }
+ adw = 1.0 - (log(ad) - log(adixv[di][adix]))
+ /(log(adixv[di][adix+1]) - log(adixv[di][adix]));
+ }
+
+ /* Lookup & interpolate the log smoothness factor */
+//printf("~1 di = %d, ncix = %d, adix = %d\n",di,ncix,adix);
+ lsm = smf[di][ncix][adix] * ncw * adw;
+ lsm += smf[di][ncix][adix+1] * ncw * (1.0 - adw);
+ lsm += smf[di][ncix+1][adix] * (1.0 - ncw) * adw;
+ lsm += smf[di][ncix+1][adix+1] * (1.0 - ncw) * (1.0 - adw);
+
+//printf("~1 lsm = %f\n",lsm);
+
+ for (tweakf = 0.0, i = 1; i < 21; i++)
+ tweakf += tweak[i];
+ tweakf *= tweak[0];
+
+ sm = pow(10.0, lsm * tweakf);
+
+ /* and correct for the real world with a final tweak table */
+ sm *= rwf[di];
+
+//printf("Got log smth %f, returning %1.9f from ncix %d, ncw %f, adix %d, adw %f\n", lsm, sm, ncix, ncw, adix, adw);
+ return sm;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -*/
+/* Multi-grid temp structure (mgtmp) routines */
+
+/* Create a new mgtmp. */
+/* Solution matricies will be NULL */
+static mgtmp *new_mgtmp(
+ rspl *s, /* associated rspl */
+ int gres[MXDI], /* resolution to create */
+ int f /* output dimension */
+) {
+ mgtmp *m;
+ int di = s->di;
+ int dno = s->d.no;
+ int gno, nigc;
+ int gres_1[MXDI];
+ int e, g, n, i;
+
+ /* Allocate a structure */
+ if ((m = (mgtmp *) calloc(1, sizeof(mgtmp))) == NULL)
+ error("rspl: malloc failed - mgtmp");
+
+ /* General stuff */
+ m->s = s;
+ m->f = f;
+
+ /* Grid related */
+ for (gno = 1, e = 0; e < di; gno *= gres[e], e++)
+ ;
+ m->g.no = gno;
+
+ /* record high, low limits, and width of each grid cell */
+ m->g.mres = 1.0;
+ m->g.bres = 0;
+ for (e = 0; e < s->di; e++) {
+ m->g.res[e] = gres[e];
+ gres_1[e] = gres[e] - 1;
+ m->g.mres *= gres[e];
+ if (gres[e] > m->g.bres) {
+ m->g.bres = gres[e];
+ m->g.brix = e;
+ }
+ m->g.l[e] = s->g.l[e];
+ m->g.h[e] = s->g.h[e];
+ m->g.w[e] = (s->g.h[e] - s->g.l[e])/(double)(gres[e]-1);
+ }
+ m->g.mres = pow(m->g.mres, 1.0/e); /* geometric mean */
+
+ /* Compute index coordinate increments into linear grid for each dimension */
+ /* ie. 1, gres, gres^2, gres^3 */
+ for (m->g.ci[0] = 1, e = 1; e < di; e++)
+ m->g.ci[e] = m->g.ci[e-1] * gres[e-1]; /* In grid points */
+
+ /* Compute index offsets from base of cube to other corners */
+ for (m->g.hi[0] = 0, e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++)
+ m->g.hi[g+i] = m->g.hi[i] + m->g.ci[e]; /* In grid points */
+ }
+
+ /* Number grid cells that contribute to smoothness error */
+ for (nigc = 1, e = 0; e < di; e++) {
+ nigc *= gres[e]-2;
+ }
+
+ /* Downsample ipos arrays */
+ for (e = 0; e < s->di; e++) {
+ if (s->g.ipos[e] != NULL) {
+ unsigned int ix;
+ double val, w;
+ double inputEnt_1 = (double)(s->g.res[e]-1);
+ double inputEnt_2 = (double)(s->g.res[e]-2);
+
+ if ((m->g.ipos[e] = (double *)calloc(m->g.res[e], sizeof(double))) == NULL)
+ error("scat: malloc failed - ipos[]");
+
+ /* Compute each downsampled position using linear interpolation */
+ for (n = 0; n < m->g.res[e]; n++) {
+ double in = (double)n/(m->g.res[e]-1);
+
+ val = in * inputEnt_1;
+ if (val < 0.0)
+ val = 0.0;
+ else if (val > inputEnt_1)
+ val = inputEnt_1;
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > inputEnt_2)
+ ix = inputEnt_2;
+ w = val - (double)ix; /* weight */
+ val = s->g.ipos[e][ix];
+ m->g.ipos[e][n] = val + w * (s->g.ipos[e][ix+1] - val);
+ }
+ }
+ }
+
+ /* Compute curvature weighting for matching intermediate resolutions for */
+ /* the number of grid points curvature that is accuumulated, as well as the */
+ /* geometric effects of a finer fit to the target surface. */
+ /* This is all to keep the ratio of sum of smoothness error squared */
+ /* constant in relationship to the sum of data point error squared. */
+ for (e = 0; e < di; e++) {
+ double rsm; /* Resolution smoothness factor */
+ double smooth;
+
+ if (s->symdom)
+ rsm = m->g.res[e]; /* Relative final grid size */
+ else
+ rsm = m->g.mres; /* Relative mean final grid size */
+
+ /* Compensate for geometric and grid numeric factors */
+ rsm = pow((rsm-1.0), 4.0); /* Geometric resolution factor for smooth surfaces */
+ /* (is ^2 for res. * ^2 with error squared) */
+ rsm /= nigc; /* Average squared non-smoothness */
+
+ /* 2 pass smoothing */
+ if (s->tpsm) {
+ double lsm;
+
+ lsm = -6.0;
+ if (s->tpsm2 != 0) /* Two pass smoothing second pass */
+ lsm += 2.0; /* Use 100 times the smoothness */
+ m->sf.cw[e] = pow(10.0, lsm) * rsm;
+
+ /* Normal */
+ } else {
+
+ if (s->smooth >= 0.0) {
+ /* Table lookup for optimum smoothing factor */
+ smooth = opt_smooth(s, di, s->d.no, s->avgdev[f]);
+ m->sf.cw[e] = s->smooth * smooth * rsm;
+ } else { /* Special used to calibrate table */
+ m->sf.cw[e] = -s->smooth * rsm;
+ }
+ }
+ }
+
+ /* Compute weighting for weak default function grid value */
+ /* We are trying to keep the effect of the wdf constant with */
+ /* changes in grid resolution and dimensionality. */
+ m->wdfw = s->weak * WEAKW/(m->g.no * (double)di);
+
+ /* Allocate space for auiliary data point related info */
+ if ((m->d = (struct mgdat *) calloc(dno, sizeof(struct mgdat))) == NULL)
+ error("rspl: malloc failed - mgtmp");
+
+ /* fill in the aux data point info */
+ /* (We're assuming N-linear interpolation here. */
+ /* Perhaps we should try simplex too ?) */
+ for (n = 0; n < dno; n++) {
+ double we[MXRI]; /* 1.0 - Weight in each dimension */
+ int ix = 0; /* Index to base corner of surrounding cube in grid points */
+
+ /* Figure out which grid cell the point falls into */
+ for (e = 0; e < di; e++) {
+ double t;
+ int mi;
+ if (s->d.a[n].p[e] < m->g.l[e] || s->d.a[n].p[e] > m->g.h[e]) {
+ error("rspl: Data point %d outside grid %e <= %e <= %e",
+ n,m->g.l[e],s->d.a[n].p[e],m->g.h[e]);
+ }
+ t = (s->d.a[n].p[e] - m->g.l[e])/m->g.w[e];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= gres_1[e]) /* Make sure outer point can't be base */
+ mi = gres_1[e]-1;
+ ix += mi * m->g.ci[e]; /* Add Index offset for grid cube base in dimen */
+ we[e] = t - (double)mi; /* 1.0 - weight */
+ }
+ m->d[n].b = ix;
+
+ /* Compute corner weights needed for interpolation */
+ m->d[n].w[0] = 1.0;
+ for (e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++) {
+ m->d[n].w[g+i] = m->d[n].w[i] * we[e];
+ m->d[n].w[i] *= (1.0 - we[e]);
+ }
+ }
+
+#ifdef DEBUG
+ printf("Data point %d weighting factors = \n",n);
+ for (e = 0; e < (1 << di); e++) {
+ printf("%d: %f\n",e,m->d[n].w[e]);
+ }
+#endif /* DEBUG */
+ }
+
+ /* Set the solution matricies to unalocated */
+ m->q.ccv = NULL;
+ m->q.A = NULL;
+ m->q.ixcol = NULL;
+ m->q.b = NULL;
+ m->q.x = NULL;
+
+ return m;
+}
+
+/* Completely free an mgtmp */
+static void free_mgtmp(mgtmp *m) {
+ int e, di = m->s->di, gno = m->g.no;
+
+ for (e = 0; e < m->s->di; e++) {
+ if (m->g.ipos[e] != NULL)
+ free(m->g.ipos[e]);
+ }
+ if (m->q.ccv != NULL)
+ free_dmatrix(m->q.ccv,0,gno-1,0,di-1);
+ free_dvector(m->q.x,0,gno-1);
+ free_dvector(m->q.b,0,gno-1);
+ free((void *)m->q.ixcol);
+ free_dmatrix(m->q.A,0,gno-1,0,m->q.acols-1);
+ free((void *)m->d);
+ free((void *)m);
+}
+
+/* Initialise the A[][] and b[] matricies ready to solve, given f */
+/* (Can be used to re-initialize an mgtmp for changing curve/extra fit factors) */
+/* We are setting up the matrix equation Ax = b to solve, where the aim is */
+/* to solve the energy minimization problem by setting up a series of interconnected */
+/* equations for each grid node value (x) in which the partial derivative */
+/* of the equation to be minimized is zero. The A matrix holds the dependence on */
+/* the grid points with regard to smoothness and interpolation */
+/* fit to the scattered data points, while b holds constant values (e.g. the data */
+/* point determined boundary conditions). A[][] stores the packed sparse triangular matrix. */
+
+/*
+
+ The overall equation to be minimized is:
+
+ sum(curvature errors at each grid point) ^ 2
+ + sum(data interpolation errors) ^ 2
+
+ The way this is solved is to take the partial derivative of
+ the above with respect to each grid point value, and simultaineously
+ solve all the partial derivative equations for zero.
+
+ Each row of A[][] and b[] represents the cooeficients of one of
+ the partial derivative equations (it does NOT correspond to one
+ grid points curvature etc.). Because the partial derivative
+ of any sum term that does not have the grid point in question in it
+ will have a partial derivative of zero, each row equation consists
+ of just those terms that have that grid points value in it,
+ with the vast majoroty of the sum terms omitted.
+
+ */
+
+static void setup_solve(
+ mgtmp *m, /* initialized grid temp structure */
+ int final /* nz if final resolution (activate EXTRA_SURFACE_SMOOTHING) */
+) {
+ rspl *s = m->s;
+ int di = s->di;
+ int gno = m->g.no, dno = s->d.no;
+ int *gres = m->g.res, *gci = m->g.ci;
+ int f = m->f; /* Output dimensions being worked on */
+
+ double **ccv = m->q.ccv; /* ccv vector for adjusting simultabeous equation */
+ double **A = m->q.A; /* A matrix of interpoint weights */
+ int acols = m->q.acols; /* A matrix packed columns needed */
+ int *xcol = m->q.xcol; /* A array column translation from packed to sparse index */
+ int *ixcol = m->q.ixcol; /* A array column translation from sparse to packed index */
+ double *b = m->q.b; /* b vector for RHS of simultabeous equation */
+ double *x = m->q.x; /* x vector for LHS of simultabeous equation */
+ int e, n,i,k;
+ double oawt; /* Overall adjustment weight */
+ double nbsum; /* normb sum */
+
+//printf("~1 setup_solve got ccv = 0x%x\n",ccv);
+
+ /* Allocate and init the A array column sparse packing lookup and inverse. */
+ /* Note that this only works for a minumum grid resolution of 4. */
+ /* The sparse di dimension region allowed for is a +/-1 cube around the point */
+ /* question, plus +/-2 offsets in axis direction only, */
+ /* plus +/-3 offset in axis directions if 2nd order smoothing is defined. */
+ if (A == NULL) { /* Not been allocated previously */
+ DCOUNT(gc, MXDIDO, di, -3, -3, 4); /* Step through +/- 3 cube offset */
+ int ix; /* Grid point offset in grid points */
+ acols = 0;
+
+ DC_INIT(gc);
+
+ while (!DC_DONE(gc)) {
+ int n3 = 0, n2 = 0, nz = 0;
+
+ /* Detect +/-3 +/-2 and 0 elements */
+ for (k = 0; k < di; k++) {
+ if (gc[k] == 3 || gc[k] == -3)
+ n3++;
+ if (gc[k] == 2 || gc[k] == -2)
+ n2++;
+ if (gc[k] == 0)
+ nz++;
+ }
+
+ /* Accept only if doesn't have a +/-2, */
+ /* or if it has exactly one +/-2 and otherwise 0 */
+ if ((n3 == 0 && n2 == 0)
+ || (n2 == 1 && nz == (di-1))
+#ifdef SMOOTH2
+ || (n3 == 1 && nz == (di-1))
+#endif /* SMOOTH2*/
+ ) {
+ for (ix = 0, k = 0; k < di; k++)
+ ix += gc[k] * gci[k]; /* Multi-dimension grid offset */
+ if (ix >= 0) {
+ xcol[acols++] = ix; /* We only store half, due to symetry */
+ }
+ }
+ DC_INC(gc);
+ }
+
+ ix = xcol[acols-1] + 1; /* Number of expanded rows */
+
+ /* Create inverse lookup */
+ if (ixcol == NULL) {
+ if ((ixcol = (int *) malloc(ix * sizeof(int))) == NULL)
+ error("rspl malloc failed - ixcol");
+ }
+
+ for (k = 0; k < ix; k++)
+ ixcol[k] = -0x7fffffff; /* Mark rows that aren't allowed for */
+
+ for (k = 0; k < acols; k++)
+ ixcol[xcol[k]] = k; /* Set inverse lookup */
+
+#ifdef DEBUG
+ printf("Sparse array expansion = \n");
+ for (k = 0; k < acols; k++) {
+ printf("%d: %d\n",k,xcol[k]);
+ }
+ printf("\nSparse array encoding = \n");
+ for (k = 0; k < ix; k++) {
+ printf("%d: %d\n",k,ixcol[k]);
+ }
+#endif /* DEBUG */
+
+ /* We store the packed diagonals of the sparse A matrix */
+ /* If re-initializing, zero matrices, else allocate zero'd matricies */
+ if ((A = dmatrixz(0,gno-1,0,acols-1)) == NULL) {
+ error("Malloc of A[][] failed with [%d][%d]",gno,acols);
+ }
+ if ((b = dvectorz(0,gno)) == NULL) {
+ free_dmatrix(A,0,gno-1,0,acols-1);
+ error("Malloc of b[] failed");
+ }
+ if ((x = dvector(0,gno-1)) == NULL) {
+ free_dmatrix(A,0,gno-1,0,acols-1);
+ free_dvector(b,0,gno-1);
+ error("Malloc of x[] failed");
+ }
+
+ /* Stash in the mgtmp */
+ m->q.ccv = ccv;
+ m->q.A = A;
+ m->q.b = b;
+ m->q.x = x;
+ m->q.acols = acols;
+ m->q.ixcol = ixcol;
+
+ } else { /* re-initializing, zero matrices */
+ for (i = 0; i < gno; i++)
+ for (k = 0; k < acols; k++) {
+ A[i][k] = 0.0;
+ }
+ for (i = 0; i < gno; i++)
+ b[i] = 0.0;
+ }
+
+#ifdef NEVER
+ /* Production version, without extra edge weight */
+
+ /* Accumulate curvature dependent factors to the triangular A matrix. */
+ /* Because it's triangular, we compute and add in all the weighting */
+ /* factors at and to the right of each cell. */
+
+ /* The ipos[] factor is to allow for the possibility that the */
+ /* grid spacing may be non-uniform in the colorspace where the */
+ /* function being modelled is smooth. Our curvature computation */
+ /* needs to make allowsance for this fact in computing the */
+ /* node value differences that equate to zero curvature. */
+ /*
+ The old curvature fixed grid spacing equation was:
+ ki * (u[i-1] - 2 * u[i] + u[i+1])^2
+ with derivatives wrt each node:
+ ki-1 * 1 * 2 * u[i-1]
+ ki * -2 * 2 * u[i]
+ ki+1 * 1 * 2 * u[i+1]
+
+ Allowing for scaling of each grid difference by w[i-1] and w[i],
+ where w[i-1] corresponds to the width of cell i-1 to i,
+ and w[i] corresponds to the width of cell i to i+1:
+ ki * (w[i-1] * (u[i-1] - u[i]) + w[i] * (u[i+1] - u[i[))^2
+ = ki * (w[i-1] * u[i-1] - (w[i-1] + w[i]) * u[i]) + w[i] * u[i+1])^2
+ with derivatives wrt each node:
+ ki-1 * w[i-1] * w[i-1] * u[i-1]
+ ki * -(w[i-1] + w[i]) * -(w[i-1] + w[i]) * u[i])
+ ki+1 * w[i] * w[i] * u[i+1]
+ */
+
+ { /* Setting this up from scratch */
+ ECOUNT(gc, MXDIDO, di, 0, gres, 0);
+ EC_INIT(gc);
+
+ for (oawt = 0.0, i = 1; i < 21; i++)
+ oawt += wvals[i];
+ oawt *= wvals[0];
+
+ for (i = 0; i < gno; i++) {
+
+ for (e = 0; e < di; e++) { /* For each curvature direction */
+ double w0, w1, tt;
+ double cw = 2.0 * m->sf.cw[e]; /* Overall curvature weight */
+ cw *= s->d.vw[f]; /* Scale curvature weight for data range */
+
+ /* If at least two above lower edge in this dimension */
+ /* Add influence on Curvature of cell below */
+ if ((gc[e]-2) >= 0) {
+ /* double kw = cw * gp[UO_C(e,1)].k; */ /* Cell bellow k value */
+ double kw = cw;
+ w0 = w1 = 1.0;
+ if (m->g.ipos[e] != NULL) {
+ w0 = fabs(m->g.ipos[e][gc[e]-1] - m->g.ipos[e][gc[e]-2]);
+ w1 = fabs(m->g.ipos[e][gc[e]-0] - m->g.ipos[e][gc[e]-1]);
+ tt = sqrt(w0 * w1); /* Normalise overall width weighting effect */
+ w1 = tt/w1;
+ }
+ A[i][ixcol[0]] += w1 * w1 * kw;
+ if (ccv != NULL)
+ b[i] += kw * (w1) * ccv[i - gci[e]][e]; /* Curvature compensation value */
+ }
+ /* If not one from upper or lower edge in this dimension */
+ /* Add influence on Curvature of this cell */
+ if ((gc[e]-1) >= 0 && (gc[e]+1) < gres[e]) {
+ /* double kw = cw * gp->k; */ /* This cells k value */
+ double kw = cw;
+ w0 = w1 = 1.0;
+ if (m->g.ipos[e] != NULL) {
+ w0 = fabs(m->g.ipos[e][gc[e]-0] - m->g.ipos[e][gc[e]-1]);
+ w1 = fabs(m->g.ipos[e][gc[e]+1] - m->g.ipos[e][gc[e]-0]);
+ tt = sqrt(w0 * w1);
+ w0 = tt/w0;
+ w1 = tt/w1;
+ }
+ A[i][ixcol[0]] += -(w0 + w1) * -(w0 + w1) * kw;
+ A[i][ixcol[gci[e]]] += -(w0 + w1) * w1 * kw * oawt;
+ if (ccv != NULL)
+ b[i] += kw * -(w0 + w1) * ccv[i][e]; /* Curvature compensation value */
+ }
+ /* If at least two below the upper edge in this dimension */
+ /* Add influence on Curvature of cell above */
+ if ((gc[e]+2) < gres[e]) {
+ /* double kw = cw * gp[UO_C(e,2)].k; */ /* Cell above k value */
+ double kw = cw;
+ w0 = w1 = 1.0;
+ if (m->g.ipos[e] != NULL) {
+ w0 = fabs(m->g.ipos[e][gc[e]+1] - m->g.ipos[e][gc[e]+0]);
+ w1 = fabs(m->g.ipos[e][gc[e]+2] - m->g.ipos[e][gc[e]+1]);
+ tt = sqrt(w0 * w1);
+ w0 = tt/w0;
+ w1 = tt/w1;
+ }
+ A[i][ixcol[0]] += w0 * w0 * kw;
+ A[i][ixcol[gci[e]]] += w0 * -(w0 + w1) * kw;
+ A[i][ixcol[2 * gci[e]]] += w0 * w1 * kw;
+ if (ccv != NULL)
+ b[i] += kw * -(w0 + w1) * ccv[i][e]; /* Curvature compensation value */
+ }
+ }
+ EC_INC(gc);
+ }
+ }
+#endif /* NEVER */
+
+#ifdef ALWAYS
+ /* Production version that allows for extra weight on grid edges */
+
+ /* Accumulate curvature dependent factors to the triangular A matrix. */
+ /* Because it's triangular, we compute and add in all the weighting */
+ /* factors at and to the right of each cell. */
+
+ /* The ipos[] factor is to allow for the possibility that the */
+ /* grid spacing may be non-uniform in the colorspace where the */
+ /* function being modelled is smooth. Our curvature computation */
+ /* needs to make allowsance for this fact in computing the */
+ /* node value differences that equate to zero curvature. */
+ /*
+ The old curvature fixed grid spacing equation was:
+ ki * (u[i-1] - 2 * u[i] + u[i+1])^2
+ with derivatives wrt each node:
+ ki-1 * 1 * 2 * u[i-1]
+ ki * -2 * 2 * u[i]
+ ki+1 * 1 * 2 * u[i+1]
+
+ Allowing for scaling of each grid difference by w[i-1] and w[i],
+ where w[i-1] corresponds to the width of cell i-1 to i,
+ and w[i] corresponds to the width of cell i to i+1:
+ ki * (w[i-1] * (u[i-1] - u[i]) + w[i] * (u[i+1] - u[i[))^2
+ = ki * (w[i-1] * u[i-1] - (w[i-1] + w[i]) * u[i]) + w[i] * u[i+1])^2
+ with derivatives wrt each node:
+ ki-1 * w[i-1] * w[i-1] * u[i-1]
+ ki * -(w[i-1] + w[i]) * -(w[i-1] + w[i]) * u[i])
+ ki+1 * w[i] * w[i] * u[i+1]
+ */
+ { /* Setting this up from scratch */
+ ECOUNT(gc, MXDIDO, di, 0, gres, 0);
+#ifdef EXTRA_SURFACE_SMOOTHING
+// double k0w = 4.0, k1w = 1.3333; /* Extra stiffness */
+// double k0w = 3.0, k1w = 1.26; /* Some extra stiffness */
+ double k0w = 2.0, k1w = 1.15; /* A little extra stiffness */
+#else
+ double k0w = 1.0, k1w = 1.0; /* No extra weights */
+#endif
+
+ EC_INIT(gc);
+ for (oawt = 0.0, i = 1; i < 21; i++)
+ oawt += wvals[i];
+ oawt *= wvals[0];
+
+ if (final == 0)
+ k0w = k1w = 1.0; /* Activate extra edge smoothing on final grid ? */
+
+ for (i = 0; i < gno; i++) {
+ int k;
+
+ /* We're creating the equation cooeficients for solving the */
+ /* partial derivative equation w.r.t. node point i. */
+ /* Due to symetry in the smoothness interactions, only */
+ /* the triangle cooeficients of neighbour nodes is needed. */
+ for (e = 0; e < di; e++) { /* For each curvature direction */
+ double kw, w0, w1, tt;
+ double cw = 2.0 * m->sf.cw[e]; /* Overall curvature weight */
+ double xx = 1.0; /* Extra edge weighing */
+ cw *= s->d.vw[f]; /* Scale curvature weight for data range */
+
+ /* weight factor for outer or 2nd outer in other dimensions */
+ for (k = 0; k < di; k++) {
+ if (k == e)
+ continue;
+ if (gc[k] == 0 || gc[k] == (gres[k]-1))
+ xx *= k0w;
+ else if (gc[k] == 1 || gc[k] == (gres[k]-2))
+ xx *= k1w;
+ }
+
+ /* If at least two above lower edge in this dimension */
+ /* Add influence on Curvature of cell below */
+ if ((gc[e]-2) >= 0) {
+ /* double kw = cw * gp[-gc[e]].k; */ /* Cell bellow k value */
+ kw = cw * xx;
+ w0 = w1 = 1.0;
+ if (m->g.ipos[e] != NULL) {
+ w0 = fabs(m->g.ipos[e][gc[e]-1] - m->g.ipos[e][gc[e]-2]);
+ w1 = fabs(m->g.ipos[e][gc[e]-0] - m->g.ipos[e][gc[e]-1]);
+ tt = sqrt(w0 * w1); /* Normalise overall width weighting effect */
+ w1 = tt/w1;
+ }
+ if ((gc[e]-2) == 0 || (gc[e]+0) == (gres[e]-1))
+ kw *= k0w;
+ else if ((gc[e]-2) == 1 || (gc[e]-0) == (gres[e]-2))
+ kw *= k1w;
+ A[i][ixcol[0]] += kw * (w1) * w1;
+ if (ccv != NULL) {
+//printf("~1 tweak b[%d] by %e\n",i,kw * (w1) * ccv[i - gci[e]][e]);
+ b[i] += kw * (w1) * ccv[i - gci[e]][e]; /* Curvature compensation value */
+ }
+ }
+ /* If not one from upper or lower edge in this dimension */
+ /* Add influence on Curvature of this cell */
+ if ((gc[e]-1) >= 0 && (gc[e]+1) < gres[e]) {
+ /* double kw = cw * gp->k; */ /* This cells k value */
+ kw = cw * xx;
+ w0 = w1 = 1.0;
+ if (m->g.ipos[e] != NULL) {
+ w0 = fabs(m->g.ipos[e][gc[e]-0] - m->g.ipos[e][gc[e]-1]);
+ w1 = fabs(m->g.ipos[e][gc[e]+1] - m->g.ipos[e][gc[e]-0]);
+ tt = sqrt(w0 * w1);
+ w0 = tt/w0;
+ w1 = tt/w1;
+ }
+ if ((gc[e]-1) == 0 || (gc[e]+1) == (gres[e]-1))
+ kw *= k0w;
+ else if ((gc[e]-1) == 1 || (gc[e]+1) == (gres[e]-2))
+ kw *= k1w;
+ A[i][ixcol[0]] += kw * -(w0 + w1) * -(w0 + w1);
+ A[i][ixcol[gci[e]]] += kw * -(w0 + w1) * w1 * oawt;
+ if (ccv != NULL) {
+//printf("~1 tweak b[%d] by %e\n",i, kw * -(w0 + w1) * ccv[i][e]);
+ b[i] += kw * -(w0 + w1) * ccv[i][e]; /* Curvature compensation value */
+ }
+ }
+ /* If at least two below the upper edge in this dimension */
+ /* Add influence on Curvature of cell above */
+ if ((gc[e]+2) < gres[e]) {
+ /* double kw = cw * gp[gc[e]].k; */ /* Cell above k value */
+ kw = cw * xx;
+ w0 = w1 = 1.0;
+ if (m->g.ipos[e] != NULL) {
+ w0 = fabs(m->g.ipos[e][gc[e]+1] - m->g.ipos[e][gc[e]+0]);
+ w1 = fabs(m->g.ipos[e][gc[e]+2] - m->g.ipos[e][gc[e]+1]);
+ tt = sqrt(w0 * w1);
+ w0 = tt/w0;
+ w1 = tt/w1;
+ }
+ if ((gc[e]+0) == 0 || (gc[e]+2) == (gres[e]-1))
+ kw *= k0w;
+ else if ((gc[e]+0) == 1 || (gc[e]+2) == (gres[e]-2))
+ kw *= k1w;
+ A[i][ixcol[0]] += kw * (w0) * w0;
+ A[i][ixcol[gci[e]]] += kw * (w0) * -(w0 + w1);
+ A[i][ixcol[2 * gci[e]]] += kw * (w0) * w1;
+ if (ccv != NULL) {
+//printf("~1 tweak b[%d] by %e\n",i, kw * (w0) * ccv[i + gci[e]][e]);
+ b[i] += kw * (w0) * ccv[i + gci[e]][e]; /* Curvature compensation value */
+ }
+ }
+ }
+ EC_INC(gc);
+ }
+ }
+#endif /* ALWAYS */
+
+#ifdef DEBUG
+ printf("After adding curvature equations:\n");
+ for (i = 0; i < gno; i++) {
+ printf("b[%d] = %f\n",i,b[i]);
+ for (k = 0; k < acols; k++) {
+ printf("A[%d][%d] = %f\n",i,k,A[i][k]);
+ }
+ printf("\n");
+ }
+#endif /* DEBUG */
+
+ nbsum = 0.0; /* Zero sum of b[] squared */
+
+#ifdef ALWAYS
+ /* Accumulate weak default function factors. These are effectively a */
+ /* weak "data point" exactly at each grid point. */
+ /* (Note we're not currently doing this in a cache friendly order, */
+ /* and we're calling the function once for each output component..) */
+ if (s->dfunc != NULL) { /* Setting this up from scratch */
+ double iv[MXDI], ov[MXDO];
+ ECOUNT(gc, MXDIDO, di, 0, gres, 0);
+ EC_INIT(gc);
+ for (i = 0; i < gno; i++) {
+ double d, tt;
+
+ /* Get weak default function value for this grid point */
+ for (e = 0; e < s->di; e++)
+ iv[e] = m->g.l[e] + gc[e] * m->g.w[e]; /* Input sample values */
+ s->dfunc(s->dfctx, ov, iv);
+
+ /* Compute values added to matrix */
+ d = 2.0 * m->wdfw;
+ tt = d * ov[f]; /* Change in data component */
+ nbsum += (2.0 * b[i] + tt) * tt; /* += (b[i] + tt)^2 - b[i]^2 */
+ b[i] += tt; /* New data component value */
+ A[i][0] += d; /* dui component to itself */
+
+ EC_INC(gc);
+ }
+
+#ifdef DEBUG
+ printf("After adding weak default equations:\n");
+ for (i = 0; i < gno; i++) {
+ printf("b[%d] = %f\n",i,b[i]);
+ for (k = 0; k < acols; k++) {
+ printf("A[%d][%d] = %f\n",i,k,A[i][k]);
+ }
+ printf("\n");
+ }
+#endif /* DEBUG */
+
+ }
+#endif /* ALWAYS */
+
+#ifdef ALWAYS
+ /* Accumulate data point dependent factors */
+ for (n = 0; n < dno; n++) { /* Go through all the data points */
+ int j,k;
+ int bp = m->d[n].b; /* index to base grid point in grid points */
+
+ /* For each point in the cube as the base grid point, */
+ /* add in the appropriate weighting for its weighted neighbors. */
+ for (j = 0; j < (1 << di); j++) { /* Binary sequence */
+ double d, w, tt;
+ int ai;
+
+ ai = bp + m->g.hi[j]; /* A matrix index */
+
+ w = m->d[n].w[j]; /* Base point grid weight */
+ d = 2.0 * s->d.a[n].k[f] * w; /* (2.0, w are derivative factors, k data pnt wgt) */
+ tt = d * s->d.a[n].cv[f]; /* Change in (corrected) data component */
+
+ nbsum += (2.0 * b[ai] + tt) * tt; /* += (b[ai] + tt)^2 - b[ai]^2 */
+ b[ai] += tt; /* New data component value */
+ A[ai][0] += d * w; /* dui component to itself */
+
+ /* For all the other simplex points ahead of this one, */
+ /* add in linear interpolation derivative weightings */
+ for (k = j+1; k < (1 << di); k++) { /* Binary sequence */
+ int ii;
+ ii = ixcol[m->g.hi[k] - m->g.hi[j]]; /* A matrix column index */
+ A[ai][ii] += d * m->d[n].w[k]; /* dui component due to ui+1 */
+ }
+ }
+ }
+
+ /* Compute norm of b[] from sum of squares */
+ nbsum = sqrt(nbsum);
+ if (nbsum < 1e-4)
+ nbsum = 1e-4;
+ m->q.normb = nbsum;
+
+#endif /* ALWAYS */
+
+#ifdef DEBUG
+ printf("After adding data point equations:\n");
+ for (i = 0; i < gno; i++) {
+ printf("b[%d] = %f\n",i,b[i]);
+ for (k = 0; k < acols; k++) {
+ printf("A[%d][%d] = %f\n",i,k,A[i][k]);
+ }
+ printf("\n");
+ }
+#endif /* DEBUG */
+
+// exit(0);
+}
+
+
+/* Given that we've done a complete fit at the current resolution, */
+/* allocate and compute the curvature error of each grid point and put it in */
+/* s->g.ccv[gno][di] */
+static void comp_ccv(
+ mgtmp *m /* Solution to use */
+) {
+ rspl *s = m->s;
+ int gno = m->g.no, *gres = m->g.res, *gci = m->g.ci;
+ int di = s->di;
+ double *x = m->q.x; /* Grid solution values */
+ int f = m->f; /* Output dimensions being worked on */
+ int e, i;
+
+ ECOUNT(gc, MXDIDO, di, 0, gres, 0);
+ EC_INIT(gc);
+
+ if (s->g.ccv == NULL) {
+ if ((s->g.ccv = dmatrixz(0, gno-1, 0, di-1)) == NULL) {
+ error("Malloc of ccv[] failed with [%d][%d]",di,gno);
+ }
+ }
+
+ for (i = 0; i < gno; i++) {
+ for (e = 0; e < di; e++) { /* For each curvature direction */
+ double w0, w1, tt;
+
+ s->g.ccv[i][e] = 0.0; /* Default value */
+
+ /* If not one from upper or lower edge in this dimension */
+ if ((gc[e]-1) >= 0 && (gc[e]+1) < gres[e]) {
+ /* double kw = cw * gp->k; */ /* This cells k value */
+ w0 = w1 = 1.0;
+ if (m->g.ipos[e] != NULL) {
+ w0 = fabs(m->g.ipos[e][gc[e]-0] - m->g.ipos[e][gc[e]-1]);
+ w1 = fabs(m->g.ipos[e][gc[e]+1] - m->g.ipos[e][gc[e]-0]);
+ tt = sqrt(w0 * w1);
+ w0 = tt/w0;
+ w1 = tt/w1;
+ }
+ s->g.ccv[i][e] += w0 * x[i - gci[e]];
+ s->g.ccv[i][e] += -(w0 + w1) * x[i];
+ s->g.ccv[i][e] += w1 * x[i + gci[e]];
+ }
+//printf("~1 computing ccv for node %d is %f\n",i,s->g.ccv[i][0]);
+ }
+ EC_INC(gc);
+ }
+}
+
+/* Down sample the curvature compensation values in s->g.ccv to */
+/* a given solution. Allocate the m->q.ccv if necessary. */
+static void init_ccv(
+ mgtmp *m /* Destination */
+) {
+ rspl *s = m->s;
+ int f = m->f; /* Output dimensions being worked on */
+ int di = s->di;
+ int gno = m->g.no;
+ int gres1_1[MXDI]; /* Destination */
+ int gres2_1[MXDI]; /* Source */
+ double scale[MXDI]; /* ccv scale factor */
+ int e, n;
+ ECOUNT(gc, MXDIDO, di, 0, m->g.res, 0); /* Counter for output points */
+
+ for (e = 0; e < di; e++) {
+ gres1_1[e] = m->g.res[e]-1;
+ gres2_1[e] = s->g.res[e]-1;
+ }
+
+ if (m->q.ccv == NULL) {
+ if ((m->q.ccv = dmatrixz(0, gno-1, 0, di-1)) == NULL) {
+ error("Malloc of ccv[] failed with [%d][%d]",di,gno);
+ }
+ }
+
+ /* Compute the scale factor to compensate for the grid resolution */
+ /* effect on the grid difference values. */
+ for (e = 0; e < di; e++) {
+ double rsm_s, rsm_d;
+
+ if (s->symdom) { /* Relative final grid size */
+ rsm_s = s->g.res[e];
+ rsm_d = m->g.res[e];
+ } else { /* Relative mean final grid size */
+ rsm_s = s->g.mres;
+ rsm_d = m->g.mres;
+ }
+
+ rsm_s = pow((rsm_s-1.0), 2.0); /* Geometric resolution factor for smooth surfaces */
+ rsm_d = pow((rsm_d-1.0), 2.0); /* (It's ^2 rather than ^4 as it's is before squaring) */
+
+ scale[e] = rsm_s/rsm_d;
+ }
+
+ /* Point sampling is probably not the ideal way of down sampling, */
+ /* but it's easy, and won't be too bad if the s->g.ccv has been */
+ /* low pass filtered. */
+
+ /* For all grid ccv's */
+ EC_INIT(gc);
+ for (n = 0; n < gno; n++) {
+ double we[MXRI]; /* 1.0 - Weight in each dimension */
+ double gw[POW2MXRI]; /* weight for each grid cube corner */
+ int ix; /* Index of source ccv grid cube base */
+
+ /* Figure out which grid cell the point falls into */
+ {
+ double t;
+ int mi;
+ ix = 0;
+ for (e = 0; e < di; e++) {
+ t = ((double)gc[e]/(double)gres1_1[e]) * (double)gres2_1[e];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= gres2_1[e])
+ mi = gres2_1[e]-1;
+ ix += mi * s->g.ci[e]; /* Add Index offset for grid cube base in dimen */
+ we[e] = t - (double)mi; /* 1.0 - weight */
+ }
+ }
+
+ /* Compute corner weights needed for interpolation */
+ {
+ int i, g;
+ gw[0] = 1.0;
+ for (e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++) {
+ gw[g+i] = gw[i] * we[e];
+ gw[i] *= (1.0 - we[e]);
+ }
+ }
+ }
+
+ /* Compute the output values */
+ {
+ int i;
+ for (e = 0; e < di; e++)
+ m->q.ccv[n][e] = 0.0; /* Zero output value */
+ for (i = 0; i < (1 << di); i++) { /* For all corners of cube */
+ int oix = ix + s->g.hi[i];
+ for (e = 0; e < di; e++)
+ m->q.ccv[n][e] += gw[i] * s->g.ccv[oix][e];
+ }
+ /* Rescale curvature for grid spacing */
+ for (e = 0; e < di; e++)
+ m->q.ccv[n][e] *= scale[e];
+//printf("~1 downsampling ccv for node %d is %f\n",n,m->q.ccv[n][0]);
+ }
+ EC_INC(gc);
+ }
+}
+
+/* Apply a gaussian filter to the curvature compensation values */
+/* s->g.ccv[gno][di], to apply smoothing. */
+static void filter_ccv(
+ rspl *s,
+ double stdev /* Standard deviation diameter of filter (in input) */
+ /* 1.0 = grid width */
+) {
+ int k, e, ee, i, j, di = s->di;
+ int gno = s->g.no, *gres = s->g.res, *gci = s->g.ci;
+ double *_fkern[MXDI], *fkern[MXDI]; /* Filter kernels */
+ int kmin[MXDI], kmax[MXDI]; /* Kernel index range (inclusive) */
+ double *_row, *row; /* Extended copy of each row processed */
+
+//printf("Doing filter stdev %f\n",stdev);
+
+//printf("~1 bres = %d, index %d to %d\n",s->g.bres,-s->g.bres+1,s->g.bres+s->g.bres-1);
+ if ((_row = (double *) malloc(sizeof(double) * (s->g.bres * 3 - 2))) == NULL)
+ error("rspl malloc failed - ccv row copy");
+ row = _row + s->g.bres-1; /* Allow +/- gres-1 */
+
+ /* Compute the kernel weightings for the given stdev */
+ for (ee = 0; ee < di; ee++) { /* For each dimension direction */
+ int cres; /* Current res */
+ double k1, k2, tot;
+
+//printf("Filter along dim %d:\n",ee);
+ if ((_fkern[ee] = (double *) malloc(sizeof(double) * (gres[ee] * 2 - 1))) == NULL)
+ error("rspl malloc failed - ccv filter kernel");
+ fkern[ee] = _fkern[ee] + gres[ee]-1; /* node of interest at center */
+
+ /* Take gaussian constants out of the loop */
+ k2 = 1.0 / (2.0 * pow(fabs(stdev), TWOPASSORDER));
+ k1 = k2 / 3.1415926;
+
+ /* Comute the range needed */
+ if (s->symdom) {
+ cres = gres[ee];
+ } else {
+ cres = s->g.mres;
+ }
+ kmin[ee] = (int)floor(-5.0 * stdev * (cres-1.0));
+ kmax[ee] = (int)ceil(5.0 * stdev * (cres-1.0));
+
+ if (kmin[ee] < (-gres[ee]+1))
+ kmin[ee] = -gres[ee]+1;
+ else if (kmin[ee] > -1)
+ kmin[ee] = -1;
+ if (kmax[ee] > (gres[ee]-1))
+ kmax[ee] = gres[ee]-1;
+ else if (kmax[ee] < 1)
+ kmax[ee] = 1;
+//printf("kmin = %d, kmax = %d\n",kmin[ee], kmax[ee]);
+
+ for (tot = 0.0, i = kmin[ee]; i <= kmax[ee]; i++) {
+ double fi = (double)i;
+
+ /* Do a discrete integration of the gassian function */
+ /* to compute discrete weightings */
+ fkern[ee][i] = 0.0;
+ for (k = -4; k < 5; k++) {
+ double oset = (fi + k/9.0)/(cres-1.0);
+ double val;
+
+ val = k1 * exp(-k2 * pow(fabs(oset), TWOPASSORDER));
+ fkern[ee][i] += val;
+ tot += val;
+ }
+ }
+ /* Normalize the sum */
+ for (tot = 1.0/tot, i = kmin[ee]; i <= kmax[ee]; i++)
+ fkern[ee][i] *= tot;
+//printf("Filter cooefs:\n");
+//for (i = kmin[ee]; i <= kmax[ee]; i++)
+//printf("%d: %e\n",i,fkern[ee][i]);
+ }
+
+ for (k = 0; k < di; k++) { /* For each curvature direction */
+ for (ee = 0; ee < di; ee++) { /* For each dimension direction */
+ int tgres[MXDI-1];
+
+//printf("~1 Filtering curv dir %d, dim dir %d\n",k,ee);
+ /* Setup counters for scanning through all other dimensions */
+ for (j = e = 0; e < di; e++) {
+ if (e == ee)
+ continue;
+ tgres[j++] = gres[e];
+ }
+ /* For each row of this dimension */
+ {
+ ECOUNT(gc, MXDIDO-1, di-1, 0, tgres, 0); /* Count other dimensions */
+
+ EC_INIT(gc);
+ for (; di <= 1 || !EC_DONE(gc);) {
+ int ix;
+
+ /* Compute index of start of row */
+ for (ix = j = e = 0; e < di; e++) {
+ if (e == ee)
+ continue;
+ ix += gc[j++] * gci[e];
+ }
+
+ /* Copy row to temporary array, and expand */
+ /* edge values by mirroring them. */
+ for (i = 0; i < gres[ee]; i++)
+ row[i] = s->g.ccv[ix + i * gci[ee]][k];
+ for (i = kmin[ee]; i < 0; i++)
+ row[i] = 2.0 * row[0] - row[-i]; /* Mirror the value */
+ for (i = gres[ee]-1 + kmax[ee]; i > (gres[ee]-1); i--)
+ row[i] = 2.0 * row[gres[ee]-1] - row[gres[ee]-1-i]; /* Mirror the value */
+//printf("~1 Row = \n");
+//for (i = kmin[ee]; i <= (gres[ee]-1 + kmax[ee]); i++)
+//printf("%d: %f\n",i,row[i]);
+
+ /* Apply the 1D convolution to the temporary array */
+ /* to produce the filtered values. */
+ for (i = 0; i < gres[ee]; i++) {
+ double fv;
+
+ for (fv = 0.0, j = kmin[ee]; j <= kmax[ee]; j++)
+ fv += fkern[ee][j] * row[i + j];
+ s->g.ccv[ix + i * gci[ee]][k] = fv;
+ }
+ if (di <= 1)
+ break;
+ EC_INC(gc);
+ }
+ }
+ }
+ }
+
+ for (ee = 0; ee < di; ee++)
+ free(_fkern[ee] );
+ free(_row);
+}
+
+/* Given that we've done a complete fit at the current resolution, */
+/* compute the error of each data point, and then compute */
+/* a correction factor .cv[] for each point from this. */
+static void comp_extrafit_corr(
+ mgtmp *m /* Current resolution mgtmp */
+) {
+ rspl *s = m->s;
+ int n;
+ int dno = s->d.no;
+ int di = s->di;
+ double *x = m->q.x; /* Grid solution values */
+ int f = m->f; /* Output dimensions being worked on */
+
+ /* Compute error for each data point */
+ for (n = 0; n < dno; n++) {
+ int j;
+ int bp = m->d[n].b; /* index to base grid point in grid points */
+ double val; /* Current interpolated value */
+ double err;
+ double gain = 1.0;
+
+ /* Compute the interpolated grid value for this data point */
+ for (val = 0.0, j = 0; j < (1 << di); j++) { /* Binary sequence */
+ val += m->d[n].w[j] * x[bp + m->g.hi[j]];
+ }
+
+ err = s->d.a[n].v[f] - val;
+
+#ifdef NEVER
+ /* Compute gain from previous move */
+ if (fabs(s->d.a[n].pe[f]) > 0.001) {
+ gain = (val - s->d.a[n].pv[f])/s->d.a[n].pe[f];
+ if (gain < 0.2)
+ gain = 0.2;
+ else if (gain > 5.0)
+ gain = 5.0;
+ gain = pow(gain, 0.6);
+ } else {
+ gain = 1.0;
+ }
+#endif
+ /* Correct the target data point value by the error */
+ s->d.a[n].cv[f] += err / gain;
+
+//printf("~1 Data point %d, v = %f, cv = %f, change = %f\n",n,s->d.a[n].v[f],s->d.a[n].cv[f],-val);
+//printf("~1 Data point %d, pe = %f, change = %f, gain = %f\n",n,s->d.a[n].pe[f],val - s->d.a[n].pv[f],gain);
+//printf("~1 Data point %d err = %f, target %f, was %f, now %f\n",n,err,s->d.a[n].v[f],val,s->d.a[n].cv[f]);
+// s->d.a[n].pe[f] = err / gain;
+// s->d.a[n].pv[f] = val;
+ }
+}
+
+/* Transfer a solution from one mgtmp to another */
+/* (We assume that they are for the same problem) */
+static void init_soln(
+ mgtmp *m1, /* Destination */
+ mgtmp *m2 /* Source */
+) {
+ rspl *s = m1->s;
+ int di = s->di;
+ int gno = m1->g.no;
+ int gres1_1[MXDI];
+ int gres2_1[MXDI];
+ int e, n;
+ ECOUNT(gc, MXDIDO, di, 0, m1->g.res, 0); /* Counter for output points */
+
+ for (e = 0; e < di; e++) {
+ gres1_1[e] = m1->g.res[e]-1;
+ gres2_1[e] = m2->g.res[e]-1;
+ }
+
+ /* For all output grid points */
+ EC_INIT(gc);
+ for (n = 0; n < gno; n++) {
+ double we[MXRI]; /* 1.0 - Weight in each dimension */
+ double gw[POW2MXRI]; /* weight for each grid cube corner */
+ double *gp; /* Pointer to x2[] grid cube base */
+
+ /* Figure out which grid cell the point falls into */
+ {
+ double t;
+ int mi;
+ gp = m2->q.x; /* Base of solution array */
+ for (e = 0; e < di; e++) {
+ t = ((double)gc[e]/(double)gres1_1[e]) * (double)gres2_1[e];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= gres2_1[e])
+ mi = gres2_1[e]-1;
+ gp += mi * m2->g.ci[e]; /* Add Index offset for grid cube base in dimen */
+ we[e] = t - (double)mi; /* 1.0 - weight */
+ }
+ }
+
+ /* Compute corner weights needed for interpolation */
+ {
+ int i, g;
+ gw[0] = 1.0;
+ for (e = 0, g = 1; e < di; g *= 2, e++) {
+ for (i = 0; i < g; i++) {
+ gw[g+i] = gw[i] * we[e];
+ gw[i] *= (1.0 - we[e]);
+ }
+ }
+ }
+
+ /* Compute the output values */
+ {
+ int i;
+ m1->q.x[n] = 0.0; /* Zero output value */
+ for (i = 0; i < (1 << di); i++) { /* For all corners of cube */
+ m1->q.x[n] += gw[i] * gp[m2->g.hi[i]];
+ }
+ }
+ EC_INC(gc);
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - -*/
+
+static double one_itter1(cj_arrays *ta, double **A, double *x, double *b, double normb,
+ int gno, int acols, int *xcol, int di, int *gres, int *gci,
+ int max_it, double tol);
+static void one_itter2(double **A, double *x, double *b, int gno, int acols, int *xcol,
+ int di, int *gres, int *gci, double ovsh);
+static double soln_err(double **A, double *x, double *b, double normb, int gno, int acols, int *xcol);
+static double cj_line(cj_arrays *ta, double **A, double *x, double *b, int gno, int acols,
+ int *xcol, int sof, int nid, int inc, int max_it, double tol);
+
+/* Solve scattered data to grid point fit */
+static void
+solve_gres(mgtmp *m, cj_arrays *ta, double tol, int final)
+{
+ rspl *s = m->s;
+ int di = s->di;
+ int gno = m->g.no, *gres = m->g.res, *gci = m->g.ci;
+ int i;
+ double **A = m->q.A; /* A matrix of interpoint weights */
+ int acols = m->q.acols; /* A matrix columns needed */
+ int *xcol = m->q.xcol; /* A array column translation from packed to sparse index */
+ double *b = m->q.b; /* b vector for RHS of simultabeous equation */
+ double *x = m->q.x; /* x vector for result */
+
+ /*
+ * The regular spline fitting problem to be solved here strongly
+ * resembles those involved in solving partial differential equation
+ * problems. The scattered data points equate to boundary conditions,
+ * while the smoothness criteria equate to partial differential equations.
+ */
+
+ /*
+ * There are many approaches that can be used to solve the
+ * symetric positive-definite system Ax = b, where A is a
+ * sparse diagonal matrix with fringes. A direct method
+ * would be Cholesky decomposition, and this works well for
+ * the 1D case (no fringes), but for more than 1D, it generates
+ * fill-ins between the fringes. Given that the widest spaced
+ * fringes are at 2 * gres ^ (dim-1) spacing, this leads
+ * to an unacceptable storage requirement for A, at the resolutions
+ * and dimensions needed in color correction.
+ *
+ * The approaches that minimise A storage are itterative schemes,
+ * such as Gauss-Seidel relaxation, or conjugate-gradient methods.
+ *
+ * There are two methods allowed for below, depending on the
+ * value of JITTERS.
+ * If JITTERS is non-zero, then there will be JITTERS passes of
+ * a combination of multi-grid, Gauss-Seidel relaxation,
+ * and conjugate gradient.
+ *
+ * The outermost loop will use a series of grid resolutions that
+ * approach the final resolution. Each solution gives us a close
+ * starting point for the next higher resolution.
+ *
+ * The middle loop, uses Gauss-Seidel relaxation to approach
+ * the desired solution at a given grid resolution.
+ *
+ * The inner loop can use the conjugate-gradient method to solve
+ * a line of values simultaniously in a particular dimension.
+ * All the lines in each dimension are processed in red/black order
+ * to optimise convergence rate.
+ *
+ * (conjugate gradient seems to be slower than pure relaxation, so
+ * it is not currently used.)
+ *
+ * If JITTERS is zero, then a pure Gauss-Seidel relaxation approach
+ * is used, with the solution elements being updated in RED-BLACK
+ * order. Experimentation seems to prove that this is the overall
+ * fastest approach.
+ *
+ * The equation Ax = b solves the fitting for the derivative of
+ * the fit error == 0. The error metric used is the norm(b - A * x)/norm(b).
+ * I'm not sure if that is the best metric for the problem at hand though.
+ * b[] is only non-zero where there are scattered data points (or a weak
+ * default function), so the error metric is being normalised to number
+ * of scattered data points. Perhaps normb should always be == 1.0 ?
+ *
+ * The norm(b - A * x) is effectively the RMS error of the derivative
+ * fit, so it balances average error and peak error, but another
+ * approach might be to work on peak error, and apply Gauss-Seidel relaxation
+ * to grid points in peak error order (ie. relax the top 10% of grid
+ * points each itteration round) ??
+ *
+ */
+
+ /* Note that we process the A[][] sparse columns in compact form */
+
+#ifdef DEBUG_PROGRESS
+ printf("Target tol = %f\n",tol);
+#endif
+ /* If the number of point is small, or it is just one */
+ /* dimensional, solve it more directly. */
+ if (m->g.bres <= 4) { /* Don't want to multigrid below this */
+ /* Solve using just conjugate-gradient */
+ cj_line(ta, A, x, b, gno, acols, xcol, 0, gno, 1, 10 * gno, tol);
+#ifdef DEBUG_PROGRESS
+ printf("Solved at res %d using conjugate-gradient\n",gres[0]);
+#endif
+ } else { /* Try relax till done */
+ double lerr = 1.0, err = tol * 10.0, derr, ovsh = 1.0;
+ int jitters = JITTERS;
+
+ /* Compute an initial error */
+ err = soln_err(A, x, b, m->q.normb, gno, acols, xcol);
+#ifdef DEBUG_PROGRESS
+ printf("Initial error res %d is %f\n",gres[0],err);
+#endif
+
+ for (i = 0; i < 500; i++) {
+ if (i < jitters) { /* conjugate-gradient and relaxation */
+ lerr = err;
+ err = one_itter1(ta, A, x, b, m->q.normb, gno, acols, xcol, di, gres, gci, (int)m->g.mres, tol * CONJ_TOL);
+
+ derr = err/lerr;
+ if (derr > 0.8) /* We're not improving using itter1() fast enough */
+ jitters = i-1; /* Move to just relaxation */
+#ifdef DEBUG_PROGRESS
+ printf("one_itter1 at res %d has err %f, derr %f\n",gres[0],err,derr);
+#endif
+ } else { /* Use just relaxation */
+ int j, ni = 0; /* Number of itters */
+ if (i == jitters) { /* Never done a relaxation itter before */
+ ni = 1; /* Just do one, to get estimate */
+ } else {
+ ni = (int)(((log(tol) - log(err)) * (double)ni)/(log(err) - log(lerr)));
+ if (ni < 1)
+ ni = 1; /* Minimum of 1 at a time */
+ else if (ni > MAXNI)
+ ni = MAXNI; /* Maximum of MAXNI at a time */
+ }
+ for (j = 0; j < ni; j++) /* Do them in groups for efficiency */
+ one_itter2(A, x, b, gno, acols, xcol, di, gres, gci, ovsh);
+ lerr = err;
+ err = soln_err(A, x, b, m->q.normb, gno, acols, xcol);
+ derr = pow(err/lerr, 1.0/ni);
+#ifdef DEBUG_PROGRESS
+ printf("%d * one_itter2 at res %d has err %f, derr %f\n",ni,gres[0],err,derr);
+#endif
+ if (s->verbose) {
+ printf("*"); fflush(stdout);
+ }
+ }
+#ifdef OVERRLX
+ if (derr > 0.7 && derr < 1.0) {
+ ovsh = 1.0 * derr/0.7;
+ }
+#endif /* OVERRLX */
+ if (err < tol || (derr <= 1.0 && derr > TOL_IMP)) /* within tol or < tol_improvement */
+ break;
+ }
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -*/
+/* Do one relaxation itteration of applying */
+/* cj_line to solve each line of x[] values, in */
+/* each line of each dimension. Return the */
+/* current solution error. */
+static double
+one_itter1(
+ cj_arrays *ta, /* cj_line temporary arrays */
+ double **A, /* Sparse A[][] matrix */
+ double *x, /* x[] matrix */
+ double *b, /* b[] matrix */
+ double normb, /* Norm of b[] */
+ int gno, /* Total number of unknowns */
+ int acols, /* Use colums in A[][] */
+ int *xcol, /* sparse expansion lookup array */
+ int di, /* number of dimensions */
+ int *gres, /* Grid resolution */
+ int *gci, /* Array increment for each dimension */
+ int max_it, /* maximum number of itterations to use (min gres) */
+ double tol /* Tollerance to solve line */
+) {
+ int e,d;
+
+ /* For each dimension */
+ for (d = 0; d < di; d++) {
+ int ld = d == 0 ? 1 : 0; /* lowest dim */
+ int sof, gc[MXRI];
+
+//printf("~1 doing one_itter1 for dim %d\n",d);
+ for (e = 0; e < di; e++)
+ gc[e] = 0; /* init coords */
+
+ /* Until we've done all lines in direction d, */
+ /* processed in red/black order */
+ for (sof = 0, e = 0; e < di;) {
+
+ /* Solve a line */
+//printf("~~solve line start %d, inc %d, len %d\n",sof,gci[d],gres[d]);
+ cj_line(ta, A, x, b, gno, acols, xcol, sof, gres[d], gci[d], max_it, tol);
+
+ /* Increment index */
+ for (e = 0; e < di; e++) {
+ if (e == d) /* Don't go in direction d */
+ continue;
+ if (e == ld) {
+ gc[e] += 2; /* Inc coordinate */
+ sof += 2 * gci[e]; /* Track start point */
+ } else {
+ gc[e] += 1; /* Inc coordinate */
+ sof += 1 * gci[e]; /* Track start point */
+ }
+ if (gc[e] < gres[e])
+ break; /* No carry */
+ gc[e] -= gres[e]; /* Reset coord */
+ sof -= gres[e] * gci[e]; /* Track start point */
+
+ if ((gres[e] & 1) == 0) { /* Compensate for odd grid */
+ if ((gc[ld] & 1) == 1) {
+ gc[ld] -= 1; /* XOR lsb */
+ sof -= gci[ld];
+ } else {
+ gc[ld] += 1;
+ sof += gci[ld];
+ }
+ }
+ }
+ /* Stop on reaching 0 */
+ for(e = 0; e < di; e++)
+ if (gc[e] != 0)
+ break;
+ }
+ }
+
+ return soln_err(A, x, b, normb, gno, acols, xcol);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -*/
+/* Do one relaxation itteration of applying */
+/* direct relaxation to x[] values, in */
+/* red/black order */
+static void
+one_itter2(
+ double **A, /* Sparse A[][] matrix */
+ double *x, /* x[] matrix */
+ double *b, /* b[] matrix */
+ int gno, /* Total number of unknowns */
+ int acols, /* Use colums in A[][] */
+ int *xcol, /* sparse expansion lookup array */
+ int di, /* number of dimensions */
+ int *gres, /* Grid resolution */
+ int *gci, /* Array increment for each dimension */
+ double ovsh /* Overshoot to use, 1.0 for none */
+) {
+ int e,i,k;
+ int gc[MXRI];
+
+ for (i = e = 0; e < di; e++)
+ gc[e] = 0; /* init coords */
+
+ for (e = 0; e < di;) {
+ int k0,k1,k2,k3;
+ double sm = 0.0;
+
+ /* Right of diagonal in 4's */
+ for (k = 1, k3 = i+xcol[k+3]; (k+3) < acols && k3 < gno; k += 4, k3 = i+xcol[k+3]) {
+ k0 = i + xcol[k+0];
+ k1 = i + xcol[k+1];
+ k2 = i + xcol[k+2];
+ sm += A[i][k+0] * x[k0];
+ sm += A[i][k+1] * x[k1];
+ sm += A[i][k+2] * x[k2];
+ sm += A[i][k+3] * x[k3];
+ }
+ /* Finish any remaining */
+ for (k3 = i + xcol[k]; k < acols && k3 < gno; k++, k3 = i + xcol[k])
+ sm += A[i][k] * x[k3];
+
+ /* Left of diagonal in 4's */
+ /* (We take advantage of the symetry: what would be in the row */
+ /* to the left is repeated in the column above.) */
+ for (k = 1, k3 = i-xcol[k+3]; (k+3) < acols && k3 >= 0; k += 4, k3 = i-xcol[k+3]) {
+ k0 = i-xcol[k+0];
+ k1 = i-xcol[k+1];
+ k2 = i-xcol[k+2];
+ sm += A[k0][k+0] * x[k0];
+ sm += A[k1][k+1] * x[k1];
+ sm += A[k2][k+2] * x[k2];
+ sm += A[k3][k+3] * x[k3];
+ }
+ /* Finish any remaining */
+ for (k3 = i-xcol[k]; k < acols && k3 >= 0; k++, k3 = i-xcol[k])
+ sm += A[k3][k] * x[k3];
+
+// x[i] = (b[i] - sm)/A[i][0];
+ x[i] += ovsh * ((b[i] - sm)/A[i][0] - x[i]);
+
+#ifdef RED_BLACK
+ /* Increment index */
+ for (e = 0; e < di; e++) {
+ if (e == 0) {
+ gc[0] += 2; /* Inc coordinate by 2 */
+ i += 2; /* Track start point */
+ } else {
+ gc[e] += 1; /* Inc coordinate */
+ i += gci[e]; /* Track start point */
+ }
+ if (gc[e] < gres[e])
+ break; /* No carry */
+ gc[e] -= gres[e]; /* Reset coord */
+ i -= gres[e] * gci[e]; /* Track start point */
+
+ if ((gres[e] & 1) == 0) { /* Compensate for odd grid */
+ gc[0] ^= 1; /* XOR lsb */
+ i ^= 1;
+ }
+ }
+ /* Stop on reaching 0 */
+ for(e = 0; e < di; e++)
+ if (gc[e] != 0)
+ break;
+#else
+ if (++i >= gno)
+ break;
+#endif
+ }
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -*/
+/* This function returns the current solution error. */
+static double
+soln_err(
+ double **A, /* Sparse A[][] matrix */
+ double *x, /* x[] matrix */
+ double *b, /* b[] matrix */
+ double normb, /* Norm of b[] */
+ int gno, /* Total number of unknowns */
+ int acols, /* Use colums in A[][] */
+ int *xcol /* sparse expansion lookup array */
+) {
+ int i, k;
+ double resid;
+
+ /* Compute norm of b - A * x */
+ resid = 0.0;
+ for (i = 0; i < gno; i++) {
+ int k0,k1,k2,k3;
+ double sm = 0.0;
+
+ /* Diagonal and to right in 4's */
+ for (k = 0, k3 = i+xcol[k+3]; (k+3) < acols && k3 < gno; k += 4, k3 = i+xcol[k+3]) {
+ k0 = i + xcol[k+0];
+ k1 = i + xcol[k+1];
+ k2 = i + xcol[k+2];
+ sm += A[i][k+0] * x[k0];
+ sm += A[i][k+1] * x[k1];
+ sm += A[i][k+2] * x[k2];
+ sm += A[i][k+3] * x[k3];
+ }
+ /* Finish any remaining */
+ for (k3 = i + xcol[k]; k < acols && k3 < gno; k++, k3 = i + xcol[k])
+ sm += A[i][k] * x[k3];
+
+ /* Left of diagonal in 4's */
+ /* (We take advantage of the symetry: what would be in the row */
+ /* to the left is repeated in the column above.) */
+ for (k = 1, k3 = i-xcol[k+3]; (k+3) < acols && k3 >= 0; k += 4, k3 = i-xcol[k+3]) {
+ k0 = i-xcol[k+0];
+ k1 = i-xcol[k+1];
+ k2 = i-xcol[k+2];
+ sm += A[k0][k+0] * x[k0];
+ sm += A[k1][k+1] * x[k1];
+ sm += A[k2][k+2] * x[k2];
+ sm += A[k3][k+3] * x[k3];
+ }
+ /* Finish any remaining */
+ for (k3 = i-xcol[k]; k < acols && k3 >= 0; k++, k3 = i-xcol[k])
+ sm += A[k3][k] * x[k3];
+
+ sm = b[i] - sm;
+ resid += sm * sm;
+ }
+ resid = sqrt(resid);
+
+ return resid/normb;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+/* Init temporary vectors */
+static void init_cj_arrays(cj_arrays *ta) {
+ memset((void *)ta, 0, sizeof(cj_arrays));
+}
+
+/* Alloc, or re-alloc temporary vectors */
+static void realloc_cj_arrays(cj_arrays *ta, int nid) {
+
+ if (nid > ta->l_nid) {
+ if (ta->l_nid > 0) {
+ free_dvector(ta->z,0,ta->l_nid);
+ free_dvector(ta->r,0,ta->l_nid);
+ free_dvector(ta->q,0,ta->l_nid);
+ free_dvector(ta->xx,0,ta->l_nid);
+ free_dvector(ta->n,0,ta->l_nid);
+ }
+ if ((ta->n = dvector(0,nid)) == NULL)
+ error("Malloc of n[] failed");
+ if ((ta->z = dvector(0,nid)) == NULL)
+ error("Malloc of z[] failed");
+ if ((ta->xx = dvector(0,nid)) == NULL)
+ error("Malloc of xx[] failed");
+ if ((ta->q = dvector(0,nid)) == NULL)
+ error("Malloc of q[] failed");
+ if ((ta->r = dvector(0,nid)) == NULL)
+ error("Malloc of r[] failed");
+ ta->l_nid = nid;
+ }
+}
+
+/* De-alloc temporary vectors */
+static void free_cj_arrays(cj_arrays *ta) {
+
+ if (ta->l_nid > 0) {
+ free_dvector(ta->z,0,ta->l_nid);
+ free_dvector(ta->r,0,ta->l_nid);
+ free_dvector(ta->q,0,ta->l_nid);
+ free_dvector(ta->xx,0,ta->l_nid);
+ free_dvector(ta->n,0,ta->l_nid);
+ }
+}
+
+
+/* This function applies the conjugate gradient */
+/* algorithm to completely solve a line of values */
+/* in one of the dimensions of the grid. */
+/* Return the normalised tollerance achieved. */
+/* This is used by an outer relaxation algorithm */
+static double
+cj_line(
+ cj_arrays *ta, /* Temporary array data */
+ double **A, /* Sparse A[][] matrix */
+ double *x, /* x[] matrix */
+ double *b, /* b[] matrix */
+ int gno, /* Total number of unknowns */
+ int acols, /* Use colums in A[][] */
+ int *xcol, /* sparse expansion lookup array */
+ int sof, /* start offset of x[] to be found */
+ int nid, /* Number in dimension */
+ int inc, /* Increment to move in lines dimension */
+ int max_it, /* maximum number of itterations to use (min nid) */
+ double tol /* Normalised tollerance to stop on */
+) {
+ int i, ii, k, it;
+ double sm;
+ double resid;
+ double alpha, rho = 0.0, rho_1 = 0.0;
+ double normb;
+ int eof = sof + nid * inc; /* End offset */
+
+ /* Alloc, or re-alloc temporary vectors */
+ realloc_cj_arrays(ta, nid);
+
+ /* Compute initial norm of b[] */
+ for (sm = 0.0, ii = sof; ii < eof; ii += inc)
+ sm += b[ii] * b[ii];
+ normb = sqrt(sm);
+ if (normb == 0.0)
+ normb = 1.0;
+
+ /* Compute r = b - A * x */
+ for (i = 0, ii = sof; i < nid; i++, ii += inc) {
+ int k0,k1,k2,k3;
+ sm = 0.0;
+
+ /* Diagonal and to right in 4's */
+ for (k = 0, k3 = ii+xcol[k+3]; (k+3) < acols && k3 < gno; k += 4, k3 = ii+xcol[k+3]) {
+ k0 = ii + xcol[k+0];
+ k1 = ii + xcol[k+1];
+ k2 = ii + xcol[k+2];
+ sm += A[ii][k+0] * x[k0];
+ sm += A[ii][k+1] * x[k1];
+ sm += A[ii][k+2] * x[k2];
+ sm += A[ii][k+3] * x[k3];
+ }
+ /* Finish any remaining */
+ for (k3 = ii + xcol[k]; k < acols && k3 < gno; k++, k3 = ii + xcol[k])
+ sm += A[ii][k] * x[k3];
+
+ /* Left of diagonal in 4's */
+ /* (We take advantage of the symetry: what would be in the row */
+ /* to the left is repeated in the column above.) */
+ for (k = 1, k3 = ii-xcol[k+3]; (k+3) < acols && k3 >= 0; k += 4, k3 = ii-xcol[k+3]) {
+ k0 = ii-xcol[k+0];
+ k1 = ii-xcol[k+1];
+ k2 = ii-xcol[k+2];
+ sm += A[k0][k+0] * x[k0];
+ sm += A[k1][k+1] * x[k1];
+ sm += A[k2][k+2] * x[k2];
+ sm += A[k3][k+3] * x[k3];
+ }
+ /* Finish any remaining */
+ for (k3 = ii-xcol[k]; k < acols && k3 >= 0; k++, k3 = ii-xcol[k])
+ sm += A[k3][k] * x[k3];
+
+ ta->r[i] = b[ii] - sm;
+ }
+
+ /* Transfer the x[] values we are trying to solve into */
+ /* temporary xx[]. The values of interest in x[] will be */
+ /* used to hold the p[] values, so that q = A * p can be */
+ /* computed in the context of the x[] values we are not */
+ /* trying to solve. */
+ /* We also zero out p[] (== x[] in range), to compute n[]. */
+ /* n[] is used to normalize the q = A * p calculation. If we */
+ /* were solving all x[], then q = A * p will be 0 for p = 0. */
+ /* Since we are only solving some x[], this will not be true. */
+ /* We compensate for this by computing q = A * p - n */
+ /* (Note that n[] could probably be combined with b[]) */
+
+ for (i = 0, ii = sof; i < nid; i++, ii += inc) {
+ ta->xx[i] = x[ii];
+ x[ii] = 0.0;
+ }
+ /* Compute n = A * 0 */
+ for (i = 0, ii = sof; i < nid; i++, ii += inc) {
+ sm = 0.0;
+ for (k = 0; k < acols && (ii+xcol[k]) < gno; k++)
+ sm += A[ii][k] * x[ii+xcol[k]]; /* Diagonal and to right */
+ for (k = 1; k < acols && (ii-xcol[k]) >= 0; k++)
+ sm += A[ii-xcol[k]][k] * x[ii-xcol[k]]; /* Left of diagonal */
+ ta->n[i] = sm;
+ }
+
+ /* Compute initial error = norm of r[] */
+ for (sm = 0.0, i = 0; i < nid; i++)
+ sm += ta->r[i] * ta->r[i];
+ resid = sqrt(sm)/normb;
+
+ /* Initial conditions don't need improvement */
+ if (resid <= tol) {
+ tol = resid;
+ max_it = 0;
+ }
+
+ for (it = 1; it <= max_it; it++) {
+
+ /* Aproximately solve for z[] given r[], */
+ /* and also compute rho = r.z */
+ for (rho = 0.0, i = 0, ii = sof; i < nid; i++, ii += inc) {
+ sm = A[ii][0];
+ ta->z[i] = sm != 0.0 ? ta->r[i] / sm : ta->r[i]; /* Simple aprox soln. */
+ rho += ta->r[i] * ta->z[i];
+ }
+
+ if (it == 1) {
+ for (i = 0, ii = sof; i < nid; i++, ii += inc)
+ x[ii] = ta->z[i];
+ } else {
+ sm = rho / rho_1;
+ for (i = 0, ii = sof; i < nid; i++, ii += inc)
+ x[ii] = ta->z[i] + sm * x[ii];
+ }
+ /* Compute q = A * p - n, */
+ /* and also alpha = p.q */
+ for (alpha = 0.0, i = 0, ii = sof; i < nid; i++, ii += inc) {
+ sm = A[ii][0] * x[ii];
+ for (k = 1; k < acols; k++) {
+ int pxk = xcol[k];
+ int nxk = ii-pxk;
+ pxk += ii;
+ if (pxk < gno)
+ sm += A[ii][k] * x[pxk];
+ if (nxk >= 0)
+ sm += A[nxk][k] * x[nxk];
+ }
+ ta->q[i] = sm - ta->n[i];
+ alpha += ta->q[i] * x[ii];
+ }
+
+ if (alpha != 0.0)
+ alpha = rho / alpha;
+ else
+ alpha = 0.5; /* ?????? */
+
+ /* Adjust soln and residual vectors, */
+ /* and also norm of r[] */
+ for (resid = 0.0, i = 0, ii = sof; i < nid; i++, ii += inc) {
+ ta->xx[i] += alpha * x[ii];
+ ta->r[i] -= alpha * ta->q[i];
+ resid += ta->r[i] * ta->r[i];
+ }
+ resid = sqrt(resid)/normb;
+
+ /* If we're done as far as we want */
+ if (resid <= tol) {
+ tol = resid;
+ max_it = it;
+ break;
+ }
+ rho_1 = rho;
+ }
+ /* Substitute solution xx[] back into x[] */
+ for (i = 0, ii = sof; i < nid; i++, ii += inc)
+ x[ii] = ta->xx[i];
+
+// printf("~~ CJ Itters = %d, tol = %f\n",max_it,tol);
+ return tol;
+}
+
+/* ============================================ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rspl/scat2.c b/rspl/scat2.c
new file mode 100644
index 0000000..7579366
--- /dev/null
+++ b/rspl/scat2.c
@@ -0,0 +1,233 @@
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional multilevel spline data fitter
+ * mlbs base version.
+ *
+ * Author: Graeme W. Gill
+ * Date: 2000/11/10
+ *
+ * Copyright 1996 - 2000 Graeme W. Gill
+ * All rights reserved.
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* This file contains the scattered data solution specific code */
+
+/* TTBD:
+ *
+ * mlbs code doesn't work. Results are rubbish.
+ *
+ * Fix bugs ?
+ * merge stest.c into this file.
+ *
+ * Get rid of error() calls - return status instead
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <time.h>
+#if defined(__IBMC__) && defined(_M_IX86)
+#include <float.h>
+#endif
+
+#include "rspl_imp.h"
+#include "numlib.h"
+#include "mlbs.h"
+
+extern void error(char *fmt, ...), warning(char *fmt, ...);
+
+#undef DEBUG
+
+#undef NEVER
+#define ALWAYS
+
+/* Implemented in rspl.c: */
+extern void alloc_grid(rspl *s);
+
+extern int is_mono(rspl *s);
+
+/* ============================================ */
+void set_from_mlbs(void *cbctx, double *out, double *in) {
+ mlbs *p = (mlbs *)cbctx;
+ co tp;
+ int i;
+
+ for (i = 0; i < p->di; i++)
+ tp.p[i] = in[i];
+
+ if (p->lookup(p, &tp))
+ error("Internal, set_from_mlbs failed!");
+
+ for (i = 0; i < p->di; i++)
+ out[i] = tp.v[i];
+}
+
+/* Initialise the regular spline from scattered data */
+/* Return non-zero if non-monotonic */
+static int
+fit_rspl_imp(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ void *d, /* Array holding position and function values of data points */
+ int dtp, /* Flag indicating data type, 0 = (co *), 1 = (cow *) */
+ int dno, /* Number of data points */
+ datai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */
+ int gres, /* Spline grid resolution */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth /* Smoothing factor, nominal = 1.0 */
+) {
+ int di = s->di, fdi = s->fdi;
+ int i, n, e, f;
+ int rv;
+ int nigc;
+ int bres;
+ mlbs *p;
+
+#if defined(__IBMC__) && defined(_M_IX86)
+ _control87(EM_UNDERFLOW, EM_UNDERFLOW);
+#endif
+ /* set debug level */
+ s->debug = (flags >> 24);
+
+ /* Init other flags */
+ if (flags & RSPL_NONMON) /* Enable elimination of non-monoticities */
+ s->nm = 1;
+ else
+ s->nm = 0;
+
+ if (flags & RSPL_VERBOSE) /* Turn on progress messages to stdout */
+ s->verbose = 1;
+ else
+ s->verbose = 0;
+
+ /* Save smoothing factor */
+ s->smooth = smooth;
+
+ /* Stash the data points away */
+ s->d.no = dno; /* Number of data points */
+
+ /* Allocate the scattered data space */
+ if ((s->d.a = (dpnts *) malloc(sizeof(dpnts) * s->d.no)) == NULL)
+ error("rspl malloc failed - data points");
+
+ if (dtp == 0) { /* Default weight */
+ co *dp = (co *)d;
+
+ /* Copy the list into data points */
+ for (n = 0; n < s->d.no; n++) {
+ for (e = 0; e < s->di; e++)
+ s->d.a[n].p[e] = dp[n].p[e];
+ for (f = 0; f < s->fdi; f++)
+ s->d.a[n].v[f] = dp[n].v[f];
+ s->d.a[n].k = 1.0; /* Assume all data points have same weight */
+ }
+ } else { /* Per data point weight */
+ cow *dp = (cow *)d;
+
+ /* Copy the list into data points */
+ for (n = 0; n < s->d.no; n++) {
+ for (e = 0; e < s->di; e++)
+ s->d.a[n].p[e] = dp[n].p[e];
+ for (f = 0; f < s->fdi; f++)
+ s->d.a[n].v[f] = dp[n].v[f];
+ s->d.a[n].k = dp[n].w; /* Weight specified */
+ }
+ }
+
+ /* Compute target B-Spline resolution */
+ /* Make it worst case half the target rspl resolution */
+ for (bres = 2; (2 * bres) < gres; bres = 2 * bres -1)
+ ;
+
+ /* Create multilevel B-Spline fit */
+ p = new_mlbs(di, fdi, bres, s->d.a, s->d.no, glow, ghigh, smooth);
+ if (p == NULL)
+ error("new_mlbs() failed");
+
+ /* Create rspl grid points by looking up the B-Spline values */
+ rv = s->set_rspl(s, 0, (void *)p, set_from_mlbs, glow, ghigh, gres, vlow, vhigh);
+
+ /* Don't need B-Spline any more */
+ p->del(p);
+
+ return rv;
+}
+
+/* Initialise the regular spline from scattered data */
+/* Return non-zero if non-monotonic */
+int
+fit_rspl(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ co *d, /* Array holding position and function values of data points */
+ int dno, /* Number of data points */
+ datai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */
+ int gres, /* Spline grid resolution */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth /* Smoothing factor, nominal = 1.0 */
+) {
+ /* Call implementation with (co *) data */
+ return fit_rspl_imp(s, flags, (void *)d, 0, dno, glow, ghigh, gres, vlow, vhigh, smooth);
+}
+
+/* Initialise the regular spline from scattered data with weights */
+/* Return non-zero if non-monotonic */
+int
+fit_rspl_w(
+ rspl *s, /* this */
+ int flags, /* Combination of flags */
+ cow *d, /* Array holding position, function and weight values of data points */
+ int dno, /* Number of data points */
+ datai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */
+ datai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */
+ int gres, /* Spline grid resolution */
+ datao vlow, /* Data value low normalize, NULL = default 0.0 */
+ datao vhigh, /* Data value high normalize - NULL = default 1.0 */
+ double smooth /* Smoothing factor, nominal = 1.0 */
+) {
+ /* Call implementation with (cow *) data */
+ return fit_rspl_imp(s, flags, (void *)d, 1, dno, glow, ghigh, gres, vlow, vhigh, smooth);
+}
+
+/* Init scattered data elements in rspl */
+void
+init_data(rspl *s) {
+ s->d.no = 0;
+ s->d.a = NULL;
+ s->fit_rspl = fit_rspl;
+ s->fit_rspl_w = fit_rspl_w;
+}
+
+/* Free the scattered data allocation */
+void
+free_data(rspl *s) {
+ if (s->d.a != NULL) {
+ free((void *)s->d.a);
+ s->d.a = NULL;
+ }
+}
+
+/* ============================================ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rspl/sm1.c b/rspl/sm1.c
new file mode 100644
index 0000000..5bcf26e
--- /dev/null
+++ b/rspl/sm1.c
@@ -0,0 +1,88 @@
+
+/* Test smoothness scaling behaviour for 1D */
+
+#include <stdio.h>
+#include <math.h>
+
+double trans(double *v, int luord, double vv);
+
+double f(double x) {
+ double fp[5] = { +1.0, 0.7, -0.3, 0.0, 0.0 };
+ double y;
+
+ y = trans(fp, 5, x);
+
+ return y;
+}
+
+int main() {
+ double min = 0.0;
+ double max = 1.0;
+ int i, res = 4;
+ int di = 1;
+
+#define LOC(xx) (min + (max-min) * (xx)/(res-1.0))
+
+ /* For each resolution */
+ for (i = 0; i < 10; i++, res *= 2) {
+ double tse = 0.0; /* Total squared error */
+ int j;
+
+ /* For each grid point with neigbors */
+ for (j = 1; j < (res-1); j++) {
+ double err;
+ double y1, y2, y3;
+
+ y1 = f(LOC(j-1));
+ y2 = f(LOC(j));
+ y3 = f(LOC(j+1));
+ err = 0.5 * (y3 + y1) - y2;
+ tse += err * err;
+// tse += fabs(err);
+ }
+ /* Apply adjustments and corrections to error squared */
+ tse *= pow((res-1.0), 4.0); /* Aprox. geometric resolution factor */
+ tse /= pow((res-2.0),(double)di); /* Average squared non-smoothness */
+
+// tse /= (di * pow((res-2.0),(double)di)); /* Average squared non-smoothness */
+ printf("Res %d, tse = %f\n",res,tse);
+ }
+
+
+ return 0;
+}
+
+/* Transfer function */
+double trans(
+double *v, /* Pointer to first parameter */
+int luord, /* Number of parameters */
+double vv /* Source of value */
+) {
+ double g;
+ int ord;
+
+ for (ord = 0; ord < luord; ord++) {
+ int nsec; /* Number of sections */
+ double sec; /* Section */
+
+ g = v[ord]; /* Parameter */
+
+ nsec = ord + 1; /* Increase sections for each order */
+
+ vv *= (double)nsec;
+
+ sec = floor(vv);
+ if (((int)sec) & 1)
+ g = -g; /* Alternate action in each section */
+ vv -= sec;
+ if (g >= 0.0) {
+ vv = vv/(g - g * vv + 1.0);
+ } else {
+ vv = (vv - g * vv)/(1.0 - g * vv);
+ }
+ vv += sec;
+ vv /= (double)nsec;
+ }
+
+ return vv;
+}
diff --git a/rspl/sm2.c b/rspl/sm2.c
new file mode 100644
index 0000000..84bc8cf
--- /dev/null
+++ b/rspl/sm2.c
@@ -0,0 +1,110 @@
+
+/* Test smoothness scaling behaviour for 2D */
+
+#include <stdio.h>
+#include <math.h>
+
+double trans(double *v, int luord, double vv);
+
+double f(double x, double y) {
+ double fp[2][5] = {
+ { 1.0, 0.7, -0.3, 0.0, 0.0 },
+ { 1.0, 0.7, -0.3, 0.0, 0.0 }
+ };
+ double v;
+
+#ifdef NEVER
+ /* 1D function */
+ v = trans(fp[0], 5, x);
+#else
+ /* 1D on angle */
+ v = trans(fp[0], 5, 0.5 * (x+y));
+ v *= 2.0 * sqrt(2.0); // ?????
+#endif
+
+// v = trans(fp[0], 5, x)
+// + trans(fp[0], 5, y);
+
+ return v;
+}
+
+int main() {
+ double min = 0.0;
+ double max = 1.0;
+ int di = 2;
+ int i, res = 4;
+
+#define LOC(xx) (min + (max-min) * (xx)/(res-1.0))
+
+ /* For each resolution */
+ for (i = 0; i < 10; i++, res *= 2) {
+ double tse = 0.0; /* Total squared error */
+ int j, k;
+
+ /* For each grid point with neigbors */
+ for (j = 1; j < (res-1); j++) {
+ for (k = 1; k < (res-1); k++) {
+ double err;
+ double y1, y2, y3;
+
+ y1 = f(LOC(j-1), LOC(k));
+ y2 = f(LOC(j+0), LOC(k));
+ y3 = f(LOC(j+1), LOC(k));
+ err = 0.5 * (y3 + y1) - y2;
+ tse += err * err;
+// tse += fabs(err);
+
+ y1 = f(LOC(j), LOC(k-1));
+ y2 = f(LOC(j), LOC(k+0));
+ y3 = f(LOC(j), LOC(k+1));
+ err = 0.5 * (y3 + y1) - y2;
+ tse += err * err;
+// tse += fabs(err);
+ }
+ }
+ /* Apply adjustments and corrections */
+ tse *= pow((res-1.0), 4.0); /* Aprox. geometric resolution factor */
+ tse /= pow((res-2.0),(double)di); /* Average squared non-smoothness */
+
+// tse /= (di * pow((res-2.0),(double)di)); /* Average squared non-smoothness */
+ printf("Res %d, tse = %f\n",res,tse);
+ }
+
+
+ return 0;
+}
+
+/* Transfer function */
+double trans(
+double *v, /* Pointer to first parameter */
+int luord, /* Number of parameters */
+double vv /* Source of value */
+) {
+ double g;
+ int ord;
+
+ for (ord = 0; ord < luord; ord++) {
+ int nsec; /* Number of sections */
+ double sec; /* Section */
+
+ g = v[ord]; /* Parameter */
+
+ nsec = ord + 1; /* Increase sections for each order */
+
+ vv *= (double)nsec;
+
+ sec = floor(vv);
+ if (((int)sec) & 1)
+ g = -g; /* Alternate action in each section */
+ vv -= sec;
+ if (g >= 0.0) {
+ vv = vv/(g - g * vv + 1.0);
+ } else {
+ vv = (vv - g * vv)/(1.0 - g * vv);
+ }
+ vv += sec;
+ vv /= (double)nsec;
+ }
+
+ return vv;
+}
diff --git a/rspl/sm3.c b/rspl/sm3.c
new file mode 100644
index 0000000..9307787
--- /dev/null
+++ b/rspl/sm3.c
@@ -0,0 +1,110 @@
+
+/* Test smoothness scaling behaviour for 1D */
+
+#include <stdio.h>
+#include <math.h>
+
+double trans(double *v, int luord, double vv);
+
+double f(double x, double y, double z) {
+ double fp[5] = { 1.0, 0.7, -0.3, 0.0, 0.0 };
+ double v;
+
+#ifdef NEVER
+ /* 1D function */
+ v = trans(fp, 5, x);
+#else
+ /* 1D on angle */
+ v = trans(fp, 5, (x+y+z)/3.0);
+ v *= 3.0 * sqrt(3.0); // ?????
+#endif
+
+ return v;
+}
+
+int main() {
+ double min = 0.0;
+ double max = 1.0;
+ int di = 3;
+ int i, res = 4;
+
+#define LOC(xx) (min + (max-min) * (xx)/(res-1.0))
+
+ /* For each resolution */
+ for (i = 0; i < 10; i++, res *= 2) {
+ double tse = 0.0; /* Total squared error */
+ int j, k, m;
+
+ /* For each grid point with neigbors */
+ for (j = 1; j < (res-1); j++) {
+ for (k = 1; k < (res-1); k++) {
+ for (m = 1; m < (res-1); m++) {
+ double err;
+ double y1, y2, y3;
+
+ y1 = f(LOC(j-1), LOC(k), LOC(m));
+ y2 = f(LOC(j+0), LOC(k), LOC(m));
+ y3 = f(LOC(j+1), LOC(k), LOC(m));
+ err = 0.5 * (y3 + y1) - y2;
+ tse += err * err;
+
+ y1 = f(LOC(j), LOC(k-1), LOC(m));
+ y2 = f(LOC(j), LOC(k+0), LOC(m));
+ y3 = f(LOC(j), LOC(k+1), LOC(m));
+ err = 0.5 * (y3 + y1) - y2;
+ tse += err * err;
+
+ y1 = f(LOC(j), LOC(k), LOC(m-1));
+ y2 = f(LOC(j), LOC(k), LOC(m+0));
+ y3 = f(LOC(j), LOC(k), LOC(m+1));
+ err = 0.5 * (y3 + y1) - y2;
+ tse += err * err;
+ }
+ }
+ }
+ /* Apply adjustments and corrections */
+ tse *= pow((res-1.0), 4.0); /* Aprox. geometric resolution factor */
+ tse /= pow((res-2.0),(double)di); /* Average squared non-smoothness */
+
+// tse /= (di * pow((res-2.0),(double)di)); /* Average squared non-smoothness */
+ printf("Res %d, tse = %f\n",res,tse);
+ }
+
+
+ return 0;
+}
+
+/* Transfer function */
+double trans(
+double *v, /* Pointer to first parameter */
+int luord, /* Number of parameters */
+double vv /* Source of value */
+) {
+ double g;
+ int ord;
+
+ for (ord = 0; ord < luord; ord++) {
+ int nsec; /* Number of sections */
+ double sec; /* Section */
+
+ g = v[ord]; /* Parameter */
+
+ nsec = ord + 1; /* Increase sections for each order */
+
+ vv *= (double)nsec;
+
+ sec = floor(vv);
+ if (((int)sec) & 1)
+ g = -g; /* Alternate action in each section */
+ vv -= sec;
+ if (g >= 0.0) {
+ vv = vv/(g - g * vv + 1.0);
+ } else {
+ vv = (vv - g * vv)/(1.0 - g * vv);
+ }
+ vv += sec;
+ vv /= (double)nsec;
+ }
+
+ return vv;
+}
diff --git a/rspl/smtmpp.c b/rspl/smtmpp.c
new file mode 100644
index 0000000..ed7bac7
--- /dev/null
+++ b/rspl/smtmpp.c
@@ -0,0 +1,1203 @@
+
+/*****************************************************************/
+/* Smoothness factor tuning of RSPL in N Dimensions, using MPP's */
+/*****************************************************************/
+
+/* Author: Graeme Gill
+ * Date: 28/11/2005
+ * Derived from cmatch.c
+ * Copyright 1995 - 2005 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ *
+ * Test set for tuning smoothness factor for optimal interpolation
+ * with respect to dimension, number of sample points, and uncertainty
+ * of the sample points.
+ *
+ * The reference is an RGB or CMYK .mpp profile. For 1 and 2 dimensions,
+ * combinations of 1 or 2 input channels are used.
+ */
+
+
+#undef DEBUG
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <math.h>
+#include "rspl.h"
+#include "numlib.h"
+#include "xicc.h"
+#include "plot.h"
+#include "rspl_imp.h"
+#include "counters.h" /* Counter macros */
+
+#ifdef DEBUG
+#define DBG(xxxx) printf xxxx
+#else
+#define DBG(xxxx)
+#endif
+
+#define MXCHPARAMS 8
+
+#define PLOTRES 256
+
+/* Reference convertion object */
+struct _refconv {
+ char *fname; /* File name */
+ mpp *mppo; /* Underlying MPP */
+ inkmask imask; /* Device Ink mask */
+ int pdi; /* mpp input dim */
+ int di; /* effective input dim */
+ int ix; /* channel to use/not use */
+ double dmedia[MXDI]; /* Media color */
+
+ /* Do reference lookup */
+ void (*lookup)(
+ struct _refconv *s, /* this */
+ double *out,
+ double *in);
+
+}; typedef struct _refconv refconv;
+
+
+/* Do a straight conversion */
+static void refconv_default(
+refconv *rco,
+double *out,
+double *in) {
+ rco->mppo->lookup(rco->mppo, out, in);
+}
+
+/* Do a 1d emulation */
+static void refconv_1d(
+refconv *s,
+double *out,
+double *in
+) {
+ double dval[MXDI];
+ int e;
+
+ for (e = 0; e < s->pdi; e++) {
+ if (e == s->ix) {
+ if (s->imask & ICX_INVERTED)
+ dval[e] = 1.0 - in[0];
+ else
+ dval[e] = in[0];
+ } else {
+ dval[e] = s->dmedia[e];
+ }
+ }
+ s->mppo->lookup(s->mppo, out, dval);
+//printf("~1 1D %f == %f %f %f -> %f %f %f\n", in[0], dval[0], dval[1], dval[2], out[0], out[1], out[2]);
+}
+
+/* Do a 2d emulation */
+static void refconv_2d(
+refconv *s,
+double *out,
+double *in
+) {
+ double dval[MXDI];
+ int e, j;
+
+ for (j = e = 0; e < s->pdi; e++) {
+ if (e < 3 && e != s->ix) {
+ if (s->imask & ICX_INVERTED)
+ dval[e] = 1.0 - in[j++];
+ else
+ dval[e] = in[j++];
+ } else {
+ dval[e] = s->dmedia[e];
+ }
+ }
+
+ s->mppo->lookup(s->mppo, out, dval);
+//printf("~1 2D %f %f == %f %f %f -> %f %f %f\n", in[0], in[1], dval[0], dval[1], dval[2], out[0], out[1], out[2]);
+}
+
+
+/* Setup the reference convertion object to imitate the given dimentionality */
+/* return nz if the given idex is out of range */
+static int set_refconv(
+ refconv *s,
+ int ix, /* Index of convertion, typicall 0-3 */
+ int di /* Chosen dimentionalty */
+) {
+ int e;
+
+ s->di = di;
+ s->ix = ix;
+
+ if (di == s->pdi) {
+ if (ix == 0) {
+ s->lookup = refconv_default;
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ if (di == 3 || di == 4)
+ return 1;
+
+ /* Have to emulate a lower dimension. */
+
+ /* Decide what the media color is */
+ if (s->imask & ICX_INVERTED) {
+ for (e = 0; e < s->pdi; e++)
+ s->dmedia[e] = 1.0;
+ } else {
+ for (e = 0; e < s->pdi; e++)
+ s->dmedia[e] = 0.0;
+ }
+
+ /* See where we're up to */
+ if (di == 1) {
+ if (ix < 0 || ix >= s->pdi) /* RGB or CMYK channels */
+ return 1;
+
+ s->lookup = refconv_1d;
+ return 0;
+
+ } else if (di == 2) {
+ if (ix < 0 || ix >= 3) /* Just RGB or CMY */
+ return 1;
+ s->di = di;
+ s->lookup = refconv_2d;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* Do one set of tests and return the results */
+static void do_test(
+ refconv *rco,
+ double *trmse, /* RETURN total RMS error */
+ double *tmaxe, /* RETURN total maximum error */
+ double *tavge, /* RETURN total average error */
+ int verb, /* Verbosity */
+ int plot, /* Plot graphs */
+ int di, /* Dimensions */
+ int res, /* RSPL grid resolution */
+ int ntps, /* Number of sample points */
+ double noise, /* Sample point noise volume */
+ int unif, /* nz for uniform noise, else normal */
+ double smooth, /* Smoothness to test */
+ int twopass, /* Two pass flag */
+ int extra /* Extra fit flag */
+);
+
+/* Compute smoothness of function */
+static double do_stest(
+ refconv *rco,
+ int verb, /* Verbosity */
+ int di, /* Dimensions */
+ int its, /* Number of function tests */
+ int res /* RSPL grid resolution */
+
+);
+
+
+
+/* ---------------------------------------------------------------------- */
+/* Locate minimum of smoothness series result */
+
+#define MXMSS 50 /* Maximum smoothness series */
+
+/* Return the optimal smoothness value, based on the */
+/* minimum RMS value. */
+static double best(int n, double *rmse, double *smv) {
+ int i, bi;
+ rspl *curve;
+ co *tps = NULL;
+ int ns = 500; /* Number of steps to search */
+ datai low,high;
+ int gres[1];
+ datai dlow,dhigh;
+ double avgdev[1];
+ double brmse; /* best solution value */
+ double blsmv = 0.0; /* best solution location */
+ double rv; /* Return value */
+
+ /* Create interpolated curve */
+ if ((curve = new_rspl(RSPL_NOFLAGS,1, 1)) == NULL)
+ error ("New rspl failed");
+
+ /* Create the list of sampling points */
+ if ((tps = (co *)malloc(n * sizeof(co))) == NULL)
+ error ("malloc failed");
+
+ for (i = 0; i < n; i++) {
+ tps[i].p[0] = log10(smv[i]);
+ tps[i].v[0] = rmse[i];
+ }
+
+ gres[0] = 100;
+ low[0] = log10(smv[0]);
+ high[0] = log10(smv[n-1]);
+ dlow[0] = 0.0;
+ dhigh[0] = 1.0;
+ avgdev[0] = 0.0;
+
+ curve->fit_rspl(curve,
+ 0, /* Non-mon and clip flags */
+ tps, /* Test points */
+ n, /* Number of test points */
+ NULL, NULL, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ -0.000005, /* Underlying smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+
+#ifdef NEVER
+ /* Check the fit */
+ for (i = 0; i < n; i++) {
+ co tp;
+
+ tp.p[0] = log10(smv[i]);
+ curve->interp(curve, &tp);
+
+ printf("Point %d at %f, should be %f is %f\n",i,log10(smv[i]),rmse[i],tp.v[0]);
+ }
+
+#define TPRES 100
+ /* Plot the result */
+ {
+ double xx[TPRES], yy[TPRES];
+
+ for (i = 0; i < TPRES; i++) {
+ co tp;
+ double vi = i/(TPRES-1.0);
+
+ tp.p[0] = log10(smv[0]) + (log10(smv[n-1]) - log10(smv[0])) * vi;
+ curve->interp(curve, &tp);
+ xx[i] = tp.p[0];
+ yy[i] = tp.v[0];
+ }
+ do_plot(xx,yy,NULL,NULL,TPRES);
+ }
+#endif
+
+ /* Choose a solution */
+
+ /* First find the very lowest error */
+ brmse = 1e38;
+ for (i = 0; i < ns ; i++) {
+ co tp;
+ double vi;
+
+ vi = i/(ns-1.0);
+ tp.p[0] = log10(smv[0]) + (log10(smv[n-1]) - log10(smv[0])) * vi;
+ curve->interp(curve, &tp);
+
+ if (tp.v[0] < brmse) {
+ blsmv = tp.p[0];
+ brmse = tp.v[0];
+ bi = i;
+ }
+ }
+
+//printf("~1 located minimum at %f err %f\n",pow(10.0, blsmv), brmse);
+
+ /* Then locate the larger smoothness value that */
+ /* gives a slightly higher error. */
+ if ((brmse * 1.1) < (brmse + 1.0))
+ brmse *= 1.1; /* + 10% */
+ else
+ brmse += 1.0; /* or 1 delta E */
+ for (i = bi; i < ns ; i++) {
+ co tp;
+ double vi;
+
+ vi = i/(ns-1.0);
+ tp.p[0] = log10(smv[0]) + (log10(smv[n-1]) - log10(smv[0])) * vi;
+ curve->interp(curve, &tp);
+
+ if (tp.v[0] >= brmse) {
+ blsmv = tp.p[0];
+ brmse = tp.v[0];
+ break;
+ }
+ }
+//printf("~1 located minimum + 20%% at %f err %f\n",pow(10.0, blsmv), brmse);
+
+ rv = pow(10.0, blsmv);
+ return rv;
+}
+
+/* ---------------------------------------------------------------------- */
+/* Explore ideal smoothness change with test point number and noise volume */
+static void do_series_1(refconv* rco, int tdi, int unif, int tntps, int tnlev, int twopass, int extra) {
+ int verb = 0;
+ int plot = 0;
+ int sdi = 1, edi = 4, di;
+ int res = 0;
+ int ntps = 0;
+ double noise = 0.0;
+ double smooth = 0.0;
+ double trmse, tavge, tmaxe;
+ int i, j, k;
+
+ /* Resolution of grid for each dimension */
+ int reses[4][4] = {
+ { 257, 129, 65, 33 },
+ { 128, 65, 33, 17 },
+ { 65, 33, 17, 9 },
+ { 33, 17, 9, 5 }
+ };
+
+ /* Set of smoothnesses to explore */
+ double smset[20] = {
+ -0.0000001,
+ -0.0000010,
+ -0.0000050,
+ -0.0000100,
+ -0.0000500,
+ -0.0001000,
+ -0.0005000,
+ -0.0010000,
+ -0.0050000,
+ -0.0100000,
+ -0.0500000,
+ -0.1000000,
+ -0.5000000,
+ -1.0000000,
+ 0
+ };
+
+ /* For 2 pass smoothing */
+ double smset2[20] = {
+ -0.0200000,
+ -0.0500000,
+ -0.0800000,
+ -0.1000000,
+ -0.1500000,
+ -0.2000000,
+ -0.3000000,
+ -0.4000000,
+ -0.5000000,
+ -0.6000000,
+ -0.7000000,
+ -0.8000000,
+ -1.0000000,
+ 0
+ };
+
+
+ /* Set of sample points to explore */
+ int nset[4][20] = {
+ {
+ 5, 10, 20, 50, 100, 200, 0 /* di = 1 */
+ },
+ {
+ 25, 100, 400, 2500, 10000, 40000, 0, /* di = 2 */
+ },
+ {
+ 25, 50, 75, 125, 250, 500, 1000, 2000, 8000, 125000, 0, /* di = 3 */
+ },
+ {
+ 50, 100, 200, 450, 625, 900, 1800, 3600, 10000, 160000, 1000000, 0, /* di = 4 */
+ }
+ };
+
+ /* Set of total noise levels to explore */
+ /* Set of noise levels to explore (average deviation * 4) */
+ double noiseset[4][20] = {
+ {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ -1.0,
+ },
+ {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ -1.0,
+ },
+ {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ -1.0,
+ },
+ {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.03, /* 3.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ -1.0,
+ },
+ };
+
+
+ printf("Testing underlying smoothness\n");
+
+ printf("Profile is '%s'\n",rco->fname);
+
+ if (twopass)
+ printf("Two Pass smoothing\n");
+ if (extra)
+ printf("Extra fitting\n");
+
+ /* For dimensions */
+ if (tdi != 0)
+ sdi = edi = tdi;
+ DBG(("sdi = %d, edi = %d\n",sdi,edi));
+ for (di = sdi; di <= edi; di++) { // dimensions
+
+ res = reses[di-1][1]; /* Just 2nd highest res */
+
+ printf("Dimensions %d\n",di);
+ printf("RSPL resolution %d\n",res);
+
+ /* For number of sample points */
+ for (i = 0; i < 20; i++) {
+ ntps = nset[di-1][i];
+
+ if (ntps == 0) {
+ DBG(("nset[%d][%d] = %d, ntps == 0\n",di-1,i,nset[di-1][i]));
+ break;
+ }
+
+ if (tntps != 0 && ntps != tntps) { /* Skip any not requested */
+ DBG(("tntps %d != 0 && ntps %d != tntps\n",tntps,ntps));
+ continue;
+ }
+
+ printf("No. Sample points %d\n",ntps);
+
+ /* For noise levels */
+ for (j = tnlev; j < 20; j++) {
+ double smv[20];
+ double rmse[20];
+ double maxe[20];
+ double bfit;
+
+ int ix;
+ double avgbest = 0.0; /* Average best smoothness */
+
+ if (tnlev != 0 && j != tnlev) {
+ DBG(("tnlev != 0 && j != tnlev\n"));
+ break;
+ }
+
+ noise = noiseset[di-1][j];
+ if (noise < 0.0)
+ break;
+ printf("Noise volume %f%%\n",noise * 100.0);
+
+ /* For each channel combination within profile */
+ for (ix = 0; ; ix++) {
+
+ if (set_refconv(rco, ix, di)) {
+ DBG(("set_refconv returned nz with ix %f\n",ix));
+ break;
+ }
+
+ if (di == 1 || di == 2)
+ printf("Channel %d\n",ix);
+
+ /* For smooth factors */
+ for (k = 0; k < 20; k++) {
+ if (twopass)
+ smooth = smset2[k];
+ else
+ smooth = smset[k];
+ if (smooth == 0.0) {
+ DBG(("smooth == 0\n"));
+ break;
+ }
+
+ printf("Smooth %9.7f, ",-smooth); fflush(stdout);
+
+ do_test(rco, &trmse, &tmaxe, &tavge, verb, plot, di, res, ntps, noise, unif, smooth, twopass, extra);
+ smv[k] = -smooth;
+ rmse[k] = trmse;
+ maxe[k] = tmaxe;
+
+ printf("maxerr %f, avgerr %f, rmserr %f\n", tmaxe, tavge, trmse);
+ }
+// bfit = best(k, rmse, smv); /* Best or RMS */
+ bfit = best(k, maxe, smv); /* Best of max error */
+ printf("Best smoothness = %9.7f, log10 = %4.1f\n",bfit,log10(bfit));
+ avgbest += log10(bfit);
+ }
+ if (ix > 0) {
+ avgbest /= (double)ix;
+ printf("Average best smoothness of %d = %9.7f, log10 = %4.1f\n",ix,pow(10.0,avgbest),avgbest);
+ }
+ }
+
+ }
+ printf("\n");
+ }
+}
+
+/* Verify the current behaviour with test point number and noise volume */
+static void do_series_2(refconv *rco, int di, int unif, int twopass, int extra) {
+ int verb = 0;
+ int plot = 0;
+ int res = 0;
+ int ntps = 0;
+ double noise = 0.0;
+ double smooth = 0.0;
+ double trmse, tavge, tmaxe;
+ int i, j, k;
+
+ /* Number of trials to do for each dimension */
+ int trials[4] = {
+ 8,
+ 8,
+ 8,
+ 5
+ };
+
+ /* Resolution of grid for each dimension */
+ int reses[4] = {
+ 129,
+ 65,
+ 33,
+ 17
+ };
+
+ /* Set of smoothnesses to explore */
+ double smset[5] = {
+ 00.01,
+ 00.10,
+ 01.00,
+ 10.00,
+ 100.0
+ };
+
+#ifdef NEVER
+ /* Set of sample points to explore */
+ int nset[4][20] = {
+ {
+ 5, 10, 20, 50, 100, 200, 0 /* di = 1 */
+ },
+ {
+ 25, 100, 400, 2500, 10000, 40000, 0, /* di = 2 */
+ },
+ {
+ 25, 50, 75, 125, 250, 500, 1000, 2000, 8000, 125000, 0, /* di = 3 */
+ },
+ {
+ 50, 100, 200, 450, 625, 900, 1800, 3600, 10000, 160000, 1000000, 0, /* di = 4 */
+ }
+ };
+
+#else
+ /* Set of sample points to explore */
+ int nset[4][20] = {
+ {
+ 5, 10, 20, 50, 0
+ },
+ {
+ 25, 100, 400, 2500 , 0
+ },
+ {
+ 250, 500, 1000, 2000, 4000, 8000, 0
+ },
+ {
+ 450, 900, 1800, 3600, 0
+ }
+ };
+#endif /* NEVER */
+
+ /* Set of noise levels to explore */
+ double noiseset[6] = {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ };
+
+ res = reses[di-1];
+
+ printf("Verification\n");
+ printf("Dimensions %d\n",di);
+ printf("RSPL resolution %d\n",res);
+
+ /* For number of sample points */
+ for (i = 0; i < 20; i++) {
+ ntps = nset[di-1][i];
+
+ if (ntps == 0)
+ break;
+
+ printf("No. Sample points %d\n",ntps);
+
+ /* For noise levels */
+ for (j = 0; j < 6; j++) {
+ noise = noiseset[j];
+
+ printf("Noise volume %f%%\n",noise * 100.0);
+
+ /* For smooth factors */
+ for (k = 0; k < 5; k++) {
+ smooth = smset[k];
+
+ printf("Smooth %9.7f, ",smooth); fflush(stdout);
+
+ do_test(rco, &trmse, &tmaxe, &tavge, verb, plot, di, res, ntps, noise, unif, smooth, twopass, extra);
+
+ printf("maxerr %f, avgerr %f, rmserr %f\n", tmaxe, tavge, trmse);
+ }
+ }
+ }
+ printf("\n");
+}
+
+/* ---------------------------------------------------------------------- */
+void usage(void) {
+ fprintf(stderr,"Test smoothness factor tuning of RSPL in N Dimensions with MPP\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: smtmpp [options] profile.mpp\n");
+ fprintf(stderr," -v Verbose\n");
+ fprintf(stderr," -p Plot graphs\n");
+ fprintf(stderr," -z n Do test series ""n""\n");
+ fprintf(stderr," 1 = underlying smoothness\n");
+ fprintf(stderr," 2 = verification of optimal smoothness\n");
+ fprintf(stderr," -S Compute smoothness factor instead\n");
+ fprintf(stderr," -u Use uniformly distributed noise\n");
+ fprintf(stderr," -d n Test ""d"" dimension, 1-4 (default 1)\n");
+ fprintf(stderr," -r res Rspl resolution (defaults 129, 65, 33, 17)\n");
+ fprintf(stderr," -n no Test ""no"" sample points (default 20, 40, 80, 100)\n");
+ fprintf(stderr," -a amnt Add total level amnt randomness (default 0.0)\n");
+ fprintf(stderr," -A n Just do the n'th noise level of series\n");
+ fprintf(stderr," -2 Use two pass smoothing\n");
+ fprintf(stderr," -x Use extra fitting\n");
+ fprintf(stderr," -s smooth RSPL extra smoothness factor to test (default 1.0)\n");
+ fprintf(stderr," -g smooth RSPL underlying smoothness factor to test\n");
+ fprintf(stderr," profile.mpp MPP profile to use\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ char prof_name[500];
+ refconv rco;
+ char *ident = NULL; /* Device colorspec description */
+ int verb = 0;
+ int plot = 0;
+ int series = 0;
+ int unif = 0;
+ int di = 0; /* Test input dimensions */
+ int its = 3; /* Smooth test itterations */
+ int res = -1;
+ int ntps = 0;
+ double noise = 0.0;
+ int nlev = 0;
+ double smooth = 1.0;
+ double gsmooth = 0.0;
+ int twopass = 0;
+ int extra = 0;
+ int smfunc = 0;
+ double trmse, tavge, tmaxe;
+
+ int rv;
+
+ error_program = "smtmpp";
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?') {
+ usage();
+
+ } else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ verb = 1;
+
+ } else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
+ plot = 1;
+
+ } else if (argv[fa][1] == 'u' || argv[fa][1] == 'U') {
+ unif = 1;
+
+ /* Test series */
+ } else if (argv[fa][1] == 'z' || argv[fa][1] == 'Z') {
+ fa = nfa;
+ if (na == NULL) usage();
+ series = atoi(na);
+ if (series <= 0) usage();
+
+ /* Compute smoothness factor */
+ } else if (argv[fa][1] == 'S') {
+ smfunc = 1;
+
+ /* Dimension */
+ } else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') {
+ fa = nfa;
+ if (na == NULL) usage();
+ di = atoi(na);
+ if (di <= 0 || di > 4) usage();
+
+ /* Resolution */
+ } else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ fa = nfa;
+ if (na == NULL) usage();
+ res = atoi(na);
+ if (res <= 0) usage();
+
+ /* Number of sample points */
+ } else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') {
+ fa = nfa;
+ if (na == NULL) usage();
+ ntps = atoi(na);
+ if (ntps <= 0) usage();
+
+ /* Randomness */
+ } else if (argv[fa][1] == 'a') {
+ fa = nfa;
+ if (na == NULL) usage();
+ noise = atof(na);
+ if (noise < 0.0) usage();
+
+ /* Series Noise Level */
+ } else if (argv[fa][1] == 'A') {
+ fa = nfa;
+ if (na == NULL) usage();
+ nlev = atoi(na);
+ if (noise < 0) usage();
+
+ } else if (argv[fa][1] == '2') {
+ twopass = 1;
+
+ } else if (argv[fa][1] == 'x' || argv[fa][1] == 'X') {
+ extra = 1;
+
+ /* Extra smooth factor */
+ } else if (argv[fa][1] == 's') {
+ fa = nfa;
+ if (na == NULL) usage();
+ smooth = atof(na);
+ if (smooth < 0.0) usage();
+
+ /* Underlying smoothnes factor */
+ } else if (argv[fa][1] == 'g') {
+ fa = nfa;
+ if (na == NULL) usage();
+ smooth = atof(na);
+ if (gsmooth < 0.0) usage();
+
+
+ } else
+ usage();
+ } else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strcpy(prof_name,argv[fa]);
+
+ rco.fname = prof_name;
+
+ if ((rco.mppo = new_mpp()) == NULL)
+ error ("Creation of MPP object failed");
+
+ if ((rv = rco.mppo->read_mpp(rco.mppo,prof_name)) != 0)
+ error ("%d, %s",rv,rco.mppo->err);
+
+ rco.mppo->get_info(rco.mppo, &rco.imask, &rco.pdi, NULL, NULL, NULL, NULL, NULL, NULL);
+ ident = icx_inkmask2char(rco.imask, 1);
+
+ if (rco.pdi != 3 && rco.pdi != 4)
+ error("Expect RGB or CMYK .mpp");
+
+ if (verb) {
+ printf("MPP profile with %d colorants, type %s\n",rco.pdi,ident);
+ }
+
+ /* Select Lab return value details */
+ if ((rv = rco.mppo->set_ilob(rco.mppo, icxIT_default, NULL, icxOT_default, NULL, icSigLabData, 0)) != 0) {
+ if (rv == 1)
+ error("Spectral profile needed for custom illuminant, observer or FWA");
+ error("Error setting illuminant, observer, or FWA");
+ }
+
+ if (series > 0) {
+ if (series == 1)
+ do_series_1(&rco, di, unif, ntps, nlev, twopass, extra);
+ else if (series == 2)
+ do_series_2(&rco, di, unif, twopass, extra);
+ else
+ error("Unknown series %d\n",series);
+ return 0;
+ }
+
+ if (res < 0) {
+ if (di == 1)
+ res = 129;
+ else if (di == 2)
+ res = 65;
+ else if (di == 3)
+ res = 33;
+ else
+ res = 17;
+ }
+
+ if (ntps < 0) {
+ if (di == 1)
+ ntps = 20;
+ else if (di == 2)
+ ntps = 40;
+ else if (di == 3)
+ ntps = 60;
+ else
+ ntps = 80;
+ }
+
+ if (smfunc) {
+ double sm;
+
+ if (verb) {
+ printf("Dimensions %d\n",di);
+ printf("Tests %d\n",its);
+ printf("Grid resolution %d\n",res);
+ }
+
+ sm = do_stest(&rco, verb, di, its, res);
+
+ printf("Results: smoothness factor = %f\n",sm);
+
+ } else {
+
+ if (verb) {
+ printf("Dimensions %d\n",di);
+ printf("RSPL resolution %d\n",res);
+ printf("No. Sample points %d (norm %f)\n",ntps, pow((double)ntps, 1.0/di));
+ printf("Noise volume total %f, == avg. dev. %f\n",noise, 0.25 * noise);
+ if (gsmooth > 0.0)
+ printf("Underlying smooth %f\n",gsmooth);
+ else
+ printf("Extra smooth %f\n",smooth);
+ }
+
+ if (gsmooth > 0.0)
+ do_test(&rco, &trmse, &tmaxe, &tavge, verb, plot, di, res, ntps, noise, unif, -gsmooth, twopass, extra);
+ else
+ do_test(&rco, &trmse, &tmaxe, &tavge, verb, plot, di, res, ntps, noise, unif, smooth, twopass, extra);
+
+ printf("Results: maxerr %f, avgerr %f, rmserr %f\n",
+ tmaxe, tavge, trmse);
+ }
+
+ rco.mppo->del(rco.mppo);
+
+ free(ident);
+
+ return 0;
+}
+
+/* ----------------------------------------------- */
+
+/* Do one set of tests and return the results */
+static void do_test(
+ refconv *rco,
+ double *trmse, /* RETURN total RMS error */
+ double *tmaxe, /* RETURN total maximum error */
+ double *tavge, /* RETURN total average error */
+ int verb, /* Verbosity */
+ int plot, /* Plot graphs */
+ int di, /* Dimensions in */
+ int res, /* RSPL grid resolution */
+ int ntps, /* Number of sample points */
+ double noise, /* Sample point noise volume */
+ int unif, /* nz for uniform noise, else normal */
+ double smooth, /* Smoothness to test, +ve for extra, -ve for underlying */
+ int twopass, /* Two pass flag */
+ int extra /* Extra fit flag */
+) {
+ sobol *so; /* Sobol sequence generator */
+ co *tps = NULL;
+ rspl *rss; /* Multi-resolution regularized spline structure */
+ datai low,high;
+ int gres[MXDI];
+ double avgdev[MXDO];
+ int i, j, it;
+ int flags = RSPL_NOFLAGS;
+
+ *trmse = 0.0;
+ *tmaxe = 0.0;
+ *tavge = 0.0;
+
+ for (j = 0; j < di; j++) {
+ low[j] = 0.0;
+ high[j] = 1.0;
+ gres[j] = res;
+ }
+
+ if (twopass)
+ flags |= RSPL_2PASSSMTH;
+
+ if (extra)
+ flags |= RSPL_EXTRAFIT2;
+
+ /* Make repeatable by setting random seed before a test set. */
+ rand32(0x12345678);
+
+ if ((so = new_sobol(di)) == NULL)
+ error("Creating sobol sequence generator failed");
+
+ {
+ double rmse, avge, maxe;
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS,di, 3);
+
+ /* Create the list of sampling points */
+ tps = (co *)malloc(ntps * sizeof(co));
+
+ so->reset(so);
+
+ if (verb) printf("Generating the sample points\n");
+
+ /* Random sobol test set */
+ for (i = 0; i < ntps; i++) {
+ double out[3];
+ int f;
+
+ so->next(so, tps[i].p);
+ rco->lookup(rco, out, tps[i].p);
+ /* Add randomness to the PCS values */
+ /* 0.25 * converts total volume to average deviation */
+ for (f = 0; f < 3; f++) {
+ if (unif) {
+ tps[i].v[f] = out[f] + 100.0 * d_rand(-0.5 * noise, 0.5 * noise);
+ } else {
+ tps[i].v[f] = out[f] + 100.0 * noise * 0.25 * 1.2533 * norm_rand();
+ }
+ }
+//printf("~1 data %d: %f %f %f -> %f %f %f, inc noise %f %f %f\n", i, tps[i].p[0], tps[i].p[1], tps[i].p[2], out[0], out[1], out[2], tps[i].v[0], tps[i].v[1], tps[i].v[2]);
+ }
+
+ /* Average deviation of ouput % */
+ avgdev[0] = 0.25 * noise;
+ avgdev[1] = 0.25 * noise;
+ avgdev[2] = 0.25 * noise;
+
+ /* Fit to scattered data */
+ if (verb) printf("Fitting the scattered data\n");
+ rss->fit_rspl(rss,
+ flags, /* Non-mon and clip flags */
+ tps, /* Test points */
+ ntps, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ smooth, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+
+ /* Plot out function values */
+ if (plot) {
+ int slice;
+ printf("Black is target, Red is rspl\n");
+ for (slice = 0; slice < (di+1); slice++) {
+ co tp; /* Test point */
+ double x[PLOTRES];
+ double ya[PLOTRES];
+ double yb[PLOTRES];
+ double yc[PLOTRES];
+ double pp[MXDI], p1[MXDI], p2[MXDI], ss[MXDI];
+ int n = PLOTRES;
+
+ /* setup slices on each axis at 0.5 and diagonal */
+ if (slice < di) {
+ for (j = 0; j < di; j++)
+ p1[j] = p2[j] = 0.5;
+ p1[slice] = 0.0;
+ p2[slice] = 1.0;
+ printf("Slice along axis %d\n",slice);
+ } else {
+ for (j = 0; j < di; j++) {
+ p1[j] = 0.0;
+ p2[j] = 1.0;
+ }
+ printf("Slice along diagonal\n");
+ }
+
+ for (j = 0; j < di; j++) {
+ ss[j] = (p2[j] - p1[j])/n;
+ pp[j] = p1[j];
+ }
+
+ for (i = 0; i < n; i++) {
+ double out[3];
+ double vv = i/(n-1.0);
+ x[i] = vv;
+
+ /* Reference */
+ rco->lookup(rco, out, pp);
+ ya[i] = 0.01 * out[0];
+
+ /* RSPL aproximation */
+ for (j = 0; j < di; j++)
+ tp.p[j] = pp[j];
+
+ if (rss->interp(rss, &tp))
+ tp.v[0] = -0.1;
+ yb[i] = tp.v[0];
+
+ /* Crude way of setting the scale: */
+ yc[i] = 0.0;
+ if (i == (n-1))
+ yc[0] = 1.0;
+
+ for (j = 0; j < di; j++)
+ pp[j] += ss[j];
+ }
+
+ /* Plot the result */
+ do_plot(x,ya,yb,yc,n);
+ }
+ }
+
+ /* Compute statistics */
+ rmse = 0.0;
+ avge = 0.0;
+ maxe = 0.0;
+// so->reset(so);
+
+
+ /* Fit to scattered data */
+ if (verb) printf("Fitting the scattered data\n");
+ for (i = 0; i < 100000; i++) {
+// for (i = 0; i < 100; i++) {
+ double out[3];
+ co tp; /* Test point */
+ double err;
+
+ if (so->next(so, tp.p))
+ error("Ran out of pseudo radom points");
+
+ /* Reference */
+ rco->lookup(rco, out, tp.p);
+
+ /* RSPL aproximation */
+ rss->interp(rss, &tp);
+
+//printf("~1 point %f %f %f -> ref %f %f %f, test %f %f %f\n", tp.p[0], tp.p[1], tp.p[2], out[0], out[1], out[2], tp.v[0], tp.v[1], tp.v[2]);
+
+ err = icmLabDE(out, tp.v);
+ avge += err;
+ rmse += err * err;
+ if (err > maxe)
+ maxe = err;
+ }
+ avge /= (double)i;
+ rmse /= (double)i;
+
+ if (verb)
+ printf("Dim %d, res %d, noise %f, points %d, maxerr %f, rmserr %f, avgerr %f\n",
+ di, res, noise, ntps, maxe, sqrt(rmse), avge);
+
+ *trmse += rmse;
+ if (maxe > *tmaxe)
+ *tmaxe = maxe;
+ *tavge += avge;
+
+ rss->del(rss);
+ free(tps);
+ }
+ so->del(so);
+
+ *trmse = sqrt(*trmse);
+}
+
+/* Do smoothness scaling check & return results */
+static double do_stest(
+ refconv *rco,
+ int verb, /* Verbosity */
+ int di, /* Dimensions */
+ int its, /* Number of function tests */
+ int res /* RSPL grid resolution */
+) {
+ DCOUNT(gc, MXDIDO, di, 1, 1, res-1);
+ int it;
+ double atse = 0.0;
+
+ /* Make repeatable by setting random seed before a test set. */
+ rand32(0x12345678);
+
+ for (it = 0; it < its; it++) {
+ double tse;
+ int fdi = it % 3; /* Rotate amongsth L, a, b */
+
+ DC_INIT(gc)
+ tse = 0.0;
+ for (; !DC_DONE(gc);) {
+ double out[3];
+ double g[MXDI];
+ int e, k;
+ double y1, y2, y3;
+ double del;
+
+ for (e = 0; e < di; e++)
+ g[e] = gc[e]/(res-1.0);
+ rco->lookup(rco, out, g);
+ y2 = 0.01 * out[fdi];
+
+ del = 1.0/(res-1.0);
+
+ for (k = 0 ; k < di; k++) {
+ double err;
+
+ g[k] -= del;
+ rco->lookup(rco, out, g);
+ y1 = 0.01 * out[fdi];
+ g[k] += 2.0 * del;
+ rco->lookup(rco, out, g);
+ y3 = 0.01 * out[fdi];
+ g[k] -= del;
+
+ err = 0.5 * (y3 + y1) - y2;
+ tse += err * err;
+ }
+
+ DC_INC(gc);
+ }
+ /* Apply adjustments and corrections */
+ tse *= pow((res-1.0), 4.0); /* Aprox. geometric resolution factor */
+ tse /= pow((res-2.0),(double)di); /* Average squared non-smoothness */
+
+ if (verb)
+ printf("smf for it %d = %f\n",it,tse);
+ atse += tse;
+ }
+
+ return atse/(double)its;
+}
+
+
diff --git a/rspl/smtnd.c b/rspl/smtnd.c
new file mode 100644
index 0000000..cdef0a2
--- /dev/null
+++ b/rspl/smtnd.c
@@ -0,0 +1,1171 @@
+
+/*****************************************************/
+/* Smoothness factor tuning of RSPL in N Dimensions. */
+/*****************************************************/
+
+/* Author: Graeme Gill
+ * Date: 28/11/2005
+ * Derived from cmatch.c
+ * Copyright 1995 - 2005 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ *
+ * Test set for tuning smoothness factor for optimal interpolation
+ * with respect to dimension, number of sample points, and uncertainty
+ * of the sample points.
+ */
+
+
+#undef DEBUG
+#undef DETAILED
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <math.h>
+#include "rspl.h"
+#include "numlib.h"
+#include "xicc.h" /* For mpp support */
+#include "plot.h"
+#include "rspl_imp.h"
+#include "counters.h" /* Counter macros */
+
+/* rspl flags */
+#define MXCHPARAMS 8
+
+#define PLOTRES 256
+
+/* Function being modeled by rspl */
+/* Similar to MPP model */
+typedef struct {
+ int di; /* Number of dimensions */
+ double ip[MXDI][MXCHPARAMS]; /* Input channel parameters */
+ double shape[MXDI][1 << MXDI]; /* Channel interaction shape parameters */
+ double op[1 << MXDI]; /* Output channel combination parameters */
+} funcp;
+
+
+/* Setup a random function in the given dimensions */
+static void setup_func(funcp *p, int di) {
+ double mn,mx;
+ int i, j;
+
+ p->di = di;
+
+ /* Setup random input parameters */
+ /* (This is the one that effects smoothness of function the most) */
+ for (j = 0; j < di; j++) {
+ for (mx = 3.0, i = 0; i < MXCHPARAMS; i++, mx *= 0.6) {
+ p->ip[j][i] = d_rand(-mx, mx);
+ }
+ }
+
+ /* Setup random shape parameters */
+ for (j = 0; j < di; j++) {
+ for (i = 0; i < (1 << di); i++) { /* Initially random */
+ p->shape[j][i] = d_rand(-1.0, 1.0);
+ }
+ }
+
+ /* Setup the random output parameters */
+ mn = 2.0;
+ mx = -1.0;
+ for (i = 0; i < (1 << di); i++) { /* Initially random */
+ p->op[i] = d_rand(0.0, 1.0);
+ if (p->op[i] < mn)
+ mn = p->op[i];
+ if (p->op[i] > mx)
+ mx = p->op[i];
+ }
+ for (i = 0; i < (1 << di); i++) { /* Then scale to between 0.0 and 1.0 */
+ p->op[i] = (p->op[i] - mn)/(mx - mn);
+ }
+}
+
+/* Lookup the function value */
+static double lookup_func(funcp *p, double *v) {
+ int m, k;
+ int di = p->di;
+ double tcnv[MPP_MXINKS]; /* Transfer curve corrected device values */
+ double tcnv1[MPP_MXINKS]; /* 1.0 - Transfer curve corrected device values */
+ double ww[MPP_MXINKS]; /* Interpolated tweak params for each channel */
+ double ov; /* Output value */
+
+ /* Input curve lookup */
+ for (m = 0; m < di; m++) {
+ tcnv[m] = icxTransFunc(p->ip[m],MXCHPARAMS,v[m]);
+ tcnv1[m] = 1.0 - tcnv[m];
+ }
+
+ for (m = 0; m < di; m++)
+ ww[m] = 0.0;
+
+ /* Lookup the shape values */
+ for (k = 0; k < (1 << di); k++) { /* For each interp vertex */
+ double vv;
+ for (vv = 1.0, m = 0; m < di; m++) { /* Compute weighting */
+ if (k & (1 << m))
+ vv *= tcnv[m];
+ else
+ vv *= tcnv1[m];
+ }
+ for (m = 0; m < di; m++) {
+ ww[m] += p->shape[m][k & ~(1<<m)] * vv; /* Apply weighting to shape vertex value */
+ }
+ }
+
+ /* Apply the shape values to adjust the primaries */
+ for (m = 0; m < di; m++) {
+ double gg = ww[m]; /* Curve adjustment */
+ double vv = tcnv[m]; /* Input value to be tweaked */
+ if (gg >= 0.0) {
+ vv = vv/(gg - gg * vv + 1.0);
+ } else {
+ vv = (vv - gg * vv)/(1.0 - gg * vv);
+ }
+ tcnv[m] = vv;
+ tcnv1[m] = 1.0 - vv;
+ }
+
+ /* Compute the primary combination values */
+ for (ov = 0.0, k = 0; k < (1 << di); k++) {
+ double vv = p->op[k];
+ for (m = 0; m < di; m++) {
+ if (k & (1 << m))
+ vv *= tcnv[m];
+ else
+ vv *= tcnv1[m];
+ }
+ ov += vv;
+ }
+
+ return ov;
+}
+
+/* Do one set of tests and return the results */
+static void do_test(
+ double *trmse, /* RETURN total RMS error */
+ double *tmaxe, /* RETURN total maximum error */
+ double *tavge, /* RETURN total average error */
+ int verb, /* Verbosity */
+ int plot, /* Plot graphs */
+ int di, /* Dimensions */
+ int its, /* Number of function tests */
+ int res, /* RSPL grid resolution */
+ int ntps, /* Number of sample points */
+ double noise, /* Sample point noise volume */
+ int unif, /* NZ if uniform rather than standard deistribution noise */
+ double smooth, /* Smoothness to test */
+ int twopass, /* Two pass flag */
+ int extra /* Extra fit flag */
+);
+
+/* Compute smoothness of function */
+static double do_stest(
+ int verb, /* Verbosity */
+ int di, /* Dimensions */
+ int its, /* Number of function tests */
+ int res /* RSPL grid resolution */
+);
+
+/* ---------------------------------------------------------------------- */
+/* Locate minimum of smoothness series result */
+
+#define MXMSS 50 /* Maximum smoothness series */
+
+/* Return the optimal smoothness value, based on the */
+/* minimum RMS value. */
+static double best(int n, double *rmse, double *smv) {
+ int i;
+ rspl *curve;
+ co *tps = NULL;
+ int ns = 500; /* Number of samples */
+ datai low,high;
+ int gres[1];
+ datai dlow,dhigh;
+ double avgdev[1];
+ double brmse; /* best solution value */
+ double blsmv = 0.0; /* best solution location */
+ double rv; /* Return value */
+
+ /* Create interpolated curve */
+ if ((curve = new_rspl(RSPL_NOFLAGS,1, 1)) == NULL)
+ error ("New rspl failed");
+
+ /* Create the list of sampling points */
+ if ((tps = (co *)malloc(n * sizeof(co))) == NULL)
+ error ("malloc failed");
+
+ for (i = 0; i < n; i++) {
+ tps[i].p[0] = log10(smv[i]);
+ tps[i].v[0] = rmse[i];
+ }
+
+ gres[0] = 100;
+ low[0] = log10(smv[0]);
+ high[0] = log10(smv[n-1]);
+ dlow[0] = 0.0;
+ dhigh[0] = 1.0;
+ avgdev[0] = 0.0;
+
+ curve->fit_rspl(curve,
+ 0, /* Non-mon and clip flags */
+ tps, /* Test points */
+ n, /* Number of test points */
+ NULL, NULL, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ -0.00001, /* Underlying smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+
+#ifdef NEVER
+ /* Check the fit */
+ for (i = 0; i < n; i++) {
+ co tp;
+
+ tp.p[0] = log10(smv[i]);
+ curve->interp(curve, &tp);
+
+ printf("Point %d at %f, should be %f is %f\n",i,log10(smv[i]),rmse[i],tp.v[0]);
+ }
+
+#define TPRES 100
+ /* Plot the result */
+ {
+ double xx[TPRES], yy[TPRES];
+
+ for (i = 0; i < TPRES; i++) {
+ co tp;
+ double vi = i/(TPRES-1.0);
+
+ tp.p[0] = log10(smv[0]) + (log10(smv[n-1]) - log10(smv[0])) * vi;
+ curve->interp(curve, &tp);
+ xx[i] = tp.p[0];
+ yy[i] = tp.v[0];
+ }
+ do_plot(xx,yy,NULL,NULL,TPRES);
+ }
+#endif
+
+ /* Choose a solution */
+ brmse = 1e38;
+ for (i = 0; i < ns ; i++) {
+ co tp;
+ double vi;
+
+ vi = i/(ns-1.0);
+ tp.p[0] = log10(smv[0]) + (log10(smv[n-1]) - log10(smv[0])) * vi;
+ curve->interp(curve, &tp);
+
+ if (tp.v[0] < brmse) {
+ blsmv = tp.p[0];
+ brmse = tp.v[0];
+ }
+ }
+
+ rv = pow(10.0, blsmv);
+ return rv;
+}
+
+/* ---------------------------------------------------------------------- */
+/* Test series */
+
+/* Explore ideal smoothness change with test point number and noise volume */
+/* If tdi != 0, just do the given dimension */
+/* If tntps != 0, just do the given number of points */
+/* If tnlev != 0, just do the given noise level */
+static void do_series_1(int unif, int tdi, int tntps, int tnlev, int twopass, int extra) {
+ int verb = 0;
+ int plot = 0;
+ int sdi = 1, edi = 4, di;
+ int its;
+ int res = 0;
+ int ntps = 0;
+ double noise = 0.0;
+ double smooth = 0.0;
+ double trmse, tavge, tmaxe;
+ int m, i, j, k;
+
+ /* Number of trials to do for each dimension */
+ int trials[4] = {
+ 8,
+ 6,
+ 4,
+ 3
+ };
+
+ /* Resolution of grid for each dimension */
+ int reses[4][4] = {
+ { 257, 129, 65, 33 },
+ { 128, 65, 33, 17 },
+ { 65, 33, 17, 9 },
+ { 33, 17, 9, 5 }
+ };
+
+ /* Set of sample points to explore */
+ int nset[4][20] = {
+ {
+ 5, 10, 20, 50, 100, 200, /* di = 1 */
+ },
+ {
+ 25, 100, 400, 2500, 10000, 40000, /* di = 2 */
+ },
+ {
+ 25, 50, 75, 125, 250, 500, 1000, 2000, 8000, 125000, /* di = 3 */
+ },
+ {
+ 50, 100, 200, 450, 625, 900, 1800, 3600, 10000, 160000, 1000000, /* di = 4 */
+ }
+ };
+
+ /* Set of smoothnesses to explore */
+ double smset[4][20] = {
+ {
+ -0.0000001,
+ -0.0000010,
+ -0.0000100,
+ -0.0001000,
+ -0.0010000,
+ -0.0100000,
+ -0.1000000,
+ -1.0000000,
+ 0.0
+ },
+ {
+ -0.0000001,
+ -0.0000010,
+ -0.0000100,
+ -0.0001000,
+ -0.0010000,
+ -0.0100000,
+ -0.1000000,
+ -1.0000000,
+ 0.0
+ },
+ {
+ -0.0000010,
+ -0.0000100,
+ -0.0001000,
+ -0.0010000,
+ -0.0100000,
+ -0.1000000,
+ -1.0000000,
+ 0.0
+ },
+ {
+ -0.0000100,
+ -0.0001000,
+ -0.0010000,
+ -0.0100000,
+ -0.1000000,
+ -1.0000000,
+ -10.000000,
+ 0.0
+ }
+ };
+
+ /* Set of smoothnesses for twopass smoothing */
+ double smset2[4][20] = {
+ {
+ -0.0000001,
+ -0.0000010,
+ -0.0000100,
+ -0.0001000,
+ -0.0010000,
+ -0.0100000,
+ -0.1000000,
+ -1.0000000,
+ 0.0
+ },
+ {
+ -0.0000001,
+ -0.0000010,
+ -0.0000100,
+ -0.0001000,
+ -0.0010000,
+ -0.0100000,
+ -0.1000000,
+ -1.0000000,
+ 0.0
+ },
+ {
+ -0.0000010,
+ -0.0000100,
+ -0.0001000,
+ -0.0010000,
+ -0.0100000,
+ -0.1000000,
+ -1.0000000,
+ 0.0
+ },
+ {
+ -0.0000100,
+ -0.0001000,
+ -0.0010000,
+ -0.0100000,
+ -0.1000000,
+ -1.0000000,
+ -10.000000,
+ 0.0
+ }
+ };
+
+ /* Set of noise levels to explore (average deviation * 4) */
+ double noiseset[4][20] = {
+ {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ -1.0,
+ },
+ {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ -1.0,
+ },
+ {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ -1.0,
+ },
+ {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.03, /* 3.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ -1.0,
+ },
+ };
+
+
+ printf("Testing effect of underlying smoothness factors\n");
+ if (twopass)
+ printf("Two Pass smoothing\n");
+ if (extra)
+ printf("Extra fitting\n");
+
+ /* For dimensions */
+ if (tdi != 0)
+ sdi = edi = tdi;
+ for (di = sdi; di <= edi; di++) { // dimensions
+
+ its = trials[di-1];
+
+ for (m = 1; m < 2; m++) { // Just 2nd-highest resolution
+ res = reses[di-1][m];
+
+ printf("Tests %d\n",its);
+ printf("Dimensions %d\n",di);
+ printf("RSPL resolution %d\n",res);
+
+ /* For noise levels */
+ for (j = tnlev; j < 20; j++) { // All noise levels
+ double smv[20];
+ double rmse[20];
+ double bfit;
+
+ if (tnlev != 0 && j != tnlev)
+ break;
+
+ noise = noiseset[di-1][j];
+ if (noise < 0.0)
+ break;
+ printf("\nNoise volume %f%%, average deviation %f%%\n",noise * 100.0, noise * 25.0);
+
+ /* For number of sample points */
+ for (i = 0; i < 20; i++) { // All test points
+ int rpts;
+ ntps = nset[di-1][i];
+
+ if (ntps == 0)
+ break;
+
+ if (tntps != 0 && ntps != tntps) /* Skip any not requested */
+ continue;
+
+ /* Make sure at least 100 points are tested */
+ rpts = 1 + 100/ntps;
+ if (rpts > 5)
+ rpts = 5;
+
+ printf("\nNo. Sample points %d, norm %8.2f, total its %d\n",ntps, pow((double)ntps, 1.0/di),its * rpts);
+
+ /* For smooth factors */
+ for (k = 0; k < 20; k++) { // All smoothing levels
+ if (twopass)
+ smooth = smset2[di-1][k];
+ else
+ smooth = smset[di-1][k];
+ if (smooth == 0.0)
+ break;
+
+ printf("Underlying smooth %9.7f, ",-smooth); fflush(stdout);
+
+ do_test(&trmse, &tmaxe, &tavge, verb, plot, di, its * rpts, res, ntps, noise, unif,smooth, twopass, extra);
+ smv[k] = -smooth;
+ rmse[k] = trmse;
+ printf("maxerr %f%%, avgerr %f%%, rmserr %f%%\n",
+ tmaxe * 100.0, tavge * 100.0, trmse * 100.0);
+ }
+
+ bfit = best(k, rmse, smv);
+ printf("Best smoothness = %9.7f, log10 = %4.1f\n",bfit,log10(bfit));
+ }
+ }
+ }
+ printf("\n");
+ }
+}
+
+/* Explore performance of "optimised" smoothness over test point number and noise volume */
+static void do_series_2(int unif, int twopass, int extra) {
+ int verb = 0;
+ int plot = 0;
+ int di = 0;
+ int its;
+ int res = 0;
+ int ntps = 0;
+ double noise = 0.0;
+ double smooth = 0.0;
+ double trmse, tavge, tmaxe;
+ int i, j, k;
+
+ /* Number of trials to do for each dimension */
+ int trials[4] = {
+ 16,
+ 12,
+ 8,
+ 5
+ };
+
+
+ /* Resolution of grid for each dimension */
+ int reses[4] = {
+ 129,
+ 65,
+ 33,
+ 17
+ };
+
+#ifdef NEVER
+ /* Set of sample points to explore */
+ int nset[4][20] = {
+ {
+ 5, 10, 20, 50, 0
+ },
+ {
+ 25, 100, 400, 2500, 0
+ },
+ {
+ 125, 1000, 8000, 125000, 0
+ },
+ {
+ 625, 10000, 160000, 1000000, 0
+ }
+ };
+#else
+ /* Set of sample points to explore */
+ int nset[4][20] = {
+ {
+ 5, 10, 20, 50, 0
+ },
+ {
+ 25, 100, 400, 2500, 0
+ },
+ {
+ 250, 500, 1000, 2000, 0
+ },
+ {
+ 450, 900, 1800, 3600, 0
+ }
+ };
+#endif /* NEVER */
+
+
+ /* Set of smoothnesses to explore */
+ double smset[5] = {
+ 00.01,
+ 00.10,
+ 01.00,
+ 10.00,
+ 100.0
+ };
+
+ /* Set of noise levels to explore (average deviation * 4) */
+ double noiseset[6] = {
+ 0.0, /* Perfect data */
+ 0.01, /* 1.0 % */
+ 0.02, /* 2.0 % */
+ 0.05, /* 5.0 % */
+ 0.10, /* 10.0 % */
+ 0.20, /* 20.0 % */
+ };
+
+
+ printf("Verifying optimised smoothness factors\n");
+
+ /* For dimensions */
+ for (di = 1; di <= 4; di++) {
+
+ its = trials[di-1];
+ res = reses[di-1];
+
+ printf("Tests %d\n",its);
+ printf("Dimensions %d\n",di);
+ printf("RSPL resolution %d\n",res);
+
+ /* For number of sample points */
+ for (i = 0; i < 20; i++) {
+ ntps = nset[di-1][i];
+
+ if (ntps == 0)
+ break;
+
+ printf("\nNo. Sample points %d, norm %8.2f\n",ntps, pow((double)ntps, 1.0/di));
+
+ /* For noise levels */
+ for (j = 0; j < 6; j++) {
+ double rmse[20];
+ double bfit;
+
+ noise = noiseset[j];
+ printf("Noise volume %f%%, average deviation %f%%\n",noise * 100.0, noise * 25.0);
+
+ /* For smooth factors */
+ for (k = 0; k < 5; k++) {
+ smooth = smset[k];
+
+ printf("Extra smooth %f, ",smooth); fflush(stdout);
+
+ do_test(&trmse, &tmaxe, &tavge, verb, plot, di, its, res, ntps, noise, unif, smooth, twopass, extra);
+
+ rmse[k] = trmse;
+ printf("maxerr %f%%, avgerr %f%%, rmserr %f%%\n",
+ tmaxe * 100.0, tavge * 100.0, trmse * 100.0);
+ }
+ bfit = best(5, rmse, smset);
+ printf("Best smoothness = %9.7f, log10 = %4.1f\n",bfit,log10(bfit));
+ }
+ }
+ printf("\n");
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+void usage(void) {
+ fprintf(stderr,"Test smoothness factor tuning of RSPL in N Dimensions\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: smtnd [options]\n");
+ fprintf(stderr," -v Verbose\n");
+ fprintf(stderr," -p Plot graphs\n");
+ fprintf(stderr," -z n Do test series ""n"" else single test\n");
+ fprintf(stderr," 1 = Underlying smoothness\n");
+ fprintf(stderr," 2 = Verify optimised smoothness\n");
+ fprintf(stderr," -S Compute smoothness factor instead\n");
+ fprintf(stderr," -u Use uniformly distributed noise\n");
+ fprintf(stderr," -d n Test ""d"" dimension, 1-4 (default 1)\n");
+ fprintf(stderr," -t n Test ""n"" random functions (default 1)\n");
+ fprintf(stderr," -r res Rspl resolution (defaults 129, 65, 33, 17)\n");
+ fprintf(stderr," -n no Test ""no"" sample points (default 20, 40, 80, 100)\n");
+ fprintf(stderr," -a amnt Add total randomness to function value (default 0.0)\n");
+ fprintf(stderr," -A n Just do the n'th noise level of series\n");
+ fprintf(stderr," -2 Use two pass smoothing\n");
+ fprintf(stderr," -x Use extra fitting\n");
+ fprintf(stderr," -s smooth RSPL extra smoothness factor to test (default 1.0)\n");
+ fprintf(stderr," -g smooth RSPL underlying smoothness factor to test\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ int verb = 0;
+ int plot = 0;
+ int series = 0;
+ int unif = 0;
+ int di = 0;
+ int its = 1;
+ int res = -1;
+ int ntps = 0;
+ double noise = 0.0;
+ int nlev = 0;
+ double smooth = 1.0;
+ double gsmooth = 0.0;
+ int twopass = 0;
+ int extra = 0;
+ int smfunc = 0;
+ double trmse, tavge, tmaxe;
+
+ error_program = "smtnd";
+
+#ifdef NEVER
+ {
+ double rmse[10], smv[10], rv;
+
+ smv[0] = 0.0000100, rmse[0] = 2.566116;
+ smv[1] = 0.0001000, rmse[1] = 2.528666;
+ smv[2] = 0.0010000, rmse[2] = 2.489116;
+ smv[3] = 0.0100000, rmse[3] = 3.409045;
+ smv[4] = 0.1000000, rmse[4] = 5.727079;
+ smv[5] = 1.0000000, rmse[5] = 6.653747;
+
+ rv = best(6,rmse, smv);
+ printf("~1 best = %f\n",rv);
+ exit(0);
+ }
+#endif
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?') {
+ usage();
+
+ } else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ verb = 1;
+
+ } else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
+ plot = 1;
+
+ } else if (argv[fa][1] == 'u' || argv[fa][1] == 'U') {
+ unif = 1;
+
+ /* Test series */
+ } else if (argv[fa][1] == 'z' || argv[fa][1] == 'Z') {
+ fa = nfa;
+ if (na == NULL) usage();
+ series = atoi(na);
+ if (series <= 0) usage();
+
+ /* Compute smoothness factor */
+ } else if (argv[fa][1] == 'S') {
+ smfunc = 1;
+
+ /* Dimension */
+ } else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') {
+ fa = nfa;
+ if (na == NULL) usage();
+ di = atoi(na);
+ if (di <= 0 || di > 4) usage();
+printf("~1 Got -d %s = %d\n",na,di);
+
+ /* Number of tests */
+ } else if (argv[fa][1] == 't' || argv[fa][1] == 'T') {
+ fa = nfa;
+ if (na == NULL) usage();
+ its = atoi(na);
+ if (its <= 0) usage();
+
+ /* Resolution */
+ } else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ fa = nfa;
+ if (na == NULL) usage();
+ res = atoi(na);
+ if (res <= 0) usage();
+
+ /* Number of sample points */
+ } else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') {
+ fa = nfa;
+ if (na == NULL) usage();
+ ntps = atoi(na);
+ if (ntps <= 0) usage();
+
+ /* Randomness */
+ } else if (argv[fa][1] == 'a') {
+ fa = nfa;
+ if (na == NULL) usage();
+ noise = atof(na);
+ if (noise < 0.0) usage();
+
+ /* Series Noise Level */
+ } else if (argv[fa][1] == 'A') {
+ fa = nfa;
+ if (na == NULL) usage();
+ nlev = atoi(na);
+ if (noise < 0) usage();
+
+ } else if (argv[fa][1] == '2') {
+ twopass = 1;
+
+ } else if (argv[fa][1] == 'x' || argv[fa][1] == 'X') {
+ extra = 1;
+
+ /* Extra smooth factor */
+ } else if (argv[fa][1] == 's') {
+ fa = nfa;
+ if (na == NULL) usage();
+ smooth = atof(na);
+ if (smooth < 0.0) usage();
+
+ /* Underlying smoothnes factor */
+ } else if (argv[fa][1] == 'g') {
+ fa = nfa;
+ if (na == NULL) usage();
+ smooth = atof(na);
+ if (gsmooth < 0.0) usage();
+
+ } else
+ usage();
+ } else
+ break;
+ }
+
+ if (series > 0) {
+ if (series == 1)
+ do_series_1(unif, di, ntps, nlev, twopass, extra);
+ else if (series == 2)
+ do_series_2(unif, twopass, extra);
+ else
+ error("Unknown series %d\n",series);
+ return 0;
+ }
+
+ if (di == 0)
+ di = 1;
+
+ if (res < 0) {
+ if (di == 1)
+ res = 129;
+ else if (di == 2)
+ res = 65;
+ else if (di == 3)
+ res = 33;
+ else
+ res = 17;
+ }
+
+ if (ntps <= 0) {
+ if (di == 1)
+ ntps = 20;
+ else if (di == 2)
+ ntps = 40;
+ else if (di == 3)
+ ntps = 60;
+ else
+ ntps = 80;
+ }
+
+ if (smfunc) {
+ double sm;
+
+ if (verb) {
+ printf("Dimensions %d\n",di);
+ printf("Tests %d\n",its);
+ printf("Grid resolution %d\n",res);
+ }
+
+ sm = do_stest(verb, di, its, res);
+
+ printf("Results: smoothness factor = %f\n",sm);
+
+ } else {
+
+ if (verb) {
+ printf("Dimensions %d\n",di);
+ printf("Tests %d\n",its);
+ printf("RSPL resolution %d\n",res);
+ printf("No. Sample points %d (norm %f)\n",ntps, pow((double)ntps, 1.0/di));
+ printf("Noise volume %f\n",noise);
+ if (gsmooth > 0.0)
+ printf("Underlying smooth %f\n",gsmooth);
+ else
+ printf("Extra smooth %f\n",smooth);
+ }
+
+ if (gsmooth > 0.0)
+ do_test(&trmse, &tmaxe, &tavge, verb, plot, di, its, res, ntps, noise, unif, -gsmooth, twopass, extra);
+ else
+ do_test(&trmse, &tmaxe, &tavge, verb, plot, di, its, res, ntps, noise, unif, smooth, twopass, extra);
+
+ printf("Results: maxerr %f%%, avgerr %f%%, rmserr %f%%\n",
+ tmaxe * 100.0, tavge * 100.0, trmse * 100.0);
+ }
+
+ return 0;
+}
+
+/* Do one set of tests and return the results */
+static void do_test(
+ double *trmse, /* RETURN total RMS error */
+ double *tmaxe, /* RETURN total maximum error */
+ double *tavge, /* RETURN total average error */
+ int verb, /* Verbosity */
+ int plot, /* Plot graphs */
+ int di, /* Dimensions */
+ int its, /* Number of function tests */
+ int res, /* RSPL grid resolution */
+ int ntps, /* Number of sample points */
+ double noise, /* Sample point noise volume (total = 4 x average deviation) */
+ int unif, /* NZ if uniform rather than standard deistribution noise */
+ double smooth, /* Smoothness to test, +ve for extra, -ve for underlying */
+ int twopass, /* Two pass flag */
+ int extra /* Extra fit flag */
+) {
+ funcp fp; /* Function parameters */
+ sobol *so; /* Sobol sequence generator */
+ co *tps = NULL;
+ rspl *rss; /* Multi-resolution regularized spline structure */
+ datai low,high;
+ double avgdev[MXDO];
+ int gres[MXDI];
+ int i, j, it;
+ int flags = RSPL_NOFLAGS;
+
+ *trmse = 0.0;
+ *tmaxe = 0.0;
+ *tavge = 0.0;
+
+ for (j = 0; j < di; j++) {
+ low[j] = 0.0;
+ high[j] = 1.0;
+ gres[j] = res;
+ }
+
+ if (twopass)
+ flags |= RSPL_2PASSSMTH;
+
+ if (extra)
+ flags |= RSPL_EXTRAFIT2;
+
+ if ((so = new_sobol(di)) == NULL)
+ error("Creating sobol sequence generator failed");
+
+ for (it = 0; it < its; it++) {
+ double rmse, avge, maxe;
+
+ /* Make repeatable by setting random seed before a test set. */
+ rand32(0x12345678 + 0x1000 * it);
+
+ /* New function */
+ setup_func(&fp, di);
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS,di, 1);
+
+ /* Create the list of sampling points */
+ if ((tps = (co *)malloc(ntps * sizeof(co))) == NULL)
+ error ("malloc failed");
+
+ so->reset(so);
+
+ if (verb) printf("Generating the sample points\n");
+
+ for (i = 0; i < ntps; i++) {
+ so->next(so, tps[i].p);
+ tps[i].v[0] = lookup_func(&fp, tps[i].p);
+ if (unif)
+ tps[i].v[0] += d_rand(-0.5 * noise, 0.5 * noise);
+ else
+ tps[i].v[0] += noise * 0.25 * 1.2533 * norm_rand();
+ }
+
+ /* Fit to scattered data */
+ if (verb) printf("Fitting the scattered data\n");
+ avgdev[0] = 0.25 * noise;
+ rss->fit_rspl(rss,
+ flags, /* Non-mon and clip flags */
+ tps, /* Test points */
+ ntps, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ low, high, /* Default data scale */
+ smooth, /* Smoothing to test */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+
+ /* Plot out function values */
+ if (plot) {
+ int slice;
+ printf("Black is target, Red is rspl\n");
+ for (slice = 0; slice < (di+1); slice++) {
+ co tp; /* Test point */
+ double x[PLOTRES];
+ double ya[PLOTRES];
+ double yb[PLOTRES];
+ double yc[PLOTRES];
+ double pp[MXDI], p1[MXDI], p2[MXDI], ss[MXDI];
+ int n = PLOTRES;
+
+ /* setup slices on each axis at 0.5 and diagonal */
+ if (slice < di) {
+ for (j = 0; j < di; j++)
+ p1[j] = p2[j] = 0.5;
+ p1[slice] = 0.0;
+ p2[slice] = 1.0;
+ printf("Slice along axis %d\n",slice);
+ } else {
+ for (j = 0; j < di; j++) {
+ p1[j] = 0.0;
+ p2[j] = 1.0;
+ }
+ printf("Slice along diagonal\n");
+ }
+
+ for (j = 0; j < di; j++) {
+ ss[j] = (p2[j] - p1[j])/n;
+ pp[j] = p1[j];
+ }
+
+ for (i = 0; i < n; i++) {
+ double vv = i/(n-1.0);
+ x[i] = vv;
+
+ /* Reference */
+ ya[i] = lookup_func(&fp, pp);
+
+ /* RSPL aproximation */
+ for (j = 0; j < di; j++)
+ tp.p[j] = pp[j];
+
+ if (rss->interp(rss, &tp))
+ tp.v[0] = -0.1;
+ yb[i] = tp.v[0];
+
+ /* Crude way of setting the scale: */
+ yc[i] = 0.0;
+ if (i == (n-1))
+ yc[0] = 1.0;
+
+ for (j = 0; j < di; j++)
+ pp[j] += ss[j];
+ }
+
+ /* Plot the result */
+ do_plot(x,ya,yb,yc,n);
+ }
+ }
+
+ /* Compute statistics */
+ rmse = 0.0;
+ avge = 0.0;
+ maxe = 0.0;
+// so->reset(so);
+
+ /* Fit to scattered data */
+ if (verb) printf("Fitting the scattered data\n");
+ for (i = 0; i <100000; i++) {
+ co tp; /* Test point */
+ double aa, bb, err;
+
+ so->next(so, tp.p);
+
+ /* Reference */
+ aa = lookup_func(&fp, tp.p);
+
+ /* RSPL aproximation */
+ rss->interp(rss, &tp);
+ bb = tp.v[0];
+
+ err = fabs(aa - bb);
+ avge += err;
+ rmse += err * err;
+ if (err > maxe)
+ maxe = err;
+ }
+ avge /= (double)i;
+ rmse /= (double)i;
+
+ if (verb)
+ printf("Dim %d, res %d, noise %f, points %d, maxerr %f%%, rmserr %f%%, avgerr %f%%\n",
+ di, res, noise, ntps, maxe * 100.0, sqrt(rmse) * 100.0, avge * 100.0);
+
+ *trmse += rmse;
+ *tmaxe += maxe;
+ *tavge += avge;
+
+ rss->del(rss);
+ free(tps);
+ }
+ so->del(so);
+
+ *trmse = sqrt(*trmse/(double)its);
+ *tmaxe /= (double)its;
+ *tavge /= (double)its;
+}
+
+/* Do smoothness scaling check & return results */
+static double do_stest(
+ int verb, /* Verbosity */
+ int di, /* Dimensions */
+ int its, /* Number of function tests */
+ int res /* RSPL grid resolution */
+) {
+ funcp fp; /* Function parameters */
+ DCOUNT(gc, MXDIDO, di, 1, 1, res-1);
+ int it;
+ double atse = 0.0;
+
+ /* Make repeatable by setting random seed before a test set. */
+ rand32(0x12345678);
+
+ for (it = 0; it < its; it++) {
+ double tse;
+ setup_func(&fp, di); /* New function */
+
+ DC_INIT(gc)
+ tse = 0.0;
+ for (; !DC_DONE(gc);) {
+ double g[MXDI];
+ int e, k;
+ double y1, y2, y3;
+ double del;
+
+ for (e = 0; e < di; e++)
+ g[e] = gc[e]/(res-1.0);
+ y2 = lookup_func(&fp, g);
+
+ del = 1.0/(res-1.0);
+
+ for (k = 0 ; k < di; k++) {
+ double err;
+
+ g[k] -= del;
+ y1 = lookup_func(&fp, g);
+ g[k] += 2.0 * del;
+ y3 = lookup_func(&fp, g);
+ g[k] -= del;
+
+ err = 0.5 * (y3 + y1) - y2;
+ tse += err * err;
+ }
+
+ DC_INC(gc);
+ }
+ /* Apply adjustments and corrections */
+ tse *= pow((res-1.0), 4.0); /* Aprox. geometric resolution factor */
+ tse /= pow((res-2.0),(double)di); /* Average squared non-smoothness */
+
+ if (verb)
+ printf("smf for it %d = %f\n",it,tse);
+ atse += tse;
+ }
+
+ return atse/(double)its;
+}
+
+
diff --git a/rspl/spline.c b/rspl/spline.c
new file mode 100644
index 0000000..dabeb0f
--- /dev/null
+++ b/rspl/spline.c
@@ -0,0 +1,352 @@
+
+/*
+ * Argyll Color Correction System
+ * Multi-dimensional regularized spline data structure
+ *
+ * Spline forward interpolation support.
+ *
+ * Author: Graeme W. Gill
+ * Date: 12/10/98
+ *
+ * Copyright 1998, Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* TTBD:
+ Get rid of error() calls - return status instead
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+#include <time.h>
+//#if defined(__IBMC__) && defined(_M_IX86)
+//#include <float.h>
+//#endif
+
+#include "rspl_imp.h"
+#include "numlib.h"
+
+int spline_interp_rspl(rspl *ss, co *cp);
+
+#undef DEBUG
+
+#undef NEVER
+#define ALWAYS
+
+/* Convention is to use:
+ i to index grid points u.a
+ n to index data points d.a
+ e to index position dimension di
+ f to index output function dimension fdi
+ j misc and cube corners
+ k misc
+ */
+
+/* ====================================================== */
+
+/* Init spline elements in rspl */
+void
+init_spline(rspl *s) {
+ s->spline.nm = 0;
+ s->spline.spline = 0;
+ s->spline.magic = NULL;
+
+ s->spline_interp = spline_interp_rspl;
+}
+
+/* Free up the spline interpolation info */
+void free_spline(
+rspl *s /* Pointer to rspl grid */
+) {
+ if (s->spline.magic != NULL) {
+ free(s->spline.magic);
+ }
+ s->spline.nm = 0;
+ s->spline.spline = 0;
+}
+
+/* ====================================================== */
+/* Setup functions first: */
+
+/* Hermite spline, magic matrix */
+/* Indexes are: param powers 0, 1, 2, 3; Offset from base vertex 0,1; Dimension mask 0,1 */
+static double hmagic[4][2][2] = {
+ { { 1.0, 0.0}, { 0.0, 0.0} },
+ { { 0.0, 1.0}, { 0.0, 0.0} },
+ { {-3.0, -2.0}, { 3.0, -1.0} },
+ { { 2.0, 1.0}, {-2.0, 1.0} }
+};
+
+/* Allocate and initialize tangency information for each grid point */
+static void make_tang(
+rspl *s /* Pointer to rspl grid */
+) {
+ int i,p,j;
+ int di = s->di;
+ int fdi = s->fdi;
+ int nig = s->g.no;
+ float *tp; /* Pointer to tangent values */
+ int nim, mix; /* Number in magic, magic index */
+ float *tang_alloc, *tang; /* Tangency info */
+ float *gt; /* Working grid point */
+
+//printf("~~make_tang called\n");
+ /* Organized as: tang[[grid]][di combs.][fdi] */
+ /* Allocate space for tangency info */
+ if ((tang_alloc = (float *) malloc(sizeof(float) * nig * (((1 << di) * fdi)+G_XTRA))) == NULL)
+ error("rspl malloc failed - tangecy points");
+ tang = tang_alloc + G_XTRA; /* Offset for flags and non-mono error */
+
+ /* For all grid points */
+ for (tp = tang, gt = s->g.a, i = 0; i < nig; i++, gt += s->g.pss, tp += G_XTRA) {
+ int ee;
+/* printf("\n~~ grid point %d\n",i); */
+
+ /* Look at surrounding grid points in combinations of +- 1 all dimensions */
+ for (ee = 0; ee < (1 << di); ee++) {
+ double av[MXRO]; /* average */
+ int nia = 0; /* Number in average */
+ int f, ec;
+
+/* printf("Dim combo %d\n",ee); */
+ /* special case - base value */
+ if (ee == 0) {
+ *((int *)(tp-2)) = *((int *)(gt-2)); /* Copy flags */
+ tp[-1] = gt[-1]; /* Copy ink limit function value */
+ for (f = 0; f < fdi; f++) {
+ *tp++ = gt[f];
+/* printf("Tang value out %d = %f\n",f,tp[-1]); */
+ }
+ continue;
+ }
+ for (f = 0; f < fdi; f++)
+ av[f] = 0.0; /* Init average */
+ /* For all surroundin grid points in this combination */
+ for (ec = 0; ec < (1 << di); ec++) {
+ int xo, io, sgn, e, ex;
+/* printf("~~checking out surrounding combo %d\n",ec); */
+ if (ec & ~ee) {
+/* printf("~~being skipped\n"); */
+ continue; /* Skip invalid combo */
+ }
+ xo = io = 0; /* Grid float offset */
+ sgn = 1; /* Sign */
+ ex = 0; /* Flag - No extrapolation */
+ for (e = 0; e < di; e++) { /* For each dimension */
+/* printf("~~checking dimension %d\n",e); */
+ if (!(ee & (1 << e))) {
+/* printf("~~dimension not active\n"); */
+ continue; /* Dimension is not active */
+ }
+ if (ec & (1 << e)) {
+ /* If + dimension is valid */
+ if (((G_FL(gt,e) & 3) > 0) || (G_FL(gt,e) & 0x4)) {
+ int to = s->g.fci[e]; /* +1 in dimension */
+ io += to; /* real/pivot point */
+ xo += to; /* reflected point */
+ } else {
+ ex = 1; /* Use extrapolation */
+ xo -= s->g.fci[e]; /* -1 in dimension */
+ }
+ } else {
+ sgn = -sgn; /* Reverse sign */
+ /* If - dimension is valid */
+ if (((G_FL(gt,e) & 3) > 0) || !(G_FL(gt,e) & 0x4)) {
+ int to = -s->g.fci[e]; /* -1 in dimension */
+ io += to; /* real/pivot point */
+ xo += to; /* reflected point */
+ } else {
+ ex = 1; /* Use extrapolation */
+ xo += s->g.fci[e]; /* +1 in dimension */
+ }
+ }
+ }
+ /* Add surrounding grid points value into the average */
+ if (!ex) {
+ for (f = 0; f < fdi; f++)
+ av[f] += (double)sgn * gt[io + f];
+ } else { /* Extrapolate point beyond edge */
+ /* Use an extrapolation that tries to maintain curvature */
+ for (f = 0; f < fdi; f++) {
+ double v0,v1,v2;
+ v0 = gt[io + f]; /* Pivot point */
+ v1 = gt[xo + f]; /* Reflection of target in pivot */
+ v2 = gt[2 * xo - io + f]; /* Reflection +2 */
+ av[f] += (double)sgn * (3.0 * (v0 - v1) + v2);
+ }
+ }
+ nia++;
+ }
+ for (f = 0; f < fdi; f++) {
+ *tp++ = (float)(av[f]/(double)nia);
+/* printf("Tang value out %d = %f, average of %d\n",f,tp[-1],nia); */
+ }
+ } /* Next dimension combination */
+ } /* Next grid point */
+
+ /* Create a full sized hermite magic matrix */
+ /* Organized as: magic[4^di][2^di][2^di] */
+ /* = [param power combos][cube vertex index][di combos], */
+ /* but then only store non-zero weight values. */
+ for (i = 0, nim = 1; i < di; nim *= 10, i++); /* Number of entries needed */
+ if (s->spline.magic == NULL) { /* Allocate space for magic matrix info */
+ if ((s->spline.magic = (magic_data *) malloc(sizeof(magic_data) * nim)) == NULL)
+ error("rspl malloc failed - hermite magic matrix data");
+ }
+
+ mix = 0;
+ for (p = 0; p < (1 << (2 * di)); p++) { /* For all combinations of parameter powers */
+ for (i = 0; i < (1 << di); i++) { /* For all corners of cube */
+ for (j = 0; j < (1 << di); j++) { /* For all dimension combinations */
+ int ii;
+ double wgt = 1.0;
+ for (ii = 0; ii < di; ii++) {
+ wgt *= hmagic[3&(p>>(2*ii))][1&(i>>ii)][1&(j>>ii)];
+ }
+ if (wgt != 0.0) { /* record non-zero weight value */
+ s->spline.magic[mix].p = p;
+ s->spline.magic[mix].i = i;
+ s->spline.magic[mix].j = fdi * j; /* Pre-scale */
+ s->spline.magic[mix].wgt = (float)wgt;
+ mix++;
+ }
+ }
+ }
+ }
+ /* mix should == nim! */
+ s->spline.nm = nim;
+
+ /* Free basic grid info, and substitute tangency enhanced version */
+ /* ~~~~!! need to free any other structures in rspl that depend on */
+ /* ~~~~!! g.pss size, ie. rev stuff ??? */
+ if (s->g.alloc != NULL)
+ free((void *)s->g.alloc);
+
+ s->g.alloc = tang_alloc;
+ s->g.a = tang;
+
+ /* Adjust index tables */
+ s->g.pss = (1 << di) * fdi + G_XTRA;
+ for (i = 0; i < di; i++)
+ s->g.fci[i] = s->g.ci[i] * s->g.pss; /* In floats */
+ for (i = 0; i < (1 << di); i++)
+ s->g.fhi[i] = s->g.hi[i] * s->g.pss; /* In floats */
+
+ s->spline.spline = 1;
+
+//printf("~~make_tang finished\n");
+}
+
+/* Do a Hermite spline smooth interpolation based on the finest grid */
+/* (To do this more accurately, the data point interpolation within */
+/* the grid itteration should be of the same order. This increases */
+/* itteration complexity quite a bit, so we won't bother for the moment.) */
+/* This code is not optimised for speed. */
+/* Return 0 if OK, 1 if input was clipped to grid */
+int spline_interp_rspl(
+rspl *s,
+co *cp /* Input value and returned function value */
+) {
+ int e,f,p,i;
+ int di = s->di;
+ int fdi = s->fdi;
+ double ppw[MXRI][4]; /* Parameter powers of 0, 1, 2, 3 */
+ float *ga[POW2MXRI]; /* Pointers to grid cubes data in tang[] */
+ magic_data *tp; /* Pointer to items in magic matrix */
+ int rv = 0;
+
+/* printf("~~smooth interp called\n"); */
+
+ /* This is a restricted size function */
+ if (di > MXRI)
+ error("rspl: spline can't handle di = %d",di);
+ if (fdi > MXRO)
+ error("rspl: spline can't handle fdi = %d",fdi);
+
+ if (s->spline.spline == 0) /* Compute tangent info if it doesn't exist */
+ make_tang(s);
+
+ /* Locate grid base point, and position with base cube */
+ ga[0] = s->g.a; /* Base pointer of cube */
+ for (e = 0; e < di; e++) {
+ double t, pe;
+ int mi, gres_1 = s->g.res[e]-1;
+
+ pe = cp->p[e];
+ if (pe < s->g.l[e]) { /* Clip to grid */
+ pe = s->g.l[e];
+ rv = 1;
+ }
+ if (pe > s->g.h[e]) {
+ pe = s->g.h[e];
+ rv = 1;
+ }
+ t = (pe - s->g.l[e])/s->g.w[e];
+ mi = (int)floor(t); /* Grid coordinate */
+ if (mi < 0) /* Limit to valid cube base index range */
+ mi = 0;
+ else if (mi >= gres_1)
+ mi = gres_1-1;
+ ga[0] += s->g.fci[e] * mi; /* Add offset in dimen */
+ t = t - (double)mi;; /* sub-cube offset = parameter in dimension e */
+ ppw[e][0] = 1.0; /* Powers of parameter */
+ ppw[e][1] = t;
+ ppw[e][2] = t * t;
+ ppw[e][3] = t * t * t;
+ }
+
+ /* Compute indexes into cube corners in tangent array */
+ for (i = 1; i < (1 << di); i++)
+ ga[i] = ga[0] + s->g.fhi[i];
+
+ /* Now compute the output values */
+ for (f = 0; f < fdi; f++) /* Zero output value sums */
+ cp->v[f] = 0.0;
+
+ /* For all non-zero combinations of parameter powers */
+ {
+ double ppc = -1000.0; /* Parameter power combination */
+ for (tp = s->spline.magic, p = -1; tp < &s->spline.magic[s->spline.nm]; tp++) {
+ double wgt; /* Magic matrix weight */
+ float *gp; /* Pointer to vertex data */
+
+ if (p != tp->p) { /* Param power needs re-calculating */
+ int pp;
+ p = tp->p;
+ for (ppc = 1.0, pp = 0; pp < di; pp++)
+ ppc *= ppw[pp][3&(p>>(2*pp))]; /* comb. of param powers value */
+ }
+
+ wgt = tp->wgt * ppc; /* matrix times parameter */
+ gp = ga[tp->i] + tp->j; /* Point to base of vertex data */
+ for (f = 0; f < fdi; f++) /* For all output values */
+ cp->v[f] += wgt * gp[f];
+ }
+ }
+/* printf("~~smooth interp finished\n"); */
+ return rv;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rspl/stest.c b/rspl/stest.c
new file mode 100644
index 0000000..d1cf2da
--- /dev/null
+++ b/rspl/stest.c
@@ -0,0 +1,654 @@
+
+/*
+ * Scattered Data Interpolation
+ * with multilevel B-splines
+ * research.
+ *
+ * This is from the paper
+ * "Scattered Data Interpolation with Multilevel B-Splines"
+ * by Seungyong Lee, George Wolberg and Sung Yong Shin,
+ */
+
+/*
+ * Author: Graeme W. Gill
+ * Date: 2001/1/1
+ *
+ * Copyright 2000 - 2001 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include "../numeric/numlib.h"
+
+
+#ifndef NUMSUP_H
+void error(char *fmt, ...), warning(char *fmt, ...);
+#endif
+
+#define MXDI 4
+#define MXDO 4
+
+/* A control point */
+typedef struct {
+ double p[MXDI];
+ double v[MXDO];
+} co;
+
+/* Neighborhood latice cache data */
+typedef struct {
+ int c[MXDI]; /* Coordinate */
+ int xo; /* Offset into srbs latice */
+ double w; /* B-spline basis weight */
+} neigh;
+
+/* Structure that represents a resolution level of B-splines */
+struct _srbs {
+ struct _mrbs *p; /* Parent structure */
+ int res; /* Basic resolution */
+ int coi[MXDI]; /* Double increment for each input dimension into latice */
+ double *lat; /* Control latice, extending from +/- 1 from 0..res-1 */
+ double *_lat; /* Allocation base of lat */
+ int lsize, loff; /* Number of doubles in _lat, offset of lat from _lat */
+ double w[MXDI]; /* Input data cell width */
+ neigh *n; /* Neighborhood latice cache */
+ int nsize; /* Number of n entries */
+}; typedef struct _srbs srbs;
+
+/* Structure that represents the whole scattered interpolation state */
+struct _mrbs {
+ int di; /* Input dimensions */
+ int fdi; /* Output dimensions */
+ int tres; /* Target resolution */
+ double smf; /* Smoothing factor */
+ int npts; /* Number of data points */
+ co *pts; /* Coordinate points */
+ double l[MXDI], h[MXDI]; /* Input data range, cell width */
+ srbs *s; /* Current B-spline latice */
+}; typedef struct _mrbs mrbs;
+
+static void delete_srbs(srbs *s);
+
+/* Create a new empty mrbs */
+static mrbs *new_mrbs(
+int di, /* Input dimensionality */
+int fdi, /* Output dimesionality */
+int res, /* Target resolution */
+double smf /* Smoothing factor */
+) {
+ mrbs *p;
+ if ((p = (mrbs *)malloc(sizeof(mrbs))) == NULL)
+ error("Malloc mrbs failed");
+
+ p->di = di;
+ p->fdi = fdi;
+ p->tres = res;
+ p->smf = smf;
+ p->s = NULL;
+
+ return p;
+}
+
+static void delete_mrbs(mrbs *p) {
+
+ if (p != NULL) {
+ delete_srbs(p->s);
+ free(p);
+ }
+}
+
+/* Create a new empty srbs */
+static srbs *new_srbs(
+mrbs *p, /* Parent mrbs */
+int res /* Resolution of this srbs */
+) {
+ srbs *s;
+ int e, f;
+ double *_lat, *lat; /* Latice base address */
+ int ix, oe, oo[MXDI]; /* Neighborhood offset index, counter */
+
+ if ((s = (srbs *)malloc(sizeof(srbs))) == NULL)
+ error("Malloc srbs failed");
+
+ s->p = p;
+ s->res = res;
+
+ for (s->lsize = p->fdi, s->nsize = 1, e = 0; e < p->di; e++) {
+ s->coi[e] = s->lsize; /* (double) increment in this input dimension */
+ s->lsize *= (res + 2); /* Latice in 1D +/- 1 */
+ s->nsize *= 4; /* Neighborhood of 4 */
+ }
+
+ if ((s->_lat = (double *)malloc(s->lsize * sizeof(double))) == NULL)
+ error("Malloc srbs latice failed");
+
+ /* Compute the base address */
+ for (s->loff = 0, e = 0; e < p->di; e++) {
+ s->loff += s->coi[e]; /* Offset by 1 in each input dimension */
+ }
+ s->lat = s->_lat + s->loff;
+
+ /* Figure the cell width */
+ for (e = 0; e < p->di; e++)
+ s->w[e] = (p->h[e] - p->l[e])/(res-1.0);
+
+ /* Setup neighborhood cache info */
+ if ((s->n = (neigh *)malloc(s->nsize * sizeof(neigh))) == NULL)
+ error("Malloc srbs neighborhood failed");
+
+ for (oe = 0; oe < p->di; oe++)
+ oo[oe] = 0;
+
+ for(ix = oe = 0; oe < p->di; ix++) {
+ int xo;
+ for (xo = e = 0; e < p->di; e++) {
+ s->n[ix].c[e] = oo[e];
+ xo += s->coi[e] * oo[e]; /* Accumulate latice offset */
+ }
+ s->n[ix].xo = xo;
+ s->n[ix].w = 0.0;
+
+ /* Increment destination offset counter */
+ for (oe = 0; oe < p->di; oe++) {
+ if (++oo[oe] <= 3) /* Counting from 0 ... 3 */
+ break;
+ oo[oe] = 0;
+ }
+ }
+
+ return s;
+}
+
+/* Destroy a srbs */
+static void delete_srbs(srbs *s) {
+ if (s != NULL) {
+ free(s->_lat);
+ free(s->n);
+ free(s);
+ }
+}
+
+/* Dump the 2D -> 1D contents of an srbs */
+static void dump_srbs(srbs *s) {
+ int e, f;
+ int ce, co[MXDI]; /* latice counter */
+ mrbs *p = s->p; /* Parent object */
+
+ /* Init the counter */
+ for (ce = 0; ce < p->di; ce++)
+ co[ce] = -1;
+ ce = 0;
+
+ f = 0;
+ while(ce < p->di) {
+ double v;
+ int off = 0; /* Latice offset */
+ for (e = 0; e < p->di; e++) {
+ off += co[e] * s->coi[e]; /* Accumulate latice offset */
+ }
+ v = s->lat[off + f]; /* Value of this latice point */
+
+ printf("Latice at [%d][%d] = %f\n",co[1],co[0],v);
+
+ /* Increment the latice counter */
+ for (ce = 0; ce < p->di; ce++) {
+ if (++co[ce] <= s->res) /* Counting from -1 ... s->res */
+ break;
+ co[ce] = -1;
+ }
+ }
+}
+
+/* Initialise an srbs with a linear approximation to the scattered data */
+static void linear_srbs(
+srbs *s
+) {
+ int i, e, f;
+ mrbs *p = s->p; /* Parent object */
+ double **A; /* A matrix holding scattered data points */
+ double *B; /* B matrix holding RHS & solution */
+
+ /* Allocate the matricies */
+ B = dvector(0, p->npts-1);
+ A = dmatrix(0, p->npts-1, 0, p->di);
+
+ /* For each output dimension, solve the linear equation coeficients */
+ for (f = 0; f < p->fdi; f++) {
+ int ce, co[MXDI]; /* latice counter */
+
+ /* Init A[][] with the scattered data points positions */
+ /* Also init B[] with the value for this output dimension */
+ for (i = 0; i < p->npts; i++) {
+ for (e = 0; e < p->di; e++)
+ A[i][e] = p->pts[i].p[e];
+ A[i][e] = 1.0;
+ B[i] = p->pts[i].v[f];
+ }
+
+ /* Solve the equation A.x = b using SVD */
+ /* (The w[] values are thresholded for best accuracy) */
+ /* Return non-zero if no solution found */
+ if (svdsolve(A, B, p->npts, p->di+1) != 0)
+ error("SVD least squares failed");
+ /* A[][] will have been changed, and B[] holds the p->di+1 coefficients */
+
+ /* Use the coefficients to initialise the srbs values */
+ for (ce = 0; ce < p->di; ce++)
+ co[ce] = -1;
+ ce = 0;
+
+ while(ce < p->di) {
+ double v = B[p->di]; /* Constant */
+ int off = 0; /* Latice offset */
+ for (e = 0; e < p->di; e++) {
+ double lv;
+ lv = p->l[e] + s->w[e] * co[e]; /* Input value for this latice location */
+ v += B[e] * lv;
+ off += co[e] * s->coi[e]; /* Accumulate latice offset */
+ }
+ s->lat[off + f] = v; /* Value of this latice point */
+
+ /* Increment the latice counter */
+ for (ce = 0; ce < p->di; ce++) {
+ if (++co[ce] <= s->res) /* Counting from -1 ... s->res */
+ break;
+ co[ce] = -1;
+ }
+ }
+ }
+ free_dmatrix(A, 0, p->npts-1, 0, p->di);
+ free_dvector(B, 0, p->npts-1);
+}
+
+/* Do a latice refinement - upsample the current */
+/* source latice to the destination latice. */
+static void refine_srbs(
+srbs *ds, /* Destination srbs */
+srbs *ss /* Source srbs */
+) {
+ mrbs *p = ss->p; /* Parent object */
+ int ce, co[MXDI]; /* Source coordinate counter */
+ int six; /* Source index */
+ int dix; /* destination index */
+ static double _wt[5] = { 1.0/8.0, 4.0/8.0, 6.0/8.0, 4.0/8.0, 1.0/8.0 };
+ static double *wt = &_wt[2]; /* 1D Distribution weighting */
+
+ /* Zero the destination latice before accumulating values */
+ for (dix = 0; dix < ds->lsize; dix++)
+ ds->_lat[dix] = 0.0;
+
+ /* Now for each source latice entry, add weighted portions */
+ /* to the associated destination points */
+
+ /* Init the source coordinate counter */
+ for (ce = 0; ce < p->di; ce++)
+ co[ce] = -1;
+ ce = 0;
+ six = -ss->loff;
+
+ while(ce < p->di) {
+ int oe, oo[MXDI]; /* Destination offset counter */
+
+//printf("Source coord %d %d, offset %d, value %f\n",co[0], co[1], six, ss->lat[six]);
+ /* calc destination index, and init offest counter */
+ for (dix = oe = 0; oe < p->di; oe++) {
+ oo[oe] = -2;
+ dix += co[oe] * 2 * ds->coi[oe]; /* Accumulate dest offset */
+ }
+ oe = 0;
+
+//printf("Dest coord %d %d\n",co[0] * 2, co[1] * 2);
+ /* For all the offsets from the destination point */
+ while(oe < p->di) {
+ int e, f, dixo; /* Destination index offset */
+ double w = 1.0; /* Weighting */
+
+//printf("dest offset %d %d\n",oo[0], oo[1]);
+ /* Compute dest index offset, and check that we are not outside the destination */
+ for (dixo = e = 0; e < p->di; e++) {
+ int x = co[e] * 2 + oo[e]; /* dest coord */
+ dixo += oo[e] * ds->coi[e]; /* Accumulate dest offset */
+//printf("x[%d] = %d\n",e, x);
+ w *= wt[oo[e]]; /* Compute distribution weighting */
+ if (x < -1 || x > ds->res)
+ break; /* No good */
+ }
+ if (e >= p->di) { /* We are within the destination latice */
+//if ((co[0] * 2 + oo[0]) == 0 && (co[1] * 2 + oo[1]) == 0) {
+//printf("Source coord %d %d, offset %d, value %f\n",co[0], co[1], six, ss->lat[six]);
+//printf("Dest coord %d %d ix %d, weight %f\n",co[0] * 2 + oo[0], co[1] * 2 + oo[1], dix+dixo, w);
+//}
+
+ for (f = 0; f < p->fdi; f++) { /* Distribute weighted values */
+ double v = ss->lat[six + f];
+//if ((co[0] * 2 + oo[0]) == 0 && (co[1] * 2 + oo[1]) == 0)
+//printf("Value being dist %f, weighted value %f\n", v, v * w);
+ ds->lat[dix + dixo + f] += v * w;
+ }
+ }
+
+ /* Increment destination offset counter */
+ for (oe = 0; oe < p->di; oe++) {
+ if (++oo[oe] <= 2) /* Counting from -2 ... +2 */
+ break;
+ oo[oe] = -2;
+ }
+ }
+
+ /* Increment the source index and coordinat counter */
+ six += p->fdi;
+ for (ce = 0; ce < p->di; ce++) {
+ if (++co[ce] <= ss->res) /* Counting from -1 ... ss->res */
+ break;
+ co[ce] = -1;
+ }
+ }
+}
+
+/* Compute the Cubic B-spline weightings for a given t */
+void basis(double b[4], double t) {
+ double _t3, _t2, _t1, _3t3, _3t2, _3t1, _6t2;
+
+ _t1 = t/6.0;
+ _t2 = _t1 * _t1;
+ _t3 = _t2 * _t1;
+ _3t1 = 3.0 * _t1;
+ _3t2 = 3.0 * _t2;
+ _3t3 = 3.0 * _t3;
+ _6t2 = 6.0 * _t2;
+
+ b[0] = - _t3 + _3t2 - _3t1 + 1.0/6.0;
+ b[1] = _3t3 - _6t2 + 4.0/6.0;
+ b[2] = -_3t3 + _3t2 + _3t1 + 1.0/6.0;
+ b[3] = _t3;
+}
+
+
+/* Improve an srbs to make it closer to the scattered data */
+static void improve_srbs(
+srbs *s
+) {
+ int i, e, f;
+ mrbs *p = s->p; /* Parent object */
+ double *delta; /* Delta accumulation */
+ double *omega; /* Omega accumulation */
+
+ /* Allocate temporary accumulation arrays */
+ if ((delta = (double *)calloc(sizeof(double), s->lsize)) == NULL)
+ error("Malloc srbs temp latice failed");
+ delta += s->loff;
+ if ((omega = (double *)calloc(sizeof(double), s->lsize)) == NULL)
+ error("Malloc srbs temp latice failed");
+ omega += s->loff;
+
+ /* For each scattered data point */
+ for (i = 0; i < p->npts; i++) {
+ int ix; /* Latice index of base of neighborhood */
+ double b[MXDI][4]; /* B-spline basis factors for each dimension */
+ double sws; /* Sum of all the basis factors squared */
+ double ve[MXDO]; /* Current output value error */
+ int nn; /* Neighbor counter */
+
+ /* Figure out our neighborhood */
+ for (ix = e = 0; e < p->di; e++) {
+ int x;
+ double t, sp, fp;
+ sp = (p->pts[i].p[e] - p->l[e])/s->w[e]; /* Scaled position */
+ fp = floor(sp);
+ x = (int)(fp - 1.0); /* Grid coordinate */
+ ix += s->coi[e] * x; /* Accume latice offset */
+ t = sp - fp; /* Spline parameter */
+ basis(b[e], t); /* Compute basis function values */
+ }
+
+ /* Compute the grid basis weight functions, */
+ /* the sum of the weights squared, and the current */
+ /* output value estimate. */
+ for (f = 0; f < p->fdi; f++)
+ ve[f] = p->pts[i].v[f]; /* Target output value */
+ for (sws = 0.0, nn = 0; nn < s->nsize; nn++) {
+ double w;
+ for (w = 1.0, e = 0; e < p->di; e++)
+ w *= b[e][s->n[nn].c[e]];
+ s->n[nn].w = w; /* cache weighting */
+ sws += w * w;
+ for (f = 0; f < p->fdi; f++)
+ ve[f] -= w * s->lat[ix + s->n[nn].xo + f]; /* Subtract current aprox value */
+ }
+printf("Error at point %d = %f\n",i,ve[0]);
+
+ /* Accumulate the delta and omega factors */
+ /* for this resolutions improvement. */
+ for (nn = 0; nn < s->nsize; nn++) {
+ double ws, ww, w = s->n[nn].w;
+ int xo = ix + s->n[nn].xo; /* Latice offset */
+ ww = w * w;
+ ws = ww * w/sws; /* Scale factor for delta */
+ omega[xo] += ww; /* Accumulate omega */
+ for (f = 0; f < p->fdi; f++)
+ delta[xo + f] += ws * ve[f]; /* Accumulate delta */
+printf("Distributing omega %f to %d %d\n",ww,s->n[nn].c[0],s->n[nn].c[1]);
+printf("Distributing delta %f to %d %d\n",ws * ve[0],s->n[nn].c[0],s->n[nn].c[1]);
+ }
+ }
+
+ omega -= s->loff; /* Base them back to -1 corner */
+ delta -= s->loff;
+
+ /* Go through the delta and omega arrays, */
+ /* compute and add the refinements to the current */
+ /* B-spline control latice. */
+ for (i = 0; i < s->lsize; i++) {
+ double om = omega[i];
+ if (om != 0.0) {
+ for (f = 0; f < p->fdi; f++)
+ s->_lat[i] += delta[i + f]/om;
+printf("Adjusting latice index %d by %f to give %f\n",i, delta[i]/om, s->_lat[i]);
+ }
+ }
+
+ /* Done with temporary arrays */
+ free(omega);
+ free(delta);
+}
+
+/* Return the interpolated value for a given point */
+/* Return NZ if input point is out of range */
+static int lookup_srbs(
+mrbs *p,
+co *c /* Point to interpolate */
+) {
+ srbs *s = p->s;
+ int e, f;
+ int ix; /* Latice index of base of neighborhood */
+ double b[MXDI][4]; /* B-spline basis factors for each dimension */
+ int nn; /* Neighbor counter */
+
+ /* Figure out our neighborhood */
+ for (ix = e = 0; e < p->di; e++) {
+ int x;
+ double t, sp, fp;
+ sp = c->p[e];
+ if (sp < p->l[e] || sp > p->h[e])
+ return 1;
+ sp = (sp - p->l[e])/s->w[e]; /* Scaled position */
+ fp = floor(sp);
+ x = (int)(fp - 1.0); /* Grid coordinate */
+ ix += s->coi[e] * x; /* Accume latice offset */
+ t = sp - fp; /* Spline parameter */
+ basis(b[e], t); /* Compute basis function values */
+ }
+
+ /* Compute the the current output value. */
+ for (f = 0; f < p->fdi; f++)
+ c->v[f] = 0.0;
+ for (nn = 0; nn < s->nsize; nn++) {
+ double w;
+ for (w = 1.0, e = 0; e < p->di; e++)
+ w *= b[e][s->n[nn].c[e]];
+ for (f = 0; f < p->fdi; f++)
+ c->v[f] += w * s->lat[ix + s->n[nn].xo + f]; /* Accume spline value */
+ }
+
+ return 0;
+}
+
+/* Take a list of scattered data points, */
+/* and setup the mrbs. */
+void set_mrbs(
+mrbs *p, /* mrbs to set up */
+co *pts, /* scattered data points (taken) */
+int npts, /* number of scattered data points */
+double *l, /* Input data range, low (May be NULL) */
+double *h /* Input data range, high (May be NULL) */
+) {
+ int res;
+ int i, e, f;
+ srbs *s0 = NULL, *s1;
+
+ /* Establish the input data range */
+ for (e = 0; e < p->di; e++) {
+ if (l == NULL)
+ p->l[e] = 1e60;
+ else
+ p->l[e] = l[e];
+ if (h == NULL)
+ p->h[e] = -1e60;
+ else
+ p->h[e] = h[e];
+ }
+ for (i = 0; i < npts; i++) {
+ for (e = 0; e < p->di; e++) {
+ if (pts[i].p[e] < p->l[e])
+ p->l[e] = pts[i].p[e];
+ if (pts[i].p[e] > p->h[e])
+ p->h[e] = pts[i].p[e];
+ }
+ }
+
+ /* Take ownership the data */
+ p->pts = pts;
+ p->npts = npts;
+
+ /* Create an initial srbs */
+ res = 2;
+ if ((s1 = new_srbs(p, 2)) == NULL)
+ error("new_srbs failed");
+
+ /* Set it up with a linear first approximation */
+ linear_srbs(s1);
+dump_srbs(s1);
+
+ /* Build up the resolution */
+ for (res = 2 * res -1; res < p->tres; res = 2 * res -1) {
+
+printf("~1 doing resolution %d\n",res);
+ delete_srbs(s0);
+ s0 = s1;
+ if ((s1 = new_srbs(p, res)) == NULL)
+ error("new_srbs failed");
+
+ refine_srbs(s1, s0);
+dump_srbs(s1);
+ improve_srbs(s1);
+ }
+
+ delete_srbs(s0);
+ p->s = s1; /* Final resolution */
+}
+
+
+/* - - - - - - - - - - - - - - - - */
+
+/* Some test points */
+#define NP 5
+co tpts[NP] = {
+ { { 1, 5 }, { 3 } },
+ { { 2, 9 }, { 6 } },
+ { { 8, 0 }, { 1 } },
+ { { 5, 4 }, { 10 } },
+ { { 3, 9 }, { 4 } }
+};
+
+#define FRES 33
+
+int
+main(void) {
+ double ll[MXDI], hh[MXDI];
+ mrbs *p;
+ int i;
+
+#ifdef NEVER /* Make points be on a plane */
+{
+for (i = 0; i < NP; i++) {
+ tpts[i].v[0] = 0.5 * tpts[i].p[0] + 0.7 * tpts[i].p[1] + 3.0;
+printf("%f, %f -> %f\n", tpts[i].p[0], tpts[i].p[1], tpts[i].v[0]);
+}
+}
+#endif
+ error_program = "stest";
+
+ printf("Starting test\n");
+ p = new_mrbs(2, 1, FRES, 1.0);
+
+ ll[0] = 0.0;
+ ll[1] = 0.0;
+ hh[0] = 10.0;
+ hh[1] = 10.0;
+ set_mrbs(p, tpts, NP, ll, hh);
+
+ printf("Interpolation setup\n");
+
+ /* Check out the accuracy */
+ for (i = 0; i < NP; i++) {
+ co tp;
+ tp.p[0] = tpts[i].p[0];
+ tp.p[1] = tpts[i].p[1];
+ if (lookup_srbs(p, &tp))
+ printf("Point %d, %f %f outside domain\n",i,tpts[i].p[0],tpts[i].p[1]);
+ else {
+ printf("Point %d has value %f, should be %f\n",i,tp.v[0], tpts[i].v[0]);
+ }
+ }
+
+ printf("Test done\n");
+ return 0;
+}
+
+#ifndef NUMSUP_H
+/* Basic printf type error() and warning() routines */
+
+void
+error(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"stest: Error - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit (-1);
+}
+
+void
+warning(char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr,"stest: Warning - ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+#endif /* NUMSUP_H */
+
diff --git a/rspl/t2d.c b/rspl/t2d.c
new file mode 100644
index 0000000..b1167ef
--- /dev/null
+++ b/rspl/t2d.c
@@ -0,0 +1,1016 @@
+/************************************************/
+/* Test RSPL in 2D */
+/************************************************/
+
+/* Author: Graeme Gill
+ * Date: 22/4/96
+ * Derived from cmatch.c
+ * Copyright 1995 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+
+#define DEBUG
+#define DETAILED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include "rspl.h"
+#include "tiffio.h"
+#include "plot.h"
+
+
+#ifdef NEVER
+FILE *verbose_out = stdout;
+int verbose_level = 6; /* Current verbosity level */
+ /* 0 = none */
+ /* !0 = diagnostics */
+#endif /* NEVER */
+
+#define PLOTRES 256
+#define WIDTH 400 /* Raster size */
+#define HEIGHT 400
+
+#define MAX_ITS 500
+#define IT_TOL 0.0005
+#define GRES0 25 /* Default resolutions */
+#define GRES1 25
+#undef NEVER
+#define ALWAYS
+
+//double t1xa[PNTS] = { 0.2, 0.25, 0.30, 0.35, 0.40, 0.44, 0.48, 0.51, 0.64, 0.75 };
+//double t1ya[PNTS] = { 0.3, 0.35, 0.4, 0.41, 0.42, 0.46, 0.5, 0.575, 0.48, 0.75 };
+
+/* 1D test function repeated 3 times along x = 0.5 */
+co test_points1[] = {
+ {{ 0.4,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.4,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.4,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.4,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.4,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.4,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.4,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.4,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.4,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.4,0.75 },{ 0.75 }}, /* 9 */
+
+ {{ 0.5,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.5,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.5,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.5,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.5,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.5,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.5,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.5,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.5,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.5,0.75 },{ 0.75 }}, /* 9 */
+
+ {{ 0.6,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.6,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.6,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.6,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.6,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.6,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.6,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.6,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.6,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.6,0.75 },{ 0.75 }} /* 9 */
+};
+
+/* x + y^2 function with one non-monotonic point */
+co test_points2[] = {
+ {{ 0.1,0.1,0.5,0.0 },{ 0.11 }}, /* 0 */
+ {{ 0.2,0.7,0.1,0.3 },{ 0.69 }}, /* 1 */
+ {{ 0.8,0.8,0.8,0.2 },{ 1.44 }}, /* 2 */
+ {{ 0.5,0.6,0.4,0.9 },{ 0.86 }}, /* 3 */
+ {{ 0.2,0.5,0.2,0.7 },{ 0.45 }}, /* 4 */
+// {{ 0.3,0.7,0.2,0.8 },{ 0.35 }}, /* nm 5 */
+ {{ 0.5,0.5,0.2,0.8 },{ 0.30 }}, /* nm 5 */
+ {{ 0.5,0.4,0.9,0.3 },{ 0.66 }}, /* 6 */
+ {{ 0.1,0.9,0.7,0.4 },{ 0.91 }}, /* 7 */
+ {{ 0.7,0.2,0.1,0.3 },{ 0.74 }}, /* 8 */
+ {{ 0.8,0.4,0.3,0.7 },{ 0.96 }}, /* 9 */
+ {{ 0.3,0.3,0.4,0.1 },{ 0.39 }} /* 10 */
+ };
+
+/* x + y^2 function */
+co test_points3[] = {
+ {{ 0.1,0.1,0.5,0.0 },{ 0.11 }}, /* 0 */
+ {{ 0.2,0.7,0.1,0.3 },{ 0.69 }}, /* 1 */
+ {{ 0.8,0.8,0.8,0.2 },{ 1.44 }}, /* 2 */
+ {{ 0.5,0.6,0.4,0.9 },{ 0.86 }}, /* 3 */
+ {{ 0.2,0.5,0.2,0.7 },{ 0.45 }}, /* 4 */
+ {{ 0.3,0.7,0.2,0.8 },{ 0.79 }}, /* 5 */
+ {{ 0.5,0.4,0.9,0.3 },{ 0.66 }}, /* 6 */
+ {{ 0.1,0.9,0.7,0.4 },{ 0.91 }}, /* 7 */
+ {{ 0.7,0.2,0.1,0.3 },{ 0.74 }}, /* 8 */
+ {{ 0.8,0.4,0.3,0.7 },{ 0.96 }}, /* 9 */
+ {{ 0.3,0.3,0.4,0.1 },{ 0.39 }} /* 10 */
+ };
+
+/* Arbitrary values */
+co test_points4[] = {
+ {{ 0.1,0.1,0.5,0.0 },{ 0.0 }}, /* 0 */
+ {{ 0.2,0.7,0.1,0.3 },{ 0.1 }}, /* 1 */
+ {{ 0.8,0.8,0.8,0.2 },{ 0.2 }}, /* 2 */
+ {{ 0.5,0.6,0.4,0.9 },{ 0.3 }}, /* 3 */
+ {{ 0.2,0.5,0.2,0.7 },{ 0.4 }}, /* 4 */
+ {{ 0.3,0.7,0.2,0.8 },{ 0.5 }}, /* 5 */
+ {{ 0.5,0.4,0.9,0.3 },{ 0.6 }}, /* 6 */
+ {{ 0.1,0.9,0.7,0.4 },{ 0.7 }}, /* 7 */
+ {{ 0.7,0.2,0.1,0.3 },{ 0.8 }}, /* 8 */
+ {{ 0.8,0.4,0.3,0.7 },{ 0.9 }}, /* 9 */
+ {{ 0.3,0.3,0.4,0.1 },{ 1.0 }} /* 10 */
+ };
+
+/* single dimension line */
+co test_points5[] = {
+ {{ 0.4,0.2 },{ 0.10 }}, /* 0 */
+ {{ 0.5,0.4 },{ 0.50 }}, /* 1 */
+ {{ 0.6,0.6 },{ 0.90 }} /* 2 */
+ };
+
+/* Single value, many points */
+co test_points6[] = {
+ {{ 0.5,0.6 },{ 0.4 }}, /* 0 */
+ {{ 0.4,0.4 },{ 0.4 }}, /* 1 */
+ {{ 0.6,0.4 },{ 0.4 }}, /* 2 */
+ {{ 0.5,0.8 },{ 0.4 }}, /* 3 */
+ {{ 0.8,0.6 },{ 0.4 }}, /* 4 */
+ {{ 0.8,0.3 },{ 0.4 }}, /* 5 */
+ {{ 0.5,0.15 },{ 0.4 }}, /* 6 */
+ {{ 0.2,0.3 },{ 0.4 }}, /* 7 */
+ {{ 0.2,0.6 },{ 0.4 }} /* 8 */
+ };
+
+/* single value triangle */
+co test_points7[] = {
+ {{ 0.2,0.5 },{ 0.4 }}, /* 0 */
+ {{ 0.6,0.2 },{ 0.4 }}, /* 1 */
+ {{ 0.6,0.8 },{ 0.4 }} /* 2 */
+ };
+
+/* 3dap C+M L* values */
+co test_points8[] = {
+ {{ 0.0, 0.0 }, { 0.96433 }},
+ {{ 1.0, 0.0 }, { 0.54532 }},
+ {{ 0.0, 1.0 }, { 0.13844 }},
+ {{ 1.0, 1.0 }, { 0.10533 }},
+ {{ 0.52015, 0.51226 }, { 0.44379 }},
+ {{ 0.016882, 0.4899 }, { 0.55744 }},
+ {{ 0.48229, 0.014288 }, { 0.71457 }},
+ {{ 0.32501, 0.99594 }, { 0.14023 }},
+ {{ 0.9429, 0.39117 }, { 0.40529 }},
+ {{ 0.25639, 0.25624 }, { 0.65594 }},
+ {{ 0.80601, 0.62945 }, { 0.32338 }},
+ {{ 0.74113, 0.20699 }, { 0.528 }},
+ {{ 0.21766, 0.72345 }, { 0.37442 }},
+ {{ 0.64679, 0.94379 }, { 0.16175 }},
+ {{ 0.24217, 0.012757 }, { 0.81892 }},
+ {{ 0.013911, 0.25268 }, { 0.72574 }},
+ {{ 0.54723, 0.74872 }, { 0.30302 }},
+ {{ 0.016488, 0.77535 }, { 0.33234 }},
+ {{ 0.82135, 0.044133 }, { 0.57191 }},
+ {{ 0.52377, 0.2403 }, { 0.58315 }},
+ {{ 0.27798, 0.51935 }, { 0.49399 }},
+ {{ 0.1186, 0.93321 }, { 0.19912 }},
+ {{ 0.95827, 0.18481 }, { 0.48037 }},
+ {{ 0.7714, 0.48664 }, { 0.39652 }},
+ {{ 0.94209, 0.7863 }, { 0.22555 }},
+ {{ 0.9429, 0.58967 }, { 0.32235 }},
+ {{ 0.12251, 0.12897 }, { 0.78743 }},
+ {{ 0.14563, 0.38536 }, { 0.60355 }},
+ {{ 0.41616, 0.65055 }, { 0.3876 }},
+ {{ 0.38091, 0.14221 }, { 0.68198 }},
+ {{ 0.388, 0.38098 }, { 0.54479 }},
+ {{ 0.74149, 0.77517 }, { 0.25597 }},
+ {{ 0.11267, 0.62485 }, { 0.45976 }},
+ {{ 0.62755, 0.37228 }, { 0.48943 }},
+ {{ 0.6502, 0.59695 }, { 0.37031 }},
+ {{ 0.32033, 0.81669 }, { 0.28831 }},
+ {{ 0.664, 0.096944 }, { 0.60575 }},
+ {{ 0.82772, 0.33242 }, { 0.45496 }},
+ {{ 0.49121, 0.89468 }, { 0.20858 }},
+ {{ 0.84729, 0.94866 }, { 0.1453 }}
+};
+
+/* 3dap C+M a* values */
+co test_points9[] = {
+ {{ 0.0, 0.0 }, { 0.504266 }},
+ {{ 1.0, 0.0 }, { 0.14962 }},
+ {{ 0.0, 1.0 }, { 0.538792 }},
+ {{ 1.0, 1.0 }, { 0.481662 }},
+ {{ 0.52015, 0.51226 }, { 0.39446 }},
+ {{ 0.016882, 0.4899 }, { 0.5038104 }},
+ {{ 0.48229, 0.014288 }, { 0.32387 }},
+ {{ 0.32501, 0.99594 }, { 0.5028343 }},
+ {{ 0.9429, 0.39117 }, { 0.24127 }},
+ {{ 0.25639, 0.25624 }, { 0.43018 }},
+ {{ 0.80601, 0.62945 }, { 0.34684 }},
+ {{ 0.74113, 0.20699 }, { 0.274 }},
+ {{ 0.21766, 0.72345 }, { 0.480491 }},
+ {{ 0.64679, 0.94379 }, { 0.472241 }},
+ {{ 0.24217, 0.012757 }, { 0.39938 }},
+ {{ 0.013911, 0.25268 }, { 0.5035042 }},
+ {{ 0.54723, 0.74872 }, { 0.433005 }},
+ {{ 0.016488, 0.77535 }, { 0.513262 }},
+ {{ 0.82135, 0.044133 }, { 0.21553 }},
+ {{ 0.52377, 0.2403 }, { 0.35082 }},
+ {{ 0.27798, 0.51935 }, { 0.451019 }},
+ {{ 0.1186, 0.93321 }, { 0.513378 }},
+ {{ 0.95827, 0.18481 }, { 0.19309 }},
+ {{ 0.7714, 0.48664 }, { 0.32019 }},
+ {{ 0.94209, 0.7863 }, { 0.37573 }},
+ {{ 0.9429, 0.58967 }, { 0.29975 }},
+ {{ 0.12251, 0.12897 }, { 0.460094 }},
+ {{ 0.14563, 0.38536 }, { 0.471575 }},
+ {{ 0.41616, 0.65055 }, { 0.439078 }},
+ {{ 0.38091, 0.14221 }, { 0.37965 }},
+ {{ 0.388, 0.38098 }, { 0.409296 }},
+ {{ 0.74149, 0.77517 }, { 0.403494 }},
+ {{ 0.11267, 0.62485 }, { 0.4905027 }},
+ {{ 0.62755, 0.37228 }, { 0.34157 }},
+ {{ 0.6502, 0.59695 }, { 0.37896 }},
+ {{ 0.32033, 0.81669 }, { 0.477444 }},
+ {{ 0.664, 0.096944 }, { 0.28 }},
+ {{ 0.82772, 0.33242 }, {0.26821 }},
+ {{ 0.49121, 0.89468 }, { 0.47241 }},
+ {{ 0.84729, 0.94866 }, { 0.459256 }}
+};
+
+/* 3dap C+M b* values */
+co test_points10[] = {
+ {{ 0.0, 0.0 }, { 0.4918362 }},
+ {{ 1.0, 0.0 }, { 0.0 }},
+ {{ 0.0, 1.0 }, { 0.457067 }},
+ {{ 1.0, 1.0 }, { 0.407021 }},
+ {{ 0.52015, 0.51226 }, { 0.34029 }},
+ {{ 0.016882, 0.4899 }, { 0.48672 }},
+ {{ 0.48229, 0.014288 }, { 0.19406 }},
+ {{ 0.32501, 0.99594 }, { 0.453116 }},
+ {{ 0.9429, 0.39117 }, { 0.1766 }},
+ {{ 0.25639, 0.25624 }, { 0.37533 }},
+ {{ 0.80601, 0.62945 }, { 0.29851 }},
+ {{ 0.74113, 0.20699 }, { 0.16808 }},
+ {{ 0.21766, 0.72345 }, { 0.448783 }},
+ {{ 0.64679, 0.94379 }, { 0.415526 }},
+ {{ 0.24217, 0.012757 }, { 0.32194 }},
+ {{ 0.013911, 0.25268 }, { 0.486404 }},
+ {{ 0.54723, 0.74872 }, { 0.38695 }},
+ {{ 0.016488, 0.77535 }, { 0.484031 }},
+ {{ 0.82135, 0.044133 }, { 0.07184 }},
+ {{ 0.52377, 0.2403 }, { 0.26192 }},
+ {{ 0.27798, 0.51935 }, { 0.412787 }},
+ {{ 0.1186, 0.93321 }, { 0.463822 }},
+ {{ 0.95827, 0.18481 }, { 0.09177 }},
+ {{ 0.7714, 0.48664 }, { 0.26049 }},
+ {{ 0.94209, 0.7863 }, { 0.3249 }},
+ {{ 0.9429, 0.58967 }, { 0.25115 }},
+ {{ 0.12251, 0.12897 }, { 0.419966 }},
+ {{ 0.14563, 0.38536 }, { 0.436771 }},
+ {{ 0.41616, 0.65055 }, { 0.39638 }},
+ {{ 0.38091, 0.14221 }, { 0.29133 }},
+ {{ 0.388, 0.38098 }, { 0.35442 }},
+ {{ 0.74149, 0.77517 }, { 0.35501 }},
+ {{ 0.11267, 0.62485 }, { 0.466548 }},
+ {{ 0.62755, 0.37228 }, { 0.2675 }},
+ {{ 0.6502, 0.59695 }, { 0.32833 }},
+ {{ 0.32033, 0.81669 }, { 0.438066 }},
+ {{ 0.664, 0.096944 }, { 0.15598 }},
+ {{ 0.82772, 0.33242 }, { 0.18834 }},
+ {{ 0.49121, 0.89468 }, { 0.423113 }},
+ {{ 0.84729, 0.94866 }, { 0.3979 }}
+};
+
+/* Values at edges test */
+double test_f11(double x, double y) { /* Function that computes values */
+ double val = 0.0;
+
+ val = (x * x - 0.5) * 0.6
+ + (y * y - 0.5) * 0.4
+ + 0.5;
+
+ return val;
+}
+co test_points11[] = {
+ {{ 0.0, 0.0 }},
+ {{ 0.0, 0.25 }},
+ {{ 0.0, 0.5 }},
+ {{ 0.0, 0.75 }},
+ {{ 0.0, 1.0 }},
+
+ {{ 0.25, 0.0 }},
+ {{ 0.25, 0.25 }},
+ {{ 0.25, 0.5 }},
+ {{ 0.25, 0.75 }},
+ {{ 0.25, 1.0 }},
+
+ {{ 0.5, 0.0 }},
+ {{ 0.5, 0.25 }},
+ {{ 0.5, 0.5 }},
+ {{ 0.5, 0.75 }},
+ {{ 0.5, 1.0 }},
+
+ {{ 0.75, 0.0 }},
+ {{ 0.75, 0.25 }},
+ {{ 0.75, 0.5 }},
+ {{ 0.75, 0.75 }},
+ {{ 0.75, 1.0 }},
+
+ {{ 1.0, 0.0 }},
+ {{ 1.0, 0.25 }},
+ {{ 1.0, 0.5 }},
+ {{ 1.0, 0.75 }},
+ {{ 1.0, 1.0 }},
+
+
+ {{ 0.2, 0.7 }},
+ {{ 0.2, 0.8 }},
+ {{ 0.2, 0.9 }},
+ {{ 0.2, 1.0 }},
+
+ {{ 0.1, 0.7 }},
+ {{ 0.1, 0.8 }},
+ {{ 0.1, 0.9 }},
+ {{ 0.1, 1.0 }},
+
+ {{ 0.0, 0.7 }},
+ {{ 0.0, 0.8 }},
+ {{ 0.0, 0.9 }}
+};
+
+/* Points consistent with a matrix interpolation */
+co test_points12[] = {
+ {{ 0.1, 0.1 }, { 0.1 }},
+ {{ 0.5, 0.1 }, { 0.5 }},
+ {{ 0.9, 0.1 }, { 0.9 }},
+ {{ 0.1, 0.5 }, { 0.1 }},
+ {{ 0.5, 0.5 }, { 0.35 }},
+ {{ 0.9, 0.5 }, { 0.6 }},
+ {{ 0.1, 0.9 }, { 0.1 }},
+ {{ 0.5, 0.9 }, { 0.2 }},
+ {{ 0.9, 0.9 }, { 0.3 }}
+};
+
+/* Points down the "neutral axis" extrapolation test */
+co test_points13[] = {
+ {{ 0.0069, 0.0071 }, { 0.0726 }},
+ {{ 0.0068, 0.0071 }, { 0.0704 }},
+ {{ 0.0069, 0.0072 }, { 0.0720 }},
+ {{ 0.0069, 0.0072 }, { 0.0734 }},
+ {{ 0.0069, 0.0072 }, { 0.0750 }},
+ {{ 0.0070, 0.0072 }, { 0.0779 }},
+ {{ 0.0070, 0.0072 }, { 0.0741 }},
+ {{ 0.0069, 0.0072 }, { 0.0745 }},
+ {{ 0.0069, 0.0072 }, { 0.0747 }},
+ {{ 0.0071, 0.0073 }, { 0.0760 }},
+ {{ 0.0070, 0.0073 }, { 0.0751 }},
+ {{ 0.0070, 0.0073 }, { 0.0759 }},
+ {{ 0.0071, 0.0074 }, { 0.0693 }},
+ {{ 0.0071, 0.0074 }, { 0.0740 }},
+ {{ 0.0072, 0.0075 }, { 0.0741 }},
+ {{ 0.0199, 0.0209 }, { 0.1019 }},
+ {{ 0.0296, 0.0306 }, { 0.1213 }},
+ {{ 0.0627, 0.0651 }, { 0.1779 }},
+ {{ 0.0831, 0.0863 }, { 0.2095 }},
+ {{ 0.1091, 0.1134 }, { 0.2487 }},
+ {{ 0.1442, 0.1497 }, { 0.2949 }},
+ {{ 0.1745, 0.1814 }, { 0.3360 }},
+ {{ 0.1747, 0.1816 }, { 0.3367 }},
+ {{ 0.1747, 0.1816 }, { 0.3364 }},
+ {{ 0.1748, 0.1816 }, { 0.3355 }},
+ {{ 0.1749, 0.1817 }, { 0.3344 }},
+ {{ 0.1748, 0.1817 }, { 0.3356 }},
+ {{ 0.1748, 0.1817 }, { 0.3354 }},
+ {{ 0.1749, 0.1817 }, { 0.3361 }},
+ {{ 0.1749, 0.1818 }, { 0.3368 }},
+ {{ 0.1749, 0.1818 }, { 0.3335 }},
+ {{ 0.1750, 0.1818 }, { 0.3367 }},
+ {{ 0.1750, 0.1819 }, { 0.3362 }},
+ {{ 0.1750, 0.1819 }, { 0.3359 }},
+ {{ 0.1751, 0.1820 }, { 0.3354 }},
+ {{ 0.1752, 0.1821 }, { 0.3355 }},
+ {{ 0.1754, 0.1823 }, { 0.3369 }},
+ {{ 0.1756, 0.1824 }, { 0.3360 }},
+ {{ 0.2743, 0.2842 }, { 0.4381 }},
+ {{ 0.3289, 0.3411 }, { 0.4922 }},
+ {{ 0.4036, 0.4184 }, { 0.5617 }},
+ {{ 0.4689, 0.4854 }, { 0.6147 }},
+ {{ 0.5379, 0.5567 }, { 0.6709 }},
+ {{ 0.7137, 0.7420 }, { 0.8045 }},
+ {{ 0.8730, 0.9105 }, { 0.9150 }},
+ {{ 0.8738, 0.9113 }, { 0.9141 }},
+ {{ 0.8741, 0.9116 }, { 0.9120 }},
+ {{ 0.8744, 0.9118 }, { 0.9173 }},
+ {{ 0.8748, 0.9123 }, { 0.9219 }},
+ {{ 0.8748, 0.9123 }, { 0.9133 }},
+ {{ 0.8748, 0.9124 }, { 0.9210 }},
+ {{ 0.8751, 0.9127 }, { 0.9207 }},
+ {{ 0.8751, 0.9127 }, { 0.9225 }},
+ {{ 0.8754, 0.9130 }, { 0.9137 }},
+ {{ 0.8757, 0.9133 }, { 0.9219 }},
+ {{ 0.8759, 0.9135 }, { 0.9166 }},
+ {{ 0.8761, 0.9137 }, { 0.9162 }},
+ {{ 0.8759, 0.9137 }, { 0.9151 }},
+ {{ 0.8765, 0.9141 }, { 0.9167 }}
+};
+
+#ifdef NEVER
+#ifdef __STDC__
+#include <stdarg.h>
+void error(char *fmt, ...), warning(char *fmt, ...), verbose(int level, char *fmt, ...);
+#else
+#include <varargs.h>
+void error(), warning(), verbose();
+#endif
+#endif /* NEVER */
+
+void write_rgb_tiff(char *name, int width, int height, unsigned char *data);
+
+void usage(void) {
+ fprintf(stderr,"Test 2D rspl interpolation\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: t2d [options]\n");
+ fprintf(stderr," -t n Test set:\n");
+ fprintf(stderr," * 1 = 1D curve along x = 0.5\n");
+ fprintf(stderr," 2 = x + y^2 with nonmon point\n");
+ fprintf(stderr," 3 = x + y^2\n");
+ fprintf(stderr," 4 = arbitrary11\n");
+ fprintf(stderr," 5 = 1D line of 3 points\n");
+ fprintf(stderr," 6 = same value 11 points\n");
+ fprintf(stderr," 7 = same value 3 points\n");
+ fprintf(stderr," 8 = C + M printer L* values\n");
+ fprintf(stderr," 9 = C + M printer a* values\n");
+ fprintf(stderr," 10 = C + M printer b* values\n");
+ fprintf(stderr," 11 = Points up to edge test\n");
+ fprintf(stderr," 12 = Four points with high smoothing\n");
+ fprintf(stderr," 13 = Neutral axis extrapolation\n");
+ fprintf(stderr," -r resx,resy Set grid resolutions (def %d %d)\n",GRES0,GRES1);
+ fprintf(stderr," -h Test half scale resolution too\n");
+ fprintf(stderr," -q Test quarter scale resolution too\n");
+ fprintf(stderr," -2 Use two pass smoothing\n");
+ fprintf(stderr," -x Use extra fitting\n");
+ fprintf(stderr," -s Test symetric smoothness (set asymetric -r !)\n");
+ fprintf(stderr," -S Test spline interpolation\n");
+ fprintf(stderr," -p plot 3 slices, x = 0.5, y = 0.5, x = y\n");
+ fprintf(stderr," -P x1:y1:x2:y2 plot a slice from x1,y1 to x2,y2\n");
+ fprintf(stderr," -m No red point markers in TIFF\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ rspl *rss; /* Regularized spline structure */
+ rspl *rss2 = NULL; /* Regularized spline structure at half resolution */
+ datai low,high;
+ int gres[MXDI];
+ int gres2[MXDI];
+ double avgdev[MXDO];
+ co *test_points = test_points1;
+ int npoints = sizeof(test_points1)/sizeof(co);
+ int dospline = 0;
+ int twopass = 0;
+ int extra = 0;
+ int dosym = 0;
+ int doplot = 0;
+ double plotpts[2][2]; /* doplot == 2 start/end points */
+ int doh = 0; /* half scale */
+ int doq = 0;
+ int rsv;
+ int flags = RSPL_NOFLAGS;
+ int markers = 1;
+ double smooth = 1.0;
+
+ low[0] = 0.0;
+ low[1] = 0.0;
+ high[0] = 1.0;
+ high[1] = 1.0;
+ gres[0] = GRES0;
+ gres[1] = GRES1;
+ avgdev[0] = 0.0;
+ avgdev[1] = 0.0;
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ /* test set */
+ else if (argv[fa][1] == 't' || argv[fa][1] == 'T') {
+ int ix;
+ fa = nfa;
+ if (na == NULL) usage();
+ ix = atoi(na);
+ switch (ix) {
+ case 1:
+ test_points = test_points1;
+ npoints = sizeof(test_points1)/sizeof(co);
+ break;
+ case 2:
+ test_points = test_points2;
+ npoints = sizeof(test_points2)/sizeof(co);
+ break;
+ case 3:
+ test_points = test_points3;
+ npoints = sizeof(test_points3)/sizeof(co);
+ break;
+ case 4:
+ test_points = test_points4;
+ npoints = sizeof(test_points4)/sizeof(co);
+ break;
+ case 5:
+ test_points = test_points5;
+ npoints = sizeof(test_points5)/sizeof(co);
+ break;
+ case 6:
+ test_points = test_points6;
+ npoints = sizeof(test_points6)/sizeof(co);
+ break;
+ case 7:
+ test_points = test_points7;
+ npoints = sizeof(test_points7)/sizeof(co);
+ break;
+ case 8:
+ test_points = test_points8;
+ npoints = sizeof(test_points8)/sizeof(co);
+ break;
+ case 9:
+ test_points = test_points9;
+ npoints = sizeof(test_points9)/sizeof(co);
+ break;
+ case 10:
+ test_points = test_points10;
+ npoints = sizeof(test_points10)/sizeof(co);
+ break;
+ case 11: {
+ int i;
+ test_points = test_points11;
+ npoints = sizeof(test_points11)/sizeof(co);
+ for (i = 0; i < npoints; i++) {
+ test_points[i].v[0] = test_f11(test_points[i].p[0],test_points[i].p[1]);
+ }
+ break;
+ case 12:
+ test_points = test_points12;
+ npoints = sizeof(test_points12)/sizeof(co);
+// smooth = -100000.0;
+// smooth = 1e-4;
+ avgdev[0] = 0.1;
+ avgdev[1] = 0.1;
+ break;
+ case 13:
+ test_points = test_points13;
+ npoints = sizeof(test_points13)/sizeof(co);
+ break;
+ }
+ default:
+ usage();
+ }
+
+ } else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ fa = nfa;
+ if (na == NULL) usage();
+ if (sscanf(na, " %d,%d ", &gres[0], &gres[1]) != 2)
+ usage();
+
+ } else if (argv[fa][1] == 'h' || argv[fa][1] == 'H') {
+ doh = 1;
+
+ } else if (argv[fa][1] == 'q' || argv[fa][1] == 'Q') {
+ doh = 1;
+ doq = 1;
+
+ } else if (argv[fa][1] == 'p') {
+ doplot = 1;
+
+ } else if (argv[fa][1] == 'P') {
+ doplot = 2;
+ fa = nfa;
+ if (na == NULL) usage();
+ if (sscanf(na,"%lf:%lf:%lf:%lf",&plotpts[0][0],&plotpts[0][1],&plotpts[1][0],&plotpts[1][1]) != 4) {
+ usage();
+ }
+
+ } else if (argv[fa][1] == 'S') {
+ dospline = 1;
+
+ } else if (argv[fa][1] == '2') {
+ twopass = 1;
+
+ } else if (argv[fa][1] == 'x' || argv[fa][1] == 'X') {
+ extra = 1;
+
+ } else if (argv[fa][1] == 's') {
+ dosym = 1;
+
+ } else if (argv[fa][1] == 'm') {
+ markers = 0;
+
+ } else
+ usage();
+ } else
+ break;
+ }
+
+
+ if (twopass)
+ flags |= RSPL_2PASSSMTH;
+
+ if (extra)
+ flags |= RSPL_EXTRAFIT2;
+
+ if (dosym)
+ flags |= RSPL_SYMDOMAIN;
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS, 2, 1);
+
+ /* Fit to scattered data */
+ rss->fit_rspl(rss,
+ flags, /* Non-mon and clip flags */
+ test_points, /* Test points */
+ npoints, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ smooth, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+ if (doh) {
+
+ if (doq) {
+ gres2[0] = gres[0]/4;
+ gres2[1] = gres[1]/4;
+ } else {
+ gres2[0] = gres[0]/2;
+ gres2[1] = gres[1]/2;
+ }
+
+ rss2 = new_rspl(RSPL_NOFLAGS, 2, 1);
+
+ /* Fit to scattered data */
+ rss2->fit_rspl(rss2,
+ flags, /* Non-mon and clip flags */
+ test_points, /* Test points */
+ npoints, /* Number of test points */
+ low, high, gres2, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ 1.0, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+ }
+
+ /* Plot the interpolation in 2D */
+ for (rsv = 0; rsv <= doh; rsv++) {
+ double x1 = -0.2; /* Plot range */
+ double x2 = 1.2;
+ double y1 = -0.2;
+ double y2 = 1.2;
+ double min = -0.0;
+ double max = 1.0;
+ rspl *rs;
+ unsigned char pa[HEIGHT][WIDTH][3];
+ co tco; /* Test point */
+ double sx,sy;
+ int i,j,k;
+
+ if (rsv == 0)
+ rs = rss;
+ else
+ rs = rss2;
+
+ sx = (x2 - x1)/(double)WIDTH;
+ sy = (y2 - y1)/(double)HEIGHT;
+
+ for (j=0; j < HEIGHT; j++) {
+ tco.p[1] = (double)((HEIGHT-1) - j) * sy + y1;
+ for (i=0; i < WIDTH; i++) {
+ tco.p[0] = (double)i * sx + x1;
+ if ((dospline && rs->spline_interp(rs, &tco))
+ || (!dospline && rs->interp(rs, &tco))) {
+ pa[j][i][0] = 0; /* Out of bounds in green */
+ pa[j][i][1] = 100;
+ pa[j][i][2] = 0;
+ } else {
+ int m;
+ /* printf("%d %d, %f %f returned %f\n",i,j,tco.p[0],tco.p[1],tco.v[0]); */
+ m = (int)((255.0 * (tco.v[0] - min)/(max - min)) + 0.5);
+ if (m < 0) {
+ pa[j][i][0] = 20; /* Dark blue */
+ pa[j][i][1] = 20;
+ pa[j][i][2] = 50;
+ } else if (m > 255) {
+ pa[j][i][0] = 230; /* Light blue */
+ pa[j][i][1] = 230;
+ pa[j][i][2] = 255;
+ } else {
+ pa[j][i][0] = m; /* Level in grey */
+ pa[j][i][1] = m;
+ pa[j][i][2] = m;
+ }
+ }
+ }
+ }
+
+ if (markers) {
+ /* Mark verticies in red */
+ for(k = 0; k < npoints; k++) {
+ j = (int)((HEIGHT * (y2 - test_points[k].p[1])/(y2 - y1)) + 0.5);
+ i = (int)((WIDTH * (test_points[k].p[0] - x1)/(x2 - x1)) + 0.5);
+ pa[j][i][0] = 255;
+ pa[j][i][1] = 0;
+ pa[j][i][2] = 0;
+ }
+ }
+ write_rgb_tiff(rsv == 0 ? "t2d.tif" : "t2dh.tif" ,WIDTH,HEIGHT,(unsigned char *)pa);
+ }
+
+ /* Plot out 3 slices */
+ if (doplot == 1) {
+ int slice;
+
+ for (slice = 0; slice < 3; slice++) {
+ co tp; /* Test point */
+ double x[PLOTRES];
+ double ya[PLOTRES];
+ double yb[PLOTRES];
+ double xx,yy;
+ double x1,x2,y1,y2;
+ double sx,sy;
+ int i,n;
+
+ /* Set up slice to plot */
+ if (slice == 0) {
+ printf("Slice along x = 0.5\n");
+ x1 = 0.5; y1 = 0.0;
+ x2 = 0.5; y2 = 1.0;
+ n = PLOTRES;
+ } else if (slice == 1) {
+ printf("Slice along y = 0.5\n");
+ x1 = 0.0; y1 = 0.5;
+ x2 = 1.0; y2 = 0.5;
+ n = PLOTRES;
+ } else {
+ printf("Slice along x = y\n");
+ x1 = 0.0; y1 = 0.0;
+ x2 = 1.0; y2 = 1.0;
+ n = PLOTRES;
+ }
+
+ sx = (x2 - x1)/n;
+ sy = (y2 - y1)/n;
+
+ xx = x1;
+ yy = y1;
+ for (i = 0; i < n; i++) {
+ double vv = i/(n-1.0);
+ x[i] = vv;
+ tp.p[0] = xx;
+ tp.p[1] = yy;
+
+ if ((dospline && rss->spline_interp(rss, &tp))
+ || (!dospline && rss->interp(rss, &tp)))
+ tp.v[0] = -0.1;
+ ya[i] = tp.v[0];
+
+ if (doh) {
+ if ((dospline && rss2->spline_interp(rss2, &tp))
+ || (!dospline && rss2->interp(rss2, &tp)))
+ tp.v[0] = -0.1;
+ yb[i] = tp.v[0];
+ }
+
+ xx += sx;
+ yy += sy;
+ }
+
+ /* Plot the result */
+ if (doh)
+ do_plot(x,ya,yb,NULL,n);
+ else
+ do_plot(x,ya,NULL,NULL,n);
+ }
+ } else if (doplot == 2) { /* Plot a given slice */
+ co tp; /* Test point */
+ double x[PLOTRES];
+ double ya[PLOTRES];
+ double yb[PLOTRES];
+ double xx,yy;
+ double x1,x2,y1,y2;
+ double sx,sy;
+ int i,n;
+
+ x1 = plotpts[0][0];
+ y1 = plotpts[0][1];
+ x2 = plotpts[1][0];
+ y2 = plotpts[1][1];
+
+ printf("Slice from %f,%f to %f,%f\n",x1,y1,x2,y2);
+ n = PLOTRES;
+
+ sx = (x2 - x1)/n;
+ sy = (y2 - y1)/n;
+
+ xx = x1;
+ yy = y1;
+ for (i = 0; i < n; i++) {
+ double vv = i/(n-1.0);
+ x[i] = vv;
+ tp.p[0] = xx;
+ tp.p[1] = yy;
+
+ if ((dospline && rss->spline_interp(rss, &tp))
+ || (!dospline && rss->interp(rss, &tp)))
+ tp.v[0] = -0.1;
+ ya[i] = tp.v[0];
+
+ if (doh) {
+ if ((dospline && rss2->spline_interp(rss2, &tp))
+ || (!dospline && rss2->interp(rss2, &tp)))
+ tp.v[0] = -0.1;
+ yb[i] = tp.v[0];
+ }
+
+ xx += sx;
+ yy += sy;
+ }
+
+ /* Plot the result */
+ if (doh)
+ do_plot(x,ya,yb,NULL,n);
+ else
+ do_plot(x,ya,NULL,NULL,n);
+ }
+
+ /* Report the fit */
+ {
+ co tco; /* Test point */
+ int k;
+ double avg = 0;
+ double max = 0.0;
+
+ for(k = 0; k < npoints; k++) {
+ double err;
+ tco.p[0] = test_points[k].p[0];
+ tco.p[1] = test_points[k].p[1];
+ if (dospline)
+ rss->spline_interp(rss, &tco);
+ else
+ rss->interp(rss, &tco);
+
+ err = tco.v[0] - test_points[k].v[0];
+ err = fabs(err);
+
+ avg += err;
+ if (err > max)
+ max = err;
+ }
+ avg /= (double)npoints;
+ printf("Max error %f%%, average %f%%\n",100.0 * max, 100.0 * avg);
+ }
+ return 0;
+}
+
+/* ---------------------- */
+/* Tiff diagnostic output */
+
+void
+write_rgb_tiff(
+char *name,
+int width,
+int height,
+unsigned char *data
+) {
+ int y;
+ unsigned char *dp;
+ TIFF *tif;
+
+ if ((tif = TIFFOpen(name, "w")) == NULL) {
+ fprintf(stderr,"Failed to open output TIFF file '%s'\n",name);
+ exit (-1);
+ }
+
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+
+ for (dp = data, y = 0; y < height; y++, dp += 3 * width) {
+ if (TIFFWriteScanline(tif, (tdata_t)dp, y, 0) < 0) {
+ fprintf(stderr,"WriteScanline Failed at line %d\n",y);
+ exit (-1);
+ }
+ }
+ (void) TIFFClose(tif);
+}
+
+#ifdef NEVER
+/******************************************************************/
+/* Error/debug output routines */
+/******************************************************************/
+
+/* Basic printf type error() and warning() routines */
+
+#ifdef __STDC__
+void
+error(char *fmt, ...)
+#else
+void
+error(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Error - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stdout);
+ exit (-1);
+}
+
+#ifdef __STDC__
+void
+warning(char *fmt, ...)
+#else
+void
+warning(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Warning - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+#ifdef __STDC__
+void
+verbose(int level, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+#else
+verbose(va_alist)
+va_dcl
+{
+ va_list args;
+ int level;
+ char *fmt;
+ va_start(args);
+ level = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+ if (verbose_level >= level)
+ {
+ fprintf(verbose_out,"cmatch: ");
+ vfprintf(verbose_out, fmt, args);
+ fprintf(verbose_out, "\n");
+ fflush(verbose_out);
+ }
+ va_end(args);
+}
+#endif /* NEVER */
diff --git a/rspl/t2ddf.c b/rspl/t2ddf.c
new file mode 100644
index 0000000..92e7e3f
--- /dev/null
+++ b/rspl/t2ddf.c
@@ -0,0 +1,517 @@
+/************************************************/
+/* Test RSPL in 2D with a weak default function */
+/************************************************/
+
+/* Author: Graeme Gill
+ * Date: 20/11/2005
+ * Derived from cmatch.c
+ * Copyright 1995, 2005 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+
+#define DEBUG
+#define DETAILED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include "rspl.h"
+#include "tiffio.h"
+#include "plot.h"
+
+#ifdef NEVER
+#define INTERP spline_interp
+#else
+#define INTERP interp
+#endif
+
+#ifdef NEVER
+FILE *verbose_out = stdout;
+int verbose_level = 6; /* Current verbosity level */
+ /* 0 = none */
+ /* !0 = diagnostics */
+#endif /* NEVER */
+
+/* rspl flags */
+#define FLAGS (0 /* | RSPL_EXTRAFIT */)
+
+#define PLOTRES 256
+#define WIDTH 400 /* Raster size */
+#define HEIGHT 400
+
+#define MAX_ITS 500
+#define IT_TOL 0.0005
+#define GRES0 25 /* Default resolutions */
+#define GRES1 25
+#undef NEVER
+#define ALWAYS
+
+/* two correction points along x = 0.5 */
+co test_points1[] = {
+// {{ 0.5,0.325 },{ 0.4 }}, /* 0 */
+// {{ 0.5,0.625 },{ 0.70 }} /* 1 */
+ {{ 0.4,0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.4,0.625 },{ 0.8 }}, /* 1 */
+ {{ 0.5,0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.5,0.625 },{ 0.8 }}, /* 1 */
+ {{ 0.6,0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.6,0.625 },{ 0.8 }} /* 1 */
+};
+
+#ifdef NEVER
+#ifdef __STDC__
+#include <stdarg.h>
+void error(char *fmt, ...), warning(char *fmt, ...), verbose(int level, char *fmt, ...);
+#else
+#include <varargs.h>
+void error(), warning(), verbose();
+#endif
+#endif /* NEVER */
+
+void write_rgb_tiff(char *name, int width, int height, unsigned char *data);
+
+/* Weak default function */
+/* Linear along y, independent of x */
+static void wfunc(void *cbntx, double *out, double *in) {
+ out[0] = in[1];
+}
+
+void usage(void) {
+ fprintf(stderr,"Test 2D rspl interpolation with weak default function\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: t2d [options]\n");
+ fprintf(stderr," -t n Test set:\n");
+ fprintf(stderr," * 1 = two points along x\n");
+ fprintf(stderr," -w wweight Set weak default function weight (default 1.0)\n");
+ fprintf(stderr," -r resx,resy Set grid resolutions (def %d %d)\n",GRES0,GRES1);
+ fprintf(stderr," -h Test half scale resolution too\n");
+ fprintf(stderr," -q Test quarter scale resolution too\n");
+ fprintf(stderr," -s Test symetric smoothness\n");
+ fprintf(stderr," -p plot 3 slices, x = 0.5, y = 0.5, x = y\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ rspl *rss; /* Regularized spline structure */
+ rspl *rss2 = NULL; /* Regularized spline structure at half resolution */
+ datai low,high;
+ int gres[MXDI];
+ int gres2[MXDI];
+ double avgdev[MXDO];
+ co *test_points = test_points1;
+ int npoints = sizeof(test_points1)/sizeof(co);
+ double wweight = 1.0;
+ int dosym = 0;
+ int doplot = 0;
+ int doh = 0;
+ int doq = 0;
+ int rsv;
+ int flags = FLAGS;
+
+ low[0] = 0.0;
+ low[1] = 0.0;
+ high[0] = 1.0;
+ high[1] = 1.0;
+ gres[0] = GRES0;
+ gres[1] = GRES1;
+ avgdev[0] = 0.0;
+ avgdev[1] = 0.0;
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ /* test set */
+ else if (argv[fa][1] == 't' || argv[fa][1] == 'T') {
+ int ix;
+ fa = nfa;
+ if (na == NULL) usage();
+ ix = atoi(na);
+ switch (ix) {
+ case 1:
+ test_points = test_points1;
+ npoints = sizeof(test_points1)/sizeof(co);
+ break;
+ default:
+ usage();
+ }
+
+ } else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
+ fa = nfa;
+ if (na == NULL) usage();
+ wweight = atof(na);
+
+ } else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ fa = nfa;
+ if (na == NULL) usage();
+ if (sscanf(na, " %d,%d ", &gres[0], &gres[1]) != 2)
+ usage();
+
+ } else if (argv[fa][1] == 'h' || argv[fa][1] == 'H') {
+ doh = 1;
+
+ } else if (argv[fa][1] == 'q' || argv[fa][1] == 'Q') {
+ doh = 1;
+ doq = 1;
+
+ } else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
+ doplot = 1;
+
+ } else if (argv[fa][1] == 's' || argv[fa][1] == 'S') {
+ dosym = 1;
+
+ } else
+ usage();
+ } else
+ break;
+ }
+
+
+ if (dosym)
+ flags |= RSPL_SYMDOMAIN;
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS, 2, 1);
+
+ /* Fit to scattered data */
+ rss->fit_rspl_df(rss,
+ flags, /* Non-mon and clip flags */
+ test_points, /* Test points */
+ npoints, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ 1.0, /* Smoothing */
+ avgdev, /* Average Deviation */
+ NULL, /* iwidth */
+ wweight, /* weak function weight */
+ NULL, /* No context */
+ wfunc /* Weak function */
+ );
+
+ if (doh) {
+
+ if (doq) {
+ gres2[0] = gres[0]/4;
+ gres2[1] = gres[1]/4;
+ } else {
+ gres2[0] = gres[0]/2;
+ gres2[1] = gres[1]/2;
+ }
+
+ rss2 = new_rspl(RSPL_NOFLAGS, 2, 1);
+
+ /* Fit to scattered data */
+ rss2->fit_rspl_df(rss2,
+ flags, /* Non-mon and clip flags */
+ test_points, /* Test points */
+ npoints, /* Number of test points */
+ low, high, gres2, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ 1.0, /* Smoothing */
+ avgdev, /* Average Deviation */
+ NULL, /* iwidth */
+ wweight, /* weak function weight */
+ NULL, /* No context */
+ wfunc /* Weak function */
+ );
+ }
+
+ /* Test the interpolation in 2D */
+ for (rsv = 0; rsv <= doh; rsv++) {
+ double x1 = -0.2;
+ double x2 = 1.2;
+ double y1 = -0.2;
+ double y2 = 1.2;
+ double min = -0.0;
+ double max = 1.0;
+ rspl *rs;
+ unsigned char pa[HEIGHT][WIDTH][3];
+ co tco; /* Test point */
+ double sx,sy;
+ int i,j,k;
+
+ if (rsv == 0)
+ rs = rss;
+ else
+ rs = rss2;
+
+ sx = (x2 - x1)/(double)WIDTH;
+ sy = (y2 - y1)/(double)HEIGHT;
+
+ for (j=0; j < HEIGHT; j++) {
+ tco.p[1] = (double)((HEIGHT-1) - j) * sy + y1;
+ for (i=0; i < WIDTH; i++) {
+ tco.p[0] = (double)i * sx + x1;
+ if (rs->INTERP(rs, &tco)) {
+ pa[j][i][0] = 0; /* Out of bounds in green */
+ pa[j][i][1] = 100;
+ pa[j][i][2] = 0;
+ } else {
+ int m;
+ /* printf("%d %d, %f %f returned %f\n",i,j,tco.p[0],tco.p[1],tco.v[0]); */
+ m = (int)((255.0 * (tco.v[0] - min)/(max - min)) + 0.5);
+ if (m < 0) {
+ pa[j][i][0] = 20; /* Dark blue */
+ pa[j][i][1] = 20;
+ pa[j][i][2] = 50;
+ } else if (m > 255) {
+ pa[j][i][0] = 230; /* Light blue */
+ pa[j][i][1] = 230;
+ pa[j][i][2] = 255;
+ } else {
+ pa[j][i][0] = m; /* Level in grey */
+ pa[j][i][1] = m;
+ pa[j][i][2] = m;
+ }
+ }
+ }
+ }
+
+ /* Mark verticies in red */
+ for(k = 0; k < npoints; k++) {
+ j = (int)((HEIGHT * (y2 - test_points[k].p[1])/(y2 - y1)) + 0.5);
+ i = (int)((WIDTH * (test_points[k].p[0] - x1)/(x2 - x1)) + 0.5);
+ pa[j][i][0] = 255;
+ pa[j][i][1] = 0;
+ pa[j][i][2] = 0;
+ }
+ write_rgb_tiff(rsv == 0 ? "t2d.tif" : "t2dh.tif" ,WIDTH,HEIGHT,(unsigned char *)pa);
+ }
+
+ /* Plot out 3 slices */
+ if (doplot) {
+ int slice;
+
+ for (slice = 0; slice < 3; slice++) {
+ co tp; /* Test point */
+ double x[PLOTRES];
+ double ya[PLOTRES];
+ double yb[PLOTRES];
+ double xx,yy;
+ double x1,x2,y1,y2;
+ double sx,sy;
+ int i,n;
+
+ /* Set up slice to plot */
+ if (slice == 0) {
+ x1 = 0.5; y1 = 0.0;
+ x2 = 0.5; y2 = 1.0;
+ n = PLOTRES;
+ printf("Plot along y at x = 0.5\n");
+ } else if (slice == 1) {
+ x1 = 0.0; y1 = 0.5;
+ x2 = 1.0; y2 = 0.5;
+ n = PLOTRES;
+ printf("Plot along x at y = 0.5\n");
+ } else {
+ x1 = 0.0; y1 = 0.0;
+ x2 = 1.0; y2 = 1.0;
+ n = PLOTRES;
+ printf("Plot along x = y\n");
+ }
+
+ sx = (x2 - x1)/n;
+ sy = (y2 - y1)/n;
+
+ xx = x1;
+ yy = y1;
+ for (i = 0; i < n; i++) {
+ double vv = i/(n-1.0);
+ x[i] = vv;
+ tp.p[0] = xx;
+ tp.p[1] = yy;
+
+ if (rss->INTERP(rss, &tp))
+ tp.v[0] = -0.1;
+ ya[i] = tp.v[0];
+
+ if (doh) {
+ if (rss2->INTERP(rss2, &tp))
+ tp.v[0] = -0.1;
+ yb[i] = tp.v[0];
+ }
+
+ xx += sx;
+ yy += sy;
+ }
+
+ /* Plot the result */
+ if (doh)
+ do_plot(x,ya,yb,NULL,n);
+ else
+ do_plot(x,ya,NULL,NULL,n);
+ }
+ }
+
+ /* Report the fit */
+ {
+ co tco; /* Test point */
+ int k;
+ double avg = 0;
+ double max = 0.0;
+
+ for(k = 0; k < npoints; k++) {
+ double err;
+ tco.p[0] = test_points[k].p[0];
+ tco.p[1] = test_points[k].p[1];
+ rss->INTERP(rss, &tco);
+
+ err = tco.v[0] - test_points[k].v[0];
+ err = fabs(err);
+
+ avg += err;
+ if (err > max)
+ max = err;
+ }
+ avg /= (double)npoints;
+ printf("Max error %f%%, average %f%%\n",100.0 * max, 100.0 * avg);
+ }
+ return 0;
+}
+
+/* ---------------------- */
+/* Tiff diagnostic output */
+
+void
+write_rgb_tiff(
+char *name,
+int width,
+int height,
+unsigned char *data
+) {
+ int y;
+ unsigned char *dp;
+ TIFF *tif;
+
+ if ((tif = TIFFOpen(name, "w")) == NULL) {
+ fprintf(stderr,"Failed to open output TIFF file '%s'\n",name);
+ exit (-1);
+ }
+
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+
+ for (dp = data, y = 0; y < height; y++, dp += 3 * width) {
+ if (TIFFWriteScanline(tif, (tdata_t)dp, y, 0) < 0) {
+ fprintf(stderr,"WriteScanline Failed at line %d\n",y);
+ exit (-1);
+ }
+ }
+ (void) TIFFClose(tif);
+}
+
+#ifdef NEVER
+/******************************************************************/
+/* Error/debug output routines */
+/******************************************************************/
+
+/* Basic printf type error() and warning() routines */
+
+#ifdef __STDC__
+void
+error(char *fmt, ...)
+#else
+void
+error(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Error - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stdout);
+ exit (-1);
+}
+
+#ifdef __STDC__
+void
+warning(char *fmt, ...)
+#else
+void
+warning(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Warning - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+#ifdef __STDC__
+void
+verbose(int level, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+#else
+verbose(va_alist)
+va_dcl
+{
+ va_list args;
+ int level;
+ char *fmt;
+ va_start(args);
+ level = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+ if (verbose_level >= level)
+ {
+ fprintf(verbose_out,"cmatch: ");
+ vfprintf(verbose_out, fmt, args);
+ fprintf(verbose_out, "\n");
+ fflush(verbose_out);
+ }
+ va_end(args);
+}
+#endif /* NEVER */
diff --git a/rspl/t3d.c b/rspl/t3d.c
new file mode 100644
index 0000000..1935545
--- /dev/null
+++ b/rspl/t3d.c
@@ -0,0 +1,905 @@
+/************************************************/
+/* Test RSPL in 3D */
+/************************************************/
+
+/* Author: Graeme Gill
+ * Date: 22/4/96
+ * Derived from cmatch.c
+ * Copyright 1995 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+
+#define DEBUG
+#define DETAILED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include "rspl.h"
+#include "tiffio.h"
+#include "plot.h"
+
+#ifdef NEVER
+#define INTERP spline_interp
+#else
+#define INTERP interp
+#endif
+
+#ifdef NEVER
+FILE *verbose_out = stdout;
+int verbose_level = 6; /* Current verbosity level */
+ /* 0 = none */
+ /* !0 = diagnostics */
+#endif /* NEVER */
+
+#define PLOTRES 256
+#define WIDTH 400 /* Raster size */
+#define HEIGHT 400
+
+#define MAX_ITS 500
+#define IT_TOL 0.0005
+#define GRES0 33 /* Default rspl resolutions */
+#define GRES1 33
+#define GRES2 33
+#undef NEVER
+#define ALWAYS
+
+//double t1xa[PNTS] = { 0.2, 0.25, 0.30, 0.35, 0.40, 0.44, 0.48, 0.51, 0.64, 0.75 };
+//double t1ya[PNTS] = { 0.3, 0.35, 0.4, 0.41, 0.42, 0.46, 0.5, 0.575, 0.48, 0.75 };
+
+/* 1D test function repeated 3 times along x = y = 0.5 */
+co test_points1[] = {
+ {{ 0.4,0.4,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.4,0.4,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.4,0.4,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.4,0.4,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.4,0.4,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.4,0.4,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.4,0.4,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.4,0.4,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.4,0.4,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.4,0.4,0.75 },{ 0.75 }}, /* 9 */
+
+ {{ 0.5,0.4,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.5,0.4,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.5,0.4,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.5,0.4,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.5,0.4,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.5,0.4,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.5,0.4,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.5,0.4,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.5,0.4,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.5,0.4,0.75 },{ 0.75 }}, /* 9 */
+
+ {{ 0.6,0.4,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.6,0.4,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.6,0.4,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.6,0.4,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.6,0.4,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.6,0.4,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.6,0.4,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.6,0.4,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.6,0.4,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.6,0.4,0.75 },{ 0.75 }}, /* 9 */
+
+
+ {{ 0.4,0.5,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.4,0.5,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.4,0.5,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.4,0.5,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.4,0.5,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.4,0.5,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.4,0.5,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.4,0.5,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.4,0.5,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.4,0.5,0.75 },{ 0.75 }}, /* 9 */
+
+ {{ 0.5,0.5,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.5,0.5,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.5,0.5,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.5,0.5,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.5,0.5,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.5,0.5,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.5,0.5,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.5,0.5,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.5,0.5,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.5,0.5,0.75 },{ 0.75 }}, /* 9 */
+
+ {{ 0.6,0.5,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.6,0.5,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.6,0.5,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.6,0.5,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.6,0.5,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.6,0.5,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.6,0.5,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.6,0.5,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.6,0.5,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.6,0.5,0.75 },{ 0.75 }}, /* 9 */
+
+
+ {{ 0.4,0.6,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.4,0.6,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.4,0.6,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.4,0.6,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.4,0.6,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.4,0.6,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.4,0.6,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.4,0.6,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.4,0.6,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.4,0.6,0.75 },{ 0.75 }}, /* 9 */
+
+ {{ 0.5,0.6,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.5,0.6,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.5,0.6,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.5,0.6,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.5,0.6,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.5,0.6,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.5,0.6,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.5,0.6,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.5,0.6,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.5,0.6,0.75 },{ 0.75 }}, /* 9 */
+
+ {{ 0.6,0.6,0.20 },{ 0.30 }}, /* 0 */
+ {{ 0.6,0.6,0.25 },{ 0.35 }}, /* 1 */
+ {{ 0.6,0.6,0.30 },{ 0.40 }}, /* 2 */
+ {{ 0.6,0.6,0.35 },{ 0.41 }}, /* 3 */
+ {{ 0.6,0.6,0.40 },{ 0.42 }}, /* 4 */
+ {{ 0.6,0.6,0.44 },{ 0.46 }}, /* 5 */
+ {{ 0.6,0.6,0.48 },{ 0.50 }}, /* 6 */
+ {{ 0.6,0.6,0.51 },{ 0.575 }}, /* 7 */
+ {{ 0.6,0.6,0.64 },{ 0.48 }}, /* 8 */
+ {{ 0.6,0.6,0.75 },{ 0.75 }} /* 9 */
+};
+
+/* function */
+co test_points2[] = {
+ {{ 0.50915, 0.50936, 0.57048 },{ 0.36209169635 }},
+ {{ 0.85943, 0.84331, 0.81487 },{ 0.91571493013 }},
+ {{ 0.11381, 0.80378, 0.82951 },{ 0.16052707023 }},
+ {{ 0.79087, 0.11157, 0.83913 },{ 0.30641388193 }},
+ {{ 0.16297, 0.23090, 0.96417 },{ 0.15005479047 }},
+ {{ 0.78181, 0.80097, 0.00192 },{ 0.32179402798 }},
+ {{ 0.16141, 0.84321, 0.16561 },{ 0.14446082013 }},
+ {{ 0.79859, 0.11111, 0.15547 },{ -0.1308162293 }},
+ {{ 0.12959, 0.16184, 0.21825 },{ 0.03520247555 }},
+ {{ 1.00000, 0.46395, 0.84399 },{ 0.63914200000 }},
+ {{ 0.48724, 0.76024, 1.00000 },{ 0.64150992880 }},
+ {{ 0.40744, 0.00000, 0.65533 },{ 0.13060244736 }},
+ {{ 0.10931, 0.43216, 0.25195 },{ 0.06329759515 }},
+ {{ 0.69401, 0.99412, 0.55335 },{ 0.75632862795 }},
+ {{ 0.51759, 0.30372, 0.13622 },{ 0.07965761859 }},
+ {{ 0.98628, 0.40857, 0.47688 },{ 0.29286006552 }},
+ {{ 0.44474, 0.26024, 1.00000 },{ 0.37263430380 }},
+ {{ 0.15229, 0.52694, 0.75101 },{ 0.16014862087 }},
+ {{ 0.50968, 0.74522, 0.14058 },{ 0.30725752992 }},
+ {{ 0.82291, 0.10614, 0.49956 },{ 0.07762756903 }},
+ {{ 0.15674, 0.78167, 0.50766 },{ 0.17389174472 }},
+ {{ 0.12961, 0.17234, 0.65990 },{ 0.08236132255 }},
+ {{ 1.00000, 0.83202, 0.35206 },{ 0.61366800000 }},
+ {{ 0.36633, 0.89555, 0.76014 },{ 0.48373766601 }},
+ {{ 0.85641, 0.39194, 0.18691 },{ 0.09699956583 }},
+ {{ 0.50918, 0.23923, 0.42132 },{ 0.16380116928 }},
+ {{ 0.65513, 0.40585, 0.83832 },{ 0.49065371733 }},
+ {{ 0.66726, 0.75472, 0.25419 },{ 0.41666516892 }},
+ {{ 0.22193, 0.58555, 0.13920 },{ 0.13003877385 }},
+ {{ 0.41507, 0.89581, 0.25539 },{ 0.37048608609 }},
+ {{ 0.43231, 0.12362, 0.17297 },{ 0.01981752271 }},
+ {{ 0.78191, 0.63297, 0.72639 },{ 0.64361123257 }}
+};
+
+co test_points3[] = {
+ {{ 0.50915, 0.50936, 0.57048 },{ -0.00010 }},
+ {{ 0.85943, 0.84331, 0.81487 },{ 0.46573 }},
+ {{ 0.11381, 0.80378, 0.82951 },{ 0.56085 }},
+ {{ 0.79087, 0.11157, 0.83913 },{ 0.33378 }},
+ {{ 0.16297, 0.23090, 0.96417 },{ 0.38872 }},
+ {{ 0.78181, 0.80097, 0.00192 },{ 0.28654 }},
+ {{ 0.16141, 0.84321, 0.16561 },{ 0.43410 }},
+ {{ 0.79859, 0.11111, 0.15547 },{ 0.35140 }},
+ {{ 0.12959, 0.16184, 0.21825 },{ 0.46711 }},
+ {{ 1.00000, 0.46395, 0.84399 },{ 0.94213 }},
+ {{ 0.48724, 0.76024, 1.00000 },{ 0.00058 }},
+ {{ 0.40744, 0.00000, 0.65533 },{ 0.00859 }},
+ {{ 0.10931, 0.43216, 0.25195 },{ 0.54679 }},
+ {{ 0.69401, 0.99412, 0.55335 },{ 0.12494 }},
+ {{ 0.51759, 0.30372, 0.13622 },{ -0.00126 }},
+ {{ 0.98628, 0.40857, 0.47688 },{ 0.89573 }},
+ {{ 0.44474, 0.26024, 1.00000 },{ 0.00280 }},
+ {{ 0.15229, 0.52694, 0.75101 },{ 0.43807 }},
+ {{ 0.50968, 0.74522, 0.14058 },{ 0.00004 }},
+ {{ 0.82291, 0.10614, 0.49956 },{ 0.40990 }},
+ {{ 0.15674, 0.78167, 0.50766 },{ 0.44271 }},
+ {{ 0.12961, 0.17234, 0.65990 },{ 0.46804 }},
+ {{ 1.00000, 0.83202, 0.35206 },{ 0.90938 }},
+ {{ 0.36633, 0.89555, 0.76014 },{ 0.07020 }},
+ {{ 0.85641, 0.39194, 0.18691 },{ 0.48559 }},
+ {{ 0.50918, 0.23923, 0.42132 },{ -0.00391 }},
+ {{ 0.65513, 0.40585, 0.83832 },{ 0.09449 }},
+ {{ 0.66726, 0.75472, 0.25419 },{ 0.10065 }},
+ {{ 0.22193, 0.58555, 0.13920 },{ 0.28186 }},
+ {{ 0.41507, 0.89581, 0.25539 },{ 0.02877 }},
+ {{ 0.43231, 0.12362, 0.17297 },{ 0.00237 }},
+ {{ 0.78191, 0.63297, 0.72639 },{ 0.29573 }}
+};
+
+
+/* x + y^2 + z^1/3 function with one non-monotonic point */
+co test_points4[] = {
+ {{ 0.1,0.1,0.5 },{ 0.11 }}, /* 0 */
+ {{ 0.2,0.7,0.1 },{ 0.69 }}, /* 1 */
+ {{ 0.8,0.8,0.8 },{ 1.44 }}, /* 2 */
+ {{ 0.5,0.6,0.4 },{ 0.86 }}, /* 3 */
+ {{ 0.2,0.5,0.2 },{ 0.45 }}, /* 4 */
+ {{ 0.3,0.7,0.2 },{ 0.35 }}, /* nm 5 */
+ {{ 0.5,0.4,0.9 },{ 0.66 }}, /* 6 */
+ {{ 0.1,0.9,0.7 },{ 0.91 }}, /* 7 */
+ {{ 0.7,0.2,0.1 },{ 0.74 }}, /* 8 */
+ {{ 0.8,0.4,0.3 },{ 0.96 }}, /* 9 */
+ {{ 0.3,0.3,0.4 },{ 0.39 }} /* 10 */
+ };
+
+/* doubled up x + y^2 + z^1/3 function with one non-monotonic point */
+co test_points5[] = {
+ {{ 0.1,0.1,0.5 },{ 0.11 }}, /* 0 */
+ {{ 0.101,0.101,0.501 },{ 0.11 }}, /* 0d */
+ {{ 0.2,0.7,0.1 },{ 0.69 }}, /* 1 */
+ {{ 0.201,0.701,0.101 },{ 0.69 }}, /* 1d */
+ {{ 0.8,0.8,0.8 },{ 1.44 }}, /* 2 */
+ {{ 0.801,0.801,0.801 },{ 1.44 }}, /* 2d */
+ {{ 0.5,0.6,0.4 },{ 0.86 }}, /* 3 */
+ {{ 0.501,0.601,0.401 },{ 0.86 }}, /* 3d */
+ {{ 0.2,0.5,0.2 },{ 0.45 }}, /* 4 */
+ {{ 0.201,0.501,0.201 },{ 0.45 }}, /* 4d */
+ {{ 0.3,0.7,0.2 },{ 0.35 }}, /* nm 5 */
+ {{ 0.301,0.701,0.201 },{ 0.35 }}, /* nm 5d */
+ {{ 0.5,0.4,0.9 },{ 0.66 }}, /* 6 */
+ {{ 0.501,0.401,0.901 },{ 0.66 }}, /* 6d */
+ {{ 0.1,0.9,0.7 },{ 0.91 }}, /* 7 */
+ {{ 0.101,0.901,0.701 },{ 0.91 }}, /* 7d */
+ {{ 0.7,0.2,0.1 },{ 0.74 }}, /* 8 */
+ {{ 0.701,0.201,0.101 },{ 0.74 }}, /* 8d */
+ {{ 0.8,0.4,0.3 },{ 0.96 }}, /* 9 */
+ {{ 0.801,0.401,0.301 },{ 0.96 }}, /* 9d */
+ {{ 0.3,0.3,0.4 },{ 0.39 }}, /* 10 */
+ {{ 0.301,0.301,0.401 },{ 0.39 }} /* 10d */
+ };
+
+co test_points6[] = {
+ {{ 0.0069, 0.0071, 0.0061 },{ 0.0726 }},
+ {{ 0.0068, 0.0071, 0.0060 },{ 0.0704 }},
+ {{ 0.0069, 0.0072, 0.0062 },{ 0.0720 }},
+ {{ 0.0069, 0.0072, 0.0061 },{ 0.0734 }},
+ {{ 0.0069, 0.0072, 0.0063 },{ 0.0750 }},
+ {{ 0.0070, 0.0072, 0.0062 },{ 0.0779 }},
+ {{ 0.0070, 0.0072, 0.0063 },{ 0.0741 }},
+ {{ 0.0069, 0.0072, 0.0061 },{ 0.0745 }},
+ {{ 0.0069, 0.0072, 0.0061 },{ 0.0747 }},
+ {{ 0.0071, 0.0073, 0.0063 },{ 0.0760 }},
+ {{ 0.0070, 0.0073, 0.0063 },{ 0.0751 }},
+ {{ 0.0070, 0.0073, 0.0062 },{ 0.0759 }},
+ {{ 0.0071, 0.0074, 0.0062 },{ 0.0693 }},
+ {{ 0.0071, 0.0074, 0.0064 },{ 0.0740 }},
+ {{ 0.0072, 0.0075, 0.0064 },{ 0.0741 }},
+ {{ 0.0199, 0.0209, 0.0184 },{ 0.1019 }},
+ {{ 0.0296, 0.0306, 0.0257 },{ 0.1213 }},
+ {{ 0.0627, 0.0651, 0.0548 },{ 0.1779 }},
+ {{ 0.0831, 0.0863, 0.0718 },{ 0.2095 }},
+ {{ 0.1091, 0.1134, 0.0946 },{ 0.2487 }},
+ {{ 0.1442, 0.1497, 0.1227 },{ 0.2949 }},
+ {{ 0.1745, 0.1814, 0.1495 },{ 0.3360 }},
+ {{ 0.1747, 0.1816, 0.1498 },{ 0.3367 }},
+ {{ 0.1747, 0.1816, 0.1496 },{ 0.3364 }},
+ {{ 0.1748, 0.1816, 0.1497 },{ 0.3355 }},
+ {{ 0.1749, 0.1817, 0.1497 },{ 0.3344 }},
+ {{ 0.1748, 0.1817, 0.1498 },{ 0.3356 }},
+ {{ 0.1748, 0.1817, 0.1498 },{ 0.3354 }},
+ {{ 0.1749, 0.1817, 0.1496 },{ 0.3361 }},
+ {{ 0.1749, 0.1818, 0.1498 },{ 0.3368 }},
+ {{ 0.1749, 0.1818, 0.1498 },{ 0.3335 }},
+ {{ 0.1750, 0.1818, 0.1499 },{ 0.3367 }},
+ {{ 0.1750, 0.1819, 0.1500 },{ 0.3362 }},
+ {{ 0.1750, 0.1819, 0.1498 },{ 0.3359 }},
+ {{ 0.1751, 0.1820, 0.1500 },{ 0.3354 }},
+ {{ 0.1752, 0.1821, 0.1501 },{ 0.3355 }},
+ {{ 0.1754, 0.1823, 0.1502 },{ 0.3369 }},
+ {{ 0.1756, 0.1824, 0.1504 },{ 0.3360 }},
+ {{ 0.2743, 0.2842, 0.2367 },{ 0.4381 }},
+ {{ 0.3289, 0.3411, 0.2834 },{ 0.4922 }},
+ {{ 0.4036, 0.4184, 0.3475 },{ 0.5617 }},
+ {{ 0.4689, 0.4854, 0.4020 },{ 0.6147 }},
+ {{ 0.5379, 0.5567, 0.4606 },{ 0.6709 }},
+ {{ 0.7137, 0.7420, 0.6169 },{ 0.8045 }},
+ {{ 0.8730, 0.9105, 0.7433 },{ 0.9150 }},
+ {{ 0.8738, 0.9113, 0.7435 },{ 0.9141 }},
+ {{ 0.8741, 0.9116, 0.7445 },{ 0.9120 }},
+ {{ 0.8744, 0.9118, 0.7443 },{ 0.9173 }},
+ {{ 0.8748, 0.9123, 0.7457 },{ 0.9219 }},
+ {{ 0.8748, 0.9123, 0.7450 },{ 0.9133 }},
+ {{ 0.8748, 0.9124, 0.7445 },{ 0.9210 }},
+ {{ 0.8751, 0.9127, 0.7462 },{ 0.9207 }},
+ {{ 0.8751, 0.9127, 0.7457 },{ 0.9225 }},
+ {{ 0.8754, 0.9130, 0.7454 },{ 0.9137 }},
+ {{ 0.8757, 0.9133, 0.7456 },{ 0.9219 }},
+ {{ 0.8759, 0.9135, 0.7470 },{ 0.9166 }},
+ {{ 0.8761, 0.9137, 0.7469 },{ 0.9162 }},
+ {{ 0.8759, 0.9137, 0.7469 },{ 0.9151 }},
+ {{ 0.8765, 0.9141, 0.7470 },{ 0.9167 }},
+};
+
+
+#ifdef NEVER
+#ifdef __STDC__
+#include <stdarg.h>
+void error(char *fmt, ...), warning(char *fmt, ...), verbose(int level, char *fmt, ...);
+#else
+#include <varargs.h>
+void error(), warning(), verbose();
+#endif
+#endif /* NEVER */
+
+void write_rgb_tiff(char *name, int width, int height, unsigned char *data);
+
+void usage(void) {
+ fprintf(stderr,"Test 3D rspl interpolation\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: t2d [options]\n");
+ fprintf(stderr," -t n Test set:\n");
+ fprintf(stderr," * 1 = 1D test set along x = y = 0.5\n");
+ fprintf(stderr," 2 = Test set 1\n");
+ fprintf(stderr," 3 = Test set 2\n");
+ fprintf(stderr," 4 = x + y^2 + z^1/3 with nonmon point\n");
+ fprintf(stderr," 5 = doubled up x + y^2 + z^1/3 with nonmon point\n");
+ fprintf(stderr," 6 = neutral axis extrapolation\n");
+ fprintf(stderr," -r resx,resy,resz Set grid resolutions (def %d %d %d)\n",GRES0,GRES1,GRES2);
+ fprintf(stderr," -h Test half scale resolution too\n");
+ fprintf(stderr," -q Test quarter scale resolution too\n");
+ fprintf(stderr," -2 Use two pass smoothing\n");
+ fprintf(stderr," -x Use extra fitting\n");
+ fprintf(stderr," -s Test symetric smoothness\n");
+ fprintf(stderr," -p plot 4 slices, xy = 0.5, yz = 0.5, xz = 0.5, x=y=z\n");
+ fprintf(stderr," -P x1:y1:z1:x2:y2:z2 plot slice from x1,y1,z1,x2,y2,z2\n");
+ fprintf(stderr," -S factor smoothing factor (default 1.0)\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ rspl *rss; /* Regularized spline structure */
+ rspl *rss2 = NULL; /* Regularized spline structure at half/quarter resolution */
+ datai low,high;
+ int gres[MXDI];
+ int gres2[MXDI];
+ double avgdev[MXDO];
+ co *test_points = test_points1;
+ int npoints = sizeof(test_points1)/sizeof(co);
+ int dosym = 0;
+ int twopass = 0;
+ int extra = 0;
+ int doplot = 0;
+ double plotpts[2][3]; /* doplot == 2 start/end points */
+ int doh = 0; /* half scale */
+ int doq = 0;
+ int rsv;
+ double smoothf = 1.0;
+ int flags = RSPL_NOFLAGS;
+
+ low[0] = 0.0;
+ low[1] = 0.0;
+ low[2] = 0.0;
+ high[0] = 1.0;
+ high[1] = 1.0;
+ high[2] = 1.0;
+ gres[0] = GRES0;
+ gres[1] = GRES1;
+ gres[2] = GRES2;
+ avgdev[0] = 0.0;
+ avgdev[1] = 0.0;
+ avgdev[2] = 0.0;
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ /* test set */
+ else if (argv[fa][1] == 't' || argv[fa][1] == 'T') {
+ int ix;
+ fa = nfa;
+ if (na == NULL) usage();
+ ix = atoi(na);
+ switch (ix) {
+ case 1:
+ test_points = test_points1;
+ npoints = sizeof(test_points1)/sizeof(co);
+ break;
+ case 2:
+ test_points = test_points2;
+ npoints = sizeof(test_points2)/sizeof(co);
+ break;
+ case 3:
+ test_points = test_points3;
+ npoints = sizeof(test_points3)/sizeof(co);
+ break;
+ case 4:
+ test_points = test_points4;
+ npoints = sizeof(test_points4)/sizeof(co);
+ break;
+ case 5:
+ test_points = test_points5;
+ npoints = sizeof(test_points5)/sizeof(co);
+ break;
+ case 6:
+ test_points = test_points6;
+ npoints = sizeof(test_points6)/sizeof(co);
+ break;
+ default:
+ usage();
+ }
+
+ } else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ fa = nfa;
+ if (na == NULL) usage();
+ if (sscanf(na, " %d,%d,%d ", &gres[0], &gres[1], &gres[2]) != 3)
+ usage();
+
+ } else if (argv[fa][1] == 'h' || argv[fa][1] == 'H') {
+ doh = 1;
+
+ } else if (argv[fa][1] == 'q' || argv[fa][1] == 'Q') {
+ doh = 1;
+ doq = 1;
+
+ } else if (argv[fa][1] == 'p') {
+ doplot = 1;
+
+ } else if (argv[fa][1] == 'P') {
+ doplot = 2;
+ fa = nfa;
+ if (na == NULL) usage();
+ if (sscanf(na,"%lf:%lf:%lf:%lf:%lf:%lf",&plotpts[0][0],&plotpts[0][1],&plotpts[0][2],&plotpts[1][0],&plotpts[1][1],&plotpts[1][2]) != 6) {
+ usage();
+ }
+
+ } else if (argv[fa][1] == 's') {
+ dosym = 1;
+
+ } else if (argv[fa][1] == '2') {
+ twopass = 1;
+
+ } else if (argv[fa][1] == 'x' || argv[fa][1] == 'X') {
+ extra = 1;
+
+ /* smoothing factor */
+ } else if (argv[fa][1] == 'S') {
+ int ix;
+ fa = nfa;
+ if (na == NULL) usage();
+ smoothf = atof(na);
+
+ } else
+ usage();
+ } else
+ break;
+ }
+
+
+ if (twopass)
+ flags |= RSPL_2PASSSMTH;
+
+ if (extra)
+ flags |= RSPL_EXTRAFIT2;
+
+ if (dosym)
+ flags |= RSPL_SYMDOMAIN;
+
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS, 3, 1);
+
+ /* Fit to scattered data */
+ rss->fit_rspl(rss,
+ flags, /* Non-mon and clip flags */
+ test_points, /* Test points */
+ npoints, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ smoothf, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+ if (doh) {
+
+ if (doq) {
+ gres2[0] = gres[0]/4;
+ gres2[1] = gres[1]/4;
+ gres2[2] = gres[2]/4;
+ } else {
+ gres2[0] = gres[0]/2;
+ gres2[1] = gres[1]/2;
+ gres2[2] = gres[2]/2;
+ }
+
+ rss2 = new_rspl(RSPL_NOFLAGS, 3, 1);
+
+ /* Fit to scattered data */
+ rss2->fit_rspl(rss2,
+ flags, /* Non-mon and clip flags */
+ test_points, /* Test points */
+ npoints, /* Number of test points */
+ low, high, gres2, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ smoothf, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+ }
+
+ /* Test the interpolation with a slice in 2D */
+ for (rsv = 0; rsv <= doh; rsv++) {
+ double z[2][2] = { { 0.1, 0.5 } , { 0.5, 0.9 } };
+ double x1 = -0.2;
+ double x2 = 1.2;
+ double y1 = -0.2;
+ double y2 = 1.2;
+ double min = -0.0;
+ double max = 1.0;
+ rspl *rs;
+ unsigned char pa[HEIGHT][WIDTH][3];
+ co tco; /* Test point */
+ double sx,sy;
+ int i,j,k;
+
+ if (rsv == 0)
+ rs = rss;
+ else
+ rs = rss2;
+
+ sx = (x2 - x1)/(double)WIDTH;
+ sy = (y2 - y1)/(double)HEIGHT;
+
+ for (j=0; j < HEIGHT; j++) {
+ double jj = j/(HEIGHT-1.0);
+ tco.p[1] = (double)((HEIGHT-1) - j) * sy + y1;
+ for (i = 0; i < WIDTH; i++) {
+ double ii = j/(HEIGHT-1.0);
+ tco.p[0] = (double)i * sx + x1;
+
+ tco.p[2] = (1.0-ii) * (1.0-jj) * z[0][0]
+ + (1.0-ii) * jj * z[0][1]
+ + ii * (1.0-jj) * z[1][0]
+ + ii * jj * z[1][1];
+
+ if (rs->INTERP(rs, &tco)) {
+ pa[j][i][0] = 0; /* Out of bounds in green */
+ pa[j][i][1] = 100;
+ pa[j][i][2] = 0;
+ } else {
+ int m;
+ /* printf("%d %d, %f %f returned %f\n",i,j,tco.p[0],tco.p[1],tco.v[0]); */
+ m = (int)((255.0 * (tco.v[0] - min)/(max - min)) + 0.5);
+ if (m < 0) {
+ pa[j][i][0] = 20; /* Dark blue */
+ pa[j][i][1] = 20;
+ pa[j][i][2] = 50;
+ } else if (m > 255) {
+ pa[j][i][0] = 230; /* Light blue */
+ pa[j][i][1] = 230;
+ pa[j][i][2] = 255;
+ } else {
+ pa[j][i][0] = m; /* Level in grey */
+ pa[j][i][1] = m;
+ pa[j][i][2] = m;
+ }
+ }
+ }
+ }
+
+ /* Mark verticies in red */
+ for(k = 0; k < npoints; k++) {
+ j = (int)((HEIGHT * (y2 - test_points[k].p[1])/(y2 - y1)) + 0.5);
+ i = (int)((WIDTH * (test_points[k].p[0] - x1)/(x2 - x1)) + 0.5);
+ pa[j][i][0] = 255;
+ pa[j][i][1] = 0;
+ pa[j][i][2] = 0;
+ }
+ write_rgb_tiff(rsv == 0 ? "t3d.tif" : "t3dh.tif" ,WIDTH,HEIGHT,(unsigned char *)pa);
+ }
+
+ /* Plot out 4 slices */
+ if (doplot == 1) {
+ int slice;
+
+ for (slice = 0; slice < 4; slice++) {
+ co tp; /* Test point */
+ double x[PLOTRES];
+ double ya[PLOTRES];
+ double yb[PLOTRES];
+ double xx,yy,zz;
+ double x1,x2,y1,y2,z1,z2;
+ double sx,sy,sz;
+ int i,n;
+
+ /* Set up slice to plot */
+ if (slice == 0) {
+ x1 = 0.5; y1 = 0.5, z1 = 0.0;
+ x2 = 0.5; y2 = 0.5, z2 = 1.0;
+ printf("Plot along z at x = y = 0.5\n");
+ n = PLOTRES;
+ } else if (slice == 1) {
+ x1 = 0.0; y1 = 0.5, z1 = 0.5;
+ x2 = 1.0; y2 = 0.5, z2 = 0.5;
+ printf("Plot along x at y = z = 0.5\n");
+ n = PLOTRES;
+ } else if (slice == 2) {
+ x1 = 0.5; y1 = 0.0, z1 = 0.5;
+ x2 = 0.5; y2 = 1.0, z2 = 0.5;
+ printf("Plot along y at x = z = 0.5\n");
+ n = PLOTRES;
+ } else {
+ x1 = 0.0; y1 = 0.0, z1 = 0.0;
+ x2 = 1.0; y2 = 1.0, z2 = 1.0;
+ printf("Plot along x = y = z\n");
+ n = PLOTRES;
+ }
+
+ sx = (x2 - x1)/n;
+ sy = (y2 - y1)/n;
+ sz = (z2 - z1)/n;
+
+ xx = x1;
+ yy = y1;
+ zz = z1;
+ for (i = 0; i < n; i++) {
+ double vv = i/(n-1.0);
+ x[i] = vv;
+ tp.p[0] = xx;
+ tp.p[1] = yy;
+ tp.p[2] = zz;
+
+ if (rss->INTERP(rss, &tp))
+ tp.v[0] = -0.1;
+ ya[i] = tp.v[0];
+
+ if (doh) {
+ if (rss2->INTERP(rss2, &tp))
+ tp.v[0] = -0.1;
+ yb[i] = tp.v[0];
+ }
+
+ xx += sx;
+ yy += sy;
+ zz += sz;
+ }
+
+ /* Plot the result */
+ if (doh)
+ do_plot(x,ya,yb,NULL,n);
+ else
+ do_plot(x,ya,NULL,NULL,n);
+ }
+ } else if (doplot == 2) {
+ co tp; /* Test point */
+ double x[PLOTRES];
+ double ya[PLOTRES];
+ double yb[PLOTRES];
+ double xx,yy,zz;
+ double x1,x2,y1,y2,z1,z2;
+ double sx,sy,sz;
+ int i,n;
+
+
+ x1 = plotpts[0][0];
+ y1 = plotpts[0][1];
+ z1 = plotpts[0][2];
+ x2 = plotpts[1][0];
+ y2 = plotpts[1][1];
+ z2 = plotpts[1][2];
+
+ printf("Plot along z at x = y = 0.5\n");
+ n = PLOTRES;
+
+ sx = (x2 - x1)/n;
+ sy = (y2 - y1)/n;
+ sz = (z2 - z1)/n;
+
+ xx = x1;
+ yy = y1;
+ zz = z1;
+ for (i = 0; i < n; i++) {
+ double vv = i/(n-1.0);
+ x[i] = vv;
+ tp.p[0] = xx;
+ tp.p[1] = yy;
+ tp.p[2] = zz;
+
+ if (rss->INTERP(rss, &tp))
+ tp.v[0] = -0.1;
+ ya[i] = tp.v[0];
+
+ if (doh) {
+ if (rss2->INTERP(rss2, &tp))
+ tp.v[0] = -0.1;
+ yb[i] = tp.v[0];
+ }
+
+ xx += sx;
+ yy += sy;
+ zz += sz;
+ }
+
+ /* Plot the result */
+ if (doh)
+ do_plot(x,ya,yb,NULL,n);
+ else
+ do_plot(x,ya,NULL,NULL,n);
+ }
+
+ /* Report the fit */
+ {
+ co tco; /* Test point */
+ int k;
+ double avg = 0;
+ double max = 0.0;
+
+ for(k = 0; k < npoints; k++) {
+ double err;
+ tco.p[0] = test_points[k].p[0];
+ tco.p[1] = test_points[k].p[1];
+ tco.p[2] = test_points[k].p[2];
+ rss->INTERP(rss, &tco);
+
+ err = tco.v[0] - test_points[k].v[0];
+ err = fabs(err);
+
+ avg += err;
+ if (err > max)
+ max = err;
+ }
+ avg /= (double)npoints;
+ printf("Max error %f%%, average %f%%\n",100.0 * max, 100.0 * avg);
+ }
+ return 0;
+}
+
+/* ---------------------- */
+/* Tiff diagnostic output */
+
+void
+write_rgb_tiff(
+char *name,
+int width,
+int height,
+unsigned char *data
+) {
+ int y;
+ unsigned char *dp;
+ TIFF *tif;
+
+ if ((tif = TIFFOpen(name, "w")) == NULL) {
+ fprintf(stderr,"Failed to open output TIFF file '%s'\n",name);
+ exit (-1);
+ }
+
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+
+ for (dp = data, y = 0; y < height; y++, dp += 3 * width) {
+ if (TIFFWriteScanline(tif, (tdata_t)dp, y, 0) < 0) {
+ fprintf(stderr,"WriteScanline Failed at line %d\n",y);
+ exit (-1);
+ }
+ }
+ (void) TIFFClose(tif);
+}
+
+#ifdef NEVER
+/******************************************************************/
+/* Error/debug output routines */
+/******************************************************************/
+
+/* Basic printf type error() and warning() routines */
+
+#ifdef __STDC__
+void
+error(char *fmt, ...)
+#else
+void
+error(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Error - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stdout);
+ exit (-1);
+}
+
+#ifdef __STDC__
+void
+warning(char *fmt, ...)
+#else
+void
+warning(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Warning - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+#ifdef __STDC__
+void
+verbose(int level, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+#else
+verbose(va_alist)
+va_dcl
+{
+ va_list args;
+ int level;
+ char *fmt;
+ va_start(args);
+ level = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+ if (verbose_level >= level)
+ {
+ fprintf(verbose_out,"cmatch: ");
+ vfprintf(verbose_out, fmt, args);
+ fprintf(verbose_out, "\n");
+ fflush(verbose_out);
+ }
+ va_end(args);
+}
+#endif /* NEVER */
diff --git a/rspl/t3ddf.c b/rspl/t3ddf.c
new file mode 100644
index 0000000..b0e7d18
--- /dev/null
+++ b/rspl/t3ddf.c
@@ -0,0 +1,570 @@
+/************************************************/
+/* Test RSPL in 3D with weak default function */
+/************************************************/
+
+/* Author: Graeme Gill
+ * Date: 20/11/2005
+ * Derived from cmatch.c
+ * Copyright 1995, 2005 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+
+#define DEBUG
+#define DETAILED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include "rspl.h"
+#include "tiffio.h"
+#include "plot.h"
+
+#ifdef NEVER
+#define INTERP spline_interp
+#else
+#define INTERP interp
+#endif
+
+#ifdef NEVER
+FILE *verbose_out = stdout;
+int verbose_level = 6; /* Current verbosity level */
+ /* 0 = none */
+ /* !0 = diagnostics */
+#endif /* NEVER */
+
+#define PLOTRES 256
+#define WIDTH 400 /* Raster size */
+#define HEIGHT 400
+
+#define MAX_ITS 500
+#define IT_TOL 0.0005
+#define GRES0 33 /* Default rspl resolutions */
+#define GRES1 33
+#define GRES2 33
+#undef NEVER
+#define ALWAYS
+
+/* two correction points along x = y = 0.5 */
+co test_points1[] = {
+// {{ 0.5, 0.5, 0.325 },{ 0.4 }}, /* 0 */
+// {{ 0.5, 0.5, 0.625 },{ 0.70 }} /* 1 */
+
+ {{ 0.4, 0.4, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.4, 0.4, 0.625 },{ 0.8 }}, /* 1 */
+ {{ 0.5, 0.4, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.5, 0.4, 0.625 },{ 0.8 }}, /* 1 */
+ {{ 0.6, 0.4, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.6, 0.4, 0.625 },{ 0.8 }}, /* 1 */
+
+ {{ 0.4, 0.5, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.4, 0.5, 0.625 },{ 0.8 }}, /* 1 */
+ {{ 0.5, 0.5, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.5, 0.5, 0.625 },{ 0.8 }}, /* 1 */
+ {{ 0.6, 0.5, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.6, 0.5, 0.625 },{ 0.8 }}, /* 1 */
+
+ {{ 0.4, 0.6, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.4, 0.6, 0.625 },{ 0.8 }}, /* 1 */
+ {{ 0.5, 0.6, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.5, 0.6, 0.625 },{ 0.8 }}, /* 1 */
+ {{ 0.6, 0.6, 0.325 },{ 0.5 }}, /* 0 */
+ {{ 0.6, 0.6, 0.625 },{ 0.8 }} /* 1 */
+};
+
+#ifdef NEVER
+#ifdef __STDC__
+#include <stdarg.h>
+void error(char *fmt, ...), warning(char *fmt, ...), verbose(int level, char *fmt, ...);
+#else
+#include <varargs.h>
+void error(), warning(), verbose();
+#endif
+#endif /* NEVER */
+
+void write_rgb_tiff(char *name, int width, int height, unsigned char *data);
+
+/* Weak default function */
+/* Linear along z, independent of x & y */
+static void wfunc(void *cbntx, double *out, double *in) {
+ out[0] = in[2];
+}
+
+void usage(void) {
+ fprintf(stderr,"Test 3D rspl interpolation with weak default function\n");
+ fprintf(stderr,"Author: Graeme W. Gill\n");
+ fprintf(stderr,"usage: t2d [options]\n");
+ fprintf(stderr," -t n Test set:\n");
+ fprintf(stderr," * 1 = two points along x & y\n");
+ fprintf(stderr," -w wweight Set weak default function weight (default 1.0)\n");
+ fprintf(stderr," -r resx,resy,resz Set grid resolutions (def %d %d %d)\n",GRES0,GRES1,GRES2);
+ fprintf(stderr," -h Test half scale resolution too\n");
+ fprintf(stderr," -q Test quarter scale resolution too\n");
+ fprintf(stderr," -x Use extra fitting\n");
+ fprintf(stderr," -s Test symetric smoothness (set asymetric -r !)\n");
+ fprintf(stderr," -s Test symetric smoothness\n");
+ fprintf(stderr," -p plot 4 slices, xy = 0.5, yz = 0.5, xz = 0.5, x=y=z\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ rspl *rss; /* Regularized spline structure */
+ rspl *rss2 = NULL; /* Regularized spline structure at half/quarter resolution */
+ datai low,high;
+ int gres[MXDI];
+ int gres2[MXDI];
+ double avgdev[MXDO];
+ co *test_points = test_points1;
+ int npoints = sizeof(test_points1)/sizeof(co);
+ double wweight = 1.0;
+ int twopass = 0;
+ int extra = 0;
+ int dosym = 0;
+ int doplot = 0;
+ int doh = 0;
+ int doq = 0;
+ int rsv;
+ int flags = RSPL_NOFLAGS;
+
+ low[0] = 0.0;
+ low[1] = 0.0;
+ low[2] = 0.0;
+ high[0] = 1.0;
+ high[1] = 1.0;
+ high[2] = 1.0;
+ gres[0] = GRES0;
+ gres[1] = GRES1;
+ gres[2] = GRES2;
+ avgdev[0] = 0.0;
+ avgdev[1] = 0.0;
+ avgdev[2] = 0.0;
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage();
+
+ /* test set */
+ else if (argv[fa][1] == 't' || argv[fa][1] == 'T') {
+ int ix;
+ fa = nfa;
+ if (na == NULL) usage();
+ ix = atoi(na);
+ switch (ix) {
+ case 1:
+ test_points = test_points1;
+ npoints = sizeof(test_points1)/sizeof(co);
+ break;
+ default:
+ usage();
+ }
+
+ } else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
+ fa = nfa;
+ if (na == NULL) usage();
+ wweight = atof(na);
+
+ } else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ fa = nfa;
+ if (na == NULL) usage();
+ if (sscanf(na, " %d,%d,%d ", &gres[0], &gres[1], &gres[2]) != 2)
+ usage();
+
+ } else if (argv[fa][1] == 'h' || argv[fa][1] == 'H') {
+ doh = 1;
+
+ } else if (argv[fa][1] == 'q' || argv[fa][1] == 'Q') {
+ doh = 1;
+ doq = 1;
+
+ } else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
+ doplot = 1;
+
+ } else if (argv[fa][1] == 's' || argv[fa][1] == 'S') {
+ dosym = 1;
+
+ } else if (argv[fa][1] == 'x' || argv[fa][1] == 'X') {
+ extra = 1;
+
+ } else if (argv[fa][1] == 's') {
+ dosym = 1;
+
+ } else
+ usage();
+ } else
+ break;
+ }
+
+
+ if (twopass)
+ flags |= RSPL_2PASSSMTH;
+
+ if (extra)
+ flags |= RSPL_EXTRAFIT2;
+
+ if (dosym)
+ flags |= RSPL_SYMDOMAIN;
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS, 3, 1);
+
+ /* Fit to scattered data */
+ rss->fit_rspl_df(rss,
+ flags, /* Non-mon and clip flags */
+ test_points, /* Test points */
+ npoints, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ low, high, /* Default data scale */
+ 1.0, /* Smoothing */
+ avgdev, /* Average Deviation */
+ NULL, /* iwidth */
+ wweight, /* weak function weight */
+ NULL, /* No context */
+ wfunc /* Weak function */
+ );
+ if (doh) {
+
+ if (doq) {
+ gres2[0] = gres[0]/4;
+ gres2[1] = gres[1]/4;
+ gres2[2] = gres[2]/4;
+ } else {
+ gres2[0] = gres[0]/2;
+ gres2[1] = gres[1]/2;
+ gres2[2] = gres[2]/2;
+ }
+
+ rss2 = new_rspl(RSPL_NOFLAGS, 3, 1);
+
+ /* Fit to scattered data */
+ rss2->fit_rspl_df(rss2,
+ flags, /* Non-mon and clip flags */
+ test_points, /* Test points */
+ npoints, /* Number of test points */
+ low, high, gres2, /* Low, high, resolution of grid */
+ low, high, /* Default data scale */
+ 1.0, /* Smoothing */
+ avgdev, /* Average Deviation */
+ NULL, /* iwidth */
+ wweight, /* weak function weight */
+ NULL, /* No context */
+ wfunc /* Weak function */
+ );
+ }
+
+ /* Test the interpolation with a slice in 2D */
+ for (rsv = 0; rsv <= doh; rsv++) {
+ double z[2][2] = { { 0.1, 0.5 }, { 0.5, 0.9 } };
+ double x1 = -0.2;
+ double x2 = 1.2;
+ double y1 = -0.2;
+ double y2 = 1.2;
+ double min = -0.0;
+ double max = 1.0;
+ rspl *rs;
+ unsigned char pa[HEIGHT][WIDTH][3];
+ co tco; /* Test point */
+ double sx,sy;
+ int i,j,k;
+
+ if (rsv == 0)
+ rs = rss;
+ else
+ rs = rss2;
+
+ sx = (x2 - x1)/(double)WIDTH;
+ sy = (y2 - y1)/(double)HEIGHT;
+
+ for (j=0; j < HEIGHT; j++) {
+ double jj = j/(HEIGHT-1.0);
+ tco.p[1] = (double)((HEIGHT-1) - j) * sy + y1;
+ for (i = 0; i < WIDTH; i++) {
+ double ii = j/(HEIGHT-1.0);
+ tco.p[0] = (double)i * sx + x1;
+
+ tco.p[2] = (1.0-ii) * (1.0-jj) * z[0][0]
+ + (1.0-ii) * jj * z[0][1]
+ + ii * (1.0-jj) * z[1][0]
+ + ii * jj * z[1][1];
+
+ if (rs->INTERP(rs, &tco)) {
+ pa[j][i][0] = 0; /* Out of bounds in green */
+ pa[j][i][1] = 100;
+ pa[j][i][2] = 0;
+ } else {
+ int m;
+ /* printf("%d %d, %f %f returned %f\n",i,j,tco.p[0],tco.p[1],tco.v[0]); */
+ m = (int)((255.0 * (tco.v[0] - min)/(max - min)) + 0.5);
+ if (m < 0) {
+ pa[j][i][0] = 20; /* Dark blue */
+ pa[j][i][1] = 20;
+ pa[j][i][2] = 50;
+ } else if (m > 255) {
+ pa[j][i][0] = 230; /* Light blue */
+ pa[j][i][1] = 230;
+ pa[j][i][2] = 255;
+ } else {
+ pa[j][i][0] = m; /* Level in grey */
+ pa[j][i][1] = m;
+ pa[j][i][2] = m;
+ }
+ }
+ }
+ }
+
+ /* Mark verticies in red */
+ for(k = 0; k < npoints; k++) {
+ j = (int)((HEIGHT * (y2 - test_points[k].p[1])/(y2 - y1)) + 0.5);
+ i = (int)((WIDTH * (test_points[k].p[0] - x1)/(x2 - x1)) + 0.5);
+ pa[j][i][0] = 255;
+ pa[j][i][1] = 0;
+ pa[j][i][2] = 0;
+ }
+ write_rgb_tiff(rsv == 0 ? "t3d.tif" : "t3dh.tif" ,WIDTH,HEIGHT,(unsigned char *)pa);
+ }
+
+ /* Plot out 4 slices */
+ if (doplot) {
+ int slice;
+
+ for (slice = 0; slice < 4; slice++) {
+ co tp; /* Test point */
+ double x[PLOTRES];
+ double ya[PLOTRES];
+ double yb[PLOTRES];
+ double xx,yy,zz;
+ double x1,x2,y1,y2,z1,z2;
+ double sx,sy,sz;
+ int i,n;
+
+ /* Set up slice to plot */
+ if (slice == 0) {
+ x1 = 0.5; y1 = 0.5, z1 = 0.0;
+ x2 = 0.5; y2 = 0.5, z2 = 1.0;
+ printf("Plot along z at x = y = 0.5\n");
+ n = PLOTRES;
+ } else if (slice == 1) {
+ x1 = 0.0; y1 = 0.5, z1 = 0.5;
+ x2 = 1.0; y2 = 0.5, z2 = 0.5;
+ printf("Plot along x at y = z = 0.5\n");
+ n = PLOTRES;
+ } else if (slice == 2) {
+ x1 = 0.5; y1 = 0.0, z1 = 0.5;
+ x2 = 0.5; y2 = 1.0, z2 = 0.5;
+ printf("Plot along y at x = z = 0.5\n");
+ n = PLOTRES;
+ } else {
+ x1 = 0.0; y1 = 0.0, z1 = 0.0;
+ x2 = 1.0; y2 = 1.0, z2 = 1.0;
+ printf("Plot along x = y = z\n");
+ n = PLOTRES;
+ }
+
+ sx = (x2 - x1)/n;
+ sy = (y2 - y1)/n;
+ sz = (z2 - z1)/n;
+
+ xx = x1;
+ yy = y1;
+ zz = z1;
+ for (i = 0; i < n; i++) {
+ double vv = i/(n-1.0);
+ x[i] = vv;
+ tp.p[0] = xx;
+ tp.p[1] = yy;
+ tp.p[2] = zz;
+
+ if (rss->INTERP(rss, &tp))
+ tp.v[0] = -0.1;
+ ya[i] = tp.v[0];
+
+ if (doh) {
+ if (rss2->INTERP(rss2, &tp))
+ tp.v[0] = -0.1;
+ yb[i] = tp.v[0];
+ }
+
+ xx += sx;
+ yy += sy;
+ zz += sz;
+ }
+
+ /* Plot the result */
+ if (doh)
+ do_plot(x,ya,yb,NULL,n);
+ else
+ do_plot(x,ya,NULL,NULL,n);
+ }
+ }
+
+ /* Report the fit */
+ {
+ co tco; /* Test point */
+ int k;
+ double avg = 0;
+ double max = 0.0;
+
+ for(k = 0; k < npoints; k++) {
+ double err;
+ tco.p[0] = test_points[k].p[0];
+ tco.p[1] = test_points[k].p[1];
+ tco.p[2] = test_points[k].p[2];
+ rss->INTERP(rss, &tco);
+
+ err = tco.v[0] - test_points[k].v[0];
+ err = fabs(err);
+
+ avg += err;
+ if (err > max)
+ max = err;
+ }
+ avg /= (double)npoints;
+ printf("Max error %f%%, average %f%%\n",100.0 * max, 100.0 * avg);
+ }
+ return 0;
+}
+
+/* ---------------------- */
+/* Tiff diagnostic output */
+
+void
+write_rgb_tiff(
+char *name,
+int width,
+int height,
+unsigned char *data
+) {
+ int y;
+ unsigned char *dp;
+ TIFF *tif;
+
+ if ((tif = TIFFOpen(name, "w")) == NULL) {
+ fprintf(stderr,"Failed to open output TIFF file '%s'\n",name);
+ exit (-1);
+ }
+
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+
+ for (dp = data, y = 0; y < height; y++, dp += 3 * width) {
+ if (TIFFWriteScanline(tif, (tdata_t)dp, y, 0) < 0) {
+ fprintf(stderr,"WriteScanline Failed at line %d\n",y);
+ exit (-1);
+ }
+ }
+ (void) TIFFClose(tif);
+}
+
+#ifdef NEVER
+/******************************************************************/
+/* Error/debug output routines */
+/******************************************************************/
+
+/* Basic printf type error() and warning() routines */
+
+#ifdef __STDC__
+void
+error(char *fmt, ...)
+#else
+void
+error(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Error - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stdout);
+ exit (-1);
+}
+
+#ifdef __STDC__
+void
+warning(char *fmt, ...)
+#else
+void
+warning(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Warning - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+#ifdef __STDC__
+void
+verbose(int level, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+#else
+verbose(va_alist)
+va_dcl
+{
+ va_list args;
+ int level;
+ char *fmt;
+ va_start(args);
+ level = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+ if (verbose_level >= level)
+ {
+ fprintf(verbose_out,"cmatch: ");
+ vfprintf(verbose_out, fmt, args);
+ fprintf(verbose_out, "\n");
+ fflush(verbose_out);
+ }
+ va_end(args);
+}
+#endif /* NEVER */
diff --git a/rspl/tnd.c b/rspl/tnd.c
new file mode 100644
index 0000000..95e4d16
--- /dev/null
+++ b/rspl/tnd.c
@@ -0,0 +1,489 @@
+
+/************************************************/
+/* Test RSPL in 3/4D */
+/************************************************/
+
+/* Author: Graeme Gill
+ * Date: 22/4/96
+ * Derived from cmatch.c
+ * Copyright 1995 - 2000 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+
+#undef DEBUG
+#undef DETAILED
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <math.h>
+#include "rspl.h"
+#include "numlib.h"
+#include "tiffio.h"
+
+#ifdef NEVER
+FILE *verbose_out = stdout;
+int verbose_level = 6; /* Current verbosity level */
+ /* 0 = none */
+ /* !0 = diagnostics */
+#endif /* NEVER */
+
+#define spline_interp interp
+
+/* rspl flags */
+#define FLAGS (0 /* */)
+
+#define TEST_FWD_2D
+#define TEST_REV_LOOKUP
+#undef TEST_SLICE
+#undef TEST_RANDOM_POINTS
+
+#define MAX_ITS 500
+#define IT_TOL 0.0005
+#define GRES 17 /* Grid resolution */
+#define DI 4 /* Dimensions in */
+#define FDI 4 /* Function (out) Dimensions */
+#undef NEVER
+#define ALWAYS
+
+/* Arbitrary values */
+static co test_points[] = {
+ {{ 0.1,0.1,0.5,0.0 },{ 0.6, 0.2, 0.3, 0.99 }}, /* 0 */
+ {{ 0.2,0.7,0.1,0.3 },{ 0.3, 0.1, 0.1, 0.45 }}, /* 1 */
+ {{ 0.8,0.8,0.8,0.2 },{ 0.1, 0.7, 0.7, 0.7 }}, /* 2 */
+ {{ 0.5,0.6,0.4,0.9 },{ 0.7, 0.6, 0.5, 0.4 }}, /* 3 */
+ {{ 0.2,0.5,0.2,0.7 },{ 0.2, 0.3, 0.2, 0.2 }}, /* 4 */
+ {{ 0.3,0.7,0.2,0.8 },{ 0.8, 0.9, 0.3, 0.5 }}, /* 5 */
+ {{ 0.5,0.4,0.9,0.3 },{ 0.6, 0.4, 0.2, 0.01 }}, /* 6 */
+ {{ 0.1,0.9,0.7,0.4 },{ 1.0, 0.9, 0.3, 0.6 }}, /* 7 */
+ {{ 0.7,0.2,0.1,0.3 },{ 0.2, 0.3, 0.7, 0.3 }}, /* 8 */
+ {{ 0.8,0.4,0.3,0.7 },{ 0.4, 0.5, 0.6, 0.2 }}, /* 9 */
+ {{ 0.3,0.3,0.4,0.1 },{ 0.8, 0.6, 0.8, 0.1 }} /* 10 */
+ };
+
+#ifdef NEVER
+/* Inverting table */
+static co test_points[] = {
+ {{ 0.1,0.1,0.5,0.0 },{ 0.9, 0.9, 0.5, 1.0 }}, /* 0 */
+ {{ 0.2,0.7,0.1,0.3 },{ 0.8, 0.3, 0.9, 0.7 }}, /* 1 */
+ {{ 0.8,0.8,0.8,0.2 },{ 0.2, 0.2, 0.2, 0.8 }}, /* 2 */
+ {{ 0.5,0.6,0.4,0.9 },{ 0.5, 0.4, 0.6, 0.1 }}, /* 3 */
+ {{ 0.2,0.5,0.2,0.7 },{ 0.8, 0.5, 0.8, 0.3 }}, /* 4 */
+ {{ 0.3,0.7,0.2,0.8 },{ 0.7, 0.3, 0.8, 0.2 }}, /* 5 */
+ {{ 0.5,0.4,0.9,0.3 },{ 0.5, 0.6, 0.1, 0.7 }}, /* 6 */
+ {{ 0.1,0.9,0.7,0.4 },{ 0.9, 0.1, 0.3, 0.6 }}, /* 7 */
+ {{ 0.7,0.2,0.1,0.3 },{ 0.3, 0.8, 0.9, 0.7 }}, /* 8 */
+ {{ 0.8,0.4,0.3,0.7 },{ 0.2, 0.6, 0.7, 0.3 }}, /* 9 */
+ {{ 0.3,0.3,0.4,0.1 },{ 0.7, 0.7, 0.6, 0.9 }} /* 10 */
+ };
+#endif /* NEVER */
+
+#ifdef NEVER
+#ifdef __STDC__
+#include <stdarg.h>
+void error(char *fmt, ...), warning(char *fmt, ...), verbose(int level, char *fmt, ...);
+#else
+#include <varargs.h>
+void error(), warning(), verbose();
+#endif
+#endif /* NEVER */
+
+void write_rgb_tiff(char *name, int width, int height, unsigned char *data);
+
+int main(int argc, char *argv[]) {
+ co *tps = NULL;
+ int ntps = 0;
+ rspl *rss; /* Multi-resolution regularized spline structure */
+ datai low,high;
+ int gres[MXDI];
+ double avgdev[MXDO];
+ low[0] = 0.0;
+ low[1] = 0.0;
+ low[2] = 0.0;
+ low[3] = 0.0;
+ high[0] = 1.0;
+ high[1] = 1.0;
+ high[2] = 1.0;
+ high[3] = 1.0;
+ gres[0] = GRES;
+ gres[1] = GRES;
+ gres[2] = GRES;
+ gres[3] = GRES;
+ avgdev[0] = 0.0;
+ avgdev[1] = 0.0;
+ avgdev[2] = 0.0;
+ avgdev[3] = 0.0;
+
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS, DI, /* di */
+ FDI); /* fdi */
+
+#ifdef TEST_RANDOM_POINTS
+ {
+ int i;
+ ntps = i_rand(30,150);
+ tps = (co *)malloc(ntps * sizeof(co));
+ for (i = 0; i < ntps; i++) {
+ tps[i].p[0] = d_rand(0.0,1.0);
+ tps[i].p[1] = d_rand(0.0,1.0);
+ tps[i].p[2] = d_rand(0.0,1.0);
+ tps[i].p[3] = d_rand(0.0,1.0);
+ tps[i].v[0] = d_rand(0.0,1.0);
+ tps[i].v[1] = d_rand(0.0,1.0);
+ tps[i].v[2] = d_rand(0.0,1.0);
+ tps[i].v[3] = d_rand(0.0,1.0);
+ }
+ }
+#else
+ tps = test_points;
+ ntps = sizeof(test_points)/sizeof(co);
+#endif
+
+ /* Fit to scattered data */
+ rss->fit_rspl(rss,
+ FLAGS, /* Non-mon and clip flags */
+ tps, /* Test points */
+ ntps, /* Number of test points */
+ low, high, gres, /* Low, high, resolution of grid */
+ NULL, NULL, /* Default data scale */
+ 1.0, /* Smoothing */
+ avgdev, /* Average deviation */
+ NULL); /* iwidth */
+ /* IT_TOL, MAX_ITS); */
+
+/* verbose(1,"Regular spline fit error = %f\n",rss->efactor(rss,0)); */
+
+ /* Do a quick check */
+ {
+ co tco; /* Test point */
+ int i,j;
+ double df,sm;
+ for (i = 0; i < ntps; i++) {
+ for (j = 0; j < DI; j++)
+ tco.p[j] = tps[i].p[j];
+
+ rss->spline_interp(rss, &tco);
+ sm = 0.0;
+ for (j = 0; j < DI; j++) {
+ df = tco.v[j] - tps[i].v[j];
+ sm += df * df;
+ }
+ printf("Error at data point %d = %f\n",i,sqrt(sm));
+ }
+ }
+
+#ifdef TEST_REV_LOOKUP
+ {
+#define NIP 10
+ int i, r;
+ double v[MXDO]; /* Target output value */
+ co tp[NIP], chp; /* Test point, check point */
+ double cvec[4]; /* Text clip vector */
+ int auxm[4]; /* Auxiliary target value valid flag */
+
+ tp[0].v[0] = v[0] = 0.5;
+ tp[0].v[1] = v[1] = 0.5;
+ tp[0].v[2] = v[2] = 0.5;
+ tp[0].v[3] = v[3] = 0.5;
+
+ /* Set auxiliary target */
+ auxm[0] = 0;
+ auxm[1] = 0;
+ auxm[2] = 1;
+ auxm[3] = 0;
+ tp[0].p[0] = -1.0;
+ tp[0].p[1] = -1.0;
+ tp[0].p[2] = 0.5;
+ tp[0].p[3] = -1.0;
+
+ for (i = 1; i < NIP; i++) { /* Make sure we can see changes */
+ tp[i].p[0] = -1.0;
+ tp[i].p[1] = -1.0;
+ tp[i].p[2] = -1.0;
+ tp[i].p[3] = -1.0;
+ }
+
+ /* Clip center */
+ cvec[0] = 0.0 - tp[0].v[0];
+ cvec[1] = 0.0 - tp[0].v[1];
+ cvec[2] = 0.0 - tp[0].v[2];
+ cvec[3] = 0.0 - tp[0].v[3];
+
+ /* Do reverse interpolation ~~~1 */
+ if ((r = rss->rev_interp(rss, 0, NIP, auxm, NULL /*cvec*/, tp)) > 0) {
+ printf("Total of %d Results\n",r);
+ for (i = 0; i < r; i++)
+ printf("Result %d = %f, %f, %f, %f\n",i, tp[i].p[0],tp[i].p[1],tp[i].p[2],tp[i].p[3]);
+
+ /* Check test result */
+ for (i = 0; i < r; i++) {
+ chp.p[0] = tp[i].p[0];
+ chp.p[1] = tp[i].p[1];
+ chp.p[2] = tp[i].p[2];
+ chp.p[3] = tp[i].p[3];
+ chp.v[0] = -1.0;
+ chp.v[1] = -1.0;
+ chp.v[2] = -1.0;
+ chp.v[3] = -1.0;
+ if (rss->interp(rss, &chp))
+ printf("Fwd check %d failed!\n",i);
+ else {
+ int p;
+ double er = 0.0;
+ for (p = 0; p < FDI; p++)
+ er += (v[p] - chp.v[p]) * (v[p] - chp.v[p]);
+ printf("Fwd check error %d = %f\n",i,er);
+ }
+ }
+ } else
+ printf("Rev lookup result returned none\n");
+ }
+#endif /* TEST_REV_LOOKUP */
+
+#ifdef TEST_SLICE
+ /* Test the interpolation */
+ {
+ co tp; /* Test point */
+ double x[50000];
+ double y[50000];
+ double ya[50000];
+ double xx,yy;
+ double x1,x2,y1,y2;
+ double sx,sy;
+ int i,j,n;
+
+ /* Set up slice to plot */
+ x1 = 0.1; y1 = 0.5; /* ~4 */
+ x2 = 0.9; y2 = 0.5;
+ n = 100;
+
+ sx = (x2 - x1)/n;
+ sy = (y2 - y1)/n;
+
+ xx = x1;
+ yy = y1;
+ for (j = i = 0; i < n; i++)
+ {
+ tp.p[0] = xx;
+ tp.p[1] = yy;
+ if (rss->spline_interp(rss, &tp))
+ {
+ tp.v[0] = -0.1;
+ }
+ x[j] = xx;
+ y[j] = tp.v[0];
+ j++;
+ xx += sx;
+ yy += sy;
+ }
+
+ /* Plot the result */
+ do_plot(x,y,NULL,NULL,j);
+ }
+#endif /* TEST_SLICE */
+
+#ifdef TEST_FWD_2D
+ /* Test the interpolation in 2D */
+ {
+#define WIDTH 200
+#define HEIGHT 200
+ double x1 = -0.2;
+ double x2 = 1.2;
+ double y1 = -0.2;
+ double y2 = 1.2;
+ double min = -0.0;
+ double max = 1.0;
+
+ unsigned char pa[HEIGHT][WIDTH][3];
+ co tco; /* Test point */
+ double sx,sy;
+ int i,j,k;
+
+ sx = (x2 - x1)/(double)WIDTH;
+ sy = (y2 - y1)/(double)HEIGHT;
+
+ tco.p[2] = 0.5; /* Set slice */
+ tco.p[3] = 0.5;
+ for (j=0; j < HEIGHT; j++)
+ {
+ tco.p[1] = (double)((HEIGHT-1) - j) * sy + y1;
+ for (i=0; i < WIDTH; i++)
+ {
+ tco.p[0] = (double)i * sx + x1;
+ if (rss->spline_interp(rss, &tco))
+ {
+ pa[j][i][0] = 0; /* Out of bounds in green */
+ pa[j][i][1] = 100;
+ pa[j][i][2] = 0;
+ }
+ else
+ {
+ int m;
+/* printf("%d %d, %f %f returned %f\n",i,j,tco.p[0],tco.p[1],tco.v[0]); */
+ m = (int)((255.0 * (tco.v[0] - min)/(max - min)) + 0.5);
+ if (m < 0)
+ {
+ pa[j][i][0] = 0; /* Dark blue */
+ pa[j][i][1] = 0;
+ pa[j][i][2] = 40;
+ }
+ else if (m > 255)
+ {
+ pa[j][i][0] = 220; /* Light blue */
+ pa[j][i][1] = 220;
+ pa[j][i][2] = 255;
+ }
+ else
+ {
+ pa[j][i][0] = m; /* Level in grey */
+ pa[j][i][1] = m;
+ pa[j][i][2] = m;
+ }
+ }
+ }
+ }
+
+ /* Mark verticies in red */
+ for(k = 0; k < ntps; k++)
+ {
+ j = (int)((HEIGHT * (y2 - tps[k].p[1])/(y2 - y1)) + 0.5);
+ i = (int)((WIDTH * (tps[k].p[0] - x1)/(x2 - x1)) + 0.5);
+ pa[j][i][0] = 255;
+ pa[j][i][1] = 0;
+ pa[j][i][2] = 0;
+ }
+
+ write_rgb_tiff("tnd.tif",WIDTH,HEIGHT,(unsigned char *)pa);
+ }
+#endif /* TEST_FWD_2D */
+ return 0;
+ }
+
+/* ---------------------- */
+/* Tiff diagnostic output */
+
+void
+write_rgb_tiff(
+char *name,
+int width,
+int height,
+unsigned char *data
+) {
+ int y;
+ unsigned char *dp;
+ TIFF *tif;
+
+ if ((tif = TIFFOpen(name, "w")) == NULL) {
+ fprintf(stderr,"Failed to open output TIFF file '%s'\n",name);
+ exit (-1);
+ }
+
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+
+ for (dp = data, y = 0; y < height; y++, dp += 3 * width) {
+ if (TIFFWriteScanline(tif, (tdata_t)dp, y, 0) < 0) {
+ fprintf(stderr,"WriteScanline Failed at line %d\n",y);
+ exit (-1);
+ }
+ }
+ (void) TIFFClose(tif);
+}
+
+#ifdef NEVER
+
+/******************************************************************/
+/* Error/debug output routines */
+/******************************************************************/
+
+/* Basic printf type error() and warning() routines */
+
+#ifdef __STDC__
+void
+error(char *fmt, ...)
+#else
+void
+error(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Error - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stdout);
+ exit (-1);
+}
+
+#ifdef __STDC__
+void
+warning(char *fmt, ...)
+#else
+void
+warning(va_alist)
+va_dcl
+#endif
+{
+ va_list args;
+#ifndef __STDC__
+ char *fmt;
+#endif
+
+ fprintf(stderr,"cmatch: Warning - ");
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+#ifdef __STDC__
+void
+verbose(int level, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+#else
+verbose(va_alist)
+va_dcl
+{
+ va_list args;
+ int level;
+ char *fmt;
+ va_start(args);
+ level = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+ if (verbose_level >= level)
+ {
+ fprintf(verbose_out,"cmatch: ");
+ vfprintf(verbose_out, fmt, args);
+ fprintf(verbose_out, "\n");
+ fflush(verbose_out);
+ }
+ va_end(args);
+}
+#endif /* NEVER */
diff --git a/rspl/trnd.c b/rspl/trnd.c
new file mode 100644
index 0000000..2676182
--- /dev/null
+++ b/rspl/trnd.c
@@ -0,0 +1,275 @@
+
+/************************************************/
+/* Test RSPL reverse lookup in 3/4D */
+/************************************************/
+
+/* Author: Graeme Gill
+ * Date: 31/10/96
+ * Derived from tnd.c
+ * Copyright 1999 - 2000 Graeme W. Gill
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+
+#undef DEBUG
+#undef DETAILED
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <math.h>
+#include "rspl.h"
+#include "numlib.h"
+
+#ifdef NEVER
+FILE *verbose_out = stdout;
+int verbose_level = 6; /* Current verbosity level */
+ /* 0 = none */
+ /* !0 = diagnostics */
+#endif /* NEVER */
+
+#define GRES 10 /* Grid resolution */
+#define DI 4 /* Dimensions in */
+#define FDI 3 /* Function (out) Dimensions */
+#define DOLIMIT
+#define LIMITV 1.50
+
+/* Fwd function approximated by rspl */
+void func(
+void *cbctx,
+double *out,
+double *in) {
+ double tt[4];
+
+#ifdef NEVER
+printf(" Got input %f %f\n",in[0],in[1]);
+ if (in[1] < 0.5 && in[0] < 0.5) { /* 0,0 */
+ out[0] = 0.1;
+ out[1] = 0.5;
+ }
+ if (in[1] < 0.5 && in[0] > 0.5) { /* 0,1 */
+ out[0] = 0.5;
+ out[1] = 0.0;
+ }
+ if (in[1] > 0.5 && in[0] < 0.5) { /* 0,1 */
+ out[0] = 0.9;
+ out[1] = 0.8;
+ }
+ if (in[1] > 0.5 && in[0] > 0.5) { /* 0,1 */
+ out[0] = 0.9;
+ out[1] = 0.2;
+ }
+#endif /* NEVER */
+
+#if DI >= 3
+ tt[0] = 0.7 * in[0] + 0.2 * in[1] + 0.1 * in[2];
+ tt[0] = tt[0] * tt[0];
+ tt[1] = 0.2 * in[0] + 0.8 * in[1] - 0.1 * in[2];
+ tt[1] = tt[1] * tt[1];
+ tt[2] = 0.3 * in[0] - 0.2 * in[1] + 0.9 * in[2];
+ tt[2] = tt[2] * tt[2];
+#endif
+
+#if DI == 4
+ tt[0] *= in[3];
+ tt[1] *= in[3];
+ tt[2] *= in[3];
+#endif
+
+ tt[3] = 0.3 * in[0] + 0.4 * in[1] + 0.3 * in[2];
+
+#if FDI > 0
+ out[0] = tt[0];
+#endif
+#if FDI > 1
+ out[1] = tt[1];
+#endif
+#if FDI > 2
+ out[2] = tt[2];
+#endif
+#if FDI > 3
+ out[3] = tt[3];
+#endif
+}
+
+/* Simplex ink limit function */
+double limitf(
+void *lcntx,
+double *in
+) {
+ int i;
+ double ov;
+
+ for (ov = 0.0, i = 0; i < DI; i++) {
+ ov += in[i];
+ }
+ return ov;
+}
+
+int
+main(
+int argc,
+char *argv[]
+) {
+ int e;
+ rspl *rss; /* Multi-resolution regularized spline structure */
+ int gres[MXDI];
+
+ for (e = 0; e < DI; e++)
+ gres[e] = GRES;
+
+ printf("Started test\n");
+ /* Create the object */
+ rss = new_rspl(RSPL_NOFLAGS, DI, FDI);
+
+ printf("Rspl allocated\n");
+ rss->set_rspl(rss, 0, (void *)NULL, func, NULL, NULL, gres, NULL, NULL);
+// rss->set_rspl(rss, RSPL_SET_APXLS, (void *)NULL, func, NULL, NULL, gres, NULL, NULL);
+
+ printf("Rspl set\n");
+
+ {
+#define NIP 10 /* Number of solutions allowed */
+ int i, r, cl;
+ double v[MXDO]; /* Target output value */
+ co tp[NIP], chp; /* Test point, check point */
+ double cvec[4]; /* Text clip vector */
+ int auxm[4]; /* Auxiliary target value valid flag */
+ double lmin[4], lmax[4]; /* Locus min/max values */
+
+ /* Output value being looked for */
+/*
+ tp[0].v[0] = v[0] = 1.5;
+ tp[0].v[1] = v[1] = 0.9;
+ tp[0].v[2] = v[2] = 1.2;
+ tp[0].v[3] = v[3] = 0.0;
+*/
+ tp[0].v[0] = v[0] = 0.3;
+ tp[0].v[1] = v[1] = 0.4;
+ tp[0].v[2] = v[2] = 0.26;
+ tp[0].v[3] = v[3] = 0.0;
+
+ /* Set auxiliary target */
+ auxm[0] = 0;
+ auxm[1] = 0;
+ auxm[2] = 0;
+ auxm[3] = 1;
+ tp[0].p[0] = 0;
+ tp[0].p[1] = 0;
+ tp[0].p[2] = 0.0;
+ tp[0].p[3] = 0.87;
+
+ for (i = 1; i < NIP; i++) { /* Make sure we can see changes */
+ tp[i].p[0] = -1.0;
+ tp[i].p[1] = -1.0;
+ tp[i].p[2] = -1.0;
+ tp[i].p[3] = -1.0;
+ }
+
+ /* Clip center */
+ cvec[0] = 0.1 - tp[0].v[0];
+ cvec[1] = 0.1 - tp[0].v[1];
+ cvec[2] = 0.1 - tp[0].v[2];
+ cvec[3] = 0.1 - tp[0].v[3];
+
+#ifdef DOLIMIT
+ /* Setup limit */
+ rss->rev_set_limit(rss,
+ limitf, /* limit function */
+ NULL, /* Function context */
+ LIMITV /* limit maximum value */
+ );
+#endif /* DOLIMIT */
+
+#if DI > FDI
+ /* Check out the locus size */
+ if ((r = rss->rev_locus(rss,
+ auxm, /* auxm Auxiliary mask flags */
+ tp, /* Input and auxiliary values */
+ lmin, /* Locus min/max return values */
+ lmax
+ )) > 0) {
+
+ printf("Total of %d Results\n",r);
+ for (i = 0; i < FDI; i++) {
+ if (auxm[i] == 0)
+ continue;
+ printf("Auxiliary %d min = %f, max = %f\n",i, lmin[i], lmax[i]);
+ }
+ } else {
+ printf("Failed to find gamut range\n");
+ }
+ printf("\n\n");
+#endif
+
+ /* Do reverse interpolation ~~~1 */
+ if ((r = rss->rev_interp(rss,
+ 0 /* RSPL_EXACTAUX */, /* Hint flags */
+ NIP, /* Number of solutions allowed */
+ auxm, /* auxm Auxiliary mask flags */
+ cvec, /* cvec Clip vector direction & length */
+ tp) /* Input and output values */
+ ) > 0) {
+
+ printf("Total of %d Results\n", r &= RSPL_NOSOLNS);
+
+ printf("Target output %f, %f, %f, %f\n", v[0], v[1], v[2], v[3]);
+
+ cl = r & RSPL_DIDCLIP; /* clipped flag */
+ r &= RSPL_NOSOLNS; /* Get number of solutions */
+
+ /* Check test result */
+ for (i = 0; i < r; i++) {
+ chp.p[0] = tp[i].p[0];
+ chp.p[1] = tp[i].p[1];
+ chp.p[2] = tp[i].p[2];
+ chp.p[3] = tp[i].p[3];
+ chp.v[0] = -1.0;
+ chp.v[1] = -1.0;
+ chp.v[2] = -1.0;
+ chp.v[3] = -1.0;
+ printf("Result %d = inp: %f, %f, %f, %f\n",i, tp[i].p[0],tp[i].p[1],tp[i].p[2],tp[i].p[3]);
+ printf(" out: %f, %f, %f, %f\n",tp[i].v[0],tp[i].v[1],tp[i].v[2],tp[i].v[3]);
+
+ if (rss->interp(rss, &chp))
+ printf("Fwd check %d failed!\n",i);
+ else {
+ int p;
+ double er = 0.0;
+ for (p = 0; p < FDI; p++)
+ er += (v[p] - chp.v[p]) * (v[p] - chp.v[p]);
+ er = sqrt(er);
+ printf("Fwd check error %d = %f\n",i,er);
+
+ if (cl != 0) {
+ /* Check if clipped result us reasonable */
+ /* ~~~ */
+ }
+ }
+ {
+ printf("Output limit = %f\n",limitf(NULL, tp[i].p));
+ }
+ }
+ } else
+ printf("Rev lookup result returned none\n");
+ }
+ rss->del(rss);
+ return 0;
+}
+
+
+#ifdef NEVER
+/* Standard interface for powell function */
+/* return err on sucess, -1.0 on failure */
+/* Result will be in cp */
+double powell(
+int di, /* Dimensionality */
+double cp[], /* Initial starting point */
+double s[], /* Size of initial search area */
+double ftol, /* Tollerance of error change to stop on */
+int maxit, /* Maximum iterations allowed */
+double (*func)(void *fdata, double tp[]), /* Error function to evaluate */
+void *fdata /* Opaque data needed by function */
+) {
+#endif