-# Makefile.in --- xscreensaver, Copyright © 1999-2024 Jamie Zawinski.
+# Makefile.in --- xscreensaver, Copyright © 1999-2025 Jamie Zawinski.
# the `../configure' script generates `Makefile' from this file.
@SET_MAKE@
MAJOR="$$1"; MINOR="$$2"; SUF="$$3"; \
VERS="$$MAJOR.$$MINOR$$SUF" ; \
if [ -z "$$SUF" ]; then \
- MINOR=`echo $$MINOR + 1 | bc | sed 's/^\(.\)/0\1/'` ; \
+ MINOR=`echo $$MINOR + 1 | bc | sed 's/^\(.\)$$/0\1/'` ; \
else \
set - `echo $$SUF | sed 's/^\([^0-9]*\)/\1 /'` ; \
AA="$$1"; BB="$$2"; \
cwd=`pwd` ; \
for d in $(SUBDIRS) ; do ( \
cd $$d ; \
+ set -e ; \
$(MAKE2) install -k INSTALL=true INSTALL_DATA=true \
INSTALL_DIRS=false SUID_FLAGS= 2>&- | \
while read s ; do \
if [ $$1 = true ]; then \
a="$$cwd/$$d/$$2" ; \
b="$$3" ; \
+ d2=`dirname "$$b"` ; \
+ [ -d "$$d2" ] || mkdir -p "$$d2" ; \
a=`echo "$$a" | sed -e 's@/\./@/@' \
-e 's@/[^/]*/\.\./@/@'` ; \
echo "ln -sf $$a $$b" ; \
cerebrum::
- rsync -vax . 10.0.1.12:xscreensaver/ \
+ rsync -vax . 10.0.1.37:xscreensaver/ \
--omit-dir-times \
--delete-during \
--exclude .git \
8. Scheme / Profile / Info: Executable: SaverTester.app.
Scheme / Run / Info: Executable: SaverTester.app.
9: Scheme / Run / Arguments: set SELECTED_SAVER environment variable.
- 10: File / Add Files / the new .c and .xml.
+ 10: File / Add Files / [x] Reference In Place / the new .c and .xml.
Add To Targets: the new target, and "XScreenSaver-iOS" and "-tvOS".
11: Re-order the two files in the file list on the left.
12: The files might not have moved. This means Xcode is gonna crash soon.
~/Library/Screen\ Savers/
18: git add xscreensaver.xcodeproj/xcshareddata/xcschemes/*.xcscheme
19: Create a man page from the XML with xml2man.pl, and update Makefile.in.
- 20: Upload a YouTube video: -record-animation 3600 -geom 1920x1080+128+64
+ 20: Upload a YouTube video: -record-animation 2min -geom 1920x1080+128+64
./upload-video.pl NAME
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleLongVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleGetInfoString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSHumanReadableCopyright</key>
- <string>6.09</string>
+ <string>6.10</string>
</dict>
</plist>
# ifndef HAVE_IPHONE
int window_count = ([saverNames count] <= 1 ? 1 : 2);
+
NSMutableArray *a = [[NSMutableArray arrayWithCapacity: window_count+1]
retain];
windows = a;
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleLongVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleGetInfoString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSHumanReadableCopyright</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSMainNibFile</key>
<string>SaverRunner</string>
<key>CFBundleIconFile</key>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleLongVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleGetInfoString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSHumanReadableCopyright</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSMainNibFile</key>
<string>Updater</string>
<key>CFBundleIconFile</key>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleLongVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleGetInfoString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSHumanReadableCopyright</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSMainNibFile</key>
<string>SaverRunner</string>
</dict>
\b0 by Jamie Zawinski\
and many others\
\
-version 6.09\
-07-Jun-2024\
+version 6.10\
+27-Apr-2025\
\
{\field{\*\fldinst{HYPERLINK "https://www.jwz.org/xscreensaver/"}}{\fldrslt \cf2 \ul \ulc2 https://www.jwz.org/xscreensaver/}}\
\pard\pardeftab720
\cf0 \
-\b To install all 250+ screen savers:\
+\b To install all 260+ screen savers:\
\pard\pardeftab720\li360
\b0 \cf0 \
\pard\pardeftab720\li360
\b0 \cf0 \
-XScreenSaver also runs on iOS and Android. The iOS version is available in the
+XScreenSaver also runs on iOS and Android. Free downloads in the
{\field{\*\fldinst{HYPERLINK
"https://itunes.apple.com/app/xscreensaver/id539014593?mt=8"}}
-{\fldrslt \cf2 \ul \ulc2 iTunes App Store}}
-and the Android version is available in the
+{\fldrslt \cf2 \ul \ulc2 Apple Store}}
+and on the
{\field{\*\fldinst{HYPERLINK
- "https://play.google.com/store/apps/details?id=org.jwz.android.xscreensaver"}}
-{\fldrslt \cf2 \ul \ulc2 Google Play Store}}, and they're both free!
+ "https://www.jwz.org/xscreensaver/"}}
+{\fldrslt \cf2 \ul \ulc2 XScreenSaver web site}},
+respectively.
}
<plist version="1.0">
<dict>
<key>URL</key>
- <string>https://play.google.com/store/apps/details?id=org.jwz.android.xscreensaver</string>
+ <string>https://www.jwz.org/xscreensaver/download.html</string>
</dict>
</plist>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleLongVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleGetInfoString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSHumanReadableCopyright</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSMainNibFile</key>
<string>iSaverRunner</string>
<key>CFBundleDisplayName</key>
{\field{\*\fldinst{HYPERLINK
"https://itunes.apple.com/app/xscreensaver/id539014593?mt=8"}}
{\fldrslt \cf2 \ul \ulc2 Apple Store}}
-and
+and on the
{\field{\*\fldinst{HYPERLINK
- "https://play.google.com/store/apps/details?id=org.jwz.android.xscreensaver"}}
-{\fldrslt \cf2 \ul \ulc2 Google Store}}
+ "https://www.jwz.org/xscreensaver/"}}
+{\fldrslt \cf2 \ul \ulc2 XScreenSaver web site}},
respectively. Source code for all versions is available on the
{\field{\*\fldinst{HYPERLINK "https://www.jwz.org/xscreensaver/"}}{\fldrslt \cf2 \ul \ulc2 XScreenSaver web site}}.
}
/* Generated file, do not edit.
- Created: Mon May 6 11:02:53 2024 by build-fntable.pl 1.14.
+ Created: Sun Apr 27 13:13:54 2025 by build-fntable.pl 1.14.
*/
#import <Foundation/Foundation.h>
dnalogo_xscreensaver_function_table,
drift_xscreensaver_function_table,
droste_xscreensaver_function_table,
+ dumpsterfire_xscreensaver_function_table,
dymaxionmap_xscreensaver_function_table,
endgame_xscreensaver_function_table,
energystream_xscreensaver_function_table,
highvoltage_xscreensaver_function_table,
hilbert_xscreensaver_function_table,
hopalong_xscreensaver_function_table,
+ hopffibration_xscreensaver_function_table,
hydrostat_xscreensaver_function_table,
hypertorus_xscreensaver_function_table,
hypnowheel_xscreensaver_function_table,
kaleidocycle_xscreensaver_function_table,
kallisti_xscreensaver_function_table,
klein_xscreensaver_function_table,
+ klondike_xscreensaver_function_table,
kumppa_xscreensaver_function_table,
lament_xscreensaver_function_table,
lavalite_xscreensaver_function_table,
piecewise_xscreensaver_function_table,
pinion_xscreensaver_function_table,
pipes_xscreensaver_function_table,
+ platonicfolding_xscreensaver_function_table,
polyhedra_xscreensaver_function_table,
polyominoes_xscreensaver_function_table,
polytopes_xscreensaver_function_table,
@"DNA Logo": [NSValue valueWithPointer:&dnalogo_xscreensaver_function_table],
@"Drift": [NSValue valueWithPointer:&drift_xscreensaver_function_table],
@"Droste": [NSValue valueWithPointer:&droste_xscreensaver_function_table],
+ @"Dumpster Fire": [NSValue valueWithPointer:&dumpsterfire_xscreensaver_function_table],
@"Dymaxion Map": [NSValue valueWithPointer:&dymaxionmap_xscreensaver_function_table],
@"Endgame": [NSValue valueWithPointer:&endgame_xscreensaver_function_table],
@"Energy Stream": [NSValue valueWithPointer:&energystream_xscreensaver_function_table],
@"High Voltage": [NSValue valueWithPointer:&highvoltage_xscreensaver_function_table],
@"Hilbert": [NSValue valueWithPointer:&hilbert_xscreensaver_function_table],
@"Hopalong": [NSValue valueWithPointer:&hopalong_xscreensaver_function_table],
+ @"Hopf Fibration": [NSValue valueWithPointer:&hopffibration_xscreensaver_function_table],
@"Hydrostat": [NSValue valueWithPointer:&hydrostat_xscreensaver_function_table],
@"Hypertorus": [NSValue valueWithPointer:&hypertorus_xscreensaver_function_table],
@"Hypnowheel": [NSValue valueWithPointer:&hypnowheel_xscreensaver_function_table],
@"Kaleidocycle": [NSValue valueWithPointer:&kaleidocycle_xscreensaver_function_table],
@"Kallisti": [NSValue valueWithPointer:&kallisti_xscreensaver_function_table],
@"Klein": [NSValue valueWithPointer:&klein_xscreensaver_function_table],
+ @"Klondike": [NSValue valueWithPointer:&klondike_xscreensaver_function_table],
@"Kumppa": [NSValue valueWithPointer:&kumppa_xscreensaver_function_table],
@"Lament": [NSValue valueWithPointer:&lament_xscreensaver_function_table],
@"Lavalite": [NSValue valueWithPointer:&lavalite_xscreensaver_function_table],
@"Piecewise": [NSValue valueWithPointer:&piecewise_xscreensaver_function_table],
@"Pinion": [NSValue valueWithPointer:&pinion_xscreensaver_function_table],
@"Pipes": [NSValue valueWithPointer:&pipes_xscreensaver_function_table],
+ @"Platonic Folding": [NSValue valueWithPointer:&platonicfolding_xscreensaver_function_table],
@"Polyhedra": [NSValue valueWithPointer:&polyhedra_xscreensaver_function_table],
@"Polyominoes": [NSValue valueWithPointer:&polyominoes_xscreensaver_function_table],
@"Polytopes": [NSValue valueWithPointer:&polytopes_xscreensaver_function_table],
--- /dev/null
+#!/opt/local/bin/perl -w
+# Copyright © 2025 Jamie Zawinski <jwz@jwz.org>
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation. No representations are made about the suitability of this
+# software for any purpose. It is provided "as is" without express or
+# implied warranty.
+#
+# Created: 25-Feb-2025.
+
+require 5;
+use diagnostics;
+use strict;
+
+my $progname = $0; $progname =~ s@.*/@@g;
+my ($version) = ('$Revision: 1.2 $' =~ m/\s(\d[.\d]+)\s/s);
+
+my $verbose = 0;
+
+BEGIN { eval 'use Perl::Tidy;' }
+
+sub minimize($$) {
+ my ($in, $out) = @_;
+
+ if (!defined($Perl::Tidy::VERSION)) {
+ print STDERR "$progname: Perl::Tidy not installed, skipping...\n";
+ return;
+ }
+
+ open (my $inf, "<:utf8", $in) || error ("$in: $!");
+ local $/ = undef; # read entire file
+ my $body = <$inf>;
+ close $inf;
+
+ my $body2;
+ my $err = Perl::Tidy::perltidy (
+ argv => join (' ',
+ '--mangle',
+ '--delete-all-comments',
+ ),
+ source => \$body,
+ destination => \$body2,
+ );
+ error ($err) if $err;
+
+ # Find all functions and variables, and shorten them.
+ #
+ my @subs = ($body2 =~ m/ \b sub \s+ ([a-zA-Z\d_]+) \s* [\{\(] /gsx);
+ my @vars = ($body2 =~ m/ \b my \s* [\$#%@] ([a-zA-Z\d_]+) [\s;,=] /gsx);
+ my @args = ($body2 =~ m/ \b my \s* \( ( .*? ) \) /gsx);
+
+ foreach my $tt (@args) {
+ foreach my $t (split (/\s*,\s*/, $tt)) {
+ $t =~ s/^[\$#%@]//s;
+ $t =~ s/=.*//s;
+ push @vars, $t;
+ }
+ }
+
+ my %tokens;
+ my $i = 0;
+ foreach my $t (@vars) {
+ if (length($t) < 4) {
+ print STDERR "$progname: skip var $t\n" if ($verbose > 1);
+ next;
+ }
+ $tokens{$t} = sprintf ("V%X", $i++);
+ }
+
+ $i = 0;
+ foreach my $t (@subs) {
+ if (length($t) < 4) {
+ print STDERR "$progname: skip sub $t\n" if ($verbose > 1);
+ next;
+ }
+ next if (length($t) < 4);
+ $tokens{$t} = sprintf ("F%X", $i++);
+ }
+
+ foreach my $k (sort keys %tokens) {
+ my $v = $tokens{$k};
+ if ($v =~ m/^S/) {
+ $body2 =~ s/ ( [^a-zA-Z\d_] ) ( $k ) ( [^a-zA-Z\d_] ) /$1$v$3/gsx;
+ } else {
+ $body2 =~ s/ ( [\$#%@] \{? ) ( $k ) ( [^a-zA-Z\d_] ) /$1$v$3/gsx;
+ }
+ print STDERR "$progname: $k => $v\n" if ($verbose > 1);
+ }
+
+ $in =~ s@^.*/@@s;
+ my $tag = "# DO NOT EDIT -- auto-generated from \"$in\"\n\n";
+ $body2 =~ s/^([^\n]+\n)/$1$tag/s;
+
+ $body2 =~ s/\n\n+/\n/gs; # Why is it double-spaced?
+
+ if ($out eq '-') {
+ print STDOUT $body2;
+ } else {
+ open (my $outf, ">:utf8", $out) || error ("$out: $!");
+ print $outf $body2;
+ close $outf;
+ print STDERR "$progname: wrote $out\n";
+ }
+}
+
+
+sub error($) {
+ my ($err) = @_;
+ print STDERR "$progname: $err\n";
+ exit 1;
+}
+
+sub usage() {
+ print STDERR "usage: $progname [--verbose] in.pl out.pl\n";
+ exit 1;
+}
+
+sub main() {
+ my ($in, $out);
+ while (@ARGV) {
+ $_ = shift @ARGV;
+ if (m/^--?verbose$/s) { $verbose++; }
+ elsif (m/^-v+$/s) { $verbose += length($_)-1; }
+# elsif (m/^--?debug$/s) { $debug_p++; }
+ elsif (m/^-./s) { usage; }
+ elsif (!$in) { $in = $_; }
+ elsif (!$out) { $out = $_; }
+ else { usage; }
+ }
+
+ usage unless ($out);
+ minimize ($in, $out);
+}
+
+main();
+exit 0;
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.entertainment</string>
<key>CFBundleShortVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleLongVersionString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleGetInfoString</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>NSHumanReadableCopyright</key>
- <string>6.09</string>
+ <string>6.10</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleIcons</key>
#!/usr/bin/perl -w
-# Copyright © 2006-2023 Jamie Zawinski <jwz@jwz.org>
+# Copyright © 2006-2025 Jamie Zawinski <jwz@jwz.org>
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
my ($exec_dir, $progname) = ($0 =~ m@^(.*?)/([^/]+)$@);
-my ($version) = ('$Revision: 1.58 $' =~ m/\s(\d[.\d]+)\s/s);
+my ($version) = ('$Revision: 1.60 $' =~ m/\s(\d[.\d]+)\s/s);
$ENV{PATH} = "/usr/local/bin:$ENV{PATH}"; # for seticon
$ENV{PATH} = "/opt/local/bin:$ENV{PATH}"; # for macports wget
}
+sub minimize_scripts($) {
+ my ($dir) = @_;
+ my $d2 = $dir . '/Contents/Resources';
+ $dir = $d2 if (-d $d2);
+ opendir (my $dirp, $dir) || error ("$dir: $!");
+ my @files = readdir ($dirp);
+ closedir $dirp;
+ foreach my $f (sort @files) {
+ next if ($f =~ m/^\./s);
+ next if ($f eq 'XScreenSaver');
+ $f = "$dir/$f";
+ next if ($f =~ m/\.(xml|png|jpg|ttf)$/s);
+ next if (-d $f);
+ next unless (-x $f); # Assume executable files are Perl scripts
+ my @cmd = "$exec_dir/perl-minimize.pl";
+ push @cmd, '--verbose' if ($verbose > 2);
+ push @cmd, ($f, $f);
+ print STDERR "$progname: exec: " . join(' ', @cmd) . "\n"
+ if ($verbose > 1);
+ system (@cmd);
+ }
+}
+
+
sub set_plist_key($$$$) {
my ($filename, $body, $key, $val) = @_;
my $vers = $1;
my ($ignore, $info_str, $name) = update_saver_xml ($app_dir, $vers);
- # No, don't do this -- the iOS version reads the XML file in a few
- # different places, and most of those places don't understand gzip.
-
if ($app_name eq 'XScreenSaver') {
compress_all_xml_files ($app_dir);
} else {
}
}
+ minimize_scripts ($app_dir);
+
# MacOS 10.12: codesign says "resource fork, Finder information, or
# similar detritus not allowed" if any bundle has an Icon\r file.
# set_icon ($app_dir);
<link>https://www.jwz.org/xscreensaver/updates.xml</link>
<description>Updates to xscreensaver.</description>
<language>en</language>
+ <item>
+ <title>Version 6.10</title>
+ <link>https://www.jwz.org/xscreensaver/xscreensaver-6.10.dmg</link>
+ <description><![CDATA[• New hacks, `dumpsterfire', `hopffibration', `platonicfolding' and `klondike'. <BR>• Rewrote the VT100 emulator for 'apple2' and 'phosphor'. Supports inverse and DEC Special Graphics. <BR>• BSOD supports systemd and bitlocker. ]]></description>
+ <pubDate>Sun, 27 Apr 2025 17:04:18 -0700</pubDate>
+ <enclosure url="https://www.jwz.org/xscreensaver/xscreensaver-6.10.dmg"
+ sparkle:version="6.10"
+ sparkle:edSignature="7xGV6QfwYePmr/DRgisDoL7cuLoHelN9ftdzzUBxpvjtI8oVzWTjcO545qvbiLYAosgXdie8ZP9XUF9Yor9lAA=="
+ length="102575147"
+ type="application/octet-stream" />
+ </item>
<item>
<title>Version 6.09</title>
<link>https://www.jwz.org/xscreensaver/xscreensaver-6.09.dmg</link>
length="90634351"
type="application/octet-stream" />
</item>
- <item>
- <title>Version 6.07</title>
- <link>https://www.jwz.org/xscreensaver/xscreensaver-6.07.dmg</link>
- <description><![CDATA[• New hacks, `droste', `skulloop', `papercube' and `cubocteversion'. <BR>• `xscreensaver-settings' was sometimes turning off the DPMS checkbox. <BR>• Log pid of caller of `deactivate' command, to give a hint about who is preventing the screen from blanking. <BR>• recanim uses libffmpeg. <BR>• Updates to `sphereeversion'. <BR>• Added some new map sources to `mapscroller'. <BR>• Worked around a macOS 13.4 bug where multi-head systems would fail to launch savers on some or all screens. <BR>• Minimum compiler target is now ISO C99 instead of ANSI C89. Didn't want to rush into it. <BR>• macOS, Android: Better looking thunbmail images. <BR>• Various other minor bug fixes.]]></description>
- <pubDate>Tue, 29 Aug 2023 18:06:48 -0700</pubDate>
- <enclosure url="https://www.jwz.org/xscreensaver/xscreensaver-6.07.dmg"
- sparkle:version="6.07"
- sparkle:edSignature="HSb5FJ/VaOJ/V3s+8EVrelYhf1/vswYWBWFDrKq1PLeAd8fhOQ9BvS4bdzxSbnwL361lSoIKkip61eQIay7mAQ=="
- length="88624232"
- type="application/octet-stream" />
- </item>
</channel>
</rss>
AFF449FE22754B5600DB8EDB /* PBXTargetDependency */,
AF3938381D0FBF5300205406 /* PBXTargetDependency */,
AF777A3F09B660B500EA3033 /* PBXTargetDependency */,
+ AFCB48BB2DBC0C3E006296D3 /* PBXTargetDependency */,
AFEC23EB1CB6ED0800DE138F /* PBXTargetDependency */,
AF777A3D09B660B500EA3033 /* PBXTargetDependency */,
AFACE8911CC8365F008B24CD /* PBXTargetDependency */,
AF1B0FC51D7AB5740011DBE4 /* PBXTargetDependency */,
AF0BF6ED29456D50000D9473 /* PBXTargetDependency */,
AF42CF712BE8A56100675AC7 /* PBXTargetDependency */,
+ AFA82E402DB1ECC600BEEE3E /* PBXTargetDependency */,
AF4F10F0143450C300E34F3F /* PBXTargetDependency */,
AFC0E8C91CDC6125008CAFAC /* PBXTargetDependency */,
AF777A1709B660B300EA3033 /* PBXTargetDependency */,
AFBFE7421786407000432B21 /* PBXTargetDependency */,
AFC644742AE47A1000D589B9 /* PBXTargetDependency */,
AF777A1109B660B300EA3033 /* PBXTargetDependency */,
+ AF2D86B82DB2105300B4F248 /* PBXTargetDependency */,
AF777A0F09B660B200EA3033 /* PBXTargetDependency */,
AF777A0D09B660B200EA3033 /* PBXTargetDependency */,
AF4FD6FF0CE7A4F9005EE58E /* PBXTargetDependency */,
AFD51B350F063B7800471C02 /* PBXTargetDependency */,
AF7779FF09B660B200EA3033 /* PBXTargetDependency */,
AF7779FD09B660B100EA3033 /* PBXTargetDependency */,
+ AF2D86942DB2032D00B4F248 /* PBXTargetDependency */,
AF7779FB09B660B100EA3033 /* PBXTargetDependency */,
AF7779F909B660B100EA3033 /* PBXTargetDependency */,
AFFAB33519158F1E0020F021 /* PBXTargetDependency */,
AF2D0D40241D7D7F0001D8B8 /* etruscanvenus.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D0D3F241D7D7F0001D8B8 /* etruscanvenus.c */; };
AF2D0D41241D7D7F0001D8B8 /* etruscanvenus.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D0D3F241D7D7F0001D8B8 /* etruscanvenus.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
AF2D29E6270BE727000B8588 /* tvLaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AF2D29E5270BE726000B8588 /* tvLaunchScreen.storyboard */; };
+ AF2D867A2DB1FCC900B4F248 /* XScreenSaverSubclass.m in Sources */ = {isa = PBXBuildFile; fileRef = AF9CC7A0099580E70075E99B /* XScreenSaverSubclass.m */; };
+ AF2D867C2DB1FCC900B4F248 /* libjwxyz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF4808C1098C3B6C00FB32B8 /* libjwxyz.a */; };
+ AF2D867D2DB1FCC900B4F248 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF976ED30989BF59001F8B92 /* ScreenSaver.framework */; };
+ AF2D867E2DB1FCC900B4F248 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2C31E515C0F7FE007A6896 /* QuartzCore.framework */; };
+ AF2D867F2DB1FCC900B4F248 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+ AF2D86802DB1FCC900B4F248 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF48112B0990A2C700FB32B8 /* Carbon.framework */; };
+ AF2D86812DB1FCC900B4F248 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE0BC611A6B0D6200C098BF /* OpenGL.framework */; };
+ AF2D86822DB1FCC900B4F248 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AF78369617DB9F25003B9FC0 /* libz.dylib */; };
+ AF2D868B2DB1FDB900B4F248 /* platonicfolding.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86892DB1FDB900B4F248 /* platonicfolding.c */; };
+ AF2D868C2DB1FDB900B4F248 /* platonicfolding.xml in Resources */ = {isa = PBXBuildFile; fileRef = AF2D868A2DB1FDB900B4F248 /* platonicfolding.xml */; };
+ AF2D868D2DB1FDB900B4F248 /* platonicfolding.xml in Resources */ = {isa = PBXBuildFile; fileRef = AF2D868A2DB1FDB900B4F248 /* platonicfolding.xml */; };
+ AF2D868E2DB1FDB900B4F248 /* platonicfolding.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86892DB1FDB900B4F248 /* platonicfolding.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AF2D868F2DB1FDB900B4F248 /* platonicfolding.xml in Resources */ = {isa = PBXBuildFile; fileRef = AF2D868A2DB1FDB900B4F248 /* platonicfolding.xml */; };
+ AF2D86902DB1FDB900B4F248 /* platonicfolding.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86892DB1FDB900B4F248 /* platonicfolding.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AF2D86912DB2013200B4F248 /* xftwrap.c in Sources */ = {isa = PBXBuildFile; fileRef = AFBD953C2C504ADC000DA52A /* xftwrap.c */; };
+ AF2D86922DB2014100B4F248 /* xftwrap.c in Sources */ = {isa = PBXBuildFile; fileRef = AFBD953C2C504ADC000DA52A /* xftwrap.c */; };
+ AF2D869C2DB20F2100B4F248 /* XScreenSaverSubclass.m in Sources */ = {isa = PBXBuildFile; fileRef = AF9CC7A0099580E70075E99B /* XScreenSaverSubclass.m */; };
+ AF2D869E2DB20F2100B4F248 /* libjwxyz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF4808C1098C3B6C00FB32B8 /* libjwxyz.a */; };
+ AF2D869F2DB20F2100B4F248 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF976ED30989BF59001F8B92 /* ScreenSaver.framework */; };
+ AF2D86A02DB20F2100B4F248 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2C31E515C0F7FE007A6896 /* QuartzCore.framework */; };
+ AF2D86A12DB20F2100B4F248 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+ AF2D86A22DB20F2100B4F248 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF48112B0990A2C700FB32B8 /* Carbon.framework */; };
+ AF2D86A32DB20F2100B4F248 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE0BC611A6B0D6200C098BF /* OpenGL.framework */; };
+ AF2D86A42DB20F2100B4F248 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AF78369617DB9F25003B9FC0 /* libz.dylib */; };
+ AF2D86AE2DB2100A00B4F248 /* klondike.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86AB2DB2100A00B4F248 /* klondike.c */; };
+ AF2D86AF2DB2100A00B4F248 /* klondike-game.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86AD2DB2100A00B4F248 /* klondike-game.c */; };
+ AF2D86B02DB2100A00B4F248 /* klondike.xml in Resources */ = {isa = PBXBuildFile; fileRef = AF2D86AC2DB2100A00B4F248 /* klondike.xml */; };
+ AF2D86B12DB2100A00B4F248 /* klondike.xml in Resources */ = {isa = PBXBuildFile; fileRef = AF2D86AC2DB2100A00B4F248 /* klondike.xml */; };
+ AF2D86B22DB2100A00B4F248 /* klondike.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86AB2DB2100A00B4F248 /* klondike.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AF2D86B32DB2100A00B4F248 /* klondike-game.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86AD2DB2100A00B4F248 /* klondike-game.c */; };
+ AF2D86B42DB2100A00B4F248 /* klondike.xml in Resources */ = {isa = PBXBuildFile; fileRef = AF2D86AC2DB2100A00B4F248 /* klondike.xml */; };
+ AF2D86B52DB2100A00B4F248 /* klondike.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86AB2DB2100A00B4F248 /* klondike.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AF2D86B62DB2100A00B4F248 /* klondike-game.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D86AD2DB2100A00B4F248 /* klondike-game.c */; };
AF2D8F321CEBA10300198014 /* jwxyz-timers.c in Sources */ = {isa = PBXBuildFile; fileRef = AF2D8F301CEBA10300198014 /* jwxyz-timers.c */; };
AF2D8F331CEBA10300198014 /* jwxyz-timers.h in Headers */ = {isa = PBXBuildFile; fileRef = AF2D8F311CEBA10300198014 /* jwxyz-timers.h */; };
AF32D9E70F3AD0B40080F535 /* XScreenSaverSubclass.m in Sources */ = {isa = PBXBuildFile; fileRef = AF9CC7A0099580E70075E99B /* XScreenSaverSubclass.m */; };
AF5C9B121A0CCF4E00B0147A /* cityflow.xml in Resources */ = {isa = PBXBuildFile; fileRef = AF5C9B0F1A0CCF4E00B0147A /* cityflow.xml */; };
AF5C9B131A0CCF4E00B0147A /* cityflow.c in Sources */ = {isa = PBXBuildFile; fileRef = AF5C9B101A0CCF4E00B0147A /* cityflow.c */; };
AF5C9B141A0CCF4E00B0147A /* cityflow.c in Sources */ = {isa = PBXBuildFile; fileRef = AF5C9B101A0CCF4E00B0147A /* cityflow.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AF5D4E3C2D6409AE009BE57E /* ansi-tty.c in Sources */ = {isa = PBXBuildFile; fileRef = AF5D4E3B2D6409AE009BE57E /* ansi-tty.c */; };
+ AF5D4E3D2D6409AE009BE57E /* ansi-tty.c in Sources */ = {isa = PBXBuildFile; fileRef = AF5D4E3B2D6409AE009BE57E /* ansi-tty.c */; };
+ AF5D4E3E2D6409AE009BE57E /* ansi-tty.c in Sources */ = {isa = PBXBuildFile; fileRef = AF5D4E3B2D6409AE009BE57E /* ansi-tty.c */; };
+ AF5D4E3F2D6409AE009BE57E /* ansi-tty.c in Sources */ = {isa = PBXBuildFile; fileRef = AF5D4E3B2D6409AE009BE57E /* ansi-tty.c */; };
AF5ECEB02116B1A400069433 /* XScreenSaverSubclass.m in Sources */ = {isa = PBXBuildFile; fileRef = AF9CC7A0099580E70075E99B /* XScreenSaverSubclass.m */; };
AF5ECEB12116B1A400069433 /* analogtv.c in Sources */ = {isa = PBXBuildFile; fileRef = AF9D4CFA09B5AC94006E59CF /* analogtv.c */; };
AF5ECEB42116B1A400069433 /* libjwxyz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF4808C1098C3B6C00FB32B8 /* libjwxyz.a */; };
AFA6AAFF20999950006D2685 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AF78369617DB9F25003B9FC0 /* libz.dylib */; };
AFA6AB0D20999A60006D2685 /* glitchpeg.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFA6AB0C20999A60006D2685 /* glitchpeg.xml */; };
AFA6AB0F20999A7B006D2685 /* glitchpeg.c in Sources */ = {isa = PBXBuildFile; fileRef = AFA6AB0E20999A7B006D2685 /* glitchpeg.c */; };
+ AFA82E242DB1EB4B00BEEE3E /* XScreenSaverSubclass.m in Sources */ = {isa = PBXBuildFile; fileRef = AF9CC7A0099580E70075E99B /* XScreenSaverSubclass.m */; };
+ AFA82E262DB1EB4B00BEEE3E /* libjwxyz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF4808C1098C3B6C00FB32B8 /* libjwxyz.a */; };
+ AFA82E272DB1EB4B00BEEE3E /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF976ED30989BF59001F8B92 /* ScreenSaver.framework */; };
+ AFA82E282DB1EB4B00BEEE3E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2C31E515C0F7FE007A6896 /* QuartzCore.framework */; };
+ AFA82E292DB1EB4B00BEEE3E /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+ AFA82E2A2DB1EB4B00BEEE3E /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF48112B0990A2C700FB32B8 /* Carbon.framework */; };
+ AFA82E2B2DB1EB4B00BEEE3E /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE0BC611A6B0D6200C098BF /* OpenGL.framework */; };
+ AFA82E2C2DB1EB4B00BEEE3E /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AF78369617DB9F25003B9FC0 /* libz.dylib */; };
+ AFA82E362DB1EC3B00BEEE3E /* hopffibration.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFA82E352DB1EC3B00BEEE3E /* hopffibration.xml */; };
+ AFA82E372DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c in Sources */ = {isa = PBXBuildFile; fileRef = AFA82E342DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AFA82E382DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c in Sources */ = {isa = PBXBuildFile; fileRef = AFA82E332DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AFA82E392DB1EC3B00BEEE3E /* hopffibration.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFA82E352DB1EC3B00BEEE3E /* hopffibration.xml */; };
+ AFA82E3A2DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c in Sources */ = {isa = PBXBuildFile; fileRef = AFA82E342DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c */; };
+ AFA82E3B2DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c in Sources */ = {isa = PBXBuildFile; fileRef = AFA82E332DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c */; };
+ AFA82E3C2DB1EC3B00BEEE3E /* hopffibration.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFA82E352DB1EC3B00BEEE3E /* hopffibration.xml */; };
+ AFA82E3D2DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c in Sources */ = {isa = PBXBuildFile; fileRef = AFA82E342DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AFA82E3E2DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c in Sources */ = {isa = PBXBuildFile; fileRef = AFA82E332DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
AFAA6B451773F07800DE720C /* ios-function-table.m in Sources */ = {isa = PBXBuildFile; fileRef = AFAA6B441773F07700DE720C /* ios-function-table.m */; };
AFAAE38E207D6343007A515C /* XScreenSaverSubclass.m in Sources */ = {isa = PBXBuildFile; fileRef = AF9CC7A0099580E70075E99B /* XScreenSaverSubclass.m */; };
AFAAE390207D6343007A515C /* libjwxyz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF4808C1098C3B6C00FB32B8 /* libjwxyz.a */; };
AFB8A69B1782BA34004EDB85 /* kaleidocycle.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFB8A69A1782BA34004EDB85 /* kaleidocycle.xml */; };
AFB8A69C1782BF6C004EDB85 /* kaleidocycle.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFB8A69A1782BA34004EDB85 /* kaleidocycle.xml */; };
AFB8A69D1782BFA6004EDB85 /* kaleidocycle.c in Sources */ = {isa = PBXBuildFile; fileRef = AF7511141782B64300380EA1 /* kaleidocycle.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AFBD953D2C504AFC000DA52A /* xftwrap.c in Sources */ = {isa = PBXBuildFile; fileRef = AFBD953C2C504ADC000DA52A /* xftwrap.c */; };
AFBE744019A7C6930018AA35 /* robot.c in Sources */ = {isa = PBXBuildFile; fileRef = AFBE743F19A7C6930018AA35 /* robot.c */; };
AFBE744119A7C6EF0018AA35 /* robot.c in Sources */ = {isa = PBXBuildFile; fileRef = AFBE743F19A7C6930018AA35 /* robot.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
AFBF893E0E41D930006A2D66 /* fps.c in Sources */ = {isa = PBXBuildFile; fileRef = AFBF893C0E41D930006A2D66 /* fps.c */; };
AFC7592E158D8E8B00C5458E /* textclient.h in Headers */ = {isa = PBXBuildFile; fileRef = AFC7592C158D8E8B00C5458E /* textclient.h */; };
AFC75930158D9A7A00C5458E /* textclient-ios.m in Sources */ = {isa = PBXBuildFile; fileRef = AFC7592F158D9A7A00C5458E /* textclient-ios.m */; };
AFCA3EED25856B6200CBCF16 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AFCA3EEC25856B6200CBCF16 /* IOKit.framework */; };
+ AFCB489F2DBC0958006296D3 /* XScreenSaverSubclass.m in Sources */ = {isa = PBXBuildFile; fileRef = AF9CC7A0099580E70075E99B /* XScreenSaverSubclass.m */; };
+ AFCB48A12DBC0958006296D3 /* libjwxyz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF4808C1098C3B6C00FB32B8 /* libjwxyz.a */; };
+ AFCB48A22DBC0958006296D3 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF976ED30989BF59001F8B92 /* ScreenSaver.framework */; };
+ AFCB48A32DBC0958006296D3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF2C31E515C0F7FE007A6896 /* QuartzCore.framework */; };
+ AFCB48A42DBC0958006296D3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+ AFCB48A52DBC0958006296D3 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF48112B0990A2C700FB32B8 /* Carbon.framework */; };
+ AFCB48A62DBC0958006296D3 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEE0BC611A6B0D6200C098BF /* OpenGL.framework */; };
+ AFCB48A72DBC0958006296D3 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AF78369617DB9F25003B9FC0 /* libz.dylib */; };
+ AFCB48B12DBC0BDB006296D3 /* dumpster_model.c in Sources */ = {isa = PBXBuildFile; fileRef = AFCB48AE2DBC0BDB006296D3 /* dumpster_model.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AFCB48B22DBC0BDB006296D3 /* dumpsterfire.c in Sources */ = {isa = PBXBuildFile; fileRef = AFCB48AF2DBC0BDB006296D3 /* dumpsterfire.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AFCB48B32DBC0BDB006296D3 /* dumpsterfire.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFCB48B02DBC0BDB006296D3 /* dumpsterfire.xml */; };
+ AFCB48B42DBC0BDB006296D3 /* dumpsterfire.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFCB48B02DBC0BDB006296D3 /* dumpsterfire.xml */; };
+ AFCB48B52DBC0BDB006296D3 /* dumpster_model.c in Sources */ = {isa = PBXBuildFile; fileRef = AFCB48AE2DBC0BDB006296D3 /* dumpster_model.c */; };
+ AFCB48B62DBC0BDB006296D3 /* dumpsterfire.c in Sources */ = {isa = PBXBuildFile; fileRef = AFCB48AF2DBC0BDB006296D3 /* dumpsterfire.c */; };
+ AFCB48B72DBC0BDB006296D3 /* dumpster_model.c in Sources */ = {isa = PBXBuildFile; fileRef = AFCB48AE2DBC0BDB006296D3 /* dumpster_model.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AFCB48B82DBC0BDB006296D3 /* dumpsterfire.c in Sources */ = {isa = PBXBuildFile; fileRef = AFCB48AF2DBC0BDB006296D3 /* dumpsterfire.c */; settings = {COMPILER_FLAGS = "-DUSE_GL"; }; };
+ AFCB48B92DBC0BDB006296D3 /* dumpsterfire.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFCB48B02DBC0BDB006296D3 /* dumpsterfire.xml */; };
AFCCCBB009BFE4B000353F4D /* rdbomb.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFCCCBAD09BFE4B000353F4D /* rdbomb.xml */; };
AFCCCBB309BFE51900353F4D /* thornbird.xml in Resources */ = {isa = PBXBuildFile; fileRef = AFC259230988A469000655EE /* thornbird.xml */; };
AFCE26332337332000BDCE10 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AFCE26322337332000BDCE10 /* LaunchScreen.storyboard */; };
remoteGlobalIDString = AF2D0D25241D7C870001D8B8;
remoteInfo = EtruscanVenus;
};
+ AF2D86742DB1FCC900B4F248 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AF4808C0098C3B6C00FB32B8;
+ remoteInfo = jwxyz;
+ };
+ AF2D86932DB2032D00B4F248 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AF2D86722DB1FCC900B4F248;
+ remoteInfo = PlatonicFolding;
+ };
+ AF2D86972DB20F2100B4F248 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AF4808C0098C3B6C00FB32B8;
+ remoteInfo = jwxyz;
+ };
+ AF2D86B72DB2105300B4F248 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AF2D86952DB20F2100B4F248;
+ remoteInfo = Klondike;
+ };
AF32D9E20F3AD0B40080F535 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
remoteGlobalIDString = AFA6AAF020999950006D2685;
remoteInfo = GlitchPEG;
};
+ AFA82E1F2DB1EB4B00BEEE3E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AF4808C0098C3B6C00FB32B8;
+ remoteInfo = jwxyz;
+ };
+ AFA82E3F2DB1ECC600BEEE3E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AFA82E1D2DB1EB4B00BEEE3E;
+ remoteInfo = HopfFibration;
+ };
AFAAE389207D6343007A515C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
remoteGlobalIDString = AF9771D60989DC4A001F8B92;
remoteInfo = SaverTester;
};
+ AFCB489A2DBC0958006296D3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AF4808C0098C3B6C00FB32B8;
+ remoteInfo = jwxyz;
+ };
+ AFCB48BA2DBC0C3E006296D3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = AFCB48982DBC0958006296D3;
+ remoteInfo = DumpsterFire;
+ };
AFCF833D1AF5B515008BB7E1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
AF2D0D3F241D7D7F0001D8B8 /* etruscanvenus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = etruscanvenus.c; path = hacks/glx/etruscanvenus.c; sourceTree = "<group>"; };
AF2D29E5270BE726000B8588 /* tvLaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = tvLaunchScreen.storyboard; sourceTree = "<group>"; };
AF2D522513E954A0002AA818 /* SaverRunner.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = SaverRunner.icns; sourceTree = "<group>"; };
+ AF2D86872DB1FCC900B4F248 /* PlatonicFolding.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PlatonicFolding.saver; sourceTree = BUILT_PRODUCTS_DIR; };
+ AF2D86892DB1FDB900B4F248 /* platonicfolding.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = platonicfolding.c; path = ../hacks/glx/platonicfolding.c; sourceTree = SOURCE_ROOT; };
+ AF2D868A2DB1FDB900B4F248 /* platonicfolding.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = platonicfolding.xml; path = ../hacks/config/platonicfolding.xml; sourceTree = SOURCE_ROOT; };
+ AF2D86A92DB20F2100B4F248 /* Klondike.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Klondike.saver; sourceTree = BUILT_PRODUCTS_DIR; };
+ AF2D86AB2DB2100A00B4F248 /* klondike.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = klondike.c; path = ../hacks/glx/klondike.c; sourceTree = SOURCE_ROOT; };
+ AF2D86AC2DB2100A00B4F248 /* klondike.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = klondike.xml; path = ../hacks/config/klondike.xml; sourceTree = SOURCE_ROOT; };
+ AF2D86AD2DB2100A00B4F248 /* klondike-game.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "klondike-game.c"; path = "../hacks/glx/klondike-game.c"; sourceTree = SOURCE_ROOT; };
AF2D8F301CEBA10300198014 /* jwxyz-timers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "jwxyz-timers.c"; path = "../jwxyz/jwxyz-timers.c"; sourceTree = "<group>"; };
AF2D8F311CEBA10300198014 /* jwxyz-timers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "jwxyz-timers.h"; path = "../jwxyz/jwxyz-timers.h"; sourceTree = "<group>"; };
AF32D9F40F3AD0B40080F535 /* RubikBlocks.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RubikBlocks.saver; sourceTree = BUILT_PRODUCTS_DIR; };
AF5C9B0D1A0CCE6E00B0147A /* Cityflow.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Cityflow.saver; sourceTree = BUILT_PRODUCTS_DIR; };
AF5C9B0F1A0CCF4E00B0147A /* cityflow.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = cityflow.xml; sourceTree = "<group>"; };
AF5C9B101A0CCF4E00B0147A /* cityflow.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cityflow.c; path = hacks/glx/cityflow.c; sourceTree = "<group>"; };
+ AF5D4E3B2D6409AE009BE57E /* ansi-tty.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "ansi-tty.c"; path = "hacks/ansi-tty.c"; sourceTree = "<group>"; };
AF5ECEC02116B1A400069433 /* VFeedback.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VFeedback.saver; sourceTree = BUILT_PRODUCTS_DIR; };
AF5ECEC22116B2CC00069433 /* vfeedback.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vfeedback.c; path = hacks/vfeedback.c; sourceTree = "<group>"; };
AF5ECEC52116B2FE00069433 /* vfeedback.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = vfeedback.xml; sourceTree = "<group>"; };
AFA6AB0520999950006D2685 /* GlitchPEG.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GlitchPEG.saver; sourceTree = BUILT_PRODUCTS_DIR; };
AFA6AB0C20999A60006D2685 /* glitchpeg.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = glitchpeg.xml; sourceTree = "<group>"; };
AFA6AB0E20999A7B006D2685 /* glitchpeg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = glitchpeg.c; path = hacks/glitchpeg.c; sourceTree = "<group>"; };
+ AFA82E312DB1EB4B00BEEE3E /* HopfFibration.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HopfFibration.saver; sourceTree = BUILT_PRODUCTS_DIR; };
+ AFA82E332DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hacks/glx/hopfanimations.c; sourceTree = "<group>"; };
+ AFA82E342DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hacks/glx/hopffibration.c; sourceTree = "<group>"; };
+ AFA82E352DB1EC3B00BEEE3E /* hopffibration.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = hopffibration.xml; sourceTree = "<group>"; };
AFAA6B441773F07700DE720C /* ios-function-table.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ios-function-table.m"; sourceTree = "<group>"; };
AFAAE39C207D6343007A515C /* Maze3D.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Maze3D.saver; sourceTree = BUILT_PRODUCTS_DIR; };
AFAAE39E207D6420007A515C /* maze3d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = maze3d.c; path = hacks/glx/maze3d.c; sourceTree = "<group>"; };
AFB5A0ED0981FF8B00871B16 /* usleep.c */ = {isa = PBXFileReference; fileEncoding = 12; lastKnownFileType = sourcecode.c.c; name = usleep.c; path = utils/usleep.c; sourceTree = "<group>"; };
AFB5A0EE0981FF8B00871B16 /* usleep.h */ = {isa = PBXFileReference; fileEncoding = 12; lastKnownFileType = sourcecode.c.h; name = usleep.h; path = utils/usleep.h; sourceTree = "<group>"; };
AFB8A69A1782BA34004EDB85 /* kaleidocycle.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = kaleidocycle.xml; sourceTree = "<group>"; };
+ AFBD953B2C504ADB000DA52A /* xftwrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xftwrap.h; path = utils/xftwrap.h; sourceTree = "<group>"; };
+ AFBD953C2C504ADC000DA52A /* xftwrap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xftwrap.c; path = utils/xftwrap.c; sourceTree = "<group>"; };
AFBE743F19A7C6930018AA35 /* robot.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = robot.c; path = hacks/glx/robot.c; sourceTree = "<group>"; };
AFBF893C0E41D930006A2D66 /* fps.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; name = fps.c; path = hacks/fps.c; sourceTree = "<group>"; };
AFBF893D0E41D930006A2D66 /* fps.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; name = fps.h; path = hacks/fps.h; sourceTree = "<group>"; };
AFC7592C158D8E8B00C5458E /* textclient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = textclient.h; path = utils/textclient.h; sourceTree = "<group>"; };
AFC7592F158D9A7A00C5458E /* textclient-ios.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "textclient-ios.m"; path = "OSX/textclient-ios.m"; sourceTree = "<group>"; };
AFCA3EEC25856B6200CBCF16 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
+ AFCB48AC2DBC0958006296D3 /* DumpsterFire.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DumpsterFire.saver; sourceTree = BUILT_PRODUCTS_DIR; };
+ AFCB48AE2DBC0BDB006296D3 /* dumpster_model.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dumpster_model.c; path = ../hacks/glx/dumpster_model.c; sourceTree = SOURCE_ROOT; };
+ AFCB48AF2DBC0BDB006296D3 /* dumpsterfire.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dumpsterfire.c; path = ../hacks/glx/dumpsterfire.c; sourceTree = SOURCE_ROOT; };
+ AFCB48B02DBC0BDB006296D3 /* dumpsterfire.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = dumpsterfire.xml; path = ../hacks/config/dumpsterfire.xml; sourceTree = SOURCE_ROOT; };
AFCCCBAD09BFE4B000353F4D /* rdbomb.xml */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.xml; path = rdbomb.xml; sourceTree = "<group>"; };
AFCE26322337332000BDCE10 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
AFCF83501AF5B515008BB7E1 /* SplitFlap.saver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SplitFlap.saver; sourceTree = BUILT_PRODUCTS_DIR; };
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AF2D867B2DB1FCC900B4F248 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AF2D867C2DB1FCC900B4F248 /* libjwxyz.a in Frameworks */,
+ AF2D867D2DB1FCC900B4F248 /* ScreenSaver.framework in Frameworks */,
+ AF2D867E2DB1FCC900B4F248 /* QuartzCore.framework in Frameworks */,
+ AF2D867F2DB1FCC900B4F248 /* Cocoa.framework in Frameworks */,
+ AF2D86802DB1FCC900B4F248 /* Carbon.framework in Frameworks */,
+ AF2D86812DB1FCC900B4F248 /* OpenGL.framework in Frameworks */,
+ AF2D86822DB1FCC900B4F248 /* libz.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ AF2D869D2DB20F2100B4F248 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AF2D869E2DB20F2100B4F248 /* libjwxyz.a in Frameworks */,
+ AF2D869F2DB20F2100B4F248 /* ScreenSaver.framework in Frameworks */,
+ AF2D86A02DB20F2100B4F248 /* QuartzCore.framework in Frameworks */,
+ AF2D86A12DB20F2100B4F248 /* Cocoa.framework in Frameworks */,
+ AF2D86A22DB20F2100B4F248 /* Carbon.framework in Frameworks */,
+ AF2D86A32DB20F2100B4F248 /* OpenGL.framework in Frameworks */,
+ AF2D86A42DB20F2100B4F248 /* libz.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AF32D9E80F3AD0B40080F535 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AFA82E252DB1EB4B00BEEE3E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AFA82E262DB1EB4B00BEEE3E /* libjwxyz.a in Frameworks */,
+ AFA82E272DB1EB4B00BEEE3E /* ScreenSaver.framework in Frameworks */,
+ AFA82E282DB1EB4B00BEEE3E /* QuartzCore.framework in Frameworks */,
+ AFA82E292DB1EB4B00BEEE3E /* Cocoa.framework in Frameworks */,
+ AFA82E2A2DB1EB4B00BEEE3E /* Carbon.framework in Frameworks */,
+ AFA82E2B2DB1EB4B00BEEE3E /* OpenGL.framework in Frameworks */,
+ AFA82E2C2DB1EB4B00BEEE3E /* libz.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AFAAE38F207D6343007A515C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AFCB48A02DBC0958006296D3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AFCB48A12DBC0958006296D3 /* libjwxyz.a in Frameworks */,
+ AFCB48A22DBC0958006296D3 /* ScreenSaver.framework in Frameworks */,
+ AFCB48A32DBC0958006296D3 /* QuartzCore.framework in Frameworks */,
+ AFCB48A42DBC0958006296D3 /* Cocoa.framework in Frameworks */,
+ AFCB48A52DBC0958006296D3 /* Carbon.framework in Frameworks */,
+ AFCB48A62DBC0958006296D3 /* OpenGL.framework in Frameworks */,
+ AFCB48A72DBC0958006296D3 /* libz.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AFCF83431AF5B515008BB7E1 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
AF70B7A32A8320A6007C1EB8 /* Skulloop.saver */,
AFC644652AE478FA00D589B9 /* Kallisti.saver */,
AF42CF622BE8A3B300675AC7 /* HighVoltage.saver */,
+ AFA82E312DB1EB4B00BEEE3E /* HopfFibration.saver */,
+ AF2D86872DB1FCC900B4F248 /* PlatonicFolding.saver */,
+ AF2D86A92DB20F2100B4F248 /* Klondike.saver */,
+ AFCB48AC2DBC0958006296D3 /* DumpsterFire.saver */,
);
name = Products;
path = ..;
AF0839AA09930C4900277BE9 /* dolphin.c */,
AF241F81107C38DF00046A84 /* dropshadow.c */,
AF241F82107C38DF00046A84 /* dropshadow.h */,
+ AFCB48AF2DBC0BDB006296D3 /* dumpsterfire.c */,
+ AFCB48AE2DBC0BDB006296D3 /* dumpster_model.c */,
AFEC23E41CB6EBC400DE138F /* dymaxionmap.c */,
AF4C300D208569A900BE1DEF /* dymaxionmap-coords.c */,
AF7778C109B65C6A00EA3033 /* e_textures.h */,
AF42CF652BE8A50E00675AC7 /* highvoltage_model.c */,
AF42CF662BE8A50E00675AC7 /* highvoltage.c */,
AF78D18A142DD96E002AAF77 /* hilbert.c */,
+ AFA82E332DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c */,
+ AFA82E342DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c */,
AFC0E8C21CDC60A9008CAFAC /* hydrostat.c */,
AFA55F59099362DF00F3E977 /* hypertorus.c */,
AF3C715D0D624C600030CC0D /* hypnowheel.c */,
AFC644672AE479BB00D589B9 /* kallisti_model.c */,
AFC644692AE479BC00D589B9 /* kallisti.c */,
AFA55F3F0993626E00F3E977 /* klein.c */,
+ AF2D86AB2DB2100A00B4F248 /* klondike.c */,
+ AF2D86AD2DB2100A00B4F248 /* klondike-game.c */,
AFA55A8E0993369100F3E977 /* lament.c */,
AFF1BA0E19A96D8B0016A88D /* lament_model.c */,
AFA55DDD09935DB600F3E977 /* lavalite.c */,
AFA561B309937DCC00F3E977 /* polyhedra.c */,
AFA561B409937DCC00F3E977 /* polyhedra.h */,
AFA560C3099371D500F3E977 /* polytopes.c */,
+ AF2D86892DB1FDB900B4F248 /* platonicfolding.c */,
AFFAB33119158EA80020F021 /* projectiveplane.c */,
AFA5621C099384F600F3E977 /* providence.c */,
AFA55B3F09933EC600F3E977 /* pulsar.c */,
AFC258970988A468000655EE /* discrete.xml */,
AFC258980988A468000655EE /* distort.xml */,
AF77787909B6545E00EA3033 /* dnalogo.xml */,
+ AFCB48B02DBC0BDB006296D3 /* dumpsterfire.xml */,
AFC258990988A468000655EE /* drift.xml */,
AF7F06112A50C18C00E35B45 /* droste.xml */,
AFEC23E51CB6EBDA00DE138F /* dymaxionmap.xml */,
AF42CF642BE8A50E00675AC7 /* highvoltage.xml */,
AF78D18E142DD99A002AAF77 /* hilbert.xml */,
AFC258C50988A468000655EE /* hopalong.xml */,
+ AFA82E352DB1EC3B00BEEE3E /* hopffibration.xml */,
AFC258C60988A468000655EE /* hyperball.xml */,
AFC0E8C31CDC60A9008CAFAC /* hydrostat.xml */,
AFC258C70988A468000655EE /* hypercube.xml */,
AFB8A69A1782BA34004EDB85 /* kaleidocycle.xml */,
AFC644682AE479BB00D589B9 /* kallisti.xml */,
AFC258D40988A468000655EE /* klein.xml */,
+ AF2D86AC2DB2100A00B4F248 /* klondike.xml */,
AFC258D50988A468000655EE /* kumppa.xml */,
AFC258D60988A468000655EE /* lament.xml */,
AFC258D70988A468000655EE /* laser.xml */,
AFC258F40988A469000655EE /* piecewise.xml */,
AFC258F50988A469000655EE /* pinion.xml */,
AFC258F60988A469000655EE /* pipes.xml */,
+ AF2D868A2DB1FDB900B4F248 /* platonicfolding.xml */,
AFC258F70988A469000655EE /* polyhedra.xml */,
AFC258F80988A469000655EE /* polyominoes.xml */,
AFC258F90988A469000655EE /* polytopes.xml */,
children = (
AFDA11211934424D003D397F /* aligned_malloc.c */,
AFDA11221934424D003D397F /* aligned_malloc.h */,
+ AF5D4E3B2D6409AE009BE57E /* ansi-tty.c */,
CE9289D119BD00E200961F22 /* async_netdb.c */,
CE9289D219BD00E300961F22 /* async_netdb.h */,
AF9D473609B52EE0006E59CF /* colorbars.c */,
AFA33BD00B0587EE002B0E7D /* webcollage-helper-cocoa.m */,
AFE943AF19DD54C1000A5E6D /* xft.c */,
AFE943B019DD54C1000A5E6D /* xft.h */,
+ AFBD953C2C504ADC000DA52A /* xftwrap.c */,
+ AFBD953B2C504ADB000DA52A /* xftwrap.h */,
AF480CBB098E37D600FB32B8 /* xlockmore.c */,
AF480C89098E346700FB32B8 /* xlockmore.h */,
AF480C8A098E34AB00FB32B8 /* xlockmoreI.h */,
productReference = AF2D0D3A241D7C870001D8B8 /* EtruscanVenus.saver */;
productType = "com.apple.product-type.bundle";
};
+ AF2D86722DB1FCC900B4F248 /* PlatonicFolding */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = AF2D86842DB1FCC900B4F248 /* Build configuration list for PBXNativeTarget "PlatonicFolding" */;
+ buildPhases = (
+ AF2D86752DB1FCC900B4F248 /* Resources */,
+ AF2D86772DB1FCC900B4F248 /* Sources */,
+ AF2D867B2DB1FCC900B4F248 /* Frameworks */,
+ AF2D86832DB1FCC900B4F248 /* Run Update Info Plist */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ AF2D86732DB1FCC900B4F248 /* PBXTargetDependency */,
+ );
+ name = PlatonicFolding;
+ productName = DangerBall;
+ productReference = AF2D86872DB1FCC900B4F248 /* PlatonicFolding.saver */;
+ productType = "com.apple.product-type.bundle";
+ };
+ AF2D86952DB20F2100B4F248 /* Klondike */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = AF2D86A62DB20F2100B4F248 /* Build configuration list for PBXNativeTarget "Klondike" */;
+ buildPhases = (
+ AF2D86982DB20F2100B4F248 /* Resources */,
+ AF2D869A2DB20F2100B4F248 /* Sources */,
+ AF2D869D2DB20F2100B4F248 /* Frameworks */,
+ AF2D86A52DB20F2100B4F248 /* Run Update Info Plist */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ AF2D86962DB20F2100B4F248 /* PBXTargetDependency */,
+ );
+ name = Klondike;
+ productName = DangerBall;
+ productReference = AF2D86A92DB20F2100B4F248 /* Klondike.saver */;
+ productType = "com.apple.product-type.bundle";
+ };
AF32D9E00F3AD0B40080F535 /* RubikBlocks */ = {
isa = PBXNativeTarget;
buildConfigurationList = AF32D9F10F3AD0B40080F535 /* Build configuration list for PBXNativeTarget "RubikBlocks" */;
productReference = AFA6AB0520999950006D2685 /* GlitchPEG.saver */;
productType = "com.apple.product-type.bundle";
};
+ AFA82E1D2DB1EB4B00BEEE3E /* HopfFibration */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = AFA82E2E2DB1EB4B00BEEE3E /* Build configuration list for PBXNativeTarget "HopfFibration" */;
+ buildPhases = (
+ AFA82E202DB1EB4B00BEEE3E /* Resources */,
+ AFA82E222DB1EB4B00BEEE3E /* Sources */,
+ AFA82E252DB1EB4B00BEEE3E /* Frameworks */,
+ AFA82E2D2DB1EB4B00BEEE3E /* Run Update Info Plist */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ AFA82E1E2DB1EB4B00BEEE3E /* PBXTargetDependency */,
+ );
+ name = HopfFibration;
+ productName = DangerBall;
+ productReference = AFA82E312DB1EB4B00BEEE3E /* HopfFibration.saver */;
+ productType = "com.apple.product-type.bundle";
+ };
AFAAE387207D6343007A515C /* Maze3D */ = {
isa = PBXNativeTarget;
buildConfigurationList = AFAAE399207D6343007A515C /* Build configuration list for PBXNativeTarget "Maze3D" */;
productReference = AFC644652AE478FA00D589B9 /* Kallisti.saver */;
productType = "com.apple.product-type.bundle";
};
+ AFCB48982DBC0958006296D3 /* DumpsterFire */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = AFCB48A92DBC0958006296D3 /* Build configuration list for PBXNativeTarget "DumpsterFire" */;
+ buildPhases = (
+ AFCB489B2DBC0958006296D3 /* Resources */,
+ AFCB489D2DBC0958006296D3 /* Sources */,
+ AFCB48A02DBC0958006296D3 /* Frameworks */,
+ AFCB48A82DBC0958006296D3 /* Run Update Info Plist */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ AFCB48992DBC0958006296D3 /* PBXTargetDependency */,
+ );
+ name = DumpsterFire;
+ productName = DangerBall;
+ productReference = AFCB48AC2DBC0958006296D3 /* DumpsterFire.saver */;
+ productType = "com.apple.product-type.bundle";
+ };
AFCF833B1AF5B515008BB7E1 /* SplitFlap */ = {
isa = PBXNativeTarget;
buildConfigurationList = AFCF834D1AF5B515008BB7E1 /* Build configuration list for PBXNativeTarget "SplitFlap" */;
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
- LastUpgradeCheck = 1500;
+ LastUpgradeCheck = 1620;
TargetAttributes = {
AF08398F09930B6B00277BE9 = {
DevelopmentTeam = 4627ATJELP;
AFF449E02275494400DB8EDB /* DeepStars */,
AF39381A1D0FBD6A00205406 /* Discoball */,
AF77786109B6536000EA3033 /* DNAlogo */,
+ AFCB48982DBC0958006296D3 /* DumpsterFire */,
AFEC23CD1CB6EAE100DE138F /* DymaxionMap */,
AFACE8731CC83458008B24CD /* EnergyStream */,
AFA55E0D09935EDC00F3E977 /* Endgame */,
AF0BF6CD29456B2E000D9473 /* HexTrail */,
AF42CF4E2BE8A3B300675AC7 /* HighVoltage */,
AF78D175142DD8F3002AAF77 /* Hilbert */,
+ AFA82E1D2DB1EB4B00BEEE3E /* HopfFibration */,
AFC0E8AB1CDC601A008CAFAC /* Hydrostat */,
AFA55F420993629000F3E977 /* Hypertorus */,
AF3C71450D624BF50030CC0D /* Hypnowheel */,
AF7510FF1782B5B900380EA1 /* Kaleidocycle */,
AFC644512AE478FA00D589B9 /* Kallisti */,
AFA55F2A0993622F00F3E977 /* Klein */,
+ AF2D86952DB20F2100B4F248 /* Klondike */,
AFA55A790993364300F3E977 /* Lament */,
AFA55DC809935D7000F3E977 /* Lavalite */,
AF4FD6E60CE7A486005EE58E /* Lockward */,
AFD51B1B0F063B4A00471C02 /* Photopile */,
AFA5621F0993852500F3E977 /* Pinion */,
AF4812B30990D3D900FB32B8 /* Pipes */,
+ AF2D86722DB1FCC900B4F248 /* PlatonicFolding */,
AFA5619D09937D7E00F3E977 /* Polyhedra */,
AFA560AE0993718D00F3E977 /* Polytopes */,
AFFAB31519158CE40020F021 /* ProjectivePlane */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AF2D86752DB1FCC900B4F248 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AF2D868C2DB1FDB900B4F248 /* platonicfolding.xml in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ AF2D86982DB20F2100B4F248 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AF2D86B02DB2100A00B4F248 /* klondike.xml in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AF32D9E30F3AD0B40080F535 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
AF56892A26E7060500CCBA38 /* distort.xml in Resources */,
AF56892B26E7060500CCBA38 /* dnalogo.xml in Resources */,
AF7F06172A50C18C00E35B45 /* droste.xml in Resources */,
+ AFCB48B92DBC0BDB006296D3 /* dumpsterfire.xml in Resources */,
AF56892C26E7060500CCBA38 /* dymaxionmap.xml in Resources */,
AF56892D26E7060500CCBA38 /* drift.xml in Resources */,
AF56892E26E7060500CCBA38 /* endgame.xml in Resources */,
AF42CF692BE8A50E00675AC7 /* highvoltage.xml in Resources */,
AF56895F26E7060500CCBA38 /* hilbert.xml in Resources */,
AF56896026E7060500CCBA38 /* hopalong.xml in Resources */,
+ AFA82E362DB1EC3B00BEEE3E /* hopffibration.xml in Resources */,
AF56896126E7060500CCBA38 /* hydrostat.xml in Resources */,
AF56896226E7060500CCBA38 /* hypertorus.xml in Resources */,
AF56896326E7060500CCBA38 /* hypnowheel.xml in Resources */,
AF56896E26E7060500CCBA38 /* kaleidescope.xml in Resources */,
AFC6446F2AE479BC00D589B9 /* kallisti.xml in Resources */,
AF56896F26E7060500CCBA38 /* klein.xml in Resources */,
+ AF2D86B12DB2100A00B4F248 /* klondike.xml in Resources */,
AF56897026E7060500CCBA38 /* kumppa.xml in Resources */,
AF56897126E7060500CCBA38 /* lament.xml in Resources */,
AF56897226E7060500CCBA38 /* lavalite.xml in Resources */,
AF56899026E7060500CCBA38 /* piecewise.xml in Resources */,
AF56899126E7060500CCBA38 /* pinion.xml in Resources */,
AF56899226E7060500CCBA38 /* pipes.xml in Resources */,
+ AF2D868F2DB1FDB900B4F248 /* platonicfolding.xml in Resources */,
AF56899326E7060500CCBA38 /* polyhedra.xml in Resources */,
AF56899426E7060500CCBA38 /* polyominoes.xml in Resources */,
AF56899526E7060500CCBA38 /* polytopes.xml in Resources */,
AF918AE7158FC53D002B5D1E /* distort.xml in Resources */,
AFCF453815986A3000E6E8CC /* dnalogo.xml in Resources */,
AF7F06162A50C18C00E35B45 /* droste.xml in Resources */,
+ AFCB48B32DBC0BDB006296D3 /* dumpsterfire.xml in Resources */,
AFEC23E81CB6EC6800DE138F /* dymaxionmap.xml in Resources */,
AF918AE9158FC53D002B5D1E /* drift.xml in Resources */,
AF918AEA158FC53D002B5D1E /* endgame.xml in Resources */,
AF42CF682BE8A50E00675AC7 /* highvoltage.xml in Resources */,
AF918B14158FC53D002B5D1E /* hilbert.xml in Resources */,
AF918B15158FC53D002B5D1E /* hopalong.xml in Resources */,
+ AFA82E3C2DB1EC3B00BEEE3E /* hopffibration.xml in Resources */,
AFC0E8C71CDC60DE008CAFAC /* hydrostat.xml in Resources */,
AF918B18158FC53D002B5D1E /* hypertorus.xml in Resources */,
AF918B19158FC53D002B5D1E /* hypnowheel.xml in Resources */,
AF918B24158FC53D002B5D1E /* kaleidescope.xml in Resources */,
AFC6446E2AE479BC00D589B9 /* kallisti.xml in Resources */,
AF918B25158FC53D002B5D1E /* klein.xml in Resources */,
+ AF2D86B42DB2100A00B4F248 /* klondike.xml in Resources */,
AF918B26158FC53D002B5D1E /* kumppa.xml in Resources */,
AF918B27158FC53D002B5D1E /* lament.xml in Resources */,
AF918B29158FC53D002B5D1E /* lavalite.xml in Resources */,
AF918B4A158FC53E002B5D1E /* piecewise.xml in Resources */,
AF918B4B158FC53E002B5D1E /* pinion.xml in Resources */,
AF918B4C158FC53E002B5D1E /* pipes.xml in Resources */,
+ AF2D868D2DB1FDB900B4F248 /* platonicfolding.xml in Resources */,
AFCF4547159878D500E6E8CC /* polyhedra.xml in Resources */,
AF918B4E158FC53E002B5D1E /* polyominoes.xml in Resources */,
AF918B4F158FC53E002B5D1E /* polytopes.xml in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AFA82E202DB1EB4B00BEEE3E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AFA82E392DB1EC3B00BEEE3E /* hopffibration.xml in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AFAAE38A207D6343007A515C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AFCB489B2DBC0958006296D3 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AFCB48B42DBC0BDB006296D3 /* dumpsterfire.xml in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AFCF833E1AF5B515008BB7E1 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX";
showEnvVarsInLog = 0;
};
+ AF2D86832DB1FCC900B4F248 /* Run Update Info Plist */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Run Update Info Plist";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX";
+ showEnvVarsInLog = 0;
+ };
+ AF2D86A52DB20F2100B4F248 /* Run Update Info Plist */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Run Update Info Plist";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX\n";
+ showEnvVarsInLog = 0;
+ };
AF32D9F00F3AD0B40080F535 /* Run Update Info Plist */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX";
+ shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX\n";
showEnvVarsInLog = 0;
};
AFA3D69409C03B6200E4CFCA /* Run Update Info Plist */ = {
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX";
+ shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX\n";
showEnvVarsInLog = 0;
};
AFA3D97309C03DD300E4CFCA /* Run Update Info Plist */ = {
shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX";
showEnvVarsInLog = 0;
};
+ AFA82E2D2DB1EB4B00BEEE3E /* Run Update Info Plist */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Run Update Info Plist";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX";
+ showEnvVarsInLog = 0;
+ };
AFAAE398207D6343007A515C /* Run Update Info Plist */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX";
showEnvVarsInLog = 0;
};
+ AFCB48A82DBC0958006296D3 /* Run Update Info Plist */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Run Update Info Plist";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "$SOURCE_ROOT/update-info-plist.pl -q $BUILT_PRODUCTS_DIR/$PRODUCT_NAME$WRAPPER_SUFFIX\n";
+ showEnvVarsInLog = 0;
+ };
AFCCCBB509C033DF00353F4D /* Run Update Info Plist */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AF2D86772DB1FCC900B4F248 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AF2D867A2DB1FCC900B4F248 /* XScreenSaverSubclass.m in Sources */,
+ AF2D868B2DB1FDB900B4F248 /* platonicfolding.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ AF2D869A2DB20F2100B4F248 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AF2D86AE2DB2100A00B4F248 /* klondike.c in Sources */,
+ AF2D86AF2DB2100A00B4F248 /* klondike-game.c in Sources */,
+ AF2D869C2DB20F2100B4F248 /* XScreenSaverSubclass.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AF32D9E50F3AD0B40080F535 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
AF568A6026E7060500CCBA38 /* wormhole.c in Sources */,
AF568A6126E7060500CCBA38 /* xanalogtv.c in Sources */,
AF568A6226E7060500CCBA38 /* xflame.c in Sources */,
+ AF2D86912DB2013200B4F248 /* xftwrap.c in Sources */,
AF568A6326E7060500CCBA38 /* xjack.c in Sources */,
AF568A6426E7060500CCBA38 /* xlyap.c in Sources */,
AF568A6526E7060500CCBA38 /* xmatrix.c in Sources */,
AF568A9326E7060500CCBA38 /* deepstars.c in Sources */,
AF568A9426E7060500CCBA38 /* dnalogo.c in Sources */,
AF568A9526E7060500CCBA38 /* dolphin.c in Sources */,
+ AFCB48B82DBC0BDB006296D3 /* dumpsterfire.c in Sources */,
+ AFCB48B72DBC0BDB006296D3 /* dumpster_model.c in Sources */,
AF568A9626E7060500CCBA38 /* dymaxionmap.c in Sources */,
AF568A0C26E7060500CCBA38 /* dymaxionmap-coords.c in Sources */,
AF568A9726E7060500CCBA38 /* dropshadow.c in Sources */,
AF42CF6C2BE8A50E00675AC7 /* highvoltage_model.c in Sources */,
AF42CF6F2BE8A50E00675AC7 /* highvoltage.c in Sources */,
AF568AB926E7060500CCBA38 /* hilbert.c in Sources */,
+ AFA82E372DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c in Sources */,
+ AFA82E382DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c in Sources */,
AF568ABA26E7060500CCBA38 /* hydrostat.c in Sources */,
AF568ABB26E7060500CCBA38 /* hypertorus.c in Sources */,
AF568ABC26E7060500CCBA38 /* hypnowheel.c in Sources */,
AFC644722AE479BC00D589B9 /* kallisti.c in Sources */,
AFC6446C2AE479BC00D589B9 /* kallisti_model.c in Sources */,
AF568AC226E7060500CCBA38 /* klein.c in Sources */,
+ AF2D86B22DB2100A00B4F248 /* klondike.c in Sources */,
+ AF2D86B32DB2100A00B4F248 /* klondike-game.c in Sources */,
AF568AC326E7060500CCBA38 /* lament.c in Sources */,
AF568AC426E7060500CCBA38 /* lament_model.c in Sources */,
AF568AC526E7060500CCBA38 /* lavalite.c in Sources */,
AF568AD426E7060500CCBA38 /* pinion.c in Sources */,
AF568AD526E7060500CCBA38 /* pipeobjs.c in Sources */,
AF568AD626E7060500CCBA38 /* pipes.c in Sources */,
+ AF2D86902DB1FDB900B4F248 /* platonicfolding.c in Sources */,
AF568AD726E7060500CCBA38 /* polyhedra-gl.c in Sources */,
AF568AD826E7060500CCBA38 /* polyhedra.c in Sources */,
AF568AD926E7060500CCBA38 /* polytopes.c in Sources */,
AF568B0A26E7060500CCBA38 /* toast2.c in Sources */,
AF568B0B26E7060500CCBA38 /* toaster.c in Sources */,
AF568B0C26E7060500CCBA38 /* toaster_base.c in Sources */,
+ AF5D4E3E2D6409AE009BE57E /* ansi-tty.c in Sources */,
AF568B0D26E7060500CCBA38 /* toaster_handle.c in Sources */,
AF568B0E26E7060500CCBA38 /* toaster_handle2.c in Sources */,
AF568B0F26E7060500CCBA38 /* toaster_jet.c in Sources */,
files = (
AF7776EA09B63ABF00EA3033 /* XScreenSaverSubclass.m in Sources */,
AF77770409B63B5F00EA3033 /* phosphor.c in Sources */,
+ AF5D4E3C2D6409AE009BE57E /* ansi-tty.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
AF9189F5158FC35E002B5D1E /* wormhole.c in Sources */,
AF9189F6158FC35E002B5D1E /* xanalogtv.c in Sources */,
AF9189F7158FC35E002B5D1E /* xflame.c in Sources */,
+ AF2D86922DB2014100B4F248 /* xftwrap.c in Sources */,
AF9189F8158FC35E002B5D1E /* xjack.c in Sources */,
AF9189F9158FC35E002B5D1E /* xlyap.c in Sources */,
AF9189FA158FC35E002B5D1E /* xmatrix.c in Sources */,
AF2C2A8A22754C31002112B9 /* deepstars.c in Sources */,
AFCF453715986A2100E6E8CC /* dnalogo.c in Sources */,
AF918A48158FC3BB002B5D1E /* dolphin.c in Sources */,
+ AFCB48B22DBC0BDB006296D3 /* dumpsterfire.c in Sources */,
+ AFCB48B12DBC0BDB006296D3 /* dumpster_model.c in Sources */,
AFEC23E91CB6EC7F00DE138F /* dymaxionmap.c in Sources */,
AF4C300F208569AA00BE1DEF /* dymaxionmap-coords.c in Sources */,
AF918A49158FC3BB002B5D1E /* dropshadow.c in Sources */,
AF62D6342180082100C57C42 /* handsy.c in Sources */,
AF96015D25759124007FA31B /* headroom.c in Sources */,
AF96015B25759124007FA31B /* headroom_model.c in Sources */,
- AF42CF6E2BE8A50E00675AC7 /* highvoltage.c in Sources */,
AF1B0FC31D7AB5500011DBE4 /* hexstrut.c in Sources */,
AF0BF6E829456CCC000D9473 /* hextrail.c in Sources */,
+ AF42CF6E2BE8A50E00675AC7 /* highvoltage.c in Sources */,
AF42CF6B2BE8A50E00675AC7 /* highvoltage_model.c in Sources */,
AF918A6A158FC3E5002B5D1E /* hilbert.c in Sources */,
+ AFA82E3D2DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c in Sources */,
+ AFA82E3E2DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c in Sources */,
AFC0E8C41CDC60B0008CAFAC /* hydrostat.c in Sources */,
AF918A6B158FC3E5002B5D1E /* hypertorus.c in Sources */,
AF918A6C158FC3E5002B5D1E /* hypnowheel.c in Sources */,
AFC644712AE479BC00D589B9 /* kallisti.c in Sources */,
AFC6446B2AE479BC00D589B9 /* kallisti_model.c in Sources */,
AF918A70158FC417002B5D1E /* klein.c in Sources */,
+ AF2D86B52DB2100A00B4F248 /* klondike.c in Sources */,
+ AF2D86B62DB2100A00B4F248 /* klondike-game.c in Sources */,
AF918A71158FC417002B5D1E /* lament.c in Sources */,
AFF1BA1019A96D8B0016A88D /* lament_model.c in Sources */,
AF918A72158FC417002B5D1E /* lavalite.c in Sources */,
AF918A7D158FC417002B5D1E /* pinion.c in Sources */,
AF918A7E158FC417002B5D1E /* pipeobjs.c in Sources */,
AF918A7F158FC417002B5D1E /* pipes.c in Sources */,
+ AF2D868E2DB1FDB900B4F248 /* platonicfolding.c in Sources */,
AFCF4545159878C300E6E8CC /* polyhedra-gl.c in Sources */,
AFCF4546159878C300E6E8CC /* polyhedra.c in Sources */,
AF918A82158FC417002B5D1E /* polytopes.c in Sources */,
AF918AA7158FC473002B5D1E /* toast2.c in Sources */,
AF918AA8158FC473002B5D1E /* toaster.c in Sources */,
AF918AA9158FC473002B5D1E /* toaster_base.c in Sources */,
+ AF5D4E3D2D6409AE009BE57E /* ansi-tty.c in Sources */,
AF918AAA158FC473002B5D1E /* toaster_handle.c in Sources */,
AF918AAB158FC473002B5D1E /* toaster_handle2.c in Sources */,
AF918AAC158FC473002B5D1E /* toaster_jet.c in Sources */,
AF9D4DB609B5B71E006E59CF /* analogtv.c in Sources */,
AF9D4DC409B5B87D006E59CF /* bsod.c in Sources */,
AF9D4DD409B5B990006E59CF /* apple2.c in Sources */,
+ AFBD953D2C504AFC000DA52A /* xftwrap.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
buildActionMask = 2147483647;
files = (
AF9D4DF209B5BB19006E59CF /* XScreenSaverSubclass.m in Sources */,
+ AF5D4E3F2D6409AE009BE57E /* ansi-tty.c in Sources */,
AF9D4DF309B5BB19006E59CF /* analogtv.c in Sources */,
AF9D4DF509B5BB19006E59CF /* apple2.c in Sources */,
AF9D4E0609B5BC9D006E59CF /* apple2-main.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AFA82E222DB1EB4B00BEEE3E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AFA82E3A2DB1EC3B00BEEE3E /* hacks/glx/hopffibration.c in Sources */,
+ AFA82E3B2DB1EC3B00BEEE3E /* hacks/glx/hopfanimations.c in Sources */,
+ AFA82E242DB1EB4B00BEEE3E /* XScreenSaverSubclass.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AFAAE38C207D6343007A515C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ AFCB489D2DBC0958006296D3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AFCB48B52DBC0BDB006296D3 /* dumpster_model.c in Sources */,
+ AFCB48B62DBC0BDB006296D3 /* dumpsterfire.c in Sources */,
+ AFCB489F2DBC0958006296D3 /* XScreenSaverSubclass.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
AFCF83401AF5B515008BB7E1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
target = AF2D0D25241D7C870001D8B8 /* EtruscanVenus */;
targetProxy = AF2D0D42241D7D9F0001D8B8 /* PBXContainerItemProxy */;
};
+ AF2D86732DB1FCC900B4F248 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = AF4808C0098C3B6C00FB32B8 /* jwxyz */;
+ targetProxy = AF2D86742DB1FCC900B4F248 /* PBXContainerItemProxy */;
+ };
+ AF2D86942DB2032D00B4F248 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = AF2D86722DB1FCC900B4F248 /* PlatonicFolding */;
+ targetProxy = AF2D86932DB2032D00B4F248 /* PBXContainerItemProxy */;
+ };
+ AF2D86962DB20F2100B4F248 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = AF4808C0098C3B6C00FB32B8 /* jwxyz */;
+ targetProxy = AF2D86972DB20F2100B4F248 /* PBXContainerItemProxy */;
+ };
+ AF2D86B82DB2105300B4F248 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = AF2D86952DB20F2100B4F248 /* Klondike */;
+ targetProxy = AF2D86B72DB2105300B4F248 /* PBXContainerItemProxy */;
+ };
AF32D9E10F3AD0B40080F535 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = AF4808C0098C3B6C00FB32B8 /* jwxyz */;
target = AFA6AAF020999950006D2685 /* GlitchPEG */;
targetProxy = AFA6AB1020999A9A006D2685 /* PBXContainerItemProxy */;
};
+ AFA82E1E2DB1EB4B00BEEE3E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = AF4808C0098C3B6C00FB32B8 /* jwxyz */;
+ targetProxy = AFA82E1F2DB1EB4B00BEEE3E /* PBXContainerItemProxy */;
+ };
+ AFA82E402DB1ECC600BEEE3E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = AFA82E1D2DB1EB4B00BEEE3E /* HopfFibration */;
+ targetProxy = AFA82E3F2DB1ECC600BEEE3E /* PBXContainerItemProxy */;
+ };
AFAAE388207D6343007A515C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = AF4808C0098C3B6C00FB32B8 /* jwxyz */;
target = AF9771D60989DC4A001F8B92 /* SaverTester */;
targetProxy = AFCAD5F80992DFE00009617A /* PBXContainerItemProxy */;
};
+ AFCB48992DBC0958006296D3 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = AF4808C0098C3B6C00FB32B8 /* jwxyz */;
+ targetProxy = AFCB489A2DBC0958006296D3 /* PBXContainerItemProxy */;
+ };
+ AFCB48BB2DBC0C3E006296D3 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = AFCB48982DBC0958006296D3 /* DumpsterFire */;
+ targetProxy = AFCB48BA2DBC0C3E006296D3 /* PBXContainerItemProxy */;
+ };
AFCF833C1AF5B515008BB7E1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = AF4808C0098C3B6C00FB32B8 /* jwxyz */;
};
name = Release;
};
+ AF2D86852DB1FCC900B4F248 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = (
+ "USE_GL=1",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ AF2D86862DB1FCC900B4F248 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = (
+ "USE_GL=1",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ AF2D86A72DB20F2100B4F248 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = (
+ "USE_GL=1",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ AF2D86A82DB20F2100B4F248 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = (
+ "USE_GL=1",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
AF32D9F20F3AD0B40080F535 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
+ AFA82E2F2DB1EB4B00BEEE3E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = (
+ "USE_GL=1",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ AFA82E302DB1EB4B00BEEE3E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = (
+ "USE_GL=1",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
AFAAE39A207D6343007A515C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
+ AFCB48AA2DBC0958006296D3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = (
+ "USE_GL=1",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ AFCB48AB2DBC0958006296D3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = (
+ "USE_GL=1",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
AFCF834E1AF5B515008BB7E1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ AF2D86842DB1FCC900B4F248 /* Build configuration list for PBXNativeTarget "PlatonicFolding" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ AF2D86852DB1FCC900B4F248 /* Debug */,
+ AF2D86862DB1FCC900B4F248 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ AF2D86A62DB20F2100B4F248 /* Build configuration list for PBXNativeTarget "Klondike" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ AF2D86A72DB20F2100B4F248 /* Debug */,
+ AF2D86A82DB20F2100B4F248 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
AF32D9F10F3AD0B40080F535 /* Build configuration list for PBXNativeTarget "RubikBlocks" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ AFA82E2E2DB1EB4B00BEEE3E /* Build configuration list for PBXNativeTarget "HopfFibration" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ AFA82E2F2DB1EB4B00BEEE3E /* Debug */,
+ AFA82E302DB1EB4B00BEEE3E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
AFAAE399207D6343007A515C /* Build configuration list for PBXNativeTarget "Maze3D" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ AFCB48A92DBC0958006296D3 /* Build configuration list for PBXNativeTarget "DumpsterFire" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ AFCB48AA2DBC0958006296D3 /* Debug */,
+ AFCB48AB2DBC0958006296D3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
AFCF834D1AF5B515008BB7E1 /* Build configuration list for PBXNativeTarget "SplitFlap" */ = {
isa = XCConfigurationList;
buildConfigurations = (
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1620"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES"
+ buildArchitectures = "Automatic">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AFCB48982DBC0958006296D3"
+ BuildableName = "DumpsterFire.saver"
+ BlueprintName = "DumpsterFire"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF9771D60989DC4A001F8B92"
+ BuildableName = "SaverTester.app"
+ BlueprintName = "SaverTester"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "SELECTED_SAVER"
+ value = "DumpsterFire"
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF9771D60989DC4A001F8B92"
+ BuildableName = "SaverTester.app"
+ BlueprintName = "SaverTester"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1530"
+ LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1620"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES"
+ buildArchitectures = "Automatic">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AFA82E1D2DB1EB4B00BEEE3E"
+ BuildableName = "HopfFibration.saver"
+ BlueprintName = "HopfFibration"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF9771D60989DC4A001F8B92"
+ BuildableName = "SaverTester.app"
+ BlueprintName = "SaverTester"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "SELECTED_SAVER"
+ value = "HopfFibration"
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF9771D60989DC4A001F8B92"
+ BuildableName = "SaverTester.app"
+ BlueprintName = "SaverTester"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1620"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES"
+ buildArchitectures = "Automatic">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF2D86952DB20F2100B4F248"
+ BuildableName = "Klondike.saver"
+ BlueprintName = "Klondike"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF9771D60989DC4A001F8B92"
+ BuildableName = "SaverTester.app"
+ BlueprintName = "SaverTester"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "SELECTED_SAVER"
+ value = "Klondike"
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF9771D60989DC4A001F8B92"
+ BuildableName = "SaverTester.app"
+ BlueprintName = "SaverTester"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1620"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES"
+ buildArchitectures = "Automatic">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF2D86722DB1FCC900B4F248"
+ BuildableName = "PlatonicFolding.saver"
+ BlueprintName = "PlatonicFolding"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF9771D60989DC4A001F8B92"
+ BuildableName = "SaverTester.app"
+ BlueprintName = "SaverTester"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "SELECTED_SAVER"
+ value = "PlatonicFolding"
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "AF9771D60989DC4A001F8B92"
+ BuildableName = "SaverTester.app"
+ BlueprintName = "SaverTester"
+ ReferencedContainer = "container:xscreensaver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "NO"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
- selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1500"
+ LastUpgradeVersion = "1620"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
these libraries. Append "-dev" or "-devel" to most of these:
perl pkg-config gettext intltool libx11 libxext libxi libxt libxft
- libxinerama libxrandr libxxf86vm libgl libglu libgle libgtk-3-0
+ libxinerama libxrandr libxxf86vm libgl libglu libgle libgtk-3
libgdk-pixbuf2.0 libjpeg libxml2 libpam libsystemd elogind
BSD systems might need gmake instead of make.
Version History
===============================================================================
+6.10 * New hacks, `dumpsterfire', `hopffibration', `platonicfolding' and
+ `klondike'.
+ * Rewrote the VT100 emulator for 'apple2' and 'phosphor'.
+ Supports inverse and DEC Special Graphics.
+ * X11: `phosphor' scrolls fast again on "modern" Linux systems.
+ * BSOD supports systemd and bitlocker.
+ * Android: Fixed bug where photo access was not being requested.
+
6.09 * New hacks,`kallisti' and `highvoltage'.
* Formal-wear for `headroom'.
-# generated automatically by aclocal 1.16.5 -*- Autoconf -*-
+# generated automatically by aclocal 1.17 -*- Autoconf -*-
-# Copyright (C) 1996-2021 Free Software Foundation, Inc.
+# Copyright (C) 1996-2024 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
AC_SUBST([$1])dnl
])
-# Copyright (C) 2006-2021 Free Software Foundation, Inc.
+# Copyright (C) 2006-2024 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
dnalogo \
drift \
droste \
+ dumpsterfire \
dymaxionmap \
endgame \
energystream \
highvoltage \
hilbert \
hopalong \
+ hopffibration \
hypertorus \
hypnowheel \
ifs \
kaleidocycle \
kallisti \
klein \
+ klondike \
kumppa \
lament \
lavalite \
piecewise \
pinion \
pipes \
+ platonicfolding \
polyominoes \
polytopes \
pong \
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.4.0'
+ classpath 'com.android.tools.build:gradle:8.9.1'
}
}
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-#distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-all.zip
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
}
android {
- compileSdkVersion 34
- buildToolsVersion "34.0.0"
+ compileSdkVersion 36
+ buildToolsVersion "36.0.0"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
# Some savers occupy more than one source file:
LOCAL_SRC_FILES += \
+ hacks/ansi-tty.c \
hacks/glx/b_draw.c \
hacks/glx/b_lockglue.c \
hacks/glx/b_sphere.c \
hacks/glx/cow_tail.c \
hacks/glx/cow_udder.c \
hacks/glx/dolphin.c \
+ hacks/glx/dumpster_model.c \
hacks/glx/dymaxionmap-coords.c \
hacks/glx/gllist.c \
hacks/glx/glschool_alg.c \
hacks/glx/handsy_model.c \
hacks/glx/headroom_model.c \
hacks/glx/highvoltage_model.c \
+ hacks/glx/hopfanimations.c \
hacks/glx/involute.c \
hacks/glx/kallisti_model.c \
+ hacks/glx/klondike-game.c \
hacks/glx/lament_model.c \
hacks/glx/pipeobjs.c \
hacks/glx/quickhull.c \
utils/usleep.c \
utils/utf8wc.c \
utils/xft.c \
+ utils/xftwrap.c \
utils/xshm.c \
utils/yarandom.c \
import android.app.WallpaperManager;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.provider.Settings;
}
void checkPermission() {
- // RES introduced in API 16
- String permission = Manifest.permission.READ_EXTERNAL_STORAGE;
+ String permission = "";
+ if (Build.VERSION.SDK_INT >= 33) {
+ permission = Manifest.permission.READ_MEDIA_IMAGES;
+ } else {
+ permission = Manifest.permission.READ_EXTERNAL_STORAGE;
+ }
if (permissionGranted(permission)) {
withProceed();
} else {
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * xscreensaver, Copyright © 2016-2021 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 2016-2024 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
import android.graphics.Color;
import android.graphics.Matrix;
import android.net.Uri;
+import android.os.Build;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
public Object[] checkThenLoadRandomImage (int target_width, int target_height,
boolean rotate_p) {
- // RES introduced in API 16
- String permission = Manifest.permission.READ_EXTERNAL_STORAGE;
+
+ String permission = "";
+ if (Build.VERSION.SDK_INT >= 33) {
+ permission = Manifest.permission.READ_MEDIA_IMAGES;
+ } else {
+ permission = Manifest.permission.READ_EXTERNAL_STORAGE;
+ }
if (havePermission(permission)) {
return loadRandomImage(target_width,target_height,rotate_p);
# Check includes
if test "$have_avutil" = yes; then
ac_save_avutil_CPPFLAGS="$CPPFLAGS"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libavutil includes" >&5
+printf %s "checking for libavutil includes... " >&6; }
+if test ${ac_cv_avutil_config_cflags+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) ac_cv_avutil_config_cflags=`$pkg_config --cflags libavutil` ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_avutil_config_cflags" >&5
+printf "%s\n" "$ac_cv_avutil_config_cflags" >&6; }
+ ac_avutil_config_cflags=$ac_cv_avutil_config_cflags
CPPFLAGS="$CPPFLAGS $ac_avutil_config_cflags"
have_avutil=no
if test "$have_avcodec" = yes; then
ac_save_avcodec_CPPFLAGS="$CPPFLAGS"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libavcodec includes" >&5
+printf %s "checking for libavcodec includes... " >&6; }
+if test ${ac_cv_avcodec_config_cflags+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) ac_cv_avcodec_config_cflags=`$pkg_config --cflags libavcodec` ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_avcodec_config_cflags" >&5
+printf "%s\n" "$ac_cv_avcodec_config_cflags" >&6; }
+ ac_avcodec_config_cflags=$ac_cv_avcodec_config_cflags
CPPFLAGS="$CPPFLAGS $ac_avcodec_config_cflags"
have_avcodec=no
if test "$have_avformat" = yes; then
ac_save_avformat_CPPFLAGS="$CPPFLAGS"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libavformat includes" >&5
+printf %s "checking for libavformat includes... " >&6; }
+if test ${ac_cv_avformat_config_cflags+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) ac_cv_avformat_config_cflags=`$pkg_config --cflags libavformat` ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_avformat_config_cflags" >&5
+printf "%s\n" "$ac_cv_avformat_config_cflags" >&6; }
+ ac_avformat_config_cflags=$ac_cv_avformat_config_cflags
CPPFLAGS="$CPPFLAGS $ac_avformat_config_cflags"
have_avformat=no
if test "$have_swscale" = yes; then
ac_save_swscale_CPPFLAGS="$CPPFLAGS"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libswscale includes" >&5
+printf %s "checking for libswscale includes... " >&6; }
+if test ${ac_cv_swscale_config_cflags+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) ac_cv_swscale_config_cflags=`$pkg_config --cflags libswscale` ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_swscale_config_cflags" >&5
+printf "%s\n" "$ac_cv_swscale_config_cflags" >&6; }
+ ac_swscale_config_cflags=$ac_cv_swscale_config_cflags
CPPFLAGS="$CPPFLAGS $ac_swscale_config_cflags"
have_swscale=no
if test "$have_swresample" = yes; then
ac_save_swresample_CPPFLAGS="$CPPFLAGS"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libswresample includes" >&5
+printf %s "checking for libswresample includes... " >&6; }
+if test ${ac_cv_swresample_config_cflags+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) ac_cv_swresample_config_cflags=`$pkg_config --cflags libswresample` ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_swresample_config_cflags" >&5
+printf "%s\n" "$ac_cv_swresample_config_cflags" >&6; }
+ ac_swresample_config_cflags=$ac_cv_swresample_config_cflags
CPPFLAGS="$CPPFLAGS $ac_swresample_config_cflags"
have_swresample=no
"$have_swscale" = yes -a \
"$have_swresample" = yes ; then
have_ffmpeg=yes
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ffmpeg includes" >&5
-printf %s "checking for ffmpeg includes... " >&6; }
-if test ${ac_cv_ffmpeg_config_cflags+y}
-then :
- printf %s "(cached) " >&6
-else case e in #(
- e) ac_cv_ffmpeg_config_cflags=`$pkg_config --cflags $pkgs` ;;
-esac
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ffmpeg_config_cflags" >&5
-printf "%s\n" "$ac_cv_ffmpeg_config_cflags" >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ffmpeg libs" >&5
printf %s "checking for ffmpeg libs... " >&6; }
if test ${ac_cv_ffmpeg_config_libs+y}
FFMPEG_OBJS='$(FFMPEG_OBJS)'
fi
-ac_ffmpeg_config_cflags=$ac_cv_ffmpeg_config_cflags
+ac_ffmpeg_config_cflags=" \
+ $ac_avutil_config_cflags \
+ $ac_avcodec_config_cflags \
+ $ac_avformat_config_cflags \
+ $ac_swscale_config_cflags \
+ $ac_swresample_config_cflags \
+"
ac_ffmpeg_config_libs=$ac_cv_ffmpeg_config_libs
FFMPEG_CFLAGS="$ac_ffmpeg_config_cflags"
FFMPEG_LIBS="$ac_ffmpeg_config_libs"
# Check includes
if test "$have_avutil" = yes; then
ac_save_avutil_CPPFLAGS="$CPPFLAGS"
+ AC_CACHE_CHECK([for libavutil includes], ac_cv_avutil_config_cflags,
+ [ac_cv_avutil_config_cflags=`$pkg_config --cflags libavutil`])
+ ac_avutil_config_cflags=$ac_cv_avutil_config_cflags
CPPFLAGS="$CPPFLAGS $ac_avutil_config_cflags"
have_avutil=no
AC_CHECK_X_HEADER(libavutil/avutil.h, [have_avutil=yes])
if test "$have_avcodec" = yes; then
ac_save_avcodec_CPPFLAGS="$CPPFLAGS"
+ AC_CACHE_CHECK([for libavcodec includes], ac_cv_avcodec_config_cflags,
+ [ac_cv_avcodec_config_cflags=`$pkg_config --cflags libavcodec`])
+ ac_avcodec_config_cflags=$ac_cv_avcodec_config_cflags
CPPFLAGS="$CPPFLAGS $ac_avcodec_config_cflags"
have_avcodec=no
AC_CHECK_X_HEADER(libavcodec/avcodec.h, [have_avcodec=yes])
if test "$have_avformat" = yes; then
ac_save_avformat_CPPFLAGS="$CPPFLAGS"
+ AC_CACHE_CHECK([for libavformat includes], ac_cv_avformat_config_cflags,
+ [ac_cv_avformat_config_cflags=`$pkg_config --cflags libavformat`])
+ ac_avformat_config_cflags=$ac_cv_avformat_config_cflags
CPPFLAGS="$CPPFLAGS $ac_avformat_config_cflags"
have_avformat=no
AC_CHECK_X_HEADER(libavformat/avformat.h, [have_avformat=yes])
if test "$have_swscale" = yes; then
ac_save_swscale_CPPFLAGS="$CPPFLAGS"
+ AC_CACHE_CHECK([for libswscale includes], ac_cv_swscale_config_cflags,
+ [ac_cv_swscale_config_cflags=`$pkg_config --cflags libswscale`])
+ ac_swscale_config_cflags=$ac_cv_swscale_config_cflags
CPPFLAGS="$CPPFLAGS $ac_swscale_config_cflags"
have_swscale=no
AC_CHECK_X_HEADER(libswscale/swscale.h, [have_swscale=yes])
if test "$have_swresample" = yes; then
ac_save_swresample_CPPFLAGS="$CPPFLAGS"
+ AC_CACHE_CHECK([for libswresample includes], ac_cv_swresample_config_cflags,
+ [ac_cv_swresample_config_cflags=`$pkg_config --cflags libswresample`])
+ ac_swresample_config_cflags=$ac_cv_swresample_config_cflags
CPPFLAGS="$CPPFLAGS $ac_swresample_config_cflags"
have_swresample=no
AC_CHECK_X_HEADER(libswresample/swresample.h, [have_swresample=yes])
"$have_swscale" = yes -a \
"$have_swresample" = yes ; then
have_ffmpeg=yes
- AC_CACHE_CHECK([for ffmpeg includes], ac_cv_ffmpeg_config_cflags,
- [ac_cv_ffmpeg_config_cflags=`$pkg_config --cflags $pkgs`])
AC_CACHE_CHECK([for ffmpeg libs], ac_cv_ffmpeg_config_libs,
[ac_cv_ffmpeg_config_libs=`$pkg_config --libs $pkgs`])
AC_DEFINE(HAVE_FFMPEG)
FFMPEG_OBJS='$(FFMPEG_OBJS)'
fi
-ac_ffmpeg_config_cflags=$ac_cv_ffmpeg_config_cflags
+ac_ffmpeg_config_cflags=" \
+ $ac_avutil_config_cflags \
+ $ac_avcodec_config_cflags \
+ $ac_avformat_config_cflags \
+ $ac_swscale_config_cflags \
+ $ac_swresample_config_cflags \
+"
ac_ffmpeg_config_libs=$ac_cv_ffmpeg_config_libs
FFMPEG_CFLAGS="$ac_ffmpeg_config_cflags"
FFMPEG_LIBS="$ac_ffmpeg_config_libs"
! a screen saver and locker for the X window system
! by Jamie Zawinski
!
-! version 6.09
-! 07-Jun-2024
+! version 6.10
+! 27-Apr-2025
!
! See "man xscreensaver" for more info. The latest version is always
! available at https://www.jwz.org/xscreensaver/
@GL_KLUDGE@ GL: papercube --root \n\
@GL_KLUDGE@ GL: skulloop --root \n\
@GL_KLUDGE@ GL: highvoltage --root \n\
-@GL_KLUDGE@ GL: kallisti --root \n
+@GL_KLUDGE@ GL: kallisti --root \n\
+@GL_KLUDGE@ GL: klondike --root \n\
+@GL_KLUDGE@ GL: dumpsterfire --root \n\
+@GL_KLUDGE@ GL: hopffibration --root \n\
+@GL_KLUDGE@ GL: platonicfolding --root \n
*hacks.decayscreen.name: Decay Screen
*hacks.deepstars.name: Deep Stars
*hacks.dnalogo.name: DNA Logo
+*hacks.dumpsterfire.name: Dumpster Fire
*hacks.dymaxionmap.name: Dymaxion Map
*hacks.energystream.name: Energy Stream
*hacks.etruscanvenus.name: Etruscan Venus
*hacks.hexstrut.name: Hex Strut
*hacks.hextrail.name: Hex Trail
*hacks.highvoltage.name: High Voltage
+*hacks.hopffibration.name: Hopf Fibration
*hacks.ifs.name: IFS
*hacks.imsmap.name: IMS Map
*hacks.jigglypuff.name: Jiggly Puff
*hacks.pacman.name: Pac-Man
*hacks.papercube.name: Paper Cube
*hacks.photopile.name: Photo Pile
+*hacks.platonicfolding.name:Platonic Folding
*hacks.popsquares.name: Pop Squares
*hacks.projectiveplane.name:Projective Plane
*hacks.quasicrystal.name: Quasi-Crystal
GL: papercube --root \\n\
GL: skulloop --root \\n\
GL: highvoltage --root \\n\
- GL: kallisti --root \\n",
+ GL: kallisti --root \\n\
+ GL: klondike --root \\n\
+ GL: dumpsterfire --root \\n\
+ GL: hopffibration --root \\n\
+ GL: platonicfolding --root \\n",
"*hacks.antinspect.name: Ant Inspect",
"*hacks.antmaze.name: Ant Maze",
"*hacks.antspotlight.name: Ant Spotlight",
"*hacks.decayscreen.name: Decay Screen",
"*hacks.deepstars.name: Deep Stars",
"*hacks.dnalogo.name: DNA Logo",
+"*hacks.dumpsterfire.name: Dumpster Fire",
"*hacks.dymaxionmap.name: Dymaxion Map",
"*hacks.energystream.name: Energy Stream",
"*hacks.etruscanvenus.name: Etruscan Venus",
"*hacks.hexstrut.name: Hex Strut",
"*hacks.hextrail.name: Hex Trail",
"*hacks.highvoltage.name: High Voltage",
+"*hacks.hopffibration.name: Hopf Fibration",
"*hacks.ifs.name: IFS",
"*hacks.imsmap.name: IMS Map",
"*hacks.jigglypuff.name: Jiggly Puff",
"*hacks.pacman.name: Pac-Man",
"*hacks.papercube.name: Paper Cube",
"*hacks.photopile.name: Photo Pile",
+"*hacks.platonicfolding.name:Platonic Folding",
"*hacks.popsquares.name: Pop Squares",
"*hacks.projectiveplane.name:Projective Plane",
"*hacks.quasicrystal.name: Quasi-Crystal",
/* auth.h --- Providing authentication mechanisms.
- * Copyright © 1993-2022 Jamie Zawinski <jwz@jwz.org>
+ * Copyright © 1993-2024 Jamie Zawinski <jwz@jwz.org>
* (c) 2007, Quest Software, Inc. All rights reserved.
* This file is part of XScreenSaver.
*
auth_response **resp);
extern void xscreensaver_auth_finished (void *closure, Bool authenticated_p);
extern void xscreensaver_splash (void *root_widget, Bool disable_settings_p);
+extern void xscreensaver_auth_test_mode (void); /* for test-passwd.c */
#endif /* __XSCREENSAVER_AUTH_H__ */
}
+/* This is horrible, but if we don't ignore XInput events when running
+ test-passwd.c, then every mouse is delivered twice (both XI and Xlib)
+ so we only see every other page, because every click is a double-click.
+ Only test_auth_conv() calls this to set this flag.
+ */
+static Bool test_mode_ignore_xi_events = False;
+void
+xscreensaver_auth_test_mode (void)
+{
+ test_mode_ignore_xi_events = True;
+}
+
+
static void
gui_main_loop (window_state *ws, Bool splash_p, Bool notification_p)
{
Bool ok =
xinput_event_to_xlib (xev.xcookie.evtype, xev.xcookie.data, &ev2);
XFreeEventData (ws->dpy, &xev.xcookie);
+ if (test_mode_ignore_xi_events)
+ ok = False;
if (ok)
xev = ev2;
}
nmsgs = 0;
msg = (auth_message *) calloc (100, sizeof(*msg));
+ xscreensaver_auth_test_mode();
+
# define DIALOG() \
fprintf (stderr, "\n%s: page %d\n", blurb(), page++); \
xscreensaver_auth_conv (closure, nmsgs, msg, resp); \
msg[nmsgs].type = AUTH_MSGTYPE_PROMPT_ECHO;
- msg[nmsgs].msg = "1/1 Page Five visible text";
+ msg[nmsgs].msg = "1/1 Page Five visible text"
+ "over several lines, probably like three or four; y and g.";
nmsgs++;
msg[nmsgs].type = AUTH_MSGTYPE_PROMPT_ECHO;
to get the default host and display number.
.TP 8
.B PATH
-to find the sub-programs to run. However, note that the sub-programs
-actually launched by \fIxscreensaver-settings\fP for display in the
-inline preview pane, but are launched by the \fIxscreensaver\fP daemon
-when run full screen, so the \fB$PATH\fP setting in both processes matters.
+to find the sub-programs to run. However, note that the sub-programs are
+launched by \fIxscreensaver-settings\fP for display in the inline preview
+pane, but by the \fIxscreensaver\fP daemon when run full screen, so the
+\fB$PATH\fP setting in both processes matters.
.TP 8
.B HOME
for the directory in which to read and write the \fI.xscreensaver\fP file.
* - When the system is about to go to sleep (e.g., laptop lid closing)
* it locks the screen *before* the system goes to sleep, by running
* "xscreensaver-command -suspend". And then when the system wakes
- * up again, it runs "xscreensaver-command -deactivate" to force the
+ * up again, it runs "xscreensaver-command --deactivate" to force the
* unlock dialog to appear immediately.
*
* - When another process on the system asks for the screen saver to be
* inhibited (e.g. because a video is playing) this program periodically
- * runs "xscreensaver-command -deactivate" to keep the display un-blanked.
+ * runs "xscreensaver-command --deactivate" to keep the display un-blanked.
* It does this until the other program asks for it to stop.
*
* For this to work at all, you must either:
* A: Be running GNOME's "org.gnome.SessionManager" D-Bus service; or
* B: Be running KDE's "org.kde.Solid.PowerManagement.PolicyAgent" svc; or
* C: Prevent your desktop environment from running the
- * "org.freedesktop.ScreenSaver "service.
+ * "org.freedesktop.ScreenSaver" service.
*
*
*****************************************************************************
*
* For decades, the traditional way for a video player to temporarily
* inhibit the screen saver was to have a heartbeat command that ran
- * "xscreensaver-command -deactivate" once a minute while the video was
+ * "xscreensaver-command --deactivate" once a minute while the video was
* playing, and ceased when the video was paused or stopped. The reason
* to do it as a heartbeat rather than a toggle is so that the player
* fails SAFE -- if the player exits abnormally, the heart stops beating,
*
* To recap: because the existing video players decided to delete the
* single line of code that they already had -- the heartbeat call to
- * "xscreensaver-command -deactivate" -- we had to respond by adding
+ * "xscreensaver-command --deactivate" -- we had to respond by adding
* TWELVE HUNDRED LINES of complicated code that talks to a server that
* may not be running, and that may not allow us to connect, and that may
* not work properly anyway.
* Bad Chromium bug #1:
*
* It inhibits when only audio is playing, and does so with the same
- * message as for audio, so we can't tell them apart. This means that,
+ * message as for video, so we can't tell them apart. This means that,
* unlike Firefox, playing audio-only in Chromium will prevent your
* screen from blanking.
*
* Bad Chromium bug #2:
*
* It inhibits when short looping videos are playing. Many sites
- * (including Twitter) auto-convert GIFs to looping MP4s to save
- * bandwidth, so Chromium will inhibit any time such a GIF is visible.
+ * auto-convert GIFs to looping MP4s to save bandwidth, so Chromium will
+ * inhibit any time such a GIF is visible.
*
* The proper way for Chrome to fix this would be to stop inhibiting once
* the video loops. That way your multi-hour movie inhibits properly, but
- * your looping GIF only inhibits for the first few seconds.
- *
+ * your looping GIF only inhibits for the first few seconds. Other
+ * reasonable choices might include: do not inhibit for silent or muted
+ * videos; only inhibit for full-screen videos.
+ *
*
*****************************************************************************
*
* I have not verified this:
*
* ~/.mplayer/config:
- * heartbeat-cmd="xscreensaver-command -deactivate >&- 2>&- &"
+ * heartbeat-cmd="xscreensaver-command --deactivate >&- 2>&- &"
*
*
*****************************************************************************
*
* This setting prevents the screen from blanking, and has a long history
* of becoming turned on accidentally. Tries org.freedesktop.ScreenSaver
- * and others before falling back to "xscreensaver-command -deactivate"
+ * and others before falling back to "xscreensaver-command --deactivate"
* as a heartbeat.
*
*
* "org.gnome.SessionManager" with the same behavior, we should probably
* listen to "InhibitorAdded" on that as well.
*
- * - Run under valgrind to check for any memory leaks.
- *
* - Apparently the two different desktops have managed to come up with
* *three* different ways for dbus clients to ask the question, "is the
* screen currently blanked?" We should probably also respond to these:
* /ScreenSaver org.freedesktop.ScreenSaver \
* UnInhibit u 1792821391
*
- * https://github.com/mato/xscreensaver-systemd
- *
*
*****************************************************************************
*/
{
char buf[1024];
int rc;
- sprintf (buf, "xscreensaver-command %.100s -%.100s",
+ sprintf (buf, "xscreensaver-command %.100s --%.100s",
(verbose_p ? "--verbose" : "--quiet"),
cmd);
if (verbose_p)
-# hacks/Makefile.in --- xscreensaver, Copyright © 1997-2024 Jamie Zawinski.
+# hacks/Makefile.in --- xscreensaver, Copyright © 1997-2025 Jamie Zawinski.
# the `../configure' script generates `hacks/Makefile' from this file.
@SET_MAKE@
X_EXTRA_LIBS = @X_EXTRA_LIBS@
XFT_LIBS = @XFT_LIBS@
+FFMPEG_CFLAGS = @FFMPEG_CFLAGS@
+
# Note: see comment in ../driver/Makefile.in for explanation of X_LIBS, etc.
#
HACK_PRE = $(LIBS) $(X_LIBS)
$(UTILS_BIN)/colorbars.o \
$(UTILS_BIN)/textclient.o $(UTILS_BIN)/aligned_malloc.o \
$(UTILS_BIN)/thread_util.o $(UTILS_BIN)/pow2.o \
- $(UTILS_BIN)/xft.o $(UTILS_BIN)/utf8wc.o \
- $(UTILS_BIN)/font-retry.o
+ $(UTILS_BIN)/xft.o $(UTILS_BIN)/xftwrap.o \
+ $(UTILS_BIN)/utf8wc.o $(UTILS_BIN)/font-retry.o
SRCS = xscreensaver-getimage.c \
attraction.c blitspin.c bouboule.c braid.c bubbles.c \
tessellimage.c delaunay.c recanim.c binaryring.c \
glitchpeg.c vfeedback.c scooter.c webcollage-cocoa.m \
webcollage-helper-cocoa.m testx11.c marbling.c \
- binaryhorizon.c droste.c ffmpeg-out.c
+ binaryhorizon.c droste.c ffmpeg-out.c ansi-tty.c
SCRIPTS = xscreensaver-getimage-file xscreensaver-getimage-video \
xscreensaver-text vidwhacker webcollage
asm6502.o abstractile.o lcdscrub.o hexadrop.o \
tessellimage.o delaunay.o recanim.o binaryring.o \
glitchpeg.o vfeedback.o scooter.o testx11.o marbling.o \
- binaryhorizon.o droste.o
+ binaryhorizon.o droste.o ansi-tty.o
EXES = attraction blitspin bouboule braid decayscreen deco \
drift flame galaxy grav greynetic halo \
HDRS = screenhack.h screenhackI.h fps.h fpsI.h xlockmore.h \
xlockmoreI.h automata.h bubbles.h ximage-loader.h \
apple2.h analogtv.h pacman.h pacman_ai.h pacman_level.h \
- asm6502.h delaunay.h recanim.h ffmpeg-out.h
+ asm6502.h delaunay.h recanim.h ffmpeg-out.h ansi-tty.h
MEN = anemone.man apollonian.man attraction.man \
blaster.man blitspin.man bouboule.man braid.man bsod.man \
bumps.man ccurve.man compass.man coral.man \
# Adds all current dependencies to Makefile
depend:
- $(DEPEND) -s '# DO NOT DELETE: updated by make depend' \
- $(DEPEND_FLAGS) -- \
- $(INCLUDES) $(DEFS) $(DEPEND_DEFINES) $(CFLAGS) $(X_CFLAGS) -- \
+ $(DEPEND) -s '# DO NOT DELETE: updated by make depend' \
+ $(DEPEND_FLAGS) -- \
+ $(INCLUDES) $(DEFS) $(DEPEND_DEFINES) $(CFLAGS) $(X_CFLAGS) $(FFMPEG_CFLAGS) -- \
$(SRCS)
# Adds some dependencies to Makefile.in -- not totally accurate, but pretty
# to include only dependencies on files which are themselves a part of this
# package.
distdepend:: m6502.h
- @echo updating dependencies in `pwd`/Makefile.in... ; \
- $(DEPEND) -w 0 -f - \
- -s '# DO NOT DELETE: updated by make distdepend' $(DEPEND_FLAGS) -- \
- $(INCLUDES_1) $(DEFS) $(DEPEND_DEFINES) $(CFLAGS) $(X_CFLAGS) -- \
- $(SRCS) 2>/dev/null | \
- sort -d | \
- ( \
- awk '/^# .*Makefile.in ---/,/^# DO .*distdepend/' < Makefile.in ; \
- sed -e '/^#.*/d' \
- -e 's@ \./@ @g;s@ /[^ ]*@@g;/^.*:$$/d' \
- -e 's@\.\./utils@$$(UTILS_SRC)@g' \
- -e 's@ \([^$$]\)@ $$(srcdir)/\1@g' \
- -e 's@ $$(srcdir)/\(.*config.h\)@ \1@g' \
- -e 's@ $$(srcdir)/\(m6502.h\)@ \1@g' \
- -e 's@ $$(srcdir)/\(images/gen/\)@ \1@g' \
- -e 's@ $$(HACK_SRC)/\(images/gen/\)@ \1@g' ; \
- echo '' \
- ) > /tmp/distdepend.$$$$ && \
+ @echo updating dependencies in `pwd`/Makefile.in... ; \
+ $(DEPEND) -w 0 -f - \
+ -s '# DO NOT DELETE: updated by make distdepend' $(DEPEND_FLAGS) -- \
+ $(INCLUDES_1) $(DEFS) $(DEPEND_DEFINES) $(CFLAGS) $(X_CFLAGS) $(FFMPEG_CFLAGS) -- \
+ $(SRCS) 2>/dev/null | \
+ sort -d | \
+ ( \
+ awk '/^# .*Makefile.in ---/,/^# DO .*distdepend/' < Makefile.in ; \
+ sed -e '/^#.*/d' \
+ -e 's@ \./@ @g;s@ /[^ ]*@@g;/^.*:$$/d' \
+ -e 's@\.\./utils@$$(UTILS_SRC)@g' \
+ -e 's@ \([^$$]\)@ $$(srcdir)/\1@g' \
+ -e 's@ $$(srcdir)/\(.*config.h\)@ \1@g' \
+ -e 's@ $$(srcdir)/\(m6502.h\)@ \1@g' \
+ -e 's@ $$(srcdir)/\(images/gen/\)@ \1@g' \
+ -e 's@ $$(HACK_SRC)/\(images/gen/\)@ \1@g' ; \
+ echo '' \
+ ) > /tmp/distdepend.$$$$ && \
mv /tmp/distdepend.$$$$ Makefile.in
TAGS: tags
fi
validate_xml:
- @cd $(srcdir) && ./check-configs.pl $(EXES)
+ @cd $(srcdir) && ./check-configs.pl --force $(EXES)
munge_ad_file:
@echo "Updating hack list in XScreenSaver.ad.in..." ; \
# How we build object files in this directory.
-HACK_CFLAGS_BASE=$(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS)
+HACK_CFLAGS_BASE=$(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) $(FFMPEG_CFLAGS)
.c.o:
$(CC) -c $(HACK_CFLAGS_BASE) $<
ATV = analogtv.o $(SHM) $(THRO)
APPLE2 = apple2.o $(ATV)
TEXT = $(UTILS_BIN)/textclient.o
+TTY = ansi-tty.o
CC_HACK = $(CC) $(LDFLAGS)
truchet: truchet.o $(HACK_OBJS) $(COL)
$(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(COL) $(HACK_LIBS)
-bsod: bsod.o $(HACK_OBJS) $(GRAB) $(APPLE2) $(PNG)
- $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(GRAB) $(APPLE2) $(PNG) $(PNG_LIBS) $(THRL)
+BSOD=$(HACK_OBJS) $(GRAB) $(APPLE2) $(PNG) $(UTILS_BIN)/xftwrap.o
+bsod: bsod.o $(BSOD)
+ $(CC_HACK) -o $@ $@.o $(BSOD) $(PNG_LIBS) $(THRL)
-apple2: apple2.o apple2-main.o $(HACK_OBJS) $(ATV) $(GRAB) $(TEXT) $(PNG)
- $(CC_HACK) -o $@ $@.o apple2-main.o $(HACK_OBJS) $(ATV) $(GRAB) $(TEXT) $(PNG) $(PNG_LIBS) $(TEXT_LIBS) $(THRL)
+apple2: apple2.o apple2-main.o $(HACK_OBJS) $(ATV) $(GRAB) $(TEXT) $(PNG) $(TTY)
+ $(CC_HACK) -o $@ $@.o apple2-main.o $(HACK_OBJS) $(ATV) $(GRAB) $(TEXT) $(PNG) $(PNG_LIBS) $(TEXT_LIBS) $(THRL) $(TTY)
xanalogtv: xanalogtv.o $(HACK_OBJS) $(ATV) $(GRAB) $(PNG)
$(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(ATV) $(GRAB) $(PNG) $(PNG_LIBS) $(HACK_LIBS) $(THRL)
critical: critical.o $(HACK_OBJS) $(COL) $(ERASE)
$(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(COL) $(ERASE) $(HACK_LIBS)
-phosphor: phosphor.o $(HACK_OBJS) $(TEXT) $(COL) $(PNG)
- $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(TEXT) $(COL) $(PNG) $(PNG_LIBS) $(TEXT_LIBS)
+phosphor: phosphor.o $(HACK_OBJS) $(TEXT) $(COL) $(PNG) $(TTY)
+ $(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(TEXT) $(COL) $(PNG) $(TTY) $(PNG_LIBS) $(TEXT_LIBS)
xmatrix: xmatrix.o $(HACK_OBJS) $(TEXT) $(PNG)
$(CC_HACK) -o $@ $@.o $(HACK_OBJS) $(TEXT) $(PNG) $(PNG_LIBS) $(TEXT_LIBS)
anemotaxis.o: $(UTILS_SRC)/xdbe.h
anemotaxis.o: $(UTILS_SRC)/xft.h
anemotaxis.o: $(UTILS_SRC)/yarandom.h
+ansi-tty.o: $(srcdir)/ansi-tty.h
+ansi-tty.o: ../config.h
+ansi-tty.o: $(srcdir)/fps.h
+ansi-tty.o: $(srcdir)/recanim.h
+ansi-tty.o: $(srcdir)/screenhackI.h
+ansi-tty.o: $(srcdir)/screenhack.h
+ansi-tty.o: $(UTILS_SRC)/colors.h
+ansi-tty.o: $(UTILS_SRC)/font-retry.h
+ansi-tty.o: $(UTILS_SRC)/grabclient.h
+ansi-tty.o: $(UTILS_SRC)/hsv.h
+ansi-tty.o: $(UTILS_SRC)/resources.h
+ansi-tty.o: $(UTILS_SRC)/usleep.h
+ansi-tty.o: $(UTILS_SRC)/utf8wc.h
+ansi-tty.o: $(UTILS_SRC)/visual.h
+ansi-tty.o: $(UTILS_SRC)/xft.h
+ansi-tty.o: $(UTILS_SRC)/yarandom.h
ant.o: $(srcdir)/automata.h
ant.o: ../config.h
ant.o: $(srcdir)/fps.h
apollonian.o: $(srcdir)/xlockmoreI.h
apollonian.o: $(srcdir)/xlockmore.h
apple2-main.o: $(srcdir)/analogtv.h
+apple2-main.o: $(srcdir)/ansi-tty.h
apple2-main.o: $(srcdir)/apple2.h
apple2-main.o: ../config.h
apple2-main.o: $(srcdir)/fps.h
bsod.o: $(UTILS_SRC)/usleep.h
bsod.o: $(UTILS_SRC)/visual.h
bsod.o: $(UTILS_SRC)/xft.h
+bsod.o: $(UTILS_SRC)/xftwrap.h
bsod.o: $(UTILS_SRC)/xshm.h
bsod.o: $(UTILS_SRC)/yarandom.h
bsod.o: $(srcdir)/ximage-loader.h
petri.o: $(UTILS_SRC)/visual.h
petri.o: $(UTILS_SRC)/xft.h
petri.o: $(UTILS_SRC)/yarandom.h
+phosphor.o: $(srcdir)/ansi-tty.h
phosphor.o: ../config.h
phosphor.o: $(srcdir)/fps.h
phosphor.o: images/gen/6x10font_png.h
-/* xanalogtv-cli, Copyright © 2018-2023 Jamie Zawinski <jwz@jwz.org>
+/* xanalogtv-cli, Copyright © 2018-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
static void
usage(const char *err)
{
- if (err) fprintf (stderr, "%s: %s unknown\n", progname, err);
- fprintf (stderr, "usage: %s [--verbose] [--duration secs] [--slideshow secs]"
- " [--audio mp3-file] [--powerup] [--size WxH]"
- " infile.png ... outfile.mp4\n",
+ if (err && *err) fprintf (stderr, "%s: %s unknown\n", progname, err);
+ fprintf (stderr,
+ "usage: %s [--verbose] [--duration secs] [--slideshow secs]\n"
+ "\t\t [--audio mp3-file] [--powerup] [--size WxH]\n"
+ "\t\t infile.png infile2.png ... outfile.mp4\n",
progname);
exit (1);
}
else if (!strcmp(argv[i], "-vvv")) verbose_p += 3;
else if (!strcmp(argv[i], "-vvvv")) verbose_p += 4;
else if (!strcmp(argv[i], "-vvvvv")) verbose_p += 5;
+ else if (!strcmp(argv[i], "-vvvvvv")) verbose_p += 6;
+ else if (!strcmp(argv[i], "-vvvvvvv")) verbose_p += 7;
+ else if (!strcmp(argv[i], "-vvvvvvvv")) verbose_p += 8;
+ else if (!strcmp(argv[i], "-vvvvvvvvv")) verbose_p += 9;
+ else if (!strcmp(argv[i], "-vvvvvvvvvv")) verbose_p += 10;
else if (!strcmp(argv[i], "-duration") && argv[i+1])
{
char dummy;
outfile = infiles[nfiles-1];
infiles[--nfiles] = 0;
+ for (i = 0; i < nfiles; i++)
+ {
+ const char *f = infiles[i];
+ struct stat st;
+ if (stat (f, &st))
+ {
+ fprintf (stderr, "%s: %s does not exist\n", progname, f);
+ usage("");
+ }
+ }
+
if (nfiles == 1)
slideshow = duration;
--- /dev/null
+/* xscreensaver, Copyright © 2025 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * An ANSI (VT100) terminal emulator. Reads control codes and renders the
+ * screen into a character grid. Other programs (apple2 and phosphor) then
+ * copy that layout to the screen while applying their own text styling.
+ *
+ * https://en.wikipedia.org/wiki/ANSI_escape_code
+ * https://vt100.net/docs/vt100-ug/chapter3.html
+ * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * https://en.wikipedia.org/wiki/ISO/IEC_2022
+ * https://invisible-island.net/vttest/
+ * https://github.com/mattiase/wraptest
+ */
+
+#include "screenhack.h"
+#include "ansi-tty.h"
+#include "utf8wc.h"
+
+#define ESC 0x1B
+#define CSI "\x1B["
+
+# undef UNDEF
+# define UNDEF -0xFFFF
+
+struct tty_state {
+ char buf[255], buf2[255];
+ int idx, idx2;
+ int awaiting_st;
+ int unicrud;
+ int auto_wrap_p;
+ int origin_relative_p;
+ int linefeed_p;
+
+ /* A fun quirk about ANSI / VT100 cursor addressing: When a character is
+ printed in column 80, the insertion point does not wrap to the next line
+ until the 81st character is printed, which will then appear in the
+ leftmost column of the following line. In this case, the cursor blinks
+ atop the 80th character instead of after it. This means that a cursor at
+ position 80 is visually ambiguous with one at position 79.
+
+ Likewise, a character printed in the bottom right cell does not cause the
+ screen to scroll up by one line until the *next* character is printed.
+
+ However, this only applies to cursor coordinates caused by text insertion.
+ Inserting "X" at column 80 lets cursor.x go to 81; but if at column 70
+ and you do "move right by 20", the cursot ends up at 80, not 81. Fun!
+
+ Details: https://github.com/mattiase/wraptest
+ */
+ int lcf; /* Last Column Flag */
+
+ struct { int y1, y2; } scroll;
+ struct { int x, y, lcf;
+ tty_flag flags; } saved;
+ tty_flag flags;
+ tty_flag g0, g1;
+ tty_color fg, bg;
+
+ char *tabs; /* 1 in each column that has a tab stop set. */
+
+ FILE *log_file;
+};
+
+
+/* The DEC Special Graphics 8-bit character set used by vt100.
+ It is unclear to me if this was incorporated into ANSI.
+ */
+const unsigned long ansi_graphics_unicode[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10-1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20-2F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 30-3F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-4F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', /* 50-5F */
+ 0x25C6, /* 0x60 ` ◆ BLACK DIAMOND */
+ 0x2592, /* 0x61 a ▒ MEDIUM SHADE */
+ 0x2409, /* 0x62 b ␉ SYMBOL FOR HORIZONTAL TABULATION */
+ 0x240C, /* 0x63 c ␌ SYMBOL FOR FORM FEED */
+ 0x240D, /* 0x64 d ␍ SYMBOL FOR CARRIAGE RETURN */
+ 0x240A, /* 0x65 e ␊ SYMBOL FOR LINE FEED */
+ 0x00B0, /* 0x66 f ° DEGREE SIGN */
+ 0x00B1, /* 0x67 g ± PLUS-MINUS SIGN */
+ 0x2424, /* 0x68 h  SYMBOL FOR NEWLINE */
+ 0x240B, /* 0x69 i ␋ SYMBOL FOR VERTICAL TABULATION */
+ 0x2518, /* 0x6A j ┘ BOX DRAWINGS LIGHT UP AND LEFT */
+ 0x2510, /* 0x6B k ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */
+ 0x250C, /* 0x6C l ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
+ 0x2514, /* 0x6D m └ BOX DRAWINGS LIGHT UP AND RIGHT */
+ 0x253C, /* 0x6E n ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
+ 0x23BA, /* 0x6F o ⎺ HORIZONTAL SCAN LINE-1 */
+ 0x23BB, /* 0x70 p ⎻ HORIZONTAL SCAN LINE-3 */
+ 0x2500, /* 0x71 q ─ BOX DRAWINGS LIGHT HORIZONTAL */
+ 0x23BC, /* 0x72 r ⎼ HORIZONTAL SCAN LINE-7 */
+ 0x23BD, /* 0x73 s ⎽ HORIZONTAL SCAN LINE-9 */
+ 0x251C, /* 0x74 t ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+ 0x2524, /* 0x75 u ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+ 0x2534, /* 0x76 v ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+ 0x252C, /* 0x77 w ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+ 0x2502, /* 0x78 x │ BOX DRAWINGS LIGHT VERTICAL */
+ 0x2264, /* 0x79 y ≤ LESS-THAN OR EQUAL TO */
+ 0x2265, /* 0x7A z ≥ GREATER-THAN OR EQUAL TO */
+ 0x03C0, /* 0x7B { π GREEK SMALL LETTER PI */
+ 0x2260, /* 0x7C | ≠ NOT EQUAL TO */
+ 0x00A3, /* 0x7D } £ POUND SIGN */
+ 0x00B7, /* 0x7E ~ · MIDDLE DOT */
+ 0, /* 0x7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90-9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0-AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0-BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0-CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0-DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0-EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0-FF */
+};
+
+
+static void
+set_color (ansi_tty *tty, int fg_p, int color)
+{
+ tty_state *st = tty->state;
+ tty_color *c = (fg_p ? &st->fg : &st->bg);
+ if (color < countof(tty->cmap))
+ {
+ *c = tty->cmap[color][fg_p ? 0 : 1];
+ }
+ else
+ {
+ c->r = (color >> 16) & 0xFF;
+ c->g = (color >> 8) & 0xFF;
+ c->b = (color >> 0) & 0xFF;
+ }
+}
+
+
+static void
+ansi_tty_default_tabs (ansi_tty *tty)
+{
+ int ts = 8;
+ int i;
+ char *tabs = tty->state->tabs;
+ for (i = 0; i < tty->width; i++)
+ tabs[i] = (i % ts) ? 0 : 1;
+}
+
+
+static void
+ansi_tty_reset (ansi_tty *tty)
+{
+ int odebug = tty->debug_p;
+ FILE *olog = tty->state->log_file;
+ char *otabs = tty->state->tabs;
+ memset (tty->grid, 0, tty->width * tty->height * sizeof (*tty->grid));
+ memset (tty->state, 0, sizeof(*tty->state));
+ tty->state->scroll.y1 = 0;
+ tty->state->scroll.y2 = tty->height;
+ tty->state->auto_wrap_p = True;
+ tty->state->fg = tty->cmap[2][0]; /* Green */
+ tty->state->bg = tty->cmap[0][1]; /* Black */
+ tty->debug_p = odebug;
+ tty->state->log_file = olog;
+ tty->state->tabs = otabs;
+ ansi_tty_default_tabs (tty);
+}
+
+
+ansi_tty *
+ansi_tty_init (int w, int h)
+{
+ ansi_tty *tty = (ansi_tty *) calloc (1, sizeof(*tty));
+ if (!tty) abort();
+ if (w <= 2 || h <= 2) abort();
+ tty->width = w;
+ tty->height = h;
+ tty->grid = (tty_char *) calloc (w * h, sizeof (*tty->grid));
+ tty->state = (tty_state *) calloc (1, sizeof(*tty->state));
+ if (!tty->grid || !tty->state) abort();
+ tty->state->tabs = (char *) calloc (w, 1);
+ ansi_tty_default_tabs (tty);
+
+ tty->debug_p = 1;
+
+# if !defined(HAVE_MOBILE) && !defined(__OPTIMIZE__)
+ if (tty->debug_p >= 4)
+ {
+ const char *fn = "/tmp/ttylog.txt";
+ tty->state->log_file = fopen (fn, "wb");
+ if (!tty->state->log_file)
+ fprintf (stderr, "%s: unable to write %s\n", progname, fn);
+ else
+ fprintf (stderr, "%s: WARNING: logging to %s\n", progname, fn);
+
+ /* Note: if you're trying to do playback using this log file,
+ be sure to do "stty -onlcr ; cat FILE" or the shell will
+ turn \r\n into \r\r\n. */
+ }
+# endif
+
+ {
+ static const tty_color cmap[] = { /* VGA colors */
+ { 0x00, 0x00, 0x00 }, /* 30 40 Black */
+ { 0xAA, 0x00, 0x00 }, /* 31 41 Red */
+ { 0x00, 0xAa, 0x00 }, /* 32 42 Green */
+ { 0xAA, 0x55, 0x00 }, /* 33 43 Yellow */
+ { 0x00, 0x00, 0xAA }, /* 34 44 Blue */
+ { 0xAA, 0x00, 0xAA }, /* 35 45 Magenta */
+ { 0x00, 0xAA, 0xAA }, /* 36 46 Cyan */
+ { 0xAA, 0xAA, 0xAA }, /* 37 47 White */
+ { 0x55, 0x55, 0x55 }, /* 90 100 Bright Black */
+ { 0xFF, 0x55, 0x55 }, /* 91 101 Bright Red */
+ { 0x55, 0xFF, 0x55 }, /* 92 102 Bright Green */
+ { 0xFF, 0xFF, 0x55 }, /* 93 103 Bright Yellow */
+ { 0x55, 0x55, 0xFF }, /* 94 104 Bright Blue */
+ { 0xFF, 0x55, 0xFF }, /* 95 105 Bright Magenta */
+ { 0x55, 0xFF, 0xFF }, /* 96 106 Bright Cyan */
+ { 0xFF, 0xFF, 0xFF }, /* 97 107 Bright White */
+ };
+ int i;
+ for (i = 0; i < countof(cmap); i++)
+ {
+ tty->cmap[i][0] = cmap[i];
+ tty->cmap[i][1] = cmap[i];
+ }
+ }
+
+ ansi_tty_reset (tty);
+
+ if (tty->debug_p > 1)
+ fprintf (stderr, "%s: tty init %dx%d\n", progname, w, h);
+
+ return tty;
+}
+
+void
+ansi_tty_free (ansi_tty *tty)
+{
+ if (tty->state->log_file)
+ fclose (tty->state->log_file);
+ free (tty->state->tabs);
+ free (tty->state);
+ free (tty);
+}
+
+void ansi_tty_resize (ansi_tty *tty, int w, int h)
+{
+ tty_state *st = tty->state;
+ tty_char *grid2;
+ int x, y;
+ if (w <= 2 || h <= 2) abort();
+ grid2 = (tty_char *) calloc (w * h, sizeof (*grid2));
+
+ for (y = 0; y < tty->height; y++)
+ for (x = 0; x < tty->width; x++)
+ if (x < w && y < h)
+ grid2[y * w + x] = tty->grid[y * tty->width + x];
+
+ free (tty->grid);
+ tty->grid = grid2;
+ tty->width = w;
+ tty->height = h;
+
+ tty->state->tabs = (char *) realloc (tty->state->tabs, w);
+
+ if (tty->x >= w) tty->x = w-1;
+ if (st->scroll.y1 > tty->height) st->scroll.y1 = tty->height;
+ if (st->scroll.y2 > tty->height) st->scroll.y2 = tty->height;
+ if (tty->y >= st->scroll.y2) tty->y = st->scroll.y2 - 1;
+ if (tty->y >= st->scroll.y2) tty->y = st->scroll.y2 - 1;
+
+ if (tty->debug_p)
+ fprintf (stderr, "%s: tty resize %dx%d\n", progname, w, h);
+}
+
+
+/* Scroll lines up or down within the character grid.
+ */
+static void
+tty_scroll (ansi_tty *tty, int lines)
+{
+ tty_state *st = tty->state;
+ unsigned char *top = (unsigned char *)
+ (tty->grid + tty->width * st->scroll.y1);
+ unsigned char *bot = (unsigned char *)
+ (tty->grid + (tty->width * st->scroll.y2));
+ int total = bot - top;
+ int move = lines * tty->width * sizeof(*tty->grid);
+
+ if ( lines >= st->scroll.y2 - st->scroll.y1 ||
+ -lines >= st->scroll.y2 - st->scroll.y1)
+ {
+ /* Scrolling more lines than exist is clearing. */
+ memset (top, 0, total);
+ }
+ else if (lines > 0)
+ {
+ memmove (top, top + move, total - move);
+ memset (top + total - move, 0, move);
+ }
+ else if (lines < 0)
+ {
+ move = -move;
+ memmove (top + move, top, total - move);
+ memset (top, 0, move);
+ }
+}
+
+
+/* Clear characters from the grid.
+ Start and end positions are inclusive.
+ */
+static void
+tty_erase (ansi_tty *tty, int x1, int y1, int x2, int y2)
+{
+ int from, to;
+ tty_char *ch, *end;
+
+ if (x1 < 0) x1 = 0;
+ if (x2 < 0) x2 = 0;
+ if (y1 < 0) y1 = 0;
+ if (y2 < 0) y2 = 0;
+ if (x1 > tty->width-1) x1 = tty->width-1;
+ if (x2 > tty->width-1) x2 = tty->width-1;
+ if (y1 > tty->height-1) y1 = tty->height-1;
+ if (y2 > tty->height-1) y2 = tty->height-1;
+
+ from = y1 * tty->width + x1;
+ to = y2 * tty->width + x2;
+ if (from > to) abort();
+
+ ch = tty->grid + from;
+ end = tty->grid + to;
+
+ while (ch <= end)
+ {
+# if 1
+ ch->c = 0;
+ ch->flags = 0;
+ ch->fg = tty->cmap[2][0]; /* Green */
+ ch->bg = tty->cmap[0][1]; /* Black */
+# else
+ /* You might assume that turning on inverse and clearing to end of
+ line would give you an inverted line, but you would be wrong. */
+ ch->c = tty->state->flags ? ' ' : 0;
+ ch->flags = tty->state->flags;
+ ch->fg = tty->state->fg;
+ ch->bg = tty->state->bg;
+# endif
+ ch++;
+ }
+}
+
+
+/* Print a line describing the non-command text that was printed,
+ once enough of it has buffered up to be legible. NULL means flush.
+ */
+static void
+tty_log_c (ansi_tty *tty, const char c)
+{
+ if (tty->debug_p > 2)
+ {
+ tty_state *st = tty->state;
+ char buf3[sizeof (st->buf2) * 4 + 100];
+ char *in, *out;
+
+ if (c)
+ {
+ st->buf2[st->idx2++] = c;
+ st->buf2[st->idx2] = 0;
+ }
+
+ if (((c >= ' ' && c <= '~') || c == '\r' || c == 0x08) &&
+ st->idx2 < sizeof(st->buf2) - 1)
+ return;
+
+ in = st->buf2;
+ out = buf3;
+ *out = 0;
+
+ while (*in) {
+ char c = *in++;
+ if (c >= ' ' && c <= '~')
+ {
+ *out++ = c;
+ *out = 0;
+ }
+ else
+ {
+ int pad = False;
+ if (c == 0x08) strcat (out, "\\b");
+ else if (c == 0x09) strcat (out, "\\t");
+ else if (c == 0x0A) strcat (out, "\\n");
+ else if (c == 0x1B) strcat (out, "\\e");
+ else if (c == 0x0D) strcat (out, "\\r");
+ else
+ {
+ pad = True;
+ if (out > buf3 && out[-1] != ' ')
+ {
+ *out++ = ' ';
+ *out = 0;
+ }
+ sprintf (out, "\\x%02X", (unsigned char) c);
+ }
+
+ out += strlen (out);
+ if (*in && pad)
+ {
+ *out++ = ' ';
+ *out = 0;
+ }
+ }
+ }
+
+ if (out > buf3)
+ fprintf (stderr, "%s: txt: \"%s\"\n", progname, buf3);
+
+ st->idx2 = 0;
+ st->buf2[0] = 0;
+ }
+}
+
+
+/* Print a line describing the terminal command being executed.
+ */
+static void
+tty_log (ansi_tty *tty, const char *kind, int ok, const char *log,
+ int ac, const int *av, const char *cmd)
+{
+ if (tty->debug_p > (ok ? 1 : 0))
+ {
+ char buf[1024], *s = buf;
+ int i;
+
+ tty_log_c (tty, 0); /* Flush */
+
+ if (ac) *s++ = ':';
+ *s = 0;
+
+ for (i = 0; i < ac; i++)
+ {
+ if (av[i] == UNDEF)
+ sprintf (s, " -");
+ else
+ sprintf (s, " %d", av[i]);
+ s += strlen(s);
+ }
+ if (!ok || tty->debug_p > 1)
+ {
+ int j = 0;
+ strcat (s, ": ");
+ s += strlen(s);
+ while (*cmd && j < 100)
+ {
+ if (*cmd == ESC)
+ strcat (s, "ESC ");
+ else if (*cmd < ' ' || *cmd > '~')
+ sprintf (s, "0x%02X", *cmd);
+ else
+ sprintf (s, "%c", *cmd);
+
+ s += strlen(s);
+ cmd++;
+ j++;
+ }
+ }
+
+ fprintf (stderr, "%s: %3s: %s%s%s\n", progname, kind,
+ (ok ? "" : "Unimplemented: "), log, buf);
+ }
+}
+
+
+/* Just gonna leave this here:
+
+ % infocmp -x vt100
+ Reconstructed via infocmp from file: /usr/share/terminfo/v/vt100
+ vt100|vt100-am|DEC VT100 (w/advanced video),
+ OTbs, am, mc5i, msgr, xenl, xon,
+ cols#80, it#8, lines#24, vt#3,
+ acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
+ bel=^G, blink=\E[5m$<2>, bold=\E[1m$<2>,
+ clear=\E[H\E[J$<50>, cr=\r, csr=\E[%i%p1%d;%p2%dr,
+ cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=\n,
+ cuf=\E[%p1%dC, cuf1=\E[C$<2>,
+ cup=\E[%i%p1%d;%p2%dH$<5>, cuu=\E[%p1%dA,
+ cuu1=\E[A$<2>, ed=\E[J$<50>, el=\E[K$<3>, el1=\E[1K$<3>,
+ enacs=\E(B\E)0, home=\E[H, ht=^I, hts=\EH, ind=\n, ka1=\EOq,
+ ka3=\EOs, kb2=\EOr, kbs=^H, kc1=\EOp, kc3=\EOn, kcub1=\EOD,
+ kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kent=\EOM, kf0=\EOy,
+ kf1=\EOP, kf10=\EOx, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\EOt,
+ kf6=\EOu, kf7=\EOv, kf8=\EOl, kf9=\EOw, lf1=pf1, lf2=pf2,
+ lf3=pf3, lf4=pf4, mc0=\E[0i, mc4=\E[4i, mc5=\E[5i, rc=\E8,
+ rev=\E[7m$<2>, ri=\EM$<5>, rmacs=^O, rmam=\E[?7l,
+ rmkx=\E[?1l\E>, rmso=\E[m$<2>, rmul=\E[m$<2>,
+ rs2=\E<\E>\E[?3;4;5l\E[?7;8h\E[r, sc=\E7,
+ sgr=\E[0%?%p1%p6%|%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;$<2>,
+ sgr0=\E[m\017$<2>, smacs=^N, smam=\E[?7h, smkx=\E[?1h\E=,
+ smso=\E[7m$<2>, smul=\E[4m$<2>, tbc=\E[3g,
+ u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?%[;0123456789]c, u9=\EZ,
+
+ Reformatted for legibility:
+
+ OTbs
+ auto_right_margin
+ prtr_silent
+ move_standout_mode
+ eat_newline_glitch
+ xon_xoff
+ cols#80
+ init_tabs#8
+ lines#24
+ virtual_terminal#3
+
+ acs_chars = ``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~
+ bell = ^G
+ enter_blink_mode = CSI 5 m
+ enter_bold_mode = CSI 1 m
+ clear_screen = CSI H
+ CSI J
+ carriage_return = \r
+ change_scroll_region = CSI <FROM> ; <TO> r
+ parm_left_cursor = CSI <N> D
+ cursor_left = ^H
+ parm_down_cursor = CSI <N> B
+ cursor_down = \n
+ parm_right_cursor = CSI <N> C
+ cursor_right = CSI C
+ cursor_address = CSI <X> ; <Y> H
+ parm_up_cursor = CSI <N> A
+ cursor_up = CSI A
+ clr_eos = CSI J
+ clr_eol = CSI K
+ clr_bol = CSI 1 K
+ ena_acs = ESC ( B ESC ) 0
+ cursor_home = CSI H
+ tab = ^I
+ set_tab = ESC H
+ scroll_forward = \n
+ key_a1 = ESC O q
+ key_a3 = ESC O s
+ key_b2 = ESC O r
+ key_backspace = ^H
+ key_c1 = ESC O p
+ key_c3 = ESC O n
+ key_left = ESC O D
+ key_down = ESC O B
+ key_right = ESC O C
+ key_up = ESC O A
+ key_enter = ESC O M
+ key_f0 = ESC O y
+ key_f1 = ESC O P
+ key_f10 = ESC O x
+ key_f2 = ESC O Q
+ key_f3 = ESC O R
+ key_f4 = ESC O S
+ key_f5 = ESC O t
+ key_f6 = ESC O u
+ key_f7 = ESC O v
+ key_f8 = ESC O l
+ key_f9 = ESC O w
+ lab_f1 = pf1
+ lab_f2 = pf2
+ lab_f3 = pf3
+ lab_f4 = pf4
+ print_screen = CSI 0 i
+ prtr_off = CSI 4 i
+ prtr_on = CSI 5 i
+ restore_cursor = ESC 8
+ enter_reverse_mode = CSI 7 m
+ scroll_reverse = ESC M
+ exit_alt_charset_mode = ^O
+ exit_am_mode = CSI ? 7 l
+ keypad_local = CSI ? 1 l ESC >
+ exit_standout_mode = CSI m
+ exit_underline_mode = CSI m
+ reset_2string = ESC < ESC >
+ CSI ? 3 ; 4 ; 5 l
+ CSI ? 7 ; 8 h
+ CSI r
+ save_cursor = ESC 7
+
+ set_attributes = CSI 0
+ IF %p1 | %p6 THEN ; 1 ENDIF
+ IF %p2 THEN ; 4 ENDIF
+ IF %p1 | %p3 THEN ; 7 ENDIF
+ IF %p4 THEN ; 5 ENDIF
+ m
+ IF %p9 THEN \016
+ ELSE \017 ENDIF
+
+ exit_attribute_mode = CSI m \017
+ enter_alt_charset_mode = ^N
+ enter_am_mode = CSI ? 7 h
+ keypad_xmit = CSI ? 1 h ESC =
+ enter_standout_mode = CSI 7 m
+ enter_underline_mode = CSI 4 m
+ clear_all_tabs = CSI 3 g
+ user6 = CSI %i %d ; %d R
+ user7 = CSI 6 n
+ user8 = CSI ? %[;0123456789] c
+ user9 = ESC Z
+ */
+
+
+/* I find state machines soothing. If the thing you're writing is not
+ a state machine, it might be evil.
+ _______________________________________________________________
+ / \
+ | Tech enthusiasts: My entire house is smart. |
+ | |
+ | Tech workers: The only piece of technology in my house is a |
+ | printer and I keep a gun next to it so I can shoot it if it |
+ | makes a noise I don't recognize. -- PPathole, 2019 |
+ \ |
+ `---------------------------------------------------------------'
+ */
+void
+ansi_tty_print (ansi_tty *tty, unsigned long c)
+{
+ tty_state *st = tty->state;
+ int done = False;
+ int scrolled_p = False;
+
+ const char *kind = "?";
+ int av[255];
+ int ac = 0;
+ int i;
+
+ if (st->log_file)
+ {
+ fputc ((char) c, st->log_file); /* Botches unicode */
+ fflush (st->log_file);
+ }
+
+# define LOG(S) tty_log (tty, kind, True, (S), ac, av, st->buf)
+# define UND(S) tty_log (tty, kind, False, (S), ac, av, st->buf)
+
+
+ /* Parse the command sequences.
+
+ nF 20-2F SPC - / 2+ bytes, [20-2F]+ 30-7E
+ Fp 30-3F 0-9:;<=>? 2 bytes
+ Fe 40-5F @A-Zp\]^_ 2 bytes, or CSI: ESC [ ... C
+ Fs 60-7E `a-z{|}~ 2 bytes
+ */
+
+ /* WTF, BS and others are allowed within command sequences! */
+ if (st->idx > 0 && c < ' ')
+ goto SELF_INSERT_INTERLUDE;
+
+ st->buf[st->idx++] = c;
+ st->buf[st->idx] = 0;
+
+
+ /******************************************************************** ESC */
+
+
+ if (st->idx >= sizeof(st->buf) - 2) /* Buffer overflow */
+ {
+ kind = "?";
+ done = True;
+ UND ("OVERFLOW");
+ }
+ else if (st->awaiting_st)
+ {
+ kind = "ST0";
+ /* Some "Fe" commands swallow all bytes until an "ST" string terminator
+ command is received, so just keep going, filling the buffer. */
+ }
+ else if (c == ESC) /* Starting any command */
+ {
+ kind = "ESC";
+ if (st->idx > 1)
+ LOG ("Aborted by ESC");
+ st->idx = 1;
+ }
+
+
+ /********************************************************************* nF */
+
+
+ else if (st->idx == 2 && /* nF escape sequence start */
+ st->buf[0] == ESC &&
+ c >= 0x20 && /* !"#$%&'()*+,-./ */
+ c <= 0x2F)
+ {
+ kind = "nFa";
+ }
+ else if (st->idx > 2 && /* nF escape sequence cont. */
+ st->buf[0] == ESC &&
+ st->buf[1] >= 0x20 && /* !"#$%&'()*+,-./ */
+ st->buf[1] <= 0x2F &&
+ c >= 0x20 && /* !"#$%&'()*+,-./ */
+ c <= 0x2F)
+ {
+ kind = "nFb";
+ }
+ else if (st->idx > 2 && /* nF escape sequence end */
+ st->buf[0] == ESC &&
+ st->buf[1] >= 0x20 && /* !"#$%&'()*+,-./ */
+ st->buf[1] <= 0x2F &&
+ c >= 0x30 && /* 0 - ~ */
+ c <= 0x7E)
+ {
+ kind = "nF";
+ done = True;
+
+ switch (st->idx) {
+ case 2: /* ESC ? */
+ kind = "nF2";
+ switch (st->buf[1]) {
+ case '`': UND ("Disable manual input"); break;
+ case 'a': UND ("Interrupt"); break;
+ case 'b': UND ("Enable manual input"); break;
+ case 'c': LOG ("Reset");
+ ansi_tty_reset (tty);
+ break;
+ case 'd': UND ("Coding method delimiter"); break;
+ case 'n': UND ("Locking shift 1"); break;
+ case 'o': UND ("Locking shift 3"); break;
+ case '|': UND ("Locking shift 3R"); break;
+ case '}': UND ("Locking shift 2R"); break;
+ case '~': UND ("Locking shift 1R"); break;
+ default: UND ("Unknown"); break;
+ }
+ break;
+
+ case 3: /* ESC ? ? */
+ kind = "nF3";
+ switch (st->buf[1]) {
+ case '!': UND ("C0-designate"); break;
+ case '"': UND ("C1-designate"); break;
+
+ case '#': /* ESC # ? */
+ st->lcf = False;
+ switch (st->buf[2]) {
+ case '3': UND ("Double Height Top"); break;
+ case '4': UND ("Double Height Bottom"); break;
+ case '5': UND ("Single Width"); break;
+ case '6': UND ("Double Width"); break;
+ case '8': LOG ("Screen Alignment");
+ {
+ tty_char *ch = tty->grid;
+ tty_char *end = tty->grid + (tty->width * tty->height);
+ while (ch < end)
+ {
+ ch->c = 'E';
+ ch++;
+ }
+ }
+ break;
+ default: UND ("Unknown #"); break;
+ }
+ break;
+
+ case '$': UND ("G0 designate"); break;
+
+ case '%': /* ESC % ? */
+ switch (st->buf[2]) {
+ case '@': UND ("Designate coding"); break;
+ case 'B': UND ("UTF-1"); break;
+ case 'G': UND ("UTF-8"); break;
+ case '/': UND ("UTF-32"); break;
+ default: UND ("Unknown %"); break;
+ }
+ break;
+ case '&': UND ("Identify revised registration"); break;
+ case '\'': UND ("Not used"); break;
+
+ case '(': /* ESC ( ? */
+ case ')': /* ESC ( ? */
+ {
+ int g0_p = (st->buf[1] == '(');
+ tty_flag *gg = (g0_p ? &st->g0 : &st->g1);
+
+ /* This says which character set g0 and g1 indicate. */
+ *gg = 0;
+ switch (st->buf[2]) {
+ case '0': LOG ("G0-designate 94-set: Line Drawing");
+ *gg = TTY_SYMBOLS;
+ break;
+
+ /* We could map these various fonts, particularly the
+ line- and box-drawing font, to their corresponding
+ Unicode code points. But since neither Phosphor and
+ Apple2 can display non-Latin1 characters, that
+ wouldn't do us any good right now. */
+
+ case '1': UND ("Gx-designate 94: Alt Char"); break;
+ case '2': UND ("Gx-designate 94: Alt Special"); break;
+ case '5': UND ("Gx-designate 94: Finnish"); break;
+ case '7': UND ("Gx-designate 94: Swedish"); break;
+ case '9': UND ("Gx-designate 94: French Canadian"); break;
+ case '<': UND ("Gx-designate 94: DEC Supplemental"); break;
+ case '>': UND ("Gx-designate 94: DEC Technical"); break;
+ case 'A': UND ("Gx-designate 94: UK"); break;
+ case 'B': LOG ("Gx-designate 94: ASCII"); break;
+ case 'C': UND ("Gx-designate 94: Finnish"); break;
+ case 'H': UND ("Gx-designate 94: Swedish"); break;
+ case 'I': UND ("Gx-designate 94: JIS Katakana"); break;
+ case 'J': UND ("Gx-designate 94: JIS Roman"); break;
+ case 'K': UND ("Gx-designate 94: German"); break;
+ case 'Q': UND ("Gx-designate 94: French Canadian"); break;
+ case 'R': UND ("Gx-designate 94: French"); break;
+ case 'Y': UND ("Gx-designate 94: Italian"); break;
+ case 'Z': UND ("Gx-designate 94: Spanish"); break;
+ case 'f': UND ("Gx-designate 94: French"); break;
+ default: UND ("Unknown Gx"); break;
+ }
+ }
+ break;
+ /* ESC # # */
+
+ case '*': UND ("G2-designate 94"); break;
+ case '+': UND ("G3-designate 94"); break;
+ case ',': UND ("Not used"); break;
+ case '-': UND ("G1-designate 96"); break;
+ case '.': UND ("G2-designate 96"); break;
+ case '/': UND ("G3-designate 96"); break;
+
+ case ' ': /* ESC SP # */
+ switch (st->buf[2]) {
+ case 'A': UND ("G0 in GL, GR unused"); break;
+ case 'B': UND ("G0 and G1 to GL"); break;
+ case 'C': UND ("G0 in GL, G1 in GR, no lock"); break;
+ case 'D': UND ("G0 in GL, G1 in GR 8-bit"); break;
+ case 'E': UND ("Shift preserved"); break;
+ case '"': UND ("C1 controls esc"); break;
+ case 'G': UND ("C1 controls CR"); break;
+ case 'H': UND ("94-char graphical"); break;
+ case 'I': UND ("94-char and/or 96-char"); break;
+ case 'J': UND ("7-bit"); break;
+ case 'K': UND ("8-bit"); break;
+ case 'L': UND ("ISO/IEC 4873 1"); break;
+ case 'M': UND ("ISO/IEC 4873 2"); break;
+ case 'N': UND ("ISO/IEC 4873 3"); break;
+ case 'P': UND ("SI / LS0"); break;
+ case 'R': UND ("SO / LS1"); break;
+ case 'S': UND ("LS1R 8-bit"); break;
+ case 'T': UND ("LS2"); break;
+ case 'U': UND ("LS2R 8-bit"); break;
+ case 'V': UND ("LS3"); break;
+ case 'W': UND ("LS3R 8-bit"); break;
+ case 'Z': UND ("SS2"); break;
+ case '%': UND ("Designate other"); break;
+ case '[': UND ("SS2"); break;
+ case '\\': UND ("Single-shift GR"); break;
+ default: UND ("Unknown SP"); break;
+ }
+ break;
+ default: UND ("Unknown"); break;
+ }
+ break;
+
+ case 4: /* ESC # # # */
+ kind = "nF4";
+ switch (st->buf[1]) {
+ case '$': /* ESC $ # # */
+ switch (st->buf[2]) {
+ case '(': UND ("G0 designate"); break;
+ case ')': UND ("G1-designate"); break;
+ case '*': UND ("G2-designate"); break;
+ case '+': UND ("G3-designate multi 94"); break;
+ case ',': UND ("Not used"); break;
+ case '-': UND ("G1-designate multi 96"); break;
+ case '.': UND ("G2-designate multi 96"); break;
+ case '/': UND ("G3-designate multi 96"); break;
+ default: UND ("Unknown $"); break;
+ }
+ case '%': /* ESC % # # */
+ switch (st->buf[2]) {
+ case '/':
+ switch (st->buf[3]) {
+ case 'I': UND ("UTF-8"); break;
+ case 'L': UND ("UTF-16"); break;
+ default: UND ("Unknown %"); break;
+ }
+ break;
+ default: UND ("Unknown"); break;
+ }
+ break;
+ default: UND ("Unknown"); break;
+ }
+ break;
+ default: UND ("Unknown"); break;
+ }
+ }
+
+
+ /********************************************************************* Fp */
+
+
+ else if (st->idx == 2 && /* Fp escape sequence */
+ st->buf[0] == ESC &&
+ c >= 0x30 && /* 0-9:;<=>? */
+ c <= 0x3F)
+ {
+ kind = "Fp";
+ done = True;
+
+ switch (c) {
+ case '7': LOG ("Save Cursor");
+ st->saved.flags = st->flags;
+ st->saved.lcf = st->lcf;
+ st->saved.x = tty->x;
+ st->saved.y = tty->y;
+ break;
+ case '8': LOG ("Restore Cursor");
+ st->flags = st->saved.flags;
+ st->lcf = st->saved.lcf;
+ tty->x = st->saved.x;
+ tty->y = st->saved.y;
+ break;
+ case '9': UND ("Forward Index"); break;
+ case '=': /* UND ("App Keypad");*/ break;
+ case '<': UND ("ANSI mode"); break;
+ case '>': /* UND ("Normal Keypad");*/ break;
+ default: UND ("Unknown"); break;
+ }
+ }
+ else if (st->idx >= 3 && /* CSI: Sequence Introducer */
+ st->buf[0] == ESC && /* intermediate bytes */
+ st->buf[1] == '[' && /* ESC [ ... C */
+ ((c >= 0x30 && c <= 0x3F) || /* parameters 0–9:;<=>? */
+ (c >= 0x20 && c <= 0x2F))) /* others !"#$%&'()*+,-./ */
+ {
+ kind = "CSIa";
+ }
+
+
+ /******************************************************************** CSI */
+
+
+ else if (st->idx >= 3 && /* CSI: Sequence Introducer */
+ st->buf[0] == ESC && /* final byte */
+ st->buf[1] == '[' && /* ESC [ ... C */
+ c >= 0x40 && /* cmd @A–Z[\]^_`a–z{|}~ */
+ c <= 0x7E)
+ {
+ kind = "CSI";
+ done = True;
+
+ /* Parse the "80;24;365" args into 'av'. */
+ {
+ int any = False;
+ for (i = 0; i < countof(av); i++)
+ av[i] = UNDEF;
+ for (i = 2; i < st->idx - 1; i++)
+ {
+ if (st->buf[i] == ';' && ac < countof(av)-1)
+ {
+ if (ac >= countof(av) - 2)
+ break;
+ ac++;
+ av[ac] = 0;
+ any = True;
+ }
+ else if (st->buf[i] >= '0' && st->buf[i] <= '9')
+ {
+ if (av[ac] == UNDEF)
+ av[ac] = 0;
+ av[ac] = (av[ac] * 10) + (st->buf[i] - '0');
+ any = True;
+ }
+ else if (st->buf[i] >= 0x3C && st->buf[i] <= 0x3F)
+ ; /* Characters "<=>?" indicate private commands. */
+ else if (st->buf[i] == '!')
+ ; /* What's this? */
+ else
+ UND ("Weird parameters");
+ }
+ if (any) ac++;
+ }
+
+ switch (c) {
+ case '@': st->lcf = False; UND ("Shift left"); break;
+ case 'A': LOG ("Cursor Up");
+ /* "CSI n SP A" means shift right n cols */
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ tty->y -= av[0];
+ st->lcf = False;
+ scrolled_p = True;
+ break;
+ case 'B': LOG ("Cursor Down");
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ tty->y += av[0];
+ st->lcf = False;
+ scrolled_p = True;
+ break;
+ case 'C': LOG ("Cursor Forward");
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ tty->x += av[0];
+ st->lcf = False;
+ scrolled_p = True;
+ break;
+ case 'D': LOG ("Cursor Back");
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ tty->x -= av[0];
+ st->lcf = False;
+ scrolled_p = True;
+ break;
+ case 'E': LOG ("Cursor Next Line");
+ if (av[0] == UNDEF) av[0] = 1;
+ tty->x = 0;
+ tty->y += av[0];
+ scrolled_p = True;
+ /* Set lcf to False here? */
+ break;
+ case 'F': LOG ("Cursor Previous Line");
+ if (av[0] == UNDEF) av[0] = 1;
+ tty->x = 0;
+ tty->y -= av[0];
+ scrolled_p = True;
+ /* Set lcf to False here? */
+ break;
+ case 'G': LOG ("Cursor Horizontal Abs");
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ tty->x = av[0]-1;
+ /* Set lcf to False here? */
+ break;
+ case 'H': LOG ("Cursor Position");
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ if (av[1] == UNDEF || av[1] == 0) av[1] = 1;
+ tty->y = av[0]-1;
+ tty->x = av[1]-1;
+ if (st->origin_relative_p) tty->y += st->scroll.y1;
+ st->lcf = False;
+ break;
+
+ case 'I': LOG ("Fwd Tab Stop");
+ {
+ int i;
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ /* Move forward N tab stops. */
+ /* Set lcf to False here? */
+ for (i = 0; i < av[0]; i++)
+ {
+ tty->x++;
+ while (tty->x < tty->width &&
+ !st->tabs[tty->x])
+ tty->x++;
+ }
+ }
+ break;
+
+ case 'J': /* Erase in Display */
+ st->lcf = False;
+ if (av[0] == UNDEF || av[0] == 0)
+ { LOG ("Erase to end of screen");
+ tty_erase (tty,
+ tty->x, tty->y,
+ tty->width-1, tty->height-1);
+ }
+ else if (av[0] == 1)
+ { LOG ("Erase to start of screen");
+ tty_erase (tty,
+ 0, 0,
+ tty->x-1, tty->y);
+ }
+ else if (av[0] == 2 || av[0] == 3)
+ { LOG ("Erase screen");
+ tty_erase (tty,
+ 0, 0,
+ tty->width-1, tty->height-1);
+ }
+ /* CSI ? n J" are vt220 */
+ break;
+
+ case 'K': /* Erase in Line */
+ st->lcf = False;
+ if (av[0] == UNDEF || av[0] == 0)
+ { LOG ("Erase to end of line");
+ tty_erase (tty,
+ tty->x, tty->y,
+ tty->width-1, tty->y);
+ }
+ else if (av[0] == 1)
+ { LOG ("Erase to start of line");
+ tty_erase (tty,
+ 0, tty->y,
+ tty->x-1, tty->y);
+ }
+ else if (av[0] == 2 || av[0] == 3)
+ { LOG ("Erase line");
+ tty_erase (tty,
+ 0, tty->y,
+ tty->width-1, tty->y);
+ }
+ break;
+
+ case 'L': UND ("Insert lines"); break;
+ case 'M': UND ("Delete lines"); break;
+ case 'P': st->lcf = False; UND ("Delete chars"); break;
+ case 'Q': UND ("Palette stack"); break;
+ case 'R': UND ("Palette restore");break;
+
+ case 'S': LOG ("Scroll Up");
+ if (av[0] == UNDEF) av[0] = 1;
+ tty_scroll (tty, av[0]);
+ scrolled_p = True;
+ /* Set lcf to False here? */
+ break;
+ case 'T': LOG ("Scroll Down");
+ if (av[0] == UNDEF) av[0] = 1;
+ tty_scroll (tty, -av[0]);
+ scrolled_p = True;
+ /* Set lcf to False here? */
+ break;
+
+ case 'W': LOG ("Reset tabs");
+ ansi_tty_default_tabs (tty);
+ break;
+
+ case 'X': st->lcf = False; UND ("Erase chars"); break;
+ case 'Z': LOG ("Back tab stop");
+ {
+ int i;
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ /* Move backward N tab stops. */
+ for (i = 0; i < av[0]; i++)
+ {
+ tty->x--;
+ while (tty->x >= 0 &&
+ !st->tabs[tty->x])
+ tty->x--;
+ }
+ }
+ break;
+
+ /* Unused: [\]_ */
+
+ case '^': UND ("Scroll down"); break;
+ case '`': UND ("Position abs"); break;
+
+ case 'a': UND ("Position rel"); break;
+ case 'b': UND ("Repeat prev"); break;
+
+ case 'c': /* Reports */
+ switch (av[0]) {
+ case 0: LOG ("Report model");
+ if (tty->tty_send)
+ {
+ char buf[40]; /* Base model */
+ sprintf (buf, CSI "?%d;%dc", 1, 0);
+ (*tty->tty_send) (tty->closure, buf);
+ }
+ break;
+ default: UND ("Unknown report request"); break;
+ break;
+ }
+ break;
+
+ case 'd': UND ("Pos abs"); break;
+ case 'e': UND ("Pos rel"); break;
+
+ case 'f': LOG ("H/V Position");
+ if (av[0] == UNDEF || av[0] == 0) av[0] = 1;
+ if (av[1] == UNDEF || av[1] == 0) av[1] = 1;
+ tty->y = av[0]-1;
+ tty->x = av[1]-1;
+ st->lcf = False;
+ break;
+
+ case 'g':
+ if (av[0] == UNDEF) av[0] = 0;
+ switch (av[0]) {
+ case 0: LOG ("Clear tab");
+ st->tabs[tty->x] = 0;
+ break;
+ case 1: LOG ("Set tab");
+ st->tabs[tty->x] = 1;
+ break;
+ case 3: LOG ("Clear all tabs");
+ memset (st->tabs, 0, tty->width);
+ break;
+ default: UND ("Unknown tab"); break;
+ }
+ break;
+
+ case 'h': /* On */
+ /* i,j,k are below */
+ case 'l': /* Off */
+ {
+ int on_p = (st->buf[st->idx-1] == 'h');
+ switch (av[0]) {
+ case 1:
+ if (on_p)
+ /* LOG ("Application cursor keys") */;
+ else
+ /* LOG ("Normal cursor keys") */;
+ break;
+ case 2: UND ("USASCII sets G0-G3"); break;
+ /* Or ANSI/VT52 Mode? */
+ case 3:
+ st->lcf = False;
+ if (on_p) UND ("132 Column Mode");
+ else LOG ("80 Column Mode");
+ break;
+ case 4: UND ("Smooth Scroll"); break;
+ case 5:
+ if (on_p)
+ LOG ("Reverse Video on");
+ else
+ LOG ("Reverse Video off");
+ tty->inverse_p = on_p;
+ break;
+ case 6:
+ st->lcf = False;
+ if (on_p)
+ LOG ("Origin Mode on");
+ else
+ LOG ("Origin Mode off");
+ st->origin_relative_p = on_p;
+ tty->x = 0;
+ tty->y = on_p ? st->scroll.y1 : 0;
+ break;
+ case 7:
+ tty->state->auto_wrap_p = on_p;
+ if (on_p)
+ LOG ("Auto-wrap on");
+ else
+ LOG ("Auto-wrap off");
+ if (!on_p)
+ st->lcf = False;
+ break;
+ case 8: UND ("Auto-Repeat Keys"); break;
+ case 9: UND ("Send Mouse"); break;
+ /* Or Interlace? */
+ case 10: UND ("Show toolbar"); break;
+ case 12: UND ("Start blinking"); break;
+ case 13: UND ("Start blinking"); break;
+ case 14: UND ("Enable XOR"); break;
+ case 18: UND ("Print Form Feed"); break;
+ case 19: UND ("Print full screen"); break;
+
+ case 20:
+ if (on_p) LOG ("Linefeed mode");
+ else LOG ("Newline mode");
+ st->linefeed_p = on_p;
+ break;
+
+ case 25: UND ("Show cursor"); break;
+ case 30: UND ("Show scrollbar"); break;
+ case 35: UND ("Enable font-shifting"); break;
+ case 38: UND ("Enter Tektronix"); break;
+ case 40: UND ("Allow 80/132"); break;
+ case 41: UND ("more"); break;
+ case 42: UND ("National replacement"); break;
+ case 43: UND ("Graphic Expanded Print"); break;
+ case 44: UND ("Graphic Print Color"); break;
+ case 45: UND ("Graphic Print Color Syntax"); break;
+ case 46: UND ("Graphic Print Background"); break;
+ case 47: UND ("Graphic Rotated Print"); break;
+ case 66: /* UND ("Application keypad"); */ break;
+ case 67: UND ("Backarrow backspace"); break;
+ case 69: UND ("Margin mode"); break;
+ case 80: UND ("Sixel Mode"); break;
+ case 95: UND ("Do not clear"); break;
+ case 1000: UND ("Send Mouse"); break;
+ case 1001: UND ("Hilite Mouse Tracking"); break;
+ case 1002: UND ("Cell Motion Mouse"); break;
+ case 1003: UND ("All Motion Mouse"); break;
+ case 1004: UND ("Focus Events"); break;
+ case 1005: UND ("UTF-8 Mouse"); break;
+ case 1006: UND ("SGR Mouse"); break;
+ case 1007: UND ("Alternate Scroll"); break;
+ case 1010: UND ("Scroll on output"); break;
+ case 1011: UND ("Scroll on press"); break;
+ case 1014: UND ("Fast scroll"); break;
+ case 1015: UND ("URXVT Mouse"); break;
+ case 1016: UND ("SGR Mouse"); break;
+ case 1024: LOG ("Enable/disable focus report"); break;
+ case 1034: UND ("Meta key"); break;
+ case 1035: UND ("Alt modifiers"); break;
+ case 1036: UND ("ESC Meta"); break;
+ case 1037: UND ("DEL Delete"); break;
+ case 1039: UND ("ESC Alt"); break;
+ case 1040: UND ("selection when not highlighted"); break;
+ case 1041: UND ("Use CLIPBOARD"); break;
+ case 1042: UND ("Enable Urgency"); break;
+ case 1043: UND ("Enable raising"); break;
+ case 1044: UND ("Reuse clipboard"); break;
+ case 1045: UND ("Extended reverse-wraparound"); break;
+ case 1046: UND ("Enable Alternate Screen"); break;
+ case 1047: UND ("Alternate Screen Buffer"); break;
+ case 1048: UND ("Save cursor"); break;
+ case 1049: UND ("Save cursor"); break;
+ case 1050: UND ("Terminfo Fn keys"); break;
+ case 1051: UND ("Set Sun Fn keys"); break;
+ case 1052: UND ("HP Fn keys"); break;
+ case 1053: UND ("SCO Fn keys"); break;
+ case 1060: UND ("Legacy keyboard emulation"); break;
+ case 1061: UND ("VT220 keyboard emulation"); break;
+ case 2001: UND ("Readline mouse 1"); break;
+ case 2002: UND ("Readline mouse 2"); break;
+ case 2003: UND ("Readline mouse 2"); break;
+ case 2004: UND ("Bracketed paste"); break;
+ case 2005: UND ("readline quoting"); break;
+ case 2006: UND ("Readline newline paste"); break;
+ default: UND ("Unknown GR H/L"); break;
+ }
+ }
+ break;
+
+ case 'i': /* Ports */
+ if (av[0] == UNDEF) av[0] = 0;
+ switch (av[0]) {
+ case 0: UND ("Print screen"); break;
+ case 1: UND ("Print cursor line"); break;
+ case 4: UND ("Aux Port Off"); break;
+ case 5: UND ("Aux Port On"); break;
+ case 10: UND ("Print dpy"); break;
+ case 11: UND ("Print all"); break;
+ default: UND ("Unknown port request"); break;
+ }
+ break;
+
+ /* Unused: 'j', 'k'; 'l' is above. */
+
+ case 'm': /* Select Graphic Rendition */
+ if (ac == 0)
+ av[ac++] = 0; /* No args means "CSI 0m" */
+ for (i = 0; i < ac; i++) {
+ switch (av[i]) {
+ case UNDEF:
+ case 0: LOG ("Reset text props");
+ st->flags = 0;
+ break;
+ case 1: LOG ("Bold");
+ st->flags |= TTY_BOLD;
+ break;
+ case 2: LOG ("Dim");
+ st->flags |= TTY_DIM;
+ break;
+ case 3: LOG ("Italic");
+ st->flags |= TTY_ITALIC;
+ break;
+ case 4: LOG ("Underline");
+ st->flags |= TTY_UNDERLINE;
+ break;
+ case 5: LOG ("Blink");
+ st->flags |= TTY_BLINK;
+ break;
+ case 6: LOG ("Blink fast");
+ st->flags |= TTY_BLINK;
+ break;
+ case 7: LOG ("Invert");
+ st->flags |= TTY_INVERSE;
+ break;
+ case 8: UND ("Hide"); break;
+ case 9: UND ("Strike"); break;
+
+ case 10: LOG ("Primary font");
+ st->flags &= ~TTY_SYMBOLS;
+ break;
+ case 11: LOG ("Alternate font 1");
+ st->flags |= TTY_SYMBOLS;
+ break;
+ case 12: UND ("Alternate font 2"); break;
+ case 13: UND ("Alternate font 3"); break;
+ case 14: UND ("Alternate font 4"); break;
+ case 15: UND ("Alternate font 5"); break;
+ case 16: UND ("Alternate font 6"); break;
+ case 17: UND ("Alternate font 7"); break;
+ case 18: UND ("Alternate font 8"); break;
+ case 19: UND ("Alternate font 9"); break;
+ case 20: UND ("Fraktur font"); break;
+ case 21: LOG ("Doubly underlined");
+ st->flags |= TTY_UNDERLINE;
+ break;
+ case 22: LOG ("Not dim");
+ st->flags &= ~TTY_DIM;
+ break;
+ case 23: LOG ("Not italic");
+ st->flags &= ~(TTY_ITALIC|TTY_BOLD);
+ break;
+ case 24: LOG ("Not underlined");
+ st->flags &= ~TTY_UNDERLINE;
+ break;
+ case 25: LOG ("No blink");
+ st->flags &= ~TTY_BLINK;
+ break;
+ case 26: LOG ("Proportional spacing"); break;
+ case 27: LOG ("Not reversed");
+ st->flags &= ~TTY_INVERSE;
+ break;
+ case 28: LOG ("Reveal"); break;
+ case 29: LOG ("Not strike"); break;
+
+ case 30: LOG ("Set foreground color 1");
+ set_color (tty, True, 0); break;
+ case 31: LOG ("Set foreground color 2");
+ set_color (tty, True, 1); break;
+ case 32: LOG ("Set foreground color 3");
+ set_color (tty, True, 2); break;
+ case 33: LOG ("Set foreground color 4");
+ set_color (tty, True, 3); break;
+ case 34: LOG ("Set foreground color 5");
+ set_color (tty, True, 4); break;
+ case 35: LOG ("Set foreground color 6");
+ set_color (tty, True, 5); break;
+ case 36: LOG ("Set foreground color 7");
+ set_color (tty, True, 6); break;
+ case 37: LOG ("Set foreground color 8");
+ set_color (tty, True, 7); break;
+
+ case 38: LOG ("Set foreground color N");
+ if (av[i] == 5)
+ set_color (tty, True, av[i+1]); /* 5;N */
+ else if (av[i] == 2)
+ {
+ st->fg.r = av[i+1]; /* 2;R;G;B */
+ st->fg.g = av[i+2];
+ st->fg.b = av[i+3];
+ }
+ i = 999; /* stop processing */
+ break;
+
+ case 39: LOG ("Default foreground color");
+ set_color (tty, True, 0); break;
+
+ case 40: LOG ("Set background color 1");
+ set_color (tty, False, 0); break;
+ case 41: LOG ("Set background color 2");
+ set_color (tty, False, 1); break;
+ case 42: LOG ("Set background color 3");
+ set_color (tty, False, 2); break;
+ case 43: LOG ("Set background color 4");
+ set_color (tty, False, 3); break;
+ case 44: LOG ("Set background color 5");
+ set_color (tty, False, 4); break;
+ case 45: LOG ("Set background color 6");
+ set_color (tty, False, 5); break;
+ case 46: LOG ("Set background color 7");
+ set_color (tty, False, 6); break;
+ case 47: LOG ("Set background color 8");
+ set_color (tty, False, 7); break;
+
+ case 48: LOG ("Set background color N");
+ if (av[i] == 5)
+ set_color (tty, False, av[i+1]); /* 5;N */
+ else if (av[i] == 2)
+ {
+ st->bg.r = av[i+1]; /* 2;R;G;B */
+ st->bg.g = av[i+2];
+ st->bg.b = av[i+3];
+ }
+ i = 999; /* stop processing */
+ break;
+
+ case 49: LOG ("Default background color");
+ set_color (tty, False, 0); break;
+
+ case 50: UND ("Fixed width"); break;
+ case 51: UND ("Framed"); break;
+ case 52: UND ("Encircled"); break;
+ case 53: UND ("Overlined"); break;
+ case 54: UND ("Not framed"); break;
+ case 55: UND ("Not overlined"); break;
+ case 58: UND ("Set underline color"); break;
+ /* 5;n or 2;r;g;b */
+ case 59: UND ("Default underline color"); break;
+ case 60: UND ("Ideogram underline"); break;
+ case 61: UND ("Ideogram double underline"); break;
+ case 62: UND ("Ideogram overline"); break;
+ case 63: UND ("Ideogram double overline"); break;
+ case 64: UND ("Ideogram stress marking"); break;
+ case 65: UND ("No ideogram"); break;
+ case 73: UND ("Superscript"); break;
+ case 74: UND ("Subscript"); break;
+ case 75: UND ("Not super/sub"); break;
+ default: UND ("Unknown"); break;
+ break;
+ }
+ }
+ break;
+
+ case 'n': /* Reports */
+ switch (av[0]) {
+ case 5: LOG ("Report status");
+ if (tty->tty_send)
+ { /* Terminal ok */
+ const char *s = CSI "0n";
+ (*tty->tty_send) (tty->closure, s);
+ }
+ break;
+ case 6: LOG ("Report cursor position");
+ if (tty->tty_send)
+ {
+ char buf[40];
+ sprintf (buf, CSI "%d;%dR", tty->y+1, tty->x+1);
+ (*tty->tty_send) (tty->closure, buf);
+ }
+ break;
+ default: UND ("Unknown report request"); break;
+ break;
+ }
+ break;
+
+ /* No 'o' */
+
+ case 'p': UND ("Pointer mode"); break;
+ case 'q': UND ("LEDs"); break;
+
+ case 'r': LOG ("Scroll Region");
+ if (av[0] == UNDEF) av[0] = 1;
+ if (av[1] == UNDEF) av[1] = tty->height;
+ /* Top and bottom lines, 1-based, inclusive.
+ So "3;3" means a single line at y=2. */
+ st->scroll.y1 = av[0] - 1;
+ st->scroll.y2 = av[1];
+ tty->x = 0;
+ tty->y = st->scroll.y1;
+ st->lcf = False;
+ break;
+
+ case 's': LOG ("Save Current Cursor Position");
+ st->saved.flags = st->flags;
+ st->saved.lcf = st->lcf;
+ st->saved.x = tty->x;
+ st->saved.y = tty->y;
+ break;
+
+ case 't': UND ("Window manip"); break;
+
+ case 'u': LOG ("Restore Saved Cursor Position");
+ st->flags = st->saved.flags;
+ st->lcf = st->saved.lcf;
+ tty->x = st->saved.x;
+ tty->y = st->saved.y;
+ /* Set lcf to False here? */
+ break;
+
+ case 'v': UND ("Display extent"); break;
+ case 'w': UND ("State report"); break;
+ case 'x': UND ("Param report"); break;
+
+ case 'y': /* Tests */
+ switch (av[0]) {
+ case 2:
+ if (av[1] == UNDEF) av[1] = 0;
+ switch (av[1]) {
+ case 0: LOG ("Reset");
+ ansi_tty_reset (tty);
+ break;
+ case 1: LOG ("Self-test");
+ if (tty->tty_send)
+ {
+ char buf[40];
+ sprintf (buf, CSI "%dn", 0); /* Ready */
+ (*tty->tty_send) (tty->closure, buf);
+ }
+ break;
+ case 2: UND ("Data loopback test"); break;
+ case 4: UND ("EIA modem test"); break;
+ case 8: UND ("Repeat tests forever"); break;
+ default: UND ("Unknown test"); break;
+ }
+ break;
+ default: UND ("Unknown y"); break;
+ break;
+ }
+ break;
+
+ case 'z': UND ("Erase rect"); break;
+ case '{': UND ("Selective erase"); break;
+ case '|': UND ("Cols per page"); break;
+ case '}': UND ("Insert cols"); break;
+ case '~': UND ("Delete cols"); break;
+ default: UND ("Unknown"); break;
+ }
+ }
+
+
+ /********************************************************************* Fe */
+
+
+ else if (st->idx == 2 && /* Fe escape sequence */
+ st->buf[0] == ESC &&
+ c >= 0x40 && /* @A-Z[\]^_ */
+ c <= 0x5F)
+ {
+ kind = "Fe";
+ done = True;
+
+ switch (c) { /* These commands await ST */
+ case 'P': /* Device Control String */
+ case ']': /* System Command */
+ case 'X': /* Start of String */
+ case '^': /* Privacy Message */
+ case '_': /* Application Program */
+ st->awaiting_st = True;
+ break;
+ case '\\': /* ST: String Terminator */
+ st->awaiting_st = False;
+ break;
+ case 'N': UND ("Single Shift Two"); break;
+ case 'O': UND ("Single Shift Three"); break;
+
+ case '[': /* CSI: Control Sequence Introducer */
+ done = False;
+ break;
+
+ case 'A': LOG ("VT52 Cursor up");
+ tty->y--;
+ break;
+ case 'B': LOG ("VT52 Cursor down");
+ tty->y++;
+ scrolled_p = True;
+ /* Set lcf to False here? */
+ break;
+ case 'C': LOG ("VT52 Cursor right");
+ tty->x++;
+ scrolled_p = True;
+ /* Set lcf to False here? */
+ break;
+ case 'D': LOG ("Cursor down");
+ goto DO_LF; /* VT52 Cursor left! */
+ break;
+ case 'E': LOG ("Next line");
+ tty->x = 0;
+ goto DO_LF;
+ break;
+ case 'F': LOG ("VT52 Graphics mode");
+ st->flags |= TTY_SYMBOLS;
+ break;
+ case 'G': LOG ("VT52 Exit graphics");
+ st->flags &= ~TTY_SYMBOLS;
+ break;
+ case 'H': LOG ("Set tab");
+ st->tabs[tty->x] = 1;
+ break;
+
+ case 'I': /* VT52 */
+ case 'M':
+ st->lcf = False;
+ tty->y--;
+ if (tty->y < st->scroll.y1)
+ {
+ LOG ("Rev LF scroll");
+ tty->y = st->scroll.y1;
+ tty_scroll (tty, -1);
+ scrolled_p = True;
+ }
+ else
+ LOG ("Rev LF");
+ break;
+
+ case 'J': LOG ("VT52 Erase to EOS");
+ tty_erase (tty,
+ tty->x, tty->x,
+ tty->width-1, tty->height-1);
+ break;
+ case 'K': LOG ("VT52 Erase to EOL");
+ tty_erase (tty,
+ tty->x, tty->y,
+ tty->width-1, tty->y);
+ break;
+
+ case 'Y': UND ("VT52 Direct cursor");
+ /* 4-byte sequence: "ESC Y y x" where x and y are \037 + N.
+ But the rest of the Fe commands are 2 bytes. */
+ break;
+
+ case 'Z': LOG ("VT52 Identify");
+ if (tty->tty_send)
+ {
+ char buf[40];
+ sprintf (buf, "%c%c%c", ESC, '/', 'Z');
+ (*tty->tty_send) (tty->closure, buf);
+ }
+ break;
+
+ default: UND ("Unknown"); break;
+ }
+ }
+
+
+ /********************************************************************* Fs */
+
+
+ else if (st->idx == 2 && /* Fs escape sequence */
+ st->buf[0] == ESC &&
+ c >= 0x60 && /* `a-z{|}~ */
+ c <= 0x7E)
+ {
+ kind = "Fs";
+ done = True;
+
+ switch (c) {
+ case '`': UND ("Disable manual input"); break;
+ case 'a': UND ("Interrupt"); break;
+ case 'b': UND ("Enable manual input"); break;
+ case 'c': LOG ("Full Reset");
+ ansi_tty_reset (tty);
+ break;
+ case 'd': UND ("Coding method delimiter");break;
+ case 'l': UND ("Memory Lock"); break;
+ case 'm': UND ("Memory Unlock"); break;
+ case 'n': UND ("G2 as GL"); break;
+ case 'o': UND ("G3 as GL"); break;
+ case '|': UND ("G3 as GR"); break;
+ case '~': UND ("G1 as GR"); break;
+ case '}': UND ("G2 as GR"); break;
+ default: UND ("Unknown"); break;
+ }
+ }
+
+ else if (st->idx > 0 &&
+ st->buf[0] == ESC)
+ {
+ /* An ESC followed by a character that matched none of the above.
+ We assume it is an unknown 2-byte sequence, which may not be true.
+ */
+ kind = "ESC";
+ done = True;
+ UND ("Unrecognized");
+ }
+
+
+ /******************************************************************* UTF8 */
+
+ /* Assemble UTF-8 multi-byte sequences into a 32 bit Unicode character. */
+
+
+ else if (st->unicrud > 0) /* Expect N more bytes of Unicode */
+ {
+ kind = "UC";
+ st->unicrud--;
+ if (st->unicrud == 0)
+ {
+ /* Finished: replace c with the unichar. */
+ c = 0;
+ utf8_decode ((unsigned char *) st->buf, st->idx, &c);
+ done = True;
+ goto SELF_INSERT;
+ }
+ }
+ else if ((c & 0xE0) == 0xC0) /* 110xxxxx: 11 bits, 2 bytes */
+ {
+ kind = "UC2";
+ st->unicrud = 1;
+ }
+ else if ((c & 0xF0) == 0xE0) /* 1110xxxx: 16 bits, 3 bytes */
+ {
+ kind = "UC3";
+ st->unicrud = 2;
+ }
+ else if ((c & 0xF8) == 0xF0) /* 11110xxx: 21 bits, 4 bytes */
+ {
+ kind = "UC4";
+ st->unicrud = 3;
+ }
+ else if ((c & 0xFC) == 0xF8) /* 111110xx: 26 bits, 5 bytes */
+ {
+ kind = "UC5";
+ st->unicrud = 4;
+ }
+ else if ((c & 0xFE) == 0xFC) /* 1111110x: 31 bits, 6 bytes */
+ {
+ kind = "UC6";
+ st->unicrud = 5;
+ }
+
+
+ /******************************************************************** Co */
+
+
+ else /* Co control codes: single chars. */
+ {
+ SELF_INSERT:
+ kind = "Co";
+ done = True;
+
+ SELF_INSERT_INTERLUDE:
+
+ switch (c) {
+ case 0: /* NOOP */
+ break;
+
+ case 0x07: /* BEL */
+ tty_log_c (tty, c);
+ break;
+
+ case 0x08: /* BS */
+ tty_log_c (tty, c);
+ if (tty->x > 0)
+ tty->x--;
+ st->lcf = False;
+ break;
+
+ case 0x09: /* HT: TAB */
+ {
+ /* Tabs are motion and do not alter what's under them. */
+ tty_log_c (tty, c);
+ tty->x++;
+ while (tty->x < tty->width && !st->tabs[tty->x])
+ tty->x++;
+ st->lcf = False;
+ }
+ break;
+
+ case 0x0A: /* LF */
+ case 0x0B: /* VT */
+ case 0x0C: /* FF */
+ tty_log_c (tty, c);
+ DO_LF:
+ if (st->linefeed_p)
+ tty->x = 0;
+ tty->y++;
+ st->lcf = False;
+ scrolled_p = True;
+ if (tty->y >= st->scroll.y2)
+ {
+ int n = tty->y - st->scroll.y2 + 1;
+ ac = 0;
+ av[ac++] = n;
+ LOG ("Scroll");
+ tty_scroll (tty, n);
+ tty->y = st->scroll.y2 - 1;
+ }
+ break;
+
+ case 0x1A: /* SUB */
+ tty_log_c (tty, c);
+ st->lcf = False;
+ break;
+
+ case 0x0D: /* CR */
+ tty_log_c (tty, c);
+ tty->x = 0;
+ st->lcf = False;
+ break;
+
+ case 0x0E: /* SO */
+ tty_log_c (tty, 0);
+ LOG ("SO font G1");
+ if (st->g1 & TTY_SYMBOLS)
+ st->flags |= TTY_SYMBOLS;
+ else
+ st->flags &= ~TTY_SYMBOLS;
+ break;
+
+ case 0x0F: /* SI */
+ tty_log_c (tty, 0);
+ LOG ("SI font G0");
+ if (st->g0 & TTY_SYMBOLS)
+ st->flags |= TTY_SYMBOLS;
+ else
+ st->flags &= ~TTY_SYMBOLS;
+ break;
+
+ default: /* Insert */
+ tty_log_c (tty, c);
+ if (c >= ' ')
+ {
+ tty_char *ch;
+ if (tty->x >= tty->width - 1)
+ {
+ tty->x = tty->width - 1;
+ if (st->lcf == False)
+ st->lcf = True;
+ else
+ {
+ st->lcf = False;
+ if (tty->state->auto_wrap_p)
+ {
+ tty->x = 0;
+ tty->y++;
+ if (tty->y >= st->scroll.y2)
+ {
+ int n = tty->y - st->scroll.y2 + 1;
+ ac = 0;
+ av[ac++] = n;
+ LOG ("Scroll wrap");
+ tty_scroll (tty, n);
+ tty->y = st->scroll.y2 - 1;
+ scrolled_p = True;
+ }
+ else if (tty->debug_p > 2)
+ LOG ("Wrap");
+ }
+ }
+ }
+
+ if (tty->x >= tty->width) abort();
+ if (tty->y >= tty->height) abort();
+
+ ch = &tty->grid[tty->y * tty->width + tty->x];
+ ch->c = c;
+ ch->flags = st->flags;
+ ch->fg = st->fg;
+ ch->bg = st->bg;
+ tty->x++;
+ }
+ break;
+ }
+ }
+
+ if (tty->x < 0) tty->x = 0;
+ if (tty->x >= tty->width) tty->x = tty->width - 1;
+
+ /* Scrolling commands, or insertions that changed Y, clip the cursor to
+ the scrolling region. But cursor-positioning commands do not. */
+ if (scrolled_p)
+ {
+ if (tty->y < st->scroll.y1) tty->y = st->scroll.y1;
+ if (tty->y >= st->scroll.y2) tty->y = st->scroll.y2 - 1;
+ }
+ else
+ {
+ if (tty->y < 0) tty->y = 0;
+ if (tty->y >= tty->height) tty->y = tty->height - 1;
+ }
+
+ if (done)
+ {
+ st->idx = 0;
+ st->buf[0] = 0;
+ }
+}
+
--- /dev/null
+/* xscreensaver, Copyright © 2025 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __XSCREENSAVER_ANSI_TTY_H__
+#define __XSCREENSAVER_ANSI_TTY_H__
+
+typedef enum {
+ TTY_BOLD = 1,
+ TTY_ITALIC = 2,
+ TTY_INVERSE = 4,
+ TTY_DIM = 8,
+ TTY_UNDERLINE = 16,
+ TTY_BLINK = 32,
+ TTY_SYMBOLS = 64,
+} tty_flag;
+
+typedef struct { unsigned char r, g, b; } tty_color;
+
+typedef struct {
+ unsigned long c; /* Unicode */
+ tty_flag flags; /* Bitmask */
+ tty_color fg, bg;
+} tty_char;
+
+typedef void (*tty_send_fn) (void *closure, const char *text);
+
+typedef struct tty_state tty_state;
+typedef struct { /* Do not change these: */
+
+ int x, y; /* Cursor position */
+ int width, height; /* Screen size */
+ int inverse_p; /* Flip the sense of TTY_INVERSE */
+ tty_char *grid; /* Characters */
+ tty_state *state; /* Internal state */
+
+ /* You can change these: */
+
+ tty_send_fn tty_send; /* Type characters to upstream */
+ void *closure;
+ tty_color cmap[16][2]; /* Default indexed colors */
+
+ int debug_p; /* 1: log errors to stderr
+ 2: and all commands
+ 3: and all text
+ 4: log to /tmp */
+} ansi_tty;
+
+extern ansi_tty *ansi_tty_init (int w, int h);
+extern void ansi_tty_resize (ansi_tty *, int w, int h);
+extern void ansi_tty_print (ansi_tty *, unsigned long c);
+extern void ansi_tty_free (ansi_tty *);
+
+extern const unsigned long ansi_graphics_unicode[256];
+
+#endif /* __XSCREENSAVER_ANSI_TTY_H__ */
-/* xscreensaver, Copyright (c) 1998-2021 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1998-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#include "screenhack.h"
#include "apple2.h"
#include "textclient.h"
+#include "ansi-tty.h"
#include "utf8wc.h"
#include <math.h>
#define NPAR 16
+#define COUNTDOWN_BANNER "intermission "
+#define COUNTDOWN_DURATION 18*60+30
+#undef COUNTDOWN_BANNER
+
struct terminal_controller_data {
Display *dpy;
char curword[256];
unsigned char lastc;
double last_emit_time;
+ time_t launch_time;
text_data *tc;
+ ansi_tty *tty;
int escstate;
int csiparam[NPAR];
int curparam;
int cursor_x, cursor_y;
int saved_x, saved_y;
- int unicruds; char unicrud[7];
union {
struct {
unsigned int bold : 1;
};
+static void a2_tty_send (void *, const char *);
+
/* The structure of closure linkage throughout this code is so amazingly
baroque that I can't get to the 'struct state' from where I need it. */
textclient_close (mine->tc);
mine->tc = 0;
}
+
+ if (mine->tty)
+ ansi_tty_free (mine->tty);
}
static int
(struct terminal_controller_data *) data;
mine->dpy = dpy;
if (event->xany.type == KeyPress && mine->tc)
- return textclient_putc (mine->tc, &event->xkey);
+ return textclient_putc_event (mine->tc, &event->xkey);
return 0;
}
+#ifdef COUNTDOWN_BANNER
+static void
+a2_draw_banner (apple2_state_t *st, struct terminal_controller_data *mine)
+{
+ int x, y;
+ int w = 40;
+ int h = 3;
+ char buf[41], *s;
+ struct timeval tv;
+ double t;
+
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++)
+ st->textlines[y][x] = ' ' | 0xC0;
+
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday (&tv, NULL);
+# else
+ gettimeofday (&tv);
+# endif
+
+ t = ((mine->launch_time + COUNTDOWN_DURATION) -
+ (tv.tv_sec + ((double) tv.tv_usec * 0.000001)));
+ if (t < 0) t = 0;
+ sprintf (buf, "%.30s %d:%02d:%02d.%1d",
+ COUNTDOWN_BANNER,
+ (int) (t/60/60),
+ (int) (t/60) % 60,
+ (int) t % 60,
+ (int) (t * 10) % 10);
+ x = (w - strlen (buf)) / 2;
+ y = 0;
+ s = buf;
+ while (*s && x < w) {
+ char c = *s++;
+ if (c >= 'a' && c <= 'z') c &= 0xDF;
+ c |= 0xC0;
+ st->textlines[y][x] = c;
+ x++;
+ }
+}
+#endif /* COUNTDOWN_BANNER */
+
+
static void
a2_ascii_printc (apple2_state_t *st, unsigned char c,
Bool bold_p, Bool blink_p, Bool rev_p,
}
else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
{
+# ifndef COUNTDOWN_BANNER
c |= 0x80;
+# endif
}
if (bold_p) c |= 0xc0;
unsigned char c)
{
apple2_state_t *st=sim->st;
- int cols = SCREEN_COLS;
- int rows = SCREEN_ROWS;
-
- int i;
- int start, end;
+ int w = SCREEN_COLS;
+ int h = SCREEN_ROWS;
+ int x, y;
+ ansi_tty *tty = state->tty;
+ ansi_tty_print (tty, c);
- /* Mostly duplicated in phosphor.c */
+ /* We could optimize this to not re-draw characters that haven't changed,
+ but it seems plenty fast enough. */
- switch (state->escstate)
- {
- case 0:
- switch (c)
- {
- case 7: /* BEL */
- /* Dummy case - we don't want the screensaver to beep */
- /* #### But maybe this should flash the screen? */
- break;
- case 8: /* BS */
- if (state->cursor_x > 0)
- state->cursor_x--;
- break;
- case 9: /* HT */
- if (state->cursor_x < cols - 8)
- {
- state->cursor_x = (state->cursor_x & ~7) + 8;
- }
- else
- {
- state->cursor_x = 0;
- if (state->cursor_y < rows - 1)
- state->cursor_y++;
- else
- a2_scroll (st);
- }
- break;
- case 10: /* LF */
-# ifndef HAVE_FORKPTY
- state->cursor_x = 0; /* No ptys on iPhone; assume CRLF. */
-# endif
- case 11: /* VT */
- case 12: /* FF */
- if (state->cursor_y < rows - 1)
- state->cursor_y++;
- else
- a2_scroll (st);
- break;
- case 13: /* CR */
- state->cursor_x = 0;
- break;
- case 14: /* SO */
- case 15: /* SI */
- /* Dummy case - there is one and only one font. */
- break;
- case 24: /* CAN */
- case 26: /* SUB */
- /* Dummy case - these interrupt escape sequences, so
- they don't do anything in this state */
- break;
- case 27: /* ESC */
- state->escstate = 1;
- break;
- case 127: /* DEL */
- /* Dummy case - this is supposed to be ignored */
- break;
- case 155: /* CSI */
- state->escstate = 2;
- for(i = 0; i < NPAR; i++)
- state->csiparam[i] = 0;
- state->curparam = 0;
- break;
- default:
-
- /* states 102-106 are for UTF-8 decoding */
-
- if ((c & 0xE0) == 0xC0) { /* 110xxxxx - 11 bits, 2 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 102;
- break;
- } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx - 16 bits, 3 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 103;
- break;
- } else if ((c & 0xF8) == 0xF0) { /* 11110xxx - 21 bits, 4 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 104;
- break;
- } else if ((c & 0xFC) == 0xF8) { /* 111110xx - 26 bits, 5 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 105;
- break;
- } else if ((c & 0xFE) == 0xFC) { /* 1111110x - 31 bits, 6 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 106;
- break;
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++)
+ {
+ tty_char *tc = &tty->grid [tty->width * y + x];
+ unsigned char ascii = 0;
+ tty_flag flag = tc->flags;
+ int inv_p = flag & (TTY_INVERSE | TTY_BOLD);
+ if (tty->inverse_p) inv_p = !inv_p;
+
+ if (tc->c <= 128)
+ ascii = tc->c;
+ else
+ {
+ /* Convert non-ASCII Unicode to closest ASCII. */
+ char utf8[10];
+ int L = utf8_encode (tc->c, utf8, sizeof(utf8)-1);
+ utf8[L] = 0;
+ ascii = '?';
+ if (L)
+ {
+ char *s = utf8_to_latin1 (utf8, True);
+ if (s)
+ {
+ ascii = s[0];
+ free (s);
+ }
+ }
}
- PRINT:
-
- /* If the cursor is in column 39 and we print a character, then
- that character shows up in column 39, and the cursor is no longer
- visible on the screen (it's in "column 40".) If another character
- is printed, then that character shows up in column 0, and the
- cursor moves to column 1.
+ if (! ascii) ascii = ' ';
+
+ a2_goto (st, y, x);
+
+ if (flag & TTY_SYMBOLS) /* Convert to the nearest ASCII */
+ switch (ascii) {
+ case 0x60: ascii = '*'; /* ◆ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x6A: ascii = 'J'; /* ┘ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x6B: ascii = 'T'; /* ┐ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x6C: ascii = 'r'; /* ┌ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x6D: ascii = 'L'; /* └ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x6E: ascii = '+'; /* ┼ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x6F: ascii = '-'; /* ⎺ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x70: ascii = '-'; /* ⎻ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x71: ascii = '='; /* ─ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x72: ascii = '_'; /* ⎼ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x73: ascii = '_'; /* ⎽ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x74: ascii = 'F'; /* ├ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x75: ascii = '+'; /* ┤ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x76: ascii = '+'; /* ┴ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x77: ascii = 'T'; /* ┬ */ flag &= ~TTY_SYMBOLS; break;
+ case 0x78: ascii = '#'; /* │ */ flag &= ~TTY_SYMBOLS; break;
+ }
- This is empirically what xterm and gnome-terminal do, so that must
- be the right thing. (In xterm, the cursor vanishes, whereas; in
- gnome-terminal, the cursor overprints the character in col 39.)
- */
- if (state->cursor_x >= cols)
- {
- state->cursor_x = 0;
- if (state->cursor_y >= rows - 1)
- a2_scroll (st);
- else
- state->cursor_y++;
- }
- a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
- a2_ascii_printc (st, c,
- state->termattrib.bf.bold,
- state->termattrib.bf.blink,
- state->termattrib.bf.rev,
+ if (flag & TTY_SYMBOLS)
+ /* Draw unknown symbol font characters as a box. */
+ a2_ascii_printc (st, ' ', 0, 0, !inv_p, False);
+ else
+ a2_ascii_printc (st, ascii,
+ 0,
+ flag & TTY_BLINK,
+ inv_p,
False);
- state->cursor_x++;
-
- break;
- }
- break;
- case 1:
- switch (c)
- {
- case 24: /* CAN */
- case 26: /* SUB */
- state->escstate = 0;
- break;
- case 'c': /* Reset */
- a2_cls(st);
- state->escstate = 0;
- break;
- case 'D': /* Linefeed */
- if (state->cursor_y < rows - 1)
- state->cursor_y++;
- else
- a2_scroll (st);
- state->escstate = 0;
- break;
- case 'E': /* Newline */
- state->cursor_x = 0;
- state->escstate = 0;
- break;
- case 'M': /* Reverse newline */
- if (state->cursor_y > 0)
- state->cursor_y--;
- state->escstate = 0;
- break;
- case '7': /* Save state */
- state->saved_x = state->cursor_x;
- state->saved_y = state->cursor_y;
- state->escstate = 0;
- break;
- case '8': /* Restore state */
- state->cursor_x = state->saved_x;
- state->cursor_y = state->saved_y;
- state->escstate = 0;
- break;
- case '[': /* CSI */
- state->escstate = 2;
- for(i = 0; i < NPAR; i++)
- state->csiparam[i] = 0;
- state->curparam = 0;
- break;
- case '%': /* Select charset */
- /* @: Select default (ISO 646 / ISO 8859-1)
- G: Select UTF-8
- 8: Select UTF-8 (obsolete)
-
- We can just ignore this and always process UTF-8, I think?
- We must still catch the last byte, though.
- */
- case '(':
- case ')':
- /* I don't support different fonts either - see above
- for SO and SI */
- state->escstate = 3;
- break;
- default:
- /* Escape sequences not supported:
- *
- * H - Set tab stop
- * Z - Terminal identification
- * > - Keypad change
- * = - Other keypad change
- * ] - OS command
- */
- state->escstate = 0;
- break;
- }
- break;
- case 2:
- switch (c)
- {
- case 24: /* CAN */
- case 26: /* SUB */
- state->escstate = 0;
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- if (state->curparam < NPAR)
- state->csiparam[state->curparam] =
- (state->csiparam[state->curparam] * 10) + (c - '0');
- break;
- case ';':
- state->csiparam[++state->curparam] = 0;
- break;
- case '[':
- state->escstate = 3;
- break;
- case '@':
- for (i = 0; i < state->csiparam[0]; i++)
- {
- if(++state->cursor_x > cols)
- {
- state->cursor_x = 0;
- if (state->cursor_y < rows - 1)
- state->cursor_y++;
- else
- a2_scroll (st);
- }
- }
- state->escstate = 0;
- break;
- case 'F':
- state->cursor_x = 0;
- case 'A':
- if (state->csiparam[0] == 0)
- state->csiparam[0] = 1;
- if ((state->cursor_y -= state->csiparam[0]) < 0)
- state->cursor_y = 0;
- state->escstate = 0;
- break;
- case 'E':
- state->cursor_x = 0;
- case 'e':
- case 'B':
- if (state->csiparam[0] == 0)
- state->csiparam[0] = 1;
- if ((state->cursor_y += state->csiparam[0]) >= rows)
- state->cursor_y = rows - 1;
- state->escstate = 0;
- break;
- case 'a':
- case 'C':
- if (state->csiparam[0] == 0)
- state->csiparam[0] = 1;
- if ((state->cursor_x += state->csiparam[0]) >= cols)
- state->cursor_x = cols - 1;
- state->escstate = 0;
- break;
- case 'D':
- if (state->csiparam[0] == 0)
- state->csiparam[0] = 1;
- if ((state->cursor_x -= state->csiparam[0]) < 0)
- state->cursor_x = 0;
- state->escstate = 0;
- break;
- case 'd':
- if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
- state->cursor_y = rows - 1;
- state->escstate = 0;
- break;
- case '`':
- case 'G':
- if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
- state->cursor_x = cols - 1;
- state->escstate = 0;
- break;
- case 'f':
- case 'H':
- if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
- state->cursor_y = rows - 1;
- if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
- state->cursor_x = cols - 1;
- if(state->cursor_y < 0)
- state->cursor_y = 0;
- if(state->cursor_x < 0)
- state->cursor_x = 0;
- state->escstate = 0;
- break;
- case 'J':
- start = 0;
- end = rows * cols;
- if (state->csiparam[0] == 0)
- start = cols * state->cursor_y + state->cursor_x;
- if (state->csiparam[0] == 1)
- end = cols * state->cursor_y + state->cursor_x;
-
- a2_goto(st, state->cursor_y, state->cursor_x);
- for (i = start; i < end; i++)
- {
- a2_ascii_printc(st, ' ', False, False, False, False);
- }
- state->escstate = 0;
- break;
- case 'K':
- start = 0;
- end = cols;
- if (state->csiparam[0] == 0)
- start = state->cursor_x;
- if (state->csiparam[1] == 1)
- end = state->cursor_x;
-
- a2_goto(st, state->cursor_y, state->cursor_x);
- for (i = start; i < end; i++)
- {
- a2_ascii_printc(st, ' ', False, False, False, False);
- }
- state->escstate = 0;
- break;
- case 'm': /* Set attributes */
- for (i = 0; i <= state->curparam; i++)
- {
- switch(state->csiparam[i])
- {
- case 0:
- state->termattrib.w = 0;
- break;
- case 1:
- state->termattrib.bf.bold = 1;
- break;
- case 5:
- state->termattrib.bf.blink = 1;
- break;
- case 7:
- state->termattrib.bf.rev = 1;
- break;
- case 21:
- case 22:
- state->termattrib.bf.bold = 0;
- break;
- case 25:
- state->termattrib.bf.blink = 0;
- break;
- case 27:
- state->termattrib.bf.rev = 0;
- break;
- }
- }
- state->escstate = 0;
- break;
- case 's': /* Save position */
- state->saved_x = state->cursor_x;
- state->saved_y = state->cursor_y;
- state->escstate = 0;
- break;
- case 'u': /* Restore position */
- state->cursor_x = state->saved_x;
- state->cursor_y = state->saved_y;
- state->escstate = 0;
- break;
- case '?': /* DEC Private modes */
- if ((state->curparam != 0) || (state->csiparam[0] != 0))
- state->escstate = 0;
- break;
- default:
- /* Known unsupported CSIs:
- *
- * L - Insert blank lines
- * M - Delete lines (I don't know what this means...)
- * P - Delete characters
- * X - Erase characters (difference with P being...?)
- * c - Terminal identification
- * g - Clear tab stop(s)
- * h - Set mode (Mainly due to its complexity and lack of good
- docs)
- * l - Clear mode
- * m - Set mode (Phosphor is, per defenition, green on black)
- * n - Status report
- * q - Set keyboard LEDs
- * r - Set scrolling region (too exhausting - noone uses this,
- right?)
- */
- state->escstate = 0;
- break;
- }
- break;
- case 3:
- state->escstate = 0;
- break;
-
- case 102:
- case 103:
- case 104:
- case 105:
- case 106:
- {
- int total = state->escstate - 100; /* see what I did there */
- if (state->unicruds < total) {
- /* Buffer more bytes of the UTF-8 sequence */
- state->unicrud[state->unicruds++] = c;
- }
-
- if (state->unicruds >= total) {
- /* Done! Convert it to ASCII and print that. */
- char *s;
- state->unicrud[state->unicruds] = 0;
- s = utf8_to_latin1 ((const char *) state->unicrud, True);
- state->unicruds = 0;
- state->escstate = 0;
- if (s) {
- c = s[0];
- free (s);
- goto PRINT;
- } else {
- /* c = 0; */
- }
- }
}
- break;
- default:
- abort();
- }
- a2_goto(st, state->cursor_y, state->cursor_x);
+ a2_goto (st, tty->y, tty->x);
}
sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
mine=(struct terminal_controller_data *) sim->controller_data;
mine->dpy = sim->dpy;
+ if (!mine->launch_time) mine->launch_time = time ((time_t *) 0);
mine->fast_p = global_fast_p;
switch(*stepno) {
case 0:
+# ifdef COUNTDOWN_BANNER
+ if (1)
+# else
if (random()%2)
+# endif
st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
showing text */
a2_cls(st);
+# ifndef COUNTDOWN_BANNER
a2_goto(st,0,16);
a2_prints(st, "APPLE ][");
a2_goto(st,2,0);
+# endif
mine->cursor_y = 2;
if (! mine->tc) {
0);
}
+ if (!mine->tty) {
+ mine->tty = ansi_tty_init (SCREEN_COLS, SCREEN_ROWS);
+ mine->tty->closure = mine;
+ mine->tty->tty_send = a2_tty_send;
+ }
+
if (! mine->fast_p)
*next_actiontime += 4.0;
*stepno = 10;
else
a2_ascii_printc (st, c, False, False, False, True);
}
+
+# ifdef COUNTDOWN_BANNER
+ a2_draw_banner (st, mine);
+# endif
}
break;
}
}
+static void
+a2_tty_send (void *closure, const char *text)
+{
+ struct terminal_controller_data *mine =
+ (struct terminal_controller_data *) closure;
+ textclient_puts (mine->tc, text);
+}
+
+
struct basic_controller_data {
int prog_line;
int x,y,k;
"affluenza",
"alertness",
"Algeria",
+ "all your base",
"antifa",
"anxiety",
"aorta",
"bells",
"belly",
"bird flu",
+ "Bitcorn",
"bliss",
"bogosity",
"boobies",
"boobs",
"booty",
"bread",
+ "bribe",
"brogrammers",
"bubba",
"burrito",
"DNA Lounge",
"doberman",
"DOOM",
+ "doom loop",
"dot com",
"dreams",
"drugs",
+ "Dunning-Krugerrands",
"easy",
"ebony",
"election",
"eloquence",
"emergency",
+ "emolument",
"eureka",
"excommunication",
"fat",
"goggles",
"goobers",
"gorilla",
+ "guillotine",
+ "H5N1",
"halibut",
"handmaid",
"happiness",
"heroin",
"heroine",
"hope",
+ "horse paste",
"hysteria",
"icepick",
"identity",
"nationalism",
"nature",
"neuron",
+ "NFTs",
"noise",
"nomenclature",
+ "NULL",
+ "null island",
"nutria",
"OBEY",
+ "ouroboros",
"ocelot",
"offspring",
"overseer",
"television",
"tenant",
"tendril",
+ "teratoma",
"terror",
"terrorism",
"terrorist",
"the unknown",
"toast",
"topography",
+ "tribble",
"truism",
"truthiness",
"turgid",
"twits",
+ "undef",
"underbrush",
"underling",
"unguent",
"yellow",
"yesterday",
"your nose",
+ "Y2038",
"Zanzibar",
"zeal",
"zebra",
#include "screenhack.h"
#include "xft.h"
+#include "xftwrap.h"
#include "ximage-loader.h"
#include "apple2.h"
}
+static struct bsod_state *
+windows_10_recovery (Display *dpy, Window window)
+{
+ struct bsod_state *bst =
+ make_bsod_state (dpy, window, "win10r", "Win10r");
+ const char *line1 = "Recovery";
+ const char *line2 = "It looks like Windows didn't load correctly";
+ const char *line3 = "If you'd like to restart and try again, choose "
+ "\"Restart my PC\" below. Otherwise, choose "
+ "\"See advanced repair options\" for troubleshooting "
+ "tools and advanced options. If you don't know which "
+ "option is right for you, contact someone you "
+ "trust to help with this.";
+ const char *line4 = " See advanced repair options ";
+ const char *line5 = " Restart my PC ";
+ int line_height = bst->fontA->ascent + bst->fontA->descent;
+ int line_heightB = bst->fontB->ascent + bst->fontB->descent;
+ int line_heightC = bst->fontC->ascent + bst->fontC->descent;
+ int y = line_heightC * 2;
+ int x, x2, x3;
+ XGlyphInfo ov;
+
+ XftTextExtentsUtf8 (bst->dpy, bst->fontB, (FcChar8 *) line2, strlen(line2),
+ &ov);
+ x = (bst->xgwa.width - (ov.xOff * 1.7)) / 2;
+ if (x < 10) x = 10;
+ BSOD_MARGINS (bst, x, x);
+
+ BSOD_WORD_WRAP (bst);
+ BSOD_FONT (bst, 2);
+ BSOD_MOVETO (bst, 0, y);
+ BSOD_TEXT (bst, LEFT, line1);
+ BSOD_TEXT (bst, LEFT, "\n");
+
+ BSOD_FONT (bst, 1);
+ BSOD_TEXT (bst, LEFT, line2);
+ BSOD_TEXT (bst, LEFT, "\n");
+
+ BSOD_FONT (bst, 0);
+ BSOD_TEXT (bst, LEFT, line3);
+
+ XftTextExtentsUtf8 (bst->dpy, bst->font, (FcChar8 *) line5, strlen(line5),
+ &ov);
+ x2 = bst->xgwa.width - x - ov.xOff;
+
+ if ( bst->xgwa.width > bst->xgwa.height)
+ y += line_heightB * 2 + line_height * 7;
+ else
+ y += line_heightB * 3 + line_height * 9;
+
+ BSOD_TRUNCATE (bst);
+ BSOD_MOVETO (bst, x2, y);
+ BSOD_TEXT (bst, LEFT, line5);
+ BSOD_RECT (bst, False, x2, y - line_height, ov.xOff,
+ line_height + bst->fontA->descent * 2);
+
+ XftTextExtentsUtf8 (bst->dpy, bst->font, (FcChar8 *) line4, strlen(line4),
+ &ov);
+ x3 = x2 - ov.xOff - line_height;
+
+ BSOD_TRUNCATE (bst);
+ BSOD_MOVETO (bst, x3, y);
+ BSOD_TEXT (bst, LEFT, line4);
+ BSOD_RECT (bst, False, x3, y - line_height, ov.xOff,
+ line_height + bst->fontA->descent * 2);
+
+
+ XClearWindow (dpy, window);
+ return bst;
+}
+
+
static struct bsod_state *
windows_10 (Display *dpy, Window window)
{
0xFF,0xFF,0xFF,0xFF,0xFF,0x01};
Pixmap pixmap;
- const char * const lines[] = {
- ":(\n",
- "\n",
- "Your PC ran into a problem and needs to restart. We're just\n",
- "collecting some error info, and then we'll restart for you.\n",
- "\n",
- "\n",
- "\n",
- "For more information about this issue and\n",
- "possible fixes, visit\n",
-/* "https://www.jwz.org/xscreensaver\n",*/
- "http://youtu.be/-RjmN9RZyr4\n",
- "\n",
- "If you call a support person, give them this info:\n",
+ const char * const lines1[] = {
+ ":(",
+
+ "\n"
+ "Your PC ran into a problem and needs to restart. We're just"
+ " collecting some error info, and then we'll restart for you."
+ "\n\n\n",
+
+ "\n\n",
+
+ "For more information about this issue and possible fixes, visit\n"
+ "http://youtu.be/-RjmN9RZyr4\n\n"
+ "If you call a support person, give them this info:\n"
"Stop code CRITICAL_PROCESS_DIED",
- };
- int i, y = 0, y0 = 0;
- int line_height0 = bst->fontB->ascent;
- int line_height1 = bst->fontA->ascent + bst->fontA->descent;
- int line_height2 = bst->fontC->ascent + bst->fontC->descent;
- int line_height = line_height0;
- int top, left0, left;
- int stop = 60 + (random() % 39);
+ };
+ const char * const lines2[] = {
+ "-\\_(:/)_/-", /* ¯\\_(ツ)_/¯ BSOD_TEXT() does not support UTF-8. */
- if (!(random() % 7))
- return windows_10_update (dpy, window);
+ "\n"
+ "Your PC ran into a ClownStrike and needs to restart 15 or more times."
+ "\n\n\n",
+
+ "\n\n",
+
+ "For more information about this issue and possible fixes, visit\n"
+ "http://youtu.be/-RjmN9RZyr4\n\n"
+ "If you call a support person, give them this info:\n"
+ "Stop code COMPLIANCE_LINE_ITEM_OK",
+ };
+ const char * const lines3[] = {
+ "x__x",
+
+ "\n"
+ "This place is a message... "
+ "and part of a system of messages... "
+ "pay attention to it! "
+ "\n\n"
+ "Sending this message was important to us. "
+ "We considered ourselves to be a powerful culture."
+ "\n\n",
+
+ "\nThis place is not a place of honor... "
+ "no highly esteemed deed is commemorated here... "
+ "nothing valued is here. "
+ "\n\n"
+ "What is here was dangerous and repulsive to us. "
+ "This message is a warning about danger. "
+ "\n",
+
+ "The danger is in a particular location... "
+ "it increases towards a center... "
+ "the center of danger is here... "
+ "of a particular size and shape, and below us. "
+ "\n\n"
+ "The danger is still present, in your time, as it was in ours. "
+ "The danger is to the body, and it can kill. "
+ "\n\n"
+ "This place is best shunned and left uninhabited."
+ };
+ int i, y = 0;
+ int top, left0, left, right, y1;
+ Bool clownp = !(random() % 10);
+ Bool honorp = !clownp && !(random() % 20);
+ const char * const * lines = (clownp ? lines2 : honorp ? lines3 : lines1);
+ int stop = 60 + (random() % 39) + (clownp ? 1300 : 0);
+ if (!(random() % 4))
+ return windows_10_recovery (dpy, window);
- line_height1 *= 1.3;
- line_height2 *= 1.5;
+ if (!(random() % 14))
+ return windows_10_update (dpy, window);
- top = ((bst->xgwa.height - bst->yoff
- - (line_height0 * 1 +
- line_height1 * 6 +
- line_height2 * 6))
- / 2);
+ top = (bst->xgwa.height > 800 ? bst->fontB->ascent : 0);
{
XGlyphInfo ov;
- const char *s = lines[2];
-
+ const char *s = lines1[1];
XftTextExtentsUtf8 (bst->dpy, bst->font, (FcChar8 *) s, strlen(s), &ov);
- left = left0 = (bst->xgwa.width - ov.xOff) / 2;
+ left = (bst->xgwa.width - (ov.xOff * 0.55)) / 2;
+ if (left < 10) left = 10;
+ left0 = right = left;
+ BSOD_MARGINS (bst, left, right);
}
pixmap = XCreatePixmapFromBitmapData (dpy, window, (char *) qr_bits,
bst->pixmap = pixmap;
y = top;
- line_height = line_height0;
BSOD_FONT (bst, 1);
- for (i = 0; i < countof(lines); i++)
+ for (i = 0; i < countof(lines1); i++)
{
- BSOD_MOVETO (bst, left, y);
- BSOD_TEXT (bst, LEFT, lines[i]);
- y += line_height;
+ int oy = y;
+ int fid = (i == 0 ? 1 : i >= 3 ? 2 : 0);
+ XftFont *font = (fid == 0 ? bst->font :
+ fid == 1 ? bst->fontB : bst->fontC);
+ char *line;
+ XGlyphInfo ov;
+
+ line = xft_word_wrap (dpy, font, lines[i],
+ bst->xgwa.width - left - right);
+ XftTextExtentsUtf8_multi (dpy, font, (FcChar8 *) line, strlen(line),
+ &ov);
+
+ BSOD_MOVETO (bst, left, y + font->ascent);
+ BSOD_FONT (bst, fid);
+ BSOD_TEXT (bst, LEFT, line);
+
+ free (line);
+ line = 0;
+ y += ov.height + font->descent;
+
if (i == 0)
{
- BSOD_FONT (bst, 0);
- line_height = line_height1;
+ BSOD_MOVETO (bst, left, y);
}
- else if (i == 4)
+ else if (i == 1)
{
- y0 = y;
- y += line_height / 2;
- BSOD_PIXMAP (bst, 0, 0, qr_width, qr_height, left, y + line_height1);
- BSOD_FONT (bst, 2);
- line_height = line_height2;
- left += qr_width + line_height2 / 2;
-# ifdef HAVE_MOBILE
- y -= 14;
-# endif
+// y += font->ascent + font->descent;
+ y1 = y;
+// y += font->ascent + font->descent;
+ }
+ else if (i == 2)
+ {
+ left += qr_width + font->ascent / 2;
+ if (bst->xgwa.width > bst->xgwa.height)
+ right += qr_width * 1.8;
+ BSOD_MARGINS (bst, left, right);
+ }
+ else if (i == 3)
+ {
+ BSOD_PIXMAP (bst, 0, 0, qr_width, qr_height, left0, oy);
}
}
- left = left0;
+ BSOD_MARGINS (bst, left0, 0);
BSOD_FONT (bst, 0);
for (i = 0; i <= stop; i++)
{
char buf[100];
sprintf (buf, "%d%% complete", i);
- BSOD_MOVETO (bst, left, y0);
+ BSOD_MOVETO (bst, left0, y1);
BSOD_TEXT (bst, LEFT, buf);
BSOD_PAUSE (bst, 85000);
}
}
+static struct bsod_state *
+bitlocker (Display *dpy, Window window)
+{
+ struct bsod_state *bst =
+ make_bsod_state (dpy, window, "bitlocker", "Bitlocker");
+ int top = bst->fontB->ascent + bst->fontB->descent;
+ int left = top * 2;
+ int right = left + bst->fontB->ascent * 22;
+ const char *bottom = ("Press Enter to reboot and try again\n"
+ "Press ESC for BitLocker recovery");
+ int which = random() % 5;
+
+ if (right > bst->xgwa.width) right = bst->xgwa.width;
+ BSOD_MARGINS (bst, left, bst->xgwa.width - right);
+ BSOD_WORD_WRAP (bst);
+ BSOD_FONT (bst, 1);
+ BSOD_MOVETO (bst, 0, top * 2);
+
+ switch (which) {
+ case 0:
+ BSOD_TEXT (bst, LEFT, "BitLocker\n\n");
+ BSOD_FONT (bst, 0);
+ BSOD_TEXT (bst, LEFT,
+ "Plug in the USB drive that has the BitLocker key\n");
+ break;
+
+ case 1:
+ BSOD_TEXT (bst, LEFT, "BitLocker recovery\n\n");
+ BSOD_FONT (bst, 0);
+ BSOD_TEXT (bst, LEFT,
+ "To recover this drive, plug in the USB drive that has "
+ "the BitLocker recovery key\n\n");
+ BSOD_FONT (bst, 2);
+ BSOD_TEXT (bst, LEFT,
+ "Bitlocker needs your recovery key to unlock your drive "
+ "because Secure Boot policy has unexpectedly changed.\n"
+ "For more information on how to retrieve this key, go to\n"
+ "https://www.jwz.org/xscreensaver/ from another PC "
+ "or mobile device.");
+ bottom = ("Press Enter to reboot and try again\n"
+ "Press Esc or the Windows key for more recovery options");
+ break;
+ case 2:
+ BSOD_TEXT (bst, LEFT, "BitLocker\n\n");
+ BSOD_FONT (bst, 0);
+ BSOD_TEXT (bst, LEFT,
+ "Enter the PIN to unlock this drive\n");
+ BSOD_INVERT (bst);
+ BSOD_TRUNCATE (bst);
+ BSOD_TEXT (bst, LEFT,
+ " "
+ " ");
+ BSOD_INVERT (bst);
+ BSOD_WORD_WRAP (bst);
+ BSOD_TEXT (bst, LEFT,
+ "\n\n\n"
+ "Use the number keys or function keys F1-F10 "
+ "(use F10 for 0)."
+ "\n\n\n"
+ "Press the Insert key to see the PIN as you type.");
+ break;
+
+ case 3:
+ BSOD_TEXT (bst, LEFT, "BitLocker recovery\n\n");
+ BSOD_FONT (bst, 0);
+ BSOD_TEXT (bst, LEFT,
+ "Enter the recovery key for this drive\n");
+ BSOD_INVERT (bst);
+ BSOD_TRUNCATE (bst);
+ BSOD_TEXT (bst, LEFT,
+ " "
+ " ");
+ BSOD_INVERT (bst);
+ BSOD_WORD_WRAP (bst);
+
+ BSOD_FONT (bst, 2);
+ BSOD_TEXT (bst, LEFT,
+ "\n\n\n"
+ "Use the number keys or function keys F1-F10 "
+ "(use F10 for 0).\n");
+ {
+ int which2;
+ char k[200];
+ sprintf (k, "Recovery key ID (to identify your key): "
+ "%08X-%04X-%04X-%04X%08X\n\n",
+ random() & 0xFFFFFFFF,
+ random() & 0xFFFF,
+ random() & 0xFFFF,
+ random() & 0xFFFF,
+ random() & 0xFFFFFFFF);
+ BSOD_TEXT (bst, LEFT, k);
+
+ which2 = random() % 4;
+ switch (which2) {
+ case 0:
+ BSOD_TEXT (bst, LEFT,
+ "Bitlocker needs your recovery key to unlock your "
+ "drive because Secure Boot policy has unexpectedly "
+ "changed."
+ "\n\n");
+ break;
+ case 1:
+ BSOD_TEXT (bst, LEFT,
+ "Bitlocker needs your recovery key to unlock your "
+ "PC's configuration has changed. This may have happened "
+ "because a disc or USB device ws inserted. Removing it "
+ "and restarting your PC may fix this problem."
+ "\n\n");
+ break;
+ case 3:
+ BSOD_TEXT (bst, LEFT,
+ "Bitlocker needs your recovery key to unlock your drive "
+ "because Secure Boot has been disabled. Either Secure "
+ "Boot must be re-enabled, or BitLocker must be suspended "
+ "for Windows to start normally."
+ "\n\n");
+ break;
+ default:
+ break;
+ }
+ }
+
+ BSOD_TEXT (bst, LEFT,
+ "Here's how to find your key:\n"
+ "- Sign in on another device and go to: "
+ "https://www.jwz.org/\n"
+ "- For more information go to: "
+ "https://www.jwz.org/xscreensaver/");
+ break;
+
+ case 4:
+ BSOD_TEXT (bst, LEFT, "Recovery\n\n");
+ BSOD_FONT (bst, 0);
+ BSOD_TEXT (bst, LEFT,
+ "There are no more BitLocker recovery options on your PC"
+ "\n\n");
+ BSOD_FONT (bst, 2);
+ BSOD_TEXT (bst, LEFT,
+ "You'll need to use the recovery tools on your installation "
+ "media. If you don't have any installation media (like a "
+ "disc or USB device), contact your system administrator or "
+ "PC manufacturer.");
+ bottom = ("Press Enter to try again\n"
+ "Press F8 for Startup Settings\n"
+ "Press Esc for UEFI Firmware Settings");
+ break;
+
+ default:
+ abort();
+ }
+
+ BSOD_FONT (bst, 0);
+ BSOD_MOVETO (bst, 0, bst->xgwa.height - top -
+ (bst->fontB->ascent + bst->fontB->descent) * 1);
+ BSOD_TEXT (bst, LEFT, bottom);
+
+ XClearWindow (dpy, window);
+ return bst;
+}
+
+
/* As seen in Portal 2. By jwz.
*/
static struct bsod_state *
}
+/* Linux kernel panic obscured by systemd, 2024.
+ https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/drm_panic.c
+ */
+static struct bsod_state *
+systemd (Display *dpy, Window window)
+{
+ struct bsod_state *bst = make_bsod_state (dpy, window, "systemd", "Systemd");
+ int lh = (bst->font->ascent + bst->font->descent);
+ BSOD_MOVETO (bst, 0, lh);
+ BSOD_TEXT (bst, LEFT,
+ " .--. _\n"
+ " |o_o | | |\n"
+ " |:_/ | | |\n"
+ " // \\ \\ |_|\n"
+ " (| | ) _\n"
+ " /'\\_ _/`\\ (_)\n"
+ " \\___)=(___/\n");
+ BSOD_MOVETO (bst, 0, (bst->xgwa.height / 2) - lh);
+ BSOD_TEXT (bst, CENTER,
+ "KERNEL PANIC !\n"
+ "\n"
+ "Please reboot your computer.");
+ XClearWindow (dpy, window);
+ return bst;
+}
+
+
/*****************************************************************************
*****************************************************************************/
{ "NT", windows_nt },
{ "Win2K", windows_other },
{ "Win10", windows_10 },
+ { "Bitlocker", bitlocker },
{ "Ransomware", windows_ransomware },
{ "Amiga", amiga },
{ "Mac", mac },
{ "Tivo", tivo },
{ "Nintendo", nintendo },
{ "Gnome", gnome },
+ { "Systemd", systemd },
};
"*doNT: True",
"*doWin2K: True",
"*doWin10: True",
+ "*doBitlocker: True",
"*doRansomware: True",
"*doAmiga: True",
"*doMac: True",
"*doTivo: True",
"*doNintendo: True",
"*doGnome: True",
+ "*doSystemd: True",
".foreground: White",
".background: Black",
".win10.foreground: White",
".win10.background: #1070AA",
+ ".win10r.foreground: White",
+ ".win10r.background: #1070AA",
+
+ ".bitlocker.foreground: White",
+ ".bitlocker.background: #1070AA",
+
".ransomware.foreground: White",
".ransomware.background: #841212",
".ransomware.foreground2: Black", /* ransom note */
".gnome.background2: #F0F0F0",
".gnome.foreground2: #2E3436",
+ ".systemd.background: #0000AA",
+ ".systemd.foreground: White",
+
"*dontClearRoot: True",
ANALOGTV_DEFAULTS
".win10.font: Arial 24, Helvetica 24",
".win10.bigFont: Arial 24, Helvetica 24",
- ".win10.fontB: Arial 36, Helvetica 36",
+ ".win10.fontB: Arial 90, Helvetica 36",
".win10.fontC: Arial 16, Helvetica 16",
+ ".win10r.font: Arial 16, Helvetica 24",
+ ".win10r.bigFont: Arial 16, Helvetica 24",
+ ".win10r.fontB: Arial 28, Helvetica 36",
+ ".win10r.fontC: Arial 50, Helvetica 16",
+
+ ".bitlocker.font: Arial 24, Helvetica 24",
+ ".bitlocker.bigFont: Arial 24, Helvetica 24",
+ ".bitlocker.fontB: Arial 36, Helvetica 36",
+ ".bitlocker.fontC: Arial 18, Helvetica 18",
+
/* "Arial" loads "ArialMT" but "Arial Bold" does not load "Arial-BoldMT"? */
".ransomware.font: Arial 12, Helvetica 12",
".ransomware.bigFont: Arial 12, Helvetica 12",
".gnome.font: Helvetica Bold 13",
".gnome.bigFont: Helvetica Bold 13",
".gnome.fontB: Helvetica 13",
+
+ ".systemd.font: Classic Console 14",
+ ".systemd.bigFont: Classic Console 14",
0
};
{ "-no-2k", ".doWin2K", XrmoptionNoArg, "False" },
{ "-win10", ".doWin10", XrmoptionNoArg, "True" },
{ "-no-win10", ".doWin10", XrmoptionNoArg, "False" },
+ { "-bitlocker", ".doBitlocker", XrmoptionNoArg, "True" },
+ { "-no-bitlocker", ".doBitlocker", XrmoptionNoArg, "False" },
{ "-ransomware", ".doRansomware", XrmoptionNoArg, "True" },
{ "-no-ransomware", ".doRansomware", XrmoptionNoArg, "False" },
{ "-amiga", ".doAmiga", XrmoptionNoArg, "True" },
{ "-no-nintendo", ".doNintendo", XrmoptionNoArg, "False" },
{ "-gnome", ".doGnome", XrmoptionNoArg, "True" },
{ "-no-gnome", ".doGnome", XrmoptionNoArg, "False" },
+ { "-systemd", ".doSystemd", XrmoptionNoArg, "True" },
+ { "-no-systemd", ".doSystemd", XrmoptionNoArg, "False" },
ANALOGTV_OPTIONS
{ 0, 0, 0, 0 }
};
#!/usr/bin/perl -w
-# Copyright © 2008-2024 Jamie Zawinski <jwz@jwz.org>
+# Copyright © 2008-2025 Jamie Zawinski <jwz@jwz.org>
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
use strict;
my $progname = $0; $progname =~ s@.*/@@g;
-my ($version) = ('$Revision: 1.44 $' =~ m/\s(\d[.\d]+)\s/s);
+my ($version) = ('$Revision: 1.49 $' =~ m/\s(\d[.\d]+)\s/s);
my $verbose = 0;
my $debug_p = 0;
}
}
+ my $desc0 = "$desc";
+ utf8::decode($desc0); # Pack UTF-8 into wide chars.
+
my $desc1 = ("$name, version $vers.\n\n" . # savername.xml
$desc . "\n" .
"\n" .
"From the XScreenSaver collection: " .
"https://www.jwz.org/xscreensaver/\n" .
- "Copyright \302\251 $year by $authors.\n");
+ "Copyright \x{A9} $year by $authors.\n");
my $desc2 = ("$name $vers,\n" . # Info.plist
- "\302\251 $year $authors.\n" .
+ "\x{A9} $year $authors.\n" .
#"From the XScreenSaver collection:\n" .
#"https://www.jwz.org/xscreensaver/\n" .
"\n" .
$desc .
"\n");
- utf8::decode($desc1); # Pack UTF-8 into wide chars.
+
+ utf8::decode($desc1); # Pack UTF-8 into wide chars (redundant?)
utf8::decode($desc2);
# unwrap lines, but only when it's obviously ok: leave blank lines,
$saver_class =~ s/\]\[/2/gs;
$saver_class =~ s/[-_\s]//gs;
+ # If the saver's title is not case-insensitively the same as its progname
+ # after removing spaces, the class name has to be the progname, or
+ # jwxyz_nativeInit is not able to find it in the function_table.
+ # (Cuboctahedron Eversion, Möbius, MöbiusGears, Moiré, Moiré2.)
+ #
+ if (lc($saver_class) ne lc($saver)) {
+ $saver_class = $saver;
+ $saver_class =~ s/^(.)/\U$1/s;
+ }
+
my $settings = '';
my $localize0 = sub($$) {
"android.permission.INTERNET\" />\n" .
" <uses-permission android:name=\"" .
"android.permission.READ_EXTERNAL_STORAGE\" />\n" .
+ " <uses-permission android:name=\"" .
+ "android.permission.READ_MEDIA_IMAGES\" />\n" .
" <application android:icon=\"\@drawable/thumbnail\"\n" .
" android:banner=\"\@drawable/thumbnail\"\n" .
}
sub usage() {
- print STDERR "usage: $progname [--verbose] [--debug]" .
+ print STDERR "usage: $progname [--verbose] [--debug] [--force]" .
" [--build-android] files ...\n";
exit 1;
}
binmode (STDOUT, ':utf8');
binmode (STDERR, ':utf8');
+ my $force_p = 0;
my $android_p = 0;
my @files = ();
while ($#ARGV >= 0) {
$_ = shift @ARGV;
if (m/^--?verbose$/) { $verbose++; }
+ elsif (m/^--?force$/) { $force_p++; }
elsif (m/^-v+$/) { $verbose += length($_)-1; }
elsif (m/^--?debug$/s) { $debug_p++; }
elsif (m/^--?build-android$/s) { $android_p++; }
# else { usage; }
}
- usage unless ($#files >= 0);
+ usage unless ($#files >= 0 || $force_p);
my $failures = 0;
foreach my $file (@files) {
$failures += check_config ($file, $android_p);
a screen saver and locker for the X window system
by Jamie Zawinski
- version 6.09
- 07-Jun-2024
+ version 6.10
+ 27-Apr-2025
https://www.jwz.org/xscreensaver/
<boolean id="nt" _label="Windows NT" arg-unset="--no-nt"/>
<boolean id="2k" _label="Windows 2000 " arg-unset="--no-2k"/>
<boolean id="win10" _label="Windows 10 " arg-unset="--no-win10"/>
- <boolean id="vmwareArm" _label="VMware ESXi-Arm" arg-unset="--no-vmwarearm"/>
+ <boolean id="bitlocker" _label="BitLocker" arg-unset="--no-bitlocker"/>
</vgroup>
<vgroup>
<boolean id="msdos" _label="MS-DOS" arg-unset="--no-msdos"/>
<boolean id="glados" _label="GLaDOS" arg-unset="--no-glados"/>
<boolean id="amiga" _label="AmigaDOS" arg-unset="--no-amiga"/>
<boolean id="android" _label="Android" arg-set="--android"/>
+ <boolean id="vmwareArm" _label="VMware ESXi-Arm" arg-unset="--no-vmwarearm"/>
</vgroup>
<vgroup>
<boolean id="apple2" _label="Apple ][" arg-unset="--no-apple2"/>
<boolean id="hpux" _label="HPUX" arg-unset="--no-hpux"/>
<boolean id="tru64" _label="Tru64" arg-unset="--no-tru64"/>
<boolean id="gnome" _label="GNOME" arg-unset="--no-gnome"/>
+ <boolean id="systemd" _label="Systemd" arg-unset="--no-systemd"/>
</vgroup>
</hgroup>
--- /dev/null
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="dumpsterfire" _label="Dumpster Fire" gl="yes">
+
+ <command arg="--root"/>
+
+ <video href="https://www.youtube.com/watch?v=9XZQ3xLS4O8"/>
+
+ <number id="delay" type="slider" arg="--delay %"
+ _label="Frame rate" _low-label="Low" _high-label="High"
+ low="0" high="100000" default="20000"
+ convert="invert"/>
+
+ <number id="speed" type="slider" arg="--speed %"
+ _label="Speed" _low-label="Slow" _high-label="Fast"
+ low="0.01" high="10.0" default="1.0"
+ convert="ratio"/>
+
+ <number id="density" type="slider" arg="--density %"
+ _label="Flame Density" _low-label="Low" _high-label="High"
+ low="0.25" high="4.0" default="1.0"
+ convert="ratio"/>
+
+ <hgroup>
+ <boolean id="wander" _label="Wander" arg-unset="--no-wander"/>
+ <boolean id="spin" _label="Spin" arg-unset="--no-spin"/>
+ <boolean id="wire" _label="Wireframe" arg-set="--wireframe"/>
+ </hgroup>
+
+ <boolean id="showfps" _label="Show frame rate" arg-set="--fps"/>
+
+ <xscreensaver-updater />
+
+ <_description>
+It's a dumpster. It's on fire. It's a metaphor for This Modern World.
+
+Written by Jamie Zawinski; 2025.
+ </_description>
+</screensaver>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<screensaver name="hopffibration" _label="Hopf Fibration" gl="yes">
+
+ <video href="https://www.youtube.com/watch?v=llVdG2yjqnY"/>
+
+ <command arg="--root"/>
+
+ <hgroup>
+
+ <vgroup>
+ <boolean id="shadows" _label="Display shadows" arg-unset="--no-shadows"/>
+
+ <boolean id="basespace" _label="Display the base space" arg-unset="--no-base-space"/>
+
+ <boolean id="antialiasing" _label="Use anti-aliasing" arg-unset="--no-anti-aliasing"/>
+ </vgroup>
+
+ <vgroup>
+ <hgroup>
+ <select id="details">
+ <option id="coarse" _label="Coarse" arg-set="--details coarse"/>
+ <option id="medium" _label="Medium"/>
+ <option id="fine" _label="Fine" arg-set="--details fine"/>
+ </select>
+
+ <select id="projection">
+ <option id="perspective" _label="Perspective"/>
+ <option id="orthographic" _label="Orthographic"
+ arg-set="--orthographic"/>
+ </select>
+ </hgroup>
+
+ <hgroup>
+ <boolean id="showfps" _label="Show frame rate" arg-set="--fps"/>
+
+ <number id="delay" type="slider" arg="--delay %"
+ _label="Frame rate" _low-label="Low" _high-label="High"
+ low="0" high="100000" default="20000"
+ convert="invert"/>
+ </hgroup>
+ <xscreensaver-updater />
+
+ </vgroup>
+ </hgroup>
+
+ <_description>
+The Hopf fibration of the 4d hypersphere S³.
+
+The Hopf fibration is based on the Hopf map, a many-to-one continuous
+function from S³ onto the the ordinary 3d sphere S² such that each
+distinct point of S² is mapped from a distinct great circle of S³.
+Hence, the inverse image of a point on S² corresponds to a great
+circle S¹ on S³. The sphere S² is called the base space, each S¹
+corresponding to a point on S² is called a fiber, and S³ is called the
+total space. The program displays various configurations of points on
+the base space and their fibers in the total space. The corresponding
+points and fibers are displayed in the same color.
+
+Any two fibers form a Hopf link. Each circle on S² creates a set of
+fibers that forms a Clifford torus on S³ (i.e., in 4d). Clifford tori
+are flat (in the same sense that the surface of a cylinder is
+flat). More generally, any closed curve on S² creates a torus-like
+surface on S³ that is flat. These surfaces are called Hopf tori or
+Bianchi-Pinkall flat tori. Look for the wave-like curve on S² in the
+animations to see a Hopf torus. A circular arc on S² creates a Hopf
+band on S³. The Hopf band is a Seifert surface of the Hopf link that
+forms the boundaries of the Hopf band.
+
+Inspired by Niles Johnson's visualization of the Hopf fibration
+(https://nilesjohnson.net/hopf.html).
+
+https://en.wikipedia.org/wiki/Hopf_fibration
+https://en.wikipedia.org/wiki/Hopf_link
+https://en.wikipedia.org/wiki/Clifford_torus
+https://en.wikipedia.org/wiki/Seifert_surface
+https://en.wikipedia.org/wiki/Dupin_cyclide
+https://en.wikipedia.org/wiki/3-sphere
+
+Written by Carsten Steger; 2025.
+ </_description>
+</screensaver>
--- /dev/null
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="klondike" _label="Klondike" gl="yes">
+
+ <command arg="--root"/>
+
+ <video href="https://www.youtube.com/watch?v=9F5YBnepT4Y"/>
+
+ <hgroup>
+ <vgroup>
+
+ <number id="delay" type="slider" arg="--delay %"
+ _label="Frame rate" _low-label="Low" _high-label="High"
+ low="0" high="100000" default="30000"
+ convert="invert"/>
+
+ <number id="animation_speed" type="slider" arg="--speed %"
+ _label="Animation Speed" _low-label="Fast" _high-label="Slow"
+ low="15" high="200" default="60"/>
+
+ <number id="camera_speed" type="slider" arg="--camera_speed %"
+ _label="Camera Speed" _low-label="Slow" _high-label="Fast"
+ low="10" high="100" default="50"/>
+
+ </vgroup>
+ <vgroup>
+
+ <select id="draw_count">
+ <option id="3" _label="Deal 3 cards to waste pile"/>
+ <option id="1" _label="Deal 1 card to waste pile" arg-set="--draw 1"/>
+ </select>
+
+ <hgroup>
+ <boolean id="sloppy" _label="Sloppy card placement" arg-unset="--no-sloppy"/>
+ </hgroup>
+
+ <hgroup>
+ <boolean id="showfps" _label="Show frame rate" arg-set="--fps"/>
+ </hgroup>
+
+ <xscreensaver-updater />
+ </vgroup>
+ </hgroup>
+
+ <_description>
+The screen saver plays solitaire.
+
+Written by Joshua Timmons; 2024.
+ </_description>
+</screensaver>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<screensaver name="platonicfolding" _label="Platonic Folding" gl="yes">
+
+ <video href="https://www.youtube.com/watch?v=c5gUsXvRkIs"/>
+
+ <command arg="--root"/>
+
+ <hgroup>
+
+ <boolean id="rotate" _label="Rotate" arg-unset="--no-rotate"/>
+
+ <select id="colors">
+ <option id="random" _label="Random coloration"/>
+ <option id="face" _label="Face colors" arg-set="--colors face"/>
+ <option id="earth" _label="Earth colors" arg-set="--colors earth"/>
+ </select>
+
+ <select id="foldings">
+ <option id="random" _label="Random number of foldings"/>
+ <option id="1" _label="1 folding" arg-set="--foldings 1"/>
+ <option id="2" _label="2 foldings" arg-set="--foldings 2"/>
+ <option id="3" _label="3 foldings" arg-set="--foldings 3"/>
+ <option id="4" _label="4 foldings" arg-set="--foldings 4"/>
+ <option id="5" _label="5 foldings" arg-set="--foldings 5"/>
+ <option id="6" _label="6 foldings" arg-set="--foldings 6"/>
+ <option id="7" _label="7 foldings" arg-set="--foldings 7"/>
+ <option id="8" _label="8 foldings" arg-set="--foldings 8"/>
+ <option id="9" _label="9 foldings" arg-set="--foldings 9"/>
+ <option id="10" _label="10 foldings" arg-set="--foldings 10"/>
+ <option id="11" _label="11 foldings" arg-set="--foldings 11"/>
+ <option id="12" _label="12 foldings" arg-set="--foldings 12"/>
+ <option id="13" _label="13 foldings" arg-set="--foldings 13"/>
+ <option id="14" _label="14 foldings" arg-set="--foldings 14"/>
+ <option id="15" _label="15 foldings" arg-set="--foldings 15"/>
+ <option id="16" _label="16 foldings" arg-set="--foldings 16"/>
+ <option id="17" _label="17 foldings" arg-set="--foldings 17"/>
+ <option id="18" _label="18 foldings" arg-set="--foldings 18"/>
+ <option id="19" _label="19 foldings" arg-set="--foldings 19"/>
+ <option id="20" _label="20 foldings" arg-set="--foldings 20"/>
+ </select>
+ </hgroup>
+
+ <hgroup>
+ <boolean id="showfps" _label="Show frame rate" arg-set="--fps"/>
+ <number id="delay" type="slider" arg="--delay %"
+ _label="Frame rate" _low-label="Low" _high-label="High"
+ low="0" high="100000" default="25000"
+ convert="invert"/>
+ </hgroup>
+ <xscreensaver-updater />
+
+ <_description>
+The unfolding and folding of the Platonic solids.
+
+For the five Platonic solids (the tetrahedron, cube, octahedron,
+dodecahedron, and icosahedron), all unfoldings of its faces are
+non-overlapping: they form a net. The tetrahedron has 16 unfoldings,
+of which two are essentially different (non-isomorphic), the cube and
+octahedron each have 384 unfoldings, of which eleven are
+non-isomorphic, and the dodecahedron and icosahedron each have
+5,184,000 unfoldings, of which 43,380 are non-isomorphic. This program
+displays randomly selected unfoldings for the five Platonic solids.
+The faces of the Platonic solids are either folded jointly or
+successively. Note that while it is guaranteed that the nets of the
+Platonic solids are non-overlapping, their faces may occasionally
+intersect during the unfolding and folding.
+
+https://en.wikipedia.org/wiki/Platonic_solid
+https://en.wikipedia.org/wiki/Net_(polyhedron)
+
+Written by Carsten Steger; 2025.
+ </_description>
+</screensaver>
<option id="ssh" _label="Ping known SSH hosts"
arg-set="--ping /etc/hosts,$HOME/.ssh/known_hosts,$HOME/.ssh/known_hosts2"/>
- <option id="popular" _label="Ping Google, Facebook, etc."
- arg-set="--ping google.com,facebook.com,twitter.com,yahoo.com,flickr.com,www.apple.com,wikipedia.org,linux.org,youtube.com,disqus.com,blogger.com,wordpress.com,tumblr.com,whitehouse.gov"/>
+ <!-- Most popular sites, as per Wikipedia 2025. -->
+ <option id="popular" _label="Ping popular web sites"
+ arg-set="--ping google.com,youtube.com,facebook.com,instagram.com,wikipedia.org,reddit.com,amazon.com,yandex.ru,baidu.com,tiktok.com,pornhub.com,apple.com,kernel.org,mastodon.social,nsa.gov"/>
<option id="sim" _label="Simulation (don't ping)"
arg-set="--ping simulation"/>
-/* ffmpeg-out, Copyright © 2023-2024 Jamie Zawinski <jwz@jwz.org>
+/* ffmpeg-out, Copyright © 2023-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
# pragma GCC diagnostic ignored "-Wpragmas"
# pragma GCC diagnostic ignored "-Wc99-extensions"
# pragma GCC diagnostic ignored "-Wlong-long"
- /* No "diagnostic pop" because some macrose use c99 features. */
+ /* No "diagnostic pop" because some macros use c99 features. */
#endif
#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
# define HAVE_CH_LAYOUT
#endif
+#if (LIBAVCODEC_VERSION_INT >= ((61<<16) | (12<<8) | 100))
+# define HAVE_AVCODEC_GET_SUPPORTED_CONFIG
+#endif
struct av_stream {
- AVCodec *codec;
+ const AVCodec *codec;
AVStream *st;
AVCodecContext *ctx;
AVFrame *frame;
#ifdef HAVE_CH_LAYOUT
-AVChannelLayout
+static AVChannelLayout
guess_channel_layout (int channels)
{
return (channels <= 1 ? (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO : (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO);
#endif /* !HAVE_CH_LAYOUT */
+#ifdef HAVE_AVCODEC_GET_SUPPORTED_CONFIG
+
+static const enum AVSampleFormat *
+get_sample_formats (struct av_stream *st)
+{
+ const enum AVSampleFormat *fmts;
+ av_check (avcodec_get_supported_config (st->ctx,
+ st->codec,
+ AV_CODEC_CONFIG_SAMPLE_FORMAT, 0,
+ (const void **) &fmts,
+ NULL));
+ return fmts;
+}
+
+static const int *
+get_sample_rates (struct av_stream *st)
+{
+ const int *rates;
+ av_check (avcodec_get_supported_config (st->ctx,
+ st->codec,
+ AV_CODEC_CONFIG_SAMPLE_RATE, 0,
+ (const void **) &rates,
+ NULL));
+ return rates;
+}
+
+/*
+static const AVChannelLayout *
+get_channel_layouts (struct av_stream *st)
+{
+ const AVChannelLayout *layouts;
+ av_check (avcodec_get_supported_config (st->ctx,
+ st->codec,
+ AV_CODEC_CONFIG_CHANNEL_LAYOUT, 0,
+ (const void **) &layouts,
+ NULL));
+ return layouts;
+}
+*/
+
+#else /* !HAVE_AVCODEC_GET_SUPPORTED_CONFIG */
+
+static const enum AVSampleFormat *
+get_sample_formats (struct av_stream *st)
+{
+ return st->codec->sample_fmts;
+}
+
+static const int *
+get_sample_rates (struct av_stream *st)
+{
+ return st->codec->supported_samplerates;
+}
+
+# ifdef HAVE_CH_LAYOUT
+
+/*
+static const AVChannelLayout *
+get_channel_layouts (struct av_stream *st)
+{
+ return st->codec->ch_layouts;
+}
+*/
+
+# else /* !HAVE_CH_LAYOUT */
+
+/*
+static const int *
+get_channel_layouts (struct av_stream *st)
+{
+ return st->codec->channel_layouts;
+}
+*/
+
+# endif /* !HAVE_CH_LAYOUT */
+
+#endif /* !HAVE_AVCODEC_GET_SUPPORTED_CONFIG */
+
+
static void
get_audio_frame (AVFormatContext *audio_fmt_ctx,
struct av_stream *audio_ist, AVPacket *audio_pkt,
const enum AVCodecID video_codec = AV_CODEC_ID_H264;
const enum AVCodecID audio_codec = AV_CODEC_ID_AAC;
+ const enum AVSampleFormat *sample_fmts = NULL;
+ const int *sample_rates = NULL;
+# ifdef HAVE_CH_LAYOUT
+ const AVChannelLayout *channel_layouts = NULL;
+# else
+ const int *channel_layouts = NULL;
+# endif
const enum AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P;
const unsigned framerate = 30;
int audio_bitrate = 96000;
NULL));
add_stream (&ffst->audio_ost, ffst->oc, audio_codec);
+
+ sample_fmts = get_sample_formats (&ffst->audio_ost);
ffst->audio_ost.ctx->sample_fmt =
- (ffst->audio_ost.codec->sample_fmts
- ? ffst->audio_ost.codec->sample_fmts[0]
+ (sample_fmts && sample_fmts[0] != AV_SAMPLE_FMT_NONE
+ ? sample_fmts[0]
: AV_SAMPLE_FMT_FLTP);
ffst->audio_ost.ctx->bit_rate = audio_bitrate;
- if (! ffst->audio_ost.codec->supported_samplerates)
+ sample_rates = get_sample_rates (&ffst->audio_ost);
+ if (! sample_rates || sample_rates[0] == 0)
{
ffst->audio_ost.ctx->sample_rate = ffst->audio_ist.ctx->sample_rate;
}
else
{
- const int *r = ffst->audio_ost.codec->supported_samplerates;
+ const int *r = sample_rates;
int best = *r;
++r;
}
# ifdef HAVE_CH_LAYOUT
- if (! ffst->audio_ost.codec->ch_layouts)
+ if (! channel_layouts || ! av_channel_layout_check(&channel_layouts[0]))
{
if (! av_channel_layout_check(&ffst->audio_ist.ctx->ch_layout))
{
else
{
/* XXX: This may or may not work. With AAC, it doesn't matter. */
- const AVChannelLayout *c = ffst->audio_ost.codec->ch_layouts;
+ const AVChannelLayout *c = channel_layouts;
uint64_t best_lost =
av_popcount64 (ffst->audio_ost.ctx->ch_layout.u.mask);
uint64_t best_added = 0;
av_channel_layout_from_mask (&ffst->audio_ost.ctx->ch_layout, best);
}
# else /* !HAVE_CH_LAYOUT */
- if (! ffst->audio_ost.codec->channel_layouts)
+ if (! channel_layouts || ! channel_layouts[0])
{
if (! ffst->audio_ist.ctx->channel_layout)
{
else
{
/* XXX: This may or may not work. With AAC, it doesn't matter. */
- const uint64_t *c = ffst->audio_ost.codec->channel_layouts;
+ const uint64_t *c = channel_layouts;
unsigned int best_lost =
av_popcount64 (ffst->audio_ost.ctx->channel_layout);
unsigned int best_added = 0;
open_stream (&ffst->audio_ost, NULL);
ffst->audio_ost.frame->format = ffst->audio_ost.ctx->sample_fmt;
-#ifdef HAVE_CH_LAYOUT
+# ifdef HAVE_CH_LAYOUT
av_channel_layout_copy(&ffst->audio_ost.frame->ch_layout,
&ffst->audio_ost.ctx->ch_layout);
-#else /* !HAVE_CH_LAYOUT */
+# else /* !HAVE_CH_LAYOUT */
ffst->audio_ost.frame->channel_layout =
ffst->audio_ost.ctx->channel_layout;
-#endif /* !HAVE_CH_LAYOUT */
+# endif /* !HAVE_CH_LAYOUT */
ffst->audio_ost.frame->sample_rate = ffst->audio_ost.ctx->sample_rate;
ffst->audio_ost.frame->nb_samples =
(ffst->audio_ost.ctx->codec->capabilities &
-# hacks/glx/Makefile.in --- xscreensaver, Copyright © 1999-2024 Jamie Zawinski.
+# hacks/glx/Makefile.in --- xscreensaver, Copyright © 1999-2025 Jamie Zawinski.
# the `../../configure' script generates `hacks/glx/Makefile' from this file.
@SET_MAKE@
mapscroller.c squirtorus.c nakagin.c chompytower.c \
teeth_model.c hextrail.c papercube.c cubocteversion.c \
skulloop.c kallisti.c kallisti_model.c highvoltage.c \
- highvoltage_model.c
+ highvoltage_model.c hopffibration.c hopfanimations.c \
+ platonicfolding.c klondike.c klondike-game.c dumpsterfire.c \
+ dumpster_model.c
SCRIPTS = mapscroller.pl
mapscroller.o squirtorus.o nakagin.o chompytower.o \
teeth_model.o hextrail.o papercube.o cubocteversion.o \
skulloop.o kallisti.o kallisti_model.o highvoltage.o \
- highvoltage_model.o
+ highvoltage_model.o platonicfolding.o klondike.o \
+ klondike-game.o dumpsterfire.o
GL_EXES = cage gears moebius pipes sproingies stairs superquadrics \
morph3d rubik atlantis lament bubble3d glplanet pulsar \
maze3d handsy gravitywell deepstars gibson etruscanvenus \
sphereeversion covid19 headroom beats mapscroller \
squirtorus nakagin chompytower hextrail papercube \
- cubocteversion skulloop kallisti highvoltage
+ cubocteversion skulloop kallisti highvoltage hopffibration \
+ platonicfolding klondike dumpsterfire
GLE_EXES = extrusion
SUID_EXES = sonar
SETCAP_EXES = sonar
glschool_alg.h topblock.h involute.h teapot.h sonar.h \
dropshadow.h starwars.h teapot2.h dnapizza.h curlicue.h \
quickhull.h dymaxionmap-coords.h handsy_anim.h \
- glsl-utils.h mapcities.h sphereeversion.h
+ glsl-utils.h mapcities.h sphereeversion.h hopfanimations.h \
+ klondike-game.h
GL_MEN = xscreensaver-gl-visual.man \
atlantis.man boxed.man bubble3d.man cage.man circuit.man \
cubenetic.man dangerball.man engine.man extrusion.man \
sphereeversion.man covid19.man headroom.man beats.man \
mapscroller.man squirtorus.man nakagin.man chompytower.man \
hextrail.man papercube.man cubocteversion.man skulloop.man \
- kallisti.man highvoltage.man
+ kallisti.man highvoltage.man hopffibration.man \
+ platonicfolding.man
MEN = @GL_MEN@
RETIRED_MEN = glforestfire.man
EXTRAS = README Makefile.in dxf2gl.pl vrml2gl.pl wfront2gl.pl \
fi
validate_xml:
- @cd $(HACK_SRC) && ./check-configs.pl $(EXES) $(RETIRED_EXES)
+ @cd $(HACK_SRC) && ./check-configs.pl --force $(EXES) $(RETIRED_EXES)
distdepend:: check_men validate_xml
$(CC) -c $(HACK_CFLAGS_BASE) $(THREAD_CFLAGS) $<
sonar: sonar.o $(SONAR_OBJS)
$(CC_HACK) -o $@ $@.o $(THREAD_CFLAGS) $(SONAR_OBJS) $(LIBCAP_LIBS) $(THREAD_LIBS) $(HACK_LIBS)
+ @if [ "$$USER" = jwz ]; then \
+ set -x ; sudo chown root $@ ; sudo chmod u+s $@ ; \
+ fi
JIGSAW_OBJS=normals.o $(UTILS_BIN)/spline.o $(HACK_TRACK_GRAB_OBJS)
jigsaw: jigsaw.o $(JIGSAW_OBJS)
highvoltage: $(HIGHVOLTAGE)
$(CC_HACK) -o $@ $(HIGHVOLTAGE) $(HACK_LIBS)
+HOPFFIBRATION_OBJS=hopfanimations.o $(HACK_TRACK_OBJS)
+hopffibration: hopffibration.o $(HOPFFIBRATION_OBJS)
+ $(CC_HACK) -o $@ $@.o $(HOPFFIBRATION_OBJS) $(HACK_LIBS)
+
+PLATONIC_OBJS=$(PNG) $(HACK_TRACK_OBJS)
+platonicfolding: platonicfolding.o $(PLATONIC_OBJS)
+ $(CC_HACK) -o $@ $@.o $(PLATONIC_OBJS) $(HACK_LIBS) $(PNG_LIBS)
+
+KLONDIKE_OBJS=$(PNG) $(HACK_TRACK_OBJS) klondike-game.o
+klondike: klondike.o $(KLONDIKE_OBJS)
+ $(CC_HACK) -o $@ $@.o $(KLONDIKE_OBJS) $(HACK_LIBS) $(PNG_LIBS)
+
+dumpster_dxf::
+ ./dxf2gl.pl --smooth --layers --normalize dumpster.dxf dumpster_model.c
+DUMPSTERFIRE=dumpsterfire.o dumpster_model.o gllist.o $(HACK_TRACK_OBJS)
+dumpsterfire: $(DUMPSTERFIRE)
+ $(CC_HACK) -o $@ $(DUMPSTERFIRE) $(HACK_LIBS)
+
##############################################################################
#
dropshadow.o: $(UTILS_SRC)/visual.h
dropshadow.o: $(UTILS_SRC)/xft.h
dropshadow.o: $(UTILS_SRC)/yarandom.h
+dumpsterfire.o: ../../config.h
+dumpsterfire.o: $(HACK_SRC)/fps.h
+dumpsterfire.o: $(srcdir)/gllist.h
+dumpsterfire.o: $(srcdir)/gltrackball.h
+dumpsterfire.o: $(HACK_SRC)/recanim.h
+dumpsterfire.o: $(srcdir)/rotator.h
+dumpsterfire.o: $(HACK_SRC)/screenhackI.h
+dumpsterfire.o: $(UTILS_SRC)/colors.h
+dumpsterfire.o: $(UTILS_SRC)/erase.h
+dumpsterfire.o: $(UTILS_SRC)/font-retry.h
+dumpsterfire.o: $(UTILS_SRC)/grabclient.h
+dumpsterfire.o: $(UTILS_SRC)/hsv.h
+dumpsterfire.o: $(UTILS_SRC)/resources.h
+dumpsterfire.o: $(UTILS_SRC)/usleep.h
+dumpsterfire.o: $(UTILS_SRC)/visual.h
+dumpsterfire.o: $(UTILS_SRC)/xft.h
+dumpsterfire.o: $(UTILS_SRC)/yarandom.h
+dumpsterfire.o: $(HACK_SRC)/xlockmoreI.h
+dumpsterfire.o: $(HACK_SRC)/xlockmore.h
dymaxionmap-coords.o: ../../config.h
dymaxionmap-coords.o: $(srcdir)/dymaxionmap-coords.h
dymaxionmap.o: ../../config.h
hilbert.o: $(UTILS_SRC)/yarandom.h
hilbert.o: $(HACK_SRC)/xlockmoreI.h
hilbert.o: $(HACK_SRC)/xlockmore.h
+hopfanimations.o: $(srcdir)/hopfanimations.h
+hopffibration.o: ../../config.h
+hopffibration.o: $(HACK_SRC)/fps.h
+hopffibration.o: $(srcdir)/glsl-utils.h
+hopffibration.o: $(srcdir)/gltrackball.h
+hopffibration.o: $(srcdir)/hopfanimations.h
+hopffibration.o: $(HACK_SRC)/recanim.h
+hopffibration.o: $(HACK_SRC)/screenhackI.h
+hopffibration.o: $(UTILS_SRC)/colors.h
+hopffibration.o: $(UTILS_SRC)/erase.h
+hopffibration.o: $(UTILS_SRC)/font-retry.h
+hopffibration.o: $(UTILS_SRC)/grabclient.h
+hopffibration.o: $(UTILS_SRC)/hsv.h
+hopffibration.o: $(UTILS_SRC)/resources.h
+hopffibration.o: $(UTILS_SRC)/usleep.h
+hopffibration.o: $(UTILS_SRC)/visual.h
+hopffibration.o: $(UTILS_SRC)/xft.h
+hopffibration.o: $(UTILS_SRC)/yarandom.h
+hopffibration.o: $(HACK_SRC)/xlockmoreI.h
+hopffibration.o: $(HACK_SRC)/xlockmore.h
hydrostat.o: ../../config.h
hydrostat.o: $(HACK_SRC)/fps.h
hydrostat.o: $(srcdir)/gltrackball.h
klein.o: $(UTILS_SRC)/yarandom.h
klein.o: $(HACK_SRC)/xlockmoreI.h
klein.o: $(HACK_SRC)/xlockmore.h
+klondike-game.o: ../../config.h
+klondike-game.o: $(HACK_SRC)/fps.h
+klondike-game.o: $(srcdir)/gltrackball.h
+klondike-game.o: $(srcdir)/klondike-game.h
+klondike-game.o: $(HACK_SRC)/recanim.h
+klondike-game.o: $(HACK_SRC)/screenhackI.h
+klondike-game.o: $(UTILS_SRC)/colors.h
+klondike-game.o: $(UTILS_SRC)/erase.h
+klondike-game.o: $(UTILS_SRC)/font-retry.h
+klondike-game.o: $(UTILS_SRC)/grabclient.h
+klondike-game.o: $(UTILS_SRC)/hsv.h
+klondike-game.o: $(UTILS_SRC)/resources.h
+klondike-game.o: $(UTILS_SRC)/usleep.h
+klondike-game.o: $(UTILS_SRC)/visual.h
+klondike-game.o: $(UTILS_SRC)/xft.h
+klondike-game.o: $(UTILS_SRC)/yarandom.h
+klondike-game.o: $(HACK_SRC)/xlockmoreI.h
+klondike-game.o: $(HACK_SRC)/xlockmore.h
+klondike.o: ../../config.h
+klondike.o: $(HACK_SRC)/fps.h
+klondike.o: $(srcdir)/gltrackball.h
+klondike.o: ../images/gen/C2_png.h
+klondike.o: ../images/gen/C3_png.h
+klondike.o: ../images/gen/C4_png.h
+klondike.o: ../images/gen/C5_png.h
+klondike.o: ../images/gen/C6_png.h
+klondike.o: ../images/gen/C7_png.h
+klondike.o: ../images/gen/C8_png.h
+klondike.o: ../images/gen/C9_png.h
+klondike.o: ../images/gen/CA_png.h
+klondike.o: ../images/gen/CJ_png.h
+klondike.o: ../images/gen/CK_png.h
+klondike.o: ../images/gen/CQ_png.h
+klondike.o: ../images/gen/CT_png.h
+klondike.o: ../images/gen/D2_png.h
+klondike.o: ../images/gen/D3_png.h
+klondike.o: ../images/gen/D4_png.h
+klondike.o: ../images/gen/D5_png.h
+klondike.o: ../images/gen/D6_png.h
+klondike.o: ../images/gen/D7_png.h
+klondike.o: ../images/gen/D8_png.h
+klondike.o: ../images/gen/D9_png.h
+klondike.o: ../images/gen/DA_png.h
+klondike.o: ../images/gen/DJ_png.h
+klondike.o: ../images/gen/DK_png.h
+klondike.o: ../images/gen/DQ_png.h
+klondike.o: ../images/gen/DT_png.h
+klondike.o: ../images/gen/H2_png.h
+klondike.o: ../images/gen/H3_png.h
+klondike.o: ../images/gen/H4_png.h
+klondike.o: ../images/gen/H5_png.h
+klondike.o: ../images/gen/H6_png.h
+klondike.o: ../images/gen/H7_png.h
+klondike.o: ../images/gen/H8_png.h
+klondike.o: ../images/gen/H9_png.h
+klondike.o: ../images/gen/HA_png.h
+klondike.o: ../images/gen/HJ_png.h
+klondike.o: ../images/gen/HK_png.h
+klondike.o: ../images/gen/HQ_png.h
+klondike.o: ../images/gen/HT_png.h
+klondike.o: ../images/gen/S2_png.h
+klondike.o: ../images/gen/S3_png.h
+klondike.o: ../images/gen/S4_png.h
+klondike.o: ../images/gen/S5_png.h
+klondike.o: ../images/gen/S6_png.h
+klondike.o: ../images/gen/S7_png.h
+klondike.o: ../images/gen/S8_png.h
+klondike.o: ../images/gen/S9_png.h
+klondike.o: ../images/gen/SA_png.h
+klondike.o: ../images/gen/SJ_png.h
+klondike.o: ../images/gen/SK_png.h
+klondike.o: ../images/gen/SQ_png.h
+klondike.o: ../images/gen/ST_png.h
+klondike.o: ../images/gen/back_png.h
+klondike.o: $(srcdir)/klondike-game.h
+klondike.o: $(HACK_SRC)/recanim.h
+klondike.o: $(HACK_SRC)/screenhackI.h
+klondike.o: $(UTILS_SRC)/colors.h
+klondike.o: $(UTILS_SRC)/erase.h
+klondike.o: $(UTILS_SRC)/font-retry.h
+klondike.o: $(UTILS_SRC)/grabclient.h
+klondike.o: $(UTILS_SRC)/hsv.h
+klondike.o: $(UTILS_SRC)/resources.h
+klondike.o: $(UTILS_SRC)/usleep.h
+klondike.o: $(UTILS_SRC)/visual.h
+klondike.o: $(UTILS_SRC)/xft.h
+klondike.o: $(UTILS_SRC)/yarandom.h
+klondike.o: $(HACK_SRC)/ximage-loader.h
+klondike.o: $(HACK_SRC)/xlockmoreI.h
+klondike.o: $(HACK_SRC)/xlockmore.h
lament_model.o: ../../config.h
lament_model.o: $(HACK_SRC)/fps.h
lament_model.o: $(srcdir)/gllist.h
pipes.o: $(UTILS_SRC)/yarandom.h
pipes.o: $(HACK_SRC)/xlockmoreI.h
pipes.o: $(HACK_SRC)/xlockmore.h
+platonicfolding.o: ../../config.h
+platonicfolding.o: $(HACK_SRC)/fps.h
+platonicfolding.o: $(srcdir)/glsl-utils.h
+platonicfolding.o: $(srcdir)/gltrackball.h
+platonicfolding.o: ../images/gen/earth_night_png.h
+platonicfolding.o: ../images/gen/earth_png.h
+platonicfolding.o: ../images/gen/earth_water_png.h
+platonicfolding.o: $(HACK_SRC)/recanim.h
+platonicfolding.o: $(HACK_SRC)/screenhackI.h
+platonicfolding.o: $(UTILS_SRC)/colors.h
+platonicfolding.o: $(UTILS_SRC)/erase.h
+platonicfolding.o: $(UTILS_SRC)/font-retry.h
+platonicfolding.o: $(UTILS_SRC)/grabclient.h
+platonicfolding.o: $(UTILS_SRC)/hsv.h
+platonicfolding.o: $(UTILS_SRC)/resources.h
+platonicfolding.o: $(UTILS_SRC)/usleep.h
+platonicfolding.o: $(UTILS_SRC)/visual.h
+platonicfolding.o: $(UTILS_SRC)/xft.h
+platonicfolding.o: $(UTILS_SRC)/yarandom.h
+platonicfolding.o: $(HACK_SRC)/ximage-loader.h
+platonicfolding.o: $(HACK_SRC)/xlockmoreI.h
+platonicfolding.o: $(HACK_SRC)/xlockmore.h
polyhedra-gl.o: ../../config.h
polyhedra-gl.o: $(HACK_SRC)/fps.h
polyhedra-gl.o: $(srcdir)/gltrackball.h
static const char sccsid[] = "@(#)cubocteversion.c 1.1 23/03/02 xlockmore";
#endif
-/* Copyright (c) 2023-2023 Carsten Steger <carsten@mirsanmir.org>. */
+/* Copyright (c) 2023-2025 Carsten Steger <carsten@mirsanmir.org>. */
/*
* Permission to use, copy, modify, and distribute this software and its
GLuint self_tri_num[NUMU*3*NUM_FACE+3*NUM_FACE];
/* The number of triangle strips in the self-intersection tubes */
GLuint self_tri_strips;
+ Bool use_shaders;
#ifdef HAVE_GLSL
- Bool use_shaders, use_mipmaps;
+ Bool use_mipmaps;
/* The precomputed texture coordinates of the cuboctahedron */
GLfloat tex[3*3*NUM_FACE];
/* The textures of earth by day (index 0), earth by night (index 1), and
static const GLchar *shader_version_3_0_es =
"#version 300 es\n"
"precision highp float;\n"
- "precision highp int;\n";
+ "precision highp int;\n"
+ "precision highp sampler2D;\n";
/* The vertex shader code is composed of code fragments that depend on
#ifdef HAVE_GLSL
-/* Draw the sphere eversion using OpenGL's programmable functionality. */
+/* Draw the cuboctahedron eversion using OpenGL's programmable
+ functionality. */
static int cuboctahedron_eversion_pf(ModeInfo *mi)
{
cubocteversionstruct *ce = &cubocteversion[MI_SCREEN(mi)];
1.0f, 1.0f,
};
- /* Determine whether to use shaders to render the sphere eversion. */
+ /* Determine whether to use shaders to render the cuboctahedron eversion. */
ce->use_shaders = False;
ce->use_mipmaps = False;
ce->poly_shader_program = 0;
*/
-ENTRYPOINT void reshape_cubocteversion(ModeInfo *mi,
- int width, int height)
+ENTRYPOINT void reshape_cubocteversion(ModeInfo *mi, int width, int height)
{
cubocteversionstruct *ce = &cubocteversion[MI_SCREEN(mi)];
int y = 0;
ce->WindW = (GLint)width;
ce->WindH = (GLint)height;
- ce->tex_scale[0] = 1.0f/(GLfloat)width;
- ce->tex_scale[1] = 1.0f/(GLfloat)height;
glViewport(0,y,width,height);
ce->aspect = (GLfloat)width/(GLfloat)height;
#ifdef HAVE_GLSL
+ ce->tex_scale[0] = 1.0f/(GLfloat)width;
+ ce->tex_scale[1] = 1.0f/(GLfloat)height;
delete_dual_peeling_render_targets(mi);
init_dual_peeling_render_targets(mi);
delete_weighted_average_render_targets(mi);
--- /dev/null
+ 0
+SECTION
+ 2
+ENTITIES
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+106.95687902348394
+30
+246.98357215579964
+11
+180.96357630756773
+21
+104.99428059828708
+31
+247.50944881889774
+12
+180.96357630756773
+22
+104.99428059828708
+32
+243.58425196850402
+13
+180.96357630756773
+23
+104.99428059828708
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+101.59496041099145
+30
+245.54685039370088
+11
+-180.96357619368754
+21
+101.06908374789336
+31
+243.58425196850402
+12
+180.96357630756773
+22
+101.06908374789336
+32
+243.58425196850402
+13
+180.96357630756773
+23
+101.06908374789336
+33
+243.58425196850402
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+101.06908374789336
+30
+243.58425196850402
+11
+180.96357630756773
+21
+101.59496041099145
+31
+245.54685039370088
+12
+-180.96357619368754
+22
+101.59496041099145
+32
+245.54685039370088
+13
+-180.96357619368754
+23
+101.59496041099145
+33
+245.54685039370088
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+101.59496041099145
+30
+245.54685039370088
+11
+-180.96357619368754
+21
+103.03168217309022
+31
+246.98357215579964
+12
+-180.96357619368754
+22
+101.59496041099145
+32
+245.54685039370088
+13
+-180.96357619368754
+23
+101.59496041099145
+33
+245.54685039370088
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+103.03168217309022
+30
+246.98357215579964
+11
+180.96357630756773
+21
+101.59496041099145
+31
+245.54685039370088
+12
+180.96357630756773
+22
+103.03168217309022
+32
+246.98357215579964
+13
+180.96357630756773
+23
+103.03168217309022
+33
+246.98357215579964
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+103.03168217309022
+30
+246.98357215579964
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+247.50944881889774
+12
+-180.96357619368754
+22
+103.03168217309022
+32
+246.98357215579964
+13
+-180.96357619368754
+23
+103.03168217309022
+33
+246.98357215579964
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+247.50944881889774
+11
+180.96357630756773
+21
+103.03168217309022
+31
+246.98357215579964
+12
+180.96357630756773
+22
+104.99428059828708
+32
+247.50944881889774
+13
+180.96357630756773
+23
+104.99428059828708
+33
+247.50944881889774
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.3936007855827
+30
+241.62165354330716
+11
+-180.96357619368754
+21
+106.95687902348394
+31
+240.1849317812084
+12
+-180.96357619368754
+22
+108.3936007855827
+32
+241.62165354330716
+13
+-180.96357619368754
+23
+108.3936007855827
+33
+241.62165354330716
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+106.95687902348394
+30
+240.1849317812084
+11
+180.96357630756773
+21
+108.3936007855827
+31
+241.62165354330716
+12
+180.96357630756773
+22
+106.95687902348394
+32
+240.1849317812084
+13
+180.96357630756773
+23
+106.95687902348394
+33
+240.1849317812084
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+247.50944881889774
+11
+-180.96357619368754
+21
+106.95687902348394
+31
+246.98357215579964
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+247.50944881889774
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+247.50944881889774
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+106.95687902348394
+30
+246.98357215579964
+11
+180.96357630756773
+21
+104.99428059828708
+31
+247.50944881889774
+12
+180.96357630756773
+22
+106.95687902348394
+32
+246.98357215579964
+13
+180.96357630756773
+23
+106.95687902348394
+33
+246.98357215579964
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+108.9194774486808
+30
+243.58425196850402
+11
+180.96357630756773
+21
+108.3936007855827
+31
+241.62165354330716
+12
+-180.96357619368754
+22
+108.3936007855827
+32
+241.62165354330716
+13
+-180.96357619368754
+23
+108.3936007855827
+33
+241.62165354330716
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.3936007855827
+30
+241.62165354330716
+11
+-180.96357619368754
+21
+108.9194774486808
+31
+243.58425196850402
+12
+180.96357630756773
+22
+108.9194774486808
+32
+243.58425196850402
+13
+180.96357630756773
+23
+108.9194774486808
+33
+243.58425196850402
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+106.95687902348394
+30
+246.98357215579964
+11
+-180.96357619368754
+21
+108.3936007855827
+31
+245.54685039370088
+12
+-180.96357619368754
+22
+106.95687902348394
+32
+246.98357215579964
+13
+-180.96357619368754
+23
+106.95687902348394
+33
+246.98357215579964
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+108.3936007855827
+30
+245.54685039370088
+11
+180.96357630756773
+21
+106.95687902348394
+31
+246.98357215579964
+12
+180.96357630756773
+22
+108.3936007855827
+32
+245.54685039370088
+13
+180.96357630756773
+23
+108.3936007855827
+33
+245.54685039370088
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+108.3936007855827
+30
+245.54685039370088
+11
+180.96357630756773
+21
+108.9194774486808
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+108.9194774486808
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+108.9194774486808
+33
+243.58425196850402
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.9194774486808
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+108.3936007855827
+31
+245.54685039370088
+12
+180.96357630756773
+22
+108.3936007855827
+32
+245.54685039370088
+13
+180.96357630756773
+23
+108.3936007855827
+33
+245.54685039370088
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+106.95687902348394
+30
+240.1849317812084
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+239.6590551181103
+12
+-180.96357619368754
+22
+106.95687902348394
+32
+240.1849317812084
+13
+-180.96357619368754
+23
+106.95687902348394
+33
+240.1849317812084
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+239.6590551181103
+11
+180.96357630756773
+21
+106.95687902348394
+31
+240.1849317812084
+12
+180.96357630756773
+22
+104.99428059828708
+32
+239.6590551181103
+13
+180.96357630756773
+23
+104.99428059828708
+33
+239.6590551181103
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+239.6590551181103
+11
+-180.96357619368754
+21
+103.03168217309022
+31
+240.1849317812084
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+239.6590551181103
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+239.6590551181103
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+103.03168217309022
+30
+240.1849317812084
+11
+180.96357630756773
+21
+104.99428059828708
+31
+239.6590551181103
+12
+180.96357630756773
+22
+103.03168217309022
+32
+240.1849317812084
+13
+180.96357630756773
+23
+103.03168217309022
+33
+240.1849317812084
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+103.03168217309022
+30
+240.1849317812084
+11
+-180.96357619368754
+21
+101.59496041099145
+31
+241.62165354330716
+12
+-180.96357619368754
+22
+103.03168217309022
+32
+240.1849317812084
+13
+-180.96357619368754
+23
+103.03168217309022
+33
+240.1849317812084
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+101.59496041099145
+30
+241.62165354330716
+11
+180.96357630756773
+21
+103.03168217309022
+31
+240.1849317812084
+12
+180.96357630756773
+22
+101.59496041099145
+32
+241.62165354330716
+13
+180.96357630756773
+23
+101.59496041099145
+33
+241.62165354330716
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+101.06908374789336
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+101.59496041099145
+31
+241.62165354330716
+12
+180.96357630756773
+22
+101.59496041099145
+32
+241.62165354330716
+13
+180.96357630756773
+23
+101.59496041099145
+33
+241.62165354330716
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+101.59496041099145
+30
+241.62165354330716
+11
+180.96357630756773
+21
+101.06908374789336
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+101.06908374789336
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+101.06908374789336
+33
+243.58425196850402
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+103.03168217309022
+31
+246.98357215579964
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+247.50944881889774
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+247.50944881889774
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.9194774486808
+30
+243.58425196850402
+11
+180.96357630756773
+21
+104.99428059828708
+31
+243.58425196850402
+12
+180.96357630756773
+22
+108.3936007855827
+32
+241.62165354330716
+13
+180.96357630756773
+23
+108.3936007855827
+33
+241.62165354330716
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+101.59496041099145
+31
+241.62165354330716
+12
+180.96357630756773
+22
+103.03168217309022
+32
+240.1849317812084
+13
+180.96357630756773
+23
+103.03168217309022
+33
+240.1849317812084
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+247.50944881889774
+11
+180.96357630756773
+21
+103.03168217309022
+31
+246.98357215579964
+12
+180.96357630756773
+22
+104.99428059828708
+32
+243.58425196850402
+13
+180.96357630756773
+23
+104.99428059828708
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.9194774486808
+30
+243.58425196850402
+11
+180.96357630756773
+21
+108.3936007855827
+31
+245.54685039370088
+12
+180.96357630756773
+22
+104.99428059828708
+32
+243.58425196850402
+13
+180.96357630756773
+23
+104.99428059828708
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+103.03168217309022
+31
+240.1849317812084
+12
+180.96357630756773
+22
+104.99428059828708
+32
+239.6590551181103
+13
+180.96357630756773
+23
+104.99428059828708
+33
+239.6590551181103
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.3936007855827
+30
+245.54685039370088
+11
+180.96357630756773
+21
+106.95687902348394
+31
+246.98357215579964
+12
+180.96357630756773
+22
+104.99428059828708
+32
+243.58425196850402
+13
+180.96357630756773
+23
+104.99428059828708
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+101.06908374789336
+31
+243.58425196850402
+12
+180.96357630756773
+22
+101.59496041099145
+32
+241.62165354330716
+13
+180.96357630756773
+23
+101.59496041099145
+33
+241.62165354330716
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+106.95687902348394
+30
+240.1849317812084
+11
+180.96357630756773
+21
+104.99428059828708
+31
+243.58425196850402
+12
+180.96357630756773
+22
+104.99428059828708
+32
+239.6590551181103
+13
+180.96357630756773
+23
+104.99428059828708
+33
+239.6590551181103
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+103.03168217309022
+31
+246.98357215579964
+12
+180.96357630756773
+22
+101.59496041099145
+32
+245.54685039370088
+13
+180.96357630756773
+23
+101.59496041099145
+33
+245.54685039370088
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+101.59496041099145
+31
+245.54685039370088
+12
+180.96357630756773
+22
+101.06908374789336
+32
+243.58425196850402
+13
+180.96357630756773
+23
+101.06908374789336
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.3936007855827
+30
+241.62165354330716
+11
+180.96357630756773
+21
+104.99428059828708
+31
+243.58425196850402
+12
+180.96357630756773
+22
+106.95687902348394
+32
+240.1849317812084
+13
+180.96357630756773
+23
+106.95687902348394
+33
+240.1849317812084
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+108.3936007855827
+30
+241.62165354330716
+11
+-180.96357619368754
+21
+106.95687902348394
+31
+240.1849317812084
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+239.6590551181103
+12
+180.96357630756773
+22
+104.99428059828708
+32
+239.6590551181103
+13
+180.96357630756773
+23
+104.99428059828708
+33
+239.6590551181103
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+239.6590551181103
+11
+180.96357630756773
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+243.58425196850402
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+104.99428059828708
+31
+243.58425196850402
+12
+180.96357630756773
+22
+104.99428059828708
+32
+247.50944881889774
+13
+180.96357630756773
+23
+104.99428059828708
+33
+247.50944881889774
+70
+13
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+104.99428059828708
+31
+247.50944881889774
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+247.50944881889774
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+247.50944881889774
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+101.06908374789336
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+101.59496041099145
+32
+245.54685039370088
+13
+-180.96357619368754
+23
+101.59496041099145
+33
+245.54685039370088
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+106.95687902348394
+30
+240.1849317812084
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+239.6590551181103
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+106.95687902348394
+31
+240.1849317812084
+12
+-180.96357619368754
+22
+106.95687902348394
+32
+240.1849317812084
+13
+-180.96357619368754
+23
+106.95687902348394
+33
+240.1849317812084
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+106.95687902348394
+30
+240.1849317812084
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+243.58425196850402
+12
+180.96357630756773
+22
+104.99428059828708
+32
+243.58425196850402
+13
+180.96357630756773
+23
+104.99428059828708
+33
+243.58425196850402
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+103.03168217309022
+32
+246.98357215579964
+13
+-180.96357619368754
+23
+103.03168217309022
+33
+246.98357215579964
+70
+13
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+103.03168217309022
+31
+246.98357215579964
+12
+180.96357630756773
+22
+103.03168217309022
+32
+246.98357215579964
+13
+180.96357630756773
+23
+103.03168217309022
+33
+246.98357215579964
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+106.95687902348394
+30
+246.98357215579964
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+247.50944881889774
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+247.50944881889774
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+101.59496041099145
+31
+245.54685039370088
+12
+-180.96357619368754
+22
+103.03168217309022
+32
+246.98357215579964
+13
+-180.96357619368754
+23
+103.03168217309022
+33
+246.98357215579964
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.3936007855827
+30
+241.62165354330716
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+108.3936007855827
+32
+241.62165354330716
+13
+-180.96357619368754
+23
+108.3936007855827
+33
+241.62165354330716
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+108.3936007855827
+31
+241.62165354330716
+12
+-180.96357619368754
+22
+101.59496041099145
+32
+245.54685039370088
+13
+-180.96357619368754
+23
+101.59496041099145
+33
+245.54685039370088
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+101.59496041099145
+30
+245.54685039370088
+11
+180.96357630756773
+21
+108.3936007855827
+31
+241.62165354330716
+12
+180.96357630756773
+22
+101.59496041099145
+32
+245.54685039370088
+13
+180.96357630756773
+23
+101.59496041099145
+33
+245.54685039370088
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+101.59496041099145
+30
+245.54685039370088
+11
+180.96357630756773
+21
+108.3936007855827
+31
+241.62165354330716
+12
+180.96357630756773
+22
+104.99428059828708
+32
+243.58425196850402
+13
+180.96357630756773
+23
+104.99428059828708
+33
+243.58425196850402
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+101.59496041099145
+31
+241.62165354330716
+12
+-180.96357619368754
+22
+101.06908374789336
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+101.06908374789336
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+108.9194774486808
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+108.3936007855827
+31
+241.62165354330716
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+101.06908374789336
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+101.06908374789336
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+101.06908374789336
+33
+243.58425196850402
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+101.06908374789336
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+108.9194774486808
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+108.9194774486808
+33
+243.58425196850402
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+108.9194774486808
+30
+243.58425196850402
+11
+180.96357630756773
+21
+101.06908374789336
+31
+243.58425196850402
+12
+180.96357630756773
+22
+108.9194774486808
+32
+243.58425196850402
+13
+180.96357630756773
+23
+108.9194774486808
+33
+243.58425196850402
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.9194774486808
+30
+243.58425196850402
+11
+180.96357630756773
+21
+101.06908374789336
+31
+243.58425196850402
+12
+180.96357630756773
+22
+104.99428059828708
+32
+243.58425196850402
+13
+180.96357630756773
+23
+104.99428059828708
+33
+243.58425196850402
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+108.3936007855827
+30
+245.54685039370088
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+106.95687902348394
+32
+246.98357215579964
+13
+-180.96357619368754
+23
+106.95687902348394
+33
+246.98357215579964
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+239.6590551181103
+11
+-180.96357619368754
+21
+103.03168217309022
+31
+240.1849317812084
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+243.58425196850402
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+103.03168217309022
+31
+240.1849317812084
+12
+180.96357630756773
+22
+103.03168217309022
+32
+240.1849317812084
+13
+180.96357630756773
+23
+103.03168217309022
+33
+240.1849317812084
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+103.03168217309022
+30
+240.1849317812084
+11
+180.96357630756773
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+104.99428059828708
+32
+243.58425196850402
+13
+-180.96357619368754
+23
+104.99428059828708
+33
+243.58425196850402
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+104.99428059828708
+31
+243.58425196850402
+12
+180.96357630756773
+22
+106.95687902348394
+32
+246.98357215579964
+13
+180.96357630756773
+23
+106.95687902348394
+33
+246.98357215579964
+70
+13
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+106.95687902348394
+31
+246.98357215579964
+12
+-180.96357619368754
+22
+106.95687902348394
+32
+246.98357215579964
+13
+-180.96357619368754
+23
+106.95687902348394
+33
+246.98357215579964
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+103.03168217309022
+31
+240.1849317812084
+12
+-180.96357619368754
+22
+101.59496041099145
+32
+241.62165354330716
+13
+-180.96357619368754
+23
+101.59496041099145
+33
+241.62165354330716
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+108.9194774486808
+30
+243.58425196850402
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+108.3936007855827
+32
+245.54685039370088
+13
+-180.96357619368754
+23
+108.3936007855827
+33
+245.54685039370088
+70
+0
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+108.3936007855827
+30
+245.54685039370088
+11
+-180.96357619368754
+21
+104.99428059828708
+31
+243.58425196850402
+12
+-180.96357619368754
+22
+108.3936007855827
+32
+245.54685039370088
+13
+-180.96357619368754
+23
+108.3936007855827
+33
+245.54685039370088
+70
+1
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+104.99428059828708
+30
+243.58425196850402
+11
+180.96357630756773
+21
+108.3936007855827
+31
+245.54685039370088
+12
+-180.96357619368754
+22
+101.59496041099145
+32
+241.62165354330716
+13
+-180.96357619368754
+23
+101.59496041099145
+33
+241.62165354330716
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+-180.96357619368754
+20
+101.59496041099145
+30
+241.62165354330716
+11
+180.96357630756773
+21
+108.3936007855827
+31
+245.54685039370088
+12
+180.96357630756773
+22
+101.59496041099145
+32
+241.62165354330716
+13
+180.96357630756773
+23
+101.59496041099145
+33
+241.62165354330716
+70
+3
+ 0
+3DFACE
+ 8
+axle
+10
+180.96357630756773
+20
+101.59496041099145
+30
+241.62165354330716
+11
+180.96357630756773
+21
+108.3936007855827
+31
+245.54685039370088
+12
+180.96357630756773
+22
+104.99428059828708
+32
+243.58425196850402
+13
+180.96357630756773
+23
+104.99428059828708
+33
+243.58425196850402
+70
+1
+ 0
+LINE
+ 8
+frame_half
+10
+192.98247766291428
+20
+-18.731889129101205
+30
+151.6787401574804
+11
+192.9824776628326
+21
+-18.729150113199566
+31
+151.6790750703387
+ 0
+LINE
+ 8
+frame_half
+10
+174.56515482826853
+20
+-18.73188967799561
+30
+151.6787401574804
+11
+174.56515482818696
+21
+-18.72915066209397
+31
+151.6790750703387
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680488186044
+20
+98.36023622047261
+30
+0.3216681102362128
+11
+5.115907697472721e-13
+21
+-98.36024133728131
+31
+0.32163719118833317
+12
+5.115907697472721e-13
+22
+98.36023110366378
+32
+0.321666641223282
+13
+5.115907697472721e-13
+23
+98.36023110366378
+33
+0.321666641223282
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+-98.36024133728131
+30
+0.32163719118833317
+11
+171.68680488186044
+21
+98.36023622047261
+31
+0.3216681102362128
+12
+171.68681074475182
+22
+-98.36023622047243
+32
+0.32163976377951986
+13
+171.68681074475182
+23
+-98.36023622047243
+33
+0.32163976377951986
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+109.82597913515951
+30
+227.14655023375423
+11
+180.96357619368825
+21
+109.82598452844579
+31
+227.1425196850395
+12
+175.47334585487522
+22
+109.82598436481948
+32
+227.1425196820503
+13
+175.47334585487522
+23
+109.82598436481948
+33
+227.1425196820503
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357619368825
+20
+109.82598452844579
+30
+227.1425196850395
+11
+5.115907697472721e-13
+21
+109.82597913515951
+31
+227.14655023375423
+12
+180.96357619368825
+22
+109.82598452844579
+32
+236.60039370078752
+13
+180.96357619368825
+23
+109.82598452844579
+33
+236.60039370078752
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357619368825
+20
+109.82598452844579
+30
+236.60039370078752
+11
+5.115907697472721e-13
+21
+109.82597913515951
+31
+227.14655023375423
+12
+5.115907697472721e-13
+22
+109.82597913515954
+32
+236.60039370078752
+13
+5.115907697472721e-13
+23
+109.82597913515954
+33
+236.60039370078752
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357619368825
+20
+109.82598452844579
+30
+236.60039370078752
+11
+5.115907697472721e-13
+21
+109.82597913515954
+31
+236.60039370078752
+12
+171.68680454014486
+22
+109.82598425196863
+32
+236.60039370078752
+13
+171.68680454014486
+23
+109.82598425196863
+33
+236.60039370078752
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681074475182
+20
+-98.36023622047243
+30
+0.32163976377951986
+11
+164.33029144123975
+21
+-98.36023643971993
+31
+7.678158957059996
+12
+7.356519303512613
+22
+-98.36024111803383
+32
+7.678156604932067
+13
+7.356519303512613
+23
+-98.36024111803383
+33
+7.678156604932067
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029144123975
+20
+-98.36023643971993
+30
+7.678158957059996
+11
+171.68681074475182
+21
+-98.36023622047243
+31
+0.32163976377951986
+12
+171.68681074475182
+22
+-98.36023622047243
+32
+176.06259842519694
+13
+171.68681074475182
+23
+-98.36023622047243
+33
+176.06259842519694
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029144123975
+20
+-98.36023643971993
+30
+7.678158957059996
+11
+171.68681074475182
+21
+-98.36023622047243
+31
+176.06259842519694
+12
+164.33029144123975
+22
+-98.36023643971994
+32
+168.7061612498017
+13
+164.33029144123975
+23
+-98.36023643971994
+33
+168.7061612498017
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029144123975
+20
+-98.36023643971994
+30
+168.7061612498017
+11
+171.68681074475182
+21
+-98.36023622047243
+31
+176.06259842519694
+12
+7.356519303512613
+22
+-98.36024111803414
+32
+168.7079137133973
+13
+7.356519303512613
+23
+-98.36024111803414
+33
+168.7079137133973
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+-98.36024133728131
+30
+0.32163719118833317
+11
+7.356519303512613
+21
+-98.36024111803383
+31
+7.678156604932067
+12
+5.115907697472721e-13
+22
+-98.36024133728166
+32
+176.06451514594315
+13
+5.115907697472721e-13
+23
+-98.36024133728166
+33
+176.06451514594315
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519303512613
+20
+-98.36024111803383
+30
+7.678156604932067
+11
+5.115907697472721e-13
+21
+-98.36024133728131
+31
+0.32163719118833317
+12
+171.68681074475182
+22
+-98.36023622047243
+32
+0.32163976377951986
+13
+171.68681074475182
+23
+-98.36023622047243
+33
+0.32163976377951986
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+-98.36024133728166
+30
+176.06451514594315
+11
+7.356519303512613
+21
+-98.36024111803383
+31
+7.678156604932067
+12
+7.356519303512613
+22
+-98.36024111803414
+32
+168.7079137133973
+13
+7.356519303512613
+23
+-98.36024111803414
+33
+168.7079137133973
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+-98.36024133728166
+30
+176.06451514594315
+11
+7.356519303512613
+21
+-98.36024111803414
+31
+168.7079137133973
+12
+171.68681074475182
+22
+-98.36023622047243
+32
+176.06259842519694
+13
+171.68681074475182
+23
+-98.36023622047243
+33
+176.06259842519694
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+98.36023110366378
+30
+0.321666641223282
+11
+7.356519303512613
+21
+98.36023132291126
+31
+7.678186007680364
+12
+171.68680488186044
+22
+98.36023622047261
+32
+0.3216681102362128
+13
+171.68680488186044
+23
+98.36023622047261
+33
+0.3216681102362128
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519303512613
+20
+98.36023132291126
+30
+7.678186007680364
+11
+5.115907697472721e-13
+21
+98.36023110366378
+31
+0.321666641223282
+12
+5.115907697472721e-13
+22
+98.36023110366338
+32
+227.1465447984691
+13
+5.115907697472721e-13
+23
+98.36023110366338
+33
+227.1465447984691
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519303512613
+20
+98.36023132291126
+30
+7.678186007680364
+11
+5.115907697472721e-13
+21
+98.36023110366338
+31
+227.1465447984691
+12
+7.356519303512613
+22
+98.36023132291092
+32
+219.78985302292708
+13
+7.356519303512613
+23
+98.36023132291092
+33
+219.78985302292708
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519303512613
+20
+98.36023132291092
+30
+219.78985302292708
+11
+5.115907697472721e-13
+21
+98.36023110366338
+31
+227.1465447984691
+12
+171.68680488186044
+22
+98.36023622047261
+32
+227.1425196850395
+13
+171.68680488186044
+23
+98.36023622047261
+33
+227.1425196850395
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680488186044
+20
+98.36023622047261
+30
+0.3216681102362128
+11
+164.33028557834837
+21
+98.3602360012251
+31
+7.6781873508033405
+12
+171.68680488186044
+22
+98.36023622047261
+32
+227.1425196850395
+13
+171.68680488186044
+23
+98.36023622047261
+33
+227.1425196850395
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33028557834837
+20
+98.3602360012251
+30
+7.6781873508033405
+11
+171.68680488186044
+21
+98.36023622047261
+31
+0.3216681102362128
+12
+7.356519303512613
+22
+98.36023132291126
+32
+7.678186007680364
+13
+7.356519303512613
+23
+98.36023132291126
+33
+7.678186007680364
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680488186044
+20
+98.36023622047261
+30
+227.1425196850395
+11
+164.33028557834837
+21
+98.3602360012251
+31
+7.6781873508033405
+12
+164.33028557834837
+22
+98.3602360012251
+32
+219.78617284951383
+13
+164.33028557834837
+23
+98.3602360012251
+33
+219.78617284951383
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680488186044
+20
+98.36023622047261
+30
+227.1425196850395
+11
+164.33028557834837
+21
+98.3602360012251
+31
+219.78617284951383
+12
+7.356519303512613
+22
+98.36023132291092
+32
+219.78985302292708
+13
+7.356519303512613
+23
+98.36023132291092
+33
+219.78985302292708
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96358267716596
+20
+-107.71732255816846
+30
+189.86102362204736
+11
+175.47286961786034
+21
+-107.717322721809
+31
+176.06259842899448
+12
+180.96358267716596
+22
+-107.71732255816846
+32
+176.06259842519694
+13
+180.96358267716596
+23
+-107.71732255816846
+33
+176.06259842519694
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+175.47286961786034
+20
+-107.717322721809
+30
+176.06259842899448
+11
+180.96358267716596
+21
+-107.71732255816846
+31
+189.86102362204736
+12
+5.115907697472721e-13
+22
+-107.71732795145476
+32
+176.06451773417749
+13
+5.115907697472721e-13
+23
+-107.71732795145476
+33
+176.06451773417749
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+-107.71732795145476
+30
+176.06451773417749
+11
+180.96358267716596
+21
+-107.71732255816846
+31
+189.86102362204736
+12
+5.115907697472721e-13
+22
+-107.71732795145476
+32
+189.86102362204736
+13
+5.115907697472721e-13
+23
+-107.71732795145476
+33
+189.86102362204736
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96358267716596
+20
+-107.71732255816846
+30
+189.86102362204736
+11
+5.115907697472721e-13
+21
+-84.34409960499816
+31
+189.86102362204736
+12
+5.115907697472721e-13
+22
+-107.71732795145476
+32
+189.86102362204736
+13
+5.115907697472721e-13
+23
+-107.71732795145476
+33
+189.86102362204736
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+-84.34409960499816
+30
+189.86102362204736
+11
+180.96358267716596
+21
+-107.71732255816846
+31
+189.86102362204736
+12
+162.47775520891634
+22
+-84.34409476264783
+32
+189.86102362204736
+13
+162.47775520891634
+23
+-84.34409476264783
+33
+189.86102362204736
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+162.47775520891634
+20
+-84.34409476264783
+30
+189.86102362204736
+11
+180.96358267716596
+21
+-107.71732255816846
+31
+189.86102362204736
+12
+180.96358198056998
+22
+-84.34409421171179
+32
+189.86102362204736
+13
+180.96358198056998
+23
+-84.34409421171179
+33
+189.86102362204736
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+162.47775005936467
+20
+88.4413383082184
+30
+236.60039370078752
+11
+5.115907697472721e-13
+21
+109.82597913515954
+31
+236.60039370078752
+12
+5.115907697472721e-13
+22
+88.44133346586824
+32
+236.60039370078752
+13
+5.115907697472721e-13
+23
+88.44133346586824
+33
+236.60039370078752
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+109.82597913515954
+30
+236.60039370078752
+11
+162.47775005936467
+21
+88.4413383082184
+31
+236.60039370078752
+12
+171.68680454014486
+22
+109.82598425196863
+32
+236.60039370078752
+13
+171.68680454014486
+23
+109.82598425196863
+33
+236.60039370078752
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680454014486
+20
+109.82598425196863
+30
+236.60039370078752
+11
+162.47775005936467
+21
+88.4413383082184
+31
+236.60039370078752
+12
+180.96357683101826
+22
+88.44133885915446
+32
+236.60039370078752
+13
+180.96357683101826
+23
+88.44133885915446
+33
+236.60039370078752
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680454014486
+20
+109.82598425196863
+30
+236.60039370078752
+11
+180.96357683101826
+21
+88.44133885915446
+31
+236.60039370078752
+12
+180.96357619368825
+22
+109.82598452844579
+32
+236.60039370078752
+13
+180.96357619368825
+23
+109.82598452844579
+33
+236.60039370078752
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+162.47775520891634
+20
+-84.34409476264783
+30
+189.86102362204736
+11
+180.96357683101826
+21
+88.44133885915446
+31
+236.60039370078752
+12
+162.47775005936467
+22
+88.4413383082184
+32
+236.60039370078752
+13
+162.47775005936467
+23
+88.4413383082184
+33
+236.60039370078752
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357683101826
+20
+88.44133885915446
+30
+236.60039370078752
+11
+162.47775520891634
+21
+-84.34409476264783
+31
+189.86102362204736
+12
+180.96358198056998
+22
+-84.34409421171179
+32
+189.86102362204736
+13
+180.96358198056998
+23
+-84.34409421171179
+33
+189.86102362204736
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681074475182
+20
+-98.36023622047243
+30
+176.06259842519694
+11
+5.115907697472721e-13
+21
+-107.71732795145476
+31
+176.06451773417749
+12
+5.115907697472721e-13
+22
+-98.36024133728166
+32
+176.06451514594315
+13
+5.115907697472721e-13
+23
+-98.36024133728166
+33
+176.06451514594315
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+-107.71732795145476
+30
+176.06451773417749
+11
+171.68681074475182
+21
+-98.36023622047243
+31
+176.06259842519694
+12
+175.47286961786034
+22
+-107.717322721809
+32
+176.06259842899448
+13
+175.47286961786034
+23
+-107.717322721809
+33
+176.06259842899448
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+175.47286961786034
+20
+-107.717322721809
+30
+176.06259842899448
+11
+171.68681074475182
+21
+-98.36023622047243
+31
+176.06259842519694
+12
+180.96358239829522
+22
+-98.36023594399529
+32
+176.06259842519694
+13
+180.96358239829522
+23
+-98.36023594399529
+33
+176.06259842519694
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+175.47286961786034
+20
+-107.717322721809
+30
+176.06259842899448
+11
+180.96358239829522
+21
+-98.36023594399529
+31
+176.06259842519694
+12
+180.96358267716596
+22
+-107.71732255816846
+32
+176.06259842519694
+13
+180.96358267716596
+23
+-107.71732255816846
+33
+176.06259842519694
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+175.47334585487522
+20
+109.82598436481948
+30
+227.1425196820503
+11
+5.115907697472721e-13
+21
+98.36023110366338
+31
+227.1465447984691
+12
+5.115907697472721e-13
+22
+109.82597913515951
+32
+227.14655023375423
+13
+5.115907697472721e-13
+23
+109.82597913515951
+33
+227.14655023375423
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+5.115907697472721e-13
+20
+98.36023110366338
+30
+227.1465447984691
+11
+175.47334585487522
+21
+109.82598436481948
+31
+227.1425196820503
+12
+171.68680488186044
+22
+98.36023622047261
+32
+227.1425196850395
+13
+171.68680488186044
+23
+98.36023622047261
+33
+227.1425196850395
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680488186044
+20
+98.36023622047261
+30
+227.1425196850395
+11
+175.47334585487522
+21
+109.82598436481948
+31
+227.1425196820503
+12
+180.96357653540383
+22
+98.36023649694977
+32
+227.1425196850395
+13
+180.96357653540383
+23
+98.36023649694977
+33
+227.1425196850395
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357653540383
+20
+98.36023649694977
+30
+227.1425196850395
+11
+175.47334585487522
+21
+109.82598436481948
+31
+227.1425196820503
+12
+180.96357619368825
+22
+109.82598452844579
+32
+227.1425196850395
+13
+180.96357619368825
+23
+109.82598452844579
+33
+227.1425196850395
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357653540383
+20
+98.36023649694977
+30
+227.1425196850395
+11
+171.68681074475182
+21
+-98.36023622047243
+31
+176.06259842519694
+12
+171.68680488186044
+22
+98.36023622047261
+32
+227.1425196850395
+13
+171.68680488186044
+23
+98.36023622047261
+33
+227.1425196850395
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681074475182
+20
+-98.36023622047243
+30
+176.06259842519694
+11
+180.96357653540383
+21
+98.36023649694977
+31
+227.1425196850395
+12
+180.96358239829522
+22
+-98.36023594399529
+32
+176.06259842519694
+13
+180.96358239829522
+23
+-98.36023594399529
+33
+176.06259842519694
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357653540383
+20
+98.36023649694977
+30
+227.1425196850395
+11
+180.96358198056998
+21
+-84.34409421171179
+31
+189.86102362204736
+12
+180.96358239829522
+22
+-98.36023594399529
+32
+176.06259842519694
+13
+180.96358239829522
+23
+-98.36023594399529
+33
+176.06259842519694
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96358198056998
+20
+-84.34409421171179
+30
+189.86102362204736
+11
+180.96357653540383
+21
+98.36023649694977
+31
+227.1425196850395
+12
+180.96357683101826
+22
+88.44133885915446
+32
+236.60039370078752
+13
+180.96357683101826
+23
+88.44133885915446
+33
+236.60039370078752
+70
+1
+ 0
+3DFACE
+ 8
+inside_half
+10
+162.47775520891634
+20
+-84.34409476264783
+30
+33.43464566929135
+11
+5.115907697472721e-13
+21
+88.44133346586825
+31
+80.17362204724412
+12
+5.115907697472721e-13
+22
+-84.34409960499816
+32
+33.43464566929135
+13
+5.115907697472721e-13
+23
+-84.34409960499816
+33
+33.43464566929135
+70
+1
+ 0
+3DFACE
+ 8
+inside_half
+10
+5.115907697472721e-13
+20
+88.44133346586825
+30
+80.17362204724412
+11
+162.47775520891634
+21
+-84.34409476264783
+31
+33.43464566929135
+12
+162.47775005936467
+22
+88.44133830821842
+32
+80.17362204724412
+13
+162.47775005936467
+23
+88.44133830821842
+33
+80.17362204724412
+70
+1
+ 0
+3DFACE
+ 8
+inside_half
+10
+162.47775005936467
+20
+88.44133830821842
+30
+236.60039370078752
+11
+5.115907697472721e-13
+21
+88.44133346586825
+31
+80.17362204724412
+12
+162.47775005936467
+22
+88.44133830821842
+32
+80.17362204724412
+13
+162.47775005936467
+23
+88.44133830821842
+33
+80.17362204724412
+70
+1
+ 0
+3DFACE
+ 8
+inside_half
+10
+5.115907697472721e-13
+20
+88.44133346586825
+30
+80.17362204724412
+11
+162.47775005936467
+21
+88.44133830821842
+31
+236.60039370078752
+12
+5.115907697472721e-13
+22
+88.44133346586825
+32
+236.60039370078752
+13
+5.115907697472721e-13
+23
+88.44133346586825
+33
+236.60039370078752
+70
+1
+ 0
+3DFACE
+ 8
+inside_half
+10
+5.115907697472721e-13
+20
+-84.34409960499816
+30
+189.86102362204736
+11
+162.47775520891634
+21
+-84.34409476264783
+31
+33.43464566929135
+12
+5.115907697472721e-13
+22
+-84.34409960499816
+32
+33.43464566929135
+13
+5.115907697472721e-13
+23
+-84.34409960499816
+33
+33.43464566929135
+70
+1
+ 0
+3DFACE
+ 8
+inside_half
+10
+162.47775520891634
+20
+-84.34409476264783
+30
+33.43464566929135
+11
+5.115907697472721e-13
+21
+-84.34409960499816
+31
+189.86102362204736
+12
+162.47775520891634
+22
+-84.34409476264783
+32
+189.86102362204736
+13
+162.47775520891634
+23
+-84.34409476264783
+33
+189.86102362204736
+70
+1
+ 0
+3DFACE
+ 8
+inside_half
+10
+162.47775005936467
+20
+88.44133830821842
+30
+236.60039370078752
+11
+162.47775520891634
+21
+-84.34409476264783
+31
+33.43464566929135
+12
+162.47775520891634
+22
+-84.34409476264783
+32
+189.86102362204736
+13
+162.47775520891634
+23
+-84.34409476264783
+33
+189.86102362204736
+70
+1
+ 0
+3DFACE
+ 8
+inside_half
+10
+162.47775520891634
+20
+-84.34409476264783
+30
+33.43464566929135
+11
+162.47775005936467
+21
+88.44133830821842
+31
+236.60039370078752
+12
+162.47775005936467
+22
+88.44133830821842
+32
+80.17362204724412
+13
+162.47775005936467
+23
+88.44133830821842
+33
+80.17362204724412
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519303512613
+20
+-98.36024111803383
+30
+7.678156604932067
+11
+7.356519154496823
+21
+-93.36024111803414
+31
+168.7079137133973
+12
+7.356519303512613
+22
+-98.36024111803414
+32
+168.7079137133973
+13
+7.356519303512613
+23
+-98.36024111803414
+33
+168.7079137133973
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519154496823
+20
+-93.36024111803414
+30
+168.7079137133973
+11
+7.356519303512613
+21
+-98.36024111803383
+31
+7.678156604932067
+12
+7.356519154496823
+22
+-93.36024111803383
+32
+7.678156604932072
+13
+7.356519154496823
+23
+-93.36024111803383
+33
+7.678156604932072
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519303512613
+20
+-98.36024111803383
+30
+7.678156604932067
+11
+164.33029129222393
+21
+-93.36023643971993
+31
+7.678158957060002
+12
+7.356519154496823
+22
+-93.36024111803383
+32
+7.678156604932072
+13
+7.356519154496823
+23
+-93.36024111803383
+33
+7.678156604932072
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029129222393
+20
+-93.36023643971993
+30
+7.678158957060002
+11
+7.356519303512613
+21
+-98.36024111803383
+31
+7.678156604932067
+12
+164.33029144123975
+22
+-98.36023643971993
+32
+7.678158957059996
+13
+164.33029144123975
+23
+-98.36023643971993
+33
+7.678158957059996
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029129222393
+20
+-93.36023643971994
+30
+168.7061612498017
+11
+164.33029144123975
+21
+-98.36023643971993
+31
+7.678158957059996
+12
+164.33029144123975
+22
+-98.36023643971994
+32
+168.7061612498017
+13
+164.33029144123975
+23
+-98.36023643971994
+33
+168.7061612498017
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029144123975
+20
+-98.36023643971993
+30
+7.678158957059996
+11
+164.33029129222393
+21
+-93.36023643971994
+31
+168.7061612498017
+12
+164.33029129222393
+22
+-93.36023643971993
+32
+7.678158957060002
+13
+164.33029129222393
+23
+-93.36023643971993
+33
+7.678158957060002
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029129222393
+20
+-93.36023643971994
+30
+168.7061612498017
+11
+7.356519303512613
+21
+-98.36024111803414
+31
+168.7079137133973
+12
+7.356519154496823
+22
+-93.36024111803414
+32
+168.7079137133973
+13
+7.356519154496823
+23
+-93.36024111803414
+33
+168.7079137133973
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519303512613
+20
+-98.36024111803414
+30
+168.7079137133973
+11
+164.33029129222393
+21
+-93.36023643971994
+31
+168.7061612498017
+12
+164.33029144123975
+22
+-98.36023643971994
+32
+168.7061612498017
+13
+164.33029144123975
+23
+-98.36023643971994
+33
+168.7061612498017
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33028557834837
+20
+98.3602360012251
+30
+219.78617284951383
+11
+164.33028572736413
+21
+93.3602360012251
+31
+7.678187350803336
+12
+164.33028572736413
+22
+93.3602360012251
+32
+219.78617284951383
+13
+164.33028572736413
+23
+93.3602360012251
+33
+219.78617284951383
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33028572736413
+20
+93.3602360012251
+30
+7.678187350803336
+11
+164.33028557834837
+21
+98.3602360012251
+31
+219.78617284951383
+12
+164.33028557834837
+22
+98.3602360012251
+32
+7.6781873508033405
+13
+164.33028557834837
+23
+98.3602360012251
+33
+7.6781873508033405
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519452528403
+20
+93.36023132291126
+30
+7.67818600768036
+11
+164.33028557834837
+21
+98.3602360012251
+31
+7.6781873508033405
+12
+7.356519303512613
+22
+98.36023132291126
+32
+7.678186007680364
+13
+7.356519303512613
+23
+98.36023132291126
+33
+7.678186007680364
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33028557834837
+20
+98.3602360012251
+30
+7.6781873508033405
+11
+7.356519452528403
+21
+93.36023132291126
+31
+7.67818600768036
+12
+164.33028572736413
+22
+93.3602360012251
+32
+7.678187350803336
+13
+164.33028572736413
+23
+93.3602360012251
+33
+7.678187350803336
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519452528403
+20
+93.36023132291126
+30
+7.67818600768036
+11
+7.356519303512613
+21
+98.36023132291092
+31
+219.78985302292708
+12
+7.356519452528403
+22
+93.36023132291092
+32
+219.78985302292708
+13
+7.356519452528403
+23
+93.36023132291092
+33
+219.78985302292708
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519303512613
+20
+98.36023132291092
+30
+219.78985302292708
+11
+7.356519452528403
+21
+93.36023132291126
+31
+7.67818600768036
+12
+7.356519303512613
+22
+98.36023132291126
+32
+7.678186007680364
+13
+7.356519303512613
+23
+98.36023132291126
+33
+7.678186007680364
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33028557834837
+20
+98.3602360012251
+30
+219.78617284951383
+11
+7.356519452528403
+21
+93.36023132291092
+31
+219.78985302292708
+12
+7.356519303512613
+22
+98.36023132291092
+32
+219.78985302292708
+13
+7.356519303512613
+23
+98.36023132291092
+33
+219.78985302292708
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+7.356519452528403
+20
+93.36023132291092
+30
+219.78985302292708
+11
+164.33028557834837
+21
+98.3602360012251
+31
+219.78617284951383
+12
+164.33028572736413
+22
+93.3602360012251
+32
+219.78617284951383
+13
+164.33028572736413
+23
+93.3602360012251
+33
+219.78617284951383
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681074475182
+20
+-98.36023622047243
+30
+0.32163976377951986
+11
+171.68681053254642
+21
+-91.24000442486981
+31
+7.441872585372755
+12
+171.68681074475182
+22
+-98.36023622047243
+32
+176.06259842519694
+13
+171.68681074475182
+23
+-98.36023622047243
+33
+176.06259842519694
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681053254642
+20
+-91.24000442486981
+30
+7.441872585372755
+11
+171.68681074475182
+21
+-98.36023622047243
+31
+0.32163976377951986
+12
+171.68680488186044
+22
+98.36023622047261
+32
+0.3216681102362128
+13
+171.68680488186044
+23
+98.36023622047261
+33
+0.3216681102362128
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681053254642
+20
+-91.24000442486981
+30
+7.441872585372755
+11
+171.68680488186044
+21
+98.36023622047261
+31
+0.3216681102362128
+12
+171.68680509406585
+22
+91.24000442487
+32
+7.441898879848383
+13
+171.68680509406585
+23
+91.24000442487
+33
+7.441898879848383
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680509406585
+20
+91.24000442487
+30
+7.441898879848383
+11
+171.68680488186044
+21
+98.36023622047261
+31
+0.3216681102362128
+12
+171.68680509406585
+22
+91.24000442487
+32
+217.9373521601451
+13
+171.68680509406585
+23
+91.24000442487
+33
+217.9373521601451
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681074475182
+20
+-98.36023622047243
+30
+176.06259842519694
+11
+171.68681053254642
+21
+-91.24000442486981
+31
+170.55507227904
+12
+171.68680488186044
+22
+98.36023622047261
+32
+227.1425196850395
+13
+171.68680488186044
+23
+98.36023622047261
+33
+227.1425196850395
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681053254642
+20
+-91.24000442486981
+30
+170.55507227904
+11
+171.68681074475182
+21
+-98.36023622047243
+31
+176.06259842519694
+12
+171.68681053254642
+22
+-91.24000442486981
+32
+7.441872585372755
+13
+171.68681053254642
+23
+-91.24000442486981
+33
+7.441872585372755
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680488186044
+20
+98.36023622047261
+30
+227.1425196850395
+11
+171.68681053254642
+21
+-91.24000442486981
+31
+170.55507227904
+12
+171.68680509406585
+22
+91.24000442487
+32
+217.9373521601451
+13
+171.68680509406585
+23
+91.24000442487
+33
+217.9373521601451
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680488186044
+20
+98.36023622047261
+30
+227.1425196850395
+11
+171.68680509406585
+21
+91.24000442487
+31
+217.9373521601451
+12
+171.68680488186044
+22
+98.36023622047261
+32
+0.3216681102362128
+13
+171.68680488186044
+23
+98.36023622047261
+33
+0.3216681102362128
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680509406585
+20
+91.24000442487
+30
+217.9373521601451
+11
+166.68681053254642
+21
+-91.2400045738856
+31
+170.55507227904
+12
+166.68680509406585
+22
+91.2400042758542
+32
+217.9373521601451
+13
+166.68680509406585
+23
+91.2400042758542
+33
+217.9373521601451
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+166.68681053254642
+20
+-91.2400045738856
+30
+170.55507227904
+11
+171.68680509406585
+21
+91.24000442487
+31
+217.9373521601451
+12
+171.68681053254642
+22
+-91.24000442486981
+32
+170.55507227904
+13
+171.68681053254642
+23
+-91.24000442486981
+33
+170.55507227904
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+166.68681053254642
+20
+-91.2400045738856
+30
+170.55507227904
+11
+171.68681053254642
+21
+-91.24000442486981
+31
+7.441872585372755
+12
+166.68681053254642
+22
+-91.2400045738856
+32
+7.441872585372755
+13
+166.68681053254642
+23
+-91.2400045738856
+33
+7.441872585372755
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68681053254642
+20
+-91.24000442486981
+30
+7.441872585372755
+11
+166.68681053254642
+21
+-91.2400045738856
+31
+170.55507227904
+12
+171.68681053254642
+22
+-91.24000442486981
+32
+170.55507227904
+13
+171.68681053254642
+23
+-91.24000442486981
+33
+170.55507227904
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+166.68681053254642
+20
+-91.2400045738856
+30
+7.441872585372755
+11
+171.68680509406585
+21
+91.24000442487
+31
+7.441898879848383
+12
+166.68680509406585
+22
+91.2400042758542
+32
+7.441898879848383
+13
+166.68680509406585
+23
+91.2400042758542
+33
+7.441898879848383
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680509406585
+20
+91.24000442487
+30
+7.441898879848383
+11
+166.68681053254642
+21
+-91.2400045738856
+31
+7.441872585372755
+12
+171.68681053254642
+22
+-91.24000442486981
+32
+7.441872585372755
+13
+171.68681053254642
+23
+-91.24000442486981
+33
+7.441872585372755
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+171.68680509406585
+20
+91.24000442487
+30
+217.9373521601451
+11
+166.68680509406585
+21
+91.2400042758542
+31
+7.441898879848383
+12
+171.68680509406585
+22
+91.24000442487
+32
+7.441898879848383
+13
+171.68680509406585
+23
+91.24000442487
+33
+7.441898879848383
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+166.68680509406585
+20
+91.2400042758542
+30
+7.441898879848383
+11
+171.68680509406585
+21
+91.24000442487
+31
+217.9373521601451
+12
+166.68680509406585
+22
+91.2400042758542
+32
+217.9373521601451
+13
+166.68680509406585
+23
+91.2400042758542
+33
+217.9373521601451
+70
+1
+ 0
+LINE
+ 8
+frame_half
+10
+192.11939488573717
+20
+-18.806592430868662
+30
+98.78595140714901
+11
+192.11958396451757
+21
+-18.812044202216626
+31
+98.7803176609457
+ 0
+3DFACE
+ 8
+frame_half
+10
+169.1868200523188
+20
+35.43805451572439
+30
+98.35513302657242
+11
+195.93208234481858
+21
+35.43803453794092
+31
+155.2043307086615
+12
+195.93208234769213
+22
+35.438032218648914
+32
+98.35511811023626
+13
+195.93208234769213
+23
+35.438032218648914
+33
+98.35511811023626
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+195.93208234481858
+20
+35.43803453794092
+30
+155.2043307086615
+11
+169.1868200523188
+21
+35.43805451572439
+31
+98.35513302657242
+12
+169.1868200523188
+22
+35.43805451572439
+32
+155.20434292468212
+13
+169.1868200523188
+23
+35.43805451572439
+33
+155.20434292468212
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+195.93206584814033
+20
+-18.806575569150453
+30
+98.3551192167518
+11
+191.93206584793933
+21
+-18.806572071228686
+31
+102.3551214476279
+12
+169.18680355276706
+22
+-18.80655327207498
+32
+98.35513413308796
+13
+169.18680355276706
+23
+-18.80655327207498
+33
+98.35513413308796
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+191.93206584793933
+20
+-18.806572071228686
+30
+102.3551214476279
+11
+195.93206584814033
+21
+-18.806575569150453
+31
+98.3551192167518
+12
+195.93206584526678
+22
+-18.80657324985846
+32
+155.20433181517726
+13
+195.93206584526678
+23
+-18.80657324985846
+33
+155.20433181517726
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+191.93206584793933
+20
+-18.806572071228686
+30
+102.3551214476279
+11
+195.93206584526678
+21
+-18.80657324985846
+31
+155.20433181517726
+12
+191.93206584546897
+22
+-18.806572283087576
+32
+151.20433364219494
+13
+191.93206584546897
+23
+-18.806572283087576
+33
+151.20433364219494
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+191.93206584546897
+20
+-18.806572283087576
+30
+151.20433364219494
+11
+195.93206584526678
+21
+-18.80657324985846
+31
+155.20433181517726
+12
+173.18680355276365
+22
+-18.80656595048204
+32
+151.20434220417928
+13
+173.18680355276365
+23
+-18.80656595048204
+33
+151.20434220417928
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+169.18680355276706
+20
+-18.80655327207498
+30
+98.35513413308796
+11
+173.18680355276558
+21
+-18.806557480875455
+31
+102.35513190221339
+12
+169.18680355276337
+22
+-18.806565694589835
+32
+155.20434403119808
+13
+169.18680355276337
+23
+-18.806565694589835
+33
+155.20434403119808
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+173.18680355276558
+20
+-18.806557480875455
+30
+102.35513190221339
+11
+169.18680355276706
+21
+-18.80655327207498
+31
+98.35513413308796
+12
+191.93206584793933
+22
+-18.806572071228686
+32
+102.3551214476279
+13
+191.93206584793933
+23
+-18.806572071228686
+33
+102.3551214476279
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+169.18680355276337
+20
+-18.806565694589835
+30
+155.20434403119808
+11
+173.18680355276558
+21
+-18.806557480875455
+31
+102.35513190221339
+12
+173.18680355276365
+22
+-18.80656595048204
+32
+151.20434220417928
+13
+173.18680355276365
+23
+-18.80656595048204
+33
+151.20434220417928
+70
+13
+ 0
+3DFACE
+ 8
+frame_half
+10
+169.18680355276337
+20
+-18.806565694589835
+30
+155.20434403119808
+11
+173.18680355276365
+21
+-18.80656595048204
+31
+151.20434220417928
+12
+195.93206584526678
+22
+-18.80657324985846
+32
+155.20433181517726
+13
+195.93206584526678
+23
+-18.80657324985846
+33
+155.20433181517726
+70
+3
+ 0
+3DFACE
+ 8
+frame_half
+10
+195.93208234769213
+20
+35.438032218648914
+30
+98.35511811023626
+11
+195.93206584526678
+21
+-18.80657324985846
+31
+155.20433181517726
+12
+195.93206584814033
+22
+-18.806575569150453
+32
+98.3551192167518
+13
+195.93206584814033
+23
+-18.806575569150453
+33
+98.3551192167518
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+195.93206584526678
+20
+-18.80657324985846
+30
+155.20433181517726
+11
+195.93208234769213
+21
+35.438032218648914
+31
+98.35511811023626
+12
+195.93208234481858
+22
+35.43803453794092
+32
+155.2043307086615
+13
+195.93208234481858
+23
+35.43803453794092
+33
+155.2043307086615
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+169.1868200523188
+20
+35.43805451572439
+30
+98.35513302657242
+11
+195.93206584814033
+21
+-18.806575569150453
+31
+98.3551192167518
+12
+169.18680355276706
+22
+-18.80655327207498
+32
+98.35513413308796
+13
+169.18680355276706
+23
+-18.80655327207498
+33
+98.35513413308796
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+195.93206584814033
+20
+-18.806575569150453
+30
+98.3551192167518
+11
+169.1868200523188
+21
+35.43805451572439
+31
+98.35513302657242
+12
+195.93208234769213
+22
+35.438032218648914
+32
+98.35511811023626
+13
+195.93208234769213
+23
+35.438032218648914
+33
+98.35511811023626
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+169.1868200523188
+20
+35.43805451572439
+30
+155.20434292468212
+11
+169.18680355276706
+21
+-18.80655327207498
+31
+98.35513413308796
+12
+169.18680355276337
+22
+-18.806565694589835
+32
+155.20434403119808
+13
+169.18680355276337
+23
+-18.806565694589835
+33
+155.20434403119808
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+169.18680355276706
+20
+-18.80655327207498
+30
+98.35513413308796
+11
+169.1868200523188
+21
+35.43805451572439
+31
+155.20434292468212
+12
+169.1868200523188
+22
+35.43805451572439
+32
+98.35513302657242
+13
+169.1868200523188
+23
+35.43805451572439
+33
+98.35513302657242
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+195.93206584526678
+20
+-18.80657324985846
+30
+155.20433181517726
+11
+169.1868200523188
+21
+35.43805451572439
+31
+155.20434292468212
+12
+169.18680355276337
+22
+-18.806565694589835
+32
+155.20434403119808
+13
+169.18680355276337
+23
+-18.806565694589835
+33
+155.20434403119808
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+169.1868200523188
+20
+35.43805451572439
+30
+155.20434292468212
+11
+195.93206584526678
+21
+-18.80657324985846
+31
+155.20433181517726
+12
+195.93208234481858
+22
+35.43803453794092
+32
+155.2043307086615
+13
+195.93208234481858
+23
+35.43803453794092
+33
+155.2043307086615
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+149.6172528342156
+20
+-76.99953980767417
+30
+0.07164228285092733
+11
+164.3302911109016
+21
+-80.5596534414427
+31
+-9.928357717148824
+12
+156.97377191349227
+22
+-80.55965344144272
+32
+-9.928357717148824
+13
+156.97377191349227
+23
+-80.55965344144272
+33
+-9.928357717148824
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.3302911109016
+20
+-80.5596534414427
+30
+-9.928357717148824
+11
+149.6172528342156
+21
+-76.99953980767417
+31
+0.07164228285092733
+12
+164.33029122903434
+22
+-76.99953980767414
+32
+0.07164228285092733
+13
+164.33029122903434
+23
+-76.99953980767414
+33
+0.07164228285092733
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029144123975
+20
+-91.24000339887927
+30
+0.07164074271531895
+11
+156.97377191349227
+21
+-87.67988534666897
+31
+-9.928358597448263
+12
+164.33029121700434
+22
+-87.67988523704514
+32
+-9.92835925728443
+13
+164.33029121700434
+23
+-87.67988523704514
+33
+-9.92835925728443
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+156.97377191349227
+20
+-87.67988534666897
+30
+-9.928358597448263
+11
+164.33029144123975
+21
+-91.24000339887927
+31
+0.07164074271531895
+12
+149.6172528342156
+22
+-91.2400036181269
+32
+0.07164140255148688
+13
+149.6172528342156
+23
+-91.2400036181269
+33
+0.07164140255148688
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.3302858950658
+20
+87.67989181709197
+30
+-9.928332989879554
+11
+156.97376669765652
+21
+80.55965991186571
+31
+-9.928333870178992
+12
+156.97376669765652
+22
+87.67989181709194
+32
+-9.928332989879554
+13
+156.97376669765652
+23
+87.67989181709194
+33
+-9.928332989879554
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+156.97376669765652
+20
+80.55965991186571
+30
+-9.928333870178992
+11
+164.3302858950658
+21
+87.67989181709197
+31
+-9.928332989879554
+12
+164.3302860011686
+22
+80.55966002148952
+32
+-9.92833453001516
+13
+164.3302860011686
+23
+80.55966002148952
+33
+-9.92833453001516
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.3302860011686
+20
+80.55966002148952
+30
+-9.92833453001516
+11
+164.3302860131986
+21
+91.24000545086054
+31
+0.07166701012019949
+12
+164.330286225404
+22
+76.99954185965541
+32
+0.07166546998458934
+13
+164.330286225404
+23
+76.99954185965541
+33
+0.07166546998458934
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.3302860131986
+20
+91.24000545086054
+30
+0.07166701012019949
+11
+164.3302860011686
+21
+80.55966002148952
+31
+-9.92833453001516
+12
+164.3302858950658
+22
+87.67989181709197
+32
+-9.928332989879554
+13
+164.3302858950658
+23
+87.67989181709197
+33
+-9.928332989879554
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+149.61724761837985
+20
+91.24000545086051
+30
+0.07166701012019949
+11
+156.97376669765652
+21
+80.55965991186571
+31
+-9.928333870178992
+12
+149.6172476183798
+22
+76.99954164040778
+32
+0.07166612982075904
+13
+149.6172476183798
+23
+76.99954164040778
+33
+0.07166612982075904
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+156.97376669765652
+20
+80.55965991186571
+30
+-9.928333870178992
+11
+149.61724761837985
+21
+91.24000545086051
+31
+0.07166701012019949
+12
+156.97376669765652
+22
+87.67989181709194
+32
+-9.928332989879554
+13
+156.97376669765652
+23
+87.67989181709194
+33
+-9.928332989879554
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.330286225404
+20
+76.99954185965541
+30
+0.07166546998458934
+11
+156.97376669765652
+21
+80.55965991186571
+31
+-9.928333870178992
+12
+164.3302860011686
+22
+80.55966002148952
+32
+-9.92833453001516
+13
+164.3302860011686
+23
+80.55966002148952
+33
+-9.92833453001516
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+156.97376669765652
+20
+80.55965991186571
+30
+-9.928333870178992
+11
+164.330286225404
+21
+76.99954185965541
+31
+0.07166546998458934
+12
+149.6172476183798
+22
+76.99954164040778
+32
+0.07166612982075904
+13
+149.6172476183798
+23
+76.99954164040778
+33
+0.07166612982075904
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+149.61724761837985
+20
+91.24000545086051
+30
+0.07166701012019949
+11
+164.3302858950658
+21
+87.67989181709197
+31
+-9.928332989879554
+12
+156.97376669765652
+22
+87.67989181709194
+32
+-9.928332989879554
+13
+156.97376669765652
+23
+87.67989181709194
+33
+-9.928332989879554
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.3302858950658
+20
+87.67989181709197
+30
+-9.928332989879554
+11
+149.61724761837985
+21
+91.24000545086051
+31
+0.07166701012019949
+12
+164.3302860131986
+22
+91.24000545086054
+32
+0.07166701012019949
+13
+164.3302860131986
+23
+91.24000545086054
+33
+0.07166701012019949
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029121700434
+20
+-87.67988523704514
+30
+-9.92835925728443
+11
+164.33029122903434
+21
+-76.99953980767414
+31
+0.07164228285092733
+12
+164.33029144123975
+22
+-91.24000339887927
+32
+0.07164074271531895
+13
+164.33029144123975
+23
+-91.24000339887927
+33
+0.07164074271531895
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.33029122903434
+20
+-76.99953980767414
+30
+0.07164228285092733
+11
+164.33029121700434
+21
+-87.67988523704514
+31
+-9.92835925728443
+12
+164.3302911109016
+22
+-80.5596534414427
+32
+-9.928357717148824
+13
+164.3302911109016
+23
+-80.5596534414427
+33
+-9.928357717148824
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+149.6172528342156
+20
+-76.99953980767417
+30
+0.07164228285092733
+11
+156.97377191349227
+21
+-87.67988534666897
+31
+-9.928358597448263
+12
+149.6172528342156
+22
+-91.2400036181269
+32
+0.07164140255148688
+13
+149.6172528342156
+23
+-91.2400036181269
+33
+0.07164140255148688
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+156.97377191349227
+20
+-87.67988534666897
+30
+-9.928358597448263
+11
+149.6172528342156
+21
+-76.99953980767417
+31
+0.07164228285092733
+12
+156.97377191349227
+22
+-80.55965344144272
+32
+-9.928357717148824
+13
+156.97377191349227
+23
+-80.55965344144272
+33
+-9.928357717148824
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+164.3302911109016
+20
+-80.5596534414427
+30
+-9.928357717148824
+11
+156.97377191349227
+21
+-87.67988534666897
+31
+-9.928358597448263
+12
+156.97377191349227
+22
+-80.55965344144272
+32
+-9.928357717148824
+13
+156.97377191349227
+23
+-80.55965344144272
+33
+-9.928357717148824
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+156.97377191349227
+20
+-87.67988534666897
+30
+-9.928358597448263
+11
+164.3302911109016
+21
+-80.5596534414427
+31
+-9.928357717148824
+12
+164.33029121700434
+22
+-87.67988523704514
+32
+-9.92835925728443
+13
+164.33029121700434
+23
+-87.67988523704514
+33
+-9.92835925728443
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96358239829522
+20
+-98.36023594399529
+30
+176.06259842519694
+11
+180.96358267716596
+21
+-107.71732255816846
+31
+189.86102362204736
+12
+180.96358267716596
+22
+-107.71732255816846
+32
+176.06259842519694
+13
+180.96358267716596
+23
+-107.71732255816846
+33
+176.06259842519694
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96358267716596
+20
+-107.71732255816846
+30
+189.86102362204736
+11
+180.96358239829522
+21
+-98.36023594399529
+31
+176.06259842519694
+12
+180.96358198056998
+22
+-84.34409421171179
+32
+189.86102362204736
+13
+180.96358198056998
+23
+-84.34409421171179
+33
+189.86102362204736
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357653540383
+20
+98.36023649694977
+30
+227.1425196850395
+11
+180.96357619368825
+21
+109.82598452844579
+31
+236.60039370078752
+12
+180.96357683101826
+22
+88.44133885915446
+32
+236.60039370078752
+13
+180.96357683101826
+23
+88.44133885915446
+33
+236.60039370078752
+70
+1
+ 0
+3DFACE
+ 8
+frame_half
+10
+180.96357619368825
+20
+109.82598452844579
+30
+236.60039370078752
+11
+180.96357653540383
+21
+98.36023649694977
+31
+227.1425196850395
+12
+180.96357619368825
+22
+109.82598452844579
+32
+227.1425196850395
+13
+180.96357619368825
+23
+109.82598452844579
+33
+227.1425196850395
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+164.33029129222393
+20
+-93.36023643971993
+30
+7.678158957060002
+11
+7.356519154496823
+21
+-93.36024111803414
+31
+168.7079137133973
+12
+7.356519154496823
+22
+-93.36024111803383
+32
+7.678156604932072
+13
+7.356519154496823
+23
+-93.36024111803383
+33
+7.678156604932072
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+7.356519154496823
+20
+-93.36024111803414
+30
+168.7079137133973
+11
+164.33029129222393
+21
+-93.36023643971993
+31
+7.678158957060002
+12
+164.33029129222393
+22
+-93.36023643971994
+32
+168.7061612498017
+13
+164.33029129222393
+23
+-93.36023643971994
+33
+168.7061612498017
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+7.356519452528403
+20
+93.36023132291092
+30
+219.78985302292708
+11
+164.33028572736413
+21
+93.3602360012251
+31
+7.678187350803336
+12
+7.356519452528403
+22
+93.36023132291126
+32
+7.67818600768036
+13
+7.356519452528403
+23
+93.36023132291126
+33
+7.67818600768036
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+164.33028572736413
+20
+93.3602360012251
+30
+7.678187350803336
+11
+7.356519452528403
+21
+93.36023132291092
+31
+219.78985302292708
+12
+164.33028572736413
+22
+93.3602360012251
+32
+219.78617284951383
+13
+164.33028572736413
+23
+93.3602360012251
+33
+219.78617284951383
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+166.68680509406585
+20
+91.2400042758542
+30
+217.9373521601451
+11
+166.68681053254642
+21
+-91.2400045738856
+31
+170.55507227904
+12
+166.68680509406585
+22
+91.2400042758542
+32
+7.441898879848383
+13
+166.68680509406585
+23
+91.2400042758542
+33
+7.441898879848383
+70
+0
+ 0
+3DFACE
+ 8
+panels_half
+10
+166.68680509406585
+20
+91.2400042758542
+30
+7.441898879848383
+11
+166.68681053254642
+21
+-91.2400045738856
+31
+170.55507227904
+12
+166.68681053254642
+22
+-91.2400045738856
+32
+7.441872585372755
+13
+166.68681053254642
+23
+-91.2400045738856
+33
+7.441872585372755
+70
+0
+ 0
+3DFACE
+ 8
+panels_half
+10
+191.93208098206628
+20
+8.315735296259732
+30
+151.2043360522785
+11
+173.1868186893629
+21
+8.315750098471852
+31
+102.35513431229701
+12
+191.93208098453664
+22
+8.315735508118621
+32
+102.35512385771152
+13
+191.93208098453664
+23
+8.315735508118621
+33
+102.35512385771152
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+173.1868186893629
+20
+8.315750098471852
+30
+102.35513431229701
+11
+191.93208098206628
+21
+8.315735296259732
+31
+151.2043360522785
+12
+173.18681868936096
+22
+8.315741628865268
+32
+151.20434461426285
+13
+173.18681868936096
+23
+8.315741628865268
+33
+151.20434461426285
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+173.18681868936096
+20
+8.315741628865268
+30
+151.20434461426285
+11
+191.93206584546897
+21
+-18.806572283087576
+31
+151.20433364219494
+12
+173.18680355276365
+22
+-18.80656595048204
+32
+151.20434220417928
+13
+173.18680355276365
+23
+-18.80656595048204
+33
+151.20434220417928
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+191.93206584546897
+20
+-18.806572283087576
+30
+151.20433364219494
+11
+173.18681868936096
+21
+8.315741628865268
+31
+151.20434461426285
+12
+191.93208098206628
+22
+8.315735296259732
+32
+151.2043360522785
+13
+191.93208098206628
+23
+8.315735296259732
+33
+151.2043360522785
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+173.18680355276558
+20
+-18.806557480875455
+30
+102.35513190221339
+11
+173.18681868936096
+21
+8.315741628865268
+31
+151.20434461426285
+12
+173.18680355276365
+22
+-18.80656595048204
+32
+151.20434220417928
+13
+173.18680355276365
+23
+-18.80656595048204
+33
+151.20434220417928
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+173.18681868936096
+20
+8.315741628865268
+30
+151.20434461426285
+11
+173.18680355276558
+21
+-18.806557480875455
+31
+102.35513190221339
+12
+173.1868186893629
+22
+8.315750098471852
+32
+102.35513431229701
+13
+173.1868186893629
+23
+8.315750098471852
+33
+102.35513431229701
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+191.93206584793933
+20
+-18.806572071228686
+30
+102.3551214476279
+11
+173.1868186893629
+21
+8.315750098471852
+31
+102.35513431229701
+12
+173.18680355276558
+22
+-18.806557480875455
+32
+102.35513190221339
+13
+173.18680355276558
+23
+-18.806557480875455
+33
+102.35513190221339
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+173.1868186893629
+20
+8.315750098471852
+30
+102.35513431229701
+11
+191.93206584793933
+21
+-18.806572071228686
+31
+102.3551214476279
+12
+191.93208098453664
+22
+8.315735508118621
+32
+102.35512385771152
+13
+191.93208098453664
+23
+8.315735508118621
+33
+102.35512385771152
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+191.93208098206628
+20
+8.315735296259732
+30
+151.2043360522785
+11
+191.93206584793933
+21
+-18.806572071228686
+31
+102.3551214476279
+12
+191.93206584546897
+22
+-18.806572283087576
+32
+151.20433364219494
+13
+191.93206584546897
+23
+-18.806572283087576
+33
+151.20433364219494
+70
+1
+ 0
+3DFACE
+ 8
+panels_half
+10
+191.93206584793933
+20
+-18.806572071228686
+30
+102.3551214476279
+11
+191.93208098206628
+21
+8.315735296259732
+31
+151.2043360522785
+12
+191.93208098453664
+22
+8.315735508118621
+32
+102.35512385771152
+13
+191.93208098453664
+23
+8.315735508118621
+33
+102.35512385771152
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+108.5456607922482
+30
+249.43983074886108
+11
+54.42013089894506
+21
+111.14544989054545
+31
+246.84004165056382
+12
+14.604382809215494
+22
+111.14544989054545
+32
+246.84004165056382
+13
+14.604382809215494
+23
+111.14544989054545
+33
+246.84004165056382
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+111.14544989054545
+30
+246.84004165056382
+11
+14.604382809215494
+21
+108.5456607922482
+31
+249.43983074886108
+12
+54.42013089894506
+22
+108.5456607922482
+32
+249.43983074886108
+13
+54.42013089894506
+23
+108.5456607922482
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+97.89152693398054
+30
+243.28866369780795
+11
+14.604382809215494
+21
+98.84311578843918
+31
+239.73728574505208
+12
+54.42013089894506
+22
+98.84311578843918
+32
+239.73728574505208
+13
+54.42013089894506
+23
+98.84311578843918
+33
+239.73728574505208
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+98.84311578843918
+30
+239.73728574505208
+11
+54.42013089894506
+21
+97.89152693398054
+31
+243.28866369780795
+12
+14.604382809215494
+22
+97.89152693398054
+32
+243.28866369780795
+13
+14.604382809215494
+23
+97.89152693398054
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+236.18590779229618
+11
+14.604382809215494
+21
+101.44290488673643
+31
+237.1374966467548
+12
+14.604382809215494
+22
+104.99428283949231
+32
+236.18590779229618
+13
+14.604382809215494
+23
+104.99428283949231
+33
+236.18590779229618
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+101.44290488673643
+30
+237.1374966467548
+11
+54.42013089894506
+21
+104.99428283949231
+31
+236.18590779229618
+12
+54.42013089894506
+22
+101.44290488673643
+32
+237.1374966467548
+13
+54.42013089894506
+23
+101.44290488673643
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+111.14544989054545
+30
+239.73728574505205
+11
+14.604382809215494
+21
+108.5456607922482
+31
+237.1374966467548
+12
+14.604382809215494
+22
+111.14544989054545
+32
+239.73728574505205
+13
+14.604382809215494
+23
+111.14544989054545
+33
+239.73728574505205
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+108.5456607922482
+30
+237.1374966467548
+11
+54.42013089894506
+21
+111.14544989054545
+31
+239.73728574505205
+12
+54.42013089894506
+22
+108.5456607922482
+32
+237.1374966467548
+13
+54.42013089894506
+23
+108.5456607922482
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+98.84311578843918
+30
+246.84004165056385
+11
+14.604382809215494
+21
+97.89152693398054
+31
+243.28866369780795
+12
+54.42013089894506
+22
+97.89152693398054
+32
+243.28866369780795
+13
+54.42013089894506
+23
+97.89152693398054
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+97.89152693398054
+30
+243.28866369780795
+11
+54.42013089894506
+21
+98.84311578843918
+31
+246.84004165056385
+12
+14.604382809215494
+22
+98.84311578843918
+32
+246.84004165056385
+13
+14.604382809215494
+23
+98.84311578843918
+33
+246.84004165056385
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+111.14544989054545
+30
+246.84004165056382
+11
+54.42013089894506
+21
+112.09703874500408
+31
+243.28866369780795
+12
+14.604382809215494
+22
+112.09703874500408
+32
+243.28866369780795
+13
+14.604382809215494
+23
+112.09703874500408
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+112.09703874500408
+30
+243.28866369780795
+11
+14.604382809215494
+21
+111.14544989054545
+31
+246.84004165056382
+12
+54.42013089894506
+22
+111.14544989054545
+32
+246.84004165056382
+13
+54.42013089894506
+23
+111.14544989054545
+33
+246.84004165056382
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+98.84311578843918
+30
+239.73728574505208
+11
+14.604382809215494
+21
+101.44290488673643
+31
+237.1374966467548
+12
+54.42013089894506
+22
+101.44290488673643
+32
+237.1374966467548
+13
+54.42013089894506
+23
+101.44290488673643
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+101.44290488673643
+30
+237.1374966467548
+11
+54.42013089894506
+21
+98.84311578843918
+31
+239.73728574505208
+12
+14.604382809215494
+22
+98.84311578843918
+32
+239.73728574505208
+13
+14.604382809215494
+23
+98.84311578843918
+33
+239.73728574505208
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+112.09703874500408
+30
+243.28866369780795
+11
+54.42013089894506
+21
+111.14544989054545
+31
+239.73728574505205
+12
+14.604382809215494
+22
+111.14544989054545
+32
+239.73728574505205
+13
+14.604382809215494
+23
+111.14544989054545
+33
+239.73728574505205
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+111.14544989054545
+30
+239.73728574505205
+11
+14.604382809215494
+21
+112.09703874500408
+31
+243.28866369780795
+12
+54.42013089894506
+22
+112.09703874500408
+32
+243.28866369780795
+13
+54.42013089894506
+23
+112.09703874500408
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+98.84311578843918
+30
+246.84004165056385
+11
+14.604382809215494
+21
+101.44290488673643
+31
+249.43983074886108
+12
+14.604382809215494
+22
+98.84311578843918
+32
+246.84004165056385
+13
+14.604382809215494
+23
+98.84311578843918
+33
+246.84004165056385
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+101.44290488673643
+30
+249.43983074886108
+11
+54.42013089894506
+21
+98.84311578843918
+31
+246.84004165056385
+12
+54.42013089894506
+22
+101.44290488673643
+32
+249.43983074886108
+13
+54.42013089894506
+23
+101.44290488673643
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+101.44290488673643
+30
+249.43983074886108
+11
+14.604382809215494
+21
+104.99428283949231
+31
+250.39141960331972
+12
+14.604382809215494
+22
+101.44290488673643
+32
+249.43983074886108
+13
+14.604382809215494
+23
+101.44290488673643
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+250.39141960331972
+11
+54.42013089894506
+21
+101.44290488673643
+31
+249.43983074886108
+12
+54.42013089894506
+22
+104.99428283949231
+32
+250.39141960331972
+13
+54.42013089894506
+23
+104.99428283949231
+33
+250.39141960331972
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+250.39141960331972
+11
+14.604382809215494
+21
+108.5456607922482
+31
+249.43983074886108
+12
+14.604382809215494
+22
+104.99428283949231
+32
+250.39141960331972
+13
+14.604382809215494
+23
+104.99428283949231
+33
+250.39141960331972
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+108.5456607922482
+30
+249.43983074886108
+11
+54.42013089894506
+21
+104.99428283949231
+31
+250.39141960331972
+12
+54.42013089894506
+22
+108.5456607922482
+32
+249.43983074886108
+13
+54.42013089894506
+23
+108.5456607922482
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+108.5456607922482
+30
+237.1374966467548
+11
+14.604382809215494
+21
+104.99428283949231
+31
+236.18590779229618
+12
+14.604382809215494
+22
+108.5456607922482
+32
+237.1374966467548
+13
+14.604382809215494
+23
+108.5456607922482
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+236.18590779229618
+11
+54.42013089894506
+21
+108.5456607922482
+31
+237.1374966467548
+12
+54.42013089894506
+22
+104.99428283949231
+32
+236.18590779229618
+13
+54.42013089894506
+23
+104.99428283949231
+33
+236.18590779229618
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+98.84311578843918
+31
+246.84004165056385
+12
+14.604382809215494
+22
+101.44290488673643
+32
+249.43983074886108
+13
+14.604382809215494
+23
+101.44290488673643
+33
+249.43983074886108
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+250.39141960331972
+11
+118.74585968501914
+21
+108.5456607922482
+31
+249.43983074886108
+12
+118.74585968501914
+22
+104.99428283949231
+32
+250.39141960331972
+13
+118.74585968501914
+23
+104.99428283949231
+33
+250.39141960331972
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+108.5456607922482
+30
+249.43983074886108
+11
+158.56160777474872
+21
+104.99428283949231
+31
+250.39141960331972
+12
+158.56160777474872
+22
+108.5456607922482
+32
+249.43983074886108
+13
+158.56160777474872
+23
+108.5456607922482
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+98.84311578843918
+30
+246.84004165056385
+11
+118.74585968501914
+21
+97.89152693398054
+31
+243.28866369780795
+12
+158.56160777474872
+22
+97.89152693398054
+32
+243.28866369780795
+13
+158.56160777474872
+23
+97.89152693398054
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+97.89152693398054
+30
+243.28866369780795
+11
+158.56160777474872
+21
+98.84311578843918
+31
+246.84004165056385
+12
+118.74585968501914
+22
+98.84311578843918
+32
+246.84004165056385
+13
+118.74585968501914
+23
+98.84311578843918
+33
+246.84004165056385
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+236.18590779229618
+11
+118.74585968501914
+21
+101.44290488673643
+31
+237.1374966467548
+12
+118.74585968501914
+22
+104.99428283949231
+32
+236.18590779229618
+13
+118.74585968501914
+23
+104.99428283949231
+33
+236.18590779229618
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+101.44290488673643
+30
+237.1374966467548
+11
+158.56160777474872
+21
+104.99428283949231
+31
+236.18590779229618
+12
+158.56160777474872
+22
+101.44290488673643
+32
+237.1374966467548
+13
+158.56160777474872
+23
+101.44290488673643
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+98.84311578843918
+30
+246.84004165056385
+11
+118.74585968501914
+21
+101.44290488673643
+31
+249.43983074886108
+12
+118.74585968501914
+22
+98.84311578843918
+32
+246.84004165056385
+13
+118.74585968501914
+23
+98.84311578843918
+33
+246.84004165056385
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+101.44290488673643
+30
+249.43983074886108
+11
+158.56160777474872
+21
+98.84311578843918
+31
+246.84004165056385
+12
+158.56160777474872
+22
+101.44290488673643
+32
+249.43983074886108
+13
+158.56160777474872
+23
+101.44290488673643
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+108.5456607922482
+30
+249.43983074886108
+11
+158.56160777474872
+21
+111.14544989054545
+31
+246.84004165056382
+12
+118.74585968501914
+22
+111.14544989054545
+32
+246.84004165056382
+13
+118.74585968501914
+23
+111.14544989054545
+33
+246.84004165056382
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+111.14544989054545
+30
+246.84004165056382
+11
+118.74585968501914
+21
+108.5456607922482
+31
+249.43983074886108
+12
+158.56160777474872
+22
+108.5456607922482
+32
+249.43983074886108
+13
+158.56160777474872
+23
+108.5456607922482
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+112.09703874500408
+30
+243.28866369780795
+11
+54.42013089894506
+21
+104.99428283949231
+31
+243.28866369780795
+12
+54.42013089894506
+22
+111.14544989054545
+32
+239.73728574505205
+13
+54.42013089894506
+23
+111.14544989054545
+33
+239.73728574505205
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+101.44290488673643
+31
+237.1374966467548
+12
+118.74585968501914
+22
+98.84311578843918
+32
+239.73728574505208
+13
+118.74585968501914
+23
+98.84311578843918
+33
+239.73728574505208
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+112.09703874500408
+30
+243.28866369780795
+11
+158.56160777474872
+21
+111.14544989054545
+31
+239.73728574505205
+12
+118.74585968501914
+22
+111.14544989054545
+32
+239.73728574505205
+13
+118.74585968501914
+23
+111.14544989054545
+33
+239.73728574505205
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+111.14544989054545
+30
+239.73728574505205
+11
+118.74585968501914
+21
+112.09703874500408
+31
+243.28866369780795
+12
+158.56160777474872
+22
+112.09703874500408
+32
+243.28866369780795
+13
+158.56160777474872
+23
+112.09703874500408
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+111.14544989054545
+30
+239.73728574505205
+11
+118.74585968501914
+21
+108.5456607922482
+31
+237.1374966467548
+12
+118.74585968501914
+22
+111.14544989054545
+32
+239.73728574505205
+13
+118.74585968501914
+23
+111.14544989054545
+33
+239.73728574505205
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+108.5456607922482
+30
+237.1374966467548
+11
+158.56160777474872
+21
+111.14544989054545
+31
+239.73728574505205
+12
+158.56160777474872
+22
+108.5456607922482
+32
+237.1374966467548
+13
+158.56160777474872
+23
+108.5456607922482
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+108.5456607922482
+30
+237.1374966467548
+11
+118.74585968501914
+21
+104.99428283949231
+31
+236.18590779229618
+12
+118.74585968501914
+22
+108.5456607922482
+32
+237.1374966467548
+13
+118.74585968501914
+23
+108.5456607922482
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+236.18590779229618
+11
+158.56160777474872
+21
+108.5456607922482
+31
+237.1374966467548
+12
+158.56160777474872
+22
+104.99428283949231
+32
+236.18590779229618
+13
+158.56160777474872
+23
+104.99428283949231
+33
+236.18590779229618
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+111.14544989054545
+30
+246.84004165056382
+11
+158.56160777474872
+21
+112.09703874500408
+31
+243.28866369780795
+12
+118.74585968501914
+22
+112.09703874500408
+32
+243.28866369780795
+13
+118.74585968501914
+23
+112.09703874500408
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+112.09703874500408
+30
+243.28866369780795
+11
+118.74585968501914
+21
+111.14544989054545
+31
+246.84004165056382
+12
+158.56160777474872
+22
+111.14544989054545
+32
+246.84004165056382
+13
+158.56160777474872
+23
+111.14544989054545
+33
+246.84004165056382
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+98.84311578843918
+30
+239.73728574505208
+11
+118.74585968501914
+21
+101.44290488673643
+31
+237.1374966467548
+12
+158.56160777474872
+22
+101.44290488673643
+32
+237.1374966467548
+13
+158.56160777474872
+23
+101.44290488673643
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+101.44290488673643
+30
+237.1374966467548
+11
+158.56160777474872
+21
+98.84311578843918
+31
+239.73728574505208
+12
+118.74585968501914
+22
+98.84311578843918
+32
+239.73728574505208
+13
+118.74585968501914
+23
+98.84311578843918
+33
+239.73728574505208
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+101.44290488673643
+30
+249.43983074886108
+11
+118.74585968501914
+21
+104.99428283949231
+31
+250.39141960331972
+12
+118.74585968501914
+22
+101.44290488673643
+32
+249.43983074886108
+13
+118.74585968501914
+23
+101.44290488673643
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+250.39141960331972
+11
+158.56160777474872
+21
+101.44290488673643
+31
+249.43983074886108
+12
+158.56160777474872
+22
+104.99428283949231
+32
+250.39141960331972
+13
+158.56160777474872
+23
+104.99428283949231
+33
+250.39141960331972
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+97.89152693398054
+30
+243.28866369780795
+11
+118.74585968501914
+21
+98.84311578843918
+31
+239.73728574505208
+12
+158.56160777474872
+22
+98.84311578843918
+32
+239.73728574505208
+13
+158.56160777474872
+23
+98.84311578843918
+33
+239.73728574505208
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+98.84311578843918
+30
+239.73728574505208
+11
+158.56160777474872
+21
+97.89152693398054
+31
+243.28866369780795
+12
+118.74585968501914
+22
+97.89152693398054
+32
+243.28866369780795
+13
+118.74585968501914
+23
+97.89152693398054
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+111.14544989054545
+30
+239.73728574505205
+11
+158.56160777474872
+21
+104.99428283949231
+31
+243.28866369780795
+12
+158.56160777474872
+22
+108.5456607922482
+32
+237.1374966467548
+13
+158.56160777474872
+23
+108.5456607922482
+33
+237.1374966467548
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+108.5456607922482
+30
+237.1374966467548
+11
+14.604382809215494
+21
+104.99428283949231
+31
+236.18590779229618
+12
+14.604382809215494
+22
+104.99428283949231
+32
+243.28866369780795
+13
+14.604382809215494
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+101.44290488673643
+31
+249.43983074886108
+12
+14.604382809215494
+22
+104.99428283949231
+32
+250.39141960331972
+13
+14.604382809215494
+23
+104.99428283949231
+33
+250.39141960331972
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+108.5456607922482
+30
+249.43983074886108
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+104.99428283949231
+32
+250.39141960331972
+13
+14.604382809215494
+23
+104.99428283949231
+33
+250.39141960331972
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+98.84311578843918
+31
+239.73728574505208
+12
+14.604382809215494
+22
+97.89152693398054
+32
+243.28866369780795
+13
+14.604382809215494
+23
+97.89152693398054
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+111.14544989054545
+30
+239.73728574505205
+11
+14.604382809215494
+21
+108.5456607922482
+31
+237.1374966467548
+12
+14.604382809215494
+22
+104.99428283949231
+32
+243.28866369780795
+13
+14.604382809215494
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+111.14544989054545
+30
+246.84004165056382
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+108.5456607922482
+32
+249.43983074886108
+13
+14.604382809215494
+23
+108.5456607922482
+33
+249.43983074886108
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+236.18590779229618
+11
+14.604382809215494
+21
+101.44290488673643
+31
+237.1374966467548
+12
+14.604382809215494
+22
+104.99428283949231
+32
+243.28866369780795
+13
+14.604382809215494
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+101.44290488673643
+31
+237.1374966467548
+12
+14.604382809215494
+22
+98.84311578843918
+32
+239.73728574505208
+13
+14.604382809215494
+23
+98.84311578843918
+33
+239.73728574505208
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+112.09703874500408
+30
+243.28866369780795
+11
+14.604382809215494
+21
+111.14544989054545
+31
+239.73728574505205
+12
+14.604382809215494
+22
+104.99428283949231
+32
+243.28866369780795
+13
+14.604382809215494
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+97.89152693398054
+31
+243.28866369780795
+12
+14.604382809215494
+22
+98.84311578843918
+32
+246.84004165056385
+13
+14.604382809215494
+23
+98.84311578843918
+33
+246.84004165056385
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+112.09703874500408
+30
+243.28866369780795
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+111.14544989054545
+32
+246.84004165056382
+13
+14.604382809215494
+23
+111.14544989054545
+33
+246.84004165056382
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+112.09703874500408
+30
+243.28866369780795
+11
+54.42013089894506
+21
+111.14544989054545
+31
+246.84004165056382
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+112.09703874500408
+30
+243.28866369780795
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+112.09703874500408
+32
+243.28866369780795
+13
+14.604382809215494
+23
+112.09703874500408
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+112.09703874500408
+31
+243.28866369780795
+12
+14.604382809215494
+22
+97.89152693398054
+32
+243.28866369780795
+13
+14.604382809215494
+23
+97.89152693398054
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+97.89152693398054
+30
+243.28866369780795
+11
+54.42013089894506
+21
+112.09703874500408
+31
+243.28866369780795
+12
+54.42013089894506
+22
+97.89152693398054
+32
+243.28866369780795
+13
+54.42013089894506
+23
+97.89152693398054
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+97.89152693398054
+30
+243.28866369780795
+11
+54.42013089894506
+21
+112.09703874500408
+31
+243.28866369780795
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+250.39141960331972
+11
+54.42013089894506
+21
+101.44290488673643
+31
+249.43983074886108
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+101.44290488673643
+31
+237.1374966467548
+12
+54.42013089894506
+22
+104.99428283949231
+32
+236.18590779229618
+13
+54.42013089894506
+23
+104.99428283949231
+33
+236.18590779229618
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+104.99428283949231
+31
+236.18590779229618
+12
+14.604382809215494
+22
+104.99428283949231
+32
+236.18590779229618
+13
+14.604382809215494
+23
+104.99428283949231
+33
+236.18590779229618
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+236.18590779229618
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+104.99428283949231
+32
+250.39141960331972
+13
+14.604382809215494
+23
+104.99428283949231
+33
+250.39141960331972
+70
+13
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+104.99428283949231
+31
+250.39141960331972
+12
+54.42013089894506
+22
+104.99428283949231
+32
+250.39141960331972
+13
+54.42013089894506
+23
+104.99428283949231
+33
+250.39141960331972
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+101.44290488673643
+31
+249.43983074886108
+12
+54.42013089894506
+22
+98.84311578843918
+32
+246.84004165056385
+13
+54.42013089894506
+23
+98.84311578843918
+33
+246.84004165056385
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+108.5456607922482
+30
+237.1374966467548
+11
+54.42013089894506
+21
+104.99428283949231
+31
+243.28866369780795
+12
+54.42013089894506
+22
+104.99428283949231
+32
+236.18590779229618
+13
+54.42013089894506
+23
+104.99428283949231
+33
+236.18590779229618
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+108.5456607922482
+31
+237.1374966467548
+12
+54.42013089894506
+22
+108.5456607922482
+32
+237.1374966467548
+13
+54.42013089894506
+23
+108.5456607922482
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+108.5456607922482
+30
+237.1374966467548
+11
+54.42013089894506
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+104.99428283949231
+32
+243.28866369780795
+13
+14.604382809215494
+23
+104.99428283949231
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+104.99428283949231
+31
+243.28866369780795
+12
+54.42013089894506
+22
+101.44290488673643
+32
+249.43983074886108
+13
+54.42013089894506
+23
+101.44290488673643
+33
+249.43983074886108
+70
+13
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+101.44290488673643
+31
+249.43983074886108
+12
+14.604382809215494
+22
+101.44290488673643
+32
+249.43983074886108
+13
+14.604382809215494
+23
+101.44290488673643
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+111.14544989054545
+30
+239.73728574505205
+11
+54.42013089894506
+21
+104.99428283949231
+31
+243.28866369780795
+12
+54.42013089894506
+22
+108.5456607922482
+32
+237.1374966467548
+13
+54.42013089894506
+23
+108.5456607922482
+33
+237.1374966467548
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+98.84311578843918
+31
+246.84004165056385
+12
+54.42013089894506
+22
+97.89152693398054
+32
+243.28866369780795
+13
+54.42013089894506
+23
+97.89152693398054
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+111.14544989054545
+30
+239.73728574505205
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+111.14544989054545
+32
+239.73728574505205
+13
+14.604382809215494
+23
+111.14544989054545
+33
+239.73728574505205
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+111.14544989054545
+31
+239.73728574505205
+12
+14.604382809215494
+22
+98.84311578843918
+32
+246.84004165056385
+13
+14.604382809215494
+23
+98.84311578843918
+33
+246.84004165056385
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+98.84311578843918
+30
+246.84004165056385
+11
+54.42013089894506
+21
+111.14544989054545
+31
+239.73728574505205
+12
+54.42013089894506
+22
+98.84311578843918
+32
+246.84004165056385
+13
+54.42013089894506
+23
+98.84311578843918
+33
+246.84004165056385
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+98.84311578843918
+30
+246.84004165056385
+11
+54.42013089894506
+21
+111.14544989054545
+31
+239.73728574505205
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+97.89152693398054
+31
+243.28866369780795
+12
+54.42013089894506
+22
+98.84311578843918
+32
+239.73728574505208
+13
+54.42013089894506
+23
+98.84311578843918
+33
+239.73728574505208
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+111.14544989054545
+30
+246.84004165056382
+11
+54.42013089894506
+21
+108.5456607922482
+31
+249.43983074886108
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+111.14544989054545
+30
+246.84004165056382
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+111.14544989054545
+32
+246.84004165056382
+13
+14.604382809215494
+23
+111.14544989054545
+33
+246.84004165056382
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+111.14544989054545
+31
+246.84004165056382
+12
+14.604382809215494
+22
+98.84311578843918
+32
+239.73728574505208
+13
+14.604382809215494
+23
+98.84311578843918
+33
+239.73728574505208
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+98.84311578843918
+30
+239.73728574505208
+11
+54.42013089894506
+21
+111.14544989054545
+31
+246.84004165056382
+12
+54.42013089894506
+22
+98.84311578843918
+32
+239.73728574505208
+13
+54.42013089894506
+23
+98.84311578843918
+33
+239.73728574505208
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+98.84311578843918
+30
+239.73728574505208
+11
+54.42013089894506
+21
+111.14544989054545
+31
+246.84004165056382
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+98.84311578843918
+31
+239.73728574505208
+12
+54.42013089894506
+22
+101.44290488673643
+32
+237.1374966467548
+13
+54.42013089894506
+23
+101.44290488673643
+33
+237.1374966467548
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+108.5456607922482
+30
+249.43983074886108
+11
+54.42013089894506
+21
+104.99428283949231
+31
+250.39141960331972
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+14.604382809215494
+20
+104.99428283949231
+30
+243.28866369780795
+11
+54.42013089894506
+21
+101.44290488673643
+31
+237.1374966467548
+12
+14.604382809215494
+22
+101.44290488673643
+32
+237.1374966467548
+13
+14.604382809215494
+23
+101.44290488673643
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+101.44290488673643
+30
+237.1374966467548
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+54.42013089894506
+22
+104.99428283949231
+32
+243.28866369780795
+13
+54.42013089894506
+23
+104.99428283949231
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+104.99428283949231
+31
+243.28866369780795
+12
+14.604382809215494
+22
+108.5456607922482
+32
+249.43983074886108
+13
+14.604382809215494
+23
+108.5456607922482
+33
+249.43983074886108
+70
+13
+ 0
+3DFACE
+ 8
+hinges_half
+10
+54.42013089894506
+20
+104.99428283949231
+30
+243.28866369780795
+11
+14.604382809215494
+21
+108.5456607922482
+31
+249.43983074886108
+12
+54.42013089894506
+22
+108.5456607922482
+32
+249.43983074886108
+13
+54.42013089894506
+23
+108.5456607922482
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+108.5456607922482
+30
+237.1374966467548
+11
+118.74585968501914
+21
+104.99428283949231
+31
+236.18590779229618
+12
+118.74585968501914
+22
+104.99428283949231
+32
+243.28866369780795
+13
+118.74585968501914
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+101.44290488673643
+31
+249.43983074886108
+12
+118.74585968501914
+22
+104.99428283949231
+32
+250.39141960331972
+13
+118.74585968501914
+23
+104.99428283949231
+33
+250.39141960331972
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+111.14544989054545
+30
+239.73728574505205
+11
+118.74585968501914
+21
+108.5456607922482
+31
+237.1374966467548
+12
+118.74585968501914
+22
+104.99428283949231
+32
+243.28866369780795
+13
+118.74585968501914
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+112.09703874500408
+30
+243.28866369780795
+11
+118.74585968501914
+21
+111.14544989054545
+31
+239.73728574505205
+12
+118.74585968501914
+22
+104.99428283949231
+32
+243.28866369780795
+13
+118.74585968501914
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+98.84311578843918
+31
+246.84004165056385
+12
+118.74585968501914
+22
+101.44290488673643
+32
+249.43983074886108
+13
+118.74585968501914
+23
+101.44290488673643
+33
+249.43983074886108
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+112.09703874500408
+30
+243.28866369780795
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+111.14544989054545
+32
+246.84004165056382
+13
+118.74585968501914
+23
+111.14544989054545
+33
+246.84004165056382
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+97.89152693398054
+31
+243.28866369780795
+12
+118.74585968501914
+22
+98.84311578843918
+32
+246.84004165056385
+13
+118.74585968501914
+23
+98.84311578843918
+33
+246.84004165056385
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+108.5456607922482
+30
+249.43983074886108
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+104.99428283949231
+32
+250.39141960331972
+13
+118.74585968501914
+23
+104.99428283949231
+33
+250.39141960331972
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+98.84311578843918
+31
+239.73728574505208
+12
+118.74585968501914
+22
+97.89152693398054
+32
+243.28866369780795
+13
+118.74585968501914
+23
+97.89152693398054
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+236.18590779229618
+11
+118.74585968501914
+21
+101.44290488673643
+31
+237.1374966467548
+12
+118.74585968501914
+22
+104.99428283949231
+32
+243.28866369780795
+13
+118.74585968501914
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+111.14544989054545
+30
+246.84004165056382
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+108.5456607922482
+32
+249.43983074886108
+13
+118.74585968501914
+23
+108.5456607922482
+33
+249.43983074886108
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+101.44290488673643
+31
+237.1374966467548
+12
+158.56160777474872
+22
+104.99428283949231
+32
+236.18590779229618
+13
+158.56160777474872
+23
+104.99428283949231
+33
+236.18590779229618
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+104.99428283949231
+31
+236.18590779229618
+12
+118.74585968501914
+22
+104.99428283949231
+32
+236.18590779229618
+13
+118.74585968501914
+23
+104.99428283949231
+33
+236.18590779229618
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+236.18590779229618
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+104.99428283949231
+32
+250.39141960331972
+13
+118.74585968501914
+23
+104.99428283949231
+33
+250.39141960331972
+70
+13
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+104.99428283949231
+31
+250.39141960331972
+12
+158.56160777474872
+22
+104.99428283949231
+32
+250.39141960331972
+13
+158.56160777474872
+23
+104.99428283949231
+33
+250.39141960331972
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+108.5456607922482
+30
+249.43983074886108
+11
+158.56160777474872
+21
+104.99428283949231
+31
+250.39141960331972
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+98.84311578843918
+31
+239.73728574505208
+12
+158.56160777474872
+22
+101.44290488673643
+32
+237.1374966467548
+13
+158.56160777474872
+23
+101.44290488673643
+33
+237.1374966467548
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+101.44290488673643
+31
+237.1374966467548
+12
+118.74585968501914
+22
+101.44290488673643
+32
+237.1374966467548
+13
+118.74585968501914
+23
+101.44290488673643
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+101.44290488673643
+30
+237.1374966467548
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+108.5456607922482
+32
+249.43983074886108
+13
+118.74585968501914
+23
+108.5456607922482
+33
+249.43983074886108
+70
+13
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+108.5456607922482
+31
+249.43983074886108
+12
+158.56160777474872
+22
+108.5456607922482
+32
+249.43983074886108
+13
+158.56160777474872
+23
+108.5456607922482
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+97.89152693398054
+31
+243.28866369780795
+12
+158.56160777474872
+22
+98.84311578843918
+32
+239.73728574505208
+13
+158.56160777474872
+23
+98.84311578843918
+33
+239.73728574505208
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+111.14544989054545
+30
+246.84004165056382
+11
+158.56160777474872
+21
+108.5456607922482
+31
+249.43983074886108
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+111.14544989054545
+30
+246.84004165056382
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+111.14544989054545
+32
+246.84004165056382
+13
+118.74585968501914
+23
+111.14544989054545
+33
+246.84004165056382
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+111.14544989054545
+31
+246.84004165056382
+12
+118.74585968501914
+22
+98.84311578843918
+32
+239.73728574505208
+13
+118.74585968501914
+23
+98.84311578843918
+33
+239.73728574505208
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+98.84311578843918
+30
+239.73728574505208
+11
+158.56160777474872
+21
+111.14544989054545
+31
+246.84004165056382
+12
+158.56160777474872
+22
+98.84311578843918
+32
+239.73728574505208
+13
+158.56160777474872
+23
+98.84311578843918
+33
+239.73728574505208
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+98.84311578843918
+30
+239.73728574505208
+11
+158.56160777474872
+21
+111.14544989054545
+31
+246.84004165056382
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+250.39141960331972
+11
+158.56160777474872
+21
+101.44290488673643
+31
+249.43983074886108
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+112.09703874500408
+30
+243.28866369780795
+11
+158.56160777474872
+21
+111.14544989054545
+31
+246.84004165056382
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+112.09703874500408
+30
+243.28866369780795
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+112.09703874500408
+32
+243.28866369780795
+13
+118.74585968501914
+23
+112.09703874500408
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+112.09703874500408
+31
+243.28866369780795
+12
+118.74585968501914
+22
+97.89152693398054
+32
+243.28866369780795
+13
+118.74585968501914
+23
+97.89152693398054
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+97.89152693398054
+30
+243.28866369780795
+11
+158.56160777474872
+21
+112.09703874500408
+31
+243.28866369780795
+12
+158.56160777474872
+22
+97.89152693398054
+32
+243.28866369780795
+13
+158.56160777474872
+23
+97.89152693398054
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+97.89152693398054
+30
+243.28866369780795
+11
+158.56160777474872
+21
+112.09703874500408
+31
+243.28866369780795
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+108.5456607922482
+30
+237.1374966467548
+11
+158.56160777474872
+21
+104.99428283949231
+31
+243.28866369780795
+12
+158.56160777474872
+22
+104.99428283949231
+32
+236.18590779229618
+13
+158.56160777474872
+23
+104.99428283949231
+33
+236.18590779229618
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+98.84311578843918
+31
+246.84004165056385
+12
+158.56160777474872
+22
+97.89152693398054
+32
+243.28866369780795
+13
+158.56160777474872
+23
+97.89152693398054
+33
+243.28866369780795
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+118.74585968501914
+21
+108.5456607922482
+31
+237.1374966467548
+12
+158.56160777474872
+22
+108.5456607922482
+32
+237.1374966467548
+13
+158.56160777474872
+23
+108.5456607922482
+33
+237.1374966467548
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+108.5456607922482
+30
+237.1374966467548
+11
+158.56160777474872
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+104.99428283949231
+32
+243.28866369780795
+13
+118.74585968501914
+23
+104.99428283949231
+33
+243.28866369780795
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+104.99428283949231
+31
+243.28866369780795
+12
+158.56160777474872
+22
+101.44290488673643
+32
+249.43983074886108
+13
+158.56160777474872
+23
+101.44290488673643
+33
+249.43983074886108
+70
+13
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+101.44290488673643
+31
+249.43983074886108
+12
+118.74585968501914
+22
+101.44290488673643
+32
+249.43983074886108
+13
+118.74585968501914
+23
+101.44290488673643
+33
+249.43983074886108
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+101.44290488673643
+31
+249.43983074886108
+12
+158.56160777474872
+22
+98.84311578843918
+32
+246.84004165056385
+13
+158.56160777474872
+23
+98.84311578843918
+33
+246.84004165056385
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+112.09703874500408
+30
+243.28866369780795
+11
+158.56160777474872
+21
+104.99428283949231
+31
+243.28866369780795
+12
+158.56160777474872
+22
+111.14544989054545
+32
+239.73728574505205
+13
+158.56160777474872
+23
+111.14544989054545
+33
+239.73728574505205
+70
+0
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+98.84311578843918
+30
+246.84004165056385
+11
+118.74585968501914
+21
+104.99428283949231
+31
+243.28866369780795
+12
+118.74585968501914
+22
+98.84311578843918
+32
+246.84004165056385
+13
+118.74585968501914
+23
+98.84311578843918
+33
+246.84004165056385
+70
+1
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+104.99428283949231
+30
+243.28866369780795
+11
+158.56160777474872
+21
+98.84311578843918
+31
+246.84004165056385
+12
+118.74585968501914
+22
+111.14544989054545
+32
+239.73728574505205
+13
+118.74585968501914
+23
+111.14544989054545
+33
+239.73728574505205
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+118.74585968501914
+20
+111.14544989054545
+30
+239.73728574505205
+11
+158.56160777474872
+21
+98.84311578843918
+31
+246.84004165056385
+12
+158.56160777474872
+22
+111.14544989054545
+32
+239.73728574505205
+13
+158.56160777474872
+23
+111.14544989054545
+33
+239.73728574505205
+70
+3
+ 0
+3DFACE
+ 8
+hinges_half
+10
+158.56160777474872
+20
+111.14544989054545
+30
+239.73728574505205
+11
+158.56160777474872
+21
+98.84311578843918
+31
+246.84004165056385
+12
+158.56160777474872
+22
+104.99428283949231
+32
+243.28866369780795
+13
+158.56160777474872
+23
+104.99428283949231
+33
+243.28866369780795
+70
+1
+ 0
+LINE
+ 8
+lid
+10
+173.57615198054154
+20
+89.62086612360419
+30
+247.01850393700798
+11
+173.57615198059867
+21
+89.61894967981907
+31
+247.0180258490584
+ 0
+LINE
+ 8
+lid
+10
+173.5761520091809
+20
+101.88267226235872
+30
+231.96338551235425
+11
+173.62378941037633
+21
+101.88267714864598
+31
+231.96338582677174
+ 0
+3DFACE
+ 8
+lid
+10
+155.25962204721512
+20
+-94.91475962800925
+30
+200.98174278680736
+11
+173.40095962196486
+21
+-61.09528078179717
+31
+209.41850264783722
+12
+155.25962103928768
+22
+-61.09527615457002
+32
+209.41889763779537
+13
+155.25962103928768
+23
+-61.09527615457002
+33
+209.41889763779537
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095962196486
+20
+-61.09528078179717
+30
+209.41850264783722
+11
+155.25962204721512
+21
+-94.91475962800925
+31
+200.98174278680736
+12
+155.2596220472446
+22
+-94.91574859551494
+32
+200.98149606299222
+13
+155.2596220472446
+23
+-94.91574859551494
+33
+200.98149606299222
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095962196486
+20
+-61.09528078179717
+30
+209.41850264783722
+11
+155.2596220472446
+21
+-94.91574859551494
+31
+200.98149606299222
+12
+173.40096062992177
+22
+-94.91575322274208
+32
+200.98149477382145
+13
+173.40096062992177
+23
+-94.91575322274208
+33
+200.98149477382145
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758553285672875
+20
+98.66928670640114
+30
+244.8444881889765
+11
+18.758559055118518
+21
+-94.91575266367778
+31
+200.98149606299222
+12
+18.75855926275502
+22
+-101.88268179753607
+32
+194.81220472440953
+13
+18.75855926275502
+23
+-101.88268179753607
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758559055118518
+20
+-94.91575266367778
+30
+200.98149606299222
+11
+18.758553285672875
+21
+98.66928670640114
+31
+244.8444881889765
+12
+18.758558047161614
+22
+-61.09528022273286
+32
+209.41889763779537
+13
+18.758558047161614
+23
+-61.09528022273286
+33
+209.41889763779537
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+18.758558047161614
+20
+-61.09528022273286
+30
+209.41889763779537
+11
+18.758553285672875
+21
+98.66928670640114
+31
+244.8444881889765
+12
+18.758554555475172
+22
+56.06298749380262
+32
+238.6468503937009
+13
+18.758554555475172
+23
+56.06298749380262
+33
+238.6468503937009
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+18.758554555475172
+20
+56.06298749380262
+30
+238.6468503937009
+11
+18.758553285672875
+21
+98.66928670640114
+31
+244.8444881889765
+12
+18.758553555344527
+22
+89.62086150955072
+32
+247.01850393700798
+13
+18.758553555344527
+23
+89.62086150955072
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+54.371033870478705
+20
+89.61503930110042
+30
+247.01705118848423
+11
+68.94111261046264
+21
+89.62086300514947
+31
+247.01850393700798
+12
+54.371033870305155
+22
+89.62086257091512
+32
+247.01850393700798
+13
+54.371033870305155
+23
+89.62086257091512
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111261046264
+20
+89.62086300514947
+30
+247.01850393700798
+11
+54.371033870478705
+21
+89.61503930110042
+31
+247.01705118848423
+12
+54.371033870788715
+22
+89.6046374506915
+32
+247.01445620777636
+13
+54.371033870788715
+23
+89.6046374506915
+33
+247.01445620777636
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111261046264
+20
+89.62086300514947
+30
+247.01850393700798
+11
+54.371033870788715
+21
+89.6046374506915
+31
+247.01445620777636
+12
+54.371038362122235
+22
+-61.09527916136846
+32
+209.41889763779537
+13
+54.371038362122235
+23
+-61.09527916136846
+33
+209.41889763779537
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111261046264
+20
+89.62086300514947
+30
+247.01850393700798
+11
+54.371038362122235
+21
+-61.09527916136846
+31
+209.41889763779537
+12
+54.371039370079146
+22
+-94.91575160231338
+32
+200.98149606299222
+13
+54.371039370079146
+23
+-94.91575160231338
+33
+200.98149606299222
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111261046264
+20
+89.62086300514947
+30
+247.01850393700798
+11
+54.371039370079146
+21
+-94.91575160231338
+31
+200.98149606299222
+12
+68.94111261059379
+22
+89.61646217847384
+32
+247.01740604706768
+13
+68.94111261059379
+23
+89.61646217847384
+33
+247.01740604706768
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111261059379
+20
+89.61646217847384
+30
+247.01740604706768
+11
+54.371039370079146
+21
+-94.91575160231338
+31
+200.98149606299222
+12
+68.9411181101801
+22
+-94.91385437913766
+32
+200.98196926170036
+13
+68.9411181101801
+23
+-94.91385437913766
+33
+200.98196926170036
+70
+15
+ 0
+3DFACE
+ 8
+lid
+10
+68.9411181101801
+20
+-94.91385437913766
+30
+200.98196926170036
+11
+54.371039370079146
+21
+-94.91575160231338
+31
+200.98149606299222
+12
+68.94111811023663
+22
+-94.91575116807901
+32
+200.98149606299222
+13
+68.94111811023663
+23
+-94.91575116807901
+33
+200.98149606299222
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111261059379
+20
+89.61646217847384
+30
+247.01740604706768
+11
+68.9411181101801
+21
+-94.91385437913766
+31
+200.98196926170036
+12
+68.94111261077951
+22
+89.61023080877725
+32
+247.01585148502548
+13
+68.94111261077951
+23
+89.61023080877725
+33
+247.01585148502548
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-5.499773591655099e-06
+20
+89.62086095048643
+30
+247.01850393700798
+11
+18.758553285672875
+21
+98.66928670640114
+31
+244.8444881889765
+12
+-5.769445243331006e-06
+22
+98.66928614733685
+32
+244.8444881889765
+13
+-5.769445243331006e-06
+23
+98.66928614733685
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758553285672875
+20
+98.66928670640114
+30
+244.8444881889765
+11
+-5.499773591655099e-06
+21
+89.62086095048643
+31
+247.01850393700798
+12
+18.758553555344527
+22
+89.62086150955072
+32
+247.01850393700798
+13
+18.758553555344527
+23
+89.62086150955072
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+2.0763689256853013e-07
+20
+-101.88268235660034
+30
+194.81220472440953
+11
+18.758559055118518
+21
+-94.91575266367778
+31
+200.98149606299222
+12
+3.9834802123550617e-13
+22
+-94.91575322274208
+32
+200.98149606299222
+13
+3.9834802123550617e-13
+23
+-94.91575322274208
+33
+200.98149606299222
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758559055118518
+20
+-94.91575266367778
+30
+200.98149606299222
+11
+2.0763689256853013e-07
+21
+-101.88268235660034
+31
+194.81220472440953
+12
+18.75855926275502
+22
+-101.88268179753607
+32
+194.81220472440953
+13
+18.75855926275502
+23
+-101.88268179753607
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.25962087534685
+20
+-55.5944887529952
+30
+216.04960629921268
+11
+155.25961754760118
+21
+56.06299156196549
+31
+238.6468503937009
+12
+155.25962103928768
+22
+-61.09527615457002
+32
+209.41889763779537
+13
+155.25962103928768
+23
+-61.09527615457002
+33
+209.41889763779537
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961754760118
+20
+56.06299156196549
+30
+238.6468503937009
+11
+155.25962087534685
+21
+-55.5944887529952
+31
+216.04960629921268
+12
+155.25961785266122
+22
+45.827164790311954
+32
+241.3515748031497
+13
+155.25961785266122
+23
+45.827164790311954
+33
+241.3515748031497
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.2596222548811
+20
+-101.8826777293732
+30
+194.81220472440953
+11
+173.40096062992177
+21
+-94.91575322274208
+31
+200.98149477382145
+12
+155.2596220472446
+22
+-94.91574859551494
+32
+200.98149606299222
+13
+155.2596220472446
+23
+-94.91574859551494
+33
+200.98149606299222
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40096062992177
+20
+-94.91575322274208
+30
+200.98149477382145
+11
+155.2596222548811
+21
+-101.8826777293732
+31
+194.81220472440953
+12
+173.40096083755827
+22
+-101.88268235660034
+32
+194.81220343523876
+13
+173.40096083755827
+23
+-101.88268235660034
+33
+194.81220343523876
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788399433434
+20
+98.66928991200895
+30
+244.8444881889765
+11
+126.31788976377997
+21
+-94.91574945806998
+31
+200.98149606299222
+12
+126.31788997141648
+22
+-101.88267859192827
+32
+194.81220472440953
+13
+126.31788997141648
+23
+-101.88267859192827
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788976377997
+20
+-94.91574945806998
+30
+200.98149606299222
+11
+126.31788399433434
+21
+98.66928991200895
+31
+244.8444881889765
+12
+126.31788426410866
+22
+89.61741980576197
+32
+247.0176445231636
+13
+126.31788426410866
+23
+89.61741980576197
+33
+247.0176445231636
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788426410866
+20
+89.61741980576197
+30
+247.0176445231636
+11
+126.31788399433434
+21
+98.66928991200895
+31
+244.8444881889765
+12
+126.31788426406312
+22
+89.61894827418696
+32
+247.01802583563773
+13
+126.31788426406312
+23
+89.61894827418696
+33
+247.01802583563773
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788426406312
+20
+89.61894827418696
+30
+247.01802583563773
+11
+126.31788399433434
+21
+98.66928991200895
+31
+244.8444881889765
+12
+126.317884264006
+22
+89.6208647151585
+32
+247.01850393700798
+13
+126.317884264006
+23
+89.6208647151585
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+111.7745771773918
+20
+89.62086428172206
+30
+247.01850393700798
+11
+126.31788399433434
+21
+98.66928991200895
+31
+244.8444881889765
+12
+111.77457690772016
+22
+98.66928947857245
+32
+244.8444881889765
+13
+111.77457690772016
+23
+98.66928947857245
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788399433434
+20
+98.66928991200895
+30
+244.8444881889765
+11
+111.7745771773918
+21
+89.62086428172206
+31
+247.01850393700798
+12
+126.317884264006
+22
+89.6208647151585
+32
+247.01850393700798
+13
+126.317884264006
+23
+89.6208647151585
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+111.7745826771658
+20
+-94.91574989150647
+30
+200.98149606299222
+11
+111.77457690772016
+21
+98.66928947857245
+31
+244.8444881889765
+12
+111.7745828848023
+22
+-101.88267902536472
+32
+194.81220472440953
+13
+111.7745828848023
+23
+-101.88267902536472
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+111.77457690772016
+20
+98.66928947857245
+30
+244.8444881889765
+11
+111.7745826771658
+21
+-94.91574989150647
+31
+200.98149606299222
+12
+111.7745771773918
+22
+89.62086428172206
+32
+247.01850393700798
+13
+111.7745771773918
+23
+89.62086428172206
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+111.7745828848023
+20
+-101.88267902536472
+30
+194.81220472440953
+11
+126.31788976377997
+21
+-94.91574945806998
+31
+200.98149606299222
+12
+111.7745826771658
+22
+-94.91574989150647
+32
+200.98149606299222
+13
+111.7745826771658
+23
+-94.91574989150647
+33
+200.98149606299222
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788976377997
+20
+-94.91574945806998
+30
+200.98149606299222
+11
+111.7745828848023
+21
+-101.88267902536472
+31
+194.81220472440953
+12
+126.31788997141648
+22
+-101.88267859192827
+32
+194.81220472440953
+13
+126.31788997141648
+23
+-101.88267859192827
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+54.371033870305155
+20
+89.62086257091512
+30
+247.01850393700798
+11
+68.941112340791
+21
+98.66928820199992
+31
+244.8444881889765
+12
+54.37103360063351
+22
+98.66928776776554
+32
+244.8444881889765
+13
+54.37103360063351
+23
+98.66928776776554
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.941112340791
+20
+98.66928820199992
+30
+244.8444881889765
+11
+54.371033870305155
+21
+89.62086257091512
+31
+247.01850393700798
+12
+68.94111261046264
+22
+89.62086300514947
+32
+247.01850393700798
+13
+68.94111261046264
+23
+89.62086300514947
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+54.371039577715635
+20
+-101.88268073617166
+30
+194.81220472440953
+11
+68.94111811023663
+21
+-94.91575116807901
+31
+200.98149606299222
+12
+54.371039370079146
+22
+-94.91575160231338
+32
+200.98149606299222
+13
+54.371039370079146
+23
+-94.91575160231338
+33
+200.98149606299222
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111811023663
+20
+-94.91575116807901
+30
+200.98149606299222
+11
+54.371039577715635
+21
+-101.88268073617166
+31
+194.81220472440953
+12
+68.94111831787316
+22
+-101.8826803019373
+32
+194.81220472440953
+13
+68.94111831787316
+23
+-101.8826803019373
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-4.499642948463389e-06
+20
+56.06298693473833
+30
+238.6468503937009
+11
+18.758553555344527
+21
+89.62086150955072
+31
+247.01850393700798
+12
+-5.499773591655099e-06
+22
+89.62086095048643
+32
+247.01850393700798
+13
+-5.499773591655099e-06
+23
+89.62086095048643
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758553555344527
+20
+89.62086150955072
+30
+247.01850393700798
+11
+-4.499642948463389e-06
+21
+56.06298693473833
+31
+238.6468503937009
+12
+18.758554555475172
+22
+56.06298749380262
+32
+238.6468503937009
+13
+18.758554555475172
+23
+56.06298749380262
+33
+238.6468503937009
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-1.1718973480512318e-06
+20
+-55.594493380222346
+30
+216.04960629921268
+11
+18.758554860535142
+21
+45.82716072214909
+31
+241.3515748031497
+12
+-4.194582979177852e-06
+22
+45.8271601630848
+32
+241.3515748031497
+13
+-4.194582979177852e-06
+23
+45.8271601630848
+33
+241.3515748031497
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758554860535142
+20
+45.82716072214909
+30
+241.3515748031497
+11
+-1.1718973480512318e-06
+21
+-55.594493380222346
+31
+216.04960629921268
+12
+18.758557883220774
+22
+-55.594492821158035
+32
+216.04960629921268
+13
+18.758557883220774
+23
+-55.594492821158035
+33
+216.04960629921268
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961654757327
+20
+89.61742066705047
+30
+247.01764453815136
+11
+173.40095513014776
+21
+89.62086095048643
+31
+247.0185026478372
+12
+155.2596165474706
+22
+89.62086557771359
+32
+247.01850393700798
+13
+155.2596165474706
+23
+89.62086557771359
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095513014776
+20
+89.62086095048643
+30
+247.0185026478372
+11
+155.25961654757327
+21
+89.61742066705047
+31
+247.01764453815136
+12
+155.25961654768776
+22
+89.61358009666061
+32
+247.01668643440806
+13
+155.25961654768776
+23
+89.61358009666061
+33
+247.01668643440806
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095513014776
+20
+89.62086095048643
+30
+247.0185026478372
+11
+155.25961654768776
+21
+89.61358009666061
+31
+247.01668643440806
+12
+155.2596165477875
+22
+89.61023337167238
+32
+247.01585152984606
+13
+155.2596165477875
+23
+89.61023337167238
+33
+247.01585152984606
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095513014776
+20
+89.62086095048643
+30
+247.0185026478372
+11
+155.2596165477875
+21
+89.61023337167238
+31
+247.01585152984606
+12
+155.25961654795418
+22
+89.60464044290792
+32
+247.0144562667884
+13
+155.25961654795418
+23
+89.60464044290792
+33
+247.0144562667884
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095513014776
+20
+89.62086095048643
+30
+247.0185026478372
+11
+155.25961654795418
+21
+89.60464044290792
+31
+247.0144562667884
+12
+155.25961754760118
+22
+56.06299156196549
+32
+238.6468503937009
+13
+155.25961754760118
+23
+56.06299156196549
+33
+238.6468503937009
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095513014776
+20
+89.62086095048643
+30
+247.0185026478372
+11
+155.25961754760118
+21
+56.06299156196549
+31
+238.6468503937009
+12
+173.40095613027842
+22
+56.06298693473833
+32
+238.64684910453013
+13
+173.40095613027842
+23
+56.06298693473833
+33
+238.64684910453013
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758558047161614
+20
+-61.09528022273286
+30
+209.41889763779537
+11
+-1.1718973480512318e-06
+21
+-55.594493380222346
+31
+216.04960629921268
+12
+-1.007956508480845e-06
+22
+-61.09528078179717
+32
+209.41850393700798
+13
+-1.007956508480845e-06
+23
+-61.09528078179717
+33
+209.41850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-1.1718973480512318e-06
+20
+-55.594493380222346
+30
+216.04960629921268
+11
+18.758558047161614
+21
+-61.09528022273286
+31
+209.41889763779537
+12
+18.758557883220774
+22
+-55.594492821158035
+32
+216.04960629921268
+13
+18.758557883220774
+23
+-55.594492821158035
+33
+216.04960629921268
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-4.194582979177852e-06
+20
+45.8271601630848
+30
+241.3515748031497
+11
+18.758554555475172
+21
+56.06298749380262
+31
+238.6468503937009
+12
+-4.499642948463389e-06
+22
+56.06298693473833
+32
+238.6468503937009
+13
+-4.499642948463389e-06
+23
+56.06298693473833
+33
+238.6468503937009
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758554555475172
+20
+56.06298749380262
+30
+238.6468503937009
+11
+-4.194582979177852e-06
+21
+45.8271601630848
+31
+241.3515748031497
+12
+18.758554860535142
+22
+45.82716072214909
+32
+241.3515748031497
+13
+18.758554860535142
+23
+45.82716072214909
+33
+241.3515748031497
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+3.9834802123550617e-13
+20
+-94.91575322274208
+30
+200.98149606299222
+11
+18.758558047161614
+21
+-61.09528022273286
+31
+209.41889763779537
+12
+-1.007956508480845e-06
+22
+-61.09528078179717
+32
+209.41850393700798
+13
+-1.007956508480845e-06
+23
+-61.09528078179717
+33
+209.41850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758558047161614
+20
+-61.09528022273286
+30
+209.41889763779537
+11
+3.9834802123550617e-13
+21
+-94.91575322274208
+31
+200.98149606299222
+12
+18.758559055118518
+22
+-94.91575266367778
+32
+200.98149606299222
+13
+18.758559055118518
+23
+-94.91575266367778
+33
+200.98149606299222
+70
+1
+ 0
+LINE
+ 8
+lid
+10
+173.57615198059867
+20
+89.61894967981907
+30
+247.0180258490584
+11
+173.57615198063553
+21
+89.61771272440437
+31
+247.01771727049086
+ 0
+LINE
+ 8
+lid
+10
+173.57615198063553
+20
+89.61771272440437
+30
+247.01771727049086
+11
+173.5761519806727
+21
+89.61646528989151
+31
+247.01740607774255
+ 0
+LINE
+ 8
+lid
+10
+173.5761519806727
+20
+89.61646528989151
+30
+247.01740607774255
+11
+173.57615198071508
+21
+89.61504284452782
+31
+247.01705122570223
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961785266122
+20
+45.827164790311954
+30
+241.3515748031497
+11
+173.40095613027842
+21
+56.06298693473833
+31
+238.64684910453013
+12
+155.25961754760118
+22
+56.06299156196549
+32
+238.6468503937009
+13
+155.25961754760118
+23
+56.06299156196549
+33
+238.6468503937009
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095613027842
+20
+56.06298693473833
+30
+238.64684910453013
+11
+155.25961785266122
+21
+45.827164790311954
+31
+241.3515748031497
+12
+173.4009564353384
+22
+45.8271601630848
+32
+241.35157351397893
+13
+173.4009564353384
+23
+45.8271601630848
+33
+241.35157351397893
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.2596165474706
+20
+89.62086557771359
+30
+247.01850393700798
+11
+173.40095486047613
+21
+98.66928614733685
+31
+244.84448689980573
+12
+155.25961627779895
+22
+98.66929077456398
+32
+244.8444881889765
+13
+155.25961627779895
+23
+98.66929077456398
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095486047613
+20
+98.66928614733685
+30
+244.84448689980573
+11
+155.2596165474706
+21
+89.62086557771359
+31
+247.01850393700798
+12
+173.40095513014776
+22
+89.62086095048643
+32
+247.0185026478372
+13
+173.40095513014776
+23
+89.62086095048643
+33
+247.0185026478372
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.25962087534685
+20
+-55.5944887529952
+30
+216.04960629921268
+11
+173.4009564353384
+21
+45.8271601630848
+31
+241.35157351397893
+12
+155.25961785266122
+22
+45.827164790311954
+32
+241.3515748031497
+13
+155.25961785266122
+23
+45.827164790311954
+33
+241.3515748031497
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.4009564353384
+20
+45.8271601630848
+30
+241.35157351397893
+11
+155.25962087534685
+21
+-55.5944887529952
+31
+216.04960629921268
+12
+173.40095945802403
+22
+-55.594493380222346
+32
+216.04960501004192
+13
+173.40095945802403
+23
+-55.594493380222346
+33
+216.04960501004192
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095945802403
+20
+-55.594493380222346
+30
+216.04960501004192
+11
+155.25962103928768
+21
+-61.09527615457002
+31
+209.41889763779537
+12
+173.40095962196486
+22
+-61.09528078179717
+32
+209.41850264783722
+13
+173.40095962196486
+23
+-61.09528078179717
+33
+209.41850264783722
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.25962103928768
+20
+-61.09527615457002
+30
+209.41889763779537
+11
+173.40095945802403
+21
+-55.594493380222346
+31
+216.04960501004192
+12
+155.25962087534685
+22
+-55.5944887529952
+32
+216.04960629921268
+13
+155.25962087534685
+23
+-55.5944887529952
+33
+216.04960629921268
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.941112340791
+20
+98.66928820199992
+30
+244.8444881889765
+11
+68.94111811023663
+21
+-94.91575116807901
+31
+200.98149606299222
+12
+68.94111831787316
+22
+-101.8826803019373
+32
+194.81220472440953
+13
+68.94111831787316
+23
+-101.8826803019373
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111811023663
+20
+-94.91575116807901
+30
+200.98149606299222
+11
+68.941112340791
+21
+98.66928820199992
+31
+244.8444881889765
+12
+68.9411181101801
+22
+-94.91385437913766
+32
+200.98196926170036
+13
+68.9411181101801
+23
+-94.91385437913766
+33
+200.98196926170036
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+68.9411181101801
+20
+-94.91385437913766
+30
+200.98196926170036
+11
+68.941112340791
+21
+98.66928820199992
+31
+244.8444881889765
+12
+68.94111261077951
+22
+89.61023080877725
+32
+247.01585148502548
+13
+68.94111261077951
+23
+89.61023080877725
+33
+247.01585148502548
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111261077951
+20
+89.61023080877725
+30
+247.01585148502548
+11
+68.941112340791
+21
+98.66928820199992
+31
+244.8444881889765
+12
+68.94111261059379
+22
+89.61646217847384
+32
+247.01740604706768
+13
+68.94111261059379
+23
+89.61646217847384
+33
+247.01740604706768
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111261059379
+20
+89.61646217847384
+30
+247.01740604706768
+11
+68.941112340791
+21
+98.66928820199992
+31
+244.8444881889765
+12
+68.94111261046264
+22
+89.62086300514947
+32
+247.01850393700798
+13
+68.94111261046264
+23
+89.62086300514947
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+54.371039370079146
+20
+-94.91575160231338
+30
+200.98149606299222
+11
+54.37103360063351
+21
+98.66928776776554
+31
+244.8444881889765
+12
+54.371039577715635
+22
+-101.88268073617166
+32
+194.81220472440953
+13
+54.371039577715635
+23
+-101.88268073617166
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+54.37103360063351
+20
+98.66928776776554
+30
+244.8444881889765
+11
+54.371039370079146
+21
+-94.91575160231338
+31
+200.98149606299222
+12
+54.371038362122235
+22
+-61.09527916136846
+32
+209.41889763779537
+13
+54.371038362122235
+23
+-61.09527916136846
+33
+209.41889763779537
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+54.37103360063351
+20
+98.66928776776554
+30
+244.8444881889765
+11
+54.371038362122235
+21
+-61.09527916136846
+31
+209.41889763779537
+12
+54.371033870788715
+22
+89.6046374506915
+32
+247.01445620777636
+13
+54.371033870788715
+23
+89.6046374506915
+33
+247.01445620777636
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+54.37103360063351
+20
+98.66928776776554
+30
+244.8444881889765
+11
+54.371033870788715
+21
+89.6046374506915
+31
+247.01445620777636
+12
+54.371033870478705
+22
+89.61503930110042
+32
+247.01705118848423
+13
+54.371033870478705
+23
+89.61503930110042
+33
+247.01705118848423
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+54.37103360063351
+20
+98.66928776776554
+30
+244.8444881889765
+11
+54.371033870478705
+21
+89.61503930110042
+31
+247.01705118848423
+12
+54.371033870305155
+22
+89.62086257091512
+32
+247.01850393700798
+13
+54.371033870305155
+23
+89.62086257091512
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40096083755827
+20
+-101.88268235660034
+30
+194.81220343523876
+11
+1.7082673453572284e-07
+21
+-100.64757308805667
+31
+189.86102362204733
+12
+173.4009608007481
+22
+-100.64757308805667
+32
+189.86102233287656
+13
+173.4009608007481
+23
+-100.64757308805667
+33
+189.86102233287656
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+1.7082673453572284e-07
+20
+-100.64757308805667
+30
+189.86102362204733
+11
+173.40096083755827
+21
+-101.88268235660034
+31
+194.81220343523876
+12
+2.0763689256853013e-07
+22
+-101.88268235660034
+32
+194.81220472440953
+13
+2.0763689256853013e-07
+23
+-101.88268235660034
+33
+194.81220472440953
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+2.0763689256853013e-07
+20
+-101.88268235660034
+30
+194.81220472440953
+11
+173.40096083755827
+21
+-101.88268235660034
+31
+194.81220343523876
+12
+155.2596222548811
+22
+-101.8826777293732
+32
+194.81220472440953
+13
+155.2596222548811
+23
+-101.8826777293732
+33
+194.81220472440953
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+2.0763689256853013e-07
+20
+-101.88268235660034
+30
+194.81220472440953
+11
+155.2596222548811
+21
+-101.8826777293732
+31
+194.81220472440953
+12
+126.31788997141648
+22
+-101.88267859192827
+32
+194.81220472440953
+13
+126.31788997141648
+23
+-101.88267859192827
+33
+194.81220472440953
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+2.0763689256853013e-07
+20
+-101.88268235660034
+30
+194.81220472440953
+11
+126.31788997141648
+21
+-101.88267859192827
+31
+194.81220472440953
+12
+111.7745828848023
+22
+-101.88267902536472
+32
+194.81220472440953
+13
+111.7745828848023
+23
+-101.88267902536472
+33
+194.81220472440953
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+2.0763689256853013e-07
+20
+-101.88268235660034
+30
+194.81220472440953
+11
+111.7745828848023
+21
+-101.88267902536472
+31
+194.81220472440953
+12
+68.94111831787316
+22
+-101.8826803019373
+32
+194.81220472440953
+13
+68.94111831787316
+23
+-101.8826803019373
+33
+194.81220472440953
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+2.0763689256853013e-07
+20
+-101.88268235660034
+30
+194.81220472440953
+11
+68.94111831787316
+21
+-101.8826803019373
+31
+194.81220472440953
+12
+54.371039577715635
+22
+-101.88268073617166
+32
+194.81220472440953
+13
+54.371039577715635
+23
+-101.88268073617166
+33
+194.81220472440953
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+2.0763689256853013e-07
+20
+-101.88268235660034
+30
+194.81220472440953
+11
+54.371039577715635
+21
+-101.88268073617166
+31
+194.81220472440953
+12
+18.75855926275502
+22
+-101.88268179753607
+32
+194.81220472440953
+13
+18.75855926275502
+23
+-101.88268179753607
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-5.830738838863425e-06
+20
+100.72590021948669
+30
+236.6003937007875
+11
+173.40095486047613
+21
+98.66928614733685
+31
+244.84448689980573
+12
+173.40095479918253
+22
+100.72590021948669
+32
+236.60039241161672
+13
+173.40095479918253
+23
+100.72590021948669
+33
+236.60039241161672
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095486047613
+20
+98.66928614733685
+30
+244.84448689980573
+11
+-5.830738838863425e-06
+21
+100.72590021948669
+31
+236.6003937007875
+12
+-5.769445243331006e-06
+22
+98.66928614733685
+32
+244.8444881889765
+13
+-5.769445243331006e-06
+23
+98.66928614733685
+33
+244.8444881889765
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095486047613
+20
+98.66928614733685
+30
+244.84448689980573
+11
+-5.769445243331006e-06
+21
+98.66928614733685
+31
+244.8444881889765
+12
+155.25961627779895
+22
+98.66929077456398
+32
+244.8444881889765
+13
+155.25961627779895
+23
+98.66929077456398
+33
+244.8444881889765
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+-5.769445243331006e-06
+21
+98.66928614733685
+31
+244.8444881889765
+12
+18.758553285672875
+22
+98.66928670640114
+32
+244.8444881889765
+13
+18.758553285672875
+23
+98.66928670640114
+33
+244.8444881889765
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+18.758553285672875
+21
+98.66928670640114
+31
+244.8444881889765
+12
+54.37103360063351
+22
+98.66928776776554
+32
+244.8444881889765
+13
+54.37103360063351
+23
+98.66928776776554
+33
+244.8444881889765
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+54.37103360063351
+21
+98.66928776776554
+31
+244.8444881889765
+12
+68.941112340791
+22
+98.66928820199992
+32
+244.8444881889765
+13
+68.941112340791
+23
+98.66928820199992
+33
+244.8444881889765
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+68.941112340791
+21
+98.66928820199992
+31
+244.8444881889765
+12
+111.77457690772016
+22
+98.66928947857245
+32
+244.8444881889765
+13
+111.77457690772016
+23
+98.66928947857245
+33
+244.8444881889765
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+111.77457690772016
+21
+98.66928947857245
+31
+244.8444881889765
+12
+126.31788399433434
+22
+98.66928991200895
+32
+244.8444881889765
+13
+126.31788399433434
+23
+98.66928991200895
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095479918253
+20
+100.72590021948669
+30
+236.60039241161672
+11
+-5.830738838863425e-06
+21
+88.4413334658682
+31
+236.6003937007875
+12
+-5.830738838863425e-06
+22
+100.72590021948669
+32
+236.6003937007875
+13
+-5.830738838863425e-06
+23
+100.72590021948669
+33
+236.6003937007875
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-5.830738838863425e-06
+20
+88.4413334658682
+30
+236.6003937007875
+11
+173.40095479918253
+21
+100.72590021948669
+31
+236.60039241161672
+12
+43.74033778568689
+22
+88.4413334658682
+32
+236.60039337559454
+13
+43.74033778568689
+23
+88.4413334658682
+33
+236.60039337559454
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+43.74033778568689
+20
+88.4413334658682
+30
+236.60039337559454
+11
+173.40095479918253
+21
+100.72590021948669
+31
+236.60039241161672
+12
+54.371033870305155
+22
+88.4413334658682
+32
+236.6003932965593
+13
+54.371033870305155
+23
+88.4413334658682
+33
+236.6003932965593
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+54.371033870305155
+20
+88.4413334658682
+30
+236.6003932965593
+11
+173.40095479918253
+21
+100.72590021948669
+31
+236.60039241161672
+12
+68.91434614140894
+22
+88.44133256583937
+32
+236.6003932965593
+13
+68.91434614140894
+23
+88.44133256583937
+33
+236.6003932965593
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+68.91434614140894
+20
+88.44133256583937
+30
+236.6003932965593
+11
+173.40095479918253
+21
+100.72590021948669
+31
+236.60039241161672
+12
+68.94111264561634
+22
+88.4413334350913
+32
+236.60039318823627
+13
+68.94111264561634
+23
+88.4413334350913
+33
+236.60039318823627
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111264561634
+20
+88.4413334350913
+30
+236.60039318823627
+11
+173.40095479918253
+21
+100.72590021948669
+31
+236.60039241161672
+12
+111.7745772127613
+22
+88.44133346590662
+32
+236.6003928697856
+13
+111.7745772127613
+23
+88.44133346590662
+33
+236.6003928697856
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+111.7745772127613
+20
+88.44133346590662
+30
+236.6003928697856
+11
+173.40095479918253
+21
+100.72590021948669
+31
+236.60039241161672
+12
+126.31788429915974
+22
+88.4413334658682
+32
+236.6003927616616
+13
+126.31788429915974
+23
+88.4413334658682
+33
+236.6003927616616
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788429915974
+20
+88.4413334658682
+30
+236.6003927616616
+11
+173.40095479918253
+21
+100.72590021948669
+31
+236.60039241161672
+12
+173.40095479918253
+22
+88.4413334658682
+32
+236.60039241161672
+13
+173.40095479918253
+23
+88.4413334658682
+33
+236.60039241161672
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+3.9834802123550617e-13
+20
+-94.91575322274208
+30
+200.98149606299222
+11
+1.7082673453572284e-07
+21
+-100.64757308805667
+31
+189.86102362204733
+12
+2.0763689256853013e-07
+22
+-101.88268235660034
+32
+194.81220472440953
+13
+2.0763689256853013e-07
+23
+-101.88268235660034
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+1.7082673453572284e-07
+20
+-100.64757308805667
+30
+189.86102362204733
+11
+3.9834802123550617e-13
+21
+-94.91575322274208
+31
+200.98149606299222
+12
+1.7082673453572284e-07
+22
+-84.34409960499812
+32
+189.86102362204733
+13
+1.7082673453572284e-07
+23
+-84.34409960499812
+33
+189.86102362204733
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+54.37103905501049
+20
+-84.34409829059867
+30
+189.86102321781914
+11
+1.7082673453572284e-07
+21
+-100.64757308805667
+31
+189.86102362204733
+12
+1.7082673453572284e-07
+22
+-84.34409960499812
+32
+189.86102362204733
+13
+1.7082673453572284e-07
+23
+-84.34409960499812
+33
+189.86102362204733
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+1.7082673453572284e-07
+20
+-100.64757308805667
+30
+189.86102362204733
+11
+54.37103905501049
+21
+-84.34409829059867
+31
+189.86102321781914
+12
+173.4009608007481
+22
+-100.64757308805667
+32
+189.86102233287656
+13
+173.4009608007481
+23
+-100.64757308805667
+33
+189.86102233287656
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+173.4009608007481
+20
+-100.64757308805667
+30
+189.86102233287656
+11
+54.37103905501049
+21
+-84.34409829059867
+31
+189.86102321781914
+12
+68.91435129096055
+22
+-84.34409829059867
+32
+189.86102321781914
+13
+68.91435129096055
+23
+-84.34409829059867
+33
+189.86102321781914
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.4009608007481
+20
+-100.64757308805667
+30
+189.86102233287656
+11
+68.91435129096055
+21
+-84.34409829059867
+31
+189.86102321781914
+12
+68.94111779516797
+22
+-84.34409811448558
+32
+189.8610231094961
+13
+68.94111779516797
+23
+-84.34409811448558
+33
+189.8610231094961
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.4009608007481
+20
+-100.64757308805667
+30
+189.86102233287656
+11
+68.94111779516797
+21
+-84.34409811448558
+31
+189.8610231094961
+12
+111.77458236206266
+22
+-84.34409690288955
+32
+189.86102279104543
+13
+111.77458236206266
+23
+-84.34409690288955
+33
+189.86102279104543
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.4009608007481
+20
+-100.64757308805667
+30
+189.86102233287656
+11
+111.77458236206266
+21
+-84.34409690288955
+31
+189.86102279104543
+12
+126.31788944871136
+22
+-84.34409739056986
+32
+189.86102268292143
+13
+126.31788944871136
+23
+-84.34409739056986
+33
+189.86102268292143
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.4009608007481
+20
+-100.64757308805667
+30
+189.86102233287656
+11
+126.31788944871136
+21
+-84.34409739056986
+31
+189.86102268292143
+12
+173.4009608007481
+22
+-84.34409541309498
+32
+189.86102233287656
+13
+173.4009608007481
+23
+-84.34409541309498
+33
+189.86102233287656
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+111.7745826771658
+20
+-94.91574989150647
+30
+200.98149606299222
+11
+126.317884264006
+21
+89.6208647151585
+31
+247.01850393700798
+12
+111.7745771773918
+22
+89.62086428172206
+32
+247.01850393700798
+13
+111.7745771773918
+23
+89.62086428172206
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+126.317884264006
+20
+89.6208647151585
+30
+247.01850393700798
+11
+111.7745826771658
+21
+-94.91574989150647
+31
+200.98149606299222
+12
+126.31788426410866
+22
+89.61741980576197
+32
+247.0176445231636
+13
+126.31788426410866
+23
+89.61741980576197
+33
+247.0176445231636
+70
+15
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788426410866
+20
+89.61741980576197
+30
+247.0176445231636
+11
+111.7745826771658
+21
+-94.91574989150647
+31
+200.98149606299222
+12
+126.31788976377997
+22
+-94.91574945806998
+32
+200.98149606299222
+13
+126.31788976377997
+23
+-94.91574945806998
+33
+200.98149606299222
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+126.317884264006
+20
+89.6208647151585
+30
+247.01850393700798
+11
+126.31788426410866
+21
+89.61741980576197
+31
+247.0176445231636
+12
+126.31788426406312
+22
+89.61894827418696
+32
+247.01802583563773
+13
+126.31788426406312
+23
+89.61894827418696
+33
+247.01802583563773
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111264561634
+20
+88.4413334350913
+30
+236.60039318823627
+11
+68.91435129096055
+21
+-84.34409829059867
+31
+189.86102321781914
+12
+68.91434614140894
+22
+88.44133256583937
+32
+236.6003932965593
+13
+68.91434614140894
+23
+88.44133256583937
+33
+236.6003932965593
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.91435129096055
+20
+-84.34409829059867
+30
+189.86102321781914
+11
+68.94111264561634
+21
+88.4413334350913
+31
+236.60039318823627
+12
+68.94111779516797
+22
+-84.34409811448558
+32
+189.8610231094961
+13
+68.94111779516797
+23
+-84.34409811448558
+33
+189.8610231094961
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+43.74033778568689
+20
+88.4413334658682
+30
+236.60039337559454
+11
+1.7082673453572284e-07
+21
+-84.34409960499812
+31
+189.86102362204733
+12
+-5.830738838863425e-06
+22
+88.4413334658682
+32
+236.6003937007875
+13
+-5.830738838863425e-06
+23
+88.4413334658682
+33
+236.6003937007875
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+1.7082673453572284e-07
+20
+-84.34409960499812
+30
+189.86102362204733
+11
+43.74033778568689
+21
+88.4413334658682
+31
+236.60039337559454
+12
+54.37103905501049
+22
+-84.34409829059867
+32
+189.86102321781914
+13
+54.37103905501049
+23
+-84.34409829059867
+33
+189.86102321781914
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+54.37103905501049
+20
+-84.34409829059867
+30
+189.86102321781914
+11
+43.74033778568689
+21
+88.4413334658682
+31
+236.60039337559454
+12
+54.371033870305155
+22
+88.4413334658682
+32
+236.6003932965593
+13
+54.371033870305155
+23
+88.4413334658682
+33
+236.6003932965593
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+111.7745772127613
+20
+88.44133346590662
+30
+236.6003928697856
+11
+68.94111779516797
+21
+-84.34409811448558
+31
+189.8610231094961
+12
+68.94111264561634
+22
+88.4413334350913
+32
+236.60039318823627
+13
+68.94111264561634
+23
+88.4413334350913
+33
+236.60039318823627
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+68.94111779516797
+20
+-84.34409811448558
+30
+189.8610231094961
+11
+111.7745772127613
+21
+88.44133346590662
+31
+236.6003928697856
+12
+111.77458236206266
+22
+-84.34409690288955
+32
+189.86102279104543
+13
+111.77458236206266
+23
+-84.34409690288955
+33
+189.86102279104543
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095479918253
+20
+88.4413334658682
+30
+236.60039241161672
+11
+126.31788944871136
+21
+-84.34409739056986
+31
+189.86102268292143
+12
+126.31788429915974
+22
+88.4413334658682
+32
+236.6003927616616
+13
+126.31788429915974
+23
+88.4413334658682
+33
+236.6003927616616
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+126.31788944871136
+20
+-84.34409739056986
+30
+189.86102268292143
+11
+173.40095479918253
+21
+88.4413334658682
+31
+236.60039241161672
+12
+173.4009608007481
+22
+-84.34409541309498
+32
+189.86102233287656
+13
+173.4009608007481
+23
+-84.34409541309498
+33
+189.86102233287656
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095613027842
+20
+56.06298693473833
+30
+238.64684910453013
+11
+173.40095945802403
+21
+-55.594493380222346
+31
+216.04960501004192
+12
+173.40095962196486
+22
+-61.09528078179717
+32
+209.41850264783722
+13
+173.40095962196486
+23
+-61.09528078179717
+33
+209.41850264783722
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095945802403
+20
+-55.594493380222346
+30
+216.04960501004192
+11
+173.40095613027842
+21
+56.06298693473833
+31
+238.64684910453013
+12
+173.4009564353384
+22
+45.8271601630848
+32
+241.35157351397893
+13
+173.4009564353384
+23
+45.8271601630848
+33
+241.35157351397893
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.4009608007481
+20
+-100.64757308805667
+30
+189.86102233287656
+11
+173.40096062992177
+21
+-94.91575322274208
+31
+200.98149477382145
+12
+173.40096083755827
+22
+-101.88268235660034
+32
+194.81220343523876
+13
+173.40096083755827
+23
+-101.88268235660034
+33
+194.81220343523876
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40096062992177
+20
+-94.91575322274208
+30
+200.98149477382145
+11
+173.4009608007481
+21
+-100.64757308805667
+31
+189.86102233287656
+12
+173.4009608007481
+22
+-84.34409541309498
+32
+189.86102233287656
+13
+173.4009608007481
+23
+-84.34409541309498
+33
+189.86102233287656
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40096062992177
+20
+-94.91575322274208
+30
+200.98149477382145
+11
+173.4009608007481
+21
+-84.34409541309498
+31
+189.86102233287656
+12
+173.40095962196486
+22
+-61.09528078179717
+32
+209.41850264783722
+13
+173.40095962196486
+23
+-61.09528078179717
+33
+209.41850264783722
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095962196486
+20
+-61.09528078179717
+30
+209.41850264783722
+11
+173.4009608007481
+21
+-84.34409541309498
+31
+189.86102233287656
+12
+173.40095479918253
+22
+88.4413334658682
+32
+236.60039241161672
+13
+173.40095479918253
+23
+88.4413334658682
+33
+236.60039241161672
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095962196486
+20
+-61.09528078179717
+30
+209.41850264783722
+11
+173.40095479918253
+21
+88.4413334658682
+31
+236.60039241161672
+12
+173.40095613027842
+22
+56.06298693473833
+32
+238.64684910453013
+13
+173.40095613027842
+23
+56.06298693473833
+33
+238.64684910453013
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095613027842
+20
+56.06298693473833
+30
+238.64684910453013
+11
+173.40095479918253
+21
+88.4413334658682
+31
+236.60039241161672
+12
+173.40095513014776
+22
+89.62086095048643
+32
+247.0185026478372
+13
+173.40095513014776
+23
+89.62086095048643
+33
+247.0185026478372
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095513014776
+20
+89.62086095048643
+30
+247.0185026478372
+11
+173.40095479918253
+21
+88.4413334658682
+31
+236.60039241161672
+12
+173.40095479918253
+22
+100.72590021948669
+32
+236.60039241161672
+13
+173.40095479918253
+23
+100.72590021948669
+33
+236.60039241161672
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095513014776
+20
+89.62086095048643
+30
+247.0185026478372
+11
+173.40095479918253
+21
+100.72590021948669
+31
+236.60039241161672
+12
+173.40095486047613
+22
+98.66928614733685
+32
+244.84448689980573
+13
+173.40095486047613
+23
+98.66928614733685
+33
+244.84448689980573
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.4567417214692
+20
+-84.3440954164166
+30
+189.86102362204733
+11
+173.4009608007481
+21
+-84.34409541309498
+31
+189.86102233287656
+12
+173.40095479918253
+22
+88.4413334658682
+32
+236.60039241161672
+13
+173.40095479918253
+23
+88.4413334658682
+33
+236.60039241161672
+70
+0
+ 0
+3DFACE
+ 8
+lid
+10
+-5.499773591655099e-06
+20
+89.62086095048643
+30
+247.01850393700798
+11
+-5.830738838863425e-06
+21
+100.72590021948669
+31
+236.6003937007875
+12
+-5.830738838863425e-06
+22
+88.4413334658682
+32
+236.6003937007875
+13
+-5.830738838863425e-06
+23
+88.4413334658682
+33
+236.6003937007875
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-5.830738838863425e-06
+20
+100.72590021948669
+30
+236.6003937007875
+11
+-5.499773591655099e-06
+21
+89.62086095048643
+31
+247.01850393700798
+12
+-5.769445243331006e-06
+22
+98.66928614733685
+32
+244.8444881889765
+13
+-5.769445243331006e-06
+23
+98.66928614733685
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-1.007956508480845e-06
+20
+-61.09528078179717
+30
+209.41850393700798
+11
+1.7082673453572284e-07
+21
+-84.34409960499812
+31
+189.86102362204733
+12
+3.9834802123550617e-13
+22
+-94.91575322274208
+32
+200.98149606299222
+13
+3.9834802123550617e-13
+23
+-94.91575322274208
+33
+200.98149606299222
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+1.7082673453572284e-07
+20
+-84.34409960499812
+30
+189.86102362204733
+11
+-1.007956508480845e-06
+21
+-61.09528078179717
+31
+209.41850393700798
+12
+-5.830738838863425e-06
+22
+88.4413334658682
+32
+236.6003937007875
+13
+-5.830738838863425e-06
+23
+88.4413334658682
+33
+236.6003937007875
+70
+3
+ 0
+3DFACE
+ 8
+lid
+10
+-5.830738838863425e-06
+20
+88.4413334658682
+30
+236.6003937007875
+11
+-1.007956508480845e-06
+21
+-61.09528078179717
+31
+209.41850393700798
+12
+-4.499642948463389e-06
+22
+56.06298693473833
+32
+238.6468503937009
+13
+-4.499642948463389e-06
+23
+56.06298693473833
+33
+238.6468503937009
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+-5.830738838863425e-06
+20
+88.4413334658682
+30
+236.6003937007875
+11
+-4.499642948463389e-06
+21
+56.06298693473833
+31
+238.6468503937009
+12
+-5.499773591655099e-06
+22
+89.62086095048643
+32
+247.01850393700798
+13
+-5.499773591655099e-06
+23
+89.62086095048643
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-1.1718973480512318e-06
+20
+-55.594493380222346
+30
+216.04960629921268
+11
+-4.499642948463389e-06
+21
+56.06298693473833
+31
+238.6468503937009
+12
+-1.007956508480845e-06
+22
+-61.09528078179717
+32
+209.41850393700798
+13
+-1.007956508480845e-06
+23
+-61.09528078179717
+33
+209.41850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-4.499642948463389e-06
+20
+56.06298693473833
+30
+238.6468503937009
+11
+-1.1718973480512318e-06
+21
+-55.594493380222346
+31
+216.04960629921268
+12
+-4.194582979177852e-06
+22
+45.8271601630848
+32
+241.3515748031497
+13
+-4.194582979177852e-06
+23
+45.8271601630848
+33
+241.3515748031497
+70
+1
+ 0
+LINE
+ 8
+lid
+10
+173.57615198071508
+20
+89.61504284452782
+30
+247.01705122570223
+11
+173.40095479918253
+21
+88.4413334658682
+31
+236.60039241161672
+ 0
+LINE
+ 8
+lid
+10
+173.40096062992177
+20
+-94.91575322274208
+30
+200.98149477382145
+11
+173.4567417214692
+21
+-84.3440954164166
+31
+189.86102362204733
+ 0
+3DFACE
+ 8
+lid
+10
+155.2596220472446
+20
+-94.91574859551494
+30
+200.98149606299222
+11
+155.25961627779895
+21
+98.66929077456398
+31
+244.8444881889765
+12
+155.2596222548811
+22
+-101.8826777293732
+32
+194.81220472440953
+13
+155.2596222548811
+23
+-101.8826777293732
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+155.2596220472446
+21
+-94.91574859551494
+31
+200.98149606299222
+12
+155.25962204721512
+22
+-94.91475962800925
+32
+200.98174278680736
+13
+155.25962204721512
+23
+-94.91475962800925
+33
+200.98174278680736
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+155.25962204721512
+21
+-94.91475962800925
+31
+200.98174278680736
+12
+155.25962103928768
+22
+-61.09527615457002
+32
+209.41889763779537
+13
+155.25962103928768
+23
+-61.09527615457002
+33
+209.41889763779537
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+155.25962103928768
+21
+-61.09527615457002
+31
+209.41889763779537
+12
+155.25961754760118
+22
+56.06299156196549
+32
+238.6468503937009
+13
+155.25961754760118
+23
+56.06299156196549
+33
+238.6468503937009
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+155.25961754760118
+21
+56.06299156196549
+31
+238.6468503937009
+12
+155.25961654795418
+22
+89.60464044290792
+32
+247.0144562667884
+13
+155.25961654795418
+23
+89.60464044290792
+33
+247.0144562667884
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+155.25961654795418
+21
+89.60464044290792
+31
+247.0144562667884
+12
+155.2596165477875
+22
+89.61023337167238
+32
+247.01585152984606
+13
+155.2596165477875
+23
+89.61023337167238
+33
+247.01585152984606
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+155.2596165477875
+21
+89.61023337167238
+31
+247.01585152984606
+12
+155.25961654768776
+22
+89.61358009666061
+32
+247.01668643440806
+13
+155.25961654768776
+23
+89.61358009666061
+33
+247.01668643440806
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+155.25961654768776
+21
+89.61358009666061
+31
+247.01668643440806
+12
+155.25961654757327
+22
+89.61742066705047
+32
+247.01764453815136
+13
+155.25961654757327
+23
+89.61742066705047
+33
+247.01764453815136
+70
+13
+ 0
+3DFACE
+ 8
+lid
+10
+155.25961627779895
+20
+98.66929077456398
+30
+244.8444881889765
+11
+155.25961654757327
+21
+89.61742066705047
+31
+247.01764453815136
+12
+155.2596165474706
+22
+89.62086557771359
+32
+247.01850393700798
+13
+155.2596165474706
+23
+89.62086557771359
+33
+247.01850393700798
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+155.25962103928768
+20
+-61.09527615457002
+30
+209.41889763779537
+11
+173.40095613027842
+21
+56.06298693473833
+31
+238.64684910453013
+12
+155.25961754760118
+22
+56.06299156196549
+32
+238.6468503937009
+13
+155.25961754760118
+23
+56.06299156196549
+33
+238.6468503937009
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+173.40095613027842
+20
+56.06298693473833
+30
+238.64684910453013
+11
+155.25962103928768
+21
+-61.09527615457002
+31
+209.41889763779537
+12
+173.40095962196486
+22
+-61.09528078179717
+32
+209.41850264783722
+13
+173.40095962196486
+23
+-61.09528078179717
+33
+209.41850264783722
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758554555475172
+20
+56.06298749380262
+30
+238.6468503937009
+11
+18.758557883220774
+21
+-55.594492821158035
+31
+216.04960629921268
+12
+18.758558047161614
+22
+-61.09528022273286
+32
+209.41889763779537
+13
+18.758558047161614
+23
+-61.09528022273286
+33
+209.41889763779537
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758557883220774
+20
+-55.594492821158035
+30
+216.04960629921268
+11
+18.758554555475172
+21
+56.06298749380262
+31
+238.6468503937009
+12
+18.758554860535142
+22
+45.82716072214909
+32
+241.3515748031497
+13
+18.758554860535142
+23
+45.82716072214909
+33
+241.3515748031497
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+-1.007956508480845e-06
+20
+-61.09528078179717
+30
+209.41850393700798
+11
+18.758554555475172
+21
+56.06298749380262
+31
+238.6468503937009
+12
+-4.499642948463389e-06
+22
+56.06298693473833
+32
+238.6468503937009
+13
+-4.499642948463389e-06
+23
+56.06298693473833
+33
+238.6468503937009
+70
+1
+ 0
+3DFACE
+ 8
+lid
+10
+18.758554555475172
+20
+56.06298749380262
+30
+238.6468503937009
+11
+-1.007956508480845e-06
+21
+-61.09528078179717
+31
+209.41850393700798
+12
+18.758558047161614
+22
+-61.09528022273286
+32
+209.41889763779537
+13
+18.758558047161614
+23
+-61.09528022273286
+33
+209.41889763779537
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+18.75855926275502
+20
+-101.88268179753607
+30
+194.81220472440953
+11
+54.37103360063351
+21
+98.66928776776552
+31
+244.8444881889765
+12
+18.758553285672875
+22
+98.66928670640112
+32
+244.8444881889765
+13
+18.758553285672875
+23
+98.66928670640112
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+54.37103360063351
+20
+98.66928776776552
+30
+244.8444881889765
+11
+18.75855926275502
+21
+-101.88268179753607
+31
+194.81220472440953
+12
+54.371039577715635
+22
+-101.88268073617166
+32
+194.81220472440953
+13
+54.371039577715635
+23
+-101.88268073617166
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+68.94111831787316
+20
+-101.8826803019373
+30
+194.81220472440953
+11
+111.77457690772016
+21
+98.66928947857247
+31
+244.8444881889765
+12
+68.941112340791
+22
+98.6692882019999
+32
+244.8444881889765
+13
+68.941112340791
+23
+98.6692882019999
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+111.77457690772016
+20
+98.66928947857247
+30
+244.8444881889765
+11
+68.94111831787316
+21
+-101.8826803019373
+31
+194.81220472440953
+12
+111.7745828848023
+22
+-101.88267902536472
+32
+194.81220472440953
+13
+111.7745828848023
+23
+-101.88267902536472
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+126.31788997141648
+20
+-101.88267859192827
+30
+194.81220472440953
+11
+155.25961627779895
+21
+98.66929077456399
+31
+244.8444881889765
+12
+126.31788399433434
+22
+98.66928991200894
+32
+244.8444881889765
+13
+126.31788399433434
+23
+98.66928991200894
+33
+244.8444881889765
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+155.25961627779895
+20
+98.66929077456399
+30
+244.8444881889765
+11
+126.31788997141648
+21
+-101.88267859192827
+31
+194.81220472440953
+12
+155.2596222548811
+22
+-101.8826777293732
+32
+194.81220472440953
+13
+155.2596222548811
+23
+-101.8826777293732
+33
+194.81220472440953
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+126.31788924104035
+20
+-77.37716733559479
+30
+196.03031412962812
+11
+111.77458236206266
+21
+-84.34409690288955
+31
+189.86102279104543
+12
+111.77458215442616
+22
+-77.37716776903129
+32
+196.03031412962812
+13
+111.77458215442616
+23
+-77.37716776903129
+33
+196.03031412962812
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+111.77458236206266
+20
+-84.34409690288955
+30
+189.86102279104543
+11
+126.31788924104035
+21
+-77.37716733559479
+31
+196.03031412962812
+12
+126.31788944871136
+22
+-84.34409739056986
+32
+189.86102268292143
+13
+126.31788944871136
+23
+-84.34409739056986
+33
+189.86102268292143
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+126.31788429915974
+20
+88.44133346586818
+30
+236.6003927616616
+11
+111.7745774822173
+21
+79.3929078355813
+31
+238.77440850969307
+12
+111.7745772127613
+22
+88.44133346590661
+32
+236.6003928697856
+13
+111.7745772127613
+23
+88.44133346590661
+33
+236.6003928697856
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+111.7745774822173
+20
+79.3929078355813
+30
+238.77440850969307
+11
+126.31788429915974
+21
+88.44133346586818
+31
+236.6003927616616
+12
+126.3178845688314
+22
+79.39290826901777
+32
+238.77440850969307
+13
+126.3178845688314
+23
+79.39290826901777
+33
+238.77440850969307
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+126.31788924104035
+20
+-77.37716733559479
+30
+196.03031412962812
+11
+126.31788429915974
+21
+88.44133346586818
+31
+236.6003927616616
+12
+126.31788944871136
+22
+-84.34409739056986
+32
+189.86102268292143
+13
+126.31788944871136
+23
+-84.34409739056986
+33
+189.86102268292143
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+126.31788429915974
+20
+88.44133346586818
+30
+236.6003927616616
+11
+126.31788924104035
+21
+-77.37716733559479
+31
+196.03031412962812
+12
+126.3178845688314
+22
+79.39290826901777
+32
+238.77440850969307
+13
+126.3178845688314
+23
+79.39290826901777
+33
+238.77440850969307
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+111.7745772127613
+20
+88.44133346590661
+30
+236.6003928697856
+11
+111.77458215442616
+21
+-77.37716776903129
+31
+196.03031412962812
+12
+111.77458236206266
+22
+-84.34409690288955
+32
+189.86102279104543
+13
+111.77458236206266
+23
+-84.34409690288955
+33
+189.86102279104543
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+111.77458215442616
+20
+-77.37716776903129
+30
+196.03031412962812
+11
+111.7745772127613
+21
+88.44133346590661
+31
+236.6003928697856
+12
+111.7745774822173
+22
+79.3929078355813
+32
+238.77440850969307
+13
+111.7745774822173
+23
+79.3929078355813
+33
+238.77440850969307
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+126.3178845688314
+20
+79.39290826901777
+30
+238.77440850969307
+11
+111.77458215442616
+21
+-77.37716776903129
+31
+196.03031412962812
+12
+111.7745774822173
+22
+79.3929078355813
+32
+238.77440850969307
+13
+111.7745774822173
+23
+79.3929078355813
+33
+238.77440850969307
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+111.77458215442616
+20
+-77.37716776903129
+30
+196.03031412962812
+11
+126.3178845688314
+21
+79.39290826901777
+31
+238.77440850969307
+12
+126.31788924104035
+22
+-77.37716733559479
+32
+196.03031412962812
+13
+126.31788924104035
+23
+-77.37716733559479
+33
+196.03031412962812
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+68.91434614140894
+20
+88.44133256583939
+30
+236.6003932965593
+11
+54.37103932446649
+21
+79.3929069355525
+31
+238.77440904459078
+12
+54.371033870305155
+22
+88.44133346586818
+32
+236.6003932965593
+13
+54.371033870305155
+23
+88.44133346586818
+33
+236.6003932965593
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+54.37103932446649
+20
+79.3929069355525
+30
+238.77440904459078
+11
+68.91434614140894
+21
+88.44133256583939
+31
+236.6003932965593
+12
+68.9143464110806
+22
+79.39290736898897
+32
+238.77440904459078
+13
+68.9143464110806
+23
+79.39290736898897
+33
+238.77440904459078
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+68.9143464110806
+20
+79.39290736898897
+30
+238.77440904459078
+11
+54.37104399667535
+21
+-77.3771686690601
+31
+196.03031466452583
+12
+54.37103932446649
+22
+79.3929069355525
+32
+238.77440904459078
+13
+54.37103932446649
+23
+79.3929069355525
+33
+238.77440904459078
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+54.37104399667535
+20
+-77.3771686690601
+30
+196.03031466452583
+11
+68.9143464110806
+21
+79.39290736898897
+31
+238.77440904459078
+12
+68.91435108328955
+22
+-77.3771682356236
+32
+196.03031466452583
+13
+68.91435108328955
+23
+-77.3771682356236
+33
+196.03031466452583
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+54.37104399667535
+20
+-77.3771686690601
+30
+196.03031466452583
+11
+68.91435129096055
+21
+-84.34409829059867
+31
+189.86102321781914
+12
+54.37103905501049
+22
+-84.34409829059867
+32
+189.86102321781914
+13
+54.37103905501049
+23
+-84.34409829059867
+33
+189.86102321781914
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+68.91435129096055
+20
+-84.34409829059867
+30
+189.86102321781914
+11
+54.37104399667535
+21
+-77.3771686690601
+31
+196.03031466452583
+12
+68.91435108328955
+22
+-77.3771682356236
+32
+196.03031466452583
+13
+68.91435108328955
+23
+-77.3771682356236
+33
+196.03031466452583
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+54.371033870305155
+20
+88.44133346586818
+30
+236.6003932965593
+11
+54.37104399667535
+21
+-77.3771686690601
+31
+196.03031466452583
+12
+54.37103905501049
+22
+-84.34409829059867
+32
+189.86102321781914
+13
+54.37103905501049
+23
+-84.34409829059867
+33
+189.86102321781914
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+54.37104399667535
+20
+-77.3771686690601
+30
+196.03031466452583
+11
+54.371033870305155
+21
+88.44133346586818
+31
+236.6003932965593
+12
+54.37103932446649
+22
+79.3929069355525
+32
+238.77440904459078
+13
+54.37103932446649
+23
+79.3929069355525
+33
+238.77440904459078
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+68.91435108328955
+20
+-77.3771682356236
+30
+196.03031466452583
+11
+68.91434614140894
+21
+88.44133256583939
+31
+236.6003932965593
+12
+68.91435129096055
+22
+-84.34409829059867
+32
+189.86102321781914
+13
+68.91435129096055
+23
+-84.34409829059867
+33
+189.86102321781914
+70
+1
+ 0
+3DFACE
+ 8
+lid_panels
+10
+68.91434614140894
+20
+88.44133256583939
+30
+236.6003932965593
+11
+68.91435108328955
+21
+-77.3771682356236
+31
+196.03031466452583
+12
+68.9143464110806
+22
+79.39290736898897
+32
+238.77440904459078
+13
+68.9143464110806
+23
+79.39290736898897
+33
+238.77440904459078
+70
+1
+ 0
+ENDSEC
+ 0
+EOF
--- /dev/null
+/* Generated from "dumpster.dxf" on 24-Apr-2025.
+ Smoothed vertex normals at 30°. Normalized to unit bounding box.
+ Components: axle, frame_half, hinges_half, inside_half, lid, lid_panels,
+ panels_half.
+ */
+
+#include "gllist.h"
+
+static const float dumpster_model_axle_data[] = {
+ 1,0,0,0.480142,0.283784,0.65531,
+ 1,0,0,0.480142,0.278577,0.656705,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 0,-0.965926,0.258819,0.480142,0.269557,0.651498,
+ 0,-0.965926,0.258819,-0.480142,0.268162,0.646291,
+ 0,-0.965926,0.258819,0.480142,0.268162,0.646291,
+ 0,-0.965926,0.258819,-0.480142,0.268162,0.646291,
+ 0,-0.965926,0.258819,0.480142,0.269557,0.651498,
+ 0,-0.965926,0.258819,-0.480142,0.269557,0.651498,
+ 0,-0.707107,0.707107,0.480142,0.269557,0.651498,
+ 0,-0.575061,0.81811,-0.480142,0.273369,0.65531,
+ 0,-0.707107,0.707107,-0.480142,0.269557,0.651498,
+ 0,-0.575061,0.81811,-0.480142,0.273369,0.65531,
+ 0,-0.707107,0.707107,0.480142,0.269557,0.651498,
+ 0,-0.420974,0.907073,0.480142,0.273369,0.65531,
+ 0,-0.420974,0.907073,0.480142,0.273369,0.65531,
+ 0,-0.258819,0.965926,-0.480142,0.278577,0.656705,
+ 0,-0.575061,0.81811,-0.480142,0.273369,0.65531,
+ 0,-0.258819,0.965926,-0.480142,0.278577,0.656705,
+ 0,-0.420974,0.907073,0.480142,0.273369,0.65531,
+ 0,-0.258819,0.965926,0.480142,0.278577,0.656705,
+ 0,0.866025,-0.5,0.480142,0.287596,0.641084,
+ 0,0.707107,-0.707107,-0.480142,0.283784,0.637272,
+ 0,0.866025,-0.5,-0.480142,0.287596,0.641084,
+ 0,0.707107,-0.707107,-0.480142,0.283784,0.637272,
+ 0,0.866025,-0.5,0.480142,0.287596,0.641084,
+ 0,0.707107,-0.707107,0.480142,0.283784,0.637272,
+ 0,0.258819,0.965926,0.480142,0.278577,0.656705,
+ 0,0.420974,0.907073,-0.480142,0.283784,0.65531,
+ 0,0.258819,0.965926,-0.480142,0.278577,0.656705,
+ 0,0.420974,0.907073,-0.480142,0.283784,0.65531,
+ 0,0.258819,0.965926,0.480142,0.278577,0.656705,
+ 0,0.575061,0.81811,0.480142,0.283784,0.65531,
+ 0,0.965926,-0.258819,-0.480142,0.288991,0.646291,
+ 0,0.866025,-0.5,0.480142,0.287596,0.641084,
+ 0,0.866025,-0.5,-0.480142,0.287596,0.641084,
+ 0,0.866025,-0.5,0.480142,0.287596,0.641084,
+ 0,0.965926,-0.258819,-0.480142,0.288991,0.646291,
+ 0,0.965926,-0.258819,0.480142,0.288991,0.646291,
+ 0,0.575061,0.81811,0.480142,0.283784,0.65531,
+ 0,0.707107,0.707107,-0.480142,0.287596,0.651498,
+ 0,0.420974,0.907073,-0.480142,0.283784,0.65531,
+ 0,0.707107,0.707107,-0.480142,0.287596,0.651498,
+ 0,0.575061,0.81811,0.480142,0.283784,0.65531,
+ 0,0.707107,0.707107,0.480142,0.287596,0.651498,
+ 0,0.965926,0.258819,-0.480142,0.287596,0.651498,
+ 0,0.965926,0.258819,0.480142,0.288991,0.646291,
+ 0,0.965926,0.258819,-0.480142,0.288991,0.646291,
+ 0,0.965926,0.258819,0.480142,0.288991,0.646291,
+ 0,0.965926,0.258819,-0.480142,0.287596,0.651498,
+ 0,0.965926,0.258819,0.480142,0.287596,0.651498,
+ 0,0.258819,-0.965926,0.480142,0.283784,0.637272,
+ 0,0.088962,-0.996035,-0.480142,0.278577,0.635876,
+ 0,0.258819,-0.965926,-0.480142,0.283784,0.637272,
+ 0,0.088962,-0.996035,-0.480142,0.278577,0.635876,
+ 0,0.258819,-0.965926,0.480142,0.283784,0.637272,
+ 0,-0.088962,-0.996035,0.480142,0.278577,0.635876,
+ 0,-0.088962,-0.996035,0.480142,0.278577,0.635876,
+ 0,-0.420974,-0.907073,-0.480142,0.273369,0.637272,
+ 0,0.088962,-0.996035,-0.480142,0.278577,0.635876,
+ 0,-0.420974,-0.907073,-0.480142,0.273369,0.637272,
+ 0,-0.088962,-0.996035,0.480142,0.278577,0.635876,
+ 0,-0.575061,-0.81811,0.480142,0.273369,0.637272,
+ 0,-0.575061,-0.81811,0.480142,0.273369,0.637272,
+ 0,-0.866025,-0.5,-0.480142,0.269557,0.641084,
+ 0,-0.420974,-0.907073,-0.480142,0.273369,0.637272,
+ 0,-0.866025,-0.5,-0.480142,0.269557,0.641084,
+ 0,-0.575061,-0.81811,0.480142,0.273369,0.637272,
+ 0,-0.866025,-0.5,0.480142,0.269557,0.641084,
+ 0,-0.965926,-0.258819,0.480142,0.268162,0.646291,
+ 0,-0.866025,-0.5,-0.480142,0.269557,0.641084,
+ 0,-0.866025,-0.5,0.480142,0.269557,0.641084,
+ 0,-0.866025,-0.5,-0.480142,0.269557,0.641084,
+ 0,-0.965926,-0.258819,0.480142,0.268162,0.646291,
+ 0,-0.965926,-0.258819,-0.480142,0.268162,0.646291,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.273369,0.65531,
+ -1,0,0,-0.480142,0.278577,0.656705,
+ 1,0,0,0.480142,0.288991,0.646291,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.287596,0.641084,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.269557,0.641084,
+ 1,0,0,0.480142,0.273369,0.637272,
+ 1,0,0,0.480142,0.278577,0.656705,
+ 1,0,0,0.480142,0.273369,0.65531,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.288991,0.646291,
+ 1,0,0,0.480142,0.287596,0.651498,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.273369,0.637272,
+ 1,0,0,0.480142,0.278577,0.635876,
+ 1,0,0,0.480142,0.287596,0.651498,
+ 1,0,0,0.480142,0.283784,0.65531,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.268162,0.646291,
+ 1,0,0,0.480142,0.269557,0.641084,
+ 1,0,0,0.480142,0.283784,0.637272,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.278577,0.635876,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.273369,0.65531,
+ 1,0,0,0.480142,0.269557,0.651498,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.269557,0.651498,
+ 1,0,0,0.480142,0.268162,0.646291,
+ 1,0,0,0.480142,0.287596,0.641084,
+ 1,0,0,0.480142,0.278577,0.646291,
+ 1,0,0,0.480142,0.283784,0.637272,
+ -1,0,0,-0.480142,0.287596,0.641084,
+ -1,0,0,-0.480142,0.283784,0.637272,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ 0,-1,0,0.480142,0.278577,0.646291,
+ 0,-1,0,-0.480142,0.278577,0.635876,
+ 0,-1,0,0.480142,0.278577,0.635876,
+ 0,-1,0,-0.480142,0.278577,0.635876,
+ 0,-1,0,0.480142,0.278577,0.646291,
+ 0,-1,0,-0.480142,0.278577,0.646291,
+ 0,-1,0,-0.480142,0.278577,0.646291,
+ 0,-1,0,0.480142,0.278577,0.646291,
+ 0,-1,0,0.480142,0.278577,0.656705,
+ 0,-1,0,-0.480142,0.278577,0.646291,
+ 0,-1,0,0.480142,0.278577,0.656705,
+ 0,-1,0,-0.480142,0.278577,0.656705,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.268162,0.646291,
+ -1,0,0,-0.480142,0.269557,0.651498,
+ -1,0,0,-0.480142,0.283784,0.637272,
+ -1,0,0,-0.480142,0.278577,0.635876,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ 0,0.866025,0.5,-0.480142,0.278577,0.646291,
+ 0,0.866025,0.5,0.480142,0.283784,0.637272,
+ 0,0.866025,0.5,-0.480142,0.283784,0.637272,
+ 0,0.866025,0.5,0.480142,0.283784,0.637272,
+ 0,0.866025,0.5,-0.480142,0.278577,0.646291,
+ 0,0.866025,0.5,0.480142,0.278577,0.646291,
+ 0,0.866025,0.5,0.480142,0.278577,0.646291,
+ 0,0.866025,0.5,-0.480142,0.278577,0.646291,
+ 0,0.866025,0.5,-0.480142,0.273369,0.65531,
+ 0,0.866025,0.5,0.480142,0.278577,0.646291,
+ 0,0.866025,0.5,-0.480142,0.273369,0.65531,
+ 0,0.866025,0.5,0.480142,0.273369,0.65531,
+ -1,0,0,-0.480142,0.283784,0.65531,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.278577,0.656705,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.269557,0.651498,
+ -1,0,0,-0.480142,0.273369,0.65531,
+ 0,-0.5,-0.866025,0.480142,0.287596,0.641084,
+ 0,-0.5,-0.866025,-0.480142,0.278577,0.646291,
+ 0,-0.5,-0.866025,-0.480142,0.287596,0.641084,
+ 0,-0.5,-0.866025,-0.480142,0.278577,0.646291,
+ 0,-0.5,-0.866025,0.480142,0.287596,0.641084,
+ 0,-0.5,-0.866025,-0.480142,0.269557,0.651498,
+ 0,-0.5,-0.866025,-0.480142,0.269557,0.651498,
+ 0,-0.5,-0.866025,0.480142,0.287596,0.641084,
+ 0,-0.5,-0.866025,0.480142,0.269557,0.651498,
+ -1,0,0,0.480142,0.269557,0.651498,
+ -1,0,0,0.480142,0.287596,0.641084,
+ -1,0,0,0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.269557,0.641084,
+ -1,0,0,-0.480142,0.268162,0.646291,
+ -1,0,0,-0.480142,0.288991,0.646291,
+ -1,0,0,-0.480142,0.287596,0.641084,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ 0,0,1,0.480142,0.268162,0.646291,
+ 0,0,1,-0.480142,0.278577,0.646291,
+ 0,0,1,-0.480142,0.268162,0.646291,
+ 0,0,1,-0.480142,0.278577,0.646291,
+ 0,0,1,0.480142,0.268162,0.646291,
+ 0,0,1,-0.480142,0.288991,0.646291,
+ 0,0,1,-0.480142,0.288991,0.646291,
+ 0,0,1,0.480142,0.268162,0.646291,
+ 0,0,1,0.480142,0.288991,0.646291,
+ 0.534871,0.774969,0.336653,0.480142,0.288991,0.646291,
+ 0.44821,-0.649406,0.614312,0.480142,0.268162,0.646291,
+ 0.920575,-0.276172,0.276172,0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.287596,0.651498,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.283784,0.65531,
+ -1,0,0,-0.480142,0.278577,0.635876,
+ -1,0,0,-0.480142,0.273369,0.637272,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ 0,-0.866025,0.5,0.480142,0.278577,0.646291,
+ 0,-0.866025,0.5,-0.480142,0.273369,0.637272,
+ 0,-0.866025,0.5,0.480142,0.273369,0.637272,
+ 0,-0.866025,0.5,-0.480142,0.273369,0.637272,
+ 0,-0.866025,0.5,0.480142,0.278577,0.646291,
+ 0,-0.866025,0.5,-0.480142,0.278577,0.646291,
+ 0,-0.866025,0.5,-0.480142,0.278577,0.646291,
+ 0,-0.866025,0.5,0.480142,0.278577,0.646291,
+ 0,-0.866025,0.5,0.480142,0.283784,0.65531,
+ 0,-0.866025,0.5,-0.480142,0.278577,0.646291,
+ 0,-0.866025,0.5,0.480142,0.283784,0.65531,
+ 0,-0.866025,0.5,-0.480142,0.283784,0.65531,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.273369,0.637272,
+ -1,0,0,-0.480142,0.269557,0.641084,
+ -1,0,0,-0.480142,0.288991,0.646291,
+ -1,0,0,-0.480142,0.278577,0.646291,
+ -1,0,0,-0.480142,0.287596,0.651498,
+ 0,0.5,-0.866025,0.480142,0.287596,0.651498,
+ 0,0.5,-0.866025,-0.480142,0.278577,0.646291,
+ 0,0.5,-0.866025,-0.480142,0.287596,0.651498,
+ 0,0.5,-0.866025,-0.480142,0.278577,0.646291,
+ 0,0.5,-0.866025,0.480142,0.287596,0.651498,
+ 0,0.5,-0.866025,-0.480142,0.269557,0.641084,
+ 0,0.5,-0.866025,-0.480142,0.269557,0.641084,
+ 0,0.5,-0.866025,0.480142,0.287596,0.651498,
+ 0,0.5,-0.866025,0.480142,0.269557,0.641084,
+ -1,0,0,0.480142,0.269557,0.641084,
+ -1,0,0,0.480142,0.287596,0.651498,
+ -1,0,0,0.480142,0.278577,0.646291
+};
+static const struct gllist dumpster_model_axle_frame = {
+ GL_N3F_V3F, GL_TRIANGLES, 216, dumpster_model_axle_data, 0
+};
+const struct gllist *dumpster_model_axle = &dumpster_model_axle_frame;
+
+static const float dumpster_model_frame_half_data[] = {
+ 0,0,-1,0.455529,0.260975,0.000853,
+ 0,0,-1,0,-0.260975,0.000853,
+ 0,0,-1,0,0.260975,0.000853,
+ 0,0,-1,0,-0.260975,0.000853,
+ 0,0,-1,0.455529,0.260975,0.000853,
+ 0,0,-1,0.455529,-0.260975,0.000853,
+ 0,1,0,0,0.291396,0.602678,
+ 0,1,0,0.480142,0.291396,0.602667,
+ 0,1,0,0.465575,0.291396,0.602667,
+ 0,1,0,0.480142,0.291396,0.602667,
+ 0,1,0,0,0.291396,0.602678,
+ 0,1,0,0.480142,0.291396,0.627761,
+ 0,1,0,0.480142,0.291396,0.627761,
+ 0,1,0,0,0.291396,0.602678,
+ 0,1,0,0,0.291396,0.627761,
+ 0,0,1,0.480142,0.291396,0.627761,
+ 0,0,1,0,0.291396,0.627761,
+ 0,0,1,0.455529,0.291396,0.627761,
+ 0,-1,0,0.455529,-0.260975,0.000853,
+ 0,-1,0,0.43601,-0.260975,0.020372,
+ 0,-1,0,0.019519,-0.260975,0.020372,
+ 0,-1,0,0.43601,-0.260975,0.020372,
+ 0,-1,0,0.455529,-0.260975,0.000853,
+ 0,-1,0,0.455529,-0.260975,0.467139,
+ 0,-1,0,0.43601,-0.260975,0.020372,
+ 0,-1,0,0.455529,-0.260975,0.467139,
+ 0,-1,0,0.43601,-0.260975,0.44762,
+ 0,-1,0,0.43601,-0.260975,0.44762,
+ 0,-1,0,0.455529,-0.260975,0.467139,
+ 0,-1,0,0.019519,-0.260975,0.447625,
+ 0,-1,0,0,-0.260975,0.000853,
+ 0,-1,0,0.019519,-0.260975,0.020372,
+ 0,-1,0,0,-0.260975,0.467144,
+ 0,-1,0,0.019519,-0.260975,0.020372,
+ 0,-1,0,0,-0.260975,0.000853,
+ 0,-1,0,0.455529,-0.260975,0.000853,
+ 0,-1,0,0,-0.260975,0.467144,
+ 0,-1,0,0.019519,-0.260975,0.020372,
+ 0,-1,0,0.019519,-0.260975,0.447625,
+ 0,-1,0,0,-0.260975,0.467144,
+ 0,-1,0,0.019519,-0.260975,0.447625,
+ 0,-1,0,0.455529,-0.260975,0.467139,
+ 0,1,0,0,0.260975,0.000853,
+ 0,1,0,0.019519,0.260975,0.020372,
+ 0,1,0,0.455529,0.260975,0.000853,
+ 0,1,0,0.019519,0.260975,0.020372,
+ 0,1,0,0,0.260975,0.000853,
+ 0,1,0,0,0.260975,0.602678,
+ 0,1,0,0.019519,0.260975,0.020372,
+ 0,1,0,0,0.260975,0.602678,
+ 0,1,0,0.019519,0.260975,0.583158,
+ 0,1,0,0.019519,0.260975,0.583158,
+ 0,1,0,0,0.260975,0.602678,
+ 0,1,0,0.455529,0.260975,0.602667,
+ 0,1,0,0.455529,0.260975,0.000853,
+ 0,1,0,0.43601,0.260975,0.020372,
+ 0,1,0,0.455529,0.260975,0.602667,
+ 0,1,0,0.43601,0.260975,0.020372,
+ 0,1,0,0.455529,0.260975,0.000853,
+ 0,1,0,0.019519,0.260975,0.020372,
+ 0,1,0,0.455529,0.260975,0.602667,
+ 0,1,0,0.43601,0.260975,0.020372,
+ 0,1,0,0.43601,0.260975,0.583149,
+ 0,1,0,0.455529,0.260975,0.602667,
+ 0,1,0,0.43601,0.260975,0.583149,
+ 0,1,0,0.019519,0.260975,0.583158,
+ 0,-1,0,0.480142,-0.285801,0.50375,
+ 0,-1,0,0.465574,-0.285801,0.467139,
+ 0,-1,0,0.480142,-0.285801,0.467139,
+ 0,-1,0,0.465574,-0.285801,0.467139,
+ 0,-1,0,0.480142,-0.285801,0.50375,
+ 0,-1,0,0,-0.285801,0.467144,
+ 0,-1,0,0,-0.285801,0.467144,
+ 0,-1,0,0.480142,-0.285801,0.50375,
+ 0,-1,0,0,-0.285801,0.50375,
+ 0,0,1,0.480142,-0.285801,0.50375,
+ 0,0,1,0,-0.223786,0.50375,
+ 0,0,1,0,-0.285801,0.50375,
+ 0,0,1,0,-0.223786,0.50375,
+ 0,0,1,0.480142,-0.285801,0.50375,
+ 0,-0.131708,0.991289,0.431095,-0.223786,0.50375,
+ 0,-0.131708,0.991289,0.431095,-0.223786,0.50375,
+ 0,0,1,0.480142,-0.285801,0.50375,
+ 0,-0.131708,0.991289,0.480142,-0.223786,0.50375,
+ 0,-0.065709,0.997839,0.431095,0.234657,0.627761,
+ 0,0,1,0,0.291396,0.627761,
+ 0,0,1,0,0.234657,0.627761,
+ 0,0,1,0,0.291396,0.627761,
+ 0,-0.065709,0.997839,0.431095,0.234657,0.627761,
+ 0,0,1,0.455529,0.291396,0.627761,
+ 0,0,1,0.455529,0.291396,0.627761,
+ 0,-0.065709,0.997839,0.431095,0.234657,0.627761,
+ 0,-0.131708,0.991289,0.480142,0.234657,0.627761,
+ 0,0,1,0.455529,0.291396,0.627761,
+ 0,-0.131708,0.991289,0.480142,0.234657,0.627761,
+ 0,0,1,0.480142,0.291396,0.627761,
+ 0,-0.131708,0.991289,0.431095,-0.223786,0.50375,
+ 0,-0.131708,0.991289,0.480142,0.234657,0.627761,
+ 0,-0.065709,0.997839,0.431095,0.234657,0.627761,
+ 0,-0.131708,0.991289,0.480142,0.234657,0.627761,
+ 0,-0.131708,0.991289,0.431095,-0.223786,0.50375,
+ 0,-0.131708,0.991289,0.480142,-0.223786,0.50375,
+ -0.000004,0.101312,-0.994855,0.455529,-0.260975,0.467139,
+ -0.000011,-0.000002,-1,0,-0.285801,0.467144,
+ -0.000011,0,-1,0,-0.260975,0.467144,
+ -0.000011,-0.000002,-1,0,-0.285801,0.467144,
+ -0.000004,0.101312,-0.994855,0.455529,-0.260975,0.467139,
+ -0.000004,-0.000001,-1,0.465574,-0.285801,0.467139,
+ -0.000004,-0.000001,-1,0.465574,-0.285801,0.467139,
+ -0.000004,0.101312,-0.994855,0.455529,-0.260975,0.467139,
+ 0,0.084378,-0.996434,0.480142,-0.260975,0.467139,
+ -0.000004,-0.000001,-1,0.465574,-0.285801,0.467139,
+ 0,0.084378,-0.996434,0.480142,-0.260975,0.467139,
+ 0,0,-1,0.480142,-0.285801,0.467139,
+ -0.000012,0.000002,-1,0.465575,0.291396,0.602667,
+ -0.000023,0.000004,-1,0,0.260975,0.602678,
+ -0.000023,0,-1,0,0.291396,0.602678,
+ -0.000023,0.000004,-1,0,0.260975,0.602678,
+ -0.000012,0.000002,-1,0.465575,0.291396,0.602667,
+ -0.000008,0.084381,-0.996434,0.455529,0.260975,0.602667,
+ -0.000008,0.084381,-0.996434,0.455529,0.260975,0.602667,
+ -0.000012,0.000002,-1,0.465575,0.291396,0.602667,
+ 0,0.126682,-0.991943,0.480142,0.260975,0.602667,
+ 0,0.126682,-0.991943,0.480142,0.260975,0.602667,
+ -0.000012,0.000002,-1,0.465575,0.291396,0.602667,
+ 0,0,-1,0.480142,0.291396,0.602667,
+ 0,0.126682,-0.991943,0.480142,0.260975,0.602667,
+ -0.000004,0.101312,-0.994855,0.455529,-0.260975,0.467139,
+ -0.000008,0.084381,-0.996434,0.455529,0.260975,0.602667,
+ -0.000004,0.101312,-0.994855,0.455529,-0.260975,0.467139,
+ 0,0.126682,-0.991943,0.480142,0.260975,0.602667,
+ 0,0.084378,-0.996434,0.480142,-0.260975,0.467139,
+ 1,0,0,0.480142,0.260975,0.602667,
+ 1,0,0,0.480142,-0.223786,0.50375,
+ 1,0,0,0.480142,-0.260975,0.467139,
+ 1,0,0,0.480142,-0.223786,0.50375,
+ 1,0,0,0.480142,0.260975,0.602667,
+ 1,0,0,0.480142,0.234657,0.627761,
+ 1,0,0,0.019519,-0.260975,0.020372,
+ 1,0,0,0.019519,-0.247708,0.447625,
+ 1,0,0,0.019519,-0.260975,0.447625,
+ 1,0,0,0.019519,-0.247708,0.447625,
+ 1,0,0,0.019519,-0.260975,0.020372,
+ 1,0,0,0.019519,-0.247708,0.020372,
+ 0,0,1,0.019519,-0.260975,0.020372,
+ 0,0,1,0.43601,-0.247708,0.020372,
+ 0,0,1,0.019519,-0.247708,0.020372,
+ 0,0,1,0.43601,-0.247708,0.020372,
+ 0,0,1,0.019519,-0.260975,0.020372,
+ 0,0,1,0.43601,-0.260975,0.020372,
+ -1,0,0,0.43601,-0.247708,0.44762,
+ -1,0,0,0.43601,-0.260975,0.020372,
+ -1,0,0,0.43601,-0.260975,0.44762,
+ -1,0,0,0.43601,-0.260975,0.020372,
+ -1,0,0,0.43601,-0.247708,0.44762,
+ -1,0,0,0.43601,-0.247708,0.020372,
+ -0.000011,0,-1,0.43601,-0.247708,0.44762,
+ -0.000011,0,-1,0.019519,-0.260975,0.447625,
+ -0.000011,0,-1,0.019519,-0.247708,0.447625,
+ -0.000011,0,-1,0.019519,-0.260975,0.447625,
+ -0.000011,0,-1,0.43601,-0.247708,0.44762,
+ -0.000011,0,-1,0.43601,-0.260975,0.44762,
+ -1,0,0,0.43601,0.260975,0.583149,
+ -1,0,0,0.43601,0.247708,0.020372,
+ -1,0,0,0.43601,0.247708,0.583149,
+ -1,0,0,0.43601,0.247708,0.020372,
+ -1,0,0,0.43601,0.260975,0.583149,
+ -1,0,0,0.43601,0.260975,0.020372,
+ 0,0,1,0.019519,0.247708,0.020372,
+ 0,0,1,0.43601,0.260975,0.020372,
+ 0,0,1,0.019519,0.260975,0.020372,
+ 0,0,1,0.43601,0.260975,0.020372,
+ 0,0,1,0.019519,0.247708,0.020372,
+ 0,0,1,0.43601,0.247708,0.020372,
+ 1,0,0,0.019519,0.247708,0.020372,
+ 1,0,0,0.019519,0.260975,0.583158,
+ 1,0,0,0.019519,0.247708,0.583158,
+ 1,0,0,0.019519,0.260975,0.583158,
+ 1,0,0,0.019519,0.247708,0.020372,
+ 1,0,0,0.019519,0.260975,0.020372,
+ -0.000023,0,-1,0.43601,0.260975,0.583149,
+ -0.000023,0,-1,0.019519,0.247708,0.583158,
+ -0.000023,0,-1,0.019519,0.260975,0.583158,
+ -0.000023,0,-1,0.019519,0.247708,0.583158,
+ -0.000023,0,-1,0.43601,0.260975,0.583149,
+ -0.000023,0,-1,0.43601,0.247708,0.583149,
+ 1,0,0,0.455529,-0.260975,0.000853,
+ 1,0,0,0.455529,-0.242083,0.019745,
+ 1,0,0,0.455529,-0.260975,0.467139,
+ 1,0,0,0.455529,-0.242083,0.019745,
+ 1,0,0,0.455529,-0.260975,0.000853,
+ 1,0,0,0.455529,0.260975,0.000853,
+ 1,0,0,0.455529,-0.242083,0.019745,
+ 1,0,0,0.455529,0.260975,0.000853,
+ 1,0,0,0.455529,0.242083,0.019745,
+ 1,0,0,0.455529,0.242083,0.019745,
+ 1,0,0,0.455529,0.260975,0.000853,
+ 1,0,0,0.455529,0.242083,0.578243,
+ 1,0,0,0.455529,-0.260975,0.467139,
+ 1,0,0,0.455529,-0.242083,0.452526,
+ 1,0,0,0.455529,0.260975,0.602667,
+ 1,0,0,0.455529,-0.242083,0.452526,
+ 1,0,0,0.455529,-0.260975,0.467139,
+ 1,0,0,0.455529,-0.242083,0.019745,
+ 1,0,0,0.455529,0.260975,0.602667,
+ 1,0,0,0.455529,-0.242083,0.452526,
+ 1,0,0,0.455529,0.242083,0.578243,
+ 1,0,0,0.455529,0.260975,0.602667,
+ 1,0,0,0.455529,0.242083,0.578243,
+ 1,0,0,0.455529,0.260975,0.000853,
+ 0,0.251323,-0.967903,0.455529,0.242083,0.578243,
+ 0,0.251323,-0.967903,0.442262,-0.242083,0.452526,
+ 0,0.251323,-0.967903,0.442262,0.242083,0.578243,
+ 0,0.251323,-0.967903,0.442262,-0.242083,0.452526,
+ 0,0.251323,-0.967903,0.455529,0.242083,0.578243,
+ 0,0.251323,-0.967903,0.455529,-0.242083,0.452526,
+ 0,1,0,0.442262,-0.242083,0.452526,
+ 0,1,0,0.455529,-0.242083,0.019745,
+ 0,1,0,0.442262,-0.242083,0.019745,
+ 0,1,0,0.455529,-0.242083,0.019745,
+ 0,1,0,0.442262,-0.242083,0.452526,
+ 0,1,0,0.455529,-0.242083,0.452526,
+ 0,0,1,0.442262,-0.242083,0.019745,
+ 0,0,1,0.455529,0.242083,0.019745,
+ 0,0,1,0.442262,0.242083,0.019745,
+ 0,0,1,0.455529,0.242083,0.019745,
+ 0,0,1,0.442262,-0.242083,0.019745,
+ 0,0,1,0.455529,-0.242083,0.019745,
+ 0,-1,0,0.455529,0.242083,0.578243,
+ 0,-1,0,0.442262,0.242083,0.019745,
+ 0,-1,0,0.455529,0.242083,0.019745,
+ 0,-1,0,0.442262,0.242083,0.019745,
+ 0,-1,0,0.455529,0.242083,0.578243,
+ 0,-1,0,0.442262,0.242083,0.578243,
+ 0.000001,1,0,0.448896,0.094026,0.260961,
+ 0.000001,1,0,0.519858,0.094026,0.411797,
+ 0.000001,1,0,0.519858,0.094026,0.260961,
+ 0.000001,1,0,0.519858,0.094026,0.411797,
+ 0.000001,1,0,0.448896,0.094026,0.260961,
+ 0.000001,1,0,0.448896,0.094026,0.411797,
+ -0.000001,-1,0,0.519858,-0.049899,0.260961,
+ -0.000001,-1,0,0.509245,-0.049899,0.271574,
+ -0.000001,-1,0,0.448896,-0.049899,0.260961,
+ -0.000001,-1,0,0.509245,-0.049899,0.271574,
+ -0.000001,-1,0,0.519858,-0.049899,0.260961,
+ 0,-1,0,0.519858,-0.049899,0.411797,
+ -0.000001,-1,0,0.509245,-0.049899,0.271574,
+ 0,-1,0,0.519858,-0.049899,0.411797,
+ 0,-1,0,0.509245,-0.049899,0.401184,
+ 0,-1,0,0.509245,-0.049899,0.401184,
+ 0,-1,0,0.519858,-0.049899,0.411797,
+ 0,-1,0,0.459509,-0.049899,0.401184,
+ -0.000001,-1,0,0.448896,-0.049899,0.260961,
+ -0.000001,-1,0,0.459509,-0.049899,0.271574,
+ 0,-1,0,0.448896,-0.049899,0.411797,
+ -0.000001,-1,0,0.459509,-0.049899,0.271574,
+ -0.000001,-1,0,0.448896,-0.049899,0.260961,
+ -0.000001,-1,0,0.509245,-0.049899,0.271574,
+ 0,-1,0,0.448896,-0.049899,0.411797,
+ -0.000001,-1,0,0.459509,-0.049899,0.271574,
+ 0,-1,0,0.459509,-0.049899,0.401184,
+ 0,-1,0,0.448896,-0.049899,0.411797,
+ 0,-1,0,0.459509,-0.049899,0.401184,
+ 0,-1,0,0.519858,-0.049899,0.411797,
+ 1,0,0,0.519858,0.094026,0.260961,
+ 1,0,0,0.519858,-0.049899,0.411797,
+ 1,0,0,0.519858,-0.049899,0.260961,
+ 1,0,0,0.519858,-0.049899,0.411797,
+ 1,0,0,0.519858,0.094026,0.260961,
+ 1,0,0,0.519858,0.094026,0.411797,
+ -0.000001,0,-1,0.448896,0.094026,0.260961,
+ -0.000001,0,-1,0.519858,-0.049899,0.260961,
+ -0.000001,0,-1,0.448896,-0.049899,0.260961,
+ -0.000001,0,-1,0.519858,-0.049899,0.260961,
+ -0.000001,0,-1,0.448896,0.094026,0.260961,
+ -0.000001,0,-1,0.519858,0.094026,0.260961,
+ -1,0,0,0.448896,0.094026,0.411797,
+ -1,0,0,0.448896,-0.049899,0.260961,
+ -1,0,0,0.448896,-0.049899,0.411797,
+ -1,0,0,0.448896,-0.049899,0.260961,
+ -1,0,0,0.448896,0.094026,0.411797,
+ -1,0,0,0.448896,0.094026,0.260961,
+ 0,0,1,0.519858,-0.049899,0.411797,
+ 0,0,1,0.448896,0.094026,0.411797,
+ 0,0,1,0.448896,-0.049899,0.411797,
+ 0,0,1,0.448896,0.094026,0.411797,
+ 0,0,1,0.519858,-0.049899,0.411797,
+ 0,0,1,0.519858,0.094026,0.411797,
+ 0,0.942079,-0.335391,0.396973,-0.204299,0.00019,
+ 0,0.942079,-0.335391,0.43601,-0.213745,-0.026342,
+ 0,0.942079,-0.335391,0.416491,-0.213745,-0.026342,
+ 0,0.942079,-0.335391,0.43601,-0.213745,-0.026342,
+ 0,0.942079,-0.335391,0.396973,-0.204299,0.00019,
+ 0,0.942079,-0.335391,0.43601,-0.204299,0.00019,
+ 0,-0.942079,-0.335391,0.43601,-0.242083,0.00019,
+ 0,-0.942079,-0.335391,0.416491,-0.232637,-0.026342,
+ 0,-0.942079,-0.335391,0.43601,-0.232637,-0.026342,
+ 0,-0.942079,-0.335391,0.416491,-0.232637,-0.026342,
+ 0,-0.942079,-0.335391,0.43601,-0.242083,0.00019,
+ 0,-0.942079,-0.335391,0.396973,-0.242083,0.00019,
+ 0,0,-1,0.43601,0.232637,-0.026342,
+ 0,0,-1,0.416491,0.213745,-0.026342,
+ 0,0,-1,0.416491,0.232637,-0.026342,
+ 0,0,-1,0.416491,0.213745,-0.026342,
+ 0,0,-1,0.43601,0.232637,-0.026342,
+ 0,0,-1,0.43601,0.213745,-0.026342,
+ 1,0,0,0.43601,0.213745,-0.026342,
+ 1,0,0,0.43601,0.242083,0.00019,
+ 1,0,0,0.43601,0.204299,0.00019,
+ 1,0,0,0.43601,0.242083,0.00019,
+ 1,0,0,0.43601,0.213745,-0.026342,
+ 1,0,0,0.43601,0.232637,-0.026342,
+ -0.805513,0,-0.592578,0.396973,0.242083,0.00019,
+ -0.805513,0,-0.592578,0.416491,0.213745,-0.026342,
+ -0.805513,0,-0.592577,0.396973,0.204299,0.00019,
+ -0.805513,0,-0.592578,0.416491,0.213745,-0.026342,
+ -0.805513,0,-0.592578,0.396973,0.242083,0.00019,
+ -0.805513,0,-0.592578,0.416491,0.232637,-0.026342,
+ 0,-0.942079,-0.335391,0.43601,0.204299,0.00019,
+ 0,-0.942079,-0.335391,0.416491,0.213745,-0.026342,
+ 0,-0.942079,-0.335391,0.43601,0.213745,-0.026342,
+ 0,-0.942079,-0.335391,0.416491,0.213745,-0.026342,
+ 0,-0.942079,-0.335391,0.43601,0.204299,0.00019,
+ 0,-0.942079,-0.335391,0.396973,0.204299,0.00019,
+ 0,0.942079,-0.335391,0.396973,0.242083,0.00019,
+ 0,0.942079,-0.335391,0.43601,0.232637,-0.026342,
+ 0,0.942079,-0.335391,0.416491,0.232637,-0.026342,
+ 0,0.942079,-0.335391,0.43601,0.232637,-0.026342,
+ 0,0.942079,-0.335391,0.396973,0.242083,0.00019,
+ 0,0.942079,-0.335391,0.43601,0.242083,0.00019,
+ 1,0,0,0.43601,-0.232637,-0.026342,
+ 1,0,0,0.43601,-0.204299,0.00019,
+ 1,0,0,0.43601,-0.242083,0.00019,
+ 1,0,0,0.43601,-0.204299,0.00019,
+ 1,0,0,0.43601,-0.232637,-0.026342,
+ 1,0,0,0.43601,-0.213745,-0.026342,
+ -0.805513,0,-0.592578,0.396973,-0.204299,0.00019,
+ -0.805513,0,-0.592578,0.416491,-0.232637,-0.026342,
+ -0.805513,0,-0.592577,0.396973,-0.242083,0.00019,
+ -0.805513,0,-0.592578,0.416491,-0.232637,-0.026342,
+ -0.805513,0,-0.592578,0.396973,-0.204299,0.00019,
+ -0.805513,0,-0.592578,0.416491,-0.213745,-0.026342,
+ 0,0,-1,0.43601,-0.213745,-0.026342,
+ 0,0,-1,0.416491,-0.232637,-0.026342,
+ 0,0,-1,0.416491,-0.213745,-0.026342,
+ 0,0,-1,0.416491,-0.232637,-0.026342,
+ 0,0,-1,0.43601,-0.213745,-0.026342,
+ 0,0,-1,0.43601,-0.232637,-0.026342,
+ 1,0,0,0.480142,-0.260975,0.467139,
+ 1,0,0,0.480142,-0.285801,0.50375,
+ 1,0,0,0.480142,-0.285801,0.467139,
+ 1,0,0,0.480142,-0.285801,0.50375,
+ 1,0,0,0.480142,-0.260975,0.467139,
+ 1,0,0,0.480142,-0.223786,0.50375,
+ 1,0,0,0.480142,0.260975,0.602667,
+ 1,0,0,0.480142,0.291396,0.627761,
+ 1,0,0,0.480142,0.234657,0.627761,
+ 1,0,0,0.480142,0.291396,0.627761,
+ 1,0,0,0.480142,0.260975,0.602667,
+ 1,0,0,0.480142,0.291396,0.602667
+};
+static const struct gllist dumpster_model_frame_half_frame = {
+ GL_N3F_V3F, GL_TRIANGLES, 360, dumpster_model_frame_half_data, 0
+};
+const struct gllist *dumpster_model_frame_half = &dumpster_model_frame_half_frame;
+
+static const float dumpster_model_hinges_half_data[] = {
+ 0,0.707107,0.707107,0.038749,0.287999,0.661827,
+ 0,0.81811,0.575061,0.14439,0.294897,0.654929,
+ 0,0.907073,0.420974,0.038749,0.294897,0.654929,
+ 0,0.81811,0.575061,0.14439,0.294897,0.654929,
+ 0,0.707107,0.707107,0.038749,0.287999,0.661827,
+ 0,0.707107,0.707107,0.14439,0.287999,0.661827,
+ 0,-0.996035,-0.088962,0.14439,0.259731,0.645507,
+ 0,-0.965926,-0.258819,0.038749,0.262256,0.636084,
+ 0,-0.965926,-0.258819,0.14439,0.262256,0.636084,
+ 0,-0.965926,-0.258819,0.038749,0.262256,0.636084,
+ 0,-0.996035,-0.088962,0.14439,0.259731,0.645507,
+ 0,-0.996035,0.088962,0.038749,0.259731,0.645507,
+ 0,-0.258819,-0.965926,0.14439,0.278577,0.626661,
+ 0,-0.5,-0.866025,0.038749,0.269154,0.629186,
+ 0,-0.258819,-0.965926,0.038749,0.278577,0.626661,
+ 0,-0.5,-0.866025,0.038749,0.269154,0.629186,
+ 0,-0.258819,-0.965926,0.14439,0.278577,0.626661,
+ 0,-0.5,-0.866025,0.14439,0.269154,0.629186,
+ 0,0.707107,-0.707107,0.14439,0.294897,0.636084,
+ 0,0.575061,-0.81811,0.038749,0.287999,0.629186,
+ 0,0.707107,-0.707107,0.038749,0.294897,0.636084,
+ 0,0.575061,-0.81811,0.038749,0.287999,0.629186,
+ 0,0.707107,-0.707107,0.14439,0.294897,0.636084,
+ 0,0.420974,-0.907073,0.14439,0.287999,0.629186,
+ 0,-0.965926,0.258819,0.14439,0.262256,0.654929,
+ 0,-0.996035,0.088962,0.038749,0.259731,0.645507,
+ 0,-0.996035,-0.088962,0.14439,0.259731,0.645507,
+ 0,-0.996035,0.088962,0.038749,0.259731,0.645507,
+ 0,-0.965926,0.258819,0.14439,0.262256,0.654929,
+ 0,-0.965926,0.258819,0.038749,0.262256,0.654929,
+ 0,0.907073,0.420974,0.038749,0.294897,0.654929,
+ 0,0.965926,0.258819,0.14439,0.297422,0.645507,
+ 0,0.965926,0.258819,0.038749,0.297422,0.645507,
+ 0,0.965926,0.258819,0.14439,0.297422,0.645507,
+ 0,0.907073,0.420974,0.038749,0.294897,0.654929,
+ 0,0.81811,0.575061,0.14439,0.294897,0.654929,
+ 0,-0.707107,-0.707107,0.14439,0.262256,0.636084,
+ 0,-0.5,-0.866025,0.038749,0.269154,0.629186,
+ 0,-0.5,-0.866025,0.14439,0.269154,0.629186,
+ 0,-0.5,-0.866025,0.038749,0.269154,0.629186,
+ 0,-0.707107,-0.707107,0.14439,0.262256,0.636084,
+ 0,-0.707107,-0.707107,0.038749,0.262256,0.636084,
+ 0,0.965926,-0.258819,0.038749,0.297422,0.645507,
+ 0,0.965926,-0.258819,0.14439,0.294897,0.636084,
+ 0,0.965926,-0.258819,0.038749,0.294897,0.636084,
+ 0,0.965926,-0.258819,0.14439,0.294897,0.636084,
+ 0,0.965926,-0.258819,0.038749,0.297422,0.645507,
+ 0,0.965926,-0.258819,0.14439,0.297422,0.645507,
+ 0,-0.707107,0.707107,0.14439,0.262256,0.654929,
+ 0,-0.575061,0.81811,0.038749,0.269154,0.661827,
+ 0,-0.707107,0.707107,0.038749,0.262256,0.654929,
+ 0,-0.575061,0.81811,0.038749,0.269154,0.661827,
+ 0,-0.707107,0.707107,0.14439,0.262256,0.654929,
+ 0,-0.420974,0.907073,0.14439,0.269154,0.661827,
+ 0,-0.420974,0.907073,0.14439,0.269154,0.661827,
+ 0,-0.258819,0.965926,0.038749,0.278577,0.664352,
+ 0,-0.575061,0.81811,0.038749,0.269154,0.661827,
+ 0,-0.258819,0.965926,0.038749,0.278577,0.664352,
+ 0,-0.420974,0.907073,0.14439,0.269154,0.661827,
+ 0,-0.258819,0.965926,0.14439,0.278577,0.664352,
+ 0,0.258819,0.965926,0.14439,0.278577,0.664352,
+ 0,0.258819,0.965926,0.038749,0.287999,0.661827,
+ 0,0.258819,0.965926,0.038749,0.278577,0.664352,
+ 0,0.258819,0.965926,0.038749,0.287999,0.661827,
+ 0,0.258819,0.965926,0.14439,0.278577,0.664352,
+ 0,0.258819,0.965926,0.14439,0.287999,0.661827,
+ 0,0.420974,-0.907073,0.14439,0.287999,0.629186,
+ 0,0.258819,-0.965926,0.038749,0.278577,0.626661,
+ 0,0.575061,-0.81811,0.038749,0.287999,0.629186,
+ 0,0.258819,-0.965926,0.038749,0.278577,0.626661,
+ 0,0.420974,-0.907073,0.14439,0.287999,0.629186,
+ 0,0.258819,-0.965926,0.14439,0.278577,0.626661,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.262256,0.654929,
+ -1,0,0,0.038749,0.269154,0.661827,
+ 0,0.258819,0.965926,0.420704,0.278577,0.664352,
+ 0,0.258819,0.965926,0.315063,0.287999,0.661827,
+ 0,0.258819,0.965926,0.315063,0.278577,0.664352,
+ 0,0.258819,0.965926,0.315063,0.287999,0.661827,
+ 0,0.258819,0.965926,0.420704,0.278577,0.664352,
+ 0,0.258819,0.965926,0.420704,0.287999,0.661827,
+ 0,-0.965926,0.258819,0.420704,0.262256,0.654929,
+ 0,-0.996035,0.088962,0.315063,0.259731,0.645507,
+ 0,-0.996035,-0.088962,0.420704,0.259731,0.645507,
+ 0,-0.996035,0.088962,0.315063,0.259731,0.645507,
+ 0,-0.965926,0.258819,0.420704,0.262256,0.654929,
+ 0,-0.965926,0.258819,0.315063,0.262256,0.654929,
+ 0,-0.258819,-0.965926,0.420704,0.278577,0.626661,
+ 0,-0.5,-0.866025,0.315063,0.269154,0.629186,
+ 0,-0.258819,-0.965926,0.315063,0.278577,0.626661,
+ 0,-0.5,-0.866025,0.315063,0.269154,0.629186,
+ 0,-0.258819,-0.965926,0.420704,0.278577,0.626661,
+ 0,-0.5,-0.866025,0.420704,0.269154,0.629186,
+ 0,-0.707107,0.707107,0.420704,0.262256,0.654929,
+ 0,-0.575061,0.81811,0.315063,0.269154,0.661827,
+ 0,-0.707107,0.707107,0.315063,0.262256,0.654929,
+ 0,-0.575061,0.81811,0.315063,0.269154,0.661827,
+ 0,-0.707107,0.707107,0.420704,0.262256,0.654929,
+ 0,-0.420974,0.907073,0.420704,0.269154,0.661827,
+ 0,0.707107,0.707107,0.315063,0.287999,0.661827,
+ 0,0.81811,0.575061,0.420704,0.294897,0.654929,
+ 0,0.907073,0.420974,0.315063,0.294897,0.654929,
+ 0,0.81811,0.575061,0.420704,0.294897,0.654929,
+ 0,0.707107,0.707107,0.315063,0.287999,0.661827,
+ 0,0.707107,0.707107,0.420704,0.287999,0.661827,
+ 1,0,0,0.14439,0.297422,0.645507,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.294897,0.636084,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.269154,0.629186,
+ -1,0,0,0.315063,0.262256,0.636084,
+ 0,0.965926,-0.258819,0.315063,0.297422,0.645507,
+ 0,0.965926,-0.258819,0.420704,0.294897,0.636084,
+ 0,0.965926,-0.258819,0.315063,0.294897,0.636084,
+ 0,0.965926,-0.258819,0.420704,0.294897,0.636084,
+ 0,0.965926,-0.258819,0.315063,0.297422,0.645507,
+ 0,0.965926,-0.258819,0.420704,0.297422,0.645507,
+ 0,0.707107,-0.707107,0.420704,0.294897,0.636084,
+ 0,0.575061,-0.81811,0.315063,0.287999,0.629186,
+ 0,0.707107,-0.707107,0.315063,0.294897,0.636084,
+ 0,0.575061,-0.81811,0.315063,0.287999,0.629186,
+ 0,0.707107,-0.707107,0.420704,0.294897,0.636084,
+ 0,0.420974,-0.907073,0.420704,0.287999,0.629186,
+ 0,0.420974,-0.907073,0.420704,0.287999,0.629186,
+ 0,0.258819,-0.965926,0.315063,0.278577,0.626661,
+ 0,0.575061,-0.81811,0.315063,0.287999,0.629186,
+ 0,0.258819,-0.965926,0.315063,0.278577,0.626661,
+ 0,0.420974,-0.907073,0.420704,0.287999,0.629186,
+ 0,0.258819,-0.965926,0.420704,0.278577,0.626661,
+ 0,0.907073,0.420974,0.315063,0.294897,0.654929,
+ 0,0.965926,0.258819,0.420704,0.297422,0.645507,
+ 0,0.965926,0.258819,0.315063,0.297422,0.645507,
+ 0,0.965926,0.258819,0.420704,0.297422,0.645507,
+ 0,0.907073,0.420974,0.315063,0.294897,0.654929,
+ 0,0.81811,0.575061,0.420704,0.294897,0.654929,
+ 0,-0.707107,-0.707107,0.420704,0.262256,0.636084,
+ 0,-0.5,-0.866025,0.315063,0.269154,0.629186,
+ 0,-0.5,-0.866025,0.420704,0.269154,0.629186,
+ 0,-0.5,-0.866025,0.315063,0.269154,0.629186,
+ 0,-0.707107,-0.707107,0.420704,0.262256,0.636084,
+ 0,-0.707107,-0.707107,0.315063,0.262256,0.636084,
+ 0,-0.420974,0.907073,0.420704,0.269154,0.661827,
+ 0,-0.258819,0.965926,0.315063,0.278577,0.664352,
+ 0,-0.575061,0.81811,0.315063,0.269154,0.661827,
+ 0,-0.258819,0.965926,0.315063,0.278577,0.664352,
+ 0,-0.420974,0.907073,0.420704,0.269154,0.661827,
+ 0,-0.258819,0.965926,0.420704,0.278577,0.664352,
+ 0,-0.996035,-0.088962,0.420704,0.259731,0.645507,
+ 0,-0.965926,-0.258819,0.315063,0.262256,0.636084,
+ 0,-0.965926,-0.258819,0.420704,0.262256,0.636084,
+ 0,-0.965926,-0.258819,0.315063,0.262256,0.636084,
+ 0,-0.996035,-0.088962,0.420704,0.259731,0.645507,
+ 0,-0.996035,0.088962,0.315063,0.259731,0.645507,
+ 1,0,0,0.420704,0.294897,0.636084,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.287999,0.629186,
+ -1,0,0,0.038749,0.287999,0.629186,
+ -1,0,0,0.038749,0.278577,0.626661,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.269154,0.661827,
+ -1,0,0,0.038749,0.278577,0.664352,
+ -1,0,0,0.038749,0.287999,0.661827,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.278577,0.664352,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.262256,0.636084,
+ -1,0,0,0.038749,0.259731,0.645507,
+ -1,0,0,0.038749,0.294897,0.636084,
+ -1,0,0,0.038749,0.287999,0.629186,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.294897,0.654929,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.287999,0.661827,
+ -1,0,0,0.038749,0.278577,0.626661,
+ -1,0,0,0.038749,0.269154,0.629186,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.269154,0.629186,
+ -1,0,0,0.038749,0.262256,0.636084,
+ -1,0,0,0.038749,0.297422,0.645507,
+ -1,0,0,0.038749,0.294897,0.636084,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.259731,0.645507,
+ -1,0,0,0.038749,0.262256,0.654929,
+ -1,0,0,0.038749,0.297422,0.645507,
+ -1,0,0,0.038749,0.278577,0.645507,
+ -1,0,0,0.038749,0.294897,0.654929,
+ 1,0,0,0.14439,0.297422,0.645507,
+ 1,0,0,0.14439,0.294897,0.654929,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 0,0,-1,0.14439,0.297422,0.645507,
+ 0,0.108104,-0.99414,0.038749,0.278577,0.645507,
+ 0,0,-1,0.038749,0.297422,0.645507,
+ 0,0.108104,-0.99414,0.038749,0.278577,0.645507,
+ 0,0,-1,0.14439,0.297422,0.645507,
+ 0,0,-1,0.038749,0.259731,0.645507,
+ 0,0,-1,0.038749,0.259731,0.645507,
+ 0,0,-1,0.14439,0.297422,0.645507,
+ 0,0,-1,0.14439,0.259731,0.645507,
+ 0.534871,-0.774969,-0.336653,0.14439,0.259731,0.645507,
+ 0.44821,0.649406,-0.614312,0.14439,0.297422,0.645507,
+ 0.920575,0.276172,-0.276172,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.278577,0.664352,
+ 1,0,0,0.14439,0.269154,0.661827,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.269154,0.629186,
+ 1,0,0,0.14439,0.278577,0.626661,
+ 0,0.99174,-0.128264,0.038749,0.278577,0.645507,
+ 0,1,0,0.14439,0.278577,0.626661,
+ 0,1,0,0.038749,0.278577,0.626661,
+ 0,1,0,0.14439,0.278577,0.626661,
+ 0,0.99174,-0.128264,0.038749,0.278577,0.645507,
+ 0,0.978392,-0.206759,0.14439,0.278577,0.645507,
+ 0,0.978392,-0.206759,0.14439,0.278577,0.645507,
+ 0,0.99174,-0.128264,0.038749,0.278577,0.645507,
+ 0,1,0,0.038749,0.278577,0.664352,
+ 0,0.978392,-0.206759,0.14439,0.278577,0.645507,
+ 0,1,0,0.038749,0.278577,0.664352,
+ 0,1,0,0.14439,0.278577,0.664352,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.269154,0.661827,
+ 1,0,0,0.14439,0.262256,0.654929,
+ 1,0,0,0.14439,0.287999,0.629186,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.278577,0.626661,
+ 0,-0.866025,-0.5,0.14439,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.038749,0.287999,0.629186,
+ 0,-0.866025,-0.5,0.14439,0.287999,0.629186,
+ 0,-0.866025,-0.5,0.038749,0.287999,0.629186,
+ 0,-0.866025,-0.5,0.14439,0.278577,0.645507,
+ 0,-0.743933,-0.668254,0.038749,0.278577,0.645507,
+ 0,-0.79474,-0.60695,0.038749,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.14439,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.14439,0.269154,0.661827,
+ 0,-0.79474,-0.60695,0.038749,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.14439,0.269154,0.661827,
+ 0,-0.866025,-0.5,0.038749,0.269154,0.661827,
+ 1,0,0,0.14439,0.294897,0.636084,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.287999,0.629186,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.262256,0.654929,
+ 1,0,0,0.14439,0.259731,0.645507,
+ 0,-0.5,-0.866025,0.14439,0.294897,0.636084,
+ 0,-0.403449,-0.915002,0.038749,0.278577,0.645507,
+ 0,-0.5,-0.866025,0.038749,0.294897,0.636084,
+ 0,-0.743933,-0.668254,0.038749,0.278577,0.645507,
+ 0,-0.5,-0.866025,0.14439,0.294897,0.636084,
+ 0,-0.5,-0.866025,0.038749,0.262256,0.654929,
+ 0,-0.5,-0.866025,0.038749,0.262256,0.654929,
+ 0,-0.5,-0.866025,0.14439,0.294897,0.636084,
+ 0,-0.5,-0.866025,0.14439,0.262256,0.654929,
+ -1,0,0,0.14439,0.262256,0.654929,
+ -1,0,0,0.14439,0.294897,0.636084,
+ -1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.259731,0.645507,
+ 1,0,0,0.14439,0.262256,0.636084,
+ 1,0,0,0.14439,0.294897,0.654929,
+ 1,0,0,0.14439,0.287999,0.661827,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 0,0.5,-0.866025,0.14439,0.294897,0.654929,
+ 0,0.5,-0.866025,0.038749,0.278577,0.645507,
+ 0,0.5,-0.866025,0.038749,0.294897,0.654929,
+ 0,0.5,-0.866025,0.038749,0.278577,0.645507,
+ 0,0.5,-0.866025,0.14439,0.294897,0.654929,
+ 0,0.5,-0.866025,0.038749,0.262256,0.636084,
+ 0,0.5,-0.866025,0.038749,0.262256,0.636084,
+ 0,0.5,-0.866025,0.14439,0.294897,0.654929,
+ 0,0.5,-0.866025,0.14439,0.262256,0.636084,
+ -1,0,0,0.14439,0.262256,0.636084,
+ -1,0,0,0.14439,0.294897,0.654929,
+ -1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 1,0,0,0.14439,0.262256,0.636084,
+ 1,0,0,0.14439,0.269154,0.629186,
+ 1,0,0,0.14439,0.287999,0.661827,
+ 1,0,0,0.14439,0.278577,0.664352,
+ 1,0,0,0.14439,0.278577,0.645507,
+ 0,0.743933,-0.668254,0.038749,0.278577,0.645507,
+ 0,0.866025,-0.5,0.14439,0.269154,0.629186,
+ 0,0.866025,-0.5,0.038749,0.269154,0.629186,
+ 0,0.866025,-0.5,0.14439,0.269154,0.629186,
+ 0,0.743933,-0.668254,0.038749,0.278577,0.645507,
+ 0,0.866025,-0.5,0.14439,0.278577,0.645507,
+ 0,0.965926,-0.258819,0.14439,0.278577,0.645507,
+ 0,0.965926,-0.258819,0.038749,0.278577,0.645507,
+ 0,0.866025,-0.5,0.038749,0.287999,0.661827,
+ 0,0.965926,-0.258819,0.14439,0.278577,0.645507,
+ 0,0.866025,-0.5,0.038749,0.287999,0.661827,
+ 0,0.866025,-0.5,0.14439,0.287999,0.661827,
+ -1,0,0,0.315063,0.287999,0.629186,
+ -1,0,0,0.315063,0.278577,0.626661,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.269154,0.661827,
+ -1,0,0,0.315063,0.278577,0.664352,
+ -1,0,0,0.315063,0.294897,0.636084,
+ -1,0,0,0.315063,0.287999,0.629186,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.297422,0.645507,
+ -1,0,0,0.315063,0.294897,0.636084,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.262256,0.654929,
+ -1,0,0,0.315063,0.269154,0.661827,
+ -1,0,0,0.315063,0.297422,0.645507,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.294897,0.654929,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.259731,0.645507,
+ -1,0,0,0.315063,0.262256,0.654929,
+ -1,0,0,0.315063,0.287999,0.661827,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.278577,0.664352,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.262256,0.636084,
+ -1,0,0,0.315063,0.259731,0.645507,
+ -1,0,0,0.315063,0.278577,0.626661,
+ -1,0,0,0.315063,0.269154,0.629186,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.294897,0.654929,
+ -1,0,0,0.315063,0.278577,0.645507,
+ -1,0,0,0.315063,0.287999,0.661827,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.269154,0.629186,
+ 1,0,0,0.420704,0.278577,0.626661,
+ 0,0.99174,-0.128264,0.315063,0.278577,0.645507,
+ 0,1,0,0.420704,0.278577,0.626661,
+ 0,1,0,0.315063,0.278577,0.626661,
+ 0,1,0,0.420704,0.278577,0.626661,
+ 0,0.99174,-0.128264,0.315063,0.278577,0.645507,
+ 0,0.978392,-0.206759,0.420704,0.278577,0.645507,
+ 0,0.978392,-0.206759,0.420704,0.278577,0.645507,
+ 0,0.99174,-0.128264,0.315063,0.278577,0.645507,
+ 0,1,0,0.315063,0.278577,0.664352,
+ 0,0.978392,-0.206759,0.420704,0.278577,0.645507,
+ 0,1,0,0.315063,0.278577,0.664352,
+ 0,1,0,0.420704,0.278577,0.664352,
+ 1,0,0,0.420704,0.287999,0.661827,
+ 1,0,0,0.420704,0.278577,0.664352,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.262256,0.636084,
+ 1,0,0,0.420704,0.269154,0.629186,
+ 0,0.743933,-0.668254,0.315063,0.278577,0.645507,
+ 0,0.866025,-0.5,0.420704,0.269154,0.629186,
+ 0,0.866025,-0.5,0.315063,0.269154,0.629186,
+ 0,0.866025,-0.5,0.420704,0.269154,0.629186,
+ 0,0.743933,-0.668254,0.315063,0.278577,0.645507,
+ 0,0.866025,-0.5,0.420704,0.278577,0.645507,
+ 0,0.965926,-0.258819,0.420704,0.278577,0.645507,
+ 0,0.965926,-0.258819,0.315063,0.278577,0.645507,
+ 0,0.866025,-0.5,0.315063,0.287999,0.661827,
+ 0,0.965926,-0.258819,0.420704,0.278577,0.645507,
+ 0,0.866025,-0.5,0.315063,0.287999,0.661827,
+ 0,0.866025,-0.5,0.420704,0.287999,0.661827,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.259731,0.645507,
+ 1,0,0,0.420704,0.262256,0.636084,
+ 1,0,0,0.420704,0.294897,0.654929,
+ 1,0,0,0.420704,0.287999,0.661827,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 0,0.5,-0.866025,0.420704,0.294897,0.654929,
+ 0,0.5,-0.866025,0.315063,0.278577,0.645507,
+ 0,0.5,-0.866025,0.315063,0.294897,0.654929,
+ 0,0.5,-0.866025,0.315063,0.278577,0.645507,
+ 0,0.5,-0.866025,0.420704,0.294897,0.654929,
+ 0,0.5,-0.866025,0.315063,0.262256,0.636084,
+ 0,0.5,-0.866025,0.315063,0.262256,0.636084,
+ 0,0.5,-0.866025,0.420704,0.294897,0.654929,
+ 0,0.5,-0.866025,0.420704,0.262256,0.636084,
+ -1,0,0,0.420704,0.262256,0.636084,
+ -1,0,0,0.420704,0.294897,0.654929,
+ -1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.278577,0.664352,
+ 1,0,0,0.420704,0.269154,0.661827,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.297422,0.645507,
+ 1,0,0,0.420704,0.294897,0.654929,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 0,0,-1,0.420704,0.297422,0.645507,
+ 0,0.258819,-0.965926,0.315063,0.278577,0.645507,
+ 0,0,-1,0.315063,0.297422,0.645507,
+ 0,0.258819,-0.965926,0.315063,0.278577,0.645507,
+ 0,0,-1,0.420704,0.297422,0.645507,
+ 0,0,-1,0.315063,0.259731,0.645507,
+ 0,0,-1,0.315063,0.259731,0.645507,
+ 0,0,-1,0.420704,0.297422,0.645507,
+ 0,0,-1,0.420704,0.259731,0.645507,
+ 0.534871,-0.774969,-0.336653,0.420704,0.259731,0.645507,
+ 0.44821,0.649406,-0.614312,0.420704,0.297422,0.645507,
+ 0.942809,0.235702,-0.235702,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.287999,0.629186,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.278577,0.626661,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.262256,0.654929,
+ 1,0,0,0.420704,0.259731,0.645507,
+ 0,-0.866025,-0.5,0.420704,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.315063,0.287999,0.629186,
+ 0,-0.866025,-0.5,0.420704,0.287999,0.629186,
+ 0,-0.866025,-0.5,0.315063,0.287999,0.629186,
+ 0,-0.866025,-0.5,0.420704,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.315063,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.315063,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.420704,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.420704,0.269154,0.661827,
+ 0,-0.866025,-0.5,0.315063,0.278577,0.645507,
+ 0,-0.866025,-0.5,0.420704,0.269154,0.661827,
+ 0,-0.866025,-0.5,0.315063,0.269154,0.661827,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.269154,0.661827,
+ 1,0,0,0.420704,0.262256,0.654929,
+ 1,0,0,0.420704,0.297422,0.645507,
+ 1,0,0,0.420704,0.278577,0.645507,
+ 1,0,0,0.420704,0.294897,0.636084,
+ 0,0.5,0.866025,0.420704,0.262256,0.654929,
+ 0,0.5,0.866025,0.315063,0.278577,0.645507,
+ 0,0.5,0.866025,0.315063,0.262256,0.654929,
+ 0,0.5,0.866025,0.315063,0.278577,0.645507,
+ 0,0.5,0.866025,0.420704,0.262256,0.654929,
+ 0,0.5,0.866025,0.315063,0.294897,0.636084,
+ 0,0.5,0.866025,0.315063,0.294897,0.636084,
+ 0,0.5,0.866025,0.420704,0.262256,0.654929,
+ 0,0.5,0.866025,0.420704,0.294897,0.636084,
+ 1,0,0,0.420704,0.294897,0.636084,
+ 1,0,0,0.420704,0.262256,0.654929,
+ 1,0,0,0.420704,0.278577,0.645507
+};
+static const struct gllist dumpster_model_hinges_half_frame = {
+ GL_N3F_V3F, GL_TRIANGLES, 432, dumpster_model_hinges_half_data, 0
+};
+const struct gllist *dumpster_model_hinges_half = &dumpster_model_hinges_half_frame;
+
+static const float dumpster_model_inside_half_data[] = {
+ 0,-0.261118,0.965307,0.431095,-0.223786,0.088711,
+ 0,-0.261118,0.965307,0,0.234657,0.212721,
+ 0,-0.261118,0.965307,0,-0.223786,0.088711,
+ 0,-0.261118,0.965307,0,0.234657,0.212721,
+ 0,-0.261118,0.965307,0.431095,-0.223786,0.088711,
+ 0,-0.261118,0.965307,0.431095,0.234657,0.212721,
+ 0,-1,0,0.431095,0.234657,0.627761,
+ 0,-1,0,0,0.234657,0.212721,
+ 0,-1,0,0.431095,0.234657,0.212721,
+ 0,-1,0,0,0.234657,0.212721,
+ 0,-1,0,0.431095,0.234657,0.627761,
+ 0,-1,0,0,0.234657,0.627761,
+ 0,1,0,0,-0.223786,0.50375,
+ 0,1,0,0.431095,-0.223786,0.088711,
+ 0,1,0,0,-0.223786,0.088711,
+ 0,1,0,0.431095,-0.223786,0.088711,
+ 0,1,0,0,-0.223786,0.50375,
+ 0,1,0,0.431095,-0.223786,0.50375,
+ -1,0,0,0.431095,0.234657,0.627761,
+ -1,0,0,0.431095,-0.223786,0.088711,
+ -1,0,0,0.431095,-0.223786,0.50375,
+ -1,0,0,0.431095,-0.223786,0.088711,
+ -1,0,0,0.431095,0.234657,0.627761,
+ -1,0,0,0.431095,0.234657,0.212721
+};
+static const struct gllist dumpster_model_inside_half_frame = {
+ GL_N3F_V3F, GL_TRIANGLES, 24, dumpster_model_inside_half_data, 0
+};
+const struct gllist *dumpster_model_inside_half = &dumpster_model_inside_half_frame;
+
+static const float dumpster_model_lid_data[] = {
+ 0.000021,-0.242057,0.970262,0.411943,-0.251833,0.533256,
+ 0.000016,-0.242055,0.970263,0.460077,-0.162101,0.555641,
+ 0.000014,-0.242057,0.970262,0.411943,-0.162101,0.555642,
+ 0.000016,-0.242055,0.970263,0.460077,-0.162101,0.555641,
+ 0.000021,-0.242057,0.970262,0.411943,-0.251833,0.533256,
+ 0.000007,-0.392342,0.919819,0.411943,-0.251836,0.533255,
+ 0.000016,-0.242055,0.970263,0.460077,-0.162101,0.555641,
+ 0.000007,-0.392342,0.919819,0.411943,-0.251836,0.533255,
+ 0,-0.536306,0.844024,0.460077,-0.251836,0.533255,
+ 1,0,0,0.049771,0.261795,0.649635,
+ 1,0,0,0.049771,-0.251836,0.533255,
+ 1,0,0,0.049771,-0.270321,0.516886,
+ 1,0,0,0.049771,-0.251836,0.533255,
+ 1,0,0,0.049771,0.261795,0.649635,
+ 1,0,0,0.049771,-0.162101,0.555642,
+ 1,0,0,0.049771,-0.162101,0.555642,
+ 1,0,0,0.049771,0.261795,0.649635,
+ 1,0,0,0.049771,0.148749,0.633191,
+ 1,0,0,0.049771,0.148749,0.633191,
+ 1,0,0,0.049771,0.261795,0.649635,
+ 1,0,0,0.049771,0.237787,0.655403,
+ 0,-0.242054,0.970263,0.14426,0.237771,0.655399,
+ 0.000005,-0.165396,0.986227,0.182918,0.237787,0.655403,
+ 0,0.077021,0.997029,0.14426,0.237787,0.655403,
+ 0.000005,-0.165396,0.986227,0.182918,0.237787,0.655403,
+ 0,-0.242054,0.970263,0.14426,0.237771,0.655399,
+ 0,-0.242054,0.970263,0.14426,0.237744,0.655392,
+ 0.000005,-0.165396,0.986227,0.182918,0.237787,0.655403,
+ 0,-0.242054,0.970263,0.14426,0.237744,0.655392,
+ 0.000016,-0.242056,0.970262,0.14426,-0.162101,0.555642,
+ 0.000005,-0.165396,0.986227,0.182918,0.237787,0.655403,
+ 0.000016,-0.242056,0.970262,0.14426,-0.162101,0.555642,
+ 0.000006,-0.332305,0.943172,0.14426,-0.251836,0.533255,
+ 0.000005,-0.165396,0.986227,0.182918,0.237787,0.655403,
+ 0.000006,-0.332305,0.943172,0.14426,-0.251836,0.533255,
+ 0,-0.242055,0.970263,0.182918,0.237775,0.6554,
+ 0,-0.242055,0.970263,0.182918,0.237775,0.6554,
+ 0.000006,-0.332305,0.943172,0.14426,-0.251836,0.533255,
+ 0,-0.242055,0.970263,0.182918,-0.251831,0.533256,
+ 0,-0.242055,0.970263,0.182918,-0.251831,0.533256,
+ 0.000006,-0.332305,0.943172,0.14426,-0.251836,0.533255,
+ 0,-0.536308,0.844022,0.182918,-0.251836,0.533255,
+ 0.995705,-0.02241,0.089831,0.182918,0.237775,0.6554,
+ 0.995705,-0.02241,0.089831,0.182918,-0.251831,0.533256,
+ 0.995705,-0.02241,0.089831,0.182918,0.237759,0.655396,
+ 0,0.077022,0.997029,0,0.237787,0.655403,
+ 0,0.156711,0.987645,0.049771,0.261795,0.649635,
+ 0,0.117625,0.993058,0,0.261795,0.649635,
+ 0,0.156711,0.987645,0.049771,0.261795,0.649635,
+ 0,0.077022,0.997029,0,0.237787,0.655403,
+ 0,-0.085677,0.996323,0.049771,0.237787,0.655403,
+ 0,-0.536304,0.844025,0,-0.270321,0.516886,
+ 0,-0.536309,0.844022,0.049771,-0.251836,0.533255,
+ -0.000007,-0.392342,0.919819,0,-0.251836,0.533255,
+ 0,-0.536309,0.844022,0.049771,-0.251836,0.533255,
+ 0,-0.536304,0.844025,0,-0.270321,0.516886,
+ 0,-0.66295,0.748664,0.049771,-0.270321,0.516886,
+ -1,0,0,0.411943,-0.147506,0.573235,
+ -1,0,0,0.411943,0.148749,0.633191,
+ -1,0,0,0.411943,-0.162101,0.555642,
+ -1,0,0,0.411943,0.148749,0.633191,
+ -1,0,0,0.411943,-0.147506,0.573235,
+ -1,0,0,0.411943,0.121591,0.640367,
+ 0,-0.536304,0.844025,0.411943,-0.270321,0.516886,
+ 0,-0.536306,0.844024,0.460077,-0.251836,0.533255,
+ 0.000007,-0.392342,0.919819,0.411943,-0.251836,0.533255,
+ 0,-0.536306,0.844024,0.460077,-0.251836,0.533255,
+ 0,-0.536304,0.844025,0.411943,-0.270321,0.516886,
+ 0,-0.465863,0.884857,0.460077,-0.270321,0.516886,
+ 1,0,0,0.335153,0.261795,0.649635,
+ 1,0,0,0.335153,-0.251836,0.533255,
+ 1,0,0,0.335153,-0.270321,0.516886,
+ 1,0,0,0.335153,-0.251836,0.533255,
+ 1,0,0,0.335153,0.261795,0.649635,
+ 0.992795,-0.029005,0.116265,0.335153,0.237778,0.655401,
+ 0.992795,-0.029005,0.116265,0.335153,0.237778,0.655401,
+ 1,0,0,0.335153,0.261795,0.649635,
+ 0.992795,-0.029005,0.116265,0.335153,0.237782,0.655402,
+ 0.992795,-0.029005,0.116265,0.335153,0.237782,0.655402,
+ 1,0,0,0.335153,0.261795,0.649635,
+ 0.983653,-0.043588,0.174722,0.335153,0.237787,0.655403,
+ 0,0.07702,0.99703,0.296566,0.237787,0.655403,
+ 0,0.233616,0.972329,0.335153,0.261795,0.649635,
+ 0,0.233616,0.972329,0.296566,0.261795,0.649635,
+ 0,0.233616,0.972329,0.335153,0.261795,0.649635,
+ 0,0.07702,0.99703,0.296566,0.237787,0.655403,
+ 0,-0.08568,0.996323,0.335153,0.237787,0.655403,
+ -1,0,0,0.296566,-0.251836,0.533255,
+ -1,0,0,0.296566,0.261795,0.649635,
+ -1,0,0,0.296566,-0.270321,0.516886,
+ -1,0,0,0.296566,0.261795,0.649635,
+ -1,0,0,0.296566,-0.251836,0.533255,
+ -1,0,0,0.296566,0.237787,0.655403,
+ 0,-0.66295,0.748664,0.296566,-0.270321,0.516886,
+ 0,-0.536308,0.844022,0.335153,-0.251836,0.533255,
+ 0,-0.354888,0.934909,0.296566,-0.251836,0.533255,
+ 0,-0.536308,0.844022,0.335153,-0.251836,0.533255,
+ 0,-0.66295,0.748664,0.296566,-0.270321,0.516886,
+ 0,-0.66295,0.748664,0.335153,-0.270321,0.516886,
+ 0,0.077021,0.997029,0.14426,0.237787,0.655403,
+ 0,0.233616,0.972329,0.182918,0.261795,0.649635,
+ 0,0.233616,0.972329,0.14426,0.261795,0.649635,
+ 0,0.233616,0.972329,0.182918,0.261795,0.649635,
+ 0,0.077021,0.997029,0.14426,0.237787,0.655403,
+ 0.000005,-0.165396,0.986227,0.182918,0.237787,0.655403,
+ 0,-0.66295,0.748664,0.14426,-0.270321,0.516886,
+ 0,-0.536308,0.844022,0.182918,-0.251836,0.533255,
+ 0.000006,-0.332305,0.943172,0.14426,-0.251836,0.533255,
+ 0,-0.536308,0.844022,0.182918,-0.251836,0.533255,
+ 0,-0.66295,0.748664,0.14426,-0.270321,0.516886,
+ 0,-0.66295,0.748664,0.182918,-0.270321,0.516886,
+ 0,-0.120502,0.992713,0,0.148749,0.633191,
+ 0,-0.085677,0.996323,0.049771,0.237787,0.655403,
+ 0,0.077022,0.997029,0,0.237787,0.655403,
+ 0,-0.085677,0.996323,0.049771,0.237787,0.655403,
+ 0,-0.120502,0.992713,0,0.148749,0.633191,
+ -0.000004,-0.044383,0.999015,0.049771,0.148749,0.633191,
+ 0,-0.242054,0.970263,0,-0.147506,0.573235,
+ 0,-0.078399,0.996922,0.049771,0.121591,0.640367,
+ 0,0.092202,0.99574,0,0.121591,0.640367,
+ 0,-0.078399,0.996922,0.049771,0.121591,0.640367,
+ 0,-0.242054,0.970263,0,-0.147506,0.573235,
+ 0,-0.242054,0.970263,0.049771,-0.147506,0.573235,
+ 0,-0.242051,0.970264,0.411943,0.237778,0.655401,
+ 0,-0.176561,0.98429,0.460077,0.237787,0.655403,
+ 0,0.077022,0.997029,0.411943,0.237787,0.655403,
+ 0,-0.176561,0.98429,0.460077,0.237787,0.655403,
+ 0,-0.242051,0.970264,0.411943,0.237778,0.655401,
+ 0,-0.242051,0.970264,0.411943,0.237768,0.655398,
+ 0,-0.176561,0.98429,0.460077,0.237787,0.655403,
+ 0,-0.242051,0.970264,0.411943,0.237768,0.655398,
+ 0,-0.242051,0.970264,0.411943,0.237759,0.655396,
+ 0,-0.176561,0.98429,0.460077,0.237787,0.655403,
+ 0,-0.242051,0.970264,0.411943,0.237759,0.655396,
+ 0,-0.242051,0.970264,0.411943,0.237744,0.655392,
+ 0,-0.176561,0.98429,0.460077,0.237787,0.655403,
+ 0,-0.242051,0.970264,0.411943,0.237744,0.655392,
+ 0,-0.120501,0.992713,0.411943,0.148749,0.633191,
+ 0,-0.176561,0.98429,0.460077,0.237787,0.655403,
+ 0,-0.120501,0.992713,0.411943,0.148749,0.633191,
+ 0.000004,-0.044383,0.999015,0.460077,0.148749,0.633191,
+ -0.000007,-0.769645,0.638473,0.049771,-0.162101,0.555642,
+ -0.000007,-0.769645,0.638473,0,-0.147506,0.573235,
+ -0.000013,-0.769654,0.638461,0,-0.162101,0.555641,
+ -0.000007,-0.769645,0.638473,0,-0.147506,0.573235,
+ -0.000007,-0.769645,0.638473,0.049771,-0.162101,0.555642,
+ 0,-0.769635,0.638484,0.049771,-0.147506,0.573235,
+ 0,0.092202,0.99574,0,0.121591,0.640367,
+ -0.000004,-0.044383,0.999015,0.049771,0.148749,0.633191,
+ 0,-0.120502,0.992713,0,0.148749,0.633191,
+ -0.000004,-0.044383,0.999015,0.049771,0.148749,0.633191,
+ 0,0.092202,0.99574,0,0.121591,0.640367,
+ 0,-0.078399,0.996922,0.049771,0.121591,0.640367,
+ -0.000007,-0.392342,0.919819,0,-0.251836,0.533255,
+ -0.000014,-0.242053,0.970263,0.049771,-0.162101,0.555642,
+ -0.000014,-0.242053,0.970263,0,-0.162101,0.555641,
+ -0.000014,-0.242053,0.970263,0.049771,-0.162101,0.555642,
+ -0.000007,-0.392342,0.919819,0,-0.251836,0.533255,
+ 0,-0.536309,0.844022,0.049771,-0.251836,0.533255,
+ 0,0.092202,0.99574,0.411943,0.121591,0.640367,
+ 0.000004,-0.044383,0.999015,0.460077,0.148749,0.633191,
+ 0,-0.120501,0.992713,0.411943,0.148749,0.633191,
+ 0.000004,-0.044383,0.999015,0.460077,0.148749,0.633191,
+ 0,0.092202,0.99574,0.411943,0.121591,0.640367,
+ 0,-0.078399,0.996922,0.460077,0.121591,0.640367,
+ 0,0.077022,0.997029,0.411943,0.237787,0.655403,
+ 0,0.233616,0.972329,0.460077,0.261795,0.649635,
+ 0,0.117625,0.993058,0.411943,0.261795,0.649635,
+ 0,0.233616,0.972329,0.460077,0.261795,0.649635,
+ 0,0.077022,0.997029,0.411943,0.237787,0.655403,
+ 0,-0.176561,0.98429,0.460077,0.237787,0.655403,
+ 0,-0.242054,0.970263,0.411943,-0.147506,0.573235,
+ 0,-0.078399,0.996922,0.460077,0.121591,0.640367,
+ 0,0.092202,0.99574,0.411943,0.121591,0.640367,
+ 0,-0.078399,0.996922,0.460077,0.121591,0.640367,
+ 0,-0.242054,0.970263,0.411943,-0.147506,0.573235,
+ 0,-0.242054,0.970263,0.460077,-0.147506,0.573235,
+ 0.000007,-0.769645,0.638473,0.460077,-0.147506,0.573235,
+ 0.000007,-0.769645,0.638473,0.411943,-0.162101,0.555642,
+ 0.000014,-0.769654,0.638461,0.460077,-0.162101,0.555641,
+ 0.000007,-0.769645,0.638473,0.411943,-0.162101,0.555642,
+ 0.000007,-0.769645,0.638473,0.460077,-0.147506,0.573235,
+ 0,-0.769635,0.638484,0.411943,-0.147506,0.573235,
+ 1,0,0,0.182918,0.261795,0.649635,
+ 1,0,0,0.182918,-0.251836,0.533255,
+ 1,0,0,0.182918,-0.270321,0.516886,
+ 1,0,0,0.182918,-0.251836,0.533255,
+ 1,0,0,0.182918,0.261795,0.649635,
+ 0.995705,-0.02241,0.089831,0.182918,-0.251831,0.533256,
+ 0.995705,-0.02241,0.089831,0.182918,-0.251831,0.533256,
+ 1,0,0,0.182918,0.261795,0.649635,
+ 0.995705,-0.02241,0.089831,0.182918,0.237759,0.655396,
+ 0.995705,-0.02241,0.089831,0.182918,0.237759,0.655396,
+ 1,0,0,0.182918,0.261795,0.649635,
+ 0.995705,-0.02241,0.089831,0.182918,0.237775,0.6554,
+ 0.995705,-0.02241,0.089831,0.182918,0.237775,0.6554,
+ 1,0,0,0.182918,0.261795,0.649635,
+ 1,0,0,0.182918,0.237787,0.655403,
+ -1,0,0,0.14426,-0.251836,0.533255,
+ -1,0,0,0.14426,0.261795,0.649635,
+ -1,0,0,0.14426,-0.270321,0.516886,
+ -1,0,0,0.14426,0.261795,0.649635,
+ -1,0,0,0.14426,-0.251836,0.533255,
+ -1,0,0,0.14426,-0.162101,0.555642,
+ -1,0,0,0.14426,0.261795,0.649635,
+ -1,0,0,0.14426,-0.162101,0.555642,
+ -1,0,0,0.14426,0.237744,0.655392,
+ -1,0,0,0.14426,0.261795,0.649635,
+ -1,0,0,0.14426,0.237744,0.655392,
+ -1,0,0,0.14426,0.237771,0.655399,
+ -1,0,0,0.14426,0.261795,0.649635,
+ -1,0,0,0.14426,0.237771,0.655399,
+ -1,0,0,0.14426,0.237787,0.655403,
+ 0,-0.970266,-0.24204,0.460077,-0.270321,0.516886,
+ 0,-0.970266,-0.24204,0,-0.267044,0.50375,
+ 0,-0.970266,-0.24204,0.460077,-0.267044,0.50375,
+ 0,-0.970266,-0.24204,0,-0.267044,0.50375,
+ 0,-0.970266,-0.24204,0.460077,-0.270321,0.516886,
+ 0,-0.970266,-0.24204,0,-0.270321,0.516886,
+ 0,-0.412008,0.91118,0,-0.270321,0.516886,
+ 0,-0.465863,0.884857,0.460077,-0.270321,0.516886,
+ 0,-0.536304,0.844025,0.411943,-0.270321,0.516886,
+ 0,0,-1,0,-0.270321,0.516886,
+ 0,0,-1,0.411943,-0.270321,0.516886,
+ 0,0,-1,0.335153,-0.270321,0.516886,
+ 0,-0.12193,0.992539,0,-0.270321,0.516886,
+ 0,0,1,0.335153,-0.270321,0.516886,
+ 0,0,1,0.296566,-0.270321,0.516886,
+ 0,0,-1,0,-0.270321,0.516886,
+ 0,0,-1,0.296566,-0.270321,0.516886,
+ 0,0,-1,0.182918,-0.270321,0.516886,
+ 0,0,-1,0,-0.270321,0.516886,
+ 0,0,-1,0.182918,-0.270321,0.516886,
+ 0,0,-1,0.14426,-0.270321,0.516886,
+ 0,0,-1,0,-0.270321,0.516886,
+ 0,0,-1,0.14426,-0.270321,0.516886,
+ 0,0,-1,0.049771,-0.270321,0.516886,
+ 0,0.970264,0.242047,0,0.267251,0.627761,
+ 0,0.970264,0.242047,0.460077,0.261795,0.649635,
+ 0,0.970264,0.242047,0.460077,0.267251,0.627761,
+ 0,0.970264,0.242047,0.460077,0.261795,0.649635,
+ 0,0.970264,0.242047,0,0.267251,0.627761,
+ 0,0.970264,0.242047,0,0.261795,0.649635,
+ 0,0.24204,-0.970266,0.460077,0.261795,0.649635,
+ 0,0.24204,-0.970266,0,0.261795,0.649635,
+ 0,0.04864,-0.998816,0.411943,0.261795,0.649635,
+ 0,0.117625,0.993058,0.411943,0.261795,0.649635,
+ 0,0.117625,0.993058,0,0.261795,0.649635,
+ 0,0.156711,0.987645,0.049771,0.261795,0.649635,
+ 0,0.04864,-0.998816,0.411943,0.261795,0.649635,
+ 0,0,-1,0.049771,0.261795,0.649635,
+ 0,0,-1,0.14426,0.261795,0.649635,
+ 0,0.04864,-0.998816,0.411943,0.261795,0.649635,
+ 0,0,-1,0.14426,0.261795,0.649635,
+ 0,0,-1,0.182918,0.261795,0.649635,
+ 0,0.04864,-0.998816,0.411943,0.261795,0.649635,
+ 0,0,-1,0.182918,0.261795,0.649635,
+ 0,0,-1,0.296566,0.261795,0.649635,
+ 0,0.04864,-0.998816,0.411943,0.261795,0.649635,
+ 0,0,-1,0.296566,0.261795,0.649635,
+ 0,0,-1,0.335153,0.261795,0.649635,
+ -0.000001,0.000004,-1,0.460077,0.267251,0.627761,
+ 0,0.087719,-0.996145,0,0.234657,0.627761,
+ 0,0,-1,0,0.267251,0.627761,
+ 0,0.087719,-0.996145,0,0.234657,0.627761,
+ -0.000001,0.000004,-1,0.460077,0.267251,0.627761,
+ 0,0.157993,-0.98744,0.116054,0.234657,0.627761,
+ 0,0.157993,-0.98744,0.116054,0.234657,0.627761,
+ -0.000001,0.000004,-1,0.460077,0.267251,0.627761,
+ 0,0.087719,-0.996145,0.14426,0.234657,0.627761,
+ 0,0.087719,-0.996145,0.14426,0.234657,0.627761,
+ -0.000001,0.000004,-1,0.460077,0.267251,0.627761,
+ -0.000006,0.08773,-0.996144,0.182847,0.234657,0.627761,
+ -0.000006,0.08773,-0.996144,0.182847,0.234657,0.627761,
+ -0.000001,0.000004,-1,0.460077,0.267251,0.627761,
+ -0.000004,0.158,-0.987439,0.182918,0.234657,0.627761,
+ -0.000004,0.158,-0.987439,0.182918,0.234657,0.627761,
+ -0.000001,0.000004,-1,0.460077,0.267251,0.627761,
+ 0,0.131708,-0.991289,0.296566,0.234657,0.627761,
+ 0,0.131708,-0.991289,0.296566,0.234657,0.627761,
+ -0.000001,0.000004,-1,0.460077,0.267251,0.627761,
+ 0,0.087719,-0.996145,0.335153,0.234657,0.627761,
+ 0,0.087719,-0.996145,0.335153,0.234657,0.627761,
+ -0.000001,0.000004,-1,0.460077,0.267251,0.627761,
+ 0.000006,0.197127,-0.980378,0.460077,0.234657,0.627761,
+ -1,0,0,0,-0.251836,0.533255,
+ -1,0,0,0,-0.267044,0.50375,
+ -1,0,0,0,-0.270321,0.516886,
+ -1,0,0,0,-0.267044,0.50375,
+ -1,0,0,0,-0.251836,0.533255,
+ -1,0,0,0,-0.223786,0.50375,
+ 0,0.105329,-0.994437,0.14426,-0.223786,0.50375,
+ 0,0,-1,0,-0.267044,0.50375,
+ 0,0.175438,-0.98449,0,-0.223786,0.50375,
+ 0,0,-1,0,-0.267044,0.50375,
+ 0,0.105329,-0.994437,0.14426,-0.223786,0.50375,
+ -0.000001,-0.000004,-1,0.460077,-0.267044,0.50375,
+ -0.000001,-0.000004,-1,0.460077,-0.267044,0.50375,
+ 0,0.105329,-0.994437,0.14426,-0.223786,0.50375,
+ -0.000006,0.131701,-0.991289,0.182847,-0.223786,0.50375,
+ -0.000001,-0.000004,-1,0.460077,-0.267044,0.50375,
+ -0.000006,0.131701,-0.991289,0.182847,-0.223786,0.50375,
+ -0.000002,0.157988,-0.987441,0.182918,-0.223786,0.50375,
+ -0.000001,-0.000004,-1,0.460077,-0.267044,0.50375,
+ -0.000002,0.157988,-0.987441,0.182918,-0.223786,0.50375,
+ 0,0.087719,-0.996145,0.296566,-0.223786,0.50375,
+ -0.000001,-0.000004,-1,0.460077,-0.267044,0.50375,
+ 0,0.087719,-0.996145,0.296566,-0.223786,0.50375,
+ 0,0.131708,-0.991289,0.335153,-0.223786,0.50375,
+ -0.000001,-0.000004,-1,0.460077,-0.267044,0.50375,
+ 0,0.131708,-0.991289,0.335153,-0.223786,0.50375,
+ 0.000007,0.175438,-0.98449,0.460077,-0.223786,0.50375,
+ 0,-0.354888,0.934909,0.296566,-0.251836,0.533255,
+ 0,-0.08568,0.996323,0.335153,0.237787,0.655403,
+ 0,0.07702,0.99703,0.296566,0.237787,0.655403,
+ 0,-0.08568,0.996323,0.335153,0.237787,0.655403,
+ 0,-0.354888,0.934909,0.296566,-0.251836,0.533255,
+ 0,-0.242055,0.970263,0.335153,0.237778,0.655401,
+ 0,-0.242055,0.970263,0.335153,0.237778,0.655401,
+ 0,-0.354888,0.934909,0.296566,-0.251836,0.533255,
+ 0,-0.536308,0.844022,0.335153,-0.251836,0.533255,
+ 0.983653,-0.043588,0.174722,0.335153,0.237787,0.655403,
+ 0.992795,-0.029005,0.116265,0.335153,0.237778,0.655401,
+ 0.992795,-0.029005,0.116265,0.335153,0.237782,0.655402,
+ -0.000004,0.158,-0.987439,0.182918,0.234657,0.627761,
+ -0.000006,0.131701,-0.991289,0.182847,-0.223786,0.50375,
+ -0.000006,0.08773,-0.996144,0.182847,0.234657,0.627761,
+ -0.000006,0.131701,-0.991289,0.182847,-0.223786,0.50375,
+ -0.000004,0.158,-0.987439,0.182918,0.234657,0.627761,
+ -0.000002,0.157988,-0.987441,0.182918,-0.223786,0.50375,
+ 0,0.157993,-0.98744,0.116054,0.234657,0.627761,
+ 0,0.175438,-0.98449,0,-0.223786,0.50375,
+ 0,0.087719,-0.996145,0,0.234657,0.627761,
+ 0,0.175438,-0.98449,0,-0.223786,0.50375,
+ 0,0.157993,-0.98744,0.116054,0.234657,0.627761,
+ 0,0.105329,-0.994437,0.14426,-0.223786,0.50375,
+ 0,0.105329,-0.994437,0.14426,-0.223786,0.50375,
+ 0,0.157993,-0.98744,0.116054,0.234657,0.627761,
+ 0,0.087719,-0.996145,0.14426,0.234657,0.627761,
+ 0,0.131708,-0.991289,0.296566,0.234657,0.627761,
+ -0.000002,0.157988,-0.987441,0.182918,-0.223786,0.50375,
+ -0.000004,0.158,-0.987439,0.182918,0.234657,0.627761,
+ -0.000002,0.157988,-0.987441,0.182918,-0.223786,0.50375,
+ 0,0.131708,-0.991289,0.296566,0.234657,0.627761,
+ 0,0.087719,-0.996145,0.296566,-0.223786,0.50375,
+ 0.000006,0.197127,-0.980378,0.460077,0.234657,0.627761,
+ 0,0.131708,-0.991289,0.335153,-0.223786,0.50375,
+ 0,0.087719,-0.996145,0.335153,0.234657,0.627761,
+ 0,0.131708,-0.991289,0.335153,-0.223786,0.50375,
+ 0.000006,0.197127,-0.980378,0.460077,0.234657,0.627761,
+ 0.000007,0.175438,-0.98449,0.460077,-0.223786,0.50375,
+ 1,0,0,0.460077,0.148749,0.633191,
+ 1,0,0,0.460077,-0.147506,0.573235,
+ 1,0,0,0.460077,-0.162101,0.555641,
+ 1,0,0,0.460077,-0.147506,0.573235,
+ 1,0,0,0.460077,0.148749,0.633191,
+ 1,0,0,0.460077,0.121591,0.640367,
+ 1,0,0,0.460077,-0.267044,0.50375,
+ 1,0,0,0.460077,-0.251836,0.533255,
+ 1,0,0,0.460077,-0.270321,0.516886,
+ 1,0,0,0.460077,-0.251836,0.533255,
+ 1,0,0,0.460077,-0.267044,0.50375,
+ 1,0,0,0.460077,-0.223786,0.50375,
+ 1,0,0,0.460077,-0.251836,0.533255,
+ 1,0,0,0.460077,-0.223786,0.50375,
+ 1,0,0,0.460077,-0.162101,0.555641,
+ 1,0,0,0.460077,-0.162101,0.555641,
+ 1,0,0,0.460077,-0.223786,0.50375,
+ 1,0,0,0.460077,0.234657,0.627761,
+ 1,0,0,0.460077,-0.162101,0.555641,
+ 1,0,0,0.460077,0.234657,0.627761,
+ 1,0,0,0.460077,0.148749,0.633191,
+ 1,0,0,0.460077,0.148749,0.633191,
+ 1,0,0,0.460077,0.234657,0.627761,
+ 1,0,0,0.460077,0.237787,0.655403,
+ 1,0,0,0.460077,0.237787,0.655403,
+ 1,0,0,0.460077,0.234657,0.627761,
+ 1,0,0,0.460077,0.267251,0.627761,
+ 1,0,0,0.460077,0.237787,0.655403,
+ 1,0,0,0.460077,0.267251,0.627761,
+ 1,0,0,0.460077,0.261795,0.649635,
+ 0.000022,0.26112,-0.965306,0.460225,-0.223786,0.50375,
+ 0.000007,0.175438,-0.98449,0.460077,-0.223786,0.50375,
+ 0.000006,0.197127,-0.980378,0.460077,0.234657,0.627761,
+ -1,0,0,0,0.237787,0.655403,
+ -1,0,0,0,0.267251,0.627761,
+ -1,0,0,0,0.234657,0.627761,
+ -1,0,0,0,0.267251,0.627761,
+ -1,0,0,0,0.237787,0.655403,
+ -1,0,0,0,0.261795,0.649635,
+ -1,0,0,0,-0.162101,0.555641,
+ -1,0,0,0,-0.223786,0.50375,
+ -1,0,0,0,-0.251836,0.533255,
+ -1,0,0,0,-0.223786,0.50375,
+ -1,0,0,0,-0.162101,0.555641,
+ -1,0,0,0,0.234657,0.627761,
+ -1,0,0,0,0.234657,0.627761,
+ -1,0,0,0,-0.162101,0.555641,
+ -1,0,0,0,0.148749,0.633191,
+ -1,0,0,0,0.234657,0.627761,
+ -1,0,0,0,0.148749,0.633191,
+ -1,0,0,0,0.237787,0.655403,
+ -1,0,0,0,-0.147506,0.573235,
+ -1,0,0,0,0.148749,0.633191,
+ -1,0,0,0,-0.162101,0.555641,
+ -1,0,0,0,0.148749,0.633191,
+ -1,0,0,0,-0.147506,0.573235,
+ -1,0,0,0,0.121591,0.640367,
+ -1,0,0,0.411943,-0.251836,0.533255,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,-0.270321,0.516886,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,-0.251836,0.533255,
+ -1,0,0,0.411943,-0.251833,0.533256,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,-0.251833,0.533256,
+ -1,0,0,0.411943,-0.162101,0.555642,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,-0.162101,0.555642,
+ -1,0,0,0.411943,0.148749,0.633191,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,0.148749,0.633191,
+ -1,0,0,0.411943,0.237744,0.655392,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,0.237744,0.655392,
+ -1,0,0,0.411943,0.237759,0.655396,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,0.237759,0.655396,
+ -1,0,0,0.411943,0.237768,0.655398,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,0.237768,0.655398,
+ -1,0,0,0.411943,0.237778,0.655401,
+ -1,0,0,0.411943,0.261795,0.649635,
+ -1,0,0,0.411943,0.237778,0.655401,
+ -1,0,0,0.411943,0.237787,0.655403,
+ 0.000014,-0.242057,0.970262,0.411943,-0.162101,0.555642,
+ 0.000004,-0.044383,0.999015,0.460077,0.148749,0.633191,
+ 0,-0.120501,0.992713,0.411943,0.148749,0.633191,
+ 0.000004,-0.044383,0.999015,0.460077,0.148749,0.633191,
+ 0.000014,-0.242057,0.970262,0.411943,-0.162101,0.555642,
+ 0.000016,-0.242055,0.970263,0.460077,-0.162101,0.555641,
+ 1,0,0,0.049771,0.148749,0.633191,
+ 1,0,0,0.049771,-0.147506,0.573235,
+ 1,0,0,0.049771,-0.162101,0.555642,
+ 1,0,0,0.049771,-0.147506,0.573235,
+ 1,0,0,0.049771,0.148749,0.633191,
+ 1,0,0,0.049771,0.121591,0.640367,
+ -0.000014,-0.242053,0.970263,0,-0.162101,0.555641,
+ -0.000004,-0.044383,0.999015,0.049771,0.148749,0.633191,
+ 0,-0.120502,0.992713,0,0.148749,0.633191,
+ -0.000004,-0.044383,0.999015,0.049771,0.148749,0.633191,
+ -0.000014,-0.242053,0.970263,0,-0.162101,0.555641,
+ -0.000014,-0.242053,0.970263,0.049771,-0.162101,0.555642
+};
+static const struct gllist dumpster_model_lid_frame = {
+ GL_N3F_V3F, GL_TRIANGLES, 453, dumpster_model_lid_data, 0
+};
+const struct gllist *dumpster_model_lid = &dumpster_model_lid_frame;
+
+static const float dumpster_model_lid_panels_data[] = {
+ 0,-0.242054,0.970263,0.049771,-0.270321,0.516886,
+ 0,-0.242054,0.970263,0.14426,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.049771,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.14426,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.049771,-0.270321,0.516886,
+ 0,-0.242054,0.970263,0.14426,-0.270321,0.516886,
+ 0,-0.242054,0.970263,0.182918,-0.270321,0.516886,
+ 0,-0.242054,0.970263,0.296566,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.182918,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.296566,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.182918,-0.270321,0.516886,
+ 0,-0.242054,0.970263,0.296566,-0.270321,0.516886,
+ 0,-0.242054,0.970263,0.335153,-0.270321,0.516886,
+ 0,-0.242054,0.970263,0.411943,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.335153,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.411943,0.261795,0.649635,
+ 0,-0.242054,0.970263,0.335153,-0.270321,0.516886,
+ 0,-0.242054,0.970263,0.411943,-0.270321,0.516886,
+ 0,0.542246,-0.84022,0.335153,-0.205301,0.520118,
+ 0,0.66295,-0.748664,0.296566,-0.223786,0.50375,
+ 0,0.405777,-0.913972,0.296566,-0.205301,0.520118,
+ 0,0.66295,-0.748664,0.296566,-0.223786,0.50375,
+ 0,0.542246,-0.84022,0.335153,-0.205301,0.520118,
+ 0,0.66295,-0.748664,0.335153,-0.223786,0.50375,
+ 0,-0.233616,-0.972329,0.335153,0.234657,0.627761,
+ 0,-0.070006,-0.997547,0.296566,0.21065,0.633529,
+ 0,-0.233616,-0.972329,0.296566,0.234657,0.627761,
+ 0,-0.070006,-0.997547,0.296566,0.21065,0.633529,
+ 0,-0.233616,-0.972329,0.335153,0.234657,0.627761,
+ 0,0.100284,-0.994959,0.335153,0.21065,0.633529,
+ -1,0,0,0.335153,-0.205301,0.520118,
+ -1,0,0,0.335153,0.234657,0.627761,
+ -1,0,0,0.335153,-0.223786,0.50375,
+ -1,0,0,0.335153,0.234657,0.627761,
+ -1,0,0,0.335153,-0.205301,0.520118,
+ -1,0,0,0.335153,0.21065,0.633529,
+ 1,0,0,0.296566,0.234657,0.627761,
+ 1,0,0,0.296566,-0.205301,0.520118,
+ 1,0,0,0.296566,-0.223786,0.50375,
+ 1,0,0,0.296566,-0.205301,0.520118,
+ 1,0,0,0.296566,0.234657,0.627761,
+ 1,0,0,0.296566,0.21065,0.633529,
+ 0,0.100284,-0.994959,0.335153,0.21065,0.633529,
+ 0,0.405777,-0.913972,0.296566,-0.205301,0.520118,
+ 0,-0.070006,-0.997547,0.296566,0.21065,0.633529,
+ 0,0.405777,-0.913972,0.296566,-0.205301,0.520118,
+ 0,0.100284,-0.994959,0.335153,0.21065,0.633529,
+ 0,0.542246,-0.84022,0.335153,-0.205301,0.520118,
+ 0,-0.233616,-0.972329,0.182847,0.234657,0.627761,
+ 0,-0.070006,-0.997547,0.14426,0.21065,0.633529,
+ 0,-0.233616,-0.972329,0.14426,0.234657,0.627761,
+ 0,-0.070006,-0.997547,0.14426,0.21065,0.633529,
+ 0,-0.233616,-0.972329,0.182847,0.234657,0.627761,
+ 0,0.100284,-0.994959,0.182847,0.21065,0.633529,
+ 0,0.100284,-0.994959,0.182847,0.21065,0.633529,
+ 0,0.475444,-0.879746,0.14426,-0.205301,0.520118,
+ 0,-0.070006,-0.997547,0.14426,0.21065,0.633529,
+ 0,0.475444,-0.879746,0.14426,-0.205301,0.520118,
+ 0,0.100284,-0.994959,0.182847,0.21065,0.633529,
+ 0,0.475444,-0.879746,0.182847,-0.205301,0.520118,
+ 0,0.475444,-0.879746,0.14426,-0.205301,0.520118,
+ 0,0.66295,-0.748664,0.182847,-0.223786,0.50375,
+ 0,0.66295,-0.748664,0.14426,-0.223786,0.50375,
+ 0,0.66295,-0.748664,0.182847,-0.223786,0.50375,
+ 0,0.475444,-0.879746,0.14426,-0.205301,0.520118,
+ 0,0.475444,-0.879746,0.182847,-0.205301,0.520118,
+ 1,0,-0.000001,0.14426,0.234657,0.627761,
+ 1,0,-0.000001,0.14426,-0.205301,0.520118,
+ 1,0,-0.000001,0.14426,-0.223786,0.50375,
+ 1,0,-0.000001,0.14426,-0.205301,0.520118,
+ 1,0,-0.000001,0.14426,0.234657,0.627761,
+ 1,0,-0.000001,0.14426,0.21065,0.633529,
+ -1,0,0,0.182847,-0.205301,0.520118,
+ -1,0,0,0.182847,0.234657,0.627761,
+ -1,0,0,0.182847,-0.223786,0.50375,
+ -1,0,0,0.182847,0.234657,0.627761,
+ -1,0,0,0.182847,-0.205301,0.520118,
+ -1,0,0,0.182847,0.21065,0.633529
+};
+static const struct gllist dumpster_model_lid_panels_frame = {
+ GL_N3F_V3F, GL_TRIANGLES, 78, dumpster_model_lid_panels_data, 0
+};
+const struct gllist *dumpster_model_lid_panels = &dumpster_model_lid_panels_frame;
+
+static const float dumpster_model_panels_half_data[] = {
+ 0,-1,0,0.43601,-0.247708,0.020372,
+ 0,-1,0,0.019519,-0.247708,0.447625,
+ 0,-1,0,0.019519,-0.247708,0.020372,
+ 0,-1,0,0.019519,-0.247708,0.447625,
+ 0,-1,0,0.43601,-0.247708,0.020372,
+ 0,-1,0,0.43601,-0.247708,0.44762,
+ 0,1,0,0.019519,0.247708,0.583158,
+ 0,1,0,0.43601,0.247708,0.020372,
+ 0,1,0,0.019519,0.247708,0.020372,
+ 0,1,0,0.43601,0.247708,0.020372,
+ 0,1,0,0.019519,0.247708,0.583158,
+ 0,1,0,0.43601,0.247708,0.583149,
+ 1,0,0,0.442262,0.242083,0.578243,
+ 1,0,0,0.442262,-0.242083,0.452526,
+ 1,0,0,0.442262,0.242083,0.019745,
+ 1,0,0,0.442262,0.242083,0.019745,
+ 1,0,0,0.442262,-0.242083,0.452526,
+ 1,0,0,0.442262,-0.242083,0.019745,
+ -0.000001,-1,0,0.509245,0.022064,0.401184,
+ -0.000001,-1,0,0.459509,0.022064,0.271574,
+ -0.000001,-1,0,0.509245,0.022064,0.271574,
+ -0.000001,-1,0,0.459509,0.022064,0.271574,
+ -0.000001,-1,0,0.509245,0.022064,0.401184,
+ 0,-1,0,0.459509,0.022064,0.401184,
+ 0,0,-1,0.459509,0.022064,0.401184,
+ 0,0,-1,0.509245,-0.049899,0.401184,
+ 0,0,-1,0.459509,-0.049899,0.401184,
+ 0,0,-1,0.509245,-0.049899,0.401184,
+ 0,0,-1,0.459509,0.022064,0.401184,
+ 0,0,-1,0.509245,0.022064,0.401184,
+ 1,-0.000001,0,0.459509,-0.049899,0.271574,
+ 1,-0.000001,0,0.459509,0.022064,0.401184,
+ 1,-0.000001,0,0.459509,-0.049899,0.401184,
+ 1,-0.000001,0,0.459509,0.022064,0.401184,
+ 1,-0.000001,0,0.459509,-0.049899,0.271574,
+ 1,-0.000001,0,0.459509,0.022064,0.271574,
+ 0.000001,0,1,0.509245,-0.049899,0.271574,
+ 0.000001,0,1,0.459509,0.022064,0.271574,
+ 0.000001,0,1,0.459509,-0.049899,0.271574,
+ 0.000001,0,1,0.459509,0.022064,0.271574,
+ 0.000001,0,1,0.509245,-0.049899,0.271574,
+ 0.000001,0,1,0.509245,0.022064,0.271574,
+ -1,0.000001,0,0.509245,0.022064,0.401184,
+ -1,0.000001,0,0.509245,-0.049899,0.271574,
+ -1,0.000001,0,0.509245,-0.049899,0.401184,
+ -1,0.000001,0,0.509245,-0.049899,0.271574,
+ -1,0.000001,0,0.509245,0.022064,0.401184,
+ -1,0.000001,0,0.509245,0.022064,0.271574
+};
+static const struct gllist dumpster_model_panels_half_frame = {
+ GL_N3F_V3F, GL_TRIANGLES, 48, dumpster_model_panels_half_data, 0
+};
+const struct gllist *dumpster_model_panels_half = &dumpster_model_panels_half_frame;
--- /dev/null
+/* dumpsterfire, Copyright © 2025 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Created by jwz: 22-Apr-2025
+ *
+ * Dumpster model by: xx_n0va_x https://skfb.ly/psRAy
+ * and slightly modified by jwz.
+ * Licensed under Creative Commons Attribution
+ * http://creativecommons.org/licenses/by/4.0/
+ */
+
+#define DEFAULTS "*delay: 20000 \n" \
+ "*showFPS: False \n" \
+ "*wireframe: False \n" \
+ "*dumpsterFrameColor: #777799" "\n" \
+ "*dumpsterPanelColor: #8888AA" "\n" \
+ "*insideColor: #112211" "\n" \
+ "*hingesColor: #666666" "\n" \
+ "*axleColor: #444444" "\n" \
+ "*lidColor: #8888FF" "\n" \
+ "*lidPanelColor: #7777EE" "\n" \
+
+# define release_dumpster 0
+
+#include "xlockmore.h"
+#include "colors.h"
+#include "rotator.h"
+#include "gltrackball.h"
+#include "gllist.h"
+#include <ctype.h>
+
+#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
+
+extern const struct gllist
+ *dumpster_model_frame_half,
+ *dumpster_model_panels_half,
+ *dumpster_model_inside_half,
+ *dumpster_model_hinges_half,
+ *dumpster_model_axle,
+ *dumpster_model_lid,
+ *dumpster_model_lid_panels;
+
+static const struct gllist * const *all_objs[] = {
+ &dumpster_model_frame_half,
+ &dumpster_model_panels_half,
+ &dumpster_model_inside_half,
+ &dumpster_model_hinges_half,
+ &dumpster_model_axle,
+ &dumpster_model_lid,
+ &dumpster_model_lid_panels,
+};
+
+enum { FRAME_HALF, PANELS_HALF, INSIDE_HALF, HINGES_HALF, AXLE,
+ LID, LID_PANELS };
+
+typedef struct { GLfloat x, y, z; } XYZ;
+
+#ifdef USE_GL /* whole file */
+
+
+#define DEF_SPIN "True"
+#define DEF_WANDER "True"
+#define DEF_SPEED "1.0"
+#define DEF_DENSITY "1.0"
+
+typedef struct {
+ float fade;
+ GLfloat color[4];
+ XYZ pos, speed, accel;
+ GLdouble sortkey;
+} particle;
+
+
+typedef struct {
+ GLXContext *glx_context;
+ rotator *rot;
+ trackball_state *trackball;
+ Bool button_down_p;
+ XYZ pos;
+ XYZ wind;
+ GLfloat tick;
+ enum { DROP, IGNITE, OPEN, BURN, QUENCH, CLOSE, ROLL } state;
+ GLfloat lid_angle[2];
+ GLuint *dlists;
+ GLfloat component_colors[countof(all_objs)][4];
+ GLuint texid;
+ GLuint sprite_dlist;
+ GLfloat density;
+ int nparticles;
+ particle *particles;
+
+} dumpster_configuration;
+
+static dumpster_configuration *bps = NULL;
+
+static Bool do_spin;
+static GLfloat speed_arg;
+static Bool do_wander;
+static GLfloat density_arg;
+
+static XrmOptionDescRec opts[] = {
+ { "-spin", ".spin", XrmoptionNoArg, "True" },
+ { "+spin", ".spin", XrmoptionNoArg, "False" },
+ { "-speed", ".speed", XrmoptionSepArg, 0 },
+ { "-wander", ".wander", XrmoptionNoArg, "True" },
+ { "+wander", ".wander", XrmoptionNoArg, "False" },
+ { "-density", ".density", XrmoptionSepArg, 0 },
+};
+
+static argtype vars[] = {
+ {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
+ {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
+ {&speed_arg, "speed", "Speed", DEF_SPEED, t_Float},
+ {&density_arg, "density", "Density", DEF_DENSITY, t_Float},
+};
+
+ENTRYPOINT ModeSpecOpt dumpster_opts = {
+ countof(opts), opts, countof(vars), vars, NULL};
+
+
+/* Color spectrum of temperature per Planck’s law.
+ */
+#undef RGB
+#define RGB(x) \
+ { (((x) >> 16) & 0xFF) / 255.0, \
+ (((x) >> 8) & 0xFF) / 255.0, \
+ (((x) >> 0) & 0xFF) / 255.0, \
+ 1.0 }
+static const GLfloat fire_colors[][4] = {
+ RGB(0x352201), /* 550°C */
+ RGB(0x542803), /* 630°C */
+ RGB(0x681100), /* 680°C */
+ RGB(0x861600), /* 740°C */
+ RGB(0xA00000), /* 780°C */
+ RGB(0xC11B1B), /* 810°C */
+ RGB(0xD44115), /* 850°C */
+ RGB(0xE9582C), /* 900°C */
+ RGB(0xE97E1C), /* 950°C */
+ RGB(0xFFAA0F), /* 1000°C */
+ RGB(0xFBC034), /* 1100°C */
+ RGB(0xFFCF61), /* 1200°C */
+ RGB(0xFFE6AD), /* >1300°C */
+ RGB(0xFFE6AD),
+ RGB(0xFFE6AD),
+ RGB(0xFFE6AD), /* Some padding so that the brightest flames aren't */
+ RGB(0xFFE6AD), /* hidden inside the dumpster. */
+ RGB(0xFFE6AD),
+ RGB(0xFFE6AD),
+ RGB(0xFFE6AD),
+ RGB(0xFFE6AD),
+ RGB(0xFFE6AD),
+ RGB(0xFFE6AD),
+ RGB(0xFFE6AD),
+};
+
+
+static void
+build_texture (ModeInfo *mi)
+{
+ dumpster_configuration *bp = &bps[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ int x, y;
+ int size = 128;
+ int s2 = size / 2;
+ int bpl = size * 4;
+ unsigned char *data = malloc (bpl * size);
+
+ for (y = 0; y < size; y++)
+ {
+ for (x = 0; x < size; x++)
+ {
+ double dist = (sqrt (((s2 - x) * (s2 - x)) +
+ ((s2 - y) * (s2 - y)))
+ / s2);
+ unsigned char *c = &data [y * bpl + x * 4];
+ unsigned char v = 0xFF * sin (dist > 1 ? 0 : (1 - dist));
+ c[0] = 0xFF;
+ c[1] = 0xFF;
+ c[2] = 0xFF;
+ c[3] = v;
+ }
+ }
+
+ glGenTextures (1, &bp->texid);
+ glBindTexture (GL_TEXTURE_2D, bp->texid);
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ check_gl_error ("texture param");
+
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, data);
+ check_gl_error ("texture");
+ free (data);
+
+ bp->sprite_dlist = glGenLists (1);
+ glNewList (bp->sprite_dlist, GL_COMPILE);
+ if (wire)
+ {
+ double th = 0;
+ glBegin(GL_LINE_LOOP);
+ for (th = 0; th < M_PI*2; th += M_PI / 6)
+ glVertex3f (0.5 * cos (th), 0.5 * sin (th), 0);
+ glEnd();
+ }
+ else
+ {
+ glBegin(GL_QUADS);
+ glTexCoord2d(1,1); glVertex3f( 0.5, 0.5, 0);
+ glTexCoord2d(0,1); glVertex3f(-0.5, 0.5, 0);
+ glTexCoord2d(0,0); glVertex3f(-0.5, -0.5, 0);
+ glTexCoord2d(1,0); glVertex3f( 0.5, -0.5, 0);
+ glEnd();
+ }
+ glEndList ();
+}
+
+
+static int
+particle_sort_cmp (const void *aa, const void *bb)
+{
+ const particle *a = (particle *) aa;
+ const particle *b = (particle *) bb;
+ return (a->sortkey == b->sortkey ? 0 : a->sortkey < b->sortkey ? -1 : 1);
+}
+
+
+static void
+draw_fire (ModeInfo *mi)
+{
+ dumpster_configuration *bp = &bps[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+
+ GLfloat size = 0.25 / bp->density;
+ GLfloat max_size = 1.20; /* Don't peek through closed lid */
+ GLfloat min_size = 0.80; /* Don't be too spotty */
+ int i;
+
+ switch (bp->state) {
+ case DROP: case CLOSE: case ROLL:
+ return;
+ default:
+ break;
+ }
+
+ /* I thought about making the sprites be tall ovals instead of circles,
+ for more of a flame shape, but with billboarding that only looks right
+ if "up" is the same for both camera and scene -- no tilting.
+ */
+
+ if (size > max_size) size = max_size;
+ if (size < min_size) size = min_size;
+ if (wire) size *= 0.6;
+
+ /* For transparency to work right, we have to draw the sprites from
+ back to front, from the perspective of the observer. So project
+ the origin of each sprite to screen coordinates, and sort that by Z.
+ */
+ {
+ GLdouble mm[16], pm[16];
+ GLint vp[4];
+ glGetDoublev (GL_MODELVIEW_MATRIX, mm);
+ glGetDoublev (GL_PROJECTION_MATRIX, pm);
+ glGetIntegerv (GL_VIEWPORT, vp);
+
+ for (i = 0; i < bp->nparticles; i++)
+ {
+ GLdouble x, y, z;
+ particle *p = &bp->particles[i];
+ gluProject (p->pos.y, p->pos.z, p->pos.x, mm, pm, vp, &x, &y, &z);
+ p->sortkey = -z;
+ }
+ qsort (bp->particles, bp->nparticles, sizeof(*bp->particles),
+ particle_sort_cmp);
+ }
+
+ /* Render each particle.
+ */
+ glPushMatrix();
+
+ if (! wire)
+ {
+ glEnable (GL_DEPTH_TEST);
+ glDepthFunc (GL_LESS);
+ glDepthMask (GL_FALSE);
+
+ glEnable (GL_TEXTURE_2D);
+ glDisable (GL_LIGHTING);
+ glShadeModel (GL_FLAT);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glPolygonMode (GL_FRONT, GL_FILL);
+ glBindTexture (GL_TEXTURE_2D, bp->texid);
+ }
+
+ /* Position at the lid that is open. */
+ {
+ GLfloat s = 0.5;
+ glTranslatef ((bp->lid_angle[0] == 0 ? -1 : 1) * 2.3,
+ 4,
+ 0);
+ glRotatef (90, -1, 0, 0); /* Z is up */
+ glScalef (s, s, s);
+ }
+
+ for (i = 0; i < bp->nparticles; i++)
+ {
+ GLfloat m[4][4];
+ particle *p = &bp->particles[i];
+
+ glColor4fv (p->color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
+ p->color);
+
+ /* Billboard the sprites to always face the camera. Sadly, we have to
+ glTranslate and then de-billboard for each sprite, or the flame
+ always points toward the top of the screen instead of toward the top
+ of the dumpster. I think this makes it impossible to draw all of
+ the polygons at once with glDrawArrays and a pair of vertex/color
+ arrays. */
+
+ glPushMatrix();
+
+ glTranslatef (p->pos.x, p->pos.y, p->pos.z);
+
+ glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]);
+ m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
+ m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
+ m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
+ glLoadIdentity();
+ glMultMatrixf (&m[0][0]);
+
+ glScalef (size, size, size);
+ glCallList (bp->sprite_dlist);
+ mi->polygon_count++;
+
+ glPopMatrix();
+ }
+
+ glPopMatrix();
+
+ if (! wire)
+ glDepthMask (GL_TRUE);
+
+
+ /* Tick each particle for the next round.
+ #### TODO: Make the fire animate faster per speed_arg. It's hard.
+ */
+ for (i = 0; i < bp->nparticles; i++)
+ {
+ particle *p = &bp->particles[i];
+
+ p->pos.x += p->speed.x;
+ p->pos.y += p->speed.y;
+ p->pos.z += p->speed.z;
+
+ if (p->pos.z > 5.0) /* Turn fire shape into a cone. */
+ {
+ p->accel.x = 0.0016 * (p->pos.x > 0 ? -1 : 1);
+ p->accel.y = 0.0016 * (p->pos.y > 0 ? -1 : 1);
+ }
+
+ p->speed.x += p->accel.x;
+ p->speed.y += p->accel.y;
+ p->speed.z += p->accel.z;
+
+ if (p->pos.z > 4.5) /* Wind outside of the dumpster. */
+ {
+ p->accel.x += bp->wind.x; /* Technically this should increase */
+ p->accel.y += bp->wind.y; /* speed, but increasing accel looks */
+ p->accel.z += bp->wind.z; /* better. */
+ }
+
+ /* Alpha is particle's remaining lifetime, and temperature. */
+ p->color[3] -= p->fade;
+
+ if (bp->state == QUENCH || bp->state == IGNITE)
+ p->color[3] -= p->fade * 3;
+
+ if (p->color[3] <= 0.0) /* Dead: reset. */
+ {
+ if (bp->state < QUENCH)
+ {
+ /* First tune speed.z and accel.z to get the vertical seething
+ to look ok. Then tune fade to control flame height. */
+ p->pos.x = 0;
+ p->pos.y = 0;
+ p->pos.z = 0;
+ p->speed.x = 0.12 * (frand(1) - 0.5);
+ p->speed.y = 0.12 * (frand(1) - 0.5);
+ p->speed.z = 0.06 * (frand(1) - 0.5);
+ p->accel.x = 0;
+ p->accel.y = 0;
+ p->accel.z = 0.0032;
+ p->fade = (frand (0.2) + 0.006);
+
+ memcpy (p->color, fire_colors[countof(fire_colors)-1],
+ sizeof(p->color));
+ }
+ }
+ else
+ {
+ /* Colorize but preserve alpha. */
+ int i = p->color[3] * countof(fire_colors);
+ if (i >= countof(fire_colors)) i = countof(fire_colors)-1;
+ memcpy (p->color, fire_colors[i], sizeof(GLfloat) * 3);
+ }
+ }
+}
+
+
+ENTRYPOINT void
+reshape_dumpster (ModeInfo *mi, int width, int height)
+{
+ GLfloat h = (GLfloat) height / (GLfloat) width;
+ int y = 0;
+
+ if (width > height * 5) { /* tiny window: show middle */
+ height = width * 9/16;
+ y = -height/2;
+ h = height / (GLfloat) width;
+ }
+
+ glViewport (0, y, (GLint) width, (GLint) height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective (30.0, 1/h, 1.0, 100.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( 0.0, 0.0, 30.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
+
+ {
+ GLfloat s = (MI_WIDTH(mi) < MI_HEIGHT(mi)
+ ? (MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi))
+ : 1);
+ glScalef (s, s, s);
+ }
+
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+ENTRYPOINT Bool
+dumpster_handle_event (ModeInfo *mi, XEvent *event)
+{
+ dumpster_configuration *bp = &bps[MI_SCREEN(mi)];
+ if (gltrackball_event_handler (event, bp->trackball,
+ MI_WIDTH (mi), MI_HEIGHT (mi),
+ &bp->button_down_p))
+ return True;
+ else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
+ {
+ switch (bp->state) {
+ case DROP: case IGNITE:
+ bp->state = ROLL-1;
+ bp->tick = 1.0;
+ return True;
+ case OPEN: case BURN:
+ bp->state = QUENCH-1;
+ bp->tick = 1.0;
+ if (bp->lid_angle[0] != 0)
+ bp->lid_angle[0] -= 0.6;
+ else
+ bp->lid_angle[1] -= 0.6;
+ return True;
+ default:
+ return False;
+ }
+ }
+
+ return False;
+}
+
+
+static void
+parse_color (ModeInfo *mi, char *key, GLfloat color[4])
+{
+ XColor xcolor;
+ char *string = get_string_resource (mi->dpy, key, "Color");
+ if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
+ {
+ fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
+ key, string);
+ exit (1);
+ }
+ free (string);
+
+ color[0] = xcolor.red / 65536.0;
+ color[1] = xcolor.green / 65536.0;
+ color[2] = xcolor.blue / 65536.0;
+ color[3] = 1;
+}
+
+
+ENTRYPOINT void
+init_dumpster (ModeInfo *mi)
+{
+ dumpster_configuration *bp;
+ int wire = MI_IS_WIREFRAME(mi);
+ int i;
+
+ MI_INIT (mi, bps);
+ bp = &bps[MI_SCREEN(mi)];
+
+ bp->glx_context = init_GL(mi);
+
+ reshape_dumpster (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+ if (!wire)
+ {
+ GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
+ GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
+ GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
+ GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
+
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
+ }
+
+ {
+ double spin_speed = speed_arg * 0.04;
+ double wander_speed = speed_arg * 0.004;
+ double spin_accel = 0.5;
+
+ bp->rot = make_rotator (do_spin ? spin_speed : 0,
+ do_spin ? spin_speed : 0,
+ do_spin ? spin_speed : 0,
+ spin_accel,
+ do_wander ? wander_speed : 0,
+ False);
+ bp->trackball = gltrackball_init (True);
+ }
+
+ bp->density = density_arg;
+ bp->nparticles = 10000 * bp->density;
+ if (bp->nparticles < 10) bp->nparticles = 10;
+ bp->particles = (particle *)
+ calloc (bp->nparticles, sizeof(*bp->particles));
+
+ build_texture (mi);
+
+ bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
+ for (i = 0; i < countof(all_objs); i++)
+ bp->dlists[i] = glGenLists (1);
+
+ bp->state = DROP;
+ bp->tick = 0;
+
+ for (i = 0; i < countof(all_objs); i++)
+ {
+ const struct gllist *gll = *all_objs[i];
+ char *key = 0;
+
+ glNewList (bp->dlists[i], GL_COMPILE);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+ glRotatef (-90, 1, 0, 0);
+
+ glBindTexture (GL_TEXTURE_2D, 0);
+
+ switch (i) {
+ case FRAME_HALF: key = "dumpsterFrameColor"; break;
+ case PANELS_HALF: key = "dumpsterPanelColor"; break;
+ case INSIDE_HALF: key = "insideColor"; break;
+ case HINGES_HALF: key = "hingesColor"; break;
+ case AXLE: key = "axleColor"; break;
+ case LID: key = "lidColor"; break;
+ case LID_PANELS: key = "lidPanelColor"; break;
+ default:
+ abort();
+ }
+
+ parse_color (mi, key, bp->component_colors[i]);
+ renderList (gll, wire);
+
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ glEndList ();
+ }
+}
+
+
+static int
+draw_component (ModeInfo *mi, int i, Bool half_p)
+{
+ dumpster_configuration *bp = &bps[MI_SCREEN(mi)];
+
+ static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
+ static const GLfloat shiny = 128.0;
+ glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
+ glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
+ bp->component_colors[i]);
+
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[i]);
+
+ if (half_p)
+ {
+ glPushMatrix();
+ glScalef (-1, 1, 1);
+ glFrontFace (GL_CW);
+ glCallList (bp->dlists[i]);
+ glPopMatrix();
+ }
+
+ return (half_p ? 2 : 1) * (*all_objs[i])->points / 3;
+}
+
+
+static double
+easeOutBounce (double i)
+{
+ double n1 = 7.5625;
+ double d1 = 2.75;
+ if (i < 1 / d1) {
+ return n1 * i * i;
+ } else if (i < 2 / d1) {
+ i -= (1.5 / d1);
+ return n1 * i * i + 0.75;
+ } else if (i < 2.5 / d1) {
+ i -= (2.25 / d1);
+ return n1 * i * i + 0.9375;
+ } else {
+ i -= (2.625 / d1);
+ return n1 * i * i + 0.984375;
+ }
+}
+
+
+static void
+tick_dumpster (ModeInfo *mi)
+{
+ dumpster_configuration *bp = &bps[MI_SCREEN(mi)];
+ GLfloat ts;
+ /* double fps = MI_DELAY(mi) ? 1000000.0 / MI_DELAY(mi) : 30; */
+ double fps = 27;
+
+ if (bp->button_down_p) return;
+
+ switch (bp->state) {
+ case DROP: ts = 3; break;
+ case IGNITE: ts = 1; break;
+ case OPEN: ts = 1; break;
+ case BURN: ts = 99; break;
+ case QUENCH: ts = 3; break;
+ case CLOSE: ts = 1; break;
+ case ROLL: ts = 3; break;
+ default: abort(); break;
+ }
+
+ bp->tick += speed_arg * (1 / (ts * fps));
+
+ if (bp->tick < 1) return;
+
+ bp->tick = 0;
+ bp->state = (bp->state + 1) % (ROLL + 1);
+
+ switch (bp->state) {
+ case IGNITE: /* Pick which lid we are opening */
+ bp->lid_angle[random() % 2] += 0.001;
+ bp->wind.x = 0.15 * (BELLRAND (1.0) - 0.5);
+ bp->wind.y = -0.15 * (BELLRAND (0.5));
+ bp->wind.z = 0;
+ break;
+ case ROLL: /* Close the rest of the way */
+ bp->lid_angle[0] = bp->lid_angle[1] = 0;
+ break;
+ case CLOSE:
+ memset (bp->particles, 0, bp->nparticles * sizeof (*bp->particles));
+ break;
+ case DROP:
+ gltrackball_reset (bp->trackball, 0, 0);
+ default:
+ break;
+ }
+}
+
+
+static void
+draw_box (ModeInfo *mi)
+{
+ dumpster_configuration *bp = &bps[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ GLfloat s;
+ int i;
+
+ glPushMatrix();
+
+ glDisable (GL_BLEND);
+ glEnable (GL_NORMALIZE);
+ glEnable (GL_DEPTH_TEST);
+ glDepthFunc (GL_LESS);
+ glDepthMask (GL_TRUE);
+
+ if (!wire)
+ {
+ glEnable (GL_LIGHTING);
+ glShadeModel (GL_SMOOTH);
+ }
+
+ s = 12;
+ glScalef (s, s, s);
+
+ switch (bp->state) {
+ case DROP:
+ bp->pos.x = bp->pos.y = 0;
+ bp->pos.z = (1 - easeOutBounce (bp->tick)) * 3;
+ break;
+ case OPEN:
+ bp->lid_angle[(bp->lid_angle[0] == 0.0 ? 1 : 0)] = bp->tick + 0.0001;
+ break;
+ case CLOSE:
+ bp->lid_angle[(bp->lid_angle[0] == 0.0 ? 1 : 0)] = (1-bp->tick) + 0.0001;
+ break;
+ case ROLL:
+ bp->pos.x += bp->tick * 2;
+ break;
+ default:
+ bp->pos.z = 0;
+ break;
+ }
+
+ if (wire)
+ glColor3f (1, 1, 1);
+
+ glTranslatef (bp->pos.x, bp->pos.z, bp->pos.y);
+
+ mi->polygon_count += draw_component (mi, FRAME_HALF, True);
+ mi->polygon_count += draw_component (mi, PANELS_HALF, True);
+ mi->polygon_count += draw_component (mi, INSIDE_HALF, True);
+ mi->polygon_count += draw_component (mi, AXLE, False);
+ mi->polygon_count += draw_component (mi, HINGES_HALF, True);
+
+ for (i = 0; i < 2; i++)
+ {
+ const GLfloat deg = 115;
+ const XYZ off = { 0, 0.63, -0.25 };
+ double a2 = (bp->state == CLOSE
+ ? 1 - easeOutBounce (1 - bp->lid_angle[i])
+ : easeOutBounce ( bp->lid_angle[i]));
+
+ glPushMatrix();
+ glTranslatef (off.x, off.y, off.z);
+ glRotatef (-deg * a2, 1, 0, 0);
+ glTranslatef (-off.x, -off.y, -off.z);
+
+ if (i == 1)
+ glTranslatef (-0.46, 0, 0);
+
+ mi->polygon_count += draw_component (mi, LID, False);
+ mi->polygon_count += draw_component (mi, LID_PANELS, False);
+ glPopMatrix();
+ }
+
+ glPopMatrix();
+}
+
+
+ENTRYPOINT void
+draw_dumpster (ModeInfo *mi)
+{
+ dumpster_configuration *bp = &bps[MI_SCREEN(mi)];
+ Display *dpy = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ GLfloat s;
+
+ if (!bp->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix ();
+
+ {
+ double x, y, z;
+ get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
+ glTranslatef((x - 0.5) * 8,
+ (y - 0.5) * 1,
+ (z - 0.5) * 15);
+
+ gltrackball_rotate (bp->trackball);
+
+ get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
+ /* glRotatef (x * 360, 1.0, 0.0, 0.0); */
+ glRotatef (y * 360, 0.0, 1.0, 0.0);
+ /* glRotatef (z * 360, 0.0, 0.0, 1.0); */
+ }
+
+ mi->polygon_count = 0;
+
+ glTranslatef (0, -6, 0);
+ s = 0.7;
+ glScalef (s, s, s);
+ glRotatef (5, 1, 0, 0);
+ glRotatef (-10, 0, 1, 0);
+
+ draw_box (mi);
+ draw_fire (mi);
+
+ glPopMatrix ();
+
+ if (mi->fps_p) do_fps (mi);
+ glFinish();
+
+ tick_dumpster (mi);
+
+ glXSwapBuffers(dpy, window);
+}
+
+
+ENTRYPOINT void
+free_dumpster (ModeInfo *mi)
+{
+ dumpster_configuration *bp = &bps[MI_SCREEN(mi)];
+ int i;
+ if (!bp->glx_context) return;
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
+ if (bp->trackball) gltrackball_free (bp->trackball);
+ free (bp->particles);
+ if (bp->texid) glDeleteTextures (1, &bp->texid);
+ for (i = 0; i < countof(all_objs); i++)
+ if (glIsList(bp->dlists[i])) glDeleteLists(bp->dlists[i], 1);
+ if (glIsList(bp->sprite_dlist)) glDeleteLists(bp->sprite_dlist, 1);
+}
+
+XSCREENSAVER_MODULE_2 ("DumpsterFire", dumpsterfire, dumpster)
+
+#endif /* USE_GL */
/* Only empty toasters barrel-roll, since we don't implement
"toast falls out". */
- if (f->loaded == 0 && !(random() % 10))
+ if (f->loaded == 0 && !(random() % 50))
f->dwz = (BELLRAND(2.0) - 1.0) * (4 + BELLRAND(6));
}
else
-/* fps, Copyright (c) 2001-2018 Jamie Zawinski <jwz@jwz.org>
+/* fps, Copyright © 2001-2025 Jamie Zawinski <jwz@jwz.org>
* Draw a frames-per-second display (Xlib and OpenGL).
*
* Permission to use, copy, modify, distribute, and sell this software and its
gl_fps_data *data = (gl_fps_data *) st->gl_fps_data;
XWindowAttributes xgwa;
XGetWindowAttributes (st->dpy, st->window, &xgwa);
+
+# ifndef HAVE_ANDROID
+ /* This crashes in the Android emulator, but not on real hardware. */
glColor3f (1, 1, 1);
+# endif
+
print_texture_label (st->dpy, data->texfont,
xgwa.width, xgwa.height,
(data->top_p ? 1 : 2),
#include <string.h>
+#define M_PI_F 3.1415926535898f
+
+
#ifdef HAVE_GLSL
extern const char *progname;
-/* Copy a 4x4 column-major matrix: c = m. */
+
+/* Normalize a vector. */
+static inline void glsl_Normalize(float v[3])
+{
+ float l;
+
+ l = sqrtf(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
+ if (l > 0.0f)
+ l = 1.0f/l;
+ v[0] *= l;
+ v[1] *= l;
+ v[2] *= l;
+}
+
+
+/* Compute the cross product v = v1 × v2. */
+static inline void glsl_Cross(float v[3], float v1[3], float v2[3])
+{
+ v[0] = v1[1]*v2[2]-v1[2]*v2[1];
+ v[1] = v1[2]*v2[0]-v1[0]*v2[2];
+ v[2] = v1[0]*v2[1]-v1[1]*v2[0];
+}
+
+
+/* Copy a 4×4 column-major matrix: c = m. */
void glsl_CopyMatrix(GLfloat c[16], GLfloat m[16])
{
int i, j;
}
-/* Create a 4x4 column-major identity matrix. */
+/* Create a 4×4 column-major identity matrix. */
void glsl_Identity(GLfloat c[16])
{
int i, j;
for (j=0; j<4; j++)
for (i=0; i<4; i++)
- c[GLSL__LINCOOR(i,j,4)] = (i==j);
+ c[GLSL__LINCOOR(i,j,4)] = (float)(i==j);
}
-/* Multiply two 4x4 column-major matrices: c = c*m. */
+/* Multiply two 4×4 column-major matrices: c = c * m. */
void glsl_MultMatrix(GLfloat c[16], GLfloat m[16])
{
int i, j;
}
-/* Multiply a 4x4 column-major matrix by a rotation matrix that rotates
- around the axis (x,y,z) by the angle angle: c = c*r(angle,x,y,z). */
+/* Multiply a 4×4 column-major matrix with a vector: o = m * v. */
+void glsl_MultMatrixVector(GLfloat o[4], GLfloat m[16], GLfloat v[4])
+{
+ int i;
+
+ for (i=0; i<4; i++)
+ o[i] = (m[GLSL__LINCOOR(i,0,4)]*v[0]+m[GLSL__LINCOOR(i,1,4)]*v[1]+
+ m[GLSL__LINCOOR(i,2,4)]*v[2]+m[GLSL__LINCOOR(i,3,4)]*v[3]);
+}
+
+
+/* Multiply a 4×4 column-major matrix by a rotation matrix that rotates
+ around the axis (x,y,z) by the angle angle: c = c * r(angle,x,y,z). */
void glsl_Rotate(GLfloat c[16], GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
{
GLfloat l, t, ct, st, omct, n[3], r[16];
n[0] = x/l;
n[1] = y/l;
n[2] = z/l;
- t = angle*M_PI/180.0f;
+ t = angle*M_PI_F/180.0f;
ct = cosf(t);
st = sinf(t);
omct = 1.0f-ct;
}
-/* Multiply a 4x4 column-major matrix by a matrix that stretches, shrinks,
+/* Multiply a 4×4 column-major matrix by a matrix that translates an object
+ by a translation vector: c = c * t(tx,ty,tz). */
+void glsl_Translate(GLfloat c[16], GLfloat tx, GLfloat ty, GLfloat tz)
+{
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ c[GLSL__LINCOOR(i,3,4)] = (tx*c[GLSL__LINCOOR(i,0,4)]+
+ ty*c[GLSL__LINCOOR(i,1,4)]+
+ tz*c[GLSL__LINCOOR(i,2,4)]+
+ c[GLSL__LINCOOR(i,3,4)]);
+ }
+}
+
+
+/* Multiply a 4×4 column-major matrix by a matrix that stretches, shrinks,
or reflects an object along the axes: c = c*m(sx,sy,sz). */
void glsl_Scale(GLfloat c[16], GLfloat sx, GLfloat sy, GLfloat sz)
{
}
-/* Multiply a 4x4 column-major matrix by a matrix that translates an object
- by a translation vector: c = c*t(tx,ty,tz). */
-void glsl_Translate(GLfloat c[16], GLfloat tx, GLfloat ty, GLfloat tz)
+/* Add a look-at viewing matrix to a 4×4 column-major matrix. */
+void glsl_LookAt(GLfloat c[16], GLfloat eyex, GLfloat eyey, GLfloat eyez,
+ GLfloat centerx, GLfloat centery, GLfloat centerz,
+ GLfloat upx, GLfloat upy, GLfloat upz)
{
- int i;
+ float forward[3], side[3], up[3];
+ GLfloat m[16];
- for (i=0; i<4; i++)
- {
- c[GLSL__LINCOOR(i,3,4)] = (tx*c[GLSL__LINCOOR(i,0,4)]+
- ty*c[GLSL__LINCOOR(i,1,4)]+
- tz*c[GLSL__LINCOOR(i,2,4)]+
- c[GLSL__LINCOOR(i,3,4)]);
- }
+ forward[0] = centerx-eyex;
+ forward[1] = centery-eyey;
+ forward[2] = centerz-eyez;
+ glsl_Normalize(forward);
+
+ up[0] = upx;
+ up[1] = upy;
+ up[2] = upz;
+
+ /* side = forward × up */
+ glsl_Cross(side,forward,up);
+ glsl_Normalize(side);
+
+ /* up = side × forward */
+ glsl_Cross(up,side,forward);
+
+ glsl_Identity(m);
+ m[GLSL__LINCOOR(0,0,4)] = side[0];
+ m[GLSL__LINCOOR(0,1,4)] = side[1];
+ m[GLSL__LINCOOR(0,2,4)] = side[2];
+ m[GLSL__LINCOOR(1,0,4)] = up[0];
+ m[GLSL__LINCOOR(1,1,4)] = up[1];
+ m[GLSL__LINCOOR(1,2,4)] = up[2];
+ m[GLSL__LINCOOR(2,0,4)] = -forward[0];
+ m[GLSL__LINCOOR(2,1,4)] = -forward[1];
+ m[GLSL__LINCOOR(2,2,4)] = -forward[2];
+
+ glsl_MultMatrix(c,m);
+ glsl_Translate(c,-eyex,-eyey,-eyez);
}
-/* Add a perspective projection to a 4x4 column-major matrix. */
+/* Add a perspective projection to a 4×4 column-major matrix. */
void glsl_Perspective(GLfloat c[16], GLfloat fovy, GLfloat aspect,
GLfloat z_near, GLfloat z_far)
{
GLfloat m[16];
- double s, cot, dz;
- double rad;
+ float s, cot, dz;
+ float rad;
- rad = fovy*(0.5f*(float)M_PI/180.0f);
+ rad = fovy*(0.5f*(float)M_PI_F/180.0f);
dz = z_far-z_near;
s = sinf(rad);
if (dz == 0.0f || s == 0.0f || aspect == 0.0f)
m[GLSL__LINCOOR(3,2,4)] = -1.0f;
m[GLSL__LINCOOR(2,3,4)] = -2.0f*z_near*z_far/dz;
m[GLSL__LINCOOR(3,3,4)] = 0.0f;
+
glsl_MultMatrix(c,m);
}
-/* Add an orthographic projection to a 4x4 column-major matrix. */
+/* Add an orthographic projection to a 4×4 column-major matrix. */
void glsl_Orthographic(GLfloat c[16], GLfloat left, GLfloat right,
GLfloat bottom, GLfloat top,
GLfloat nearval, GLfloat farval)
m[GLSL__LINCOOR(1,3,4)] = -(top+bottom)/(top-bottom);
m[GLSL__LINCOOR(2,2,4)] = -2.0f/(farval-nearval);
m[GLSL__LINCOOR(2,3,4)] = -(farval+nearval)/(farval-nearval);
+
glsl_MultMatrix(c,m);
}
{
const char *gl_version, *glsl_version;
int n;
- const char *err = 0;
+ const char *err = NULL;
*gl_major = -1;
*gl_minor = -1;
- *glsl_major = 1;
+ *glsl_major = -1;
*glsl_minor = -1;
*gl_gles3 = GL_FALSE;
gl_version = (const char *)glGetString(GL_VERSION);
glsl_version = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
if (gl_version == NULL || glsl_version == NULL)
- {
- err = "GL version unknown";
- goto DONE;
- }
+ {
+ err = "GL version unknown";
+ goto DONE;
+ }
if (!strncmp(gl_version,"OpenGL ES",9))
{
if (!strncmp(glsl_version,"OpenGL ES GLSL ES",17))
+ {
*gl_gles3 = GL_TRUE;
+ }
else
- {
- err = "GLSL not supported";
- goto DONE;
- }
+ {
+ err = "GLSL not supported";
+ goto DONE;
+ }
}
if (*gl_gles3)
n = sscanf(&gl_version[9],"%d.%d",gl_major,gl_minor);
else
n = sscanf(gl_version,"%d.%d",gl_major,gl_minor);
if (n != 2)
- {
- err = "GL version number unparsable";
- goto DONE;
- }
+ {
+ err = "GL version number unparsable";
+ goto DONE;
+ }
if (*gl_gles3)
n = sscanf(&glsl_version[17],"%d.%d",glsl_major,glsl_minor);
else
n = sscanf(glsl_version,"%d.%d",glsl_major,glsl_minor);
if (n != 2)
- {
- err = "GLSL version number unparsable";
- goto DONE;
- }
+ {
+ err = "GLSL version number unparsable";
+ goto DONE;
+ }
DONE:
/* Multiply two 4x4 column-major matrices: c = c*m. */
extern void glsl_MultMatrix(GLfloat c[16], GLfloat m[16]);
+/* Multiply a 4×4 column-major matrix with a vector: o = m * v. */
+extern void glsl_MultMatrixVector(GLfloat o[4], GLfloat m[16], GLfloat v[4]);
+
/* Multiply a 4x4 column-major matrix by a rotation matrix that rotates
around the axis (x,y,z) by the angle angle: c = c*r(angle,x,y,z). */
extern void glsl_Rotate(GLfloat c[16], GLfloat angle, GLfloat x, GLfloat y,
GLfloat z);
+/* Multiply a 4x4 column-major matrix by a matrix that translates an object
+ by a translation vector: c = c * t(tx,ty,tz). */
+extern void glsl_Translate(GLfloat c[16], GLfloat tx, GLfloat ty, GLfloat tz);
+
/* Multiply a 4x4 column-major matrix by a matrix that stretches, shrinks,
or reflects an object along the axes: c = c*s(sx,sy,sz). */
extern void glsl_Scale(GLfloat c[16], GLfloat sx, GLfloat sy, GLfloat sz);
-/* Multiply a 4x4 column-major matrix by a matrix that translates an object
- by a translation vector: c = c*t(tx,ty,tz). */
-extern void glsl_Translate(GLfloat c[16], GLfloat tx, GLfloat ty, GLfloat tz);
+/* Add a look-at viewing matrix to a 4×4 column-major matrix. */
+extern void glsl_LookAt(GLfloat c[16],
+ GLfloat eyex, GLfloat eyey, GLfloat eyez,
+ GLfloat centerx, GLfloat centery, GLfloat centerz,
+ GLfloat upx, GLfloat upy, GLfloat upz);
/* Add a perspective projection to a 4x4 column-major matrix. */
extern void glsl_Perspective(GLfloat c[16], GLfloat fovy, GLfloat aspect,
RIGHT, ZERO, RIGHT, ZERO, ZERO }
},
+ /* Models by stixpjr@gmail.com */
+ { "begging dog",
+ { ZERO, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT,
+ LEFT, PIN, RIGHT, RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN,
+ RIGHT, LEFT, PIN, LEFT }
+ },
+ { "swan",
+ { ZERO, PIN, ZERO, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO,
+ RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO,
+ LEFT, ZERO, LEFT }
+ },
+
/* These models come from the website at
* http://www.geocities.com/stigeide/snake */
#if 0
-/* gltrackball, Copyright (c) 2002-2017 Jamie Zawinski <jwz@jwz.org>
+/* gltrackball, Copyright © 2002-2025 Jamie Zawinski <jwz@jwz.org>
* GL-flavored wrapper for trackball.c
*
* Permission to use, copy, modify, distribute, and sell this software and its
}
build_rotmatrix (m, ts->q);
+
+# ifndef HAVE_ANDROID
+ /* This crashes in the Android emulator, presumably because of the hellscape
+ that is OpenGLES 3.0. But since Android has no way to interact with hacks
+ as either screen savers or live wallpapers, the trackball code is all a
+ no-op anyway. */
glMultMatrixf (&m[0][0]);
+# endif
}
--- /dev/null
+/* hopfanimations.c --- Definition of the animations used in the Hopf
+ fibration. */
+/* Copyright (c) 2025 Carsten Steger <carsten@mirsanmir.org>.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * REVISION HISTORY:
+ * C. Steger - 26/02/06: Initial version
+ */
+
+#ifdef USE_GL
+
+#include "hopfanimations.h"
+
+
+#define ANIM_SO_NAME(f) anim_ ## f ## _s
+#define ANIM_SO_DEF(f) static animation_single_obj ANIM_SO_NAME(f)[]
+#define ANIM_SO_NUM(f) sizeof(ANIM_SO_NAME(f))/sizeof(*ANIM_SO_NAME(f))
+#define ANIM_MO_NAME(f) anim_ ## f ## _m
+#define ANIM_MO_REF(f) &ANIM_MO_NAME(f)
+#define ANIM_MO_DEF(f) static animation_multi_obj ANIM_MO_NAME(f)
+#define ANIM_PH_NAME(f) anim_ ## f ## _p
+#define ANIM_PH_DEF(f) static animation_multi_obj *ANIM_PH_NAME(f)[]
+#define ANIM_PH_NUM(f) sizeof(ANIM_PH_NAME(f))/sizeof(*ANIM_PH_NAME(f))
+#define ANIM_PS_NAME(f) anim_ ## f
+#define ANIM_PS_REF(f) &ANIM_PS_NAME(f)
+#define ANIM_PS_DEF(f) static animation_phases ANIM_PS_NAME(f)
+#define ANIMS_M_NAME(f) anims_ ## f ## _m
+#define ANIMS_M_DEF(f) static animation_phases *ANIMS_M_NAME(f)[]
+#define ANIMS_M_NUM(f) sizeof(ANIMS_M_NAME(f))/sizeof(*ANIMS_M_NAME(f))
+#define ANIMS_NAME(f) anims_ ## f
+#define ANIMS_REF(f) &ANIMS_NAME(f)
+#define ANIMS_DEF(f) static animations ANIMS_NAME(f)
+
+
+
+/*****************************************************************************
+ * The set of all transformations from a single point.
+ *****************************************************************************/
+
+/* The set of possible animations for a single point. */
+
+/* Rotate a point on the equator around the z axis. */
+ANIM_SO_DEF(single_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_rot_z) = {
+ ANIM_SO_NUM(single_point_rot_z),
+ ANIM_SO_NAME(single_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_rot_z) = {
+ ANIM_MO_REF(single_point_rot_z)
+};
+
+ANIM_PS_DEF(single_point_rot_z) = {
+ ANIM_PH_NUM(single_point_rot_z),
+ ANIM_PH_NAME(single_point_rot_z)
+};
+
+/* Move a point on the equator along a Hopf torus. */
+ANIM_SO_DEF(single_point_move_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 4, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_move_hopf_torus) = {
+ ANIM_SO_NUM(single_point_move_hopf_torus),
+ ANIM_SO_NAME(single_point_move_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_move_hopf_torus) = {
+ ANIM_MO_REF(single_point_move_hopf_torus)
+};
+
+ANIM_PS_DEF(single_point_move_hopf_torus) = {
+ ANIM_PH_NUM(single_point_move_hopf_torus),
+ ANIM_PH_NAME(single_point_move_hopf_torus)
+};
+
+/* Move a point on the equator along a Hopf spiral. */
+ANIM_SO_DEF(single_point_move_hopf_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, -4.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_move_hopf_spiral) = {
+ ANIM_SO_NUM(single_point_move_hopf_spiral),
+ ANIM_SO_NAME(single_point_move_hopf_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_move_hopf_spiral) = {
+ ANIM_MO_REF(single_point_move_hopf_spiral)
+};
+
+ANIM_PS_DEF(single_point_move_hopf_spiral) = {
+ ANIM_PH_NUM(single_point_move_hopf_spiral),
+ ANIM_PH_NAME(single_point_move_hopf_spiral)
+};
+
+/* Split a single point on the equator into two points, rotate the two
+ points around the z axis, and merge the two points again. */
+
+/* Phase 1: Split a single point on the equator into two points on the
+ equator by moving the second point on the equator. */
+ANIM_SO_DEF(single_point_to_double_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_ACCEL, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_double_point_rot_z) = {
+ ANIM_SO_NUM(single_point_to_double_point_rot_z),
+ ANIM_SO_NAME(single_point_to_double_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+/* Phase 2: Rotate the two points around the z axis. */
+ANIM_SO_DEF(double_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, M_PI_F, EASING_LIN, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_point_rot_z) = {
+ ANIM_SO_NUM(double_point_rot_z),
+ ANIM_SO_NAME(double_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+/* Phase 3: Merge the two points on the equator into a single point on the
+ equator. */
+ANIM_SO_DEF(double_point_to_single_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 2.0f*M_PI_F, EASING_DECEL, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_point_to_single_point_rot_z) = {
+ ANIM_SO_NUM(double_point_to_single_point_rot_z),
+ ANIM_SO_NAME(double_point_to_single_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(double_point_rot_z) = {
+ ANIM_MO_REF(single_point_to_double_point_rot_z),
+ ANIM_MO_REF(double_point_rot_z),
+ ANIM_MO_REF(double_point_to_single_point_rot_z),
+};
+
+ANIM_PS_DEF(double_point_rot_z) = {
+ ANIM_PH_NUM(double_point_rot_z),
+ ANIM_PH_NAME(double_point_rot_z)
+};
+
+/* Split a single point on the equator into two points, move the two
+ points along a Hopf torus, and merge the two points again. */
+
+/* Phase 1: Split a single point on the equator into two points on the
+ equator by moving the second point on a meridian. */
+ANIM_SO_DEF(single_point_to_double_point_move_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, -1.0f, 0.0f }, 0.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_double_point_move_hopf_torus) = {
+ ANIM_SO_NUM(single_point_to_double_point_move_hopf_torus),
+ ANIM_SO_NAME(single_point_to_double_point_move_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+/* Phase 2: Move the two points along the Hopf torus. */
+ANIM_SO_DEF(double_point_move_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 4, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 4, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_point_move_hopf_torus) = {
+ ANIM_SO_NUM(double_point_move_hopf_torus),
+ ANIM_SO_NAME(double_point_move_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 3: Merge the two points on the equator into a single point on the
+ equator by moving the second point along a meridian. */
+ANIM_SO_DEF(double_point_to_single_point_move_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, -1.0f, 0.0f }, M_PI_F, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_point_to_single_point_move_hopf_torus) = {
+ ANIM_SO_NUM(double_point_to_single_point_move_hopf_torus),
+ ANIM_SO_NAME(double_point_to_single_point_move_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(double_point_move_hopf_torus) = {
+ ANIM_MO_REF(single_point_to_double_point_move_hopf_torus),
+ ANIM_MO_REF(double_point_move_hopf_torus),
+ ANIM_MO_REF(double_point_to_single_point_move_hopf_torus),
+};
+
+ANIM_PS_DEF(double_point_move_hopf_torus) = {
+ ANIM_PH_NUM(double_point_move_hopf_torus),
+ ANIM_PH_NAME(double_point_move_hopf_torus)
+};
+
+/* Split a single point on the equator into two points, move the two
+ points along a Hopf spiral, and merge the two points again. */
+
+/* Phase 1: Split a single point on the equator into two points on the
+ equator by moving the second point along a Hopf spiral. */
+ANIM_SO_DEF(single_point_to_double_point_move_hopf_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, -2.0f*M_PI_F, EASING_ACCEL, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_double_point_move_hopf_spiral) = {
+ ANIM_SO_NUM(single_point_to_double_point_move_hopf_spiral),
+ ANIM_SO_NAME(single_point_to_double_point_move_hopf_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+/* Phase 2: Move the two points along the Hopf spiral. */
+ANIM_SO_DEF(double_point_move_hopf_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, -4.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 2.0f*M_PI_F, -2.0f*M_PI_F, EASING_LIN, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_point_move_hopf_spiral) = {
+ ANIM_SO_NUM(double_point_move_hopf_spiral),
+ ANIM_SO_NAME(double_point_move_hopf_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 3: Merge the two points on the equator into a single point on the
+ equator by moving the second point along a Hopf spiral. */
+ANIM_SO_DEF(double_point_to_single_point_move_hopf_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 2.0f*M_PI_F, 0.0f, EASING_DECEL, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_point_to_single_point_move_hopf_spiral) = {
+ ANIM_SO_NUM(double_point_to_single_point_move_hopf_spiral),
+ ANIM_SO_NAME(double_point_to_single_point_move_hopf_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(double_point_move_hopf_spiral) = {
+ ANIM_MO_REF(single_point_to_double_point_move_hopf_spiral),
+ ANIM_MO_REF(double_point_move_hopf_spiral),
+ ANIM_MO_REF(double_point_to_single_point_move_hopf_spiral),
+};
+
+ANIM_PS_DEF(double_point_move_hopf_spiral) = {
+ ANIM_PH_NUM(double_point_move_hopf_spiral),
+ ANIM_PH_NAME(double_point_move_hopf_spiral)
+};
+
+ANIMS_M_DEF(single_point) = {
+ ANIM_PS_REF(single_point_rot_z),
+ ANIM_PS_REF(single_point_move_hopf_torus),
+ ANIM_PS_REF(single_point_move_hopf_spiral),
+ ANIM_PS_REF(double_point_rot_z),
+ ANIM_PS_REF(double_point_move_hopf_torus),
+ ANIM_PS_REF(double_point_move_hopf_spiral)
+};
+
+ANIMS_DEF(single_point) = {
+ ANIMS_M_NUM(single_point),
+ ANIMS_M_NAME(single_point)
+};
+
+
+
+/* The set of possible animations for the transition from a single point
+ to a single torus. */
+
+/* Expand a point on the equator to a torus on the equator. */
+ANIM_SO_DEF(single_point_to_single_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_single_torus_rot_z) = {
+ ANIM_SO_NUM(single_point_to_single_torus_rot_z),
+ ANIM_SO_NAME(single_point_to_single_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_single_torus_rot_z) = {
+ ANIM_MO_REF(single_point_to_single_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_point_to_single_torus_rot_z) = {
+ ANIM_PH_NUM(single_point_to_single_torus_rot_z),
+ ANIM_PH_NAME(single_point_to_single_torus_rot_z)
+};
+
+/* Expand a point on the equator to a torus on the equator and rotate around
+ a random axis. */
+ANIM_SO_DEF(single_point_to_single_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_single_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_point_to_single_torus_rot_rnd),
+ ANIM_SO_NAME(single_point_to_single_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_single_torus_rot_rnd) = {
+ ANIM_MO_REF(single_point_to_single_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_point_to_single_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_point_to_single_torus_rot_rnd),
+ ANIM_PH_NAME(single_point_to_single_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(single_point_to_single_torus) = {
+ ANIM_PS_REF(single_point_to_single_torus_rot_z),
+ ANIM_PS_REF(single_point_to_single_torus_rot_rnd)
+};
+
+ANIMS_DEF(single_point_to_single_torus) = {
+ ANIMS_M_NUM(single_point_to_single_torus),
+ ANIMS_M_NAME(single_point_to_single_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single point
+ to a double torus. */
+
+/* Expand the point on the equator to two tori, rotate them around the
+ z axis, and move them to latitude ±45°. */
+ANIM_SO_DEF(single_point_to_double_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, -2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_double_torus_rot_z) = {
+ ANIM_SO_NUM(single_point_to_double_torus_rot_z),
+ ANIM_SO_NAME(single_point_to_double_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_double_torus_rot_z) = {
+ ANIM_MO_REF(single_point_to_double_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_point_to_double_torus_rot_z) = {
+ ANIM_PH_NUM(single_point_to_double_torus_rot_z),
+ ANIM_PH_NAME(single_point_to_double_torus_rot_z)
+};
+
+/* Expand the point on the equator to two tori, rotate them around a random
+ axis, and move them to latitude ±45°. */
+ANIM_SO_DEF(single_point_to_double_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, -2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_double_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_point_to_double_torus_rot_rnd),
+ ANIM_SO_NAME(single_point_to_double_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_double_torus_rot_rnd) = {
+ ANIM_MO_REF(single_point_to_double_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_point_to_double_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_point_to_double_torus_rot_rnd),
+ ANIM_PH_NAME(single_point_to_double_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(single_point_to_double_torus) = {
+ ANIM_PS_REF(single_point_to_double_torus_rot_z),
+ ANIM_PS_REF(single_point_to_double_torus_rot_rnd)
+};
+
+ANIMS_DEF(single_point_to_double_torus) = {
+ ANIMS_M_NUM(single_point_to_double_torus),
+ ANIMS_M_NAME(single_point_to_double_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single point
+ to a triple torus. */
+
+/* Expand the point on the equator to three tori, rotate two of them around
+ the z axis, and move them to latitude ±45°. */
+ANIM_SO_DEF(single_point_to_triple_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, -2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_triple_torus_rot_z) = {
+ ANIM_SO_NUM(single_point_to_triple_torus_rot_z),
+ ANIM_SO_NAME(single_point_to_triple_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_triple_torus_rot_z) = {
+ ANIM_MO_REF(single_point_to_triple_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_point_to_triple_torus_rot_z) = {
+ ANIM_PH_NUM(single_point_to_triple_torus_rot_z),
+ ANIM_PH_NAME(single_point_to_triple_torus_rot_z)
+};
+
+/* Expand the point on the equator to three tori, rotate them around a
+ random axis, and move them to latitude ±45°. */
+ANIM_SO_DEF(single_point_to_triple_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, -2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_triple_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_point_to_triple_torus_rot_rnd),
+ ANIM_SO_NAME(single_point_to_triple_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_triple_torus_rot_rnd) = {
+ ANIM_MO_REF(single_point_to_triple_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_point_to_triple_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_point_to_triple_torus_rot_rnd),
+ ANIM_PH_NAME(single_point_to_triple_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(single_point_to_triple_torus) = {
+ ANIM_PS_REF(single_point_to_triple_torus_rot_z),
+ ANIM_PS_REF(single_point_to_triple_torus_rot_rnd)
+};
+
+ANIMS_DEF(single_point_to_triple_torus) = {
+ ANIMS_M_NUM(single_point_to_triple_torus),
+ ANIMS_M_NAME(single_point_to_triple_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single point
+ to a single Seifert surface. */
+
+/* Expand a point on the equator to a Seifert surface on the equator. */
+ANIM_SO_DEF(single_point_to_single_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_single_seifert_rot_z) = {
+ ANIM_SO_NUM(single_point_to_single_seifert_rot_z),
+ ANIM_SO_NAME(single_point_to_single_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_single_seifert_rot_z) = {
+ ANIM_MO_REF(single_point_to_single_seifert_rot_z)
+};
+
+ANIM_PS_DEF(single_point_to_single_seifert_rot_z) = {
+ ANIM_PH_NUM(single_point_to_single_seifert_rot_z),
+ ANIM_PH_NAME(single_point_to_single_seifert_rot_z)
+};
+
+/* Expand a point on the equator to a Seifert surface on the equator and
+ rotate around a random axis. */
+ANIM_SO_DEF(single_point_to_single_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_single_seifert_rot_rnd) = {
+ ANIM_SO_NUM(single_point_to_single_seifert_rot_rnd),
+ ANIM_SO_NAME(single_point_to_single_seifert_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_single_seifert_rot_rnd) = {
+ ANIM_MO_REF(single_point_to_single_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(single_point_to_single_seifert_rot_rnd) = {
+ ANIM_PH_NUM(single_point_to_single_seifert_rot_rnd),
+ ANIM_PH_NAME(single_point_to_single_seifert_rot_rnd)
+};
+
+ANIMS_M_DEF(single_point_to_single_seifert) = {
+ ANIM_PS_REF(single_point_to_single_seifert_rot_z),
+ ANIM_PS_REF(single_point_to_single_seifert_rot_rnd)
+};
+
+ANIMS_DEF(single_point_to_single_seifert) = {
+ ANIMS_M_NUM(single_point_to_single_seifert),
+ ANIMS_M_NAME(single_point_to_single_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single point
+ to a triple Seifert surface. */
+
+/* Expand the point at the equator to a Seifert surface on the without
+ rotating it. Expand the point at the equator to two Seifert surfaces at
+ latitude ±45° and rotate them around the z axis. */
+ANIM_SO_DEF(single_point_to_triple_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_triple_seifert_rot_z) = {
+ ANIM_SO_NUM(single_point_to_triple_seifert_rot_z),
+ ANIM_SO_NAME(single_point_to_triple_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_triple_seifert_rot_z) = {
+ ANIM_MO_REF(single_point_to_triple_seifert_rot_z)
+};
+
+ANIM_PS_DEF(single_point_to_triple_seifert_rot_z) = {
+ ANIM_PH_NUM(single_point_to_triple_seifert_rot_z),
+ ANIM_PH_NAME(single_point_to_triple_seifert_rot_z)
+};
+
+/* Move the single point at the equator to the south pole along a Hopf
+ spiral, then successively expand three Seifert surfaces from the single
+ point at the south pole and move them to latitudes 70°, 0°, and -70°,
+ respectively, then decrease the point density by a factor of two and move
+ the Seifert surfaces at latitude ±70° to latidude ±45°, all the while
+ rotating all Seifert surfaces around the z axis in opposite directions. */
+
+/* Phase 1: Move the single point at the equator to the south pole along a
+ Hopf Spiral. */
+ANIM_SO_DEF(single_point_to_triple_seifert_rot_z_move_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ -2.0f, -2.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+};
+
+ANIM_MO_DEF(single_point_to_triple_seifert_rot_z_move_spiral) = {
+ ANIM_SO_NUM(single_point_to_triple_seifert_rot_z_move_spiral),
+ ANIM_SO_NAME(single_point_to_triple_seifert_rot_z_move_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 120 /* num_steps */
+};
+
+/* Phase 2: Expand a Seifert surface from the south pole and move it to
+ latitude 70°. */
+ANIM_SO_DEF(single_point_to_triple_seifert_rot_z_move_north_1) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F, M_PI_F, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_triple_seifert_rot_z_move_north_1) = {
+ ANIM_SO_NUM(single_point_to_triple_seifert_rot_z_move_north_1),
+ ANIM_SO_NAME(single_point_to_triple_seifert_rot_z_move_north_1),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 3: Expand a Seifert surface from the south pole and move it to
+ latitude 0°. */
+ANIM_SO_DEF(single_point_to_triple_seifert_rot_z_move_north_2) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F, M_PI_F, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_triple_seifert_rot_z_move_north_2) = {
+ ANIM_SO_NUM(single_point_to_triple_seifert_rot_z_move_north_2),
+ ANIM_SO_NAME(single_point_to_triple_seifert_rot_z_move_north_2),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 4: Expand a Seifert surface from the south pole and move it to
+ latitude -70°. */
+ANIM_SO_DEF(single_point_to_triple_seifert_rot_z_move_north_3) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_triple_seifert_rot_z_move_north_3) = {
+ ANIM_SO_NUM(single_point_to_triple_seifert_rot_z_move_north_3),
+ ANIM_SO_NAME(single_point_to_triple_seifert_rot_z_move_north_3),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 5: Rotate the three Seifert surfaces for one revolution. */
+ANIM_SO_DEF(single_point_to_triple_seifert_rot_z_linear) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 8.0f*M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_triple_seifert_rot_z_linear) = {
+ ANIM_SO_NUM(single_point_to_triple_seifert_rot_z_linear),
+ ANIM_SO_NAME(single_point_to_triple_seifert_rot_z_linear),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 1: Rotate the three Seifert surfaces for one revolution, move the
+ Seifert surfaces at latitudes ±70° to latitudes ±45°, and decrease the
+ point density by a factor of two. */
+ANIM_SO_DEF(single_point_to_triple_seifert_rot_z_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_triple_seifert_rot_z_loosen) = {
+ ANIM_SO_NUM(single_point_to_triple_seifert_rot_z_loosen),
+ ANIM_SO_NAME(single_point_to_triple_seifert_rot_z_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_triple_seifert_move_north) = {
+ ANIM_MO_REF(single_point_to_triple_seifert_rot_z_move_spiral),
+ ANIM_MO_REF(single_point_to_triple_seifert_rot_z_move_north_1),
+ ANIM_MO_REF(single_point_to_triple_seifert_rot_z_move_north_2),
+ ANIM_MO_REF(single_point_to_triple_seifert_rot_z_move_north_3),
+ ANIM_MO_REF(single_point_to_triple_seifert_rot_z_linear),
+ ANIM_MO_REF(single_point_to_triple_seifert_rot_z_loosen)
+};
+
+ANIM_PS_DEF(single_point_to_triple_seifert_move_north) = {
+ ANIM_PH_NUM(single_point_to_triple_seifert_move_north),
+ ANIM_PH_NAME(single_point_to_triple_seifert_move_north)
+};
+
+ANIMS_M_DEF(single_point_to_triple_seifert) = {
+ ANIM_PS_REF(single_point_to_triple_seifert_rot_z),
+ ANIM_PS_REF(single_point_to_triple_seifert_move_north)
+};
+
+ANIMS_DEF(single_point_to_triple_seifert) = {
+ ANIMS_M_NUM(single_point_to_triple_seifert),
+ ANIMS_M_NAME(single_point_to_triple_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single point
+ to a single Hopf torus. */
+
+/* Expand a point on the equator to a Hopf torus on the equator and rotate
+ around a random axis. */
+ANIM_SO_DEF(single_point_to_single_hopf_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_single_hopf_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_point_to_single_hopf_torus_rot_rnd),
+ ANIM_SO_NAME(single_point_to_single_hopf_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_single_hopf_torus_rot_rnd) = {
+ ANIM_MO_REF(single_point_to_single_hopf_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_point_to_single_hopf_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_point_to_single_hopf_torus_rot_rnd),
+ ANIM_PH_NAME(single_point_to_single_hopf_torus_rot_rnd)
+};
+
+/* Expand the point to a loose Hopf torus and increase the point densify of
+ the Hopf torus by a factor of five. */
+
+/* Phase 1: Expand the point on the equator to a loose Hopf torus. */
+ANIM_SO_DEF(single_point_to_single_loose_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_point_to_single_loose_hopf_torus) = {
+ ANIM_SO_NUM(single_point_to_single_loose_hopf_torus),
+ ANIM_SO_NAME(single_point_to_single_loose_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+/* Phase 2: Increase the density of the Hopf torus. */
+ANIM_SO_DEF(single_hopf_torus_to_single_dense_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_to_single_dense_hopf_torus) = {
+ ANIM_SO_NUM(single_hopf_torus_to_single_dense_hopf_torus),
+ ANIM_SO_NAME(single_hopf_torus_to_single_dense_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+ANIM_PH_DEF(single_point_to_single_hopf_torus_densify) = {
+ ANIM_MO_REF(single_point_to_single_loose_hopf_torus),
+ ANIM_MO_REF(single_hopf_torus_to_single_dense_hopf_torus)
+};
+
+ANIM_PS_DEF(single_point_to_single_hopf_torus_densify) = {
+ ANIM_PH_NUM(single_point_to_single_hopf_torus_densify),
+ ANIM_PH_NAME(single_point_to_single_hopf_torus_densify)
+};
+
+ANIMS_M_DEF(single_point_to_single_hopf_torus) = {
+ ANIM_PS_REF(single_point_to_single_hopf_torus_rot_rnd),
+ ANIM_PS_REF(single_point_to_single_hopf_torus_densify)
+};
+
+ANIMS_DEF(single_point_to_single_hopf_torus) = {
+ ANIMS_M_NUM(single_point_to_single_hopf_torus),
+ ANIMS_M_NAME(single_point_to_single_hopf_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single point to
+ a single Hopf spiral. */
+
+/* Expand a point on the equator to a Hopf spiral and rotate around a random
+ axis with a certain probability. */
+ANIM_SO_DEF(single_hopf_spiral_to_single_point_expand_sector) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_single_point_expand_sector) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_single_point_expand_sector),
+ ANIM_SO_NAME(single_hopf_spiral_to_single_point_expand_sector),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_single_point_expand_sector) = {
+ ANIM_MO_REF(single_hopf_spiral_to_single_point_expand_sector)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_single_point_expand_sector) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_single_point_expand_sector),
+ ANIM_PH_NAME(single_hopf_spiral_to_single_point_expand_sector)
+};
+
+ANIMS_M_DEF(single_point_to_single_hopf_spiral) = {
+ ANIM_PS_REF(single_hopf_spiral_to_single_point_expand_sector)
+};
+
+ANIMS_DEF(single_point_to_single_hopf_spiral) = {
+ ANIMS_M_NUM(single_point_to_single_hopf_spiral),
+ ANIMS_M_NAME(single_point_to_single_hopf_spiral)
+};
+
+
+
+/*****************************************************************************
+ * The set of all transformations from a single torus.
+ *****************************************************************************/
+
+/* The set of possible animations for the transition from a single torus
+ to a single point. */
+
+/* Shrink a torus on the equator to a point on the equator. */
+ANIM_SO_DEF(single_torus_to_single_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_point_rot_z) = {
+ ANIM_SO_NUM(single_torus_to_single_point_rot_z),
+ ANIM_SO_NAME(single_torus_to_single_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_single_point_rot_z) = {
+ ANIM_MO_REF(single_torus_to_single_point_rot_z)
+};
+
+ANIM_PS_DEF(single_torus_to_single_point_rot_z) = {
+ ANIM_PH_NUM(single_torus_to_single_point_rot_z),
+ ANIM_PH_NAME(single_torus_to_single_point_rot_z)
+};
+
+/* Shrink a torus on the equator to a point on the equator and rotate around
+ a random axis. */
+ANIM_SO_DEF(single_torus_to_single_point_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_point_rot_rnd) = {
+ ANIM_SO_NUM(single_torus_to_single_point_rot_rnd),
+ ANIM_SO_NAME(single_torus_to_single_point_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_single_point_rot_rnd) = {
+ ANIM_MO_REF(single_torus_to_single_point_rot_rnd)
+};
+
+ANIM_PS_DEF(single_torus_to_single_point_rot_rnd) = {
+ ANIM_PH_NUM(single_torus_to_single_point_rot_rnd),
+ ANIM_PH_NAME(single_torus_to_single_point_rot_rnd)
+};
+
+ANIMS_M_DEF(single_torus_to_single_point) = {
+ ANIM_PS_REF(single_torus_to_single_point_rot_z),
+ ANIM_PS_REF(single_torus_to_single_point_rot_rnd)
+};
+
+ANIMS_DEF(single_torus_to_single_point) = {
+ ANIMS_M_NUM(single_torus_to_single_point),
+ ANIMS_M_NAME(single_torus_to_single_point)
+};
+
+
+
+/* The set of possible animations for a single torus. */
+
+/* Rotate a torus on the equator around the x axis of the total space. */
+ANIM_SO_DEF(single_torus_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_rot_x) = {
+ ANIM_SO_NUM(single_torus_rot_x),
+ ANIM_SO_NAME(single_torus_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_rot_x) = {
+ ANIM_MO_REF(single_torus_rot_x)
+};
+
+ANIM_PS_DEF(single_torus_rot_x) = {
+ ANIM_PH_NUM(single_torus_rot_x),
+ ANIM_PH_NAME(single_torus_rot_x)
+};
+
+/* Rotate a torus on the equator around the z axis. */
+ANIM_SO_DEF(single_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_rot_z) = {
+ ANIM_SO_NUM(single_torus_rot_z),
+ ANIM_SO_NAME(single_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_rot_z) = {
+ ANIM_MO_REF(single_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_torus_rot_z) = {
+ ANIM_PH_NUM(single_torus_rot_z),
+ ANIM_PH_NAME(single_torus_rot_z)
+};
+
+/* Rotate a torus on the equator around a random axis. */
+ANIM_SO_DEF(single_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_torus_rot_rnd),
+ ANIM_SO_NAME(single_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_rot_rnd) = {
+ ANIM_MO_REF(single_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_torus_rot_rnd),
+ ANIM_PH_NAME(single_torus_rot_rnd)
+};
+
+/* Move a torus on the equator up and down to latitude ±80° while rotating
+ it around the z axis and, with a certain probability, around a random
+ axis. */
+ANIM_SO_DEF(single_torus_move) = {
+ {
+ GEN_TORUS, /* generator */
+ 1.0f*M_PI_F/18.0f, 17.0f*M_PI_F/18.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_move) = {
+ ANIM_SO_NUM(single_torus_move),
+ ANIM_SO_NAME(single_torus_move),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_move) = {
+ ANIM_MO_REF(single_torus_move)
+};
+
+ANIM_PS_DEF(single_torus_move) = {
+ ANIM_PH_NUM(single_torus_move),
+ ANIM_PH_NAME(single_torus_move)
+};
+
+/* Rotate a torus on the equator to a meridian, yielding a parabolic ring
+ cyclide, and increase its density by a factor of three. Then, rotate
+ it around the x axis of the total space. Finally, rotate the torus back
+ to the equator and decrease its density by a factor of three. */
+
+/* Phase 1: Rotate the torus by 90° to a meridian and increase its point
+ density by a factor of three. */
+ANIM_SO_DEF(single_torus_to_meridian_torus_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_meridian_torus_densify) = {
+ ANIM_SO_NUM(single_torus_to_meridian_torus_densify),
+ ANIM_SO_NAME(single_torus_to_meridian_torus_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+/* Phase 2: Rotate the torus by 360° around the x axis of the total space. */
+ANIM_SO_DEF(single_meridian_torus_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_meridian_torus_rot_x) = {
+ ANIM_SO_NUM(single_meridian_torus_rot_x),
+ ANIM_SO_NAME(single_meridian_torus_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 3: Rotate the meridian torus by 90° to the equator and decrease its
+ point density by a factor of three. */
+ANIM_SO_DEF(single_meridian_torus_to_torus_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 2.0f*M_PI_F/2.0f,
+ EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 2.0f*M_PI_F/2.0f,
+ EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 2.0f*M_PI_F/2.0f,
+ EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_meridian_torus_to_torus_loosen) = {
+ ANIM_SO_NUM(single_meridian_torus_to_torus_loosen),
+ ANIM_SO_NAME(single_meridian_torus_to_torus_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_meridian) = {
+ ANIM_MO_REF(single_torus_to_meridian_torus_densify),
+ ANIM_MO_REF(single_meridian_torus_rot_x),
+ ANIM_MO_REF(single_meridian_torus_to_torus_loosen)
+};
+
+ANIM_PS_DEF(single_torus_meridian) = {
+ ANIM_PH_NUM(single_torus_meridian),
+ ANIM_PH_NAME(single_torus_meridian)
+};
+
+ANIMS_M_DEF(single_torus) = {
+ ANIM_PS_REF(single_torus_rot_x),
+ ANIM_PS_REF(single_torus_rot_z),
+ ANIM_PS_REF(single_torus_rot_rnd),
+ ANIM_PS_REF(single_torus_move),
+ ANIM_PS_REF(single_torus_meridian)
+};
+
+ANIMS_DEF(single_torus) = {
+ ANIMS_M_NUM(single_torus),
+ ANIMS_M_NAME(single_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single torus
+ to a double torus. */
+
+/* Split the torus at the equator into a dense torus, then split the torus
+ into two tori, rotate them around the z axis, and move them to latitude
+ ±45°. */
+
+/* Phase 1: Increase the point density by a factor of two. */
+ANIM_SO_DEF(single_torus_to_single_dense_torus_densify_two) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_dense_torus_densify_two) = {
+ ANIM_SO_NUM(single_torus_to_single_dense_torus_densify_two),
+ ANIM_SO_NAME(single_torus_to_single_dense_torus_densify_two),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 30 /* num_steps */
+};
+
+/* Phase 2: Split the torus into two tori, rotate them around the z axis,
+ and move them to latitude ±45°. */
+ANIM_SO_DEF(single_dense_torus_to_double_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_torus_to_double_torus_rot_z) = {
+ ANIM_SO_NUM(single_dense_torus_to_double_torus_rot_z),
+ ANIM_SO_NAME(single_dense_torus_to_double_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_double_torus_rot_z) = {
+ ANIM_MO_REF(single_torus_to_single_dense_torus_densify_two),
+ ANIM_MO_REF(single_dense_torus_to_double_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_torus_to_double_torus_rot_z) = {
+ ANIM_PH_NUM(single_torus_to_double_torus_rot_z),
+ ANIM_PH_NAME(single_torus_to_double_torus_rot_z)
+};
+
+/* Split the torus at the equator into two tori, rotate them around a random
+ axis, and move them to latitude ±45°. */
+ANIM_SO_DEF(single_torus_to_double_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_double_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_torus_to_double_torus_rot_rnd),
+ ANIM_SO_NAME(single_torus_to_double_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_double_torus_rot_rnd) = {
+ ANIM_MO_REF(single_torus_to_double_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_torus_to_double_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_torus_to_double_torus_rot_rnd),
+ ANIM_PH_NAME(single_torus_to_double_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(single_torus_to_double_torus) = {
+ ANIM_PS_REF(single_torus_to_double_torus_rot_z),
+ ANIM_PS_REF(single_torus_to_double_torus_rot_rnd)
+};
+
+ANIMS_DEF(single_torus_to_double_torus) = {
+ ANIMS_M_NUM(single_torus_to_double_torus),
+ ANIMS_M_NAME(single_torus_to_double_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single torus
+ to a triple torus. */
+
+/* Increase the density of the torus at the equator by a factor of three,
+ split the dense torus into three tori, move two of the tori to latitude
+ ±45°, and rotate all three tori around the z axis. */
+
+/* Phase 1: Increase the point density by a factor of three. */
+ANIM_SO_DEF(single_torus_to_single_dense_torus_densify_three) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_dense_torus_densify_three) = {
+ ANIM_SO_NUM(single_torus_to_single_dense_torus_densify_three),
+ ANIM_SO_NAME(single_torus_to_single_dense_torus_densify_three),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 30 /* num_steps */
+};
+
+/* Phase 2: Split the torus into three tori, rotate them around the z axis,
+ and move them to latitudes ±45° and 0°. */
+ANIM_SO_DEF(single_dense_torus_to_triple_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_torus_to_triple_torus_rot_z) = {
+ ANIM_SO_NUM(single_dense_torus_to_triple_torus_rot_z),
+ ANIM_SO_NAME(single_dense_torus_to_triple_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_triple_torus_rot_z) = {
+ ANIM_MO_REF(single_torus_to_single_dense_torus_densify_three),
+ ANIM_MO_REF(single_dense_torus_to_triple_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_torus_to_triple_torus_rot_z) = {
+ ANIM_PH_NUM(single_torus_to_triple_torus_rot_z),
+ ANIM_PH_NAME(single_torus_to_triple_torus_rot_z)
+};
+
+/* Split the torus at the equator into three tori, rotate them around a
+ random axis, and move them to latitudes ±45° and 0°. */
+ANIM_SO_DEF(single_torus_to_triple_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_triple_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_torus_to_triple_torus_rot_rnd),
+ ANIM_SO_NAME(single_torus_to_triple_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_triple_torus_rot_rnd) = {
+ ANIM_MO_REF(single_torus_to_triple_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_torus_to_triple_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_torus_to_triple_torus_rot_rnd),
+ ANIM_PH_NAME(single_torus_to_triple_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(single_torus_to_triple_torus) = {
+ ANIM_PS_REF(single_torus_to_triple_torus_rot_z),
+ ANIM_PS_REF(single_torus_to_triple_torus_rot_rnd)
+};
+
+ANIMS_DEF(single_torus_to_triple_torus) = {
+ ANIMS_M_NUM(single_torus_to_triple_torus),
+ ANIMS_M_NAME(single_torus_to_triple_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single torus
+ to a single Seifert surface. */
+
+/* Shrink the torus on the equator to a Seifert surface on the equator. */
+ANIM_SO_DEF(single_torus_to_single_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_seifert_rot_z) = {
+ ANIM_SO_NUM(single_torus_to_single_seifert_rot_z),
+ ANIM_SO_NAME(single_torus_to_single_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_single_seifert_rot_z) = {
+ ANIM_MO_REF(single_torus_to_single_seifert_rot_z)
+};
+
+ANIM_PS_DEF(single_torus_to_single_seifert_rot_z) = {
+ ANIM_PH_NUM(single_torus_to_single_seifert_rot_z),
+ ANIM_PH_NAME(single_torus_to_single_seifert_rot_z)
+};
+
+/* Shrink the torus on the equator to a Seifert surface on the equator and
+ rotate it around a random axis. */
+ANIM_SO_DEF(single_torus_to_single_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_seifert_rot_rnd) = {
+ ANIM_SO_NUM(single_torus_to_single_seifert_rot_rnd),
+ ANIM_SO_NAME(single_torus_to_single_seifert_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_single_seifert_rot_rnd) = {
+ ANIM_MO_REF(single_torus_to_single_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(single_torus_to_single_seifert_rot_rnd) = {
+ ANIM_PH_NUM(single_torus_to_single_seifert_rot_rnd),
+ ANIM_PH_NAME(single_torus_to_single_seifert_rot_rnd)
+};
+
+/* Flip half of the torus on the equator around a suitable axis on the
+ equator to form a Seifert surface on the equator. */
+ANIM_SO_DEF(single_torus_to_single_seifert_flip) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { -0.99785892f, 0.06540313f, 0.0f },
+ 0.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_seifert_flip) = {
+ ANIM_SO_NUM(single_torus_to_single_seifert_flip),
+ ANIM_SO_NAME(single_torus_to_single_seifert_flip),
+ 0.25f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_single_seifert_flip) = {
+ ANIM_MO_REF(single_torus_to_single_seifert_flip)
+};
+
+ANIM_PS_DEF(single_torus_to_single_seifert_flip) = {
+ ANIM_PH_NUM(single_torus_to_single_seifert_flip),
+ ANIM_PH_NAME(single_torus_to_single_seifert_flip)
+};
+
+ANIMS_M_DEF(single_torus_to_single_seifert) = {
+ ANIM_PS_REF(single_torus_to_single_seifert_rot_z),
+ ANIM_PS_REF(single_torus_to_single_seifert_rot_rnd),
+ ANIM_PS_REF(single_torus_to_single_seifert_flip)
+};
+
+ANIMS_DEF(single_torus_to_single_seifert) = {
+ ANIMS_M_NUM(single_torus_to_single_seifert),
+ ANIMS_M_NAME(single_torus_to_single_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single torus
+ to a triple Seifert surface. */
+
+/* Increase the density of the torus at the equator by a factor of three,
+ split the dense torus into three Seifert surfaces of sector 120°,
+ increase their sectors to 180°, and move two of the Seifert surfaces to
+ latitude ±45°, while rotating all three Seifert surfaces around the
+ z axis. */
+
+/* Phase 1: Increase the point density by a factor of three. Already
+ defined above. */
+
+/* Phase 2: Split the dense torus into three Seifert surfaces of sector
+ 120°, increase their sectors to 180°, and move two of the Seifert
+ surfaces to latitude ±45°, while rotating all three Seifert surfaces
+ around the z axis. */
+ANIM_SO_DEF(single_dense_torus_to_triple_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 4.0f*M_PI_F/3.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 2.0f*M_PI_F/3.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_torus_to_triple_seifert_rot_z) = {
+ ANIM_SO_NUM(single_dense_torus_to_triple_seifert_rot_z),
+ ANIM_SO_NAME(single_dense_torus_to_triple_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_triple_seifert_rot_z) = {
+ ANIM_MO_REF(single_torus_to_single_dense_torus_densify_three),
+ ANIM_MO_REF(single_dense_torus_to_triple_seifert_rot_z)
+};
+
+ANIM_PS_DEF(single_torus_to_triple_seifert_rot_z) = {
+ ANIM_PH_NUM(single_torus_to_triple_seifert_rot_z),
+ ANIM_PH_NAME(single_torus_to_triple_seifert_rot_z)
+};
+
+/* Split the torus at the equator into three Seifert surfaces, rotate them
+ around a random axis, shrink their sectors from 360° to 180°, and move
+ two of the Seifert surfaces from the equator to latitude ±45°. */
+ANIM_SO_DEF(single_torus_to_triple_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_SO_NUM(single_torus_to_triple_seifert_rot_rnd),
+ ANIM_SO_NAME(single_torus_to_triple_seifert_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_MO_REF(single_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(single_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_PH_NUM(single_torus_to_triple_seifert_rot_rnd),
+ ANIM_PH_NAME(single_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIMS_M_DEF(single_torus_to_triple_seifert) = {
+ ANIM_PS_REF(single_torus_to_triple_seifert_rot_z),
+ ANIM_PS_REF(single_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIMS_DEF(single_torus_to_triple_seifert) = {
+ ANIMS_M_NUM(single_torus_to_triple_seifert),
+ ANIMS_M_NAME(single_torus_to_triple_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single torus
+ to a single Hopf torus. */
+
+/* Fill the torus on the equator to a torus on the equator of five times
+ the point density and then deform it to the Hopf torus. */
+
+/* Phase 1: Increase the density of the torus on the equator. */
+ANIM_SO_DEF(single_torus_to_single_dense_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_dense_torus) = {
+ ANIM_SO_NUM(single_torus_to_single_dense_torus),
+ ANIM_SO_NAME(single_torus_to_single_dense_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Deform the torus on the equator to a Hopf torus. */
+ANIM_SO_DEF(single_dense_torus_to_single_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_torus_to_single_hopf_torus) = {
+ ANIM_SO_NUM(single_dense_torus_to_single_hopf_torus),
+ ANIM_SO_NAME(single_dense_torus_to_single_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_single_hopf_torus_densify) = {
+ ANIM_MO_REF(single_torus_to_single_dense_torus),
+ ANIM_MO_REF(single_dense_torus_to_single_hopf_torus)
+};
+
+ANIM_PS_DEF(single_torus_to_single_hopf_torus_densify) = {
+ ANIM_PH_NUM(single_torus_to_single_hopf_torus_densify),
+ ANIM_PH_NAME(single_torus_to_single_hopf_torus_densify)
+};
+
+ANIMS_M_DEF(single_torus_to_single_hopf_torus) = {
+ ANIM_PS_REF(single_torus_to_single_hopf_torus_densify)
+};
+
+ANIMS_DEF(single_torus_to_single_hopf_torus) = {
+ ANIMS_M_NUM(single_torus_to_single_hopf_torus),
+ ANIMS_M_NAME(single_torus_to_single_hopf_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single torus to
+ a single Hopf spiral. */
+
+/* Increase the point density of a torus on the equator (a collapsed Hopf
+ spiral) by a factor of three, increase its winding number to two and its
+ height to a Hopf spiral.. */
+
+/* Phase 1: Increase the density of the torus by a factor of three. */
+ANIM_SO_DEF(single_torus_to_single_dense_unwound_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_dense_unwound_torus) = {
+ ANIM_SO_NUM(single_torus_to_single_dense_unwound_torus),
+ ANIM_SO_NAME(single_torus_to_single_dense_unwound_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Wind the collapsed spiral and expand it. */
+ANIM_SO_DEF(single_dense_unwound_torus_to_single_hopf_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 1.0f, 2.0f, EASING_CUBIC, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_unwound_torus_to_single_hopf_spiral) = {
+ ANIM_SO_NUM(single_dense_unwound_torus_to_single_hopf_spiral),
+ ANIM_SO_NAME(single_dense_unwound_torus_to_single_hopf_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_single_hopf_spiral_wind) = {
+ ANIM_MO_REF(single_torus_to_single_dense_unwound_torus),
+ ANIM_MO_REF(single_dense_unwound_torus_to_single_hopf_spiral)
+};
+
+ANIM_PS_DEF(single_torus_to_single_hopf_spiral_wind) = {
+ ANIM_PH_NUM(single_torus_to_single_hopf_spiral_wind),
+ ANIM_PH_NAME(single_torus_to_single_hopf_spiral_wind)
+};
+
+/* Decrease the winding number of a Hopf spiral on the equator to three and
+ expand its height. */
+ANIM_SO_DEF(single_torus_to_single_hopf_spiral_unwind) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 3.0f, 2.0f, EASING_CUBIC, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_torus_to_single_hopf_spiral_unwind) = {
+ ANIM_SO_NUM(single_torus_to_single_hopf_spiral_unwind),
+ ANIM_SO_NAME(single_torus_to_single_hopf_spiral_unwind),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_torus_to_single_hopf_spiral_unwind) = {
+ ANIM_MO_REF(single_torus_to_single_hopf_spiral_unwind)
+};
+
+ANIM_PS_DEF(single_torus_to_single_hopf_spiral_unwind) = {
+ ANIM_PH_NUM(single_torus_to_single_hopf_spiral_unwind),
+ ANIM_PH_NAME(single_torus_to_single_hopf_spiral_unwind)
+};
+
+ANIMS_M_DEF(single_torus_to_single_hopf_spiral) = {
+ ANIM_PS_REF(single_torus_to_single_hopf_spiral_wind),
+ ANIM_PS_REF(single_torus_to_single_hopf_spiral_unwind)
+};
+
+ANIMS_DEF(single_torus_to_single_hopf_spiral) = {
+ ANIMS_M_NUM(single_torus_to_single_hopf_spiral),
+ ANIMS_M_NAME(single_torus_to_single_hopf_spiral)
+};
+
+
+
+/*****************************************************************************
+ * The set of all transformations from a double torus.
+ *****************************************************************************/
+
+/* The set of possible animations for the transition from a double torus
+ to a single point. */
+
+/* Rotate two tori at latitude ±45° around the z axis, shrink them to a
+ single point, and move them to the equator. */
+ANIM_SO_DEF(double_torus_to_single_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ -2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_single_point_rot_z) = {
+ ANIM_SO_NUM(double_torus_to_single_point_rot_z),
+ ANIM_SO_NAME(double_torus_to_single_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_single_point_rot_z) = {
+ ANIM_MO_REF(double_torus_to_single_point_rot_z)
+};
+
+ANIM_PS_DEF(double_torus_to_single_point_rot_z) = {
+ ANIM_PH_NUM(double_torus_to_single_point_rot_z),
+ ANIM_PH_NAME(double_torus_to_single_point_rot_z)
+};
+
+/* Rotate two tori at latitude ±45° around a random axis, shrink them to a
+ single point, and move them to the equator. */
+ANIM_SO_DEF(double_torus_to_single_point_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ -2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_single_point_rot_rnd) = {
+ ANIM_SO_NUM(double_torus_to_single_point_rot_rnd),
+ ANIM_SO_NAME(double_torus_to_single_point_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_single_point_rot_rnd) = {
+ ANIM_MO_REF(double_torus_to_single_point_rot_rnd)
+};
+
+ANIM_PS_DEF(double_torus_to_single_point_rot_rnd) = {
+ ANIM_PH_NUM(double_torus_to_single_point_rot_rnd),
+ ANIM_PH_NAME(double_torus_to_single_point_rot_rnd)
+};
+
+ANIMS_M_DEF(double_torus_to_single_point) = {
+ ANIM_PS_REF(double_torus_to_single_point_rot_z),
+ ANIM_PS_REF(double_torus_to_single_point_rot_rnd)
+};
+
+ANIMS_DEF(double_torus_to_single_point) = {
+ ANIMS_M_NUM(double_torus_to_single_point),
+ ANIMS_M_NAME(double_torus_to_single_point)
+};
+
+
+
+/* The set of possible animations for the transition from a double torus
+ to a single torus. */
+
+/* Rotate two tori at latitude ±45° around the z axis and merge them into
+ a dense torus at the equator, then reduce the point density by a factor
+ of two. */
+
+/* Phase 1: Rotate two tori at latitude ±45° around the z axis and merge
+ them into a dense torus at the equator. */
+ANIM_SO_DEF(double_torus_to_single_dense_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_single_dense_torus_rot_z) = {
+ ANIM_SO_NUM(double_torus_to_single_dense_torus_rot_z),
+ ANIM_SO_NAME(double_torus_to_single_dense_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 2: Reduce the point density by a factor of two. */
+ANIM_SO_DEF(single_dense_torus_to_single_torus_loosen_two) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_torus_to_single_torus_loosen_two) = {
+ ANIM_SO_NUM(single_dense_torus_to_single_torus_loosen_two),
+ ANIM_SO_NAME(single_dense_torus_to_single_torus_loosen_two),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 30 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_single_torus_rot_z) = {
+ ANIM_MO_REF(double_torus_to_single_dense_torus_rot_z),
+ ANIM_MO_REF(single_dense_torus_to_single_torus_loosen_two)
+};
+
+ANIM_PS_DEF(double_torus_to_single_torus_rot_z) = {
+ ANIM_PH_NUM(double_torus_to_single_torus_rot_z),
+ ANIM_PH_NAME(double_torus_to_single_torus_rot_z)
+};
+
+/* Rotate two tori at latitude ±45° around a random axis and move them to
+ the equator. */
+ANIM_SO_DEF(double_torus_to_single_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_single_torus_rot_rnd) = {
+ ANIM_SO_NUM(double_torus_to_single_torus_rot_rnd),
+ ANIM_SO_NAME(double_torus_to_single_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_single_torus_rot_rnd) = {
+ ANIM_MO_REF(double_torus_to_single_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(double_torus_to_single_torus_rot_rnd) = {
+ ANIM_PH_NUM(double_torus_to_single_torus_rot_rnd),
+ ANIM_PH_NAME(double_torus_to_single_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(double_torus_to_single_torus) = {
+ ANIM_PS_REF(double_torus_to_single_torus_rot_z),
+ ANIM_PS_REF(double_torus_to_single_torus_rot_rnd)
+};
+
+ANIMS_DEF(double_torus_to_single_torus) = {
+ ANIMS_M_NUM(double_torus_to_single_torus),
+ ANIMS_M_NAME(double_torus_to_single_torus)
+};
+
+
+
+/* The set of possible animations for a double torus. */
+
+/* Rotate two tori at latitude ±45° around the x axis of the total space
+ while rotating them around the z axis in opposite directions. */
+ANIM_SO_DEF(double_torus_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_rot_x) = {
+ ANIM_SO_NUM(double_torus_rot_x),
+ ANIM_SO_NAME(double_torus_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_rot_x) = {
+ ANIM_MO_REF(double_torus_rot_x)
+};
+
+ANIM_PS_DEF(double_torus_rot_x) = {
+ ANIM_PH_NUM(double_torus_rot_x),
+ ANIM_PH_NAME(double_torus_rot_x)
+};
+
+/* Rotate two tori at latitude ±45° by 90° around the y axis, then rotate
+ them by 360° around the x axis of the total space, and then once more by
+ 90° around the y axis. */
+
+/* Phase 1: Rotate the tori around the y axis by 90°. */
+ANIM_SO_DEF(double_torus_rot_y_p1) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 1.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 1.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_rot_y_p1) = {
+ ANIM_SO_NUM(double_torus_rot_y_p1),
+ ANIM_SO_NAME(double_torus_rot_y_p1),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+/* Phase 2: Rotate the two rotated tori by 360° around the x axis of the
+ total space. */
+ANIM_SO_DEF(double_torus_rot_y_p2) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 1.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 1.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_rot_y_p2) = {
+ ANIM_SO_NUM(double_torus_rot_y_p2),
+ ANIM_SO_NAME(double_torus_rot_y_p2),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 180 /* num_steps */
+};
+
+/* Phase 3: Rotate the tori around the y axis by an additional 90°. */
+ANIM_SO_DEF(double_torus_rot_y_p3) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 1.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 1.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_rot_y_p3) = {
+ ANIM_SO_NUM(double_torus_rot_y_p3),
+ ANIM_SO_NAME(double_torus_rot_y_p3),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_rot_y) = {
+ ANIM_MO_REF(double_torus_rot_y_p1),
+ ANIM_MO_REF(double_torus_rot_y_p2),
+ ANIM_MO_REF(double_torus_rot_y_p3)
+};
+
+ANIM_PS_DEF(double_torus_rot_y) = {
+ ANIM_PH_NUM(double_torus_rot_y),
+ ANIM_PH_NAME(double_torus_rot_y)
+};
+
+/* Move two tori at latitudes ±45° to latitude ±70° and increase their point
+ density by a factor of two, rotate the tori around a random axis, and
+ move them back to ±45° while decreasing their point density by a factor
+ of two. */
+
+/* Phase 1: Move two tori at latitude ±45° to latitude ±70° and increase
+ their point density by a factor of two. */
+ANIM_SO_DEF(double_torus_rot_rnd_move_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_rot_rnd_move_densify) = {
+ ANIM_SO_NUM(double_torus_rot_rnd_move_densify),
+ ANIM_SO_NAME(double_torus_rot_rnd_move_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Rotate the two tori by 360° around a random axis. */
+ANIM_SO_DEF(double_torus_rot_rnd_rot) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, M_PI_F/48.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 8.0f*M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, M_PI_F/48.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_rot_rnd_rot) = {
+ ANIM_SO_NUM(double_torus_rot_rnd_rot),
+ ANIM_SO_NAME(double_torus_rot_rnd_rot),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 3: Move two tori at latitude ±70° to latitude ±45° and decrease
+ their point density by a factor of two. */
+ANIM_SO_DEF(double_torus_rot_rnd_move_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_rot_rnd_move_loosen) = {
+ ANIM_SO_NUM(double_torus_rot_rnd_move_loosen),
+ ANIM_SO_NAME(double_torus_rot_rnd_move_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_rot_rnd) = {
+ ANIM_MO_REF(double_torus_rot_rnd_move_densify),
+ ANIM_MO_REF(double_torus_rot_rnd_rot),
+ ANIM_MO_REF(double_torus_rot_rnd_move_loosen)
+};
+
+ANIM_PS_DEF(double_torus_rot_rnd) = {
+ ANIM_PH_NUM(double_torus_rot_rnd),
+ ANIM_PH_NAME(double_torus_rot_rnd)
+};
+
+/* Move two tori at latitude ±45° to latitude ±5°, to latitude ±85° and
+ back to latitude ±45° while rotating them around the z axis and, with
+ a certain probability, around a random axis. */
+ANIM_SO_DEF(double_torus_move) = {
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/18.0f, M_PI_F/18.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 10.0f*M_PI_F/18.0f, 17.0f*M_PI_F/18.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_move) = {
+ ANIM_SO_NUM(double_torus_move),
+ ANIM_SO_NAME(double_torus_move),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_move) = {
+ ANIM_MO_REF(double_torus_move)
+};
+
+ANIM_PS_DEF(double_torus_move) = {
+ ANIM_PH_NUM(double_torus_move),
+ ANIM_PH_NAME(double_torus_move)
+};
+
+ANIMS_M_DEF(double_torus) = {
+ ANIM_PS_REF(double_torus_rot_x),
+ ANIM_PS_REF(double_torus_rot_y),
+ ANIM_PS_REF(double_torus_rot_rnd),
+ ANIM_PS_REF(double_torus_move)
+};
+
+ANIMS_DEF(double_torus) = {
+ ANIMS_M_NUM(double_torus),
+ ANIMS_M_NAME(double_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a double torus
+ to a triple torus. */
+
+/* Rotate the three tori around the z axis, split the torus at the equator
+ into two tori, and move them to latitude ±45°. */
+ANIM_SO_DEF(double_torus_to_triple_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/12.0f, M_PI_F/12.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_triple_torus_rot_z) = {
+ ANIM_SO_NUM(double_torus_to_triple_torus_rot_z),
+ ANIM_SO_NAME(double_torus_to_triple_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_triple_torus_rot_z) = {
+ ANIM_MO_REF(double_torus_to_triple_torus_rot_z)
+};
+
+ANIM_PS_DEF(double_torus_to_triple_torus_rot_z) = {
+ ANIM_PH_NUM(double_torus_to_triple_torus_rot_z),
+ ANIM_PH_NAME(double_torus_to_triple_torus_rot_z)
+};
+
+/* Rotate two tori at around a random axis, split the tori at latitude ±45°
+ into two tori, and move them to the equator. */
+ANIM_SO_DEF(double_torus_to_triple_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_triple_torus_rot_rnd) = {
+ ANIM_SO_NUM(double_torus_to_triple_torus_rot_rnd),
+ ANIM_SO_NAME(double_torus_to_triple_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_triple_torus_rot_rnd) = {
+ ANIM_MO_REF(double_torus_to_triple_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(double_torus_to_triple_torus_rot_rnd) = {
+ ANIM_PH_NUM(double_torus_to_triple_torus_rot_rnd),
+ ANIM_PH_NAME(double_torus_to_triple_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(double_torus_to_triple_torus) = {
+ ANIM_PS_REF(double_torus_to_triple_torus_rot_z),
+ ANIM_PS_REF(double_torus_to_triple_torus_rot_rnd)
+};
+
+ANIMS_DEF(double_torus_to_triple_torus) = {
+ ANIMS_M_NUM(double_torus_to_triple_torus),
+ ANIMS_M_NAME(double_torus_to_triple_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a double torus
+ to a single Seifert surface. */
+
+/* Shrink the two tori from 360° sectors to 90° sectors, join them at the
+ equator, and reduce the point density by a factor of two. */
+
+/* Phase 1: Shrink the two tori from 360° sectors to 90° sectors and join
+ them at the equator. */
+ANIM_SO_DEF(double_torus_to_single_dense_seifert_two_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/2.0f, -49.0f*M_PI_F/96.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/2.0f, -51.0f*M_PI_F/96.0f, EASING_CUBIC, /* offset */
+ -2.0f*M_PI_F, -M_PI_F/2.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_single_dense_seifert_two_rot_z) = {
+ ANIM_SO_NUM(double_torus_to_single_dense_seifert_two_rot_z),
+ ANIM_SO_NAME(double_torus_to_single_dense_seifert_two_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 2: Reduce the point density by a factor of two. */
+ANIM_SO_DEF(single_dense_seifert_two_to_single_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 95.0f*M_PI_F/96.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 97.0f*M_PI_F/96.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_seifert_two_to_single_seifert_rot_z) = {
+ ANIM_SO_NUM(single_dense_seifert_two_to_single_seifert_rot_z),
+ ANIM_SO_NAME(single_dense_seifert_two_to_single_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 30 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_single_seifert_rot_z) = {
+ ANIM_MO_REF(double_torus_to_single_dense_seifert_two_rot_z),
+ ANIM_MO_REF(single_dense_seifert_two_to_single_seifert_rot_z)
+};
+
+ANIM_PS_DEF(double_torus_to_single_seifert_rot_z) = {
+ ANIM_PH_NUM(double_torus_to_single_seifert_rot_z),
+ ANIM_PH_NAME(double_torus_to_single_seifert_rot_z)
+};
+
+/* Shrink the two tori from 360° sectors to 180° sectors, reduce their
+ point point density by a factor of two, interleave them at the
+ equator, and rotate around a random axis. */
+ANIM_SO_DEF(double_torus_to_single_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 13.0f*M_PI_F/12.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 13.0f*M_PI_F/12.0f, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_single_seifert_rot_rnd) = {
+ ANIM_SO_NUM(double_torus_to_single_seifert_rot_rnd),
+ ANIM_SO_NAME(double_torus_to_single_seifert_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_single_seifert_rot_rnd) = {
+ ANIM_MO_REF(double_torus_to_single_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(double_torus_to_single_seifert_rot_rnd) = {
+ ANIM_PH_NUM(double_torus_to_single_seifert_rot_rnd),
+ ANIM_PH_NAME(double_torus_to_single_seifert_rot_rnd)
+};
+
+ANIMS_M_DEF(double_torus_to_single_seifert) = {
+ ANIM_PS_REF(double_torus_to_single_seifert_rot_z),
+ ANIM_PS_REF(double_torus_to_single_seifert_rot_rnd)
+};
+
+ANIMS_DEF(double_torus_to_single_seifert) = {
+ ANIMS_M_NUM(double_torus_to_single_seifert),
+ ANIMS_M_NAME(double_torus_to_single_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a double torus
+ to a triple Seifert surface. */
+
+/* Rotate the two tori around the z axis, split off two tori from the tori
+ at latitide ±45°, decrease their sector from 360° to 180°, and move them
+ to the equator. */
+ANIM_SO_DEF(double_torus_to_triple_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, M_PI_F, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, M_PI_F, 0.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_triple_seifert_rot_z) = {
+ ANIM_SO_NUM(double_torus_to_triple_seifert_rot_z),
+ ANIM_SO_NAME(double_torus_to_triple_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_triple_seifert_rot_z) = {
+ ANIM_MO_REF(double_torus_to_triple_seifert_rot_z)
+};
+
+ANIM_PS_DEF(double_torus_to_triple_seifert_rot_z) = {
+ ANIM_PH_NUM(double_torus_to_triple_seifert_rot_z),
+ ANIM_PH_NAME(double_torus_to_triple_seifert_rot_z)
+};
+
+/* Split off two tori from the tori at latitude ±45°, decrease their sector
+ from 360° to 180°, move them to the equator, and rotate all Seifert
+ surfaces around a random axis with a certain probability. */
+ANIM_SO_DEF(double_torus_to_triple_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_SO_NUM(double_torus_to_triple_seifert_rot_rnd),
+ ANIM_SO_NAME(double_torus_to_triple_seifert_rot_rnd),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_MO_REF(double_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(double_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_PH_NUM(double_torus_to_triple_seifert_rot_rnd),
+ ANIM_PH_NAME(double_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIMS_M_DEF(double_torus_to_triple_seifert) = {
+ ANIM_PS_REF(double_torus_to_triple_seifert_rot_z),
+ ANIM_PS_REF(double_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIMS_DEF(double_torus_to_triple_seifert) = {
+ ANIMS_M_NUM(double_torus_to_triple_seifert),
+ ANIMS_M_NAME(double_torus_to_triple_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a double torus
+ to a Hopf torus. */
+
+/* Decrease the density of the two tori by a factor of two, deform them to
+ an interleaved Hopf torus, and increase the density of the Hopf torus by
+ a factor of five. */
+
+/* Phase 1: Decrease the density of the tori at ±45°. */
+ANIM_SO_DEF(double_torus_to_loose_double_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/12.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/12.0f, M_PI_F/12.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_loose_double_torus) = {
+ ANIM_SO_NUM(double_torus_to_loose_double_torus),
+ ANIM_SO_NAME(double_torus_to_loose_double_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Deform the two to loose tori an interleaved Hopf torus. */
+ANIM_SO_DEF(loose_double_torus_to_loose_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/12.0f, M_PI_F/12.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(loose_double_torus_to_loose_hopf_torus) = {
+ ANIM_SO_NUM(loose_double_torus_to_loose_hopf_torus),
+ ANIM_SO_NAME(loose_double_torus_to_loose_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+/* Phase 3: Increase the density of the Hopf torus by a factor of five. */
+ANIM_SO_DEF(loose_hopf_torus_to_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(loose_hopf_torus_to_hopf_torus) = {
+ ANIM_SO_NUM(loose_hopf_torus_to_hopf_torus),
+ ANIM_SO_NAME(loose_hopf_torus_to_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_single_hopf_torus_densify) = {
+ ANIM_MO_REF(double_torus_to_loose_double_torus),
+ ANIM_MO_REF(loose_double_torus_to_loose_hopf_torus),
+ ANIM_MO_REF(loose_hopf_torus_to_hopf_torus)
+};
+
+ANIM_PS_DEF(double_torus_to_single_hopf_torus_densify) = {
+ ANIM_PH_NUM(double_torus_to_single_hopf_torus_densify),
+ ANIM_PH_NAME(double_torus_to_single_hopf_torus_densify)
+};
+
+ANIMS_M_DEF(double_torus_to_single_hopf_torus) = {
+ ANIM_PS_REF(double_torus_to_single_hopf_torus_densify)
+};
+
+ANIMS_DEF(double_torus_to_single_hopf_torus) = {
+ ANIMS_M_NUM(double_torus_to_single_hopf_torus),
+ ANIMS_M_NAME(double_torus_to_single_hopf_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a double torus
+ to a single Hopf spiral. */
+
+/* Deform two tori at latitiude ±45° to two Hopf spirals, merge them, and
+ increase the point density by a factor of three halves. */
+
+/* Phase 1: Deform two tori at latitiude ±45° to two Hopf spirals and merge
+ them. */
+ANIM_SO_DEF(double_torus_to_single_loose_hopf_spiral_merge) = {
+ {
+ GEN_SPIRAL, /* generator */
+ -M_PI_F/4.0f, 0.0f, EASING_CUBIC, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ M_PI_F/4.0f, 0.0f, EASING_CUBIC, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(double_torus_to_single_loose_hopf_spiral_merge) = {
+ ANIM_SO_NUM(double_torus_to_single_loose_hopf_spiral_merge),
+ ANIM_SO_NAME(double_torus_to_single_loose_hopf_spiral_merge),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 2: Increase the density of the Hopf spiral by a factor of three
+ halves. */
+ANIM_SO_DEF(single_loose_hopf_spiral_to_single_hopf_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -23.0f*M_PI_F/24.0f, -35.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -23.0f*M_PI_F/24.0f, -34.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_loose_hopf_spiral_to_single_hopf_spiral) = {
+ ANIM_SO_NUM(single_loose_hopf_spiral_to_single_hopf_spiral),
+ ANIM_SO_NAME(single_loose_hopf_spiral_to_single_hopf_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+ANIM_PH_DEF(double_torus_to_single_hopf_spiral_merge) = {
+ ANIM_MO_REF(double_torus_to_single_loose_hopf_spiral_merge),
+ ANIM_MO_REF(single_loose_hopf_spiral_to_single_hopf_spiral)
+};
+
+ANIM_PS_DEF(double_torus_to_single_hopf_spiral_merge) = {
+ ANIM_PH_NUM(double_torus_to_single_hopf_spiral_merge),
+ ANIM_PH_NAME(double_torus_to_single_hopf_spiral_merge)
+};
+
+ANIMS_M_DEF(double_torus_to_single_hopf_spiral) = {
+ ANIM_PS_REF(double_torus_to_single_hopf_spiral_merge)
+};
+
+ANIMS_DEF(double_torus_to_single_hopf_spiral) = {
+ ANIMS_M_NUM(double_torus_to_single_hopf_spiral),
+ ANIMS_M_NAME(double_torus_to_single_hopf_spiral)
+};
+
+
+
+/*****************************************************************************
+ * The set of all transformations from a triple torus.
+ *****************************************************************************/
+
+/* The set of possible animations for the transition from a triple torus
+ to a single point. */
+
+/* Rotate two tori at latitude ±45° around the z axis, shrink them to a
+ single point, and move them to the equator. Shrink the torus on the
+ equator to a single point without rotating it. */
+ANIM_SO_DEF(triple_torus_to_single_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ -2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_single_point_rot_z) = {
+ ANIM_SO_NUM(triple_torus_to_single_point_rot_z),
+ ANIM_SO_NAME(triple_torus_to_single_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_single_point_rot_z) = {
+ ANIM_MO_REF(triple_torus_to_single_point_rot_z)
+};
+
+ANIM_PS_DEF(triple_torus_to_single_point_rot_z) = {
+ ANIM_PH_NUM(triple_torus_to_single_point_rot_z),
+ ANIM_PH_NAME(triple_torus_to_single_point_rot_z)
+};
+
+/* Rotate three tori around a random axis, move the two tori at latitude
+ ±45° to the equator, and shrink all three tori to a single point. */
+ANIM_SO_DEF(triple_torus_to_single_point_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ -2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_single_point_rot_rnd) = {
+ ANIM_SO_NUM(triple_torus_to_single_point_rot_rnd),
+ ANIM_SO_NAME(triple_torus_to_single_point_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_single_point_rot_rnd) = {
+ ANIM_MO_REF(triple_torus_to_single_point_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_torus_to_single_point_rot_rnd) = {
+ ANIM_PH_NUM(triple_torus_to_single_point_rot_rnd),
+ ANIM_PH_NAME(triple_torus_to_single_point_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_torus_to_single_point) = {
+ ANIM_PS_REF(triple_torus_to_single_point_rot_z),
+ ANIM_PS_REF(triple_torus_to_single_point_rot_rnd)
+};
+
+ANIMS_DEF(triple_torus_to_single_point) = {
+ ANIMS_M_NUM(triple_torus_to_single_point),
+ ANIMS_M_NAME(triple_torus_to_single_point)
+};
+
+
+
+/* The set of possible animations for the transition from a triple torus
+ to a single torus. */
+
+/* Rotate two tori at latitude ±45° around the z axis and interleave them
+ into a dense torus at the equator, then reduce the point density by a
+ factor of three. */
+
+/* Phase 1: Rotate two tori at latitude ±45° around the z axis and merge
+ them into a dense torus at the equator. */
+ANIM_SO_DEF(triple_torus_to_single_dense_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_single_dense_torus_rot_z) = {
+ ANIM_SO_NUM(triple_torus_to_single_dense_torus_rot_z),
+ ANIM_SO_NAME(triple_torus_to_single_dense_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 2: Reduce the point density by a factor of three. */
+ANIM_SO_DEF(single_dense_torus_to_single_torus_loosen_three) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_torus_to_single_torus_loosen_three) = {
+ ANIM_SO_NUM(single_dense_torus_to_single_torus_loosen_three),
+ ANIM_SO_NAME(single_dense_torus_to_single_torus_loosen_three),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 30 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_single_torus_rot_z) = {
+ ANIM_MO_REF(triple_torus_to_single_dense_torus_rot_z),
+ ANIM_MO_REF(single_dense_torus_to_single_torus_loosen_three)
+};
+
+ANIM_PS_DEF(triple_torus_to_single_torus_rot_z) = {
+ ANIM_PH_NUM(triple_torus_to_single_torus_rot_z),
+ ANIM_PH_NAME(triple_torus_to_single_torus_rot_z)
+};
+
+/* Rotate three tori around a random axis and move the tori at latitudes
+ ±45° to the equator. */
+ANIM_SO_DEF(triple_torus_to_single_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_single_torus_rot_rnd) = {
+ ANIM_SO_NUM(triple_torus_to_single_torus_rot_rnd),
+ ANIM_SO_NAME(triple_torus_to_single_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_single_torus_rot_rnd) = {
+ ANIM_MO_REF(triple_torus_to_single_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_torus_to_single_torus_rot_rnd) = {
+ ANIM_PH_NUM(triple_torus_to_single_torus_rot_rnd),
+ ANIM_PH_NAME(triple_torus_to_single_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_torus_to_single_torus) = {
+ ANIM_PS_REF(triple_torus_to_single_torus_rot_z),
+ ANIM_PS_REF(triple_torus_to_single_torus_rot_rnd)
+};
+
+ANIMS_DEF(triple_torus_to_single_torus) = {
+ ANIMS_M_NUM(triple_torus_to_single_torus),
+ ANIMS_M_NAME(triple_torus_to_single_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a triple torus
+ to a double torus. */
+
+/* Rotate the three tori around the z axis, split the torus at the equator
+ into two tori, and move them to latitude ±45°. */
+ANIM_SO_DEF(triple_torus_to_double_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/12.0f, M_PI_F/12.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_double_torus_rot_z) = {
+ ANIM_SO_NUM(triple_torus_to_double_torus_rot_z),
+ ANIM_SO_NAME(triple_torus_to_double_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_double_torus_rot_z) = {
+ ANIM_MO_REF(triple_torus_to_double_torus_rot_z)
+};
+
+ANIM_PS_DEF(triple_torus_to_double_torus_rot_z) = {
+ ANIM_PH_NUM(triple_torus_to_double_torus_rot_z),
+ ANIM_PH_NAME(triple_torus_to_double_torus_rot_z)
+};
+
+/* Rotate three tori at around a random axis, split the torus at the
+ equator into two tori, and move them to latitude ±45°. */
+ANIM_SO_DEF(triple_torus_to_double_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_double_torus_rot_rnd) = {
+ ANIM_SO_NUM(triple_torus_to_double_torus_rot_rnd),
+ ANIM_SO_NAME(triple_torus_to_double_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_double_torus_rot_rnd) = {
+ ANIM_MO_REF(triple_torus_to_double_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_torus_to_double_torus_rot_rnd) = {
+ ANIM_PH_NUM(triple_torus_to_double_torus_rot_rnd),
+ ANIM_PH_NAME(triple_torus_to_double_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_torus_to_double_torus) = {
+ ANIM_PS_REF(triple_torus_to_double_torus_rot_z),
+ ANIM_PS_REF(triple_torus_to_double_torus_rot_rnd)
+};
+
+ANIMS_DEF(triple_torus_to_double_torus) = {
+ ANIMS_M_NUM(triple_torus_to_double_torus),
+ ANIMS_M_NAME(triple_torus_to_double_torus)
+};
+
+
+
+/* The set of possible animations for a triple torus. */
+
+/* Rotate three tori at latitudes ±45° and 0° around the x axis of the
+ total space while rotating them around the z axis in different
+ directions. */
+ANIM_SO_DEF(triple_torus_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_rot_x) = {
+ ANIM_SO_NUM(triple_torus_rot_x),
+ ANIM_SO_NAME(triple_torus_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_rot_x) = {
+ ANIM_MO_REF(triple_torus_rot_x)
+};
+
+ANIM_PS_DEF(triple_torus_rot_x) = {
+ ANIM_PH_NUM(triple_torus_rot_x),
+ ANIM_PH_NAME(triple_torus_rot_x)
+};
+
+/* Rotate the three tori to a a vertical orientation, yielding a parabolic
+ ring cyclide and two interlocking tori, and increase their density by a
+ factor of three. Then, rotate them around the x axis of the total space.
+ Next, move the two tori not on the equator between latitudes 1° and ±89°,
+ showing that two solid interlocking tori fill up the complete S³.
+ Finally, rotate the tori back to 0° and ±45° and decrease their density
+ by a factor of three. */
+
+/* Phase 1: Rotate the tori by 90° to a vertical position, increase their
+ point density by a factor of three, and rotate them around the x axis
+ of the total space by 90°. */
+ANIM_SO_DEF(triple_torus_to_vertical_triple_torus_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_vertical_triple_torus_densify) = {
+ ANIM_SO_NUM(triple_torus_to_vertical_triple_torus_densify),
+ ANIM_SO_NAME(triple_torus_to_vertical_triple_torus_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { -1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC, /* rot_space */
+ 120 /* num_steps */
+};
+
+/* Phase 2: Rotate the three tori by 360° around the x axis of the total
+ space. */
+ANIM_SO_DEF(vertical_triple_torus_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(vertical_triple_torus_rot_x) = {
+ ANIM_SO_NUM(vertical_triple_torus_rot_x),
+ ANIM_SO_NAME(vertical_triple_torus_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 5.0f*M_PI_F/2.0f,
+ EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 3: Move the two tori at latitides ±45° between latitudes 1° and
+ ±89°. */
+ANIM_SO_DEF(vertical_triple_torus_move_z) = {
+ {
+ GEN_TORUS, /* generator */
+ 179.0f*M_PI_F/180.0f, 91.0f*M_PI_F/180.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 1.0f*M_PI_F/180.0f, 89.0f*M_PI_F/180.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(vertical_triple_torus_move_z) = {
+ ANIM_SO_NUM(vertical_triple_torus_move_z),
+ ANIM_SO_NAME(vertical_triple_torus_move_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 4: Rotate the tori back to 0° and ±45° and decrease their density
+ by a factor of three. */
+ANIM_SO_DEF(vertical_triple_torus_to_triple_torus_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(vertical_triple_torus_to_triple_torus_loosen) = {
+ ANIM_SO_NUM(vertical_triple_torus_to_triple_torus_loosen),
+ ANIM_SO_NAME(vertical_triple_torus_to_triple_torus_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC, /* rot_space */
+ 120 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_three_sphere) = {
+ ANIM_MO_REF(triple_torus_to_vertical_triple_torus_densify),
+ ANIM_MO_REF(vertical_triple_torus_rot_x),
+ ANIM_MO_REF(vertical_triple_torus_move_z),
+ ANIM_MO_REF(vertical_triple_torus_to_triple_torus_loosen)
+};
+
+ANIM_PS_DEF(triple_torus_three_sphere) = {
+ ANIM_PH_NUM(triple_torus_three_sphere),
+ ANIM_PH_NAME(triple_torus_three_sphere)
+};
+
+/* Move the two tori at latitudes ±45° to latitude ±70° and increase the
+ point density of all three tori by a factor of two, rotate the tori
+ around a random axis, move the two tori at latitude ±70° back to ±45°,
+ and decrease the point density of all three tori by a factor of two. */
+
+/* Phase 1: Move two tori at latitude ±45° to latitude ±70° and increase
+ the point density of all three tori by a factor of two. */
+ANIM_SO_DEF(triple_torus_rot_rnd_move_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_rot_rnd_move_densify) = {
+ ANIM_SO_NUM(triple_torus_rot_rnd_move_densify),
+ ANIM_SO_NAME(triple_torus_rot_rnd_move_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Rotate the two tori by 360° around a random axis. */
+ANIM_SO_DEF(triple_torus_rot_rnd_rot) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, M_PI_F/48.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, M_PI_F/48.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 8.0f*M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, M_PI_F/48.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_rot_rnd_rot) = {
+ ANIM_SO_NUM(triple_torus_rot_rnd_rot),
+ ANIM_SO_NAME(triple_torus_rot_rnd_rot),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 3: Move the two tori at latitude ±70° to latitude ±45° and
+ decrease the point density of all three tori by a factor of two. */
+ANIM_SO_DEF(triple_torus_rot_rnd_move_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_rot_rnd_move_loosen) = {
+ ANIM_SO_NUM(triple_torus_rot_rnd_move_loosen),
+ ANIM_SO_NAME(triple_torus_rot_rnd_move_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_rot_rnd) = {
+ ANIM_MO_REF(triple_torus_rot_rnd_move_densify),
+ ANIM_MO_REF(triple_torus_rot_rnd_rot),
+ ANIM_MO_REF(triple_torus_rot_rnd_move_loosen)
+};
+
+ANIM_PS_DEF(triple_torus_rot_rnd) = {
+ ANIM_PH_NUM(triple_torus_rot_rnd),
+ ANIM_PH_NAME(triple_torus_rot_rnd)
+};
+
+/* Move the two tori at latitude ±45° to latitude ±5°, to latitude ±85° and
+ back to latitude ±45° while rotating all three tori around the z axis
+ and, with a certain probability, around a random axis. */
+ANIM_SO_DEF(triple_torus_move) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/18.0f, 8.0f*M_PI_F/18.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 17.0f*M_PI_F/18.0f, 10.0f*M_PI_F/18.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_move) = {
+ ANIM_SO_NUM(triple_torus_move),
+ ANIM_SO_NAME(triple_torus_move),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_move) = {
+ ANIM_MO_REF(triple_torus_move)
+};
+
+ANIM_PS_DEF(triple_torus_move) = {
+ ANIM_PH_NUM(triple_torus_move),
+ ANIM_PH_NAME(triple_torus_move)
+};
+
+ANIMS_M_DEF(triple_torus) = {
+ ANIM_PS_REF(triple_torus_rot_x),
+ ANIM_PS_REF(triple_torus_three_sphere),
+ ANIM_PS_REF(triple_torus_rot_rnd),
+ ANIM_PS_REF(triple_torus_move)
+};
+
+ANIMS_DEF(triple_torus) = {
+ ANIMS_M_NUM(triple_torus),
+ ANIMS_M_NAME(triple_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a triple torus
+ to a single Seifert surface. */
+
+/* Shrink the three tori from 360° sectors to 60° sectors, join them at the
+ equator, and reduce the point density by a factor of three. */
+
+/* Phase 1: Shrink the three tori from 360° sectors to 60° sectors and join
+ them at the equator. */
+ANIM_SO_DEF(triple_torus_to_single_dense_seifert_three_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -3.0f*M_PI_F/2.0f, -25.0f*M_PI_F/72.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -3.0f*M_PI_F/2.0f, -49.0f*M_PI_F/72.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -3.0f*M_PI_F/2.0f, -73.0f*M_PI_F/72.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_single_dense_seifert_three_rot_z) = {
+ ANIM_SO_NUM(triple_torus_to_single_dense_seifert_three_rot_z),
+ ANIM_SO_NAME(triple_torus_to_single_dense_seifert_three_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 2: Reduce the point density by a factor of three. */
+ANIM_SO_DEF(single_dense_seifert_three_to_single_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 71.0f*M_PI_F/72.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 73.0f*M_PI_F/72.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_seifert_three_to_single_seifert_rot_z) = {
+ ANIM_SO_NUM(single_dense_seifert_three_to_single_seifert_rot_z),
+ ANIM_SO_NAME(single_dense_seifert_three_to_single_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 30 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_single_seifert_rot_z) = {
+ ANIM_MO_REF(triple_torus_to_single_dense_seifert_three_rot_z),
+ ANIM_MO_REF(single_dense_seifert_three_to_single_seifert_rot_z)
+};
+
+ANIM_PS_DEF(triple_torus_to_single_seifert_rot_z) = {
+ ANIM_PH_NUM(triple_torus_to_single_seifert_rot_z),
+ ANIM_PH_NAME(triple_torus_to_single_seifert_rot_z)
+};
+
+/* Shrink the three tori from 360° sectors to 180° sectors, reduce their
+ point point density by a factor of three, interleave them at the
+ equator, and rotate around a random axis. */
+ANIM_SO_DEF(triple_torus_to_single_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 13.0f*M_PI_F/12.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 14.0f*M_PI_F/12.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 13.0f*M_PI_F/12.0f, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 14.0f*M_PI_F/12.0f, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 13.0f*M_PI_F/12.0f, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 14.0f*M_PI_F/12.0f, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_single_seifert_rot_rnd) = {
+ ANIM_SO_NUM(triple_torus_to_single_seifert_rot_rnd),
+ ANIM_SO_NAME(triple_torus_to_single_seifert_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_single_seifert_rot_rnd) = {
+ ANIM_MO_REF(triple_torus_to_single_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_torus_to_single_seifert_rot_rnd) = {
+ ANIM_PH_NUM(triple_torus_to_single_seifert_rot_rnd),
+ ANIM_PH_NAME(triple_torus_to_single_seifert_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_torus_to_single_seifert) = {
+ ANIM_PS_REF(triple_torus_to_single_seifert_rot_z),
+ ANIM_PS_REF(triple_torus_to_single_seifert_rot_rnd)
+};
+
+ANIMS_DEF(triple_torus_to_single_seifert) = {
+ ANIMS_M_NUM(triple_torus_to_single_seifert),
+ ANIMS_M_NAME(triple_torus_to_single_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a triple torus
+ to a triple Seifert surface. */
+
+/* Rotate the three tori around the z axis and decrease their sector from
+ 360° to 180°. */
+ANIM_SO_DEF(triple_torus_to_triple_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, -M_PI_F/2.0f, 2.0f*M_PI_F,
+ EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, -M_PI_F/2.0f, 2.0f*M_PI_F,
+ EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_triple_seifert_rot_z) = {
+ ANIM_SO_NUM(triple_torus_to_triple_seifert_rot_z),
+ ANIM_SO_NAME(triple_torus_to_triple_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_triple_seifert_rot_z) = {
+ ANIM_MO_REF(triple_torus_to_triple_seifert_rot_z)
+};
+
+ANIM_PS_DEF(triple_torus_to_triple_seifert_rot_z) = {
+ ANIM_PH_NUM(triple_torus_to_triple_seifert_rot_z),
+ ANIM_PH_NAME(triple_torus_to_triple_seifert_rot_z)
+};
+
+/* Decrease the sector of the tori from 360° to 180° and rotate them around
+ a random axis with a certain probability. */
+ANIM_SO_DEF(triple_torus_to_triple_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_SO_NUM(triple_torus_to_triple_seifert_rot_rnd),
+ ANIM_SO_NAME(triple_torus_to_triple_seifert_rot_rnd),
+ 0.75f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_MO_REF(triple_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_torus_to_triple_seifert_rot_rnd) = {
+ ANIM_PH_NUM(triple_torus_to_triple_seifert_rot_rnd),
+ ANIM_PH_NAME(triple_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_torus_to_triple_seifert) = {
+ ANIM_PS_REF(triple_torus_to_triple_seifert_rot_z),
+ ANIM_PS_REF(triple_torus_to_triple_seifert_rot_rnd)
+};
+
+ANIMS_DEF(triple_torus_to_triple_seifert) = {
+ ANIMS_M_NUM(triple_torus_to_triple_seifert),
+ ANIMS_M_NAME(triple_torus_to_triple_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a triple torus
+ to a Hopf torus. */
+
+/* Transform the three tori to a Hopf torus on the equator while increasing
+ the point density of the two tori at latitude ±45° by a factor of two. */
+ANIM_SO_DEF(triple_torus_to_single_hopf_torus_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, -1.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 1.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_single_hopf_torus_densify) = {
+ ANIM_SO_NUM(triple_torus_to_single_hopf_torus_densify),
+ ANIM_SO_NAME(triple_torus_to_single_hopf_torus_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 120 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_single_hopf_torus_densify) = {
+ ANIM_MO_REF(triple_torus_to_single_hopf_torus_densify)
+};
+
+ANIM_PS_DEF(triple_torus_to_single_hopf_torus_densify) = {
+ ANIM_PH_NUM(triple_torus_to_single_hopf_torus_densify),
+ ANIM_PH_NAME(triple_torus_to_single_hopf_torus_densify)
+};
+
+ANIMS_M_DEF(triple_torus_to_single_hopf_torus) = {
+ ANIM_PS_REF(triple_torus_to_single_hopf_torus_densify)
+};
+
+ANIMS_DEF(triple_torus_to_single_hopf_torus) = {
+ ANIMS_M_NUM(triple_torus_to_single_hopf_torus),
+ ANIMS_M_NAME(triple_torus_to_single_hopf_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a triple torus
+ to a single Hopf spiral. */
+
+/* Deform the three tori to three Hopf spirals and merge them. */
+ANIM_SO_DEF(triple_torus_to_single_hopf_spiral_merge) = {
+ {
+ GEN_SPIRAL, /* generator */
+ -M_PI_F/4.0f, 0.0f, EASING_CUBIC, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F/2.0f, -M_PI_F/3.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ M_PI_F/4.0f, 0.0f, EASING_CUBIC, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, M_PI_F/3.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_torus_to_single_hopf_spiral_merge) = {
+ ANIM_SO_NUM(triple_torus_to_single_hopf_spiral_merge),
+ ANIM_SO_NAME(triple_torus_to_single_hopf_spiral_merge),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_torus_to_single_hopf_spiral_merge) = {
+ ANIM_MO_REF(triple_torus_to_single_hopf_spiral_merge)
+};
+
+ANIM_PS_DEF(triple_torus_to_single_hopf_spiral_merge) = {
+ ANIM_PH_NUM(triple_torus_to_single_hopf_spiral_merge),
+ ANIM_PH_NAME(triple_torus_to_single_hopf_spiral_merge)
+};
+
+ANIMS_M_DEF(triple_torus_to_single_hopf_spiral) = {
+ ANIM_PS_REF(triple_torus_to_single_hopf_spiral_merge)
+};
+
+ANIMS_DEF(triple_torus_to_single_hopf_spiral) = {
+ ANIMS_M_NUM(triple_torus_to_single_hopf_spiral),
+ ANIMS_M_NAME(triple_torus_to_single_hopf_spiral)
+};
+
+
+
+/*****************************************************************************
+ * The set of all transformations from a single Seifert surface.
+ *****************************************************************************/
+
+/* The set of possible animations for the transition from a single Seifert
+ surface to a single point. */
+
+/* Shrink a Seifert surface on the equator to a point on the equator. */
+ANIM_SO_DEF(single_seifert_to_single_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_point_rot_z) = {
+ ANIM_SO_NUM(single_seifert_to_single_point_rot_z),
+ ANIM_SO_NAME(single_seifert_to_single_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_single_point_rot_z) = {
+ ANIM_MO_REF(single_seifert_to_single_point_rot_z)
+};
+
+ANIM_PS_DEF(single_seifert_to_single_point_rot_z) = {
+ ANIM_PH_NUM(single_seifert_to_single_point_rot_z),
+ ANIM_PH_NAME(single_seifert_to_single_point_rot_z)
+};
+
+/* Shrink a Seifert surface on the equator to a point on the equator and
+ rotate around a random axis. */
+ANIM_SO_DEF(single_seifert_to_single_point_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_point_rot_rnd) = {
+ ANIM_SO_NUM(single_seifert_to_single_point_rot_rnd),
+ ANIM_SO_NAME(single_seifert_to_single_point_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_single_point_rot_rnd) = {
+ ANIM_MO_REF(single_seifert_to_single_point_rot_rnd)
+};
+
+ANIM_PS_DEF(single_seifert_to_single_point_rot_rnd) = {
+ ANIM_PH_NUM(single_seifert_to_single_point_rot_rnd),
+ ANIM_PH_NAME(single_seifert_to_single_point_rot_rnd)
+};
+
+ANIMS_M_DEF(single_seifert_to_single_point) = {
+ ANIM_PS_REF(single_seifert_to_single_point_rot_z),
+ ANIM_PS_REF(single_seifert_to_single_point_rot_rnd)
+};
+
+ANIMS_DEF(single_seifert_to_single_point) = {
+ ANIMS_M_NUM(single_seifert_to_single_point),
+ ANIMS_M_NAME(single_seifert_to_single_point)
+};
+
+
+
+/* The set of possible animations for the transition from a single Seifert
+ surface to a single torus. */
+
+/* Expand the Seifert surface on the equator to a torus on the equator. */
+ANIM_SO_DEF(single_seifert_to_single_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_torus_rot_z) = {
+ ANIM_SO_NUM(single_seifert_to_single_torus_rot_z),
+ ANIM_SO_NAME(single_seifert_to_single_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_single_torus_rot_z) = {
+ ANIM_MO_REF(single_seifert_to_single_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_seifert_to_single_torus_rot_z) = {
+ ANIM_PH_NUM(single_seifert_to_single_torus_rot_z),
+ ANIM_PH_NAME(single_seifert_to_single_torus_rot_z)
+};
+
+/* Expand the Seifert surface on the equator to a torus on the equator and
+ rotate it around a random axis. */
+ANIM_SO_DEF(single_seifert_to_single_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_seifert_to_single_torus_rot_rnd),
+ ANIM_SO_NAME(single_seifert_to_single_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_single_torus_rot_rnd) = {
+ ANIM_MO_REF(single_seifert_to_single_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_seifert_to_single_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_seifert_to_single_torus_rot_rnd),
+ ANIM_PH_NAME(single_seifert_to_single_torus_rot_rnd)
+};
+
+/* Flip half of a Seifert surface on the equator around a suitable axis on
+ the equator to form a torus on the equator. */
+ANIM_SO_DEF(single_seifert_to_single_torus_flip) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.99785892f, -0.06540313f, 0.0f },
+ M_PI_F, 0.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_torus_flip) = {
+ ANIM_SO_NUM(single_seifert_to_single_torus_flip),
+ ANIM_SO_NAME(single_seifert_to_single_torus_flip),
+ 0.25f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_single_torus_flip) = {
+ ANIM_MO_REF(single_seifert_to_single_torus_flip)
+};
+
+ANIM_PS_DEF(single_seifert_to_single_torus_flip) = {
+ ANIM_PH_NUM(single_seifert_to_single_torus_flip),
+ ANIM_PH_NAME(single_seifert_to_single_torus_flip)
+};
+
+ANIMS_M_DEF(single_seifert_to_single_torus) = {
+ ANIM_PS_REF(single_seifert_to_single_torus_rot_z),
+ ANIM_PS_REF(single_seifert_to_single_torus_rot_rnd),
+ ANIM_PS_REF(single_seifert_to_single_torus_flip)
+};
+
+ANIMS_DEF(single_seifert_to_single_torus) = {
+ ANIMS_M_NUM(single_seifert_to_single_torus),
+ ANIMS_M_NAME(single_seifert_to_single_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single Seifert
+ surface to a double torus. */
+
+/* Increase the point density of the two Seifert surfaces by a factor of
+ two, expand them from 90° sectors to 360° sectors, and split them at
+ the equator. */
+
+/* Phase 1: Increase the point density by a factor of two. */
+ANIM_SO_DEF(single_seifert_to_single_dense_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 95.0f*M_PI_F/96.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 97.0f*M_PI_F/96.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_dense_seifert_rot_z) = {
+ ANIM_SO_NUM(single_seifert_to_single_dense_seifert_rot_z),
+ ANIM_SO_NAME(single_seifert_to_single_dense_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 30 /* num_steps */
+};
+
+/* Phase 2: Expand the two Seifert surfaces from 90° sectors to 360°
+ sectors and split them at the equator. */
+ANIM_SO_DEF(single_dense_seifert_to_double_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -49.0f*M_PI_F/96.0f, -M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F/2.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -51.0f*M_PI_F/96.0f, -M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ -M_PI_F/2.0f, -2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_seifert_to_double_torus_rot_z) = {
+ ANIM_SO_NUM(single_dense_seifert_to_double_torus_rot_z),
+ ANIM_SO_NAME(single_dense_seifert_to_double_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_double_torus_rot_z) = {
+ ANIM_MO_REF(single_seifert_to_single_dense_seifert_rot_z),
+ ANIM_MO_REF(single_dense_seifert_to_double_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_seifert_to_double_torus_rot_z) = {
+ ANIM_PH_NUM(single_seifert_to_double_torus_rot_z),
+ ANIM_PH_NAME(single_seifert_to_double_torus_rot_z)
+};
+
+/* Split the two Seifert surfaces at the equator, expand them from 180°
+ sectors to 360° sectors, increase their point density by a factor of two,
+ and rotate around a random axis. */
+ANIM_SO_DEF(single_seifert_to_double_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 13.0f*M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, 13.0f*M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_double_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_seifert_to_double_torus_rot_rnd),
+ ANIM_SO_NAME(single_seifert_to_double_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_double_torus_rot_rnd) = {
+ ANIM_MO_REF(single_seifert_to_double_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_seifert_to_double_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_seifert_to_double_torus_rot_rnd),
+ ANIM_PH_NAME(single_seifert_to_double_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(single_seifert_to_double_torus) = {
+ ANIM_PS_REF(single_seifert_to_double_torus_rot_z),
+ ANIM_PS_REF(single_seifert_to_double_torus_rot_rnd)
+};
+
+ANIMS_DEF(single_seifert_to_double_torus) = {
+ ANIMS_M_NUM(single_seifert_to_double_torus),
+ ANIMS_M_NAME(single_seifert_to_double_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single Seifert
+ surface to a triple torus. */
+
+/* Increase the point density of the three Seifert surfaces by a factor of
+ three, expand them from 60° sectors to 360° sectors, and split them at
+ the equator. */
+
+/* Phase 1: Increase the point density by a factor of three. */
+ANIM_SO_DEF(single_seifert_to_single_dense_seifert_three_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 71.0f*M_PI_F/72.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 73.0f*M_PI_F/72.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_dense_seifert_three_rot_z) = {
+ ANIM_SO_NUM(single_seifert_to_single_dense_seifert_three_rot_z),
+ ANIM_SO_NAME(single_seifert_to_single_dense_seifert_three_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 30 /* num_steps */
+};
+
+/* Phase 2: Expand the three Seifert surfaces from 60° sectors to 360°
+ sectors and split them at the equator. */
+ANIM_SO_DEF(single_dense_seifert_three_to_triple_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -25.0f*M_PI_F/72.0f, -3.0f*M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F/3.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -49.0f*M_PI_F/72.0f, -3.0f*M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F/3.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -73.0f*M_PI_F/72.0f, -3.0f*M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F/3.0f, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_seifert_three_to_triple_torus_rot_z) = {
+ ANIM_SO_NUM(single_dense_seifert_three_to_triple_torus_rot_z),
+ ANIM_SO_NAME(single_dense_seifert_three_to_triple_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_triple_torus_rot_z) = {
+ ANIM_MO_REF(single_seifert_to_single_dense_seifert_three_rot_z),
+ ANIM_MO_REF(single_dense_seifert_three_to_triple_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_seifert_to_triple_torus_rot_z) = {
+ ANIM_PH_NUM(single_seifert_to_triple_torus_rot_z),
+ ANIM_PH_NAME(single_seifert_to_triple_torus_rot_z)
+};
+
+/* Split the three Seifert surfaces at the equator, expand them from 180°
+ sectors to 360° sectors, increase their point density by a factor of
+ three, and rotate around a random axis. */
+ANIM_SO_DEF(single_seifert_to_triple_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 13.0f*M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 14.0f*M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, 13.0f*M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, 14.0f*M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, 13.0f*M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, 14.0f*M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_triple_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_seifert_to_triple_torus_rot_rnd),
+ ANIM_SO_NAME(single_seifert_to_triple_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_triple_torus_rot_rnd) = {
+ ANIM_MO_REF(single_seifert_to_triple_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_seifert_to_triple_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_seifert_to_triple_torus_rot_rnd),
+ ANIM_PH_NAME(single_seifert_to_triple_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(single_seifert_to_triple_torus) = {
+ ANIM_PS_REF(single_seifert_to_triple_torus_rot_z),
+ ANIM_PS_REF(single_seifert_to_triple_torus_rot_rnd)
+};
+
+ANIMS_DEF(single_seifert_to_triple_torus) = {
+ ANIMS_M_NUM(single_seifert_to_triple_torus),
+ ANIMS_M_NAME(single_seifert_to_triple_torus)
+};
+
+
+
+/* The set of possible animations for a single Seifert surface. */
+
+/* Rotate a Seifert surface on the x axis of the total space. */
+ANIM_SO_DEF(single_seifert_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_rot_x) = {
+ ANIM_SO_NUM(single_seifert_rot_x),
+ ANIM_SO_NAME(single_seifert_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_rot_x) = {
+ ANIM_MO_REF(single_seifert_rot_x)
+};
+
+ANIM_PS_DEF(single_seifert_rot_x) = {
+ ANIM_PH_NUM(single_seifert_rot_x),
+ ANIM_PH_NAME(single_seifert_rot_x)
+};
+
+/* Rotate a Seifert surface on the equator around the z axis. */
+ANIM_SO_DEF(single_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_rot_z) = {
+ ANIM_SO_NUM(single_seifert_rot_z),
+ ANIM_SO_NAME(single_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_rot_z) = {
+ ANIM_MO_REF(single_seifert_rot_z)
+};
+
+ANIM_PS_DEF(single_seifert_rot_z) = {
+ ANIM_PH_NUM(single_seifert_rot_z),
+ ANIM_PH_NAME(single_seifert_rot_z)
+};
+
+/* Rotate a Seifert surface on the equator around a random axis. */
+ANIM_SO_DEF(single_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_rot_rnd) = {
+ ANIM_SO_NUM(single_seifert_rot_rnd),
+ ANIM_SO_NAME(single_seifert_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_rot_rnd) = {
+ ANIM_MO_REF(single_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(single_seifert_rot_rnd) = {
+ ANIM_PH_NUM(single_seifert_rot_rnd),
+ ANIM_PH_NAME(single_seifert_rot_rnd)
+};
+
+/* Move a Seifert surface on the equator up and down to latitude ±80° while
+ rotating it around the z axis and, with a certain probability, around a
+ random axis. */
+ANIM_SO_DEF(single_seifert_move) = {
+ {
+ GEN_TORUS, /* generator */
+ 1.0f*M_PI_F/18.0f, 17.0f*M_PI_F/18.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_move) = {
+ ANIM_SO_NUM(single_seifert_move),
+ ANIM_SO_NAME(single_seifert_move),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_move) = {
+ ANIM_MO_REF(single_seifert_move)
+};
+
+ANIM_PS_DEF(single_seifert_move) = {
+ ANIM_PH_NUM(single_seifert_move),
+ ANIM_PH_NAME(single_seifert_move)
+};
+
+ANIMS_M_DEF(single_seifert) = {
+ ANIM_PS_REF(single_seifert_rot_x),
+ ANIM_PS_REF(single_seifert_rot_z),
+ ANIM_PS_REF(single_seifert_rot_rnd),
+ ANIM_PS_REF(single_seifert_move)
+};
+
+ANIMS_DEF(single_seifert) = {
+ ANIMS_M_NUM(single_seifert),
+ ANIMS_M_NAME(single_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single Seifert
+ surface to a triple Seifert surface. */
+
+/* Increase the point density of the three Seifert surfaces by a factor of
+ three, expand them from 60° sectors to 180° sectors, and split them at
+ the equator. */
+
+/* Phase 1: Increase the point density by a factor of three. Already
+ defined above. */
+
+/* Phase 2: Expand the three Seifert surfaces from 60° sectors to 360°
+ sectors and split them at the equator. */
+ANIM_SO_DEF(single_dense_seifert_three_to_triple_seifert_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -25.0f*M_PI_F/72.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -49.0f*M_PI_F/72.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -73.0f*M_PI_F/72.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_seifert_three_to_triple_seifert_rot_z) = {
+ ANIM_SO_NUM(single_dense_seifert_three_to_triple_seifert_rot_z),
+ ANIM_SO_NAME(single_dense_seifert_three_to_triple_seifert_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_triple_seifert_rot_z) = {
+ ANIM_MO_REF(single_seifert_to_single_dense_seifert_three_rot_z),
+ ANIM_MO_REF(single_dense_seifert_three_to_triple_seifert_rot_z)
+};
+
+ANIM_PS_DEF(single_seifert_to_triple_seifert_rot_z) = {
+ ANIM_PH_NUM(single_seifert_to_triple_seifert_rot_z),
+ ANIM_PH_NAME(single_seifert_to_triple_seifert_rot_z)
+};
+
+/* Split the three Seifert surfaces at the equator, increase their point
+ density by a factor of three, and rotate around a random axis. */
+ANIM_SO_DEF(single_seifert_to_triple_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_triple_seifert_rot_rnd) = {
+ ANIM_SO_NUM(single_seifert_to_triple_seifert_rot_rnd),
+ ANIM_SO_NAME(single_seifert_to_triple_seifert_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_triple_seifert_rot_rnd) = {
+ ANIM_MO_REF(single_seifert_to_triple_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(single_seifert_to_triple_seifert_rot_rnd) = {
+ ANIM_PH_NUM(single_seifert_to_triple_seifert_rot_rnd),
+ ANIM_PH_NAME(single_seifert_to_triple_seifert_rot_rnd)
+};
+
+ANIMS_M_DEF(single_seifert_to_triple_seifert) = {
+ ANIM_PS_REF(single_seifert_to_triple_seifert_rot_z),
+ ANIM_PS_REF(single_seifert_to_triple_seifert_rot_rnd)
+};
+
+ANIMS_DEF(single_seifert_to_triple_seifert) = {
+ ANIMS_M_NUM(single_seifert_to_triple_seifert),
+ ANIMS_M_NAME(single_seifert_to_triple_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single Seifert
+ surface to a single Hopf torus. */
+
+/* Expand the Seifert surface on the equator, fill it to five times the
+ density, and deform it to a Hopf torus. */
+ANIM_SO_DEF(single_seifert_to_single_hopf_torus_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 61.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 62.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 63.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 64.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_hopf_torus_densify) = {
+ ANIM_SO_NUM(single_seifert_to_single_hopf_torus_densify),
+ ANIM_SO_NAME(single_seifert_to_single_hopf_torus_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_single_hopf_torus_densify) = {
+ ANIM_MO_REF(single_seifert_to_single_hopf_torus_densify)
+};
+
+ANIM_PS_DEF(single_seifert_to_single_hopf_torus_densify) = {
+ ANIM_PH_NUM(single_seifert_to_single_hopf_torus_densify),
+ ANIM_PH_NAME(single_seifert_to_single_hopf_torus_densify)
+};
+
+ANIMS_M_DEF(single_seifert_to_single_hopf_torus) = {
+ ANIM_PS_REF(single_seifert_to_single_hopf_torus_densify)
+};
+
+ANIMS_DEF(single_seifert_to_single_hopf_torus) = {
+ ANIMS_M_NUM(single_seifert_to_single_hopf_torus),
+ ANIMS_M_NAME(single_seifert_to_single_hopf_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a Seifert surface
+ to a single single Hopf spiral. */
+
+/* Decrease the winding number of a Hopf spiral to one and collapse its
+ height to a Seifert surface on the equator, and decrease the point
+ density by a factor of three. */
+ANIM_SO_DEF(single_seifert_to_single_hopf_spiral_wind_hor) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 1.0f, 2.0f, EASING_CUBIC, /* r */
+ M_PI_F/24.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 1.0f, 2.0f, EASING_CUBIC, /* r */
+ M_PI_F/24.0f, -35.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 1.0f, 2.0f, EASING_CUBIC, /* r */
+ M_PI_F/24.0f, -34.0f*M_PI_F/36.0f , EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_hopf_spiral_wind_hor) = {
+ ANIM_SO_NUM(single_seifert_to_single_hopf_spiral_wind_hor),
+ ANIM_SO_NAME(single_seifert_to_single_hopf_spiral_wind_hor),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_single_hopf_spiral_wind_hor) = {
+ ANIM_MO_REF(single_seifert_to_single_hopf_spiral_wind_hor)
+};
+
+ANIM_PS_DEF(single_seifert_to_single_hopf_spiral_wind_hor) = {
+ ANIM_PH_NUM(single_seifert_to_single_hopf_spiral_wind_hor),
+ ANIM_PH_NAME(single_seifert_to_single_hopf_spiral_wind_hor)
+};
+
+/* Increase the point density of a Seifert surface on the equator by a
+ factor of three and rotate it to a vertical position, then wind the
+ Seifert surface to a Hopf spiral. */
+
+/* Phase 1: Increase the density of the Seifert surface and rotate it to a
+ vertical position. */
+ANIM_SO_DEF(single_seifert_to_single_dense_seifert_vert) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -2.0f*M_PI_F, -M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -2.0f*M_PI_F, -35.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -2.0f*M_PI_F, -34.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_seifert_to_single_dense_seifert_vert) = {
+ ANIM_SO_NUM(single_seifert_to_single_dense_seifert_vert),
+ ANIM_SO_NAME(single_seifert_to_single_dense_seifert_vert),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 120 /* num_steps */
+};
+
+/* Phase 2: Wind the Hopf spiral and expand it. */
+ANIM_SO_DEF(single_dense_seifert_to_single_hopf_spiral_wind) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 0.0f, 2.0f, EASING_CUBIC, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_seifert_to_single_hopf_spiral_wind) = {
+ ANIM_SO_NUM(single_dense_seifert_to_single_hopf_spiral_wind),
+ ANIM_SO_NAME(single_dense_seifert_to_single_hopf_spiral_wind),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_seifert_to_single_hopf_spiral_wind_vert) = {
+ ANIM_MO_REF(single_seifert_to_single_dense_seifert_vert),
+ ANIM_MO_REF(single_dense_seifert_to_single_hopf_spiral_wind)
+};
+
+ANIM_PS_DEF(single_seifert_to_single_hopf_spiral_wind_vert) = {
+ ANIM_PH_NUM(single_seifert_to_single_hopf_spiral_wind_vert),
+ ANIM_PH_NAME(single_seifert_to_single_hopf_spiral_wind_vert)
+};
+
+ANIMS_M_DEF(single_seifert_to_single_hopf_spiral) = {
+ ANIM_PS_REF(single_seifert_to_single_hopf_spiral_wind_hor),
+ ANIM_PS_REF(single_seifert_to_single_hopf_spiral_wind_vert)
+};
+
+ANIMS_DEF(single_seifert_to_single_hopf_spiral) = {
+ ANIMS_M_NUM(single_seifert_to_single_hopf_spiral),
+ ANIMS_M_NAME(single_seifert_to_single_hopf_spiral)
+};
+
+
+
+/*****************************************************************************
+ * The set of all transformations from a triple Seifert surface.
+ *****************************************************************************/
+
+/* The set of possible animations for the transition from a triple Seifert
+ surface to a single point. */
+
+/* Rotate two Seifert surfaces at latitude ±45° around the z axis, shrink
+ them to a single point, and move them to the equator. Shrink the Seifert
+ surface on the equator to a single point without rotating it. */
+ANIM_SO_DEF(triple_seifert_to_single_point_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/24.0f, 0.0f, EASING_CUBIC, /* offset */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/24.0f, 0.0f, EASING_CUBIC, /* offset */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_point_rot_z) = {
+ ANIM_SO_NUM(triple_seifert_to_single_point_rot_z),
+ ANIM_SO_NAME(triple_seifert_to_single_point_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_single_point_rot_z) = {
+ ANIM_MO_REF(triple_seifert_to_single_point_rot_z)
+};
+
+ANIM_PS_DEF(triple_seifert_to_single_point_rot_z) = {
+ ANIM_PH_NUM(triple_seifert_to_single_point_rot_z),
+ ANIM_PH_NAME(triple_seifert_to_single_point_rot_z)
+};
+
+/* Rotate the three Seifert surfaces around the z axis in opposite
+ directions, move the Seifert surfaces at latitudes ±45° to latitudes
+ ±70°, and increase their point density by a factor of two, then move them
+ successively to the south pole while continuing to rotate, creating a
+ single point at the south pole, then move the point on the south pole to
+ its canonical position on the equator along a Hopf spiral. */
+
+/* Phase 1: Rotate the three Seifert surfaces for one revolution, move the
+ Seifert surfaces at latitudes ±45° to latitudes ±70°, and increase the
+ point density by a factor of two. */
+ANIM_SO_DEF(triple_seifert_to_single_point_rot_z_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_ACCEL /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_point_rot_z_densify) = {
+ ANIM_SO_NUM(triple_seifert_to_single_point_rot_z_densify),
+ ANIM_SO_NAME(triple_seifert_to_single_point_rot_z_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 2: Rotate the three Seifert surfaces for one revolution. */
+ANIM_SO_DEF(triple_seifert_to_single_point_rot_z_linear) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 8.0f*M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_point_rot_z_linear) = {
+ ANIM_SO_NUM(triple_seifert_to_single_point_rot_z_linear),
+ ANIM_SO_NAME(triple_seifert_to_single_point_rot_z_linear),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 3: Move the Seifert surface at latitude -70° to the south pole. */
+ANIM_SO_DEF(triple_seifert_to_single_point_rot_z_move_south_1) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, M_PI_F, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_point_rot_z_move_south_1) = {
+ ANIM_SO_NUM(triple_seifert_to_single_point_rot_z_move_south_1),
+ ANIM_SO_NAME(triple_seifert_to_single_point_rot_z_move_south_1),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 4: Move the Seifert surface at latitude 0° to the south pole. */
+ANIM_SO_DEF(triple_seifert_to_single_point_rot_z_move_south_2) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_LIN /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F, M_PI_F, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_point_rot_z_move_south_2) = {
+ ANIM_SO_NUM(triple_seifert_to_single_point_rot_z_move_south_2),
+ ANIM_SO_NAME(triple_seifert_to_single_point_rot_z_move_south_2),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 5: Move the Seifert surface at latitude 70° to the south pole. */
+ANIM_SO_DEF(triple_seifert_to_single_point_rot_z_move_south_3) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_DECEL /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F, M_PI_F, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_point_rot_z_move_south_3) = {
+ ANIM_SO_NUM(triple_seifert_to_single_point_rot_z_move_south_3),
+ ANIM_SO_NAME(triple_seifert_to_single_point_rot_z_move_south_3),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 6: Move the single point at the south pole to the equator along a
+ Hopf Spiral. */
+ANIM_SO_DEF(triple_seifert_to_single_point_rot_z_move_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 0.0f, 0.0f, EASING_NONE, /* sector */
+ 0, 1, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_point_rot_z_move_spiral) = {
+ ANIM_SO_NUM(triple_seifert_to_single_point_rot_z_move_spiral),
+ ANIM_SO_NAME(triple_seifert_to_single_point_rot_z_move_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 120 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_single_point_move_south) = {
+ ANIM_MO_REF(triple_seifert_to_single_point_rot_z_densify),
+ ANIM_MO_REF(triple_seifert_to_single_point_rot_z_linear),
+ ANIM_MO_REF(triple_seifert_to_single_point_rot_z_move_south_1),
+ ANIM_MO_REF(triple_seifert_to_single_point_rot_z_move_south_2),
+ ANIM_MO_REF(triple_seifert_to_single_point_rot_z_move_south_3),
+ ANIM_MO_REF(triple_seifert_to_single_point_rot_z_move_spiral)
+};
+
+ANIM_PS_DEF(triple_seifert_to_single_point_move_south) = {
+ ANIM_PH_NUM(triple_seifert_to_single_point_move_south),
+ ANIM_PH_NAME(triple_seifert_to_single_point_move_south)
+};
+
+ANIMS_M_DEF(triple_seifert_to_single_point) = {
+ ANIM_PS_REF(triple_seifert_to_single_point_rot_z),
+ ANIM_PS_REF(triple_seifert_to_single_point_move_south)
+};
+
+ANIMS_DEF(triple_seifert_to_single_point) = {
+ ANIMS_M_NUM(triple_seifert_to_single_point),
+ ANIMS_M_NAME(triple_seifert_to_single_point)
+};
+
+
+
+/* The set of possible animations for the transition from a triple Seifert
+ surface to a single torus. */
+
+/* Rotate three Seifert surfaces around the z axis, reduce their sectors
+ from 180° to 120°, move the two Seifert surfaces at latitude ±45° to the
+ equator, and merge all three Seifert surfaces into a dense torus at the
+ equator, then reduce the point density by a factor of three. */
+
+/* Phase 1: Rotate three Seifert surfaces around the z axis, reduce their
+ sectors from 180° to 120°, move the two Seifert surfaces at latitude
+ ±45° to the equator, and merge all three Seifert surfaces into a dense
+ torus at the equator. */
+ANIM_SO_DEF(triple_seifert_to_single_dense_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 4.0f*M_PI_F/3.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_dense_torus_rot_z) = {
+ ANIM_SO_NUM(triple_seifert_to_single_dense_torus_rot_z),
+ ANIM_SO_NAME(triple_seifert_to_single_dense_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 2: Reduce the point density by a factor of three. Already defined
+ above. */
+
+ANIM_PH_DEF(triple_seifert_to_single_torus_rot_z) = {
+ ANIM_MO_REF(triple_seifert_to_single_dense_torus_rot_z),
+ ANIM_MO_REF(single_dense_torus_to_single_torus_loosen_three)
+};
+
+ANIM_PS_DEF(triple_seifert_to_single_torus_rot_z) = {
+ ANIM_PH_NUM(triple_seifert_to_single_torus_rot_z),
+ ANIM_PH_NAME(triple_seifert_to_single_torus_rot_z)
+};
+
+/* Rotate three Seifert surfaces around a random axis, expand their sectors
+ from 180° to 360°, and move the Seifert surfaces at latitude ±45° to the
+ equator. */
+ANIM_SO_DEF(triple_seifert_to_single_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_torus_rot_rnd) = {
+ ANIM_SO_NUM(triple_seifert_to_single_torus_rot_rnd),
+ ANIM_SO_NAME(triple_seifert_to_single_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_single_torus_rot_rnd) = {
+ ANIM_MO_REF(triple_seifert_to_single_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_seifert_to_single_torus_rot_rnd) = {
+ ANIM_PH_NUM(triple_seifert_to_single_torus_rot_rnd),
+ ANIM_PH_NAME(triple_seifert_to_single_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_seifert_to_single_torus) = {
+ ANIM_PS_REF(triple_seifert_to_single_torus_rot_z),
+ ANIM_PS_REF(triple_seifert_to_single_torus_rot_rnd)
+};
+
+ANIMS_DEF(triple_seifert_to_single_torus) = {
+ ANIMS_M_NUM(triple_seifert_to_single_torus),
+ ANIMS_M_NAME(triple_seifert_to_single_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a triple Seifert
+ surface to a double torus. */
+
+/* Rotate the three Seifert surfaces around the z axis, split the Seifert
+ surface at the equator into two Seifert surfaces, increase their sector
+ from 180° to 360°, and move them to latitude ±45°. */
+ANIM_SO_DEF(triple_seifert_to_double_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_double_torus_rot_z) = {
+ ANIM_SO_NUM(triple_seifert_to_double_torus_rot_z),
+ ANIM_SO_NAME(triple_seifert_to_double_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_double_torus_rot_z) = {
+ ANIM_MO_REF(triple_seifert_to_double_torus_rot_z)
+};
+
+ANIM_PS_DEF(triple_seifert_to_double_torus_rot_z) = {
+ ANIM_PH_NUM(triple_seifert_to_double_torus_rot_z),
+ ANIM_PH_NAME(triple_seifert_to_double_torus_rot_z)
+};
+
+/* Split the Seifert surface at the equator into two Seifert surfaces,
+ increase their sector from 180° to 360°, move them to latitude ±45°, and
+ rotate all Seifert surfaces around a random axis with a certain
+ probability. */
+ANIM_SO_DEF(triple_seifert_to_double_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_double_torus_rot_rnd) = {
+ ANIM_SO_NUM(triple_seifert_to_double_torus_rot_rnd),
+ ANIM_SO_NAME(triple_seifert_to_double_torus_rot_rnd),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_double_torus_rot_rnd) = {
+ ANIM_MO_REF(triple_seifert_to_double_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_seifert_to_double_torus_rot_rnd) = {
+ ANIM_PH_NUM(triple_seifert_to_double_torus_rot_rnd),
+ ANIM_PH_NAME(triple_seifert_to_double_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_seifert_to_double_torus) = {
+ ANIM_PS_REF(triple_seifert_to_double_torus_rot_z),
+ ANIM_PS_REF(triple_seifert_to_double_torus_rot_rnd)
+};
+
+ANIMS_DEF(triple_seifert_to_double_torus) = {
+ ANIMS_M_NUM(triple_seifert_to_double_torus),
+ ANIMS_M_NAME(triple_seifert_to_double_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a triple Seifert
+ surface to a triple torus. */
+
+/* Rotate the three Seifert surfaces around the z axis and increase their
+ sector from 180° to 360°. */
+ANIM_SO_DEF(triple_seifert_to_triple_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 3.0f*M_PI_F/2.0f,
+ EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 3.0f*M_PI_F/2.0f,
+ EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_triple_torus_rot_z) = {
+ ANIM_SO_NUM(triple_seifert_to_triple_torus_rot_z),
+ ANIM_SO_NAME(triple_seifert_to_triple_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_triple_torus_rot_z) = {
+ ANIM_MO_REF(triple_seifert_to_triple_torus_rot_z)
+};
+
+ANIM_PS_DEF(triple_seifert_to_triple_torus_rot_z) = {
+ ANIM_PH_NUM(triple_seifert_to_triple_torus_rot_z),
+ ANIM_PH_NAME(triple_seifert_to_triple_torus_rot_z)
+};
+
+/* Increase the sector of the Seifert surfaces from 180° to 360° and rotate
+ them around a random axis with a certain probability. */
+ANIM_SO_DEF(triple_seifert_to_triple_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE , /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_triple_torus_rot_rnd) = {
+ ANIM_SO_NUM(triple_seifert_to_triple_torus_rot_rnd),
+ ANIM_SO_NAME(triple_seifert_to_triple_torus_rot_rnd),
+ 0.75f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_triple_torus_rot_rnd) = {
+ ANIM_MO_REF(triple_seifert_to_triple_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_seifert_to_triple_torus_rot_rnd) = {
+ ANIM_PH_NUM(triple_seifert_to_triple_torus_rot_rnd),
+ ANIM_PH_NAME(triple_seifert_to_triple_torus_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_seifert_to_triple_torus) = {
+ ANIM_PS_REF(triple_seifert_to_triple_torus_rot_z),
+ ANIM_PS_REF(triple_seifert_to_triple_torus_rot_rnd)
+};
+
+ANIMS_DEF(triple_seifert_to_triple_torus) = {
+ ANIMS_M_NUM(triple_seifert_to_triple_torus),
+ ANIMS_M_NAME(triple_seifert_to_triple_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a triple Seifert
+ surface to a single Seifert surface. */
+
+/* Shrink the three Seifert surfaces from 180° sectors to 60° sectors, join
+ them at the equator, and reduce the point density by a factor of three. */
+
+/* Phase 1: Shrink the three Seifert surfaces from 180° sectors to 60°
+ sectors and join them at the equator. */
+ANIM_SO_DEF(triple_seifert_to_single_dense_seifert_three_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, -25.0f*M_PI_F/72.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, -49.0f*M_PI_F/72.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, -73.0f*M_PI_F/72.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_dense_seifert_three_rot_z) = {
+ ANIM_SO_NUM(triple_seifert_to_single_dense_seifert_three_rot_z),
+ ANIM_SO_NAME(triple_seifert_to_single_dense_seifert_three_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 2: Decrease the point density by a factor of three. Already
+ defined above. */
+
+ANIM_PH_DEF(triple_seifert_to_single_seifert_rot_z) = {
+ ANIM_MO_REF(triple_seifert_to_single_dense_seifert_three_rot_z),
+ ANIM_MO_REF(single_dense_seifert_three_to_single_seifert_rot_z)
+};
+
+ANIM_PS_DEF(triple_seifert_to_single_seifert_rot_z) = {
+ ANIM_PH_NUM(triple_seifert_to_single_seifert_rot_z),
+ ANIM_PH_NAME(triple_seifert_to_single_seifert_rot_z)
+};
+
+/* Reduce the point density of the three Seifert surfaces by a factor of
+ three, interleave them at the equator, and rotate around a random axis. */
+ANIM_SO_DEF(triple_seifert_to_single_seifert_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, 25.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 25.0f*M_PI_F/24.0f, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 26.0f*M_PI_F/24.0f, 26.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 8, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_seifert_rot_rnd) = {
+ ANIM_SO_NUM(triple_seifert_to_single_seifert_rot_rnd),
+ ANIM_SO_NAME(triple_seifert_to_single_seifert_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_single_seifert_rot_rnd) = {
+ ANIM_MO_REF(triple_seifert_to_single_seifert_rot_rnd)
+};
+
+ANIM_PS_DEF(triple_seifert_to_single_seifert_rot_rnd) = {
+ ANIM_PH_NUM(triple_seifert_to_single_seifert_rot_rnd),
+ ANIM_PH_NAME(triple_seifert_to_single_seifert_rot_rnd)
+};
+
+ANIMS_M_DEF(triple_seifert_to_single_seifert) = {
+ ANIM_PS_REF(triple_seifert_to_single_seifert_rot_z),
+ ANIM_PS_REF(triple_seifert_to_single_seifert_rot_rnd)
+};
+
+ANIMS_DEF(triple_seifert_to_single_seifert) = {
+ ANIMS_M_NUM(triple_seifert_to_single_seifert),
+ ANIMS_M_NAME(triple_seifert_to_single_seifert)
+};
+
+
+
+/* The set of possible animations for a triple Seifert surface. */
+
+/* Rotate three Seifert surfaces at latitudes ±45° and 0° around the x axis
+ of the total space while rotating them around the z axis in different
+ directions. */
+ANIM_SO_DEF(triple_seifert_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_rot_x) = {
+ ANIM_SO_NUM(triple_seifert_rot_x),
+ ANIM_SO_NAME(triple_seifert_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_rot_x) = {
+ ANIM_MO_REF(triple_seifert_rot_x)
+};
+
+ANIM_PS_DEF(triple_seifert_rot_x) = {
+ ANIM_PH_NUM(triple_seifert_rot_x),
+ ANIM_PH_NAME(triple_seifert_rot_x)
+};
+
+/* Rotate the three Seifert surfaces to a a vertical orientation, yielding
+ half of a parabolic ring cyclide and two interlocking Seifert surfaces,
+ and increase their density by a factor of two. Then, rotate them around
+ the x axis of the total space. Next, move the two Seifert surfaces not
+ on the equator between latitudes 1° and ±89°. Finally, rotate the
+ Seifert surfaces back to 0° and ±45° and decrease their density by a
+ factor of two. */
+
+/* Phase 1: Rotate the Seifert surfaces by 90° to a vertical position,
+ increase their point density by a factor of two, and rotate them around
+ the x axis of the total space by 90°. */
+ANIM_SO_DEF(triple_seifert_to_vertical_triple_seifert_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_vertical_triple_seifert_densify) = {
+ ANIM_SO_NUM(triple_seifert_to_vertical_triple_seifert_densify),
+ ANIM_SO_NAME(triple_seifert_to_vertical_triple_seifert_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { -1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC, /* rot_space */
+ 120 /* num_steps */
+};
+
+/* Phase 2: Rotate the three Seifert surfaces by 360° around the x axis of
+ the total space. */
+ANIM_SO_DEF(vertical_triple_seifert_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(vertical_triple_seifert_rot_x) = {
+ ANIM_SO_NUM(vertical_triple_seifert_rot_x),
+ ANIM_SO_NAME(vertical_triple_seifert_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 5.0f*M_PI_F/2.0f,
+ EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 3: Move the two Seifert surfaces at latitides ±45° between
+ latitudes 1° and ±89°. */
+ANIM_SO_DEF(vertical_triple_seifert_move_z) = {
+ {
+ GEN_TORUS, /* generator */
+ 179.0f*M_PI_F/180.0f, 91.0f*M_PI_F/180.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 1.0f*M_PI_F/180.0f, 89.0f*M_PI_F/180.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(vertical_triple_seifert_move_z) = {
+ ANIM_SO_NUM(vertical_triple_seifert_move_z),
+ ANIM_SO_NAME(vertical_triple_seifert_move_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, M_PI_F/2.0f,
+ EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 4: Rotate the Seifert surfaces back to 0° and ±45° and decrease
+ their density by a factor of two. */
+ANIM_SO_DEF(vertical_triple_seifert_to_triple_seifert_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC /* rot_axis */
+ },
+};
+
+ANIM_MO_DEF(vertical_triple_seifert_to_triple_seifert_loosen) = {
+ ANIM_SO_NUM(vertical_triple_seifert_to_triple_seifert_loosen),
+ ANIM_SO_NAME(vertical_triple_seifert_to_triple_seifert_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { -1.0f, 0.0f, 0.0f }, M_PI_F/2.0f, 0.0f, EASING_CUBIC, /* rot_space */
+ 120 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_three_sphere) = {
+ ANIM_MO_REF(triple_seifert_to_vertical_triple_seifert_densify),
+ ANIM_MO_REF(vertical_triple_seifert_rot_x),
+ ANIM_MO_REF(vertical_triple_seifert_move_z),
+ ANIM_MO_REF(vertical_triple_seifert_to_triple_seifert_loosen)
+};
+
+ANIM_PS_DEF(triple_seifert_three_sphere) = {
+ ANIM_PH_NUM(triple_seifert_three_sphere),
+ ANIM_PH_NAME(triple_seifert_three_sphere)
+};
+
+/* Move the two Seifert surfaces at latitudes ±45° to latitude ±70° and
+ increase the point density of all three Seifert surfaces by a factor of
+ two, rotate the Seifert surfaces around the z axis and, with a certain
+ probability, around a random axis, move the two Seifert surfaces at
+ latitude ±70° back to ±45°, and decrease the point density of all three
+ Seifert surfaces by a factor of two. */
+
+/* Phase 1: Move two Seifert surfaces at latitude ±45° to latitude ±70° and
+ increase the point density of all three Seifert surfaces by a factor of
+ two. */
+ANIM_SO_DEF(triple_seifert_rot_rnd_move_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 8.0f*M_PI_F/9.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_rot_rnd_move_densify) = {
+ ANIM_SO_NUM(triple_seifert_rot_rnd_move_densify),
+ ANIM_SO_NAME(triple_seifert_rot_rnd_move_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Rotate the two tori by 360° around the z axis and, with a
+ certain probability, around a random axis. */
+ANIM_SO_DEF(triple_seifert_rot_rnd_rot) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 8.0f*M_PI_F/9.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 48, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_rot_rnd_rot) = {
+ ANIM_SO_NUM(triple_seifert_rot_rnd_rot),
+ ANIM_SO_NAME(triple_seifert_rot_rnd_rot),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 3: Move the two Seifert surfaces at latitude ±70° to latitude ±45°
+ and decrease the point density of all three Seifert surfaces by a factor
+ of two. */
+ANIM_SO_DEF(triple_seifert_rot_rnd_move_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/9.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 8.0f*M_PI_F/9.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 49.0f*M_PI_F/48.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_rot_rnd_move_loosen) = {
+ ANIM_SO_NUM(triple_seifert_rot_rnd_move_loosen),
+ ANIM_SO_NAME(triple_seifert_rot_rnd_move_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_rot_rnd) = {
+ ANIM_MO_REF(triple_seifert_rot_rnd_move_densify),
+ ANIM_MO_REF(triple_seifert_rot_rnd_rot),
+ ANIM_MO_REF(triple_seifert_rot_rnd_move_loosen)
+};
+
+ANIM_PS_DEF(triple_seifert_rot_rnd) = {
+ ANIM_PH_NUM(triple_seifert_rot_rnd),
+ ANIM_PH_NAME(triple_seifert_rot_rnd)
+};
+
+/* Move the two Seifert surfaces at latitude ±45° to latitude ±5°, to
+ latitude ±85° and back to latitude ±45° while rotating all three Seifert
+ surfaces around the z axis and, with a certain probability, around a
+ random axis. */
+ANIM_SO_DEF(triple_seifert_move) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/18.0f, 8.0f*M_PI_F/18.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 17.0f*M_PI_F/18.0f, 10.0f*M_PI_F/18.0f, EASING_SIN, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, -1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_move) = {
+ ANIM_SO_NUM(triple_seifert_move),
+ ANIM_SO_NAME(triple_seifert_move),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_move) = {
+ ANIM_MO_REF(triple_seifert_move)
+};
+
+ANIM_PS_DEF(triple_seifert_move) = {
+ ANIM_PH_NUM(triple_seifert_move),
+ ANIM_PH_NAME(triple_seifert_move)
+};
+
+ANIMS_M_DEF(triple_seifert) = {
+ ANIM_PS_REF(triple_seifert_rot_x),
+ ANIM_PS_REF(triple_seifert_three_sphere),
+ ANIM_PS_REF(triple_seifert_rot_rnd),
+ ANIM_PS_REF(triple_seifert_move)
+};
+
+ANIMS_DEF(triple_seifert) = {
+ ANIMS_M_NUM(triple_seifert),
+ ANIMS_M_NAME(triple_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a triple Seifert
+ surface to a single Hopf torus. */
+
+/* Expand the sectors of the three Seifert surfaces from 180° to 360°,
+ transform them to a Hopf torus on the equator while increasing the point
+ density of the two Seifert surfaces at latitude ±45° by a factor of two. */
+ANIM_SO_DEF(triple_seifert_to_single_hopf_torus_densify) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 58.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 59.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, M_PI_F/2.0f, EASING_CUBIC, /* p */
+ 0.0f, M_PI_F/8.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, 2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ M_PI_F, 2.0f*M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_hopf_torus_densify) = {
+ ANIM_SO_NUM(triple_seifert_to_single_hopf_torus_densify),
+ ANIM_SO_NAME(triple_seifert_to_single_hopf_torus_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_single_hopf_torus_densify) = {
+ ANIM_MO_REF(triple_seifert_to_single_hopf_torus_densify)
+};
+
+ANIM_PS_DEF(triple_seifert_to_single_hopf_torus_densify) = {
+ ANIM_PH_NUM(triple_seifert_to_single_hopf_torus_densify),
+ ANIM_PH_NAME(triple_seifert_to_single_hopf_torus_densify)
+};
+
+ANIMS_M_DEF(triple_seifert_to_single_hopf_torus) = {
+ ANIM_PS_REF(triple_seifert_to_single_hopf_torus_densify)
+};
+
+ANIMS_DEF(triple_seifert_to_single_hopf_torus) = {
+ ANIMS_M_NUM(triple_seifert_to_single_hopf_torus),
+ ANIMS_M_NAME(triple_seifert_to_single_hopf_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a triple Seifert
+ surface to a single Hopf spiral. */
+
+/* Deform the three Seifert surfaces to three Hopf spirals and merge them. */
+ANIM_SO_DEF(triple_seifert_to_single_hopf_spiral_merge) = {
+ {
+ GEN_SPIRAL, /* generator */
+ -M_PI_F/4.0f, 0.0f, EASING_CUBIC, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, -M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F/2.0f, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ M_PI_F/48.0f, -M_PI_F/3.0f, EASING_CUBIC, /* offset */
+ M_PI_F/2.0f, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ M_PI_F/4.0f, 0.0f, EASING_CUBIC, /* p */
+ 0.0f, 1.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -47.0f*M_PI_F/48.0f, M_PI_F/3.0f, EASING_CUBIC, /* offset */
+ M_PI_F/2.0f, 2.0f*M_PI_F/3.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(triple_seifert_to_single_hopf_spiral_merge) = {
+ ANIM_SO_NUM(triple_seifert_to_single_hopf_spiral_merge),
+ ANIM_SO_NAME(triple_seifert_to_single_hopf_spiral_merge),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(triple_seifert_to_single_hopf_spiral_merge) = {
+ ANIM_MO_REF(triple_seifert_to_single_hopf_spiral_merge)
+};
+
+ANIM_PS_DEF(triple_seifert_to_single_hopf_spiral_merge) = {
+ ANIM_PH_NUM(triple_seifert_to_single_hopf_spiral_merge),
+ ANIM_PH_NAME(triple_seifert_to_single_hopf_spiral_merge)
+};
+
+ANIMS_M_DEF(triple_seifert_to_single_hopf_spiral) = {
+ ANIM_PS_REF(triple_seifert_to_single_hopf_spiral_merge)
+};
+
+ANIMS_DEF(triple_seifert_to_single_hopf_spiral) = {
+ ANIMS_M_NUM(triple_seifert_to_single_hopf_spiral),
+ ANIMS_M_NAME(triple_seifert_to_single_hopf_spiral)
+};
+
+
+
+/*****************************************************************************
+ * The set of all transformations from a single Hopf torus.
+ *****************************************************************************/
+
+/* The set of possible animations for the transition from a single Hopf
+ torus to a single point. */
+
+/* Shrink a Hopf torus on the equator to a point on the equator and rotate
+ around a random axis. */
+ANIM_SO_DEF(single_hopf_torus_to_single_point_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_to_single_point_rot_rnd) = {
+ ANIM_SO_NUM(single_hopf_torus_to_single_point_rot_rnd),
+ ANIM_SO_NAME(single_hopf_torus_to_single_point_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_to_single_point_rot_rnd) = {
+ ANIM_MO_REF(single_hopf_torus_to_single_point_rot_rnd)
+};
+
+ANIM_PS_DEF(single_hopf_torus_to_single_point_rot_rnd) = {
+ ANIM_PH_NUM(single_hopf_torus_to_single_point_rot_rnd),
+ ANIM_PH_NAME(single_hopf_torus_to_single_point_rot_rnd)
+};
+
+/* Reduce the Hopf torus to a Hopf torus of one fifth the point density and
+ then deform it to a point on the equator. */
+
+/* Phase 1: Decrease the density of the Hopf torus. */
+ANIM_SO_DEF(single_dense_hopf_torus_to_single_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -2.0f*M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 2.0f*M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_hopf_torus_to_single_hopf_torus) = {
+ ANIM_SO_NUM(single_dense_hopf_torus_to_single_hopf_torus),
+ ANIM_SO_NAME(single_dense_hopf_torus_to_single_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Shrink the loose Hopf torus to a point on the equator. */
+ANIM_SO_DEF(single_loose_hopf_torus_to_single_point) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_loose_hopf_torus_to_single_point) = {
+ ANIM_SO_NUM(single_loose_hopf_torus_to_single_point),
+ ANIM_SO_NAME(single_loose_hopf_torus_to_single_point),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_to_single_point_loosen) = {
+ ANIM_MO_REF(single_dense_hopf_torus_to_single_hopf_torus),
+ ANIM_MO_REF(single_loose_hopf_torus_to_single_point)
+};
+
+ANIM_PS_DEF(single_hopf_torus_to_single_point_loosen) = {
+ ANIM_PH_NUM(single_hopf_torus_to_single_point_loosen),
+ ANIM_PH_NAME(single_hopf_torus_to_single_point_loosen)
+};
+
+ANIMS_M_DEF(single_hopf_torus_to_single_point) = {
+ ANIM_PS_REF(single_hopf_torus_to_single_point_rot_rnd),
+ ANIM_PS_REF(single_hopf_torus_to_single_point_loosen)
+};
+
+ANIMS_DEF(single_hopf_torus_to_single_point) = {
+ ANIMS_M_NUM(single_hopf_torus_to_single_point),
+ ANIMS_M_NAME(single_hopf_torus_to_single_point)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ torus to a single torus. */
+
+/* Reduce the Hopf torus to a Hopf torus of one fifth the point density and
+ then deform it to a torus on the equator. */
+
+/* Phase 1: Decrease the density of the Hopf torus. Already defined above. */
+
+/* Phase 2: Deform the Hopf torus to a torus on the equator. */
+ANIM_SO_DEF(single_hopf_torus_to_single_loose_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_to_single_loose_torus) = {
+ ANIM_SO_NUM(single_hopf_torus_to_single_loose_torus),
+ ANIM_SO_NAME(single_hopf_torus_to_single_loose_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_to_single_torus_loosen) = {
+ ANIM_MO_REF(single_dense_hopf_torus_to_single_hopf_torus),
+ ANIM_MO_REF(single_hopf_torus_to_single_loose_torus)
+};
+
+ANIM_PS_DEF(single_hopf_torus_to_single_torus_loosen) = {
+ ANIM_PH_NUM(single_hopf_torus_to_single_torus_loosen),
+ ANIM_PH_NAME(single_hopf_torus_to_single_torus_loosen)
+};
+
+ANIMS_M_DEF(single_hopf_torus_to_single_torus) = {
+ ANIM_PS_REF(single_hopf_torus_to_single_torus_loosen)
+};
+
+ANIMS_DEF(single_hopf_torus_to_single_torus) = {
+ ANIMS_M_NUM(single_hopf_torus_to_single_torus),
+ ANIMS_M_NAME(single_hopf_torus_to_single_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a Hopf torus
+ to a double torus. */
+
+/* Decrease the density of the Hopf torus by a factor of five, disentangle
+ and deform the Hopf torus to two loose tori at latitude ±45°, and
+ increase the density of the two tori by a factor of two. */
+
+/* Phase 1: Decrease the density of the Hopf torus by a factor of five. */
+ANIM_SO_DEF(hopf_torus_to_loose_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -2.0f*M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 2.0f*M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(hopf_torus_to_loose_hopf_torus) = {
+ ANIM_SO_NUM(hopf_torus_to_loose_hopf_torus),
+ ANIM_SO_NAME(hopf_torus_to_loose_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Deform the interleaved Hopf torus to two loose tori. */
+ANIM_SO_DEF(loose_hopf_torus_to_loose_double_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/12.0f, M_PI_F/12.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(loose_hopf_torus_to_loose_double_torus) = {
+ ANIM_SO_NUM(loose_hopf_torus_to_loose_double_torus),
+ ANIM_SO_NAME(loose_hopf_torus_to_loose_double_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+/* Phase 3: Increase the density of the tori at ±45°. */
+ANIM_SO_DEF(loose_double_torus_to_double_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/4.0f, M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F/12.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/12.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ 3.0f*M_PI_F/4.0f, 3.0f*M_PI_F/4.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/12.0f, M_PI_F/12.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 12, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(loose_double_torus_to_double_torus) = {
+ ANIM_SO_NUM(loose_double_torus_to_double_torus),
+ ANIM_SO_NAME(loose_double_torus_to_double_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_to_double_torus_loosen) = {
+ ANIM_MO_REF(hopf_torus_to_loose_hopf_torus),
+ ANIM_MO_REF(loose_hopf_torus_to_loose_double_torus),
+ ANIM_MO_REF(loose_double_torus_to_double_torus)
+};
+
+ANIM_PS_DEF(single_hopf_torus_to_double_torus_loosen) = {
+ ANIM_PH_NUM(single_hopf_torus_to_double_torus_loosen),
+ ANIM_PH_NAME(single_hopf_torus_to_double_torus_loosen)
+};
+
+ANIMS_M_DEF(single_hopf_torus_to_double_torus) = {
+ ANIM_PS_REF(single_hopf_torus_to_double_torus_loosen)
+};
+
+ANIMS_DEF(single_hopf_torus_to_double_torus) = {
+ ANIMS_M_NUM(single_hopf_torus_to_double_torus),
+ ANIMS_M_NAME(single_hopf_torus_to_double_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ torus to a triple torus. */
+
+/* Transform Hopf torus on the equator into three tori while decreasing the
+ point density of the two tori at latitude ±45° by a factor of two. */
+ANIM_SO_DEF(single_hopf_torus_to_triple_torus_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -2.0f*M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -1.0f*M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 1.0f*M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 2.0f*M_PI_F/60.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_to_triple_torus_loosen) = {
+ ANIM_SO_NUM(single_hopf_torus_to_triple_torus_loosen),
+ ANIM_SO_NAME(single_hopf_torus_to_triple_torus_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 120 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_to_triple_torus_loosen) = {
+ ANIM_MO_REF(single_hopf_torus_to_triple_torus_loosen)
+};
+
+ANIM_PS_DEF(single_hopf_torus_to_triple_torus_loosen) = {
+ ANIM_PH_NUM(single_hopf_torus_to_triple_torus_loosen),
+ ANIM_PH_NAME(single_hopf_torus_to_triple_torus_loosen)
+};
+
+ANIMS_M_DEF(single_hopf_torus_to_triple_torus) = {
+ ANIM_PS_REF(single_hopf_torus_to_triple_torus_loosen)
+};
+
+ANIMS_DEF(single_hopf_torus_to_triple_torus) = {
+ ANIMS_M_NUM(single_hopf_torus_to_triple_torus),
+ ANIMS_M_NAME(single_hopf_torus_to_triple_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ torus to a single Seifert surface. */
+
+/* Shrink the Hopf torus, decrease its density to one fifth, and deform it
+ to a Seifert surface on the equator. */
+ANIM_SO_DEF(single_hopf_torus_to_single_seifert_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F, M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 61.0f*M_PI_F/60.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 62.0f*M_PI_F/60.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 63.0f*M_PI_F/60.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 64.0f*M_PI_F/60.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_to_single_seifert_loosen) = {
+ ANIM_SO_NUM(single_hopf_torus_to_single_seifert_loosen),
+ ANIM_SO_NAME(single_hopf_torus_to_single_seifert_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_to_single_seifert_loosen) = {
+ ANIM_MO_REF(single_hopf_torus_to_single_seifert_loosen)
+};
+
+ANIM_PS_DEF(single_hopf_torus_to_single_seifert_loosen) = {
+ ANIM_PH_NUM(single_hopf_torus_to_single_seifert_loosen),
+ ANIM_PH_NAME(single_hopf_torus_to_single_seifert_loosen)
+};
+
+ANIMS_M_DEF(single_hopf_torus_to_single_seifert) = {
+ ANIM_PS_REF(single_hopf_torus_to_single_seifert_loosen)
+};
+
+ANIMS_DEF(single_hopf_torus_to_single_seifert) = {
+ ANIMS_M_NUM(single_hopf_torus_to_single_seifert),
+ ANIMS_M_NAME(single_hopf_torus_to_single_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ torus to a triple Seifert surface. */
+
+/* Transform the Hopf torus on the equator into three Seifert surfaces while
+ decreasing the point density of the two Seifert surfaces at latitude ±45°
+ by a factor of two. */
+ANIM_SO_DEF(single_hopf_torus_to_triple_seifert_loosen) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 58.0f*M_PI_F/60.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 59.0f*M_PI_F/60.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/2.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/60.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, 3.0f*M_PI_F/4.0f, EASING_CUBIC, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 2.0f*M_PI_F/60.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_to_triple_seifert_loosen) = {
+ ANIM_SO_NUM(single_hopf_torus_to_triple_seifert_loosen),
+ ANIM_SO_NAME(single_hopf_torus_to_triple_seifert_loosen),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_to_triple_seifert_loosen) = {
+ ANIM_MO_REF(single_hopf_torus_to_triple_seifert_loosen)
+};
+
+ANIM_PS_DEF(single_hopf_torus_to_triple_seifert_loosen) = {
+ ANIM_PH_NUM(single_hopf_torus_to_triple_seifert_loosen),
+ ANIM_PH_NAME(single_hopf_torus_to_triple_seifert_loosen)
+};
+
+ANIMS_M_DEF(single_hopf_torus_to_triple_seifert) = {
+ ANIM_PS_REF(single_hopf_torus_to_triple_seifert_loosen)
+};
+
+ANIMS_DEF(single_hopf_torus_to_triple_seifert) = {
+ ANIMS_M_NUM(single_hopf_torus_to_triple_seifert),
+ ANIMS_M_NAME(single_hopf_torus_to_triple_seifert)
+};
+
+
+
+/* The set of possible animations for a single Hopf torus. */
+
+/* Rotate a Hopf torus around the x axis of the total space. */
+ANIM_SO_DEF(single_hopf_torus_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_rot_x) = {
+ ANIM_SO_NUM(single_hopf_torus_rot_x),
+ ANIM_SO_NAME(single_hopf_torus_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_rot_x) = {
+ ANIM_MO_REF(single_hopf_torus_rot_x)
+};
+
+ANIM_PS_DEF(single_hopf_torus_rot_x) = {
+ ANIM_PH_NUM(single_hopf_torus_rot_x),
+ ANIM_PH_NAME(single_hopf_torus_rot_x)
+};
+
+/* Rotate a Hopf torus around the z axis. */
+ANIM_SO_DEF(single_hopf_torus_rot_z) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_rot_z) = {
+ ANIM_SO_NUM(single_hopf_torus_rot_z),
+ ANIM_SO_NAME(single_hopf_torus_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_rot_z) = {
+ ANIM_MO_REF(single_hopf_torus_rot_z)
+};
+
+ANIM_PS_DEF(single_hopf_torus_rot_z) = {
+ ANIM_PH_NUM(single_hopf_torus_rot_z),
+ ANIM_PH_NAME(single_hopf_torus_rot_z)
+};
+
+/* Rotate a Hopf torus around a random axis. */
+ANIM_SO_DEF(single_hopf_torus_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_rot_rnd) = {
+ ANIM_SO_NUM(single_hopf_torus_rot_rnd),
+ ANIM_SO_NAME(single_hopf_torus_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_rot_rnd) = {
+ ANIM_MO_REF(single_hopf_torus_rot_rnd)
+};
+
+ANIM_PS_DEF(single_hopf_torus_rot_rnd) = {
+ ANIM_PH_NUM(single_hopf_torus_rot_rnd),
+ ANIM_PH_NAME(single_hopf_torus_rot_rnd)
+};
+
+/* Wave a Hopf torus around a random axis, i.e., animate the parameter q,
+ and rotate it with a certain probability around a random axis. */
+ANIM_SO_DEF(single_hopf_torus_wave) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ -M_PI_F/8.0f, M_PI_F/8.0f, EASING_COS, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_wave) = {
+ ANIM_SO_NUM(single_hopf_torus_wave),
+ ANIM_SO_NAME(single_hopf_torus_wave),
+ 0.25f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_wave) = {
+ ANIM_MO_REF(single_hopf_torus_wave)
+};
+
+ANIM_PS_DEF(single_hopf_torus_wave) = {
+ ANIM_PH_NUM(single_hopf_torus_wave),
+ ANIM_PH_NAME(single_hopf_torus_wave)
+};
+
+/* Move a Hopf torus on the equator up and down to latitude ±60° while
+ rotating it around the z axis and, with a certain probability, around
+ a random axis. */
+ANIM_SO_DEF(single_hopf_torus_move) = {
+ {
+ GEN_TORUS, /* generator */
+ 1.0f*M_PI_F/6.0f, 5.0f*M_PI_F/6.0f, EASING_SIN, /* p */
+ M_PI_F/24.0f, M_PI_F/8.0f, EASING_COS, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_move) = {
+ ANIM_SO_NUM(single_hopf_torus_move),
+ ANIM_SO_NAME(single_hopf_torus_move),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_torus_move) = {
+ ANIM_MO_REF(single_hopf_torus_move)
+};
+
+ANIM_PS_DEF(single_hopf_torus_move) = {
+ ANIM_PH_NUM(single_hopf_torus_move),
+ ANIM_PH_NAME(single_hopf_torus_move)
+};
+
+/* Deform the Hopf torus into a Hopf flower, rotate the Hopf flower around
+ the x axis of the total space, and deform the Hopf flower back to the
+ Hopf torus. */
+
+/* Phase 1: Deform the Hopf torus into a Hopf flower. */
+ANIM_SO_DEF(single_hopf_torus_to_single_hopf_flower) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ 0.0f, -0.5f, EASING_CUBIC, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_to_single_hopf_flower) = {
+ ANIM_SO_NUM(single_hopf_torus_to_single_hopf_flower),
+ ANIM_SO_NAME(single_hopf_torus_to_single_hopf_flower),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+/* Phase 2: Rotate the Hopf flower around the x axis of the total space. */
+ANIM_SO_DEF(single_hopf_flower_rot_x) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ -0.5f, -0.5f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_flower_rot_x) = {
+ ANIM_SO_NUM(single_hopf_flower_rot_x),
+ ANIM_SO_NAME(single_hopf_flower_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 3: Deform the Hopf flower into a Hopf torus. */
+ANIM_SO_DEF(single_hopf_flower_to_single_hopf_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ -0.5f, 0.0f, EASING_CUBIC, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_flower_to_single_hopf_torus) = {
+ ANIM_SO_NUM(single_hopf_flower_to_single_hopf_torus),
+ ANIM_SO_NAME(single_hopf_flower_to_single_hopf_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_flower_rot_x) = {
+ ANIM_MO_REF(single_hopf_torus_to_single_hopf_flower),
+ ANIM_MO_REF(single_hopf_flower_rot_x),
+ ANIM_MO_REF(single_hopf_flower_to_single_hopf_torus)
+};
+
+ANIM_PS_DEF(single_hopf_flower_rot_x) = {
+ ANIM_PH_NUM(single_hopf_flower_rot_x),
+ ANIM_PH_NAME(single_hopf_flower_rot_x)
+};
+
+/* Deform the Hopf torus into a Hopf flower, rotate the Hopf flower around
+ the a random axis, and deform the Hopf flower back to the Hopf torus. */
+
+/* Phase 1: Deform the Hopf torus into a Hopf flower. Already defined
+ above. */
+
+/* Phase 2: Rotate a Hopf flower around a random axis. */
+ANIM_SO_DEF(single_hopf_flower_rot_rnd) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, M_PI_F/8.0f, EASING_NONE, /* q */
+ -0.5f, -0.5f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_flower_rot_rnd) = {
+ ANIM_SO_NUM(single_hopf_flower_rot_rnd),
+ ANIM_SO_NAME(single_hopf_flower_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+/* Phase 3: Deform the Hopf flower into a Hopf torus. Already defined
+ above. */
+
+ANIM_PH_DEF(single_hopf_flower_rot_rnd) = {
+ ANIM_MO_REF(single_hopf_torus_to_single_hopf_flower),
+ ANIM_MO_REF(single_hopf_flower_rot_rnd),
+ ANIM_MO_REF(single_hopf_flower_to_single_hopf_torus)
+};
+
+ANIM_PS_DEF(single_hopf_flower_rot_rnd) = {
+ ANIM_PH_NUM(single_hopf_flower_rot_rnd),
+ ANIM_PH_NAME(single_hopf_flower_rot_rnd)
+};
+
+/* Deform the Hopf torus into a Hopf flower, move the Hopf flower on the
+ equator up and down to latitude ±60° while rotating it around the z axis
+ and, with a certain probability, around a random axis, and deform the
+ Hopf flower back to the Hopf torus. */
+
+/* Phase 1: Deform the Hopf torus into a Hopf flower. Already defined
+ above. */
+
+/* Phase 2: Move the Hopf flower on the equator up and down to latitude ±60°
+ while rotating it around the z axis and, with a certain probability,
+ around a random axis. */
+ANIM_SO_DEF(single_hopf_flower_move) = {
+ {
+ GEN_TORUS, /* generator */
+ 1.0f*M_PI_F/6.0f, 5.0f*M_PI_F/6.0f, EASING_SIN, /* p */
+ M_PI_F/16.0f, M_PI_F/8.0f, EASING_COS, /* q */
+ -0.5f, -0.5f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 120, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_flower_move) = {
+ ANIM_SO_NUM(single_hopf_flower_move),
+ ANIM_SO_NAME(single_hopf_flower_move),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+/* Phase 3: Deform the Hopf flower into a Hopf torus. Already defined
+ above. */
+
+ANIM_PH_DEF(single_hopf_flower_move) = {
+ ANIM_MO_REF(single_hopf_torus_to_single_hopf_flower),
+ ANIM_MO_REF(single_hopf_flower_move),
+ ANIM_MO_REF(single_hopf_flower_to_single_hopf_torus)
+};
+
+ANIM_PS_DEF(single_hopf_flower_move) = {
+ ANIM_PH_NUM(single_hopf_flower_move),
+ ANIM_PH_NAME(single_hopf_flower_move)
+};
+
+ANIMS_M_DEF(single_hopf_torus) = {
+ ANIM_PS_REF(single_hopf_torus_rot_x),
+ ANIM_PS_REF(single_hopf_torus_rot_z),
+ ANIM_PS_REF(single_hopf_torus_rot_rnd),
+ ANIM_PS_REF(single_hopf_torus_move),
+ ANIM_PS_REF(single_hopf_torus_wave),
+ ANIM_PS_REF(single_hopf_flower_rot_x),
+ ANIM_PS_REF(single_hopf_flower_rot_rnd),
+ ANIM_PS_REF(single_hopf_flower_move)
+};
+
+ANIMS_DEF(single_hopf_torus) = {
+ ANIMS_M_NUM(single_hopf_torus),
+ ANIMS_M_NAME(single_hopf_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ torus to a single Hopf spiral. */
+
+/* Reduce the Hopf torus to a Hopf torus of three fifths the point density
+ and deform it to a point on the equator, then deform the torus to a Hopf
+ spiral. */
+
+/* Phase 1: Decrease the density of the Hopf torus and deform it to a torus
+ on the equator. */
+ANIM_SO_DEF(single_hopf_torus_to_single_mid_density_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 1.0f*M_PI_F/60.0f, 1.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 2.0f*M_PI_F/60.0f, 1.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 3.0f*M_PI_F/60.0f, 2.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ M_PI_F/8.0f, 0.0f, EASING_CUBIC, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 4.0f*M_PI_F/60.0f, 2.0f*M_PI_F/36.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 4, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_torus_to_single_mid_density_torus) = {
+ ANIM_SO_NUM(single_hopf_torus_to_single_mid_density_torus),
+ ANIM_SO_NAME(single_hopf_torus_to_single_mid_density_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 90 /* num_steps */
+};
+
+/* Phase 2: Wind the collapsed spiral and expand it. Already defined above. */
+
+ANIM_PH_DEF(single_hopf_torus_to_single_hopf_spiral_wind) = {
+ ANIM_MO_REF(single_hopf_torus_to_single_mid_density_torus),
+ ANIM_MO_REF(single_dense_unwound_torus_to_single_hopf_spiral)
+};
+
+ANIM_PS_DEF(single_hopf_torus_to_single_hopf_spiral_wind) = {
+ ANIM_PH_NUM(single_hopf_torus_to_single_hopf_spiral_wind),
+ ANIM_PH_NAME(single_hopf_torus_to_single_hopf_spiral_wind)
+};
+
+ANIMS_M_DEF(single_hopf_torus_to_single_hopf_spiral) = {
+ ANIM_PS_REF(single_hopf_torus_to_single_hopf_spiral_wind)
+};
+
+ANIMS_DEF(single_hopf_torus_to_single_hopf_spiral) = {
+ ANIMS_M_NUM(single_hopf_torus_to_single_hopf_spiral),
+ ANIMS_M_NAME(single_hopf_torus_to_single_hopf_spiral)
+};
+
+
+
+/*****************************************************************************
+ * The set of all transformations from a single Hopf spiral.
+ *****************************************************************************/
+
+/* The set of possible animations for the transition from a single Hopf
+ spiral to a single point. */
+
+/* Shrink a Hopf spiral to a point on the equator and rotate around a random
+ axis with a certain probability. */
+ANIM_SO_DEF(single_hopf_spiral_to_single_point_shrink_sector) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 0.0f, EASING_CUBIC, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_single_point_shrink_sector) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_single_point_shrink_sector),
+ ANIM_SO_NAME(single_hopf_spiral_to_single_point_shrink_sector),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_single_point_shrink_sector) = {
+ ANIM_MO_REF(single_hopf_spiral_to_single_point_shrink_sector)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_single_point_shrink_sector) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_single_point_shrink_sector),
+ ANIM_PH_NAME(single_hopf_spiral_to_single_point_shrink_sector)
+};
+
+ANIMS_M_DEF(single_hopf_spiral_to_single_point) = {
+ ANIM_PS_REF(single_hopf_spiral_to_single_point_shrink_sector)
+};
+
+ANIMS_DEF(single_hopf_spiral_to_single_point) = {
+ ANIMS_M_NUM(single_hopf_spiral_to_single_point),
+ ANIMS_M_NAME(single_hopf_spiral_to_single_point)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ spiral to a single torus. */
+
+/* Decrease the winding number of a Hopf spiral to one and collapse its
+ height to a torus on the equator, and then decrease the point density
+ of the torus by a factor of three. */
+
+/* Phase 1: Unwind the spiral and collapse it to a dense torus. */
+ANIM_SO_DEF(single_hopf_spiral_to_single_dense_torus_unwind) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_single_dense_torus_unwind) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_single_dense_torus_unwind),
+ ANIM_SO_NAME(single_hopf_spiral_to_single_dense_torus_unwind),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+/* Phase 2: Decrease the density of the torus by a factor of three. */
+ANIM_SO_DEF(single_dense_unwound_torus_to_single_torus) = {
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ 0.0f, 0.0f, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_TORUS, /* generator */
+ M_PI_F/2.0f, M_PI_F/2.0f, EASING_NONE, /* p */
+ 0.0f, 0.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F/36.0f, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_unwound_torus_to_single_torus) = {
+ ANIM_SO_NUM(single_dense_unwound_torus_to_single_torus),
+ ANIM_SO_NAME(single_dense_unwound_torus_to_single_torus),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_single_torus_unwind) = {
+ ANIM_MO_REF(single_hopf_spiral_to_single_dense_torus_unwind),
+ ANIM_MO_REF(single_dense_unwound_torus_to_single_torus)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_single_torus_unwind) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_single_torus_unwind),
+ ANIM_PH_NAME(single_hopf_spiral_to_single_torus_unwind)
+};
+
+/* Increase the winding number of a Hopf spiral to three and collapse its
+ height to a torus on the equator. */
+ANIM_SO_DEF(single_hopf_spiral_to_single_torus_wind) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 3.0f, EASING_CUBIC, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_single_torus_wind) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_single_torus_wind),
+ ANIM_SO_NAME(single_hopf_spiral_to_single_torus_wind),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_single_torus_wind) = {
+ ANIM_MO_REF(single_hopf_spiral_to_single_torus_wind)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_single_torus_wind) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_single_torus_wind),
+ ANIM_PH_NAME(single_hopf_spiral_to_single_torus_wind)
+};
+
+ANIMS_M_DEF(single_hopf_spiral_to_single_torus) = {
+ ANIM_PS_REF(single_hopf_spiral_to_single_torus_unwind),
+ ANIM_PS_REF(single_hopf_spiral_to_single_torus_wind)
+};
+
+ANIMS_DEF(single_hopf_spiral_to_single_torus) = {
+ ANIMS_M_NUM(single_hopf_spiral_to_single_torus),
+ ANIMS_M_NAME(single_hopf_spiral_to_single_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ spiral to a double torus. */
+
+/* Decrease the point density of a Hopf spiral by a factor of two thirds,
+ split the spiral into two parts, and deform the parts to two tori at
+ latitiude ±45°. */
+
+/* Phase 1: Decrease the density of the Hopf spiral by a factor of two
+ thirds. */
+ANIM_SO_DEF(single_hopf_spiral_to_single_loose_hopf_spiral) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -35.0f*M_PI_F/36.0f, -23.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -34.0f*M_PI_F/36.0f, -23.0f*M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_single_loose_hopf_spiral) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_single_loose_hopf_spiral),
+ ANIM_SO_NAME(single_hopf_spiral_to_single_loose_hopf_spiral),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 60 /* num_steps */
+};
+
+/* Phase 2: Split the spiral into two parts and deform it to two tori at
+ latitiude ±45°. */
+ANIM_SO_DEF(single_loose_hopf_spiral_to_double_torus_split) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, -M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, -2.0f*M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ 0.0f, M_PI_F, EASING_CUBIC, /* offset */
+ M_PI_F, M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_loose_hopf_spiral_to_double_torus_split) = {
+ ANIM_SO_NUM(single_loose_hopf_spiral_to_double_torus_split),
+ ANIM_SO_NAME(single_loose_hopf_spiral_to_double_torus_split),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_double_torus_split) = {
+ ANIM_MO_REF(single_hopf_spiral_to_single_loose_hopf_spiral),
+ ANIM_MO_REF(single_loose_hopf_spiral_to_double_torus_split)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_double_torus_split) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_double_torus_split),
+ ANIM_PH_NAME(single_hopf_spiral_to_double_torus_split)
+};
+
+ANIMS_M_DEF(single_hopf_spiral_to_double_torus) = {
+ ANIM_PS_REF(single_hopf_spiral_to_double_torus_split)
+};
+
+ANIMS_DEF(single_hopf_spiral_to_double_torus) = {
+ ANIMS_M_NUM(single_hopf_spiral_to_double_torus),
+ ANIMS_M_NAME(single_hopf_spiral_to_double_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ spiral to a triple torus. */
+
+/* Split the Hopf spiral into three parts and Deform them to three tori. */
+ANIM_SO_DEF(single_hopf_spiral_to_triple_torus_split) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, -M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, -2.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F/3.0f, -M_PI_F/2.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_triple_torus_split) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_triple_torus_split),
+ ANIM_SO_NAME(single_hopf_spiral_to_triple_torus_split),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_triple_torus_split) = {
+ ANIM_MO_REF(single_hopf_spiral_to_triple_torus_split)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_triple_torus_split) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_triple_torus_split),
+ ANIM_PH_NAME(single_hopf_spiral_to_triple_torus_split)
+};
+
+ANIMS_M_DEF(single_hopf_spiral_to_triple_torus) = {
+ ANIM_PS_REF(single_hopf_spiral_to_triple_torus_split)
+};
+
+ANIMS_DEF(single_hopf_spiral_to_triple_torus) = {
+ ANIMS_M_NUM(single_hopf_spiral_to_triple_torus),
+ ANIMS_M_NAME(single_hopf_spiral_to_triple_torus)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ spiral to a single Seifert surface. */
+
+/* Decrease the winding number of a Hopf spiral to one and collapse its
+ height to a Seifert surface on the equator, and decrease the point
+ density by a factor of three. */
+ANIM_SO_DEF(single_hopf_spiral_to_single_seifert_unwind_hor) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -M_PI_F, M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -35.0f*M_PI_F/36.0f, M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -34.0f*M_PI_F/36.0f, M_PI_F/24.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, M_PI_F, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_single_seifert_unwind_hor) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_single_seifert_unwind_hor),
+ ANIM_SO_NAME(single_hopf_spiral_to_single_seifert_unwind_hor),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_single_seifert_unwind_hor) = {
+ ANIM_MO_REF(single_hopf_spiral_to_single_seifert_unwind_hor)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_single_seifert_unwind_hor) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_single_seifert_unwind_hor),
+ ANIM_PH_NAME(single_hopf_spiral_to_single_seifert_unwind_hor)
+};
+
+/* Decrease the winding number of a Hopf spiral to zero to create a vertical
+ Seifert surface, then decrease the point density by a factor of three and
+ rotate it to the equator. */
+
+/* Phase 1: Unwind the spiral and collapse it to a dense vertical Seifert
+ surface. */
+ANIM_SO_DEF(single_hopf_spiral_to_single_dense_seifert_unwind) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 0.0f, EASING_CUBIC, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_single_dense_seifert_unwind) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_single_dense_seifert_unwind),
+ ANIM_SO_NAME(single_hopf_spiral_to_single_dense_seifert_unwind),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 180 /* num_steps */
+};
+
+/* Phase 2: Decrease the density of the vertical Seifert surface and rotate
+ it to its canonical position. */
+ANIM_SO_DEF(single_dense_seifert_vert_to_single_seifert) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -M_PI_F, -2.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -35.0f*M_PI_F/36.0f, -2.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 0.0f, 0.0f, EASING_NONE, /* r */
+ -34.0f*M_PI_F/36.0f, -2.0f*M_PI_F, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, M_PI_F/2.0f, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_dense_seifert_vert_to_single_seifert) = {
+ ANIM_SO_NUM(single_dense_seifert_vert_to_single_seifert),
+ ANIM_SO_NAME(single_dense_seifert_vert_to_single_seifert),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 120 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_single_seifert_unwind_vert) = {
+ ANIM_MO_REF(single_hopf_spiral_to_single_dense_seifert_unwind),
+ ANIM_MO_REF(single_dense_seifert_vert_to_single_seifert)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_single_seifert_unwind_vert) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_single_seifert_unwind_vert),
+ ANIM_PH_NAME(single_hopf_spiral_to_single_seifert_unwind_vert)
+};
+
+ANIMS_M_DEF(single_hopf_spiral_to_single_seifert) = {
+ ANIM_PS_REF(single_hopf_spiral_to_single_seifert_unwind_hor),
+ ANIM_PS_REF(single_hopf_spiral_to_single_seifert_unwind_vert)
+};
+
+ANIMS_DEF(single_hopf_spiral_to_single_seifert) = {
+ ANIMS_M_NUM(single_hopf_spiral_to_single_seifert),
+ ANIMS_M_NAME(single_hopf_spiral_to_single_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ spiral to a triple Seifert surface. */
+
+/* Deform the three Seifert surfaces to three Hopf spirals and merge them. */
+ANIM_SO_DEF(single_hopf_spiral_to_triple_seifert_split) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, -M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, -95.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F/2.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F/3.0f, M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F/2.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, M_PI_F/4.0f, EASING_CUBIC, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ M_PI_F/3.0f, 49.0f*M_PI_F/48.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F/3.0f, M_PI_F/2.0f, EASING_CUBIC, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_triple_seifert_split) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_triple_seifert_split),
+ ANIM_SO_NAME(single_hopf_spiral_to_triple_seifert_split),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_to_triple_seifert_split) = {
+ ANIM_MO_REF(single_hopf_spiral_to_triple_seifert_split)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_triple_seifert_split) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_triple_seifert_split),
+ ANIM_PH_NAME(single_hopf_spiral_to_triple_seifert_split)
+};
+
+ANIMS_M_DEF(single_hopf_spiral_to_triple_seifert) = {
+ ANIM_PS_REF(single_hopf_spiral_to_triple_seifert_split)
+};
+
+ANIMS_DEF(single_hopf_spiral_to_triple_seifert) = {
+ ANIMS_M_NUM(single_hopf_spiral_to_triple_seifert),
+ ANIMS_M_NAME(single_hopf_spiral_to_triple_seifert)
+};
+
+
+
+/* The set of possible animations for the transition from a single Hopf
+ spiral to a single Hopf torus. */
+
+/* Decrease the winding number of a Hopf spiral to one and collapse its
+ height to a torus on the equator, and increase the point density
+ of the torus by a factor of five thirds, then deform the torus to a
+ Hopf torus. */
+
+/* Phase 1: Unwind the spiral and collapse it to a dense torus. */
+ANIM_SO_DEF(single_hopf_spiral_to_single_torus_densify) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -M_PI_F, 0.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -35.0f*M_PI_F/36.0f, 1.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -35.0f*M_PI_F/36.0f, 2.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -34.0f*M_PI_F/36.0f, 3.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ },
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 0.0f, EASING_CUBIC, /* q */
+ 2.0f, 1.0f, EASING_CUBIC, /* r */
+ -34.0f*M_PI_F/36.0f, 4.0f*M_PI_F/60.0f, EASING_CUBIC, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 24, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_to_single_torus_densify) = {
+ ANIM_SO_NUM(single_hopf_spiral_to_single_torus_densify),
+ ANIM_SO_NAME(single_hopf_spiral_to_single_torus_densify),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 240 /* num_steps */
+};
+
+/* Phase 2: Deform the torus on the equator to a Hopf torus. Already
+ defined above. */
+
+ANIM_PH_DEF(single_hopf_spiral_to_single_hopf_torus_unwind) = {
+ ANIM_MO_REF(single_hopf_spiral_to_single_torus_densify),
+ ANIM_MO_REF(single_dense_torus_to_single_hopf_torus)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_to_single_hopf_torus_unwind) = {
+ ANIM_PH_NUM(single_hopf_spiral_to_single_hopf_torus_unwind),
+ ANIM_PH_NAME(single_hopf_spiral_to_single_hopf_torus_unwind)
+};
+
+ANIMS_M_DEF(single_hopf_spiral_to_single_hopf_torus) = {
+ ANIM_PS_REF(single_hopf_spiral_to_single_hopf_torus_unwind)
+};
+
+ANIMS_DEF(single_hopf_spiral_to_single_hopf_torus) = {
+ ANIMS_M_NUM(single_hopf_spiral_to_single_hopf_torus),
+ ANIMS_M_NAME(single_hopf_spiral_to_single_hopf_torus)
+};
+
+
+
+/* The set of possible animations for a single Hopf spiral. */
+
+/* Rotate a Hopf torus around the x axis of the total space. */
+ANIM_SO_DEF(single_hopf_spiral_rot_x) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_rot_x) = {
+ ANIM_SO_NUM(single_hopf_spiral_rot_x),
+ ANIM_SO_NAME(single_hopf_spiral_rot_x),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 1.0f, 0.0f, 0.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_rot_x) = {
+ ANIM_MO_REF(single_hopf_spiral_rot_x)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_rot_x) = {
+ ANIM_PH_NUM(single_hopf_spiral_rot_x),
+ ANIM_PH_NAME(single_hopf_spiral_rot_x)
+};
+
+/* Rotate a Hopf torus around the z axis. */
+ANIM_SO_DEF(single_hopf_spiral_rot_z) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 1.0f }, 0.0f, 2.0f*M_PI_F, EASING_CUBIC /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_rot_z) = {
+ ANIM_SO_NUM(single_hopf_spiral_rot_z),
+ ANIM_SO_NAME(single_hopf_spiral_rot_z),
+ 0.0f, EASING_NONE, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_rot_z) = {
+ ANIM_MO_REF(single_hopf_spiral_rot_z)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_rot_z) = {
+ ANIM_PH_NUM(single_hopf_spiral_rot_z),
+ ANIM_PH_NAME(single_hopf_spiral_rot_z)
+};
+
+/* Rotate a Hopf spiral around a random axis. */
+ANIM_SO_DEF(single_hopf_spiral_rot_rnd) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 2.0f, 2.0f, EASING_NONE, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_rot_rnd) = {
+ ANIM_SO_NUM(single_hopf_spiral_rot_rnd),
+ ANIM_SO_NAME(single_hopf_spiral_rot_rnd),
+ 1.0f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_rot_rnd) = {
+ ANIM_MO_REF(single_hopf_spiral_rot_rnd)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_rot_rnd) = {
+ ANIM_PH_NUM(single_hopf_spiral_rot_rnd),
+ ANIM_PH_NAME(single_hopf_spiral_rot_rnd)
+};
+
+/* Animate the winding number of a Hopf spiral and rotate it with a certain
+ probability around a random axis. */
+ANIM_SO_DEF(single_hopf_spiral_winding) = {
+ {
+ GEN_SPIRAL, /* generator */
+ 0.0f, 0.0f, EASING_NONE, /* p */
+ 1.0f, 1.0f, EASING_NONE, /* q */
+ 1.0f, 3.0f, EASING_SIN, /* r */
+ -M_PI_F, -M_PI_F, EASING_NONE, /* offset */
+ 2.0f*M_PI_F, 2.0f*M_PI_F, EASING_NONE, /* sector */
+ 0, 72, /* n, num */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE /* rot_axis */
+ }
+};
+
+ANIM_MO_DEF(single_hopf_spiral_winding) = {
+ ANIM_SO_NUM(single_hopf_spiral_winding),
+ ANIM_SO_NAME(single_hopf_spiral_winding),
+ 0.5f, EASING_CUBIC, /* rotate_prob */
+ { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f, EASING_NONE, /* rot_space */
+ 360 /* num_steps */
+};
+
+ANIM_PH_DEF(single_hopf_spiral_winding) = {
+ ANIM_MO_REF(single_hopf_spiral_winding)
+};
+
+ANIM_PS_DEF(single_hopf_spiral_winding) = {
+ ANIM_PH_NUM(single_hopf_spiral_winding),
+ ANIM_PH_NAME(single_hopf_spiral_winding)
+};
+
+ANIMS_M_DEF(single_hopf_spiral) = {
+ ANIM_PS_REF(single_hopf_spiral_rot_x),
+ ANIM_PS_REF(single_hopf_spiral_rot_z),
+ ANIM_PS_REF(single_hopf_spiral_rot_rnd),
+ ANIM_PS_REF(single_hopf_spiral_winding)
+};
+
+ANIMS_DEF(single_hopf_spiral) = {
+ ANIMS_M_NUM(single_hopf_spiral),
+ ANIMS_M_NAME(single_hopf_spiral)
+};
+
+
+
+/*****************************************************************************
+ * The set of all possible animations.
+ *****************************************************************************/
+animations *hopf_animations[NUM_ANIM_STATES][NUM_ANIM_STATES] = {
+ {
+ ANIMS_REF(single_point),
+ ANIMS_REF(single_point_to_single_torus),
+ ANIMS_REF(single_point_to_double_torus),
+ ANIMS_REF(single_point_to_triple_torus),
+ ANIMS_REF(single_point_to_single_seifert),
+ ANIMS_REF(single_point_to_triple_seifert),
+ ANIMS_REF(single_point_to_single_hopf_torus),
+ ANIMS_REF(single_point_to_single_hopf_spiral)
+ },
+ {
+ ANIMS_REF(single_torus_to_single_point),
+ ANIMS_REF(single_torus),
+ ANIMS_REF(single_torus_to_double_torus),
+ ANIMS_REF(single_torus_to_triple_torus),
+ ANIMS_REF(single_torus_to_single_seifert),
+ ANIMS_REF(single_torus_to_triple_seifert),
+ ANIMS_REF(single_torus_to_single_hopf_torus),
+ ANIMS_REF(single_torus_to_single_hopf_spiral)
+ },
+ {
+ ANIMS_REF(double_torus_to_single_point),
+ ANIMS_REF(double_torus_to_single_torus),
+ ANIMS_REF(double_torus),
+ ANIMS_REF(double_torus_to_triple_torus),
+ ANIMS_REF(double_torus_to_single_seifert),
+ ANIMS_REF(double_torus_to_triple_seifert),
+ ANIMS_REF(double_torus_to_single_hopf_torus),
+ ANIMS_REF(double_torus_to_single_hopf_spiral)
+ },
+ {
+ ANIMS_REF(triple_torus_to_single_point),
+ ANIMS_REF(triple_torus_to_single_torus),
+ ANIMS_REF(triple_torus_to_double_torus),
+ ANIMS_REF(triple_torus),
+ ANIMS_REF(triple_torus_to_single_seifert),
+ ANIMS_REF(triple_torus_to_triple_seifert),
+ ANIMS_REF(triple_torus_to_single_hopf_torus),
+ ANIMS_REF(triple_torus_to_single_hopf_spiral)
+ },
+ {
+ ANIMS_REF(single_seifert_to_single_point),
+ ANIMS_REF(single_seifert_to_single_torus),
+ ANIMS_REF(single_seifert_to_double_torus),
+ ANIMS_REF(single_seifert_to_triple_torus),
+ ANIMS_REF(single_seifert),
+ ANIMS_REF(single_seifert_to_triple_seifert),
+ ANIMS_REF(single_seifert_to_single_hopf_torus),
+ ANIMS_REF(single_seifert_to_single_hopf_spiral)
+ },
+ {
+ ANIMS_REF(triple_seifert_to_single_point),
+ ANIMS_REF(triple_seifert_to_single_torus),
+ ANIMS_REF(triple_seifert_to_double_torus),
+ ANIMS_REF(triple_seifert_to_triple_torus),
+ ANIMS_REF(triple_seifert_to_single_seifert),
+ ANIMS_REF(triple_seifert),
+ ANIMS_REF(triple_seifert_to_single_hopf_torus),
+ ANIMS_REF(triple_seifert_to_single_hopf_spiral),
+ },
+ {
+ ANIMS_REF(single_hopf_torus_to_single_point),
+ ANIMS_REF(single_hopf_torus_to_single_torus),
+ ANIMS_REF(single_hopf_torus_to_double_torus),
+ ANIMS_REF(single_hopf_torus_to_triple_torus),
+ ANIMS_REF(single_hopf_torus_to_single_seifert),
+ ANIMS_REF(single_hopf_torus_to_triple_seifert),
+ ANIMS_REF(single_hopf_torus),
+ ANIMS_REF(single_hopf_torus_to_single_hopf_spiral)
+ },
+ {
+ ANIMS_REF(single_hopf_spiral_to_single_point),
+ ANIMS_REF(single_hopf_spiral_to_single_torus),
+ ANIMS_REF(single_hopf_spiral_to_double_torus),
+ ANIMS_REF(single_hopf_spiral_to_triple_torus),
+ ANIMS_REF(single_hopf_spiral_to_single_seifert),
+ ANIMS_REF(single_hopf_spiral_to_triple_seifert),
+ ANIMS_REF(single_hopf_spiral_to_single_hopf_torus),
+ ANIMS_REF(single_hopf_spiral)
+ }
+};
+
+#endif /* USE_GL */
--- /dev/null
+/* hopfanimations.h --- Definition of the animations used in the Hopf
+ fibration. */
+/* Copyright (c) 2025 Carsten Steger <carsten@mirsanmir.org>.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * REVISION HISTORY:
+ * C. Steger - 26/02/06: Initial version
+ */
+
+#ifndef __HOPFANIMATIONS_H__
+#define __HOPFANIMATIONS_H__
+
+#ifdef USE_GL
+
+#include <stdbool.h>
+
+#define M_PI_F 3.1415926535898f
+
+#define ANIM_SINGLE_POINT 0
+#define ANIM_SINGLE_TORUS 1
+#define ANIM_DOUBLE_TORUS 2
+#define ANIM_TRIPLE_TORUS 3
+#define ANIM_SINGLE_SEIFERT 4
+#define ANIM_TRIPLE_SEIFERT 5
+#define ANIM_SINGLE_HOPF_TORUS 6
+#define ANIM_SINGLE_HOPF_SPIRAL 7
+#define NUM_ANIM_STATES (ANIM_SINGLE_HOPF_SPIRAL+1)
+
+#define MAX_ANIM_GEOM 10
+
+#define GEN_TORUS 0
+#define GEN_SPIRAL 1
+
+#define EASING_NONE 0
+#define EASING_CUBIC 1
+#define EASING_SIN 2
+#define EASING_COS 3
+#define EASING_LIN 4
+#define EASING_ACCEL 5
+#define EASING_DECEL 6
+
+
+/* A data structure that holds the data for the animation state of a single
+ object. */
+typedef struct {
+ int generator;
+ float p;
+ float q;
+ float r;
+ float offset;
+ float sector;
+ int n;
+ int num;
+ bool rotate;
+ float quat_base[4];
+} animation_geometry;
+
+/* A data structure that holds the data for an animation using a single
+ object. */
+typedef struct {
+ int generator;
+ float p_start, p_end;
+ int easing_function_p;
+ float q_start, q_end;
+ int easing_function_q;
+ float r_start, r_end;
+ int easing_function_r;
+ float offset_start, offset_end;
+ int easing_function_offset;
+ float sector_start, sector_end;
+ int easing_function_sector;
+ int n;
+ int num;
+ float rot_axis_base[3];
+ float angle_start, angle_end;
+ int easing_function_rotate;
+} animation_single_obj;
+
+/* A data structure that holds the data for an animation using multiple
+ objects that are animated at the same time. */
+typedef struct {
+ int num;
+ animation_single_obj *anim_so;
+ float rotate_prob;
+ int easing_function_rot_rnd;
+ float rot_axis_space[3];
+ float angle_start, angle_end;
+ int easing_function_rot_space;
+ int num_steps;
+} animation_multi_obj;
+
+/* A data structure that holds the data for an animation that consists of
+ multiple phases that are animated consecutively. */
+typedef struct {
+ int num_phases;
+ animation_multi_obj **anim_mo;
+} animation_phases;
+
+/* A data structure that holds a set of related animations. */
+typedef struct {
+ int num_anim;
+ animation_phases **anim;
+} animations;
+
+
+/* The set of all possible animations. */
+extern animations *hopf_animations[NUM_ANIM_STATES][NUM_ANIM_STATES];
+
+
+#endif /* USE_GL */
+
+#endif /* __HOPFANIMATIONS_H__ */
--- /dev/null
+/* hopffibration --- Displays the Hopf fibration of the 4D hypersphere S³. */
+
+#if 0
+static const char sccsid[] = "@(#)hopffibration.c 1.1 25/02/01 xlockmore";
+#endif
+
+/* Copyright (c) 2025 Carsten Steger <carsten@mirsanmir.org>. */
+
+/*
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * REVISION HISTORY:
+ * C. Steger - 25/02/06: Initial version
+ */
+
+/*
+ * This program shows the Hopf fibration of the 4d hypersphere S³.
+ * The Hopf fibration is based on the Hopf map, a many-to-one
+ * continuous function from S³ onto the the ordinary 3d sphere S² such
+ * that each distinct point of S² is mapped from a distinct great
+ * circle S¹ of S³. Hence, the inverse image of a point on S²
+ * corresponds to a great circle S¹ on S³. The sphere S² is called the
+ * base space, each S¹ corresponding to a point on S² is called a
+ * fiber, and S³ is called the total space.
+ *
+ * The program displays the base space S² as a semi-transparent gray
+ * sphere in the bottom right corner of the display. The points on
+ * the base space are displayed as small colored spheres. The fibers
+ * in the total space are displayed in the same color as the
+ * corresponding point on the base space.
+ *
+ * The fibers in the total space are projected from 4d to 3d using
+ * stereographic projection and then compressing the infinite 3d space
+ * to a finite 3d ball to display the fibers compactly. All fibers
+ * except one fiber that passes through the north pole of S³ are thus
+ * projected to deformed circles in 3d. The program displays these
+ * deformed circles as closed tubes (topological tori). The single
+ * fiber that passes through the north pole of S³ is projected to an
+ * infinite line by the stereographic projection. This line passes
+ * through infinity in 3d and therefore topologically is a circle.
+ * Compressing this infinite line to a finite ball maps it to a
+ * straight line segment. The program displays this line segment as a
+ * cylinder. However, it should be thought of as a circle through
+ * infinity.
+ *
+ * The fibers, base space, and base points are then projected to the
+ * screen either perspectively or orthographically.
+ *
+ * The program displays various interesting configurations of base
+ * points and fibers. Look out for the following configurations:
+ *
+ * • Any two fibers form a Hopf link.
+ * • More generally, each fiber is linked with each other fiber
+ * exactly once.
+ * • Each circle on S² creates a set of fibers that forms a Clifford
+ * torus on S³ (i.e., in 4d). Clifford tori are flat (in the same
+ * sense that the surface of a cylinder is flat).
+ * • If a circle on S² is not a circle of latitude, the projection
+ * of the Clifford torus to 3d results in a (compressed) Dupin
+ * cyclide.
+ * • More generally, any closed curve on S² creates a torus-like
+ * surface on S³ that is flat. These surfaces are called Hopf
+ * tori or Bianchi-Pinkall flat tori. Look for the wave-like
+ * curve on S² to see a Hopf torus.
+ * • A circular arc on S² creates a Hopf band on S³. The Hopf
+ * band is a Seifert surface of the Hopf link that forms the
+ * boundaries of the Hopf band.
+ * • Two or more circles of latitude on S² create two or more nested
+ * Clifford tori on S³.
+ * • More generally, two or more disjoint circles on S² create two
+ * or more linked Clifford tori on S³.
+ * • A great circle through the north pole of S² creates a parabolic
+ * ring cyclide (which is compressed to lie within the ball in the
+ * 3d projection). A parabolic ring cyclide divides the entire 3d
+ * space into two congruent parts that are interlocked, i.e.,
+ * linked.
+ * • By turning a circle on S² so that it passes through the north
+ * pole of S², the projection of the corresponding Clifford torus
+ * reverses its inside and outside in 3d.
+ * • The Clifford torus corresponding to a great circle on S²
+ * divides S³ into two congruent solid tori that fill the entire
+ * S³. The two solid tori on S³ correspond to the two hemispheres
+ * into which the great circle divides S². The solid tori in S³
+ * are attached to each other at the Clifford torus. The
+ * congruence of the solid tori is visible in a particularly
+ * striking manner if the great circle that creates the Clifford
+ * torus is rotated so that it passes through the north pole of
+ * S², thereby creating a parabolic ring cyclide via the
+ * projection of the Clifford torus to 3d (see above).
+ *
+ * During the animations, two kinds of motions are used. Usually, the
+ * points on the base space are moved or rotated to particular
+ * configurations. This is apparent by the small spheres that
+ * represent the base points changing their position on the base
+ * space, which leads to a corresponding change of the configuration
+ * of the fibers. The base space itself, however, is not moved or
+ * rotated, i.e., its orientation remains fixed. Sometimes, only the
+ * projection of the fibers is rotated in 3d to show some interesting
+ * configurations more clearly, e.g., that a Hopf torus has a hole
+ * like a regular torus. In this case, the base space also maintains
+ * its orientation in space. Since a rotation in 3d does not change
+ * the configuration of the fibers, in this kind of animation, the
+ * points on the base space also remain fixed. Sometimes, both types
+ * of animations are combined, e.g., when the projection of one or
+ * more Clifford tori is rotated in 3d while the base points of the
+ * Clifford tori also rotate on the base space. In this case, the
+ * base space will only show the movement of the base points on the
+ * base space and not the 3d rotation of the projection of the fibers.
+ *
+ * To enhance the 3d depth impression, the program displays the
+ * shadows of the fibers and base points by default. This is done by
+ * way of a two-pass rendering algorithm in which the geometry is
+ * rendered twice. Depending on the speed of the GPU, displaying
+ * shadows might slow down the rendering significantly. If this is
+ * the case, the rendering of shadows can be switched off, saving one
+ * render pass and thus speeding up the rendering.
+ *
+ * Some of the animations render complex geometries with a very large
+ * number of polygons. This can cause the rendering to become slow on
+ * some types of GPU. To speed up the rendering process, the amount
+ * of details that are rendered can be controlled in three
+ * granularities (coarse, medium, and fine). Devices with relatively
+ * small screens and relatively low-powered GPUs, such as phones or
+ * tablets, should typically select coarse details. Standard GPUs
+ * should select medium details (the default). High-powered GPUs on
+ * large screens may benefit from fine details.
+ *
+ * By default, the base space and base points are displayed as
+ * described above. If desired, the display of the base space and
+ * base points can be switched off so that only the fibers are
+ * displayed.
+ *
+ * During the animation of the Hopf fibration, sometimes multiple
+ * fibers that are very close to each other are displayed. This can
+ * create disturbing aliasing artifacts that are especially noticeable
+ * when the fibers are moving or turning slowly. Therefore, by
+ * default, the rendering is performed using anti-aliasing. This
+ * typically has a negligible effect on the rendering speed. However,
+ * if shadows have already been switched off, coarse details have been
+ * selected, and the rendering is still slow, anti-aliasing also can
+ * be switched off to check whether it has a noticeable effect on the
+ * rendering speed.
+ *
+ * This program was inspired by Niles Johnson's visualization of the
+ * Hopf fibration (https://nilesjohnson.net/hopf.html).
+ */
+
+
+#define DISP_PERSPECTIVE 0
+#define DISP_ORTHOGRAPHIC 1
+
+#define DISP_DETAILS_COARSE 0
+#define DISP_DETAILS_MEDIUM 1
+#define DISP_DETAILS_FINE 2
+
+#define DEF_SHADOWS "True"
+#define DEF_BASE_SPACE "True"
+#define DEF_ANTI_ALIASING "True"
+#define DEF_DETAILS "medium"
+#define DEF_PROJECTION "perspective"
+
+#ifdef STANDALONE
+# define DEFAULTS "*delay: 20000 \n" \
+ "*showFPS: False \n" \
+ "*prefersGLSL: True \n" \
+
+# define release_hopffibration 0
+# include "xlockmore.h" /* from the xscreensaver distribution */
+#else /* !STANDALONE */
+# include "xlock.h" /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+#ifdef USE_GL
+
+#include "glsl-utils.h"
+#include "gltrackball.h"
+#include "hopfanimations.h"
+
+
+#if (defined(HAVE_COCOA) || defined(__APPLE__)) && !defined(HAVE_IPHONE)
+#ifndef GL_COMPARE_REF_TO_TEXTURE
+#define GL_COMPARE_REF_TO_TEXTURE GL_COMPARE_R_TO_TEXTURE_ARB
+#endif
+#ifndef GL_TEXTURE_COMPARE_MODE
+#define GL_TEXTURE_COMPARE_MODE GL_TEXTURE_COMPARE_MODE_ARB
+#endif
+#ifndef GL_TEXTURE_COMPARE_FUNC
+#define GL_TEXTURE_COMPARE_FUNC GL_TEXTURE_COMPARE_FUNC_ARB
+#endif
+#endif
+
+
+/* Define ROT_BASE_SPACE if the base space and the base points should be
+ rotated in 3d in the same manner as the fibers in the total space. This
+ is currently not defined since it makes the rotations of the base points
+ in 3d harder to distinguish from the animations that change the base
+ points on the base space and therefore change the fibers. */
+#undef ROT_BASE_SPACE
+
+#define LINCOOR(r,c,w) ((r)*(w)+(c))
+
+#define M_SQRT2_F 1.4142135623731f
+#define M_COS_1_F 0.9998476951564f
+
+#define MAX_CIRCLE_PNT 512
+#define MAX_CIRCLE_STACK 64
+#define TUBE_RADIUS 0.01f
+#define BASE_POINT_RADIUS 0.01f
+#define BASE_SPHERE_RADIUS 0.2f
+#define BASE_SPHERE_S 14
+
+#define NUM_TUBE_COARSE 8
+#define NUM_TUBE_MEDIUM 12
+#define NUM_TUBE_FINE 16
+
+#define BASE_SPHERE_S_COARSE 12
+#define BASE_SPHERE_S_MEDIUM 14
+#define BASE_SPHERE_S_FINE 20
+
+#define BASE_POINT_S_COARSE 2
+#define BASE_POINT_S_MEDIUM 2
+#define BASE_POINT_S_FINE 3
+
+#define MAX_CIRCLE_DIST_COARSE 0.0010f
+#define MAX_CIRCLE_DIST_MEDIUM 0.0005f
+#define MAX_CIRCLE_DIST_FINE 0.0003f
+
+#define MSAA_SAMPLES 4
+
+
+/* The number of vertices and triangles of an icosahedron. */
+#define NUM_ICOSA_VERT 12
+#define NUM_ICOSA_TRI 20
+
+/* The nontrivial coordinates of an icosahedron. */
+#define ICO_X1 0.8944271910f
+#define ICO_X2 0.2763932023f
+#define ICO_X3 0.7236067977f
+#define ICO_Y1 0.8506508084f
+#define ICO_Y2 0.5257311121f
+#define ICO_Z 0.4472135955f
+
+/* The vertices of an icosahedron. */
+static float icosa_vert[3*NUM_ICOSA_VERT] = {
+ 0.0f, 0.0f, 1.0f,
+ ICO_X1, 0.0f, ICO_Z,
+ ICO_X2, ICO_Y1, ICO_Z,
+ -ICO_X3, ICO_Y2, ICO_Z,
+ -ICO_X3, -ICO_Y2, ICO_Z,
+ ICO_X2, -ICO_Y1, ICO_Z,
+ ICO_X3, ICO_Y2, -ICO_Z,
+ -ICO_X2, ICO_Y1, -ICO_Z,
+ -ICO_X1, 0.0f, -ICO_Z,
+ -ICO_X2, -ICO_Y1, -ICO_Z,
+ ICO_X3, -ICO_Y2, -ICO_Z,
+ 0.0f, 0.0f, -1.0f
+};
+
+/* The triangles of an icosahedron. */
+static int icosa_tri[3*NUM_ICOSA_TRI] = {
+ 0, 1, 2,
+ 0, 2, 3,
+ 0, 3, 4,
+ 0, 4, 5,
+ 0, 5, 1,
+ 1, 6, 2,
+ 2, 7, 3,
+ 3, 8, 4,
+ 4, 9, 5,
+ 5, 10, 1,
+ 2, 6, 7,
+ 3, 7, 8,
+ 4, 8, 9,
+ 5, 9, 10,
+ 1, 10, 6,
+ 6, 11, 7,
+ 7, 11, 8,
+ 8, 11, 9,
+ 9, 11, 10,
+ 10, 11, 6
+};
+
+/* A data structure that stores the data of an icosahedron. vert stores the
+ num_vert vertices, while tri stores the num_tri triangles of the
+ icosahedron. */
+typedef struct {
+ float *vert;
+ int num_vert;
+ int *tri;
+ int num_tri;
+} icosahedron_data;
+
+/* A data structure that stores the data of an icosphere. vert stores the
+ num_vert vertices, while norm stores the corresponding normals at the
+ vertices. tri stores the num_tri triangles. stri stores the num_tri
+ triangles that have been sorted according to the depth (z value) of
+ their centroid after the vertices have been transformed by the rotation
+ matrix stored in smat. */
+typedef struct {
+ float *vert;
+ float *norm;
+ int num_vert;
+ int *tri;
+ int *stri;
+ int num_tri;
+ float smat[3][3];
+} icosphere_data;
+
+/* An instance of an icosahedron. */
+static icosahedron_data icosa = {
+ icosa_vert, NUM_ICOSA_VERT, icosa_tri, NUM_ICOSA_TRI
+};
+
+/* A data structure to sort triangles by the z value of their centroid. */
+typedef struct {
+ float z;
+ int idx;
+} trisort;
+
+
+/* A data structure that contains a base point of the Hopf fibration. This
+ is a point on the unit sphere S² in 3D. Therefore, the Euclidean length
+ of the vector (a,b,c) is assumed to be 1. */
+typedef struct {
+ float a, b, c;
+} hopf_base_point;
+
+/* A data structure that holds the data that is necessary to construct a
+ Hopf circle fiber that has been projected from 4D to 3D. See the
+ function init_hopf_circle. */
+typedef struct {
+ hopf_base_point base;
+ float al, be;
+ float atab;
+} hopf_circle_par;
+
+/* A data structure that holds the data for one Hopf circle fiber point in
+ 3D. p is the point, while t, n, and b define the Frenet-Serret frame of
+ the Hopf circle at p, i.e, t is the unit tangent vector, n the unit
+ normal vector, and b the unit binormal vector at p. phi is the angle
+ parameter of p on the circle. See the functions gen_hopf_circle_points,
+ hopf_circle_point, hopf_circle_tangent, and hopf_circle_binormal. */
+typedef struct {
+ float p[3];
+ float t[3];
+ float n[3];
+ float b[3];
+ float phi;
+} hopf_circle_pnt;
+
+/* A data structure that defines a segment from the starting point s to the
+ end point e on a Hopf circle. This data structure is used to construct
+ the Hopf circle projected to 3D recursively in gen_hopf_circle_points. */
+typedef struct {
+ hopf_circle_pnt s, e;
+} hopf_circle_seg;
+
+
+#ifdef USE_MODULES
+ModStruct hopffibration_description =
+{"hopffibration", "init_hopffibration", "draw_hopffibration",
+ NULL, "draw_hopffibration", "change_hopffibration",
+ "free_hopffibration", hopffibration_opts, 20000, 1, 1, 1, 1.0, 4, "",
+ "Display a Hopf fibration",
+ 0, NULL};
+#endif
+
+
+static Bool shadows;
+static Bool base_space;
+static Bool anti_aliasing;
+static char *det;
+static char *proj;
+
+
+static XrmOptionDescRec opts[] =
+{
+ {"-shadows", ".shadows", XrmoptionNoArg, "on"},
+ {"+shadows", ".shadows", XrmoptionNoArg, "off"},
+ {"-base-space", ".baseSpace", XrmoptionNoArg, "on"},
+ {"+base-space", ".baseSpace", XrmoptionNoArg, "off"},
+ {"-anti-aliasing", ".antiAliasing", XrmoptionNoArg, "on"},
+ {"+anti-aliasing", ".antiAliasing", XrmoptionNoArg, "off"},
+ {"-details", ".details", XrmoptionSepArg, 0 },
+ {"-perspective", ".projection", XrmoptionNoArg, "perspective" },
+ {"-orthographic", ".projection", XrmoptionNoArg, "orthographic" },
+};
+
+static argtype vars[] =
+{
+ { &shadows, "shadows", "Shadows", DEF_SHADOWS, t_Bool },
+ { &base_space, "baseSpace", "BaseSpace", DEF_BASE_SPACE, t_Bool },
+ { &anti_aliasing, "antiAliasing", "AntiAliasing", DEF_ANTI_ALIASING, t_Bool },
+ { &det, "details", "Details", DEF_DETAILS, t_String },
+ { &proj, "projection", "Projection", DEF_PROJECTION, t_String }
+};
+
+ENTRYPOINT ModeSpecOpt hopffibration_opts =
+{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL};
+
+
+typedef struct {
+ GLint WindH, WindW;
+ GLXContext *glx_context;
+ /* Options */
+ Bool shadows;
+ Bool base_space;
+ Bool anti_aliasing;
+ int projection;
+ int details;
+ int num_tube;
+ int base_sphere_s;
+ int base_point_s;
+ float max_circle_dist;
+ /* Aspect ratio of the current window */
+ float aspect;
+ /* Trackball states */
+ trackball_state *trackball;
+ Bool button_pressed;
+ /* 3D rotation angles */
+ float alpha, beta, delta;
+ /* Rotation angles for the base space. */
+ float zeta, eta, theta;
+ /* A rotation axis around which the base points are rotated. */
+ float rot_axis_base[3];
+ /* The quaternion corresponding to a rotation by the current angle around
+ rot_axis_base. */
+ float quat_base[4];
+ /* A rotation axis around which the Hopf fibers in the projection of the
+ total space are rotated. */
+ float rot_axis_space[3];
+ /* The quaternion corresponding to a rotation by the current angle around
+ rot_axis_space. */
+ float quat_space[4];
+ /* The angle range through which the Hopf fibers in the projection of the
+ total space are rotated. */
+ float angle_space_start, angle_space_end;
+ /* The current state of the animation. */
+ int anim_state;
+ /* A variable that counts how many cycles to remain in the current state. */
+ int anim_remain_in_state;
+ /* The phase of the current animation. */
+ int anim_phase;
+ /* The number of phases in the current animation. */
+ int anim_phase_num;
+ /* The number of steps in the current animation. */
+ int anim_step;
+ /* The easing function for a rotation around a rot_axis_base. */
+ int anim_easing_fct_rot_rnd;
+ /* The easing function for a rotation around rot_axis_space. */
+ int anim_easing_fct_rot_space;
+ /* If true, perform a rotation around rot_axis_base. */
+ Bool anim_rotate_rnd;
+ /* If true, perform a rotation around rot_axis_space. */
+ Bool anim_rotate_space;
+ /* The phases of the current animation. */
+ animation_phases *anim_phases;
+ /* The objects that are animated in the current phase of the current
+ animation. */
+ animation_multi_obj *anim;
+ /* The state variables current phase of the current animation. */
+ animation_geometry anim_geom[MAX_ANIM_GEOM];
+ /* The set of base points for which Hopf fibers should be computed and
+ displayed. */
+ hopf_base_point *base_points;
+ /* The maximum number of base points over all animations in
+ hopfanimations.c. */
+ int max_base_points;
+ /* The number of elements in base_points that have been set. */
+ int num_base_points;
+ /* An icosphere that is used to display the base sphere S². */
+ icosphere_data *sphere_base;
+ /* An icosphere that is used to display the base points in base_points on
+ the base sphere S². */
+ icosphere_data *sphere_base_point;
+ /* The number of polygons that were drawn. */
+ int num_poly;
+#ifdef HAVE_GLSL
+ float *vert;
+ float *norm;
+ int *tri;
+ Bool use_shaders, use_msaa_fbo, use_shadow_fbo;
+ Bool shadow_fbo_req_col_tex;
+ GLuint shader_program, shadow_program;
+ GLint pos_index, normal_index, color_index;
+ GLint mv_index, proj_index, lmvp_index;
+ GLint glbl_ambient_index, lt_ambient_index;
+ GLint lt_diffuse_index, lt_specular_index;
+ GLint lt_direction_index, lt_halfvect_index;
+ GLint ambient_index, diffuse_index;
+ GLint specular_index, shininess_index;
+ GLint use_shadows_index, shadow_smpl_index;
+ GLint shadow_pix_size_index;
+ GLint shadow_pos_index, shadow_lmvp_index;
+ GLuint *fiber_pos_buffer, *fiber_normal_buffer;
+ GLuint *fiber_indices_buffer;
+ GLuint *fiber_num_tri;
+ GLuint sphere_base_pos_buffer, sphere_base_normal_buffer;
+ GLuint sphere_base_indices_buffer;
+ GLuint sphere_base_num_tri;
+ GLuint base_point_pos_buffer, base_point_normal_buffer;
+ GLuint base_point_indices_buffer;
+ GLuint base_point_num_tri;
+ GLuint default_draw_fbo, default_read_fbo;
+ GLuint msaa_fbo, msaa_rb_color, msaa_rb_depth;
+ GLint msaa_samples;
+ GLuint shadow_fbo;
+ GLuint shadow_tex, shadow_col_tex, shadow_dummy_tex;
+ GLint shadow_tex_size, max_tex_size;
+#endif /* HAVE_GLSL */
+} hopffibrationstruct;
+
+static hopffibrationstruct *hopffibration = (hopffibrationstruct *) NULL;
+
+
+/* The camera position. */
+static float eye_pos[3] = {
+ 0.0f, 0.0f, 3.0f
+};
+
+/* The position of the base sphere S² with respect to the projection of the
+ fibers in the total space S³, which all lie in a ball in 3-space
+ centrered at the origin. */
+static float base_offset[3] = {
+ 0.9f, -0.9f, 0.25f
+};
+
+
+
+#ifdef HAVE_GLSL
+
+/* The GLSL versions that correspond to different versions of OpenGL. */
+static const GLchar *shader_version_2_1 =
+ "#version 120\n";
+static const GLchar *shader_version_3_0 =
+ "#version 130\n";
+static const GLchar *shader_version_3_0_es =
+ "#version 300 es\n"
+ "precision highp float;\n"
+ "precision highp int;\n"
+ "precision highp sampler2DShadow;\n";
+
+/* The vertex shader code is composed of code fragments that depend on
+ the OpenGL version and code fragments that are version-independent.
+ They are concatenated by glShaderSource in the function init_glsl(). */
+static const GLchar *vertex_shader_attribs_2_1 =
+ "attribute vec3 VertexPosition;\n"
+ "attribute vec3 VertexNormal;\n"
+ "attribute vec4 VertexColor;\n"
+ "\n"
+ "varying vec3 Normal;\n"
+ "varying vec4 Color;\n"
+ "varying vec4 ShadowCoord;\n"
+ "\n";
+static const GLchar *vertex_shader_attribs_3_0 =
+ "in vec3 VertexPosition;\n"
+ "in vec3 VertexNormal;\n"
+ "in vec4 VertexColor;\n"
+ "\n"
+ "out vec3 Normal;\n"
+ "out vec4 Color;\n"
+ "out vec4 ShadowCoord;\n"
+ "\n";
+static const GLchar *vertex_shader_main =
+ "uniform mat4 MatModelView;\n"
+ "uniform mat4 MatProj;\n"
+ "uniform mat4 MatLightMVP;\n"
+ "\n"
+ "void main (void)\n"
+ "{\n"
+ " Color = VertexColor;\n"
+ " Normal = normalize(MatModelView*vec4(VertexNormal,0.0f)).xyz;\n"
+ " vec4 Position = MatModelView*vec4(VertexPosition,1.0f);\n"
+ " gl_Position = MatProj*Position;\n"
+ " ShadowCoord = 0.5f*MatLightMVP*vec4(VertexPosition,1.0f)+0.5f;\n"
+ "}\n";
+
+/* The fragment shader code is composed of code fragments that depend on
+ the OpenGL version and code fragments that are version-independent.
+ They are concatenated by glsl_CompileAndLinkShaders in the function
+ init_glsl(). */
+static const GLchar *fragment_shader_attribs_2_1 =
+ "varying vec3 Normal;\n"
+ "varying vec4 Color;\n"
+ "varying vec4 ShadowCoord;\n"
+ "\n"
+ "uniform vec4 LtGlblAmbient;\n"
+ "uniform vec4 LtAmbient, LtDiffuse, LtSpecular;\n"
+ "uniform vec3 LtDirection, LtHalfVector;\n"
+ "uniform vec4 MatAmbient;\n"
+ "uniform vec4 MatDiffuse;\n"
+ "uniform vec4 MatSpecular;\n"
+ "uniform float MatShininess;\n"
+ "uniform bool UseShadows;\n"
+ "uniform float ShadowPixSize;\n"
+ "uniform sampler2DShadow ShadowTex;\n"
+ "\n"
+ "float SampleShadowPCF(int x, int y)\n"
+ "{\n"
+ " vec4 offset;\n"
+ " \n"
+ " offset = vec4(x*ShadowPixSize,y*ShadowPixSize,0.0f,0.0f);\n"
+ " return shadow2DProj(ShadowTex,ShadowCoord+offset).x;\n"
+ "}\n"
+ "\n";
+static const GLchar *fragment_shader_attribs_3_0 =
+ "in vec3 Normal;\n"
+ "in vec4 Color;\n"
+ "in vec4 ShadowCoord;\n"
+ "\n"
+ "out vec4 FragColor;\n"
+ "\n"
+ "uniform vec4 LtGlblAmbient;\n"
+ "uniform vec4 LtAmbient, LtDiffuse, LtSpecular;\n"
+ "uniform vec3 LtDirection, LtHalfVector;\n"
+ "uniform vec4 MatAmbient;\n"
+ "uniform vec4 MatDiffuse;\n"
+ "uniform vec4 MatSpecular;\n"
+ "uniform float MatShininess;\n"
+ "uniform bool UseShadows;\n"
+ "uniform float ShadowPixSize;\n"
+ "uniform sampler2DShadow ShadowTex;\n"
+ "\n"
+ "float SampleShadowPCF(int x, int y)\n"
+ "{\n"
+ " vec4 offset;\n"
+ " \n"
+ " offset = vec4(float(x)*ShadowPixSize,float(y)*ShadowPixSize,0.0f,0.0f);\n"
+ " return textureProj(ShadowTex,ShadowCoord+offset);\n"
+ "}\n"
+ "\n";
+static const GLchar *fragment_shader_main =
+ "void main (void)\n"
+ "{\n"
+ " vec3 normalDirection;\n"
+ " vec4 ambientColor, diffuseColor, sceneColor;\n"
+ " vec4 ambientLighting, diffuseReflection, specularReflection, color;\n"
+ " float ndotl, ndoth, pf, shadowPCF;\n"
+ " int i, j;\n"
+ " \n"
+ " if (gl_FrontFacing)\n"
+ " normalDirection = normalize(Normal);\n"
+ " else\n"
+ " normalDirection = -normalize(Normal);\n"
+ " sceneColor = Color*MatAmbient*LtGlblAmbient;\n"
+ " ambientColor = Color*MatAmbient;\n"
+ " diffuseColor = Color*MatDiffuse;\n"
+ " \n"
+ " if (UseShadows)\n"
+ " {\n"
+ " shadowPCF = 0.0f;\n"
+ " for (i=-1; i<=1; i++)\n"
+ " for (j=-1; j<=1; j++)\n"
+ " shadowPCF += SampleShadowPCF(i,j);\n"
+ " shadowPCF *= 1.0f/9.0f;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " shadowPCF = 1.0f;\n"
+ " }\n"
+ " \n"
+ " ndotl = max(0.0f,dot(normalDirection,LtDirection));\n"
+ " ndoth = max(0.0f,dot(normalDirection,LtHalfVector));\n"
+ " if (ndotl == 0.0f)\n"
+ " pf = 0.0f;\n"
+ " else\n"
+ " pf = pow(ndoth,MatShininess);\n"
+ " ambientLighting = ambientColor*LtAmbient;\n"
+ " diffuseReflection = LtDiffuse*diffuseColor*ndotl;\n"
+ " specularReflection = LtSpecular*MatSpecular*pf;\n"
+ " color = sceneColor+ambientLighting;\n"
+ " color += shadowPCF*(diffuseReflection+specularReflection);\n"
+ " color.a = diffuseColor.a;\n";
+static const GLchar *fragment_shader_out_2_1 =
+ " gl_FragColor = clamp(color,0.0f,1.0f);\n"
+ "}\n";
+static const GLchar *fragment_shader_out_3_0 =
+ " FragColor = clamp(color,0.0f,1.0f);\n"
+ "}\n";
+
+
+/* The shadow vertex shader code is composed of code fragments that depend
+ on the OpenGL version and code fragments that are version-independent.
+ They are concatenated by glShaderSource in the function init_glsl(). */
+static const GLchar *shadow_vertex_shader_attribs_2_1 =
+ "attribute vec4 VertexPosition;\n"
+ "\n";
+static const GLchar *shadow_vertex_shader_attribs_3_0 =
+ "in vec4 VertexPosition;\n"
+ "\n";
+static const GLchar *shadow_vertex_shader_main =
+ "uniform mat4 MatLightMVP;\n"
+ "\n"
+ "void main (void)\n"
+ "{\n"
+ " gl_Position = MatLightMVP*VertexPosition;\n"
+ "}\n";
+
+/* The shadow fragment shader code is composed of a single code fragment
+ that does not depend on the OpenGL version. */
+static const GLchar *shadow_fragment_shader_main =
+ "void main (void)\n"
+ "{\n"
+ " // Nothing to do since depth is handled automatically by OpenGL.\n"
+ "}\n";
+
+#endif /* HAVE_GLSL */
+
+
+/* Compute the Euclidean norm of a vector. */
+static inline float norm(const float v[3])
+{
+ return sqrtf(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
+}
+
+
+/* Normalize a vector. */
+static inline void normalize(float v[3])
+{
+ float l;
+
+ l = norm(v);
+ if (l > 0.0f)
+ l = 1.0f/l;
+ v[0] *= l;
+ v[1] *= l;
+ v[2] *= l;
+}
+
+
+/* Normalize a vector to length r. */
+static inline void normalize_to_length(float v[3], float r)
+{
+ float l;
+
+ l = norm(v);
+ if (l > 0.0f)
+ l = r/l;
+ v[0] *= l;
+ v[1] *= l;
+ v[2] *= l;
+}
+
+
+/* Compute the vector difference s = a - b. */
+static inline void sub(float s[3], const float a[3], const float b[3])
+{
+ s[0] = a[0]-b[0];
+ s[1] = a[1]-b[1];
+ s[2] = a[2]-b[2];
+}
+
+
+/* Compute the cross product c = m × n. */
+static inline void cross(float c[3], const float m[3], const float n[3])
+{
+ c[0] = m[1]*n[2]-m[2]*n[1];
+ c[1] = m[2]*n[0]-m[0]*n[2];
+ c[2] = m[0]*n[1]-m[1]*n[0];
+}
+
+
+/* Compute the distance of the point p from the line given by the two points
+ q and r. It is assumed that q and r are distinct points. */
+static float distance_point_line(const float p[3], const float q[3],
+ const float r[3])
+{
+ float v[3], w[3], c[3], lv, lc;
+
+ sub(v,r,q);
+ lv = norm(v);
+ sub(w,p,q);
+ cross(c,w,v);
+ lc = norm(c);
+ if (lv > 0.0f)
+ return lc/lv;
+ else
+ return 0.0f;
+}
+
+
+/* Compute the identity matrix. */
+static void identity(float m[3][3])
+{
+ int i, j;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ m[i][j] = (float)(i==j);
+}
+
+
+/* Add a rotation around the x axis to the matrix m. */
+static void rotatex(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI_F/180.0f;
+ c = cosf(phi);
+ s = sinf(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][1];
+ v = m[i][2];
+ m[i][1] = c*u+s*v;
+ m[i][2] = -s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the y axis to the matrix m. */
+static void rotatey(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI_F/180.0f;
+ c = cosf(phi);
+ s = sinf(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][2];
+ m[i][0] = c*u-s*v;
+ m[i][2] = s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the z axis to the matrix m. */
+static void rotatez(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI_F/180.0f;
+ c = cosf(phi);
+ s = sinf(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][1];
+ m[i][0] = c*u+s*v;
+ m[i][1] = -s*u+c*v;
+ }
+}
+
+
+/* Compute the rotation matrix m from the rotation angles. */
+static void rotateall(float al, float be, float de, float m[3][3])
+{
+ int i, j;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ m[i][j] = (i==j);
+ rotatex(m,al);
+ rotatey(m,be);
+ rotatez(m,de);
+}
+
+
+/* Multiply two rotation matrices: o = m * n. */
+static void mult_rotmat(float o[3][3], float m[3][3], float n[3][3])
+{
+ int i, j, k;
+
+ for (i=0; i<3; i++)
+ {
+ for (j=0; j<3; j++)
+ {
+ o[i][j] = 0.0f;
+ for (k=0; k<3; k++)
+ o[i][j] += m[i][k]*n[k][j];
+ }
+ }
+}
+
+
+/* Multiply a rotation matrix with a vector: o = m * v. */
+static void mult_rotmat_vec(float o[3], float m[3][3], float v[3])
+{
+ int i, j;
+
+ for (i=0; i<3; i++)
+ {
+ o[i] = 0.0f;
+ for (j=0; j<3; j++)
+ o[i] += m[i][j]*v[j];
+ }
+}
+
+
+/* Create a look-at rotation matrix. */
+static void look_at_rotmat(float c[3][3],
+ GLfloat eyex, GLfloat eyey, GLfloat eyez,
+ GLfloat centerx, GLfloat centery, GLfloat centerz,
+ GLfloat upx, GLfloat upy, GLfloat upz)
+{
+ float forward[3], side[3], up[3];
+
+ forward[0] = centerx-eyex;
+ forward[1] = centery-eyey;
+ forward[2] = centerz-eyez;
+ normalize(forward);
+
+ up[0] = upx;
+ up[1] = upy;
+ up[2] = upz;
+
+ /* side = forward × up */
+ cross(side,forward,up);
+ normalize(side);
+
+ /* up = side × forward */
+ cross(up,side,forward);
+
+ c[0][0] = side[0];
+ c[0][1] = side[1];
+ c[0][2] = side[2];
+ c[1][0] = up[0];
+ c[1][1] = up[1];
+ c[1][2] = up[2];
+ c[2][0] = -forward[0];
+ c[2][1] = -forward[1];
+ c[2][2] = -forward[2];
+}
+
+
+/* Create a rotation matrix from a quaternion. */
+static void quat_to_rotmat(float q[4], float m[3][3])
+{
+ m[0][0] = q[0]*q[0]+q[1]*q[1]-q[2]*q[2]-q[3]*q[3];
+ m[0][1] = 2.0f*(q[1]*q[2]-q[0]*q[3]);
+ m[0][2] = 2.0f*(q[1]*q[3]+q[0]*q[2]);
+ m[1][0] = 2.0f*(q[1]*q[2]+q[0]*q[3]);
+ m[1][1] = q[0]*q[0]-q[1]*q[1]+q[2]*q[2]-q[3]*q[3];
+ m[1][2] = 2.0f*(q[2]*q[3]-q[0]*q[1]);
+ m[2][0] = 2.0f*(q[1]*q[3]-q[0]*q[2]);
+ m[2][1] = 2.0f*(q[2]*q[3]+q[0]*q[1]);
+ m[2][2] = q[0]*q[0]-q[1]*q[1]-q[2]*q[2]+q[3]*q[3];
+}
+
+
+/* Create a 4×4 column-major rotation matrix from a quaternion. */
+static void quat_to_rotmat_4x4(GLfloat q[4], GLfloat c[16])
+{
+ c[0] = q[0]*q[0]+q[1]*q[1]-q[2]*q[2]-q[3]*q[3];
+ c[1] = 2.0f*(q[1]*q[2]+q[0]*q[3]);
+ c[2] = 2.0f*(q[1]*q[3]-q[0]*q[2]);
+ c[3] = 0.0f;
+ c[4] = 2.0f*(q[1]*q[2]-q[0]*q[3]);
+ c[5] = q[0]*q[0]-q[1]*q[1]+q[2]*q[2]-q[3]*q[3];
+ c[6] = 2.0f*(q[2]*q[3]+q[0]*q[1]);
+ c[7] = 0.0f;
+ c[8] = 2.0f*(q[1]*q[3]+q[0]*q[2]);
+ c[9] = 2.0f*(q[2]*q[3]-q[0]*q[1]);
+ c[10] = q[0]*q[0]-q[1]*q[1]-q[2]*q[2]+q[3]*q[3];
+ c[11] = 0.0f;
+ c[12] = 0.0f;
+ c[13] = 0.0f;
+ c[14] = 0.0f;
+ c[15] = 1.0f;
+}
+
+
+/* Compute a 3×3 rotation matrix from an xscreensaver unit quaternion. Note
+ that xscreensaver has a different convention for unit quaternions than
+ the one that is used in this hack. */
+static void quat_to_rotmat_trackball(float p[4], float m[3][3])
+{
+ float al, be, de;
+ float r00, r01, r02, r12, r22;
+
+ r00 = 1.0f-2.0f*(p[1]*p[1]+p[2]*p[2]);
+ r01 = 2.0f*(p[0]*p[1]+p[2]*p[3]);
+ r02 = 2.0f*(p[2]*p[0]-p[1]*p[3]);
+ r12 = 2.0f*(p[1]*p[2]+p[0]*p[3]);
+ r22 = 1.0f-2.0f*(p[1]*p[1]+p[0]*p[0]);
+
+ al = atan2f(-r12,r22)*180.0f/M_PI_F;
+ be = atan2f(r02,sqrtf(r00*r00+r01*r01))*180.0f/M_PI_F;
+ de = atan2f(-r01,r00)*180.0f/M_PI_F;
+ rotateall(al,be,de,m);
+}
+
+
+/* Compute a 4×4 rotation matrix from an xscreensaver unit quaternion. Note
+ that xscreensaver has a different convention for unit quaternions than
+ the one that is used in this hack. */
+static void quat_to_rotmat_trackball_4x4(float p[4], float r[16])
+{
+ float m[3][3];
+ int i, j;
+
+ quat_to_rotmat_trackball(p,m);
+ for (i=0; i<3; i++)
+ {
+ for (j=0; j<3; j++)
+ r[j*4+i] = m[i][j];
+ r[3*4+i] = 0.0f;
+ r[i*4+3] = 0.0f;
+ }
+ r[3*4+3] = 1.0f;
+}
+
+
+/* Generate the vertices and triangles of the icosphere icosp with radius r
+ by subdividing the triangles of the icosahedron icosa into s segments
+ along each edge, resulting in s*s triangles for each triangle of the
+ icosahedron. */
+static void gen_icosphere(const icosahedron_data *icosa, icosphere_data *icosp,
+ int s, float r)
+{
+ int i, j, k, m, n, o, num_edge;
+ int i1, i2, i3, l1, l2, l3, k1, k2;
+ int vf, vt;
+ float t;
+ int num_vert, num_tri;
+ const float *v;
+ const int *tri;
+ unsigned char *adj;
+ int *edge_index_arr;
+ int **edge_indices;
+ float *vs;
+ int *vi, *ts;
+
+ /* Sanity check. */
+ if (s < 1)
+ s = 1;
+
+ v = icosa->vert;
+ num_vert = icosa->num_vert;
+ tri = icosa->tri;
+ num_tri = icosa->num_tri;
+ vs = icosp->vert;
+ ts = icosp->tri;
+
+ /* Allocate and clear the adjacency matrix. */
+ adj = calloc(num_vert*num_vert,sizeof(*adj));
+ /* Allocate the array that stores the vertex indices of a subdivided
+ triangle. */
+ vi = malloc((s+1)*(s+2)*sizeof(*vi));
+ if (adj == NULL || vi == NULL)
+ abort();
+
+ /* Initialize the adjacency matrix. */
+ for (i=0; i<num_tri; i++)
+ {
+ for (j=0; j<3; j++)
+ {
+ vf = tri[3*i+j];
+ vt = tri[3*i+(j+1)%3];
+ adj[LINCOOR(vf,vt,num_vert)] = 1;
+ adj[LINCOOR(vt,vf,num_vert)] = 1;
+ }
+ }
+
+ /* Count the number of edges of the polyhedron. */
+ num_edge = 0;
+ for (i=0; i<num_vert; i++)
+ {
+ for (j=i+1; j<num_vert; j++)
+ num_edge += adj[LINCOOR(i,j,num_vert)];
+ }
+
+ /* Allocate the array that holds all the vertex indices for the newly
+ created vertices on the edges. */
+ edge_index_arr = malloc(2*num_edge*(s-1)*sizeof(*edge_index_arr));
+ /* Allocate the matrix that holds the pointers to the indices for the
+ newly created vertices on the edges and populate the matrix with
+ pointers into edge_index_arr. */
+ edge_indices = malloc(num_vert*num_vert*sizeof(*edge_indices));
+ if (edge_index_arr == NULL || edge_indices == NULL)
+ abort();
+
+ n = 0;
+ for (i=0; i<num_vert; i++)
+ {
+ for (j=0; j<num_vert; j++)
+ {
+ k = LINCOOR(i,j,num_vert);
+ if (adj[k])
+ {
+ edge_indices[k] = &edge_index_arr[n*(s-1)];
+ n++;
+ }
+ else
+ {
+ edge_indices[k] = NULL;
+ }
+ }
+ }
+
+ /* Copy the input vertices to the output vertices. */
+ for (i=0; i<num_vert; i++)
+ {
+ k = 3*i;
+ vs[k+0] = v[k+0];
+ vs[k+1] = v[k+1];
+ vs[k+2] = v[k+2];
+ }
+ n = num_vert;
+
+ /* Compute the s-1 vertices that are added on each edge and set the edge
+ indices in the upper right half of the edge_indices array. */
+ for (i=0; i<num_vert; i++)
+ {
+ l1 = 3*i;
+ for (j=i+1; j<num_vert; j++)
+ {
+ l2 = 3*j;
+ k = LINCOOR(i,j,num_vert);
+ if (adj[k])
+ {
+ for (m=1; m<=s-1; m++)
+ {
+ l3 = 3*n;
+ t = (float)m/(float)s;
+ vs[l3+0] = (1.0f-t)*v[l1+0]+t*v[l2+0];
+ vs[l3+1] = (1.0f-t)*v[l1+1]+t*v[l2+1];
+ vs[l3+2] = (1.0f-t)*v[l1+2]+t*v[l2+2];
+ edge_indices[k][m-1] = n;
+ n++;
+ }
+ }
+ }
+ }
+
+ /* Set the lower left half of the edge_indices array to the reverse list
+ stored in the corresponding entry in the upper right half. */
+ for (i=0; i<num_vert; i++)
+ {
+ for (j=i+1; j<num_vert; j++)
+ {
+ k1 = LINCOOR(i,j,num_vert);
+ k2 = LINCOOR(j,i,num_vert);
+ if (adj[k1])
+ {
+ for (m=1; m<=s-1; m++)
+ edge_indices[k2][s-1-m] = edge_indices[k1][m-1];
+ }
+ }
+ }
+
+ m = 0;
+ for (i=0; i<num_tri; i++)
+ {
+ /* Compute the vertices that are added inside each triangle. */
+ i1 = tri[3*i+0];
+ i2 = tri[3*i+1];
+ i3 = tri[3*i+2];
+ o = 0;
+ vi[o++] = i2;
+ for (j=1; j<=s-1; j++)
+ {
+ k1 = edge_indices[LINCOOR(i2,i3,num_vert)][j-1];
+ k2 = edge_indices[LINCOOR(i2,i1,num_vert)][j-1];
+ vi[o++] = k1;
+ for (k=1; k<=j-1; k++)
+ {
+ l1 = 3*k1;
+ l2 = 3*k2;
+ l3 = 3*n;
+ t = (float)k/(float)j;
+ vs[l3+0] = (1.0f-t)*vs[l1+0]+t*vs[l2+0];
+ vs[l3+1] = (1.0f-t)*vs[l1+1]+t*vs[l2+1];
+ vs[l3+2] = (1.0f-t)*vs[l1+2]+t*vs[l2+2];
+ vi[o++] = n;
+ n++;
+ }
+ vi[o++] = k2;
+ }
+ vi[o++] = i3;
+ for (k=1; k<=j-1; k++)
+ {
+ k1 = edge_indices[LINCOOR(i3,i1,num_vert)][k-1];
+ vi[o++] = k1;
+ }
+ vi[o++] = i1;
+
+ /* Compute the triangles of the subdivided triangle. */
+ for (j=0; j<s; j++)
+ {
+ k1 = j*(j+1)/2;
+ k2 = k1+j+1;
+ for (k=0; k<j; k++)
+ {
+ ts[m+0] = vi[k1+k];
+ ts[m+1] = vi[k2+k];
+ ts[m+2] = vi[k2+k+1];
+ ts[m+3] = vi[k1+k];
+ ts[m+4] = vi[k2+k+1];
+ ts[m+5] = vi[k1+k+1];
+ m += 6;
+ }
+ ts[m+0] = vi[k1+k];
+ ts[m+1] = vi[k2+k];
+ ts[m+2] = vi[k2+k+1];
+ m += 3;
+ }
+ }
+
+ /* Normalize the length of the vertices so that they lie on the sphere of
+ radius r. */
+ for (i=0; i<n; i++)
+ {
+ k = 3*i;
+ normalize_to_length(&vs[k],r);
+ }
+
+ /* Free the temporary data structures. */
+ free(edge_indices);
+ free(edge_index_arr);
+ free(vi);
+ free(adj);
+}
+
+
+/* Generate the normals of the icosphere icosp. */
+static void gen_icosphere_normals(icosphere_data *icosp)
+{
+ int i, k, n;
+ const float *vert;
+ float *norm;
+
+ vert = icosp->vert;
+ norm = icosp->norm;
+ n = icosp->num_vert;
+
+ for (i=0; i<n; i++)
+ {
+ k = 3*i;
+ norm[k+0] = vert[k+0];
+ norm[k+1] = vert[k+1];
+ norm[k+2] = vert[k+2];
+ normalize(&norm[k]);
+ }
+}
+
+
+/* Generate the complete data necessary to render an icosphere with radius r
+ by subdividing the triangles of the icosahedron icosa into s segments
+ along each edge, resulting in s*s triangles for each triangle of the
+ icosahedron. */
+static icosphere_data *gen_icosphere_data(const icosahedron_data *icosa,
+ int s, float r)
+{
+ icosphere_data *icosp;
+
+ icosp = malloc(sizeof(*icosp));
+ if (icosp == NULL)
+ return NULL;
+
+ icosp->num_vert = s*s*(icosa->num_vert-2)+2;
+ icosp->num_tri = s*s*icosa->num_tri;
+ icosp->vert = malloc(3*(icosp->num_vert)*sizeof(*icosp->vert));
+ icosp->norm = malloc(3*icosp->num_vert*sizeof(*icosp->norm));
+ icosp->tri = malloc(3*(icosp->num_tri)*sizeof(*icosp->tri));
+ icosp->stri = malloc(3*(icosp->num_tri)*sizeof(*icosp->stri));
+ if (icosp->vert == NULL || icosp->norm == NULL ||
+ icosp->tri == NULL || icosp->stri == NULL)
+ return NULL;
+
+ gen_icosphere(icosa,icosp,s,r);
+ gen_icosphere_normals(icosp);
+
+ /* Cause the sorted triangles and sorted triangle normals to be computed
+ in sort_icosphere_triangles by setting the sort matrix to a null
+ matrix. */
+ memset(icosp->smat,0,sizeof(icosp->smat));
+
+ return icosp;
+}
+
+
+/* Compare the z values of two triangles; used by qsort. */
+static int compare_z(const void *p1, const void *p2)
+{
+ const trisort *t1, *t2;
+
+ t1 = (const trisort *)p1;
+ t2 = (const trisort *)p2;
+ if (t1->z > t2->z)
+ return 1;
+ else if (t1->z < t2->z)
+ return -1;
+ else
+ return 0;
+}
+
+
+/* Sort the triangles of the icosphere icosp based on the rotation given by
+ mat. The sort is only performed if mat is different from the matrix smat
+ stored in icosp. Return false if no sort was necessary and true if a
+ sort has been performed. */
+static Bool sort_icosphere_triangles(icosphere_data *icosp, float mat[3][3])
+{
+ int i, j, k, l;
+ float *vertz;
+ trisort *tris;
+ int num_vert, num_tri;
+ const float *vert;
+ const int *tri;
+ int *stri;
+
+ if (!memcmp(icosp->smat,mat,sizeof(icosp->smat)))
+ return False;
+
+ num_vert = icosp->num_vert;
+ num_tri = icosp->num_tri;
+ vert = icosp->vert;
+ tri = icosp->tri;
+ stri = icosp->stri;
+
+ vertz = malloc(num_vert*sizeof(*vertz));
+ tris = malloc(num_tri*sizeof(*tris));
+ if (vertz == NULL || tris == NULL)
+ abort();
+
+ /* Initialize the indices of the triangle sorting data structure. */
+ for (i=0; i<num_tri; i++)
+ tris[i].idx = i;
+
+ /* Compute the z coordinates of the vertices transformed with mat. */
+ for (i=0; i<num_vert; i++)
+ {
+ l = 3*i;
+ vertz[i] = mat[2][0]*vert[l+0]+mat[2][1]*vert[l+1]+mat[2][2]*vert[l+2];
+ }
+
+ /* Compute the z coordinate of the centroid of the transformed triangles. */
+ for (i=0; i<num_tri; i++)
+ {
+ l = 3*i;
+ tris[i].z = (1.0f/3.0f)*(vertz[tri[l+0]]+vertz[tri[l+1]]+vertz[tri[l+2]]);
+ }
+
+ /* Sort the z values of the centroids of the triangles. */
+ qsort(tris,num_tri,sizeof(*tris),compare_z);
+
+ /* Compute the sorted triangles according to the indices returned by
+ sorting the triangles. */
+ for (i=0; i<num_tri; i++)
+ {
+ k = 3*i;
+ l = 3*tris[i].idx;
+ for (j=0; j<3; j++)
+ stri[k+j] = tri[l+j];
+ }
+
+ free(tris);
+ free(vertz);
+
+ /* Store the current transformation matrix with the icosphere. */
+ memcpy(icosp->smat,mat,sizeof(icosp->smat));
+
+ return True;
+}
+
+
+/* Free the icosphere data stored in icosp. */
+static void free_icosphere_data(icosphere_data *icosp)
+{
+ free(icosp->stri);
+ free(icosp->tri);
+ free(icosp->norm);
+ free(icosp->vert);
+ free(icosp);
+}
+
+
+/* Initialize a Hopf circle fiber with base (a,b,c). It is assumed that the
+ vector (a,b,c) has Euclidean length 1, i.e., that it lies on the unit
+ sphere S² in 3D. */
+static void init_hopf_circle(const hopf_base_point *hb, hopf_circle_par *hc)
+{
+ hc->base = *hb;
+ hc->al = sqrtf(0.5f*(1.0f+hb->c));
+ hc->be = sqrtf(0.5f*(1.0f-hb->c));
+ hc->atab = atan2f(-hb->a,hb->b);
+}
+
+
+/* Calculate a Hopf circle point with parameter phi. */
+static void hopf_circle_point(const hopf_circle_par *hc, float phi,
+ hopf_circle_pnt *hcp)
+{
+ float pht, tht;
+ float w, x, y, z, r;
+
+ tht = phi;
+ pht = hc->atab-phi;
+
+ w = hc->al*cosf(tht);
+ x = -hc->be*cosf(pht);
+ y = -hc->be*sinf(pht);
+ z = hc->al*sinf(tht);
+ r = acosf(w)/(M_PI_F*sqrtf(1.0f-w*w));
+ hcp->p[0] = x*r;
+ hcp->p[1] = y*r;
+ hcp->p[2] = z*r;
+ hcp->phi = phi;
+}
+
+
+/* Calculate the unit tangent vector of a Hopf circle point with parameter
+ phi. */
+static void hopf_circle_tangent(const hopf_circle_par *hc, float phi,
+ float t[3])
+{
+ float pht, tht;
+ float w, x, y, z, r, d, n, s;
+ float dwdp, dxdp, dydp, dzdp, drdp;
+ float dndp, dddp;
+
+ tht = phi;
+ pht = hc->atab-phi;
+
+ w = hc->al*cosf(tht);
+ x = -hc->be*cosf(pht);
+ y = -hc->be*sinf(pht);
+ z = hc->al*sinf(tht);
+
+ s = sqrtf(1.0f-w*w);
+ n = acosf(w);
+ d = M_PI_F*s;
+ r = n/d;
+
+ dwdp = -hc->al*sinf(tht);
+ dxdp = -hc->be*sinf(pht);
+ dydp = hc->be*cosf(pht);
+ dzdp = hc->al*cosf(tht);
+
+ dndp = -dwdp/s;
+ dddp = -M_PI_F*w*dwdp/s;
+ drdp = (dndp*d-n*dddp)/(d*d);
+
+ t[0] = dxdp*r+x*drdp;
+ t[1] = dydp*r+y*drdp;
+ t[2] = dzdp*r+z*drdp;
+ normalize(t);
+}
+
+
+/* Calculate the unit binormal vector for a Hopf circle. */
+static void hopf_circle_binormal(const hopf_circle_par *hc, float n[3])
+{
+ float a, b, c, sab, pisqr8, sq1pc, sq1mc, ac;
+
+ a = hc->base.a;
+ b = hc->base.b;
+ c = hc->base.c;
+ sab = sqrtf(a*a+b*b);
+ if (sab > 0.0f)
+ {
+ pisqr8 = 2.0f*M_SQRT2_F*M_PI_F;
+ sq1mc = sqrtf(1.0f-c);
+ sq1pc = sqrtf(1.0f+c);
+ ac = acosf(-sq1pc/M_SQRT2_F);
+ n[0] = -a*sq1pc*ac/(pisqr8*sab);
+ n[1] = -b*sq1pc*ac/(pisqr8*sab);
+ n[2] = sq1mc*ac/pisqr8;
+ normalize(n);
+ }
+ else if (c >= 0.0f) /* Actually, c = 1.0f in this case. */
+ {
+ n[0] = 1.0f;
+ n[1] = 0.0f;
+ n[2] = 0.0f;
+ }
+ else /* c < 0.0f, i.e., c = -1.0f in this case. */
+ {
+ n[0] = 0.0f;
+ n[1] = 0.0f;
+ n[2] = 1.0f;
+ }
+}
+
+
+/* Generate a Hopf circle as a polygon that approximates the true Hopf
+ circle with a maximum deviation <= MAX_CIRCLE_DIST. */
+static void gen_hopf_circle_points(ModeInfo *mi, const hopf_circle_par *hc,
+ hopf_circle_pnt *hcpa, int *num)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ int i, n, sp;
+ float phi, d, b[3];
+ hopf_circle_pnt hcp, hcps, hcpe;
+ hopf_circle_seg hcs[MAX_CIRCLE_STACK];
+
+ if (hc->base.c < M_COS_1_F)
+ {
+ /* Compute the initial four Hopf circle segments. */
+ sp = 0;
+ for (i=3; i>=0; i--)
+ {
+ phi = (float)i*M_PI_F/2.0f;
+ hopf_circle_point(hc,phi,&hcs[sp].s);
+ phi = (float)(i+1)*M_PI_F/2.0f;
+ hopf_circle_point(hc,phi,&hcs[sp].e);
+ sp++;
+ }
+
+ /* Recursively subdivide the Hopf circle segments until they approximate
+ the Hopf circle by a distance <= MAX_CIRCLE_DIST. */
+ n = 0;
+ sp--;
+ while (sp >= 0)
+ {
+ hcps = hcs[sp].s;
+ hcpe = hcs[sp].e;
+ phi = 0.5f*(hcps.phi+hcpe.phi);
+ hopf_circle_point(hc,phi,&hcp);
+ d = distance_point_line(hcp.p,hcps.p,hcpe.p);
+ if (d > hf->max_circle_dist)
+ {
+ if (sp >= MAX_CIRCLE_STACK-2)
+ abort();
+ hcs[sp].s.p[0] = hcp.p[0];
+ hcs[sp].s.p[1] = hcp.p[1];
+ hcs[sp].s.p[2] = hcp.p[2];
+ hcs[sp].s.phi = phi;
+ hcs[sp].e = hcpe;
+ sp++;
+ hcs[sp].s = hcps;
+ hcs[sp].e.p[0] = hcp.p[0];
+ hcs[sp].e.p[1] = hcp.p[1];
+ hcs[sp].e.p[2] = hcp.p[2];
+ hcs[sp].e.phi = phi;
+ sp++;
+ }
+ else
+ {
+ if (n >= MAX_CIRCLE_PNT-1)
+ abort();
+ hcpa[n] = hcps;
+ n++;
+ }
+ sp--;
+ }
+ hcpa[n] = hcpe;
+ n++;
+
+ /* Compute the unit tangent, unit normal, and unit binormal vectors of
+ each Hopf circle point. Note that the binormal vector is identical
+ for all Hopf circle points since the projected Hopf circle lies in a
+ plane in 3D. */
+ hopf_circle_binormal(hc,b);
+ for (i=0; i<n; i++)
+ {
+ hcpa[i].b[0] = b[0];
+ hcpa[i].b[1] = b[1];
+ hcpa[i].b[2] = b[2];
+ hopf_circle_tangent(hc,hcpa[i].phi,hcpa[i].t);
+ cross(hcpa[i].n,hcpa[i].b,hcpa[i].t);
+ }
+ }
+ else /* hc->base.c >= M_COS_1_F */
+ {
+ /* The circle for (a,b,c) = (0,0,1) projects to a vertical line segment. */
+ hcpa[0].p[0] = 0.0f;
+ hcpa[0].p[1] = 0.0f;
+ hcpa[0].p[2] = 1.0f;
+ hcpa[0].t[0] = 0.0f;
+ hcpa[0].t[1] = 0.0f;
+ hcpa[0].t[2] = 1.0f;
+ hcpa[0].n[0] = 0.0f;
+ hcpa[0].n[1] = 1.0f;
+ hcpa[0].n[2] = 0.0f;
+ hcpa[0].b[0] = 1.0f;
+ hcpa[0].b[1] = 0.0f;
+ hcpa[0].b[2] = 0.0f;
+ hcpa[0].phi = 0.0f;
+
+ hcpa[1].p[0] = 0.0f;
+ hcpa[1].p[1] = 0.0f;
+ hcpa[1].p[2] = -1.0f;
+ hcpa[1].t[0] = 0.0f;
+ hcpa[1].t[1] = 0.0f;
+ hcpa[1].t[2] = 1.0f;
+ hcpa[1].n[0] = 0.0f;
+ hcpa[1].n[1] = 1.0f;
+ hcpa[1].n[2] = 0.0f;
+ hcpa[1].b[0] = 1.0f;
+ hcpa[1].b[1] = 0.0f;
+ hcpa[1].b[2] = 0.0f;
+ hcpa[1].phi = 2.0f*M_PI_F;
+
+ n = 2;
+ }
+
+ *num = n;
+}
+
+
+/* Rotate the base point bp with the rotation matrix m. */
+static void rotate_base_point(float m[3][3], const hopf_base_point *bp,
+ hopf_base_point *bpr)
+{
+ bpr->a = m[0][0]*bp->a+m[0][1]*bp->b+m[0][2]*bp->c;
+ bpr->b = m[1][0]*bp->a+m[1][1]*bp->b+m[1][2]*bp->c;
+ bpr->c = m[2][0]*bp->a+m[2][1]*bp->b+m[2][2]*bp->c;
+}
+
+
+/* Compute the color corresponding to the fiber (a,b,c). */
+static inline void color(const hopf_base_point *hb, float col[4])
+{
+ col[0] = 0.5f*(1.0f+hb->a);
+ col[1] = 0.5f*(1.0f+hb->b);
+ col[2] = 0.5f*(1.0f+hb->c);
+ col[3] = 1.0f;
+}
+
+
+/* Draw the icosphere icosp using the OpenGL fixed functionality. */
+static void draw_icosphere_ff(ModeInfo *mi, icosphere_data *icosp)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ int i, j, l;
+ float mat[3][3];
+ int num_tri;
+ const float *vert;
+ const int *tri;
+ const float *norm;
+#ifdef ROT_BASE_SPACE
+ float matl[3][3], mats[3][3], matq[3][3];
+ float qu[4], qr[3][3];
+
+ gltrackball_get_quaternion(hf->trackball,qu);
+ quat_to_rotmat_trackball(qu,qr);
+ quat_to_rotmat(hf->quat_space,mats);
+ look_at_rotmat(matl,eye_pos[0],eye_pos[1],eye_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ mult_rotmat(matq,matl,qr);
+ mult_rotmat(mat,matq,mats);
+#else
+ look_at_rotmat(mat,eye_pos[0],eye_pos[1],eye_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+#endif
+ rotatex(mat,hf->alpha);
+ rotatey(mat,hf->beta);
+ rotatez(mat,hf->delta);
+ sort_icosphere_triangles(icosp,mat);
+
+ num_tri = icosp->num_tri;
+ vert = icosp->vert;
+ tri = icosp->stri;
+ norm = icosp->norm;
+
+ glBegin(GL_TRIANGLES);
+ for (i=0; i<num_tri; i++)
+ {
+ for (j=0; j<3; j++)
+ {
+ l = tri[3*i+j];
+ glNormal3fv(&norm[3*l]);
+ glVertex3fv(&vert[3*l]);
+ }
+ }
+ glEnd();
+
+ hf->num_poly += num_tri;
+}
+
+
+/* Draw a Hopf circle with base (a,b,c) using the OpenGL fixed
+ functionality. */
+static void draw_hopf_circle_ff(ModeInfo *mi, const hopf_base_point *hb)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ int i, j, k, l, m, num;
+ float p[3], n[3], phi, cp, sp, t, sgn;
+ hopf_circle_par hc;
+ hopf_circle_pnt hcpa[MAX_CIRCLE_PNT];
+
+ init_hopf_circle(hb,&hc);
+ gen_hopf_circle_points(mi,&hc,hcpa,&num);
+
+ for (i=0; i<num-1; i++)
+ {
+ glBegin(GL_TRIANGLE_STRIP);
+ for (j=hf->num_tube; j>=0; j--)
+ {
+ for (k=0; k<=1; k++)
+ {
+ l = i+k;
+ phi = j*(2.0f*M_PI_F/hf->num_tube);
+ cp = cosf(phi);
+ sp = sinf(phi);
+ for (m=0; m<3; m++)
+ {
+ t = cp*hcpa[l].n[m]+sp*hcpa[l].b[m];
+ p[m] = hcpa[l].p[m]+TUBE_RADIUS*t;
+ n[m] = t;
+ }
+ glNormal3fv(n);
+ glVertex3fv(p);
+ }
+ }
+ glEnd();
+ }
+
+ hf->num_poly += 2*(num-1)*hf->num_tube;
+
+ if (hc.base.c >= M_COS_1_F)
+ {
+ /* In this case, the Hopf circle projects to a vertical tube, which is
+ drawn by the above code. In addition, we must draw a circle at each
+ end of the tube. Note that we rely on the fact that num == 2 in this
+ case. */
+ for (i=0; i<num; i++)
+ {
+ if (i == 0)
+ sgn = 1.0f;
+ else
+ sgn = -1.0f;
+ n[0] = 0.0f;
+ n[1] = 0.0f;
+ n[2] = sgn;
+
+ glBegin(GL_TRIANGLE_FAN);
+ for (m=0; m<3; m++)
+ p[m] = hcpa[i].p[m];
+ glNormal3fv(n);
+ glVertex3fv(p);
+
+ for (j=hf->num_tube; j>=0; j--)
+ {
+ phi = sgn*j*(2.0f*M_PI_F/hf->num_tube);
+ cp = cosf(phi);
+ sp = sinf(phi);
+ for (m=0; m<3; m++)
+ {
+ t = cp*hcpa[i].n[m]+sp*hcpa[i].b[m];
+ p[m] = hcpa[i].p[m]+TUBE_RADIUS*t;
+ }
+ glNormal3fv(n);
+ glVertex3fv(p);
+ }
+ glEnd();
+ }
+
+ hf->num_poly += hf->num_tube;
+ }
+}
+
+
+/* Draw the num_bp Hopf circles given by hb using the OpenGL fixed
+ functionality. */
+static int draw_hopf_circles_ff(ModeInfo *mi)
+{
+ static const GLfloat light_ambient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
+ static const GLfloat light_diffuse[] = { 0.7f, 0.7f, 0.7f, 1.0f };
+ static const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ static const GLfloat light_pos[] = { 0.7f, 0.7f, 1.0f, 0.0f };
+ static const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ static const GLfloat mat_gray[] = { 0.7f, 0.7f, 0.7f, 0.6f };
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ hopf_base_point ht;
+ float col[4], mat[3][3], mat1[3][3], mat2[3][3], mats[16];
+ float qu[4], qr[16];
+ int i;
+
+ hf->num_poly = 0;
+
+ /* Compute the rotation matrix that rotates the base points. */
+ identity(mat1);
+ rotatex(mat1,hf->zeta);
+ rotatey(mat1,hf->eta);
+ rotatez(mat1,hf->theta);
+ quat_to_rotmat(hf->quat_base,mat2);
+ mult_rotmat(mat,mat2,mat1);
+
+ /* Compute the rotation matrix that rotates the space points. */
+ quat_to_rotmat_4x4(hf->quat_space,mats);
+
+ /* Compute the space rotation matrix from the trackball state. */
+ gltrackball_get_quaternion(hf->trackball,qu);
+ quat_to_rotmat_trackball_4x4(qu,qr);
+
+ glViewport(0,0,hf->WindW,hf->WindH);
+
+ glClearColor(0.0f,0.0f,0.0f,0.0f);
+ glClearDepth(1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ if (hf->projection == DISP_ORTHOGRAPHIC)
+ {
+ if (hf->aspect >= 1.0f)
+ glOrtho(-1.2*hf->aspect,1.2*hf->aspect,-1.2,1.2,0.1,10.0);
+ else
+ glOrtho(-1.2,1.2,-1.2/hf->aspect,1.2/hf->aspect,0.1,10.0);
+ }
+ else /* hf->projection == DISP_PERSPECTIVE */
+ {
+ if (hf->aspect >= 1.0f)
+ gluPerspective(45.0,hf->aspect,0.1,10.0);
+ else
+ gluPerspective(360.0/M_PI*atan(tan(45.0*M_PI/360.0)/hf->aspect),
+ hf->aspect,0.1,10.0);
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glShadeModel(GL_SMOOTH);
+ glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
+ glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
+ glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
+ glLightfv(GL_LIGHT0,GL_POSITION,light_pos);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
+ glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0f);
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+
+ gluLookAt(eye_pos[0],eye_pos[1],eye_pos[2],0.0,0.0,0.0,0.0,1.0,0.0);
+ glMultMatrixf(qr);
+ glMultMatrixf(mats);
+ glRotatef(hf->alpha,1.0f,0.0f,0.0f);
+ glRotatef(hf->beta,0.0f,1.0f,0.0f);
+ glRotatef(hf->delta,0.0f,0.0f,1.0f);
+
+ /* Draw the fibers (Hopf circles). */
+ for (i=0; i<hf->num_base_points; i++)
+ {
+ rotate_base_point(mat,&hf->base_points[i],&ht);
+
+ color(&ht,col);
+ glColor3fv(col);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,col);
+
+ draw_hopf_circle_ff(mi,&ht);
+ }
+
+ if (hf->base_space)
+ {
+ /* Draw the base points. */
+ for (i=0; i<hf->num_base_points; i++)
+ {
+ rotate_base_point(mat,&hf->base_points[i],&ht);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ gluLookAt(eye_pos[0],eye_pos[1],eye_pos[2],0.0,0.0,0.0,0.0,1.0,0.0);
+ glTranslatef(base_offset[0],base_offset[1],base_offset[2]);
+#ifdef ROT_BASE_SPACE
+ glMultMatrixf(qr);
+ glMultMatrixf(mats);
+#endif
+ glRotatef(hf->alpha,1.0f,0.0f,0.0f);
+ glRotatef(hf->beta,0.0f,1.0f,0.0f);
+ glRotatef(hf->delta,0.0f,0.0f,1.0f);
+ glTranslatef(ht.a*BASE_SPHERE_RADIUS,ht.b*BASE_SPHERE_RADIUS,
+ ht.c*BASE_SPHERE_RADIUS);
+
+ color(&ht,col);
+ glColor3fv(col);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,col);
+
+ draw_icosphere_ff(mi,hf->sphere_base_point);
+ }
+
+ /* Draw the base sphere. */
+ glDepthMask(GL_FALSE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_CULL_FACE);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ gluLookAt(eye_pos[0],eye_pos[1],eye_pos[2],0.0,0.0,0.0,0.0,1.0,0.0);
+ glTranslatef(base_offset[0],base_offset[1],base_offset[2]);
+#ifdef ROT_BASE_SPACE
+ glMultMatrixf(qr);
+ glMultMatrixf(mats);
+#endif
+ glRotatef(hf->alpha,1.0f,0.0f,0.0f);
+ glRotatef(hf->beta,0.0f,1.0f,0.0f);
+ glRotatef(hf->delta,0.0f,0.0f,1.0f);
+
+ glColor3fv(mat_gray);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_gray);
+ draw_icosphere_ff(mi,hf->sphere_base);
+
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ }
+
+ return hf->num_poly;
+}
+
+
+#ifdef HAVE_GLSL
+
+/* Generate vertex, normal, and index buffers for the icosphere icosp. */
+static GLuint gen_icosphere_buffers(ModeInfo *mi, icosphere_data *icosp,
+ GLint pos_buffer, GLuint normal_buffer,
+ GLuint indices_buffer)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ float mat[3][3];
+ int num_tri, num_vert;
+ const float *vert;
+ const int *tri;
+ const float *norm;
+#ifdef ROT_BASE_SPACE
+ float matl[3][3], mats[3][3], matq[3][3];
+ float qu[4], qr[3][3];
+
+ gltrackball_get_quaternion(hf->trackball,qu);
+ quat_to_rotmat_trackball(qu,qr);
+ quat_to_rotmat(hf->quat_space,mats);
+ look_at_rotmat(matl,eye_pos[0],eye_pos[1],eye_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ mult_rotmat(matq,matl,qr);
+ mult_rotmat(mat,matq,mats);
+#else
+ look_at_rotmat(mat,eye_pos[0],eye_pos[1],eye_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+#endif
+ rotatex(mat,hf->alpha);
+ rotatey(mat,hf->beta);
+ rotatez(mat,hf->delta);
+ sort_icosphere_triangles(icosp,mat);
+
+ num_tri = icosp->num_tri;
+ num_vert = icosp->num_vert;
+ vert = icosp->vert;
+ tri = icosp->stri;
+ norm = icosp->norm;
+
+ glBindBuffer(GL_ARRAY_BUFFER,pos_buffer);
+ glBufferData(GL_ARRAY_BUFFER,3*num_vert*sizeof(GLfloat),vert,
+ GL_STREAM_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+
+ glBindBuffer(GL_ARRAY_BUFFER,normal_buffer);
+ glBufferData(GL_ARRAY_BUFFER,3*num_vert*sizeof(GLfloat),norm,
+ GL_STREAM_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,indices_buffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,3*num_tri*sizeof(GLuint),tri,
+ GL_STREAM_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+
+ return num_tri;
+}
+
+
+/* Generate vertex, normal, and index buffers for a Hopf circle with base
+ point (a,b,c). */
+static int gen_hopf_circle_buffers(ModeInfo *mi, const hopf_base_point *hb,
+ GLint pos_buffer, GLuint normal_buffer,
+ GLuint indices_buffer)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ int i, j, m, num;
+ int num_vert, num_tri;
+ float phi, cp, sp, t, sgn, n[3];
+ hopf_circle_par hc;
+ hopf_circle_pnt hcpa[MAX_CIRCLE_PNT];
+ float *vert, *norm;
+ int *tri;
+
+ vert = hf->vert;
+ norm = hf->norm;
+ tri = hf->tri;
+
+ init_hopf_circle(hb,&hc);
+ gen_hopf_circle_points(mi,&hc,hcpa,&num);
+
+ num_tri = 0;
+ for (i=0; i<num; i++)
+ {
+ for (j=hf->num_tube-1; j>=0; j--)
+ {
+ phi = j*(2.0f*M_PI_F/hf->num_tube);
+ cp = cosf(phi);
+ sp = sinf(phi);
+ for (m=0; m<3; m++)
+ {
+ t = cp*hcpa[i].n[m]+sp*hcpa[i].b[m];
+ vert[3*(i*hf->num_tube+j)+m] = hcpa[i].p[m]+TUBE_RADIUS*t;
+ norm[3*(i*hf->num_tube+j)+m] = t;
+ }
+ if (i < num-1)
+ {
+ tri[3*num_tri+0] = i*hf->num_tube+(j+1)%hf->num_tube;
+ tri[3*num_tri+1] = (i+1)*hf->num_tube+(j+1)%hf->num_tube;
+ tri[3*num_tri+2] = i*hf->num_tube+j;
+ tri[3*num_tri+3] = i*hf->num_tube+j;
+ tri[3*num_tri+4] = (i+1)*hf->num_tube+(j+1)%hf->num_tube;
+ tri[3*num_tri+5] = (i+1)*hf->num_tube+j;
+ num_tri += 2;
+ }
+ }
+ }
+ num_vert = num*hf->num_tube;
+
+ if (hc.base.c >= M_COS_1_F)
+ {
+ /* In this case, the Hopf circle projects to a vertical tube, which is
+ created by the above code. In addition, we must draw a disk at each
+ end of the tube. Note that we rely on the fact that num == 2 in this
+ case.*/
+ for (i=0; i<num; i++)
+ {
+ if (i == 0)
+ sgn = 1.0f;
+ else
+ sgn = -1.0f;
+ n[0] = 0.0f;
+ n[1] = 0.0f;
+ n[2] = sgn;
+
+ /* Add the center of the circle to the vertex and normal arrays. */
+ for (m=0; m<3; m++)
+ {
+ vert[3*num_vert+m] = hcpa[i].p[m];
+ norm[3*num_vert+m] = n[m];
+ }
+
+ /* Add the vertices, normals, and triangles for the disk. */
+ for (j=hf->num_tube; j>=0; j--)
+ {
+ phi = sgn*j*(2.0f*M_PI_F/hf->num_tube);
+ cp = cosf(phi);
+ sp = sinf(phi);
+ for (m=0; m<3; m++)
+ {
+ t = cp*hcpa[i].n[m]+sp*hcpa[i].b[m];
+ vert[3*(num_vert+j+1)+m] = hcpa[i].p[m]+TUBE_RADIUS*t;
+ norm[3*(num_vert+j+1)+m] = n[m];
+ }
+ if (j > 0)
+ {
+ tri[3*num_tri+0] = num_vert;
+ tri[3*num_tri+1] = num_vert+j+1;
+ tri[3*num_tri+2] = num_vert+j;
+ num_tri++;
+ }
+ }
+ num_vert += hf->num_tube+2;
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER,pos_buffer);
+ glBufferData(GL_ARRAY_BUFFER,3*num_vert*sizeof(GLfloat),vert,
+ GL_STREAM_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER,normal_buffer);
+ glBufferData(GL_ARRAY_BUFFER,3*num_vert*sizeof(GLfloat),norm,
+ GL_STREAM_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,indices_buffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,3*num_tri*sizeof(GLuint),tri,
+ GL_STREAM_DRAW);
+
+ return num_tri;
+}
+
+
+/* Draw a set of vertex, normal, and index buffers. */
+static void draw_buffers(ModeInfo *mi, int from_light, GLint pos_buffer,
+ GLuint normal_buffer, GLuint indices_buffer,
+ GLuint num_tri)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+
+ if (from_light)
+ {
+ glEnableVertexAttribArray(hf->shadow_pos_index);
+ glBindBuffer(GL_ARRAY_BUFFER,pos_buffer);
+ glVertexAttribPointer(hf->shadow_pos_index,3,GL_FLOAT,GL_FALSE,0,0);
+ }
+ else
+ {
+ glEnableVertexAttribArray(hf->pos_index);
+ glBindBuffer(GL_ARRAY_BUFFER,pos_buffer);
+ glVertexAttribPointer(hf->pos_index,3,GL_FLOAT,GL_FALSE,0,0);
+
+ glEnableVertexAttribArray(hf->normal_index);
+ glBindBuffer(GL_ARRAY_BUFFER,normal_buffer);
+ glVertexAttribPointer(hf->normal_index,3,GL_FLOAT,GL_FALSE,0,0);
+
+ glDisableVertexAttribArray(hf->color_index);
+ }
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,indices_buffer);
+
+ glDrawElements(GL_TRIANGLES,3*num_tri,GL_UNSIGNED_INT,(const GLvoid *)0);
+
+ if (from_light)
+ {
+ glDisableVertexAttribArray(hf->shadow_pos_index);
+ }
+ else
+ {
+ glDisableVertexAttribArray(hf->pos_index);
+ glDisableVertexAttribArray(hf->normal_index);
+ }
+ glBindBuffer(GL_ARRAY_BUFFER,0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+
+ hf->num_poly += num_tri;
+}
+
+
+/* Draw the num_bp Hopf circles given by hb using the OpenGL programmable
+ functionality. */
+static int draw_hopf_circles_pf(ModeInfo *mi)
+{
+ static const GLfloat light_model_ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f };
+ static const GLfloat light_ambient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
+ static const GLfloat light_diffuse[] = { 0.7f, 0.7f, 0.7f, 1.0f };
+ static const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ static const GLfloat light_pos[] = { 0.7f, 0.7f, 1.0f, 0.0f };
+ static const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ static const GLfloat mat_diff_white[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ static const GLfloat mat_gray[] = { 0.7f, 0.7f, 0.7f, 0.6f };
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ GLfloat p_mat[16], mv_mat[16];
+ GLfloat lp_mat[16], lmv_mat[16], lmvp_mat[16], lmvp_bp_mat[16];
+ GLfloat light_direction[3], half_vector[3], len;
+ hopf_base_point ht;
+ float col[4], mat[3][3], mat1[3][3], mat2[3][3], mats[16];
+ float qu[4], qr[16];
+ float mid_depth;
+ int i, from_light;
+
+ hf->num_poly = 0;
+
+ len = norm(light_pos);
+ light_direction[0] = light_pos[0]/len;
+ light_direction[1] = light_pos[1]/len;
+ light_direction[2] = light_pos[2]/len;
+ half_vector[0] = light_direction[0];
+ half_vector[1] = light_direction[1];
+ half_vector[2] = light_direction[2]+1.0f;
+ len = sqrtf(half_vector[0]*half_vector[0]+
+ half_vector[1]*half_vector[1]+
+ half_vector[2]*half_vector[2]);
+ half_vector[0] /= len;
+ half_vector[1] /= len;
+ half_vector[2] /= len;
+
+ /* Compute the rotation matrix that rotates the base points. */
+ identity(mat1);
+ rotatex(mat1,hf->zeta);
+ rotatey(mat1,hf->eta);
+ rotatez(mat1,hf->theta);
+ quat_to_rotmat(hf->quat_base,mat2);
+ mult_rotmat(mat,mat2,mat1);
+
+ /* Compute the rotation matrix that rotates the space points. */
+ quat_to_rotmat_4x4(hf->quat_space,mats);
+
+ /* Compute the space rotation matrix from the trackball state. */
+ gltrackball_get_quaternion(hf->trackball,qu);
+ quat_to_rotmat_trackball_4x4(qu,qr);
+
+ /* Generate the icosphere for the base. */
+ hf->sphere_base_num_tri =
+ gen_icosphere_buffers(mi,hf->sphere_base,hf->sphere_base_pos_buffer,
+ hf->sphere_base_normal_buffer,
+ hf->sphere_base_indices_buffer);
+
+ /* Generate the icosphere for a base point. */
+ hf->base_point_num_tri =
+ gen_icosphere_buffers(mi,hf->sphere_base_point,hf->base_point_pos_buffer,
+ hf->base_point_normal_buffer,
+ hf->base_point_indices_buffer);
+
+ /* Generate the Hopf circles. */
+ for (i=0; i<hf->num_base_points; i++)
+ {
+ rotate_base_point(mat,&hf->base_points[i],&ht);
+ hf->fiber_num_tri[i] =
+ gen_hopf_circle_buffers(mi,&ht,hf->fiber_pos_buffer[i],
+ hf->fiber_normal_buffer[i],
+ hf->fiber_indices_buffer[i]);
+ }
+
+ mid_depth = norm(eye_pos);
+ glsl_Identity(p_mat);
+ if (hf->projection == DISP_ORTHOGRAPHIC)
+ {
+ if (hf->aspect >= 1.0f)
+ glsl_Orthographic(p_mat,-1.2f*hf->aspect,1.2f*hf->aspect,-1.2f,1.2f,
+ mid_depth-1.2f,mid_depth+1.2f);
+ else
+ glsl_Orthographic(p_mat,-1.2f,1.2f,-1.2f/hf->aspect,1.2f/hf->aspect,
+ mid_depth-1.2f,mid_depth+1.2f);
+ }
+ else /* hf->projection == DISP_PERSPECTIVE */
+ {
+ if (hf->aspect >= 1.0f)
+ glsl_Perspective(p_mat,45.0f,hf->aspect,mid_depth-1.2f,mid_depth+1.2f);
+ else
+ glsl_Perspective(p_mat,
+ 360.0f/M_PI_F*atanf(tanf(45.0f*M_PI_F/360.0f)/
+ hf->aspect),
+ hf->aspect,mid_depth-1.2f,mid_depth+1.2f);
+ }
+
+ mid_depth = norm(light_pos);
+ glsl_Identity(lp_mat);
+ glsl_Orthographic(lp_mat,-1.4f,1.4f,-1.4f,1.4f,
+ mid_depth-1.4f,mid_depth+1.4f);
+
+ for (from_light = (hf->shadows && hf->use_shadow_fbo); from_light >= 0;
+ from_light--)
+ {
+ glsl_Identity(mv_mat);
+ glsl_LookAt(mv_mat,eye_pos[0],eye_pos[1],eye_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ glsl_MultMatrix(mv_mat,qr);
+ glsl_MultMatrix(mv_mat,mats);
+ glsl_Rotate(mv_mat,hf->alpha,1.0f,0.0f,0.0f);
+ glsl_Rotate(mv_mat,hf->beta,0.0f,1.0f,0.0f);
+ glsl_Rotate(mv_mat,hf->delta,0.0f,0.0f,1.0f);
+
+ glsl_Identity(lmv_mat);
+ glsl_LookAt(lmv_mat,light_pos[0],light_pos[1],light_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ glsl_MultMatrix(lmv_mat,qr);
+ glsl_MultMatrix(lmv_mat,mats);
+ glsl_Rotate(lmv_mat,hf->alpha,1.0f,0.0f,0.0f);
+ glsl_Rotate(lmv_mat,hf->beta,0.0f,1.0f,0.0f);
+ glsl_Rotate(lmv_mat,hf->delta,0.0f,0.0f,1.0f);
+
+ glsl_CopyMatrix(lmvp_mat,lp_mat);
+ glsl_MultMatrix(lmvp_mat,lmv_mat);
+
+ if (from_light)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER,hf->shadow_fbo);
+
+ glUseProgram(hf->shadow_program);
+
+ glViewport(0,0,hf->shadow_tex_size,hf->shadow_tex_size);
+
+ glClearDepth(1.0f);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
+
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(4.0f,4.0f);
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+
+ glUniformMatrix4fv(hf->shadow_lmvp_index,1,GL_FALSE,lmvp_mat);
+ }
+ else
+ {
+ if (hf->use_msaa_fbo)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER,hf->msaa_fbo);
+ }
+ else
+ {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,hf->default_draw_fbo);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER,hf->default_read_fbo);
+ }
+
+ glUseProgram(hf->shader_program);
+
+ glViewport(0,0,hf->WindW,hf->WindH);
+
+ glClearColor(0.0f,0.0f,0.0f,0.0f);
+ glClearDepth(1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+
+ glUniform4fv(hf->glbl_ambient_index,1,light_model_ambient);
+ glUniform4fv(hf->lt_ambient_index,1,light_ambient);
+ glUniform4fv(hf->lt_diffuse_index,1,light_diffuse);
+ glUniform4fv(hf->lt_specular_index,1,light_specular);
+ glUniform3fv(hf->lt_direction_index,1,light_direction);
+ glUniform3fv(hf->lt_halfvect_index,1,half_vector);
+ glUniform4fv(hf->ambient_index,1,mat_diff_white);
+ glUniform4fv(hf->diffuse_index,1,mat_diff_white);
+ glUniform4fv(hf->specular_index,1,mat_specular);
+ glUniform1f(hf->shininess_index,50.0f);
+
+ glUniformMatrix4fv(hf->proj_index,1,GL_FALSE,p_mat);
+ glUniformMatrix4fv(hf->mv_index,1,GL_FALSE,mv_mat);
+ glUniformMatrix4fv(hf->lmvp_index,1,GL_FALSE,lmvp_mat);
+
+ glVertexAttrib4f(hf->color_index,1.0f,1.0f,1.0f,1.0f);
+
+ glUniform1i(hf->use_shadows_index,(hf->shadows && hf->use_shadow_fbo));
+ glUniform1f(hf->shadow_pix_size_index,1.0f/hf->shadow_tex_size);
+ glActiveTexture(GL_TEXTURE0);
+ if (hf->shadows && hf->use_shadow_fbo)
+ glBindTexture(GL_TEXTURE_2D,hf->shadow_tex);
+ else
+ glBindTexture(GL_TEXTURE_2D,hf->shadow_dummy_tex);
+ glUniform1i(hf->shadow_smpl_index,0);
+ }
+
+ /* Draw the fibers (Hopf circles). */
+ for (i=0; i<hf->num_base_points; i++)
+ {
+ rotate_base_point(mat,&hf->base_points[i],&ht);
+
+ if (!from_light)
+ {
+ color(&ht,col);
+ glVertexAttrib4fv(hf->color_index,col);
+ }
+
+ draw_buffers(mi,from_light,hf->fiber_pos_buffer[i],
+ hf->fiber_normal_buffer[i],hf->fiber_indices_buffer[i],
+ hf->fiber_num_tri[i]);
+ }
+
+ if (hf->base_space)
+ {
+ /* Draw the base points. */
+ for (i=0; i<hf->num_base_points; i++)
+ {
+ rotate_base_point(mat,&hf->base_points[i],&ht);
+
+ glsl_Identity(mv_mat);
+ glsl_LookAt(mv_mat,eye_pos[0],eye_pos[1],eye_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ glsl_Translate(mv_mat,base_offset[0],base_offset[1],base_offset[2]);
+#ifdef ROT_BASE_SPACE
+ glsl_MultMatrix(mv_mat,qr);
+ glsl_MultMatrix(mv_mat,mats);
+#endif
+ glsl_Rotate(mv_mat,hf->alpha,1.0f,0.0f,0.0f);
+ glsl_Rotate(mv_mat,hf->beta,0.0f,1.0f,0.0f);
+ glsl_Rotate(mv_mat,hf->delta,0.0f,0.0f,1.0f);
+ glsl_Translate(mv_mat,
+ ht.a*BASE_SPHERE_RADIUS,
+ ht.b*BASE_SPHERE_RADIUS,
+ ht.c*BASE_SPHERE_RADIUS);
+
+ glsl_Identity(lmv_mat);
+ glsl_LookAt(lmv_mat,light_pos[0],light_pos[1],light_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ glsl_Translate(lmv_mat,base_offset[0],base_offset[1],base_offset[2]);
+#ifdef ROT_BASE_SPACE
+ glsl_MultMatrix(lmv_mat,qr);
+ glsl_MultMatrix(lmv_mat,mats);
+#endif
+ glsl_Rotate(lmv_mat,hf->alpha,1.0f,0.0f,0.0f);
+ glsl_Rotate(lmv_mat,hf->beta,0.0f,1.0f,0.0f);
+ glsl_Rotate(lmv_mat,hf->delta,0.0f,0.0f,1.0f);
+ glsl_Translate(lmv_mat,
+ ht.a*BASE_SPHERE_RADIUS,
+ ht.b*BASE_SPHERE_RADIUS,
+ ht.c*BASE_SPHERE_RADIUS);
+
+ glsl_CopyMatrix(lmvp_bp_mat,lp_mat);
+ glsl_MultMatrix(lmvp_bp_mat,lmv_mat);
+
+ if (from_light)
+ {
+ glUniformMatrix4fv(hf->shadow_lmvp_index,1,GL_FALSE,lmvp_bp_mat);
+ }
+ else
+ {
+ glUniformMatrix4fv(hf->proj_index,1,GL_FALSE,p_mat);
+ glUniformMatrix4fv(hf->mv_index,1,GL_FALSE,mv_mat);
+ glUniformMatrix4fv(hf->lmvp_index,1,GL_FALSE,lmvp_bp_mat);
+
+ color(&ht,col);
+ glVertexAttrib4fv(hf->color_index,col);
+ }
+
+ draw_buffers(mi,from_light,hf->base_point_pos_buffer,
+ hf->base_point_normal_buffer,
+ hf->base_point_indices_buffer,hf->base_point_num_tri);
+ }
+
+ if (!from_light)
+ {
+ /* Draw the base sphere, but not for the shadow buffer pass since
+ it is transparent. */
+ glDepthMask(GL_FALSE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_CULL_FACE);
+
+ glsl_Identity(mv_mat);
+ glsl_LookAt(mv_mat,eye_pos[0],eye_pos[1],eye_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ glsl_Translate(mv_mat,base_offset[0],base_offset[1],base_offset[2]);
+#ifdef ROT_BASE_SPACE
+ glsl_MultMatrix(mv_mat,qr);
+ glsl_MultMatrix(mv_mat,mats);
+#endif
+ glsl_Rotate(mv_mat,hf->alpha,1.0f,0.0f,0.0f);
+ glsl_Rotate(mv_mat,hf->beta,0.0f,1.0f,0.0f);
+ glsl_Rotate(mv_mat,hf->delta,0.0f,0.0f,1.0f);
+
+ glsl_Identity(lmv_mat);
+ glsl_LookAt(lmv_mat,light_pos[0],light_pos[1],light_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ glsl_Translate(lmv_mat,base_offset[0],base_offset[1],base_offset[2]);
+#ifdef ROT_BASE_SPACE
+ glsl_MultMatrix(lmv_mat,qr);
+ glsl_MultMatrix(lmv_mat,mats);
+#endif
+ glsl_Rotate(lmv_mat,hf->alpha,1.0f,0.0f,0.0f);
+ glsl_Rotate(lmv_mat,hf->beta,0.0f,1.0f,0.0f);
+ glsl_Rotate(lmv_mat,hf->delta,0.0f,0.0f,1.0f);
+
+ glsl_CopyMatrix(lmvp_bp_mat,lp_mat);
+ glsl_MultMatrix(lmvp_bp_mat,lmv_mat);
+
+ glUniformMatrix4fv(hf->proj_index,1,GL_FALSE,p_mat);
+ glUniformMatrix4fv(hf->mv_index,1,GL_FALSE,mv_mat);
+ glUniformMatrix4fv(hf->lmvp_index,1,GL_FALSE,lmvp_bp_mat);
+
+ glVertexAttrib4fv(hf->color_index,mat_gray);
+
+ draw_buffers(mi,from_light,hf->sphere_base_pos_buffer,
+ hf->sphere_base_normal_buffer,
+ hf->sphere_base_indices_buffer,hf->sphere_base_num_tri);
+
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ }
+ }
+
+ glUseProgram(0);
+
+ if (from_light)
+ {
+ glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ }
+ else
+ {
+ if (hf->use_msaa_fbo)
+ {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,hf->default_draw_fbo);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER,hf->msaa_fbo);
+
+ glBlitFramebuffer(0,0,hf->WindW,hf->WindH,0,0,hf->WindW,hf->WindH,
+ GL_COLOR_BUFFER_BIT,GL_NEAREST);
+ }
+ }
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,hf->default_draw_fbo);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER,hf->default_read_fbo);
+ }
+
+ glBindTexture(GL_TEXTURE_2D,0);
+
+ return hf->num_poly;
+}
+
+#endif /* HAVE_GLSL */
+
+
+/* Generate a torus, a Hopf torus, or a Hopf flower on the base. A torus is
+ generated if q = 0.0 and r = 0.0. A Hopf torus is generated if r = 0.0.
+ Otherwise, a Hopf flower is generated. */
+static void gen_hopf_torus_base(ModeInfo *mi, float p, float q, float r,
+ int n, float offset, float sector, int num,
+ Bool rotate, float quat[4])
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ float t, g, h;
+ float bp[3], bpr[3];
+ float m[3][3];
+ int i;
+
+ if (num < 1)
+ num = 1;
+ if ((p == 0.0f || p == M_PI_F) && q == 0.0f)
+ num = 1;
+ if (sector == 0.0f)
+ num = 1;
+
+ if (rotate)
+ quat_to_rotmat(quat,m);
+
+ for (i=0; i<num; i++)
+ {
+ t = offset+i*sector/num;
+ if (q == 0.0f)
+ g = p;
+ else
+ g = p+q*sinf(n*t);
+ if (r == 0.0f)
+ h = t;
+ else
+ h = t+r*cosf(n*t);
+ bp[0] = cosf(h)*sinf(g);
+ bp[1] = sinf(h)*sinf(g);
+ bp[2] = cosf(g);
+ if (!rotate)
+ {
+ hf->base_points[hf->num_base_points].a = bp[0];
+ hf->base_points[hf->num_base_points].b = bp[1];
+ hf->base_points[hf->num_base_points].c = bp[2];
+ }
+ else
+ {
+ mult_rotmat_vec(bpr,m,bp);
+ hf->base_points[hf->num_base_points].a = bpr[0];
+ hf->base_points[hf->num_base_points].b = bpr[1];
+ hf->base_points[hf->num_base_points].c = bpr[2];
+ }
+ hf->num_base_points++;
+ }
+}
+
+
+/* Generate a Hopf spiral on the base. */
+static void gen_hopf_spiral_base(ModeInfo *mi, float p, float q, float r,
+ float offset, float sector, int num,
+ Bool rotate, float quat[4])
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ float t;
+ float bp[3], bpr[3];
+ float m[3][3];
+ int i;
+
+ if (num < 1)
+ num = 1;
+ if (sector == 0.0f)
+ num = 1;
+
+ if (rotate)
+ quat_to_rotmat(quat,m);
+
+ for (i=0; i<num; i++)
+ {
+ t = offset+i*sector/num;
+ bp[0] = cosf(-r*t)*cosf(p+0.5f*q*t);
+ bp[1] = sinf(-r*t)*cosf(p+0.5f*q*t);
+ bp[2] = -sinf(p+0.5f*q*t);
+ if (!rotate)
+ {
+ hf->base_points[hf->num_base_points].a = bp[0];
+ hf->base_points[hf->num_base_points].b = bp[1];
+ hf->base_points[hf->num_base_points].c = bp[2];
+ }
+ else
+ {
+ mult_rotmat_vec(bpr,m,bp);
+ hf->base_points[hf->num_base_points].a = bpr[0];
+ hf->base_points[hf->num_base_points].b = bpr[1];
+ hf->base_points[hf->num_base_points].c = bpr[2];
+ }
+ hf->num_base_points++;
+ }
+}
+
+
+/* An easing function for values of t between 0 and 1. */
+static float ease(float t, int easing)
+{
+ if (easing == EASING_NONE)
+ return 0.0f;
+ else if (easing == EASING_CUBIC)
+ return t*t*(3.0f-2.0f*t);
+ else if (easing == EASING_SIN)
+ return (t < 0.25f ? 0.5f*ease(4.0f*t,EASING_CUBIC)+0.5f :
+ (t > 0.75f ? 0.5f*ease(4.0f*t-3.0f,EASING_CUBIC) :
+ 1.0f-ease(2.0f*t-0.5f,EASING_CUBIC)));
+ else if (easing == EASING_COS)
+ return (t < 0.5f ? 1.0f-ease(2.0f*t,EASING_CUBIC) :
+ ease(2.0f*t-1.0f,EASING_CUBIC));
+ else if (easing == EASING_LIN)
+ return t;
+ else if (easing == EASING_ACCEL)
+ return t*t*(2.0f-t);
+ else if (easing == EASING_DECEL)
+ return t*(1.0f+t*(1.0f-t));
+ else
+ return 0.0f;
+}
+
+
+/* Compute a quaternion from a rotation axis and an angle in radians. */
+static void axis_angle_to_quat(float rot_axis[3], float angle, float q[4])
+{
+ q[0] = cosf(0.5f*angle);
+ q[1] = sinf(0.5f*angle)*rot_axis[0];
+ q[2] = sinf(0.5f*angle)*rot_axis[1];
+ q[3] = sinf(0.5f*angle)*rot_axis[2];
+}
+
+
+/* Compute a uniformly distributed random rotation axis, i.e., a vector of
+ length 1 that is uniformly distributed on the unit sphere S². */
+static void gen_random_rot_axis(float rot_axis[3])
+{
+ float p, t;
+
+ t = (float)frand(2.0f*M_PI_F);
+ p = acosf((float)frand(2.0f)-1.0f);
+ rot_axis[0] = sinf(p)*cosf(t);
+ rot_axis[1] = sinf(p)*sinf(t);
+ rot_axis[2] = cosf(p);
+}
+
+
+/* Set the animation quaternions based on the animation time t. */
+static void set_animation_quats(ModeInfo *mi, float t)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ float te;
+
+ if (hf->anim_rotate_rnd)
+ {
+ te = 2.0f*M_PI_F*ease(t,hf->anim_easing_fct_rot_rnd);
+ axis_angle_to_quat(hf->rot_axis_base,te,hf->quat_base);
+ }
+ if (hf->anim_rotate_space)
+ {
+ te = ease(t,hf->anim_easing_fct_rot_space);
+ te = (1.0f-te)*hf->angle_space_start+te*hf->angle_space_end;
+ axis_angle_to_quat(hf->rot_axis_space,te,hf->quat_space);
+ }
+}
+
+
+/* Set the geometry parameters for the animation object i based on the
+ animation time t. */
+static void set_animation_geometry(ModeInfo *mi, float t, int i)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ float te, angle;
+
+ /* Sanity check. */
+ if (i >= MAX_ANIM_GEOM)
+ return;
+
+ hf->anim_geom[i].num = hf->anim->anim_so[i].num;
+ te = ease(t,hf->anim->anim_so[i].easing_function_p);
+ hf->anim_geom[i].p = ((1.0f-te)*hf->anim->anim_so[i].p_start+
+ te*hf->anim->anim_so[i].p_end);
+ te = ease(t,hf->anim->anim_so[i].easing_function_q);
+ hf->anim_geom[i].q = ((1.0f-te)*hf->anim->anim_so[i].q_start+
+ te*hf->anim->anim_so[i].q_end);
+ te = ease(t,hf->anim->anim_so[i].easing_function_r);
+ hf->anim_geom[i].r = ((1.0f-te)*hf->anim->anim_so[i].r_start+
+ te*hf->anim->anim_so[i].r_end);
+ te = ease(t,hf->anim->anim_so[i].easing_function_offset);
+ hf->anim_geom[i].offset = ((1.0f-te)*hf->anim->anim_so[i].offset_start+
+ te*hf->anim->anim_so[i].offset_end);
+ te = ease(t,hf->anim->anim_so[i].easing_function_sector);
+ hf->anim_geom[i].sector = ((1.0f-te)*hf->anim->anim_so[i].sector_start+
+ te*hf->anim->anim_so[i].sector_end);
+ if (hf->anim_geom[i].rotate)
+ {
+ te = ease(t,hf->anim->anim_so[i].easing_function_rotate);
+ angle = ((1.0f-te)*hf->anim->anim_so[i].angle_start+
+ te*hf->anim->anim_so[i].angle_end);
+ axis_angle_to_quat(hf->anim->anim_so[i].rot_axis_base,angle,
+ hf->anim_geom[i].quat_base);
+ }
+}
+
+
+/* Select the next animation phase. */
+static void init_next_anim_phase(ModeInfo *mi, int phase)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ int i;
+ float nrm;
+
+ hf->anim = hf->anim_phases->anim_mo[phase];
+ hf->anim_step = 0;
+
+ for (i=0; i<hf->anim->num; i++)
+ {
+ /* Sanity check. */
+ if (i >= MAX_ANIM_GEOM)
+ break;
+
+ hf->anim_geom[i].generator = hf->anim->anim_so[i].generator;
+
+ hf->anim_geom[i].n = hf->anim->anim_so[i].n;
+
+ nrm = norm(hf->anim->anim_so[i].rot_axis_base);
+ hf->anim_geom[i].rotate = (nrm > 0.0f);
+ hf->anim_geom[i].quat_base[0] = 1.0f;
+ hf->anim_geom[i].quat_base[1] = 0.0f;
+ hf->anim_geom[i].quat_base[2] = 0.0f;
+ hf->anim_geom[i].quat_base[3] = 0.0f;
+ }
+
+ if (hf->anim->rotate_prob == 0.0f)
+ hf->anim_rotate_rnd = False;
+ else if (hf->anim->rotate_prob == 1.0f)
+ hf->anim_rotate_rnd = True;
+ else
+ hf->anim_rotate_rnd = (frand(1.0f) < hf->anim->rotate_prob);
+ hf->anim_easing_fct_rot_rnd = hf->anim->easing_function_rot_rnd;
+
+ gen_random_rot_axis(hf->rot_axis_base);
+
+ hf->anim_easing_fct_rot_space = hf->anim->easing_function_rot_space;
+ hf->rot_axis_space[0] = hf->anim->rot_axis_space[0];
+ hf->rot_axis_space[1] = hf->anim->rot_axis_space[1];
+ hf->rot_axis_space[2] = hf->anim->rot_axis_space[2];
+ nrm = norm(hf->rot_axis_space);
+ hf->anim_rotate_space = (nrm > 0.0f);
+ hf->angle_space_start = hf->anim->angle_start;
+ hf->angle_space_end = hf->anim->angle_end;
+
+ set_animation_quats(mi,0.0f);
+ for (i=0; i<hf->anim->num; i++)
+ set_animation_geometry(mi,0.0f,i);
+}
+
+
+/* Select the next animation. */
+static void set_next_anim(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ int anim_state_next, idx_anim, num_anim;
+ animations *anims_next;
+
+ if (hf->anim_remain_in_state <= 0)
+ anim_state_next = (int)floor(frand(NUM_ANIM_STATES));
+ else
+ anim_state_next = hf->anim_state;
+
+ if (anim_state_next == hf->anim_state)
+ {
+ hf->anim_remain_in_state--;
+ }
+ else
+ {
+ /* Set the number of animations to execute in the next animation state
+ based on the number of available animations in that state. */
+ hf->anim_remain_in_state =
+ (hopf_animations[anim_state_next][anim_state_next]->num_anim+2)/3;
+ }
+
+ anims_next = hopf_animations[hf->anim_state][anim_state_next];
+ hf->anim_state = anim_state_next;
+
+ num_anim = anims_next->num_anim;
+
+ if (num_anim > 1)
+ {
+ /* If there are animations to choose from, avoid using the same
+ animation twice in succession. */
+ do
+ idx_anim = (int)floor(frand(num_anim));
+ while (hf->anim_phases == anims_next->anim[idx_anim]);
+ }
+ else
+ {
+ /* Select the only available choice. */
+ idx_anim = 0;
+ }
+
+ hf->anim_phases = anims_next->anim[idx_anim];
+ hf->anim_phase_num = hf->anim_phases->num_phases;
+ hf->anim_phase = 0;
+
+ init_next_anim_phase(mi,hf->anim_phase);
+}
+
+
+/* Display the Hopf fibration. */
+static void display_hopffibration(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ float t;
+ int i;
+
+ if (!hf->button_pressed)
+ {
+ hf->anim_step++;
+ t = (float)hf->anim_step/(float)hf->anim->num_steps;
+
+ set_animation_quats(mi,t);
+ for (i=0; i<hf->anim->num; i++)
+ set_animation_geometry(mi,t,i);
+
+ if (hf->anim_step >= hf->anim->num_steps)
+ {
+ hf->anim_step = 0;
+ if (hf->anim_phase < hf->anim_phase_num-1)
+ {
+ hf->anim_phase++;
+ init_next_anim_phase(mi,hf->anim_phase);
+ }
+ else
+ {
+ set_next_anim(mi);
+ }
+ }
+ }
+
+ gltrackball_rotate(hf->trackball);
+
+ hf->num_base_points = 0;
+
+ for (i=0; i<hf->anim->num; i++)
+ {
+ if (hf->anim_geom[i].generator == GEN_TORUS)
+ gen_hopf_torus_base(mi,hf->anim_geom[i].p,hf->anim_geom[i].q,
+ hf->anim_geom[i].r,hf->anim_geom[i].n,
+ hf->anim_geom[i].offset,hf->anim_geom[i].sector,
+ hf->anim_geom[i].num,hf->anim_geom[i].rotate,
+ hf->anim_geom[i].quat_base);
+ else /* hf->anim_geom[i].generator == GEN_SPIRAL */
+ gen_hopf_spiral_base(mi,hf->anim_geom[i].p,hf->anim_geom[i].q,
+ hf->anim_geom[i].r,hf->anim_geom[i].offset,
+ hf->anim_geom[i].sector,hf->anim_geom[i].num,
+ hf->anim_geom[i].rotate,hf->anim_geom[i].quat_base);
+ }
+
+#ifdef HAVE_GLSL
+ if (hf->use_shaders)
+ mi->polygon_count = draw_hopf_circles_pf(mi);
+ else
+#endif /* HAVE_GLSL */
+ mi->polygon_count = draw_hopf_circles_ff(mi);
+}
+
+
+#ifdef HAVE_GLSL
+
+/* Compute the next highest power of 2 of v. */
+static unsigned int next_pow2(unsigned int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ v += (v == 0);
+ return v;
+}
+
+
+/* Delete the storage for the MSAA framebuffer object. */
+static void delete_msaa_fbo(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+
+ if (hf->use_msaa_fbo)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER,hf->msaa_fbo);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,0);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER,0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,hf->default_draw_fbo);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER,hf->default_read_fbo);
+ glDeleteRenderbuffers(1,&hf->msaa_rb_color);
+ glDeleteRenderbuffers(1,&hf->msaa_rb_depth);
+ glDeleteFramebuffers(1,&hf->msaa_fbo);
+ }
+}
+
+
+/* Allocate the storage for the MSAA framebuffer object. */
+static void init_msaa_fbo(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ GLenum status;
+
+ if (hf->use_msaa_fbo)
+ {
+ glGenFramebuffers(1,&hf->msaa_fbo);
+
+ glGenRenderbuffers(1,&hf->msaa_rb_color);
+ glGenRenderbuffers(1,&hf->msaa_rb_depth);
+
+ glBindRenderbuffer(GL_RENDERBUFFER,hf->msaa_rb_color);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER,hf->msaa_samples,GL_RGBA8,
+ hf->WindW,hf->WindH);
+
+ glBindRenderbuffer(GL_RENDERBUFFER,hf->msaa_rb_depth);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER,hf->msaa_samples,
+ GL_DEPTH_COMPONENT24,
+ hf->WindW,hf->WindH);
+ glBindRenderbuffer(GL_RENDERBUFFER,0);
+
+ glBindFramebuffer(GL_FRAMEBUFFER,hf->msaa_fbo);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,hf->msaa_rb_color);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER,hf->msaa_rb_depth);
+
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ delete_msaa_fbo(mi);
+ hf->use_msaa_fbo = False;
+ }
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,hf->default_draw_fbo);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER,hf->default_read_fbo);
+ }
+}
+
+
+/* Delete the storage for the shadow map framebuffer object. */
+static void delete_shadow_fbo(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+
+ if (hf->use_shadow_fbo)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER,hf->shadow_fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,
+ 0,0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,hf->default_draw_fbo);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER,hf->default_read_fbo);
+ glDeleteTextures(1,&hf->shadow_tex);
+ if (hf->shadow_fbo_req_col_tex)
+ glDeleteTextures(1,&hf->shadow_col_tex);
+ glDeleteFramebuffers(1,&hf->shadow_fbo);
+ }
+}
+
+
+/* Allocate the storage for the shadow map framebuffer object. */
+static void init_shadow_fbo(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ GLenum status;
+ GLenum draw_buffer = GL_NONE;
+
+ if (hf->use_shadow_fbo)
+ {
+ glGenFramebuffers(1,&hf->shadow_fbo);
+
+ glGenTextures(1,&hf->shadow_tex);
+
+ hf->shadow_tex_size = MIN(next_pow2(hf->WindW),next_pow2(hf->WindH));
+ hf->shadow_tex_size = MIN(hf->shadow_tex_size,hf->max_tex_size);
+ glBindTexture(GL_TEXTURE_2D,hf->shadow_tex);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE,
+ GL_COMPARE_REF_TO_TEXTURE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL);
+ glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT32F,hf->shadow_tex_size,
+ hf->shadow_tex_size,0,GL_DEPTH_COMPONENT,GL_FLOAT,NULL);
+ glBindTexture(GL_TEXTURE_2D,0);
+
+ glBindFramebuffer(GL_FRAMEBUFFER,hf->shadow_fbo);
+ glDrawBuffers(1,&draw_buffer);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,
+ hf->shadow_tex,0);
+
+ if (hf->shadow_fbo_req_col_tex)
+ {
+ draw_buffer = GL_COLOR_ATTACHMENT0;
+
+ glGenTextures(1,&hf->shadow_col_tex);
+
+ glBindTexture(GL_TEXTURE_2D,hf->shadow_col_tex);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,hf->shadow_tex_size,
+ hf->shadow_tex_size,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);
+ glBindTexture(GL_TEXTURE_2D,0);
+
+ glDrawBuffers(1,&draw_buffer);
+ glBindFramebuffer(GL_FRAMEBUFFER,hf->shadow_fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,
+ hf->shadow_col_tex,0);
+ }
+ else
+ {
+ hf->shadow_col_tex = 0;
+ }
+
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ delete_shadow_fbo(mi);
+ hf->use_shadow_fbo = False;
+ }
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER,hf->default_draw_fbo);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER,hf->default_read_fbo);
+ }
+}
+
+
+/* Initialize the programmable OpenGL functionality. */
+static void init_glsl(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+ GLint gl_major, gl_minor, glsl_major, glsl_minor;
+ GLboolean gl_gles3;
+ const GLchar *vertex_shader_source[3];
+ const GLchar *fragment_shader_source[4];
+ GLint samples, sample_buffers;
+ const char *gl_ext;
+ static float one = 1.0f;
+
+ hf->use_shaders = False;
+ hf->use_msaa_fbo = False;
+ hf->use_shadow_fbo = False;
+ hf->shader_program = 0;
+ hf->shadow_program = 0;
+
+ if (!glsl_GetGlAndGlslVersions(&gl_major,&gl_minor,&glsl_major,&glsl_minor,
+ &gl_gles3))
+ return;
+
+ if (!gl_gles3)
+ {
+ if (gl_major < 3 ||
+ (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 30)))
+ {
+ if ((gl_major < 2 || (gl_major == 2 && gl_minor < 1)) ||
+ (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 20)))
+ return;
+ /* We have at least OpenGL 2.1 and at least GLSL 1.20. */
+ vertex_shader_source[0] = shader_version_2_1;
+ vertex_shader_source[1] = vertex_shader_attribs_2_1;
+ vertex_shader_source[2] = vertex_shader_main;
+ fragment_shader_source[0] = shader_version_2_1;
+ fragment_shader_source[1] = fragment_shader_attribs_2_1;
+ fragment_shader_source[2] = fragment_shader_main;
+ fragment_shader_source[3] = fragment_shader_out_2_1;
+ }
+ else
+ {
+ /* We have at least OpenGL 3.0 and at least GLSL 1.30. */
+ vertex_shader_source[0] = shader_version_3_0;
+ vertex_shader_source[1] = vertex_shader_attribs_3_0;
+ vertex_shader_source[2] = vertex_shader_main;
+ fragment_shader_source[0] = shader_version_3_0;
+ fragment_shader_source[1] = fragment_shader_attribs_3_0;
+ fragment_shader_source[2] = fragment_shader_main;
+ fragment_shader_source[3] = fragment_shader_out_3_0;
+ }
+ }
+ else /* gl_gles3 */
+ {
+ if (gl_major < 3 || glsl_major < 3)
+ return;
+ /* We have at least OpenGL ES 3.0 and at least GLSL ES 3.0. */
+ vertex_shader_source[0] = shader_version_3_0_es;
+ vertex_shader_source[1] = vertex_shader_attribs_3_0;
+ vertex_shader_source[2] = vertex_shader_main;
+ fragment_shader_source[0] = shader_version_3_0_es;
+ fragment_shader_source[1] = fragment_shader_attribs_3_0;
+ fragment_shader_source[2] = fragment_shader_main;
+ fragment_shader_source[3] = fragment_shader_out_3_0;
+ }
+
+ if (!glsl_CompileAndLinkShaders(3,vertex_shader_source,
+ 4,fragment_shader_source,
+ &hf->shader_program))
+ return;
+
+ hf->pos_index = glGetAttribLocation(hf->shader_program,
+ "VertexPosition");
+ hf->normal_index = glGetAttribLocation(hf->shader_program,
+ "VertexNormal");
+ hf->color_index = glGetAttribLocation(hf->shader_program,
+ "VertexColor");
+ hf->mv_index = glGetUniformLocation(hf->shader_program,
+ "MatModelView");
+ hf->proj_index = glGetUniformLocation(hf->shader_program,
+ "MatProj");
+ hf->lmvp_index = glGetUniformLocation(hf->shader_program,
+ "MatLightMVP");
+ hf->glbl_ambient_index = glGetUniformLocation(hf->shader_program,
+ "LtGlblAmbient");
+ hf->lt_ambient_index = glGetUniformLocation(hf->shader_program,
+ "LtAmbient");
+ hf->lt_diffuse_index = glGetUniformLocation(hf->shader_program,
+ "LtDiffuse");
+ hf->lt_specular_index = glGetUniformLocation(hf->shader_program,
+ "LtSpecular");
+ hf->lt_direction_index = glGetUniformLocation(hf->shader_program,
+ "LtDirection");
+ hf->lt_halfvect_index = glGetUniformLocation(hf->shader_program,
+ "LtHalfVector");
+ hf->ambient_index = glGetUniformLocation(hf->shader_program,
+ "MatAmbient");
+ hf->diffuse_index = glGetUniformLocation(hf->shader_program,
+ "MatDiffuse");
+ hf->specular_index = glGetUniformLocation(hf->shader_program,
+ "MatSpecular");
+ hf->shininess_index = glGetUniformLocation(hf->shader_program,
+ "MatShininess");
+ hf->use_shadows_index = glGetUniformLocation(hf->shader_program,
+ "UseShadows");
+ hf->shadow_smpl_index = glGetUniformLocation(hf->shader_program,
+ "ShadowTex");
+ hf->shadow_pix_size_index = glGetUniformLocation(hf->shader_program,
+ "ShadowPixSize");
+ if (hf->pos_index == -1 ||
+ hf->normal_index == -1 ||
+ hf->color_index == -1 ||
+ hf->mv_index == -1 ||
+ hf->proj_index == -1 ||
+ hf->lmvp_index == -1 ||
+ hf->glbl_ambient_index == -1 ||
+ hf->lt_ambient_index == -1 ||
+ hf->lt_diffuse_index == -1 ||
+ hf->lt_specular_index == -1 ||
+ hf->lt_direction_index == -1 ||
+ hf->lt_halfvect_index == -1 ||
+ hf->ambient_index == -1 ||
+ hf->diffuse_index == -1 ||
+ hf->specular_index == -1 ||
+ hf->shininess_index == -1 ||
+ hf->use_shadows_index == -1 ||
+ hf->shadow_smpl_index == -1 ||
+ hf->shadow_pix_size_index == -1)
+ {
+ glDeleteProgram(hf->shader_program);
+ hf->shader_program = 0;
+ return;
+ }
+
+ if (!gl_gles3)
+ {
+ if (gl_major < 3 ||
+ (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 30)))
+ {
+ if ((gl_major < 2 || (gl_major == 2 && gl_minor < 1)) ||
+ (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 20)))
+ return;
+ /* We have at least OpenGL 2.1 and at least GLSL 1.20. */
+ vertex_shader_source[0] = shader_version_2_1;
+ vertex_shader_source[1] = shadow_vertex_shader_attribs_2_1;
+ vertex_shader_source[2] = shadow_vertex_shader_main;
+ fragment_shader_source[0] = shader_version_2_1;
+ fragment_shader_source[1] = shadow_fragment_shader_main;
+ }
+ else
+ {
+ /* We have at least OpenGL 3.0 and at least GLSL 1.30. */
+ vertex_shader_source[0] = shader_version_3_0;
+ vertex_shader_source[1] = shadow_vertex_shader_attribs_3_0;
+ vertex_shader_source[2] = shadow_vertex_shader_main;
+ fragment_shader_source[0] = shader_version_3_0;
+ fragment_shader_source[1] = shadow_fragment_shader_main;
+ }
+ }
+ else /* gl_gles3 */
+ {
+ if (gl_major < 3 || glsl_major < 3)
+ return;
+ /* We have at least OpenGL ES 3.0 and at least GLSL ES 3.0. */
+ vertex_shader_source[0] = shader_version_3_0_es;
+ vertex_shader_source[1] = shadow_vertex_shader_attribs_3_0;
+ vertex_shader_source[2] = shadow_vertex_shader_main;
+ fragment_shader_source[0] = shader_version_3_0_es;
+ fragment_shader_source[1] = shadow_fragment_shader_main;
+ }
+
+ if (!glsl_CompileAndLinkShaders(3,vertex_shader_source,
+ 2,fragment_shader_source,
+ &hf->shadow_program))
+ {
+ glDeleteProgram(hf->shader_program);
+ hf->shader_program = 0;
+ return;
+ }
+
+ hf->use_shadow_fbo = True;
+ hf->shadow_pos_index = glGetAttribLocation(hf->shadow_program,
+ "VertexPosition");
+ hf->shadow_lmvp_index = glGetUniformLocation(hf->shadow_program,
+ "MatLightMVP");
+ if (hf->shadow_pos_index == -1 ||
+ hf->shadow_lmvp_index == -1)
+ {
+ glDeleteProgram(hf->shader_program);
+ glDeleteProgram(hf->shadow_program);
+ hf->shader_program = 0;
+ hf->shadow_program = 0;
+ hf->use_shadow_fbo = False;
+ }
+
+ hf->fiber_pos_buffer = calloc(hf->max_base_points,
+ sizeof(*hf->fiber_pos_buffer));
+ hf->fiber_normal_buffer = calloc(hf->max_base_points,
+ sizeof(*hf->fiber_normal_buffer));
+ hf->fiber_indices_buffer = calloc(hf->max_base_points,
+ sizeof(*hf->fiber_indices_buffer));
+ hf->fiber_num_tri = calloc(hf->max_base_points,sizeof(*hf->fiber_num_tri));
+ if (hf->fiber_pos_buffer == NULL || hf->fiber_normal_buffer == NULL ||
+ hf->fiber_indices_buffer == NULL || hf->fiber_num_tri == NULL)
+ abort();
+ glGenBuffers(hf->max_base_points,hf->fiber_pos_buffer);
+ glGenBuffers(hf->max_base_points,hf->fiber_normal_buffer);
+ glGenBuffers(hf->max_base_points,hf->fiber_indices_buffer);
+ glGenBuffers(1,&hf->sphere_base_pos_buffer);
+ glGenBuffers(1,&hf->sphere_base_normal_buffer);
+ glGenBuffers(1,&hf->sphere_base_indices_buffer);
+ glGenBuffers(1,&hf->base_point_pos_buffer);
+ glGenBuffers(1,&hf->base_point_normal_buffer);
+ glGenBuffers(1,&hf->base_point_indices_buffer);
+
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING,(GLint *)&hf->default_draw_fbo);
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING,(GLint *)&hf->default_read_fbo);
+
+ glGetIntegerv(GL_SAMPLES,&samples);
+ glGetIntegerv(GL_SAMPLE_BUFFERS,&sample_buffers);
+ hf->use_msaa_fbo = (hf->anti_aliasing &&
+ (sample_buffers == 0 || samples == 0));
+ if (!gl_gles3 && gl_major < 3 && hf->use_msaa_fbo)
+ {
+ gl_ext = (const char *)glGetString(GL_EXTENSIONS);
+ if (gl_ext == NULL)
+ hf->use_msaa_fbo = False;
+ else
+ hf->use_msaa_fbo =
+ (strstr(gl_ext,"GL_EXT_framebuffer_object") != NULL &&
+ strstr(gl_ext,"GL_EXT_framebuffer_blit") != NULL &&
+ strstr(gl_ext,"GL_EXT_framebuffer_multisample") != NULL);
+ }
+
+ if (hf->use_msaa_fbo)
+ {
+ glEnable(GL_MULTISAMPLE);
+ glGetIntegerv(GL_MAX_SAMPLES,&hf->msaa_samples);
+ hf->msaa_samples = MIN(hf->msaa_samples,MSAA_SAMPLES);
+ init_msaa_fbo(mi);
+ }
+
+ hf->shadow_fbo_req_col_tex = (!gl_gles3 && gl_major < 3);
+ if (!gl_gles3 && gl_major < 3 && hf->use_shadow_fbo)
+ {
+ gl_ext = (const char *)glGetString(GL_EXTENSIONS);
+ if (gl_ext == NULL)
+ hf->use_shadow_fbo = GL_FALSE;
+ else
+ hf->use_shadow_fbo =
+ (strstr(gl_ext,"GL_ARB_shadow") != NULL &&
+ strstr(gl_ext,"GL_ARB_depth_buffer_float") != NULL);
+ }
+
+ if (hf->use_shadow_fbo)
+ {
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE,&hf->max_tex_size);
+ init_shadow_fbo(mi);
+ }
+
+ glGenTextures(1,&hf->shadow_dummy_tex);
+ glBindTexture(GL_TEXTURE_2D,hf->shadow_dummy_tex);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE,
+ GL_COMPARE_REF_TO_TEXTURE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL);
+ glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT32F,1,1,0,
+ GL_DEPTH_COMPONENT,GL_FLOAT,&one);
+ glBindTexture(GL_TEXTURE_2D,0);
+
+ hf->vert = calloc(3*MAX_CIRCLE_PNT*hf->num_tube,sizeof(*hf->vert));
+ hf->norm = calloc(3*MAX_CIRCLE_PNT*hf->num_tube,sizeof(*hf->norm));
+ hf->tri = calloc(2*3*MAX_CIRCLE_PNT*hf->num_tube,sizeof(*hf->tri));
+
+ hf->use_shaders = True;
+}
+
+#endif /* HAVE_GLSL */
+
+
+static int get_max_base_points(void)
+{
+ int i, j, k, l, m, n, max_base_points;
+ animations *anims;
+ animation_phases *anim_ps;
+ animation_multi_obj *anim_mo;
+
+ max_base_points = 0;
+ for (i=0; i<NUM_ANIM_STATES; i++)
+ {
+ for (j=0; j<NUM_ANIM_STATES; j++)
+ {
+ anims = hopf_animations[i][j];
+ for (k=0; k<anims->num_anim; k++)
+ {
+ anim_ps = anims->anim[k];
+ for (l=0; l<anim_ps->num_phases; l++)
+ {
+ anim_mo = anim_ps->anim_mo[l];
+ n = 0;
+ for (m=0; m<anim_mo->num; m++)
+ n += anim_mo->anim_so[m].num;
+ if (n > max_base_points)
+ max_base_points = n;
+ }
+ }
+ }
+ }
+ return max_base_points;
+}
+
+
+static void init(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+
+ if (hf->details == DISP_DETAILS_COARSE)
+ {
+ hf->num_tube = NUM_TUBE_COARSE;
+ hf->base_sphere_s = BASE_SPHERE_S_COARSE;
+ hf->base_point_s = BASE_POINT_S_COARSE;
+ hf->max_circle_dist = MAX_CIRCLE_DIST_COARSE;
+ }
+ else if (hf->details == DISP_DETAILS_FINE)
+ {
+ hf->num_tube = NUM_TUBE_FINE;
+ hf->base_sphere_s = BASE_SPHERE_S_FINE;
+ hf->base_point_s = BASE_POINT_S_FINE;
+ hf->max_circle_dist = MAX_CIRCLE_DIST_FINE;
+ }
+ else /* hf->details == DISP_DETAILS_MEDIUM) */
+ {
+ hf->num_tube = NUM_TUBE_MEDIUM;
+ hf->base_sphere_s = BASE_SPHERE_S_MEDIUM;
+ hf->base_point_s = BASE_POINT_S_MEDIUM;
+ hf->max_circle_dist = MAX_CIRCLE_DIST_MEDIUM;
+ }
+
+ hf->alpha = 290.0f;
+ hf->beta = 0.0f;
+ hf->delta = 270.0f;
+
+ hf->zeta = 0.0f;
+ hf->eta = 0.0f;
+ hf->theta = 0.0f;
+
+ hf->rot_axis_base[0] = 1.0f;
+ hf->rot_axis_base[1] = 0.0f;
+ hf->rot_axis_base[2] = 0.0f;
+ hf->quat_base[0] = 1.0f;
+ hf->quat_base[1] = 0.0f;
+ hf->quat_base[2] = 0.0f;
+ hf->quat_base[3] = 0.0f;
+
+
+ hf->rot_axis_space[0] = 1.0f;
+ hf->rot_axis_space[1] = 0.0f;
+ hf->rot_axis_space[2] = 0.0f;
+ hf->quat_space[0] = 1.0f;
+ hf->quat_space[1] = 0.0f;
+ hf->quat_space[2] = 0.0f;
+ hf->quat_space[3] = 0.0f;
+
+ hf->angle_space_start = 0.0f;
+ hf->angle_space_end = 2.0f*M_PI_F;
+
+ hf->anim = NULL;
+ hf->anim_state = (int)floor(frand(NUM_ANIM_STATES));
+ hf->anim_remain_in_state = 1;
+ hf->anim_step = 0;
+ hf->anim_phases = NULL;
+ hf->anim_phase_num = 0;
+ hf->anim_phase = 0;
+ hf->anim_rotate_rnd = False;
+ hf->anim_rotate_space = False;
+ hf->anim_easing_fct_rot_rnd = EASING_NONE;
+ hf->anim_easing_fct_rot_space = EASING_NONE;
+ set_next_anim(mi);
+
+ hf->max_base_points = get_max_base_points();
+ hf->base_points = calloc(hf->max_base_points,sizeof(*hf->base_points));
+ if (hf->base_points == NULL)
+ abort();
+ hf->num_base_points = 0;
+
+ hf->sphere_base = gen_icosphere_data(&icosa,hf->base_sphere_s,
+ BASE_SPHERE_RADIUS);
+ hf->sphere_base_point = gen_icosphere_data(&icosa,hf->base_point_s,
+ BASE_POINT_RADIUS);
+ if (hf->sphere_base == NULL || hf->sphere_base_point == NULL)
+ abort();
+
+#ifdef HAVE_GLSL
+ init_glsl(mi);
+#endif /* HAVE_GLSL */
+}
+
+
+ENTRYPOINT void reshape_hopffibration(ModeInfo *mi, int width, int height)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+
+ hf->WindW = (GLint)width;
+ hf->WindH = (GLint)height;
+ hf->aspect = (GLfloat)width/(GLfloat)height;
+#ifdef HAVE_GLSL
+ delete_msaa_fbo(mi);
+ init_msaa_fbo(mi);
+ delete_shadow_fbo(mi);
+ init_shadow_fbo(mi);
+#endif /* HAVE_GLSL */
+}
+
+
+ENTRYPOINT Bool hopffibration_handle_event(ModeInfo *mi, XEvent *event)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+
+ if (event->xany.type == ButtonPress &&
+ event->xbutton.button == Button1)
+ {
+ hf->button_pressed = True;
+ gltrackball_start(hf->trackball,event->xbutton.x,event->xbutton.y,
+ MI_WIDTH(mi),MI_HEIGHT(mi));
+ return True;
+ }
+ else if (event->xany.type == ButtonRelease &&
+ event->xbutton.button == Button1)
+ {
+ hf->button_pressed = False;
+ gltrackball_stop(hf->trackball);
+ return True;
+ }
+ else if (event->xany.type == MotionNotify && hf->button_pressed)
+ {
+ gltrackball_track(hf->trackball,event->xmotion.x,event->xmotion.y,
+ MI_WIDTH(mi),MI_HEIGHT(mi));
+ return True;
+ }
+
+ return False;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ * Xlock hooks.
+ *-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ */
+
+/*
+ *-----------------------------------------------------------------------------
+ * Initialize hopffibration. Called each time the window changes.
+ *-----------------------------------------------------------------------------
+ */
+
+ENTRYPOINT void init_hopffibration(ModeInfo *mi)
+{
+ hopffibrationstruct *hf;
+
+ MI_INIT(mi,hopffibration);
+ hf = &hopffibration[MI_SCREEN(mi)];
+
+ hf->shadows = shadows;
+
+ hf->base_space = base_space;
+
+ hf->anti_aliasing = anti_aliasing;
+
+ /* Set the projection mode. */
+ if (!strcasecmp(proj,"perspective"))
+ {
+ hf->projection = DISP_PERSPECTIVE;
+ }
+ else if (!strcasecmp(proj,"orthographic"))
+ {
+ hf->projection = DISP_ORTHOGRAPHIC;
+ }
+ else
+ {
+ hf->projection = DISP_PERSPECTIVE;
+ }
+
+ /* Set the detail level. */
+ if (!strcasecmp(det,"coarse"))
+ {
+ hf->details = DISP_DETAILS_COARSE;
+ }
+ else if (!strcasecmp(det,"fine"))
+ {
+ hf->details = DISP_DETAILS_FINE;
+ }
+ else
+ {
+ hf->details = DISP_DETAILS_MEDIUM;
+ }
+
+ hf->trackball = gltrackball_init(False);
+ hf->button_pressed = False;
+
+ if ((hf->glx_context = init_GL(mi)) != NULL)
+ {
+ reshape_hopffibration(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
+ init(mi);
+ }
+ else
+ {
+ MI_CLEARWINDOW(mi);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Called by the mainline code periodically to update the display.
+ *-----------------------------------------------------------------------------
+ */
+ENTRYPOINT void draw_hopffibration(ModeInfo *mi)
+{
+ Display *display = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ hopffibrationstruct *hf;
+
+ if (hopffibration == NULL)
+ return;
+ hf = &hopffibration[MI_SCREEN(mi)];
+
+ MI_IS_DRAWN(mi) = True;
+ if (!hf->glx_context)
+ return;
+
+ glXMakeCurrent(display,window,*hf->glx_context);
+
+ display_hopffibration(mi);
+
+ if (MI_IS_FPS(mi))
+ do_fps (mi);
+
+ glFlush();
+
+ glXSwapBuffers(display,window);
+}
+
+
+#ifndef STANDALONE
+ENTRYPOINT void change_hopffibration(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+
+ if (!hf->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*hf->glx_context);
+ init(mi);
+}
+#endif /* !STANDALONE */
+
+
+ENTRYPOINT void free_hopffibration(ModeInfo *mi)
+{
+ hopffibrationstruct *hf = &hopffibration[MI_SCREEN(mi)];
+
+ if (!hf->glx_context) return;
+ glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*hf->glx_context);
+
+ gltrackball_free(hf->trackball);
+
+ free(hf->base_points);
+ free_icosphere_data(hf->sphere_base_point);
+ free_icosphere_data(hf->sphere_base);
+
+#ifdef HAVE_GLSL
+ if (hf->use_shaders)
+ {
+ glUseProgram(0);
+ if (hf->shader_program != 0)
+ glDeleteProgram(hf->shader_program);
+ if (hf->shadow_program != 0)
+ glDeleteProgram(hf->shadow_program);
+ glDeleteBuffers(1,&hf->base_point_pos_buffer);
+ glDeleteBuffers(1,&hf->base_point_normal_buffer);
+ glDeleteBuffers(1,&hf->base_point_indices_buffer);
+ glDeleteBuffers(1,&hf->sphere_base_pos_buffer);
+ glDeleteBuffers(1,&hf->sphere_base_normal_buffer);
+ glDeleteBuffers(1,&hf->sphere_base_indices_buffer);
+ glDeleteBuffers(hf->max_base_points,hf->fiber_pos_buffer);
+ glDeleteBuffers(hf->max_base_points,hf->fiber_normal_buffer);
+ glDeleteBuffers(hf->max_base_points,hf->fiber_indices_buffer);
+ glDeleteTextures(1,&hf->shadow_dummy_tex);
+ delete_msaa_fbo(mi);
+ delete_shadow_fbo(mi);
+ free(hf->fiber_pos_buffer);
+ free(hf->fiber_normal_buffer);
+ free(hf->fiber_indices_buffer);
+ free(hf->fiber_num_tri);
+ free(hf->vert);
+ free(hf->norm);
+ free(hf->tri);
+ }
+#endif /* HAVE_GLSL */
+}
+
+
+XSCREENSAVER_MODULE ("HopfFibration", hopffibration)
+
+#endif /* USE_GL */
--- /dev/null
+.TH XScreenSaver 1 "" "X Version 11"
+.SH NAME
+hopffibration \- Draws the Hopf fibration of the 4d hypersphere
+.SH SYNOPSIS
+.B hopffibration
+[\-\-display \fIhost:display.screen\fP]
+[\-\-install]
+[\-\-visual \fIvisual\fP]
+[\-\-window]
+[\-\-root]
+[\-\-window\-id \fInumber\fP]
+[\-\-delay \fIusecs\fP]
+[\-\-fps]
+[\-\-shadows]
+[\-\-details coarse | medium | fine]
+[\-\-base-space]
+[\-\-anti-aliasing]
+[\-\-perspective]
+[\-\-orthographic]
+.SH DESCRIPTION
+The \fIhopffibration\fP program shows the Hopf fibration of the 4d
+hypersphere. The Hopf fibration is based on the Hopf map, a
+many-to-one continuous function from the 4d hypersphere (the 3-sphere)
+onto the the ordinary 3d sphere (the 2-sphere) such that each distinct
+point of the 2-sphere is mapped from a distinct great circle (a
+1-sphere) of the 3-sphere. Hence, the inverse image of a point on the
+2-sphere corresponds to a great circle on the 3-sphere. The 2-sphere
+is called the base space, each circle corresponding to a point on the
+2-sphere is called a fiber, and the 3-sphere is called the total
+space.
+.PP
+The program displays the base space (the 2-sphere) as a
+semi-transparent gray sphere in the bottom right corner of the
+display. The points on the base space are displayed as small colored
+spheres. The fibers in the total space are displayed in the same
+color as the corresponding points on the base space.
+.PP
+The fibers in the total space are projected from 4d to 3d using
+stereographic projection and then compressing the infinite 3d space to
+a finite 3d ball to display the fibers compactly. All fibers except
+one fiber that passes through the north pole of the 3-sphere are thus
+projected to deformed circles in 3d. The program displays these
+deformed circles as closed tubes (topological tori). The single fiber
+that passes through the north pole of the 3-sphere is projected to an
+infinite line by the stereographic projection. This line passes
+through infinity in 3d and therefore topologically is a circle.
+Compressing this infinite line to a finite ball maps it to a straight
+line segment. The program displays this line segment as a cylinder.
+However, it should be thought of as a circle through infinity.
+.PP
+The fibers, base space, and base points are then projected to the
+screen either perspectively or orthographically.
+.PP
+The program displays various interesting configurations of base points
+and fibers. Look out for the following configurations:
+.nr PI 2n
+.IP \[bu]
+Any two fibers form a Hopf link.
+.IP \[bu]
+More generally, each fiber is linked with each other fiber exactly
+once.
+.IP \[bu]
+Each circle on the 2-sphere creates a set of fibers that forms a
+Clifford torus on the 3-sphere (i.e., in 4d). Clifford tori are flat
+(in the same sense that the surface of a cylinder is flat).
+.IP \[bu]
+If a circle on the 2-sphere is not a circle of latitude, the
+projection of the Clifford torus to 3d results in a (compressed) Dupin
+cyclide.
+.IP \[bu]
+More generally, any closed curve on the 2-sphere creates a torus-like
+surface on the 3-sphere that is flat. These surfaces are called Hopf
+tori or Bianchi-Pinkall flat tori. Look for the wave-like curve on
+the 2-sphere to see a Hopf torus.
+.IP \[bu]
+A circular arc on the 2-sphere creates a Hopf band on the 3-sphere.
+The Hopf band is a Seifert surface of the Hopf link that forms the
+boundaries of the Hopf band.
+.IP \[bu]
+Two or more circles of latitude on the 2-sphere create two or more
+nested Clifford tori on the 3-sphere.
+.IP \[bu]
+More generally, two or more disjoint circles on the 2-sphere create
+two or more linked Clifford tori on the 3-sphere.
+.IP \[bu]
+A great circle through the north pole of the 2-sphere creates a
+parabolic ring cyclide (which is compressed to lie within the ball in
+the 3d projection). A parabolic ring cyclide divides the entire 3d
+space into two congruent parts that are interlocked, i.e., linked.
+.IP \[bu]
+By turning a circle on the 2-sphere so that it passes through the
+north pole of the 2-sphere, the projection of the corresponding
+Clifford torus reverses its inside and outside in 3d.
+.IP \[bu]
+The Clifford torus corresponding to a great circle on the 2-sphere
+divides the 3-sphere into two congruent solid tori that fill the
+entire 3-sphere. The two solid tori on the 3-sphere correspond to the
+two hemispheres into which the great circle divides the 2-sphere. The
+solid tori in the 3-sphere are attached to each other at the Clifford
+torus. The congruence of the solid tori is visible in a particularly
+striking manner if the great circle that creates the Clifford torus is
+rotated so that it passes through the north pole of the 2-sphere,
+thereby creating a parabolic ring cyclide via the projection of the
+Clifford torus to 3d (see above).
+.PP
+During the animations, two kinds of motions are used. Usually, the
+points on the base space are moved or rotated to particular
+configurations. This is apparent by the small spheres that represent
+the base points changing their position on the base space, which leads
+to a corresponding change of the configuration of the fibers. The
+base space itself, however, is not moved or rotated, i.e., its
+orientation remains fixed. Sometimes, only the projection of the
+fibers is rotated in 3d to show some interesting configurations more
+clearly, e.g., that a Hopf torus has a hole like a regular torus. In
+this case, the base space also maintains its orientation in space.
+Since a rotation in 3d does not change the configuration of the
+fibers, in this kind of animation, the points on the base space also
+remain fixed. Sometimes, both types of animations are combined, e.g.,
+when the projection of one or more Clifford tori is rotated in 3d
+while the base points of the Clifford tori also rotate on the base
+space. In this case, the base space will only show the movement of
+the base points on the base space and not the 3d rotation of the
+projection of the fibers.
+.PP
+To enhance the 3d depth impression, the program displays the shadows
+of the fibers and base points by default. This is done by way of a
+two-pass rendering algorithm in which the geometry is rendered twice.
+Depending on the speed of the GPU, displaying shadows might slow down
+the rendering significantly. If this is the case, the rendering of
+shadows can be switched off, saving one render pass and thus speeding
+up the rendering.
+.PP
+Some of the animations render complex geometries with a very large
+number of polygons. This can cause the rendering to become slow on
+some types of GPU. To speed up the rendering process, the amount of
+details that are rendered can be controlled in three granularities
+(coarse, medium, and fine). Devices with relatively small screens and
+relatively low-powered GPUs, such as phones or tablets, should
+typically select coarse details. Standard GPUs should select medium
+details (the default). High-powered GPUs on large screens may benefit
+from fine details.
+.PP
+By default, the base space and base points are displayed as described
+above. If desired, the display of the base space and base points can
+be switched off so that only the fibers are displayed.
+.PP
+During the animation of the Hopf fibration, sometimes multiple fibers
+that are very close to each other are displayed. This can create
+disturbing aliasing artifacts that are especially noticeable when the
+fibers are moving or turning slowly. Therefore, by default, the
+rendering is performed using anti-aliasing. This typically has a
+negligible effect on the rendering speed. However, if shadows have
+already been switched off, coarse details have been selected, and the
+rendering is still slow, anti-aliasing also can be switched off to
+check whether it has a noticeable effect on the rendering speed.
+.PP
+This program was inspired by Niles Johnson's visualization of the Hopf
+fibration (https://nilesjohnson.net/hopf.html).
+.SH OPTIONS
+.I hopffibration
+accepts the following options:
+.TP 8
+.B \-\-window
+Draw on a newly-created window. This is the default.
+.TP 8
+.B \-\-root
+Draw on the root window.
+.TP 8
+.B \-\-window\-id \fInumber\fP
+Draw on the specified window.
+.TP 8
+.B \-\-install
+Install a private colormap for the window.
+.TP 8
+.B \-\-visual \fIvisual\fP
+Specify which visual to use. Legal values are the name of a visual
+class, or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-\-delay \fImicroseconds\fP
+How much of a delay should be introduced between steps of the
+animation. Default 20000, or 1/50th second.
+.PP
+The following options determine whether shadows are displayed.
+.TP 8
+.B \-\-shadows
+Display the fibers, base space, and base points with shadows
+(default).
+.TP 8
+.B \-\-no-shadows
+Display the fibers, base space, and base points without shadows.
+.PP
+The following three options are mutually exclusive. They determine
+with what level of detail the fibers, base space, and base points are
+rendered.
+.TP 8
+.B \-\-details coarse
+Render the fibers, base space, and base points with a level of detail
+that is suitable for low-powered GPUs and small screens, e.g., phones
+or tablets.
+.TP 8
+.B \-\-details medium
+Render the fibers, base space, and base points with a level of detail
+that is suitable for regular GPUs (default).
+.TP 8
+.B \-\-details fine
+Render the fibers, base space, and base points with a level of detail
+that is suitable for high-powered GPUs and large screens.
+.PP
+The following options determine whether the base space and base points
+are displayed.
+.TP 8
+.B \-\-base-space
+Display the base space and base points (default).
+.TP 8
+.B \-\-no-base-space
+Do not display the base space and base points.
+.PP
+The following options determine whether anti-aliasing is used to
+display the fibers, base space, and base points.
+.TP 8
+.B \-\-anti-aliasing
+Display the fibers, base space, and base points with anti-aliasing
+(default).
+.TP 8
+.B \-\-no-anti-aliasing
+Display the fibers, base space, and base points without anti-aliasing.
+.PP
+The following two options are mutually exclusive. They determine how
+the fibers, base space, and base points are projected from 3d to 2d
+(i.e., to the screen).
+.TP 8
+.B \-\-perspective
+Project the fibers, base space, and base points from 3d to 2d using a
+perspective projection (default).
+.TP 8
+.B \-\-orthographic
+Project the fibers, base space, and base points from 3d to 2d using a
+orthographic projection.
+.TP 8
+.B \-\-fps
+Display the current frame rate, CPU load, and polygon count.
+.SH INTERACTION
+If you run this program in standalone mode, you can rotate the fibers
+by dragging the mouse while pressing the left mouse button.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.TP 8
+.B XSCREENSAVER_WINDOW
+The window ID to use with \fI\-\-root\fP.
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1),
+.BR hypertorus (1)
+.SH FURTHER INFORMATION
+.nr PI 2n
+.IP \[bu]
+https://en.wikipedia.org/wiki/Hopf_fibration
+.IP \[bu]
+https://en.wikipedia.org/wiki/Hopf_link
+.IP \[bu]
+https://en.wikipedia.org/wiki/Clifford_torus
+.IP \[bu]
+https://en.wikipedia.org/wiki/Seifert_surface
+.IP \[bu]
+https://en.wikipedia.org/wiki/Dupin_cyclide
+.IP \[bu]
+https://en.wikipedia.org/wiki/3-sphere
+.SH COPYRIGHT
+Copyright \(co 2025 by Carsten Steger. Permission to use, copy,
+modify, distribute, and sell this software and its documentation for
+any purpose is hereby granted without fee, provided that the above
+copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation.
+No representations are made about the suitability of this software for
+any purpose. It is provided "as is" without express or implied
+warranty.
+.SH AUTHOR
+Carsten Steger <carsten@mirsanmir.org>, 06-feb-2025.
--- /dev/null
+/* klondike, Copyright (c) 2024 Joshua Timmons <josh@developerx.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "xlockmore.h"
+#include <ctype.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "gltrackball.h"
+#include "klondike-game.h"
+
+// random position offset for sloppy mode
+#define RANDOM_POSITION_OFFSET (bp->sloppy ? (((float)random()) / ((float)RAND_MAX) - 0.5) * 0.0125 : 0)
+
+// static const char *suits[] = {"Diamonds", "Clubs", "Hearts", "Spades"};
+// static const char *short_suits[] = {"D", "C", "H", "S"};
+// static const char *ranks[] = {"", "Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"};
+// static const char *short_ranks[] = {"", "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K"};
+
+static void remove_card_from_deck(game_state_struct *, card_struct *);
+
+// initialize the deck
+void klondike_initialize_deck(klondike_configuration *bp)
+{
+ card_struct *deck = bp->game_state->deck;
+ int index = 0;
+
+ for (int suit = 0; suit < NUM_SUITS; suit++)
+ {
+ for (int rank = ACE; rank <= KING; rank++)
+ {
+
+ deck[index].suit = (Suit)suit;
+ deck[index].rank = (Rank)rank;
+ deck[index].is_face_up = 0;
+ deck[index].x = bp->deck_x;
+ deck[index].y = bp->deck_y;
+ deck[index].start_x = bp->deck_x;
+ deck[index].start_y = bp->deck_y;
+ deck[index].dest_x = bp->deck_x;
+ deck[index].dest_y = bp->deck_y;
+ deck[index].start_frame = 0;
+ deck[index].end_frame = 0;
+ deck[index].start_angle = 0.0f;
+ deck[index].end_angle = 0.0f;
+ deck[index].angle = 0.0f;
+ deck[index].z = 0.0f;
+ deck[index].start_z = 0.0f;
+ index++;
+ }
+ }
+}
+
+// shuffle the deck
+void klondike_shuffle_deck(card_struct deck[])
+{
+ for (int i = NUM_CARDS - 1; i > 0; i--)
+ {
+ int j = random() % (i + 1);
+ card_struct temp = deck[i];
+ deck[i] = deck[j];
+ deck[j] = temp;
+ }
+}
+
+// deal the cards to the tableaus
+void klondike_deal_cards(klondike_configuration *bp)
+{
+ game_state_struct *game_state = bp->game_state;
+
+ for (int i = 0; i < 7; i++)
+ {
+ game_state->tableau_size[i] = 0;
+
+ for (int j = 0; j <= i; j++)
+ {
+ game_state->tableau[i][j] = game_state->deck[0];
+ remove_card_from_deck(game_state, &game_state->tableau[i][j]);
+ if (j == i)
+ {
+ game_state->tableau[i][j].is_face_up = 1; // The top card of each pile is face up
+ }
+ game_state->tableau_size[i]++;
+ }
+ }
+
+ game_state->waste_size = 0;
+ game_state->foundation_size[CLUBS] = 0;
+ game_state->foundation_size[DIAMONDS] = 0;
+ game_state->foundation_size[HEARTS] = 0;
+ game_state->foundation_size[SPADES] = 0;
+ game_state->moves = 0;
+ game_state->moves_since_waste_flip = 0;
+}
+
+// clone the game state
+static game_state_struct *clone_game_state(game_state_struct *game_state)
+{
+ game_state_struct *newgame_state = (game_state_struct *)malloc(sizeof(game_state_struct));
+ for (int i = 0; i < NUM_CARDS; i++)
+ {
+ newgame_state->deck[i] = game_state->deck[i];
+ }
+ for (int i = 0; i < 7; i++)
+ {
+ for (int j = 0; j < 20; j++)
+ {
+ newgame_state->tableau[i][j] = game_state->tableau[i][j];
+ }
+ newgame_state->tableau_size[i] = game_state->tableau_size[i];
+ }
+ for (int i = 0; i < MAX_WASTE; i++)
+ {
+ newgame_state->waste[i] = game_state->waste[i];
+ }
+ newgame_state->waste_size = game_state->waste_size;
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < MAX_FOUNDATION; j++)
+ {
+ newgame_state->foundation[i][j] = game_state->foundation[i][j];
+ }
+ newgame_state->foundation_size[i] = game_state->foundation_size[i];
+ }
+
+ newgame_state->moves = game_state->moves;
+ newgame_state->moves_since_waste_flip = game_state->moves_since_waste_flip;
+
+ return newgame_state;
+}
+
+// free the game state
+void klondike_free_game_state(game_state_struct *game_state)
+{
+ free(game_state);
+}
+
+// get the number of cards remaining in the deck by subtracting the sum of the tableau, foundations, and waste size from 52
+int klondike_deck_size(game_state_struct *game_state)
+{
+ // count the cards in the tableau
+ int tableauSize = 0;
+ for (int i = 0; i < 7; i++)
+ {
+ tableauSize += game_state->tableau_size[i];
+ }
+
+ // count the cards in the foundation
+ int foundation_size = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ foundation_size += game_state->foundation_size[i];
+ }
+
+ // count the cards in the waste
+ int waste_size = game_state->waste_size;
+ int ret = 52 - tableauSize - foundation_size - waste_size;
+
+ return ret;
+}
+
+// reset the waste pile
+static void reset_waste(klondike_configuration *bp, game_state_struct *game_state)
+{
+ for (int i = 0; i < game_state->waste_size; i++)
+ {
+ game_state->deck[i] = game_state->waste[i];
+
+ card_struct *animated_card = &game_state->deck[i];
+ animated_card->start_frame = bp->tick + (i+5) * bp->animation_ticks / 10;
+ animated_card->end_frame = bp->tick + (i+5) * bp->animation_ticks / 10 + bp->animation_ticks;
+ animated_card->start_x = animated_card->x;
+ animated_card->start_y = animated_card->y;
+ animated_card->dest_x = bp->deck_x + RANDOM_POSITION_OFFSET;
+ animated_card->dest_y = bp->deck_y + RANDOM_POSITION_OFFSET;
+ animated_card->start_angle = 180.0f;
+ animated_card->end_angle = 360.0f;
+ animated_card->start_z = animated_card->z;
+
+ game_state->waste[i].rank = NONE;
+ game_state->waste[i].suit = 0;
+ game_state->waste[i].is_face_up = 0;
+ }
+
+ game_state->waste_size = 0;
+ game_state->moves_since_waste_flip = 0;
+}
+
+// remove a card from the deck and move the subsequent cards up
+static void remove_card_from_deck(game_state_struct *state, card_struct *card)
+{
+ for (int i = 0; i < NUM_CARDS; i++)
+ {
+ if (state->deck[i].suit == card->suit && state->deck[i].rank == card->rank)
+ {
+ for (int j = i; j < NUM_CARDS - 1; j++)
+ {
+ state->deck[j] = state->deck[j + 1];
+ }
+
+ state->deck[NUM_CARDS - 1].rank = 0;
+ state->deck[NUM_CARDS - 1].suit = 0;
+ state->deck[NUM_CARDS - 1].is_face_up = 0;
+ break;
+ }
+ }
+}
+
+// find the card in either the tableau or the waste pile
+// and move it to the foundation
+static game_state_struct *move_card_to_foundation(klondike_configuration *bp, game_state_struct *game_state, card_struct *card)
+{
+ // create a new game_state clone
+ game_state_struct *ret = clone_game_state(game_state);
+
+ for (int i = 0; i < 7; i++)
+ {
+ int j = ret->tableau_size[i] - 1;
+ if (ret->tableau[i][j].rank == (card->rank) && ret->tableau[i][j].suit == card->suit && card->is_face_up)
+ {
+ if (card->rank == ACE || ret->foundation[card->suit][ret->foundation_size[card->suit] - 1].rank == card->rank - 1)
+ {
+ ret->foundation[card->suit][ret->foundation_size[card->suit]] = game_state->tableau[i][j];
+ ret->tableau_size[i]--;
+ for (int k = j; k < ret->tableau_size[i]; k++)
+ {
+ ret->tableau[i][k] = ret->tableau[i][k + 1];
+ }
+
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_frame = bp->tick;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].end_frame = bp->tick + bp->animation_ticks;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_x = ret->foundation[card->suit][ret->foundation_size[card->suit]].x;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_y = ret->foundation[card->suit][ret->foundation_size[card->suit]].y;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].dest_x = bp->foundation_placeholders[card->suit].x;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].dest_y = bp->foundation_placeholders[card->suit].y;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_angle = ret->foundation[card->suit][ret->foundation_size[card->suit]].end_angle;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_z = ret->foundation[card->suit][ret->foundation_size[card->suit]].z + 3;
+
+ ret->foundation_size[card->suit]++;
+
+ //("Moved %s of %s from tableau to foundation\n", ranks[card->rank], suits[card->suit]);
+ return ret;
+ }
+ }
+ }
+
+ if (ret->waste[ret->waste_size - 1].rank == card->rank && ret->waste[ret->waste_size - 1].suit == card->suit && card->is_face_up)
+ {
+ if (card->rank == ACE || ret->foundation[card->suit][ret->foundation_size[card->suit] - 1].rank == card->rank - 1)
+ {
+ ret->foundation[card->suit][ret->foundation_size[card->suit]] = game_state->waste[ret->waste_size - 1];
+
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_frame = bp->tick;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].end_frame = bp->tick + bp->animation_ticks;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_x = ret->foundation[card->suit][ret->foundation_size[card->suit]].x;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_y = ret->foundation[card->suit][ret->foundation_size[card->suit]].y;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].dest_x = bp->foundation_placeholders[card->suit].x;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].dest_y = bp->foundation_placeholders[card->suit].y;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_angle = ret->foundation[card->suit][ret->foundation_size[card->suit]].end_angle;
+ ret->foundation[card->suit][ret->foundation_size[card->suit]].start_z = ret->foundation[card->suit][ret->foundation_size[card->suit]].z + 3;
+
+ ret->foundation_size[card->suit]++;
+ ret->waste_size--;
+
+ return ret;
+ }
+ }
+
+ klondike_free_game_state(ret);
+ return NULL;
+}
+
+// Move the king with the fewest hidden cards on tableau pile to empty tableau
+static game_state_struct *move_king_to_empty_tableau(klondike_configuration *bp)
+{
+ game_state_struct *ret = clone_game_state(bp->game_state);
+ int minHidden = 20;
+ int minPile = -1;
+ for (int i = 0; i < 7; i++)
+ {
+ if (ret->tableau_size[i] > 0 && ret->tableau[i][ret->tableau_size[i] - 1].rank == KING)
+ {
+ int hidden = 0;
+ for (int j = 0; j < ret->tableau_size[i] - 1; j++)
+ {
+ if (!ret->tableau[i][j].is_face_up)
+ {
+ hidden++;
+ }
+ }
+ if (hidden < minHidden && hidden > 0)
+ {
+ minHidden = hidden;
+ minPile = i;
+ }
+ }
+ }
+ if (minPile != -1)
+ {
+ for (int i = 0; i < 7; i++)
+ {
+ if (ret->tableau_size[i] == 0)
+ {
+ ret->tableau[i][0] = ret->tableau[minPile][ret->tableau_size[minPile] - 1];
+ ret->tableau_size[i]++;
+ ret->tableau_size[minPile]--;
+
+ card_struct *animated_card = &ret->tableau[i][0];
+ animated_card->start_frame = bp->tick;
+ animated_card->end_frame = bp->tick + bp->animation_ticks;
+ animated_card->start_x = animated_card->x;
+ animated_card->start_y = animated_card->y;
+ animated_card->dest_x = bp->tableau_placeholders[i].x + RANDOM_POSITION_OFFSET;
+ animated_card->dest_y = bp->tableau_placeholders[i].y + RANDOM_POSITION_OFFSET;
+ animated_card->start_angle = 180.0f;
+ animated_card->end_angle = 180.0f;
+ animated_card->start_z = animated_card->z;
+
+ return ret;
+ }
+ }
+ }
+
+ klondike_free_game_state(ret);
+ return NULL;
+}
+
+// Allow moves to a tableau if the top card on the destination tableau is the opposite color and one rank higher
+static int can_move_to_tableau(game_state_struct *game_state, card_struct *card, int toPile)
+{
+ // which pile is the card in?
+ int pile = -1;
+ for (int i = 0; i < 7 && pile == -1; i++)
+ {
+ for (int j = 0; j < game_state->tableau_size[i]; j++)
+ {
+ if (game_state->tableau[i][j].rank == card->rank && game_state->tableau[i][j].suit == card->suit)
+ {
+ pile = i;
+ break;
+ }
+ }
+ }
+
+ // number of face down cards in that pile
+ int hidden = 0;
+ if (pile != -1)
+ {
+ for (int i = 0; i < game_state->tableau_size[pile] - 1; i++)
+ {
+ if (!game_state->tableau[pile][i].is_face_up)
+ {
+ hidden++;
+ }
+ }
+ }
+
+ if (hidden > 0 && game_state->tableau_size[toPile] == 0 && card->rank == KING && card->is_face_up)
+ {
+ return 1;
+ }
+ card_struct *topCard = &game_state->tableau[toPile][game_state->tableau_size[toPile] - 1];
+ if (card->is_face_up && topCard->is_face_up && card->rank == topCard->rank - 1 && card->suit % 2 != topCard->suit % 2)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+// TODO: Prefer moving tableaus with more hidden cards
+// Move the visible cards from one table tableau onto another tableau if the top card of the destination tableau is the
+//opposite color and one rank higher than the bottom card of the source tableau
+static game_state_struct *move_tableau_base_card_to_tableau(klondike_configuration *bp)
+{
+ game_state_struct *game_state = bp->game_state;
+ game_state_struct *ret = clone_game_state(game_state);
+
+ for (unsigned int preferredRank = 13; preferredRank > 0; preferredRank--)
+ {
+ for (int preferredHidden = 6; preferredHidden >= 0; preferredHidden--)
+ {
+ for (int i = 0; i < 7; i++)
+ {
+ // i is the index of the source tableau
+ // proceed if the number of hidden cards in tableau[i] is equal to preferredHidden
+ if (ret->tableau_size[i] >= 0)
+ {
+ int hidden = 0;
+ for (int j = 0; j < ret->tableau_size[i] - 1; j++)
+ {
+ if (!ret->tableau[i][j].is_face_up)
+ {
+ hidden++;
+ }
+ }
+
+ if (hidden != preferredHidden)
+ {
+ continue;
+ }
+
+ for (int j = 0; j < 7; j++)
+ {
+ if (i != j && ret->tableau_size[j] >= 0)
+ {
+ // base case is the first face up card in the tableau
+ int baseCardIndex = -1;
+ for (int k = 0; k < ret->tableau_size[i]; k++)
+ {
+ if (ret->tableau[i][k].is_face_up)
+ {
+ baseCardIndex = k;
+ break;
+ }
+ }
+
+ if (baseCardIndex > -1 && ret->tableau[i][baseCardIndex].rank == preferredRank && can_move_to_tableau(ret, &ret->tableau[i][baseCardIndex], j))
+ {
+ int is_face_up = 0;
+ for (int l = 0; l < ret->tableau_size[j]; l++)
+ {
+ if (ret->tableau[j][l].is_face_up)
+ {
+ is_face_up++;
+ }
+ }
+
+ for (int k = baseCardIndex; k < ret->tableau_size[i]; k++)
+ {
+ ret->tableau[j][ret->tableau_size[j]] = ret->tableau[i][k];
+
+ card_struct *animated_card = &ret->tableau[j][ret->tableau_size[j]];
+ animated_card->start_frame = bp->tick;
+ animated_card->end_frame = bp->tick + bp->animation_ticks;
+ animated_card->start_x = animated_card->x;
+ animated_card->start_y = animated_card->y;
+ animated_card->dest_x = bp->tableau_placeholders[j].x + RANDOM_POSITION_OFFSET;
+ animated_card->dest_y = bp->tableau_placeholders[j].y - (is_face_up + k - baseCardIndex) * 0.05 + RANDOM_POSITION_OFFSET;
+ animated_card->start_angle = animated_card->end_angle;
+ animated_card->start_z = animated_card->z;
+
+ ret->tableau_size[j]++;
+ }
+ ret->tableau_size[i] = baseCardIndex;
+
+ return ret;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ klondike_free_game_state(ret);
+ return NULL;
+}
+
+// find the next card for each foundation and see if it is one of the visible cards on one of the tableaus
+static game_state_struct *reveal_foundation_move(klondike_configuration *bp)
+{
+ game_state_struct *ret = clone_game_state(bp->game_state);
+ for (int i = 0; i < 4; i++)
+ {
+ if (ret->foundation_size[i] > 0)
+ {
+ for (int j = 0; j < 7; j++)
+ {
+ for (int k = 0; k < ret->tableau_size[j]; k++)
+ {
+ if (ret->tableau[j][k].rank == ret->foundation[i][ret->foundation_size[i] - 1].rank + 1 && ret->tableau[j][k].is_face_up == 1 && ret->foundation[i][ret->foundation_size[i] - 1].suit == ret->tableau[j][k].suit)
+ {
+ // see if the the card at ret->tableau[j][k+1] can be moved to another foundation
+ if (ret->tableau_size[j] > k + 1)
+ {
+ game_state_struct *ret2 = NULL;
+ if ((ret2 = move_card_to_foundation(bp, ret, &ret->tableau[j][k + 1])))
+ {
+ klondike_free_game_state(ret);
+ return ret2;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ klondike_free_game_state(ret);
+ return NULL;
+}
+
+// Move the base card of a tableau to the foundation if possible
+static game_state_struct *move_tableau_base_card_to_foundation(klondike_configuration *bp)
+{
+ game_state_struct *ret = clone_game_state(bp->game_state);
+ for (int i = 0; i < 7; i++)
+ {
+ if (ret->tableau_size[i] > 0)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ if (ret->foundation_size[j] == 0 && ret->tableau[i][ret->tableau_size[i] - 1].rank == ACE)
+ {
+ game_state_struct *ret2;
+
+ if ((ret2 = move_card_to_foundation(bp, ret, &ret->tableau[i][ret->tableau_size[i] - 1])))
+ {
+ klondike_free_game_state(ret);
+ return ret2;
+ }
+ }
+ else if (ret->foundation_size[j] > 0 && ret->foundation[j][ret->foundation_size[j] - 1].rank == ret->tableau[i][ret->tableau_size[i] - 1].rank - 1 && ret->foundation[j][ret->foundation_size[j] - 1].suit == ret->tableau[i][ret->tableau_size[i] - 1].suit && ret->tableau[i][ret->tableau_size[i] - 1].is_face_up)
+ {
+ game_state_struct *ret2;
+ if ((ret2 = move_card_to_foundation(bp, ret, &ret->tableau[i][ret->tableau_size[i] - 1])))
+ {
+ klondike_free_game_state(ret);
+ return ret2;
+ }
+ }
+ }
+ }
+ }
+
+ klondike_free_game_state(ret);
+ return NULL;
+}
+
+// Move the top card on the waste pile to one of the foundations if possible
+static game_state_struct *move_waste_to_foundation(klondike_configuration *bp)
+{
+ game_state_struct *ret = clone_game_state(bp->game_state);
+ if (ret->waste_size > 0)
+ {
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ if ((ret->foundation_size[i] > 0 && ret->foundation[i][ret->foundation_size[i] - 1].rank == ret->waste[ret->waste_size - 1].rank - 1) || (ret->foundation_size[i] == 0 && ret->waste[ret->waste_size - 1].rank == ACE && ret->waste[ret->waste_size - 1].suit == i))
+ {
+ game_state_struct *ret2;
+ if ((ret2 = move_card_to_foundation(bp, ret, &ret->waste[ret->waste_size - 1])))
+ {
+ klondike_free_game_state(ret);
+ ret = NULL;
+ return ret2;
+ }
+ }
+ }
+ }
+
+ klondike_free_game_state(ret);
+ return NULL;
+}
+
+// move the top card on the waste pile to one of the tableaus if possible
+static game_state_struct *move_waste_to_tableau(klondike_configuration *bp)
+{
+ game_state_struct *ret = clone_game_state(bp->game_state);
+ if (ret->waste_size > 0)
+ {
+ for (int i = 0; i < 7; i++)
+ {
+ if (ret->tableau_size[i] > 0 && ret->tableau[i][ret->tableau_size[i] - 1].rank == ret->waste[ret->waste_size - 1].rank + 1 && ret->tableau[i][ret->tableau_size[i] - 1].suit % 2 != ret->waste[ret->waste_size - 1].suit % 2)
+ {
+ ret->tableau[i][ret->tableau_size[i]] = ret->waste[ret->waste_size - 1];
+
+ int is_face_up = 0;
+ for (int k = 0; k < ret->tableau_size[i]; k++)
+ {
+ if (ret->tableau[i][k].is_face_up)
+ {
+ is_face_up++;
+ }
+ }
+
+ card_struct *animated_card = &ret->tableau[i][ret->tableau_size[i]];
+ animated_card->start_frame = bp->tick;
+ animated_card->end_frame = bp->tick + bp->animation_ticks;
+ animated_card->start_x = animated_card->x;
+ animated_card->start_y = animated_card->y;
+ animated_card->dest_x = bp->tableau_placeholders[i].x + RANDOM_POSITION_OFFSET;
+ animated_card->dest_y = bp->tableau_placeholders[i].y - (is_face_up) * 0.05 + RANDOM_POSITION_OFFSET;
+ animated_card->start_angle = 180.0f;
+ animated_card->end_angle = 180.0f;
+ animated_card->start_z = animated_card->z;
+
+ ret->tableau_size[i]++;
+ ret->waste_size--;
+ return ret;
+ }
+
+ if (ret->tableau_size[i] == 0 && ret->waste[ret->waste_size - 1].rank == KING)
+ {
+ ret->tableau[i][0] = ret->waste[ret->waste_size - 1];
+
+ int is_face_up = 0;
+
+ card_struct *animated_card = &ret->tableau[i][0];
+ animated_card->start_frame = bp->tick;
+ animated_card->end_frame = bp->tick + bp->animation_ticks;
+ animated_card->start_x = animated_card->x;
+ animated_card->start_y = animated_card->y;
+ animated_card->dest_x = bp->tableau_placeholders[i].x + RANDOM_POSITION_OFFSET;
+ animated_card->dest_y = bp->tableau_placeholders[i].y - (is_face_up) * 0.05 + RANDOM_POSITION_OFFSET;
+ animated_card->start_angle = 180.0f;
+ animated_card->end_angle = 180.0f;
+ animated_card->start_z = animated_card->z;
+
+ ret->tableau_size[i]++;
+ ret->waste_size--;
+ return ret;
+ }
+ }
+ }
+
+ klondike_free_game_state(ret);
+ return NULL;
+}
+
+// move a card from the deck to the waste pile if there is at least one card remaining in the deck
+static game_state_struct *move_deck_to_waste(klondike_configuration *bp)
+{
+ game_state_struct *ret = clone_game_state(bp->game_state);
+
+ // get the number of cards already on the board
+ int boardSize = 0;
+ for (int i = 0; i < 7; i++)
+ {
+ boardSize += ret->tableau_size[i];
+ }
+ for (int i = 0; i < 4; i++)
+ {
+ boardSize += ret->foundation_size[i];
+ }
+ boardSize += ret->waste_size;
+
+ if (boardSize == 52)
+ {
+ klondike_free_game_state(ret);
+ return NULL;
+ }
+
+ for (int i = 0; i < bp->draw_count; i++)
+ {
+ if (boardSize < 52)
+ {
+ ret->waste[ret->waste_size] = ret->deck[0];
+ ret->waste[ret->waste_size].is_face_up = 1;
+
+ card_struct *animated_card = &ret->waste[ret->waste_size];
+ animated_card->start_frame = bp->tick + bp->animation_ticks / 4 * i;
+ animated_card->end_frame = animated_card->start_frame + bp->animation_ticks;
+ animated_card->start_x = bp->deck_x;
+ animated_card->start_y = bp->deck_y;
+ animated_card->dest_x = bp->waste_x + 0.025 * ret->waste_size + RANDOM_POSITION_OFFSET;
+ animated_card->dest_y = bp->waste_y + RANDOM_POSITION_OFFSET;
+ animated_card->start_angle = 0.0f;
+ animated_card->end_angle = 180.0f;
+ animated_card->start_z = animated_card->z;
+
+ ret->waste_size++;
+
+ remove_card_from_deck(ret, &ret->deck[0]);
+ }
+
+ boardSize++;
+ }
+
+ return ret;
+}
+
+// Turn any over last tableau card that is face down
+static game_state_struct *turn_over_last_tableau_card(klondike_configuration *bp)
+{
+ game_state_struct *ret = clone_game_state(bp->game_state);
+ for (int i = 0; i < 7; i++)
+ {
+ if (ret->tableau_size[i] > 0 && ret->tableau[i][ret->tableau_size[i] - 1].is_face_up == 0)
+ {
+ ret->tableau[i][ret->tableau_size[i] - 1].is_face_up = 1;
+
+ ret->tableau[i][ret->tableau_size[i] - 1].start_frame = bp->tick;
+ ret->tableau[i][ret->tableau_size[i] - 1].end_frame = bp->tick + bp->animation_ticks;
+ ret->tableau[i][ret->tableau_size[i] - 1].start_x = ret->tableau[i][ret->tableau_size[i] - 1].x;
+ ret->tableau[i][ret->tableau_size[i] - 1].start_y = ret->tableau[i][ret->tableau_size[i] - 1].y;
+ ret->tableau[i][ret->tableau_size[i] - 1].dest_x = ret->tableau[i][ret->tableau_size[i] - 1].x;
+ ret->tableau[i][ret->tableau_size[i] - 1].dest_y = ret->tableau[i][ret->tableau_size[i] - 1].y;
+ ret->tableau[i][ret->tableau_size[i] - 1].start_angle = 0.0f;
+ ret->tableau[i][ret->tableau_size[i] - 1].end_angle = 180.0f;
+
+ return ret;
+ }
+ }
+
+ klondike_free_game_state(ret);
+ return NULL;
+}
+
+// This function should return a new game state that is the result of making the next move in the game
+// The function should not modify the original game state
+// The function should return NULL if there are no possible moves
+static game_state_struct *next_move_inner(klondike_configuration *bp)
+{
+ game_state_struct *ret = NULL;
+
+ if ((ret = turn_over_last_tableau_card(bp)))
+ {
+ ret->moves_since_waste_flip++;
+ return ret;
+ }
+
+ if ((ret = move_tableau_base_card_to_foundation(bp)))
+ {
+ ret->moves_since_waste_flip++;
+ return ret;
+ }
+
+ if ((ret = move_king_to_empty_tableau(bp)))
+ {
+ ret->moves_since_waste_flip++;
+ return ret;
+ }
+
+ if ((ret = move_tableau_base_card_to_tableau(bp)))
+ {
+ ret->moves_since_waste_flip++;
+ return ret;
+ }
+
+ if ((ret = reveal_foundation_move(bp)))
+ {
+ ret->moves_since_waste_flip++;
+ return ret;
+ }
+
+ if ((ret = move_waste_to_foundation(bp)))
+ {
+ ret->moves_since_waste_flip++;
+ return ret;
+ }
+
+ if ((ret = move_waste_to_tableau(bp)))
+ {
+ ret->moves_since_waste_flip++;
+ return ret;
+ }
+
+ if ((ret = move_deck_to_waste(bp)))
+ {
+ return ret;
+ }
+
+ if (bp->game_state->moves_since_waste_flip > 0)
+ {
+ ret = clone_game_state(bp->game_state);
+ reset_waste(bp, ret);
+ return ret;
+ }
+
+ return NULL;
+}
+
+game_state_struct *klondike_next_move(klondike_configuration *bp)
+{
+ game_state_struct *ret = NULL;
+ if ((ret = next_move_inner(bp)))
+ {
+ ret->moves++;
+
+ // zero the cards in the foundations arrays past the foundation length
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = ret->foundation_size[i]; j < 13; j++)
+ {
+ ret->foundation[i][j].rank = NONE;
+ ret->foundation[i][j].suit = 0;
+ ret->foundation[i][j].is_face_up = 0;
+ }
+ }
+
+ // zero the cards in the tableau arrays past the tableau length
+ for (int i = 0; i < 7; i++)
+ {
+ for (int j = ret->tableau_size[i]; j < 13; j++)
+ {
+ ret->tableau[i][j].rank = 0;
+ ret->tableau[i][j].suit = 0;
+ ret->tableau[i][j].is_face_up = 0;
+ }
+ }
+
+ // zero the cards in the waste array past the waste length
+ for (int i = ret->waste_size; i < 24; i++)
+ {
+ ret->waste[i].rank = 0;
+ ret->waste[i].suit = 0;
+ ret->waste[i].is_face_up = 0;
+ }
+
+ return ret;
+ }
+
+ return NULL;
+}
--- /dev/null
+/* klondike, Copyright (c) 2024 Joshua Timmons <josh@developerx.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __KLONDIKE_GAME_H__
+#define __KLONDIKE_GAME_H__
+
+#include "gltrackball.h"
+
+#define NUM_SUITS 4
+#define NUM_RANKS 13
+#define NUM_CARDS 52
+#define MAX_WASTE 24
+#define MAX_FOUNDATION 13
+#define MAX_TABLEAU 20
+#define MAX_TEXTURE 53
+#define BACK_TEXTURE 52
+
+typedef enum { DIAMONDS, CLUBS, HEARTS, SPADES } Suit;
+typedef enum { NONE=0, ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING } Rank;
+
+typedef struct {
+ Suit suit;
+ Rank rank;
+ int is_face_up; // 0 for face down, 1 for face up
+
+ float x;
+ float y;
+ float z;
+ float start_x;
+ float start_y;
+ float dest_x;
+ float dest_y;
+ float start_frame;
+ float end_frame;
+ float angle;
+ float start_angle;
+ float end_angle;
+ float start_z;
+} card_struct;
+
+typedef struct {
+ card_struct deck[NUM_CARDS];
+ card_struct tableau[7][MAX_TABLEAU];
+ int tableau_size[7];
+ card_struct waste[MAX_WASTE];
+ int waste_size;
+ card_struct foundation[4][MAX_FOUNDATION];
+ int foundation_size[4];
+
+ int moves;
+ int moves_since_waste_flip;
+
+ // todo: the tableaus do not track the number of face up cards. This is a bug.
+} game_state_struct;
+
+typedef struct {
+ GLXContext *glx_context;
+ card_struct foundation_placeholders[4];
+ card_struct tableau_placeholders[7];
+ float waste_x;
+ float waste_y;
+ float deck_x;
+ float deck_y;
+
+ float scale;
+
+ int tick;
+ int universe_tick;
+ float camera_phase;
+
+ int final_animation;
+ int redeal;
+
+ game_state_struct *game_state;
+
+ GLuint fronts[52];
+ GLuint back;
+
+ trackball_state *trackball;
+ Bool button_down_p;
+
+ // Preferences
+ GLuint animation_ticks;
+ int draw_count;
+ int camera_speed;
+ Bool sloppy;
+
+} klondike_configuration;
+
+
+void klondike_initialize_deck(klondike_configuration *bp);
+void klondike_shuffle_deck(card_struct deck[]);
+void klondike_deal_cards(klondike_configuration *bp);
+void klondike_free_game_state(game_state_struct *game_state);
+int klondike_deck_size(game_state_struct *game_state);
+void klondike_reset_board(klondike_configuration *bp);
+game_state_struct *klondike_next_move(klondike_configuration *bp);
+
+#endif /* __KLONDIKE_GAME_H__ */
--- /dev/null
+/* klondike, Copyright (c) 2024 Joshua Timmons <josh@developerx.com>\r
+ *\r
+ * Permission to use, copy, modify, distribute, and sell this software and its\r
+ * documentation for any purpose is hereby granted without fee, provided that\r
+ * the above copyright notice appear in all copies and that both that\r
+ * copyright notice and this permission notice appear in supporting\r
+ * documentation. No representations are made about the suitability of this\r
+ * software for any purpose. It is provided "as is" without express or\r
+ * implied warranty.\r
+ */\r
+\r
+#define DEFAULTS "*delay: 30000 \n" \\r
+ \r
+#define release_klondike 0\r
+\r
+#include "xlockmore.h"\r
+#include <ctype.h>\r
+#include "gltrackball.h"\r
+#include "klondike-game.h"\r
+#include "ximage-loader.h"\r
+#define I_HAVE_XPM\r
+#include "../images/gen/back_png.h"\r
+#include "../images/gen/CA_png.h"\r
+#include "../images/gen/C2_png.h"\r
+#include "../images/gen/C3_png.h"\r
+#include "../images/gen/C4_png.h"\r
+#include "../images/gen/C5_png.h"\r
+#include "../images/gen/C6_png.h"\r
+#include "../images/gen/C7_png.h"\r
+#include "../images/gen/C8_png.h"\r
+#include "../images/gen/C9_png.h"\r
+#include "../images/gen/CT_png.h"\r
+#include "../images/gen/CJ_png.h"\r
+#include "../images/gen/CQ_png.h"\r
+#include "../images/gen/CK_png.h"\r
+\r
+#include "../images/gen/DA_png.h"\r
+#include "../images/gen/D2_png.h"\r
+#include "../images/gen/D3_png.h"\r
+#include "../images/gen/D4_png.h"\r
+#include "../images/gen/D5_png.h"\r
+#include "../images/gen/D6_png.h"\r
+#include "../images/gen/D7_png.h"\r
+#include "../images/gen/D8_png.h"\r
+#include "../images/gen/D9_png.h"\r
+#include "../images/gen/DT_png.h"\r
+#include "../images/gen/DJ_png.h"\r
+#include "../images/gen/DQ_png.h"\r
+#include "../images/gen/DK_png.h"\r
+\r
+#include "../images/gen/SA_png.h"\r
+#include "../images/gen/S2_png.h"\r
+#include "../images/gen/S3_png.h"\r
+#include "../images/gen/S4_png.h"\r
+#include "../images/gen/S5_png.h"\r
+#include "../images/gen/S6_png.h"\r
+#include "../images/gen/S7_png.h"\r
+#include "../images/gen/S8_png.h"\r
+#include "../images/gen/S9_png.h"\r
+#include "../images/gen/ST_png.h"\r
+#include "../images/gen/SJ_png.h"\r
+#include "../images/gen/SQ_png.h"\r
+#include "../images/gen/SK_png.h"\r
+\r
+#include "../images/gen/HA_png.h"\r
+#include "../images/gen/H2_png.h"\r
+#include "../images/gen/H3_png.h"\r
+#include "../images/gen/H4_png.h"\r
+#include "../images/gen/H5_png.h"\r
+#include "../images/gen/H6_png.h"\r
+#include "../images/gen/H7_png.h"\r
+#include "../images/gen/H8_png.h"\r
+#include "../images/gen/H9_png.h"\r
+#include "../images/gen/HT_png.h"\r
+#include "../images/gen/HJ_png.h"\r
+#include "../images/gen/HQ_png.h"\r
+#include "../images/gen/HK_png.h"\r
+\r
+#ifdef USE_GL /* whole file */\r
+\r
+#define DEF_CAMERA_SPEED "50"\r
+#define DEF_SPEED "60"\r
+#define DEF_DRAW_COUNT "3"\r
+#define DEF_SLOPPY "True"\r
+\r
+// global variables\r
+static klondike_configuration *bps = NULL;\r
+\r
+static GLuint animation_ticks;\r
+static int draw_count;\r
+static int camera_speed;\r
+static Bool sloppy;\r
+\r
+// options\r
+static XrmOptionDescRec opts[] = {\r
+ {"-speed", ".speed", XrmoptionSepArg, 0},\r
+ {"-camera_speed", ".cameraSpeed", XrmoptionSepArg, 0},\r
+ {"-sloppy", ".sloppy", XrmoptionNoArg, "True"},\r
+ {"+sloppy", ".sloppy", XrmoptionNoArg, "False"},\r
+ {"-draw", ".drawCount", XrmoptionSepArg, 0}};\r
+\r
+// variables for the options\r
+static argtype vars[] = {\r
+ {&sloppy, "sloppy", "Sloppy", DEF_SLOPPY, t_Bool},\r
+ {&animation_ticks, "speed", "Speed", DEF_SPEED, t_Int},\r
+ {&draw_count, "drawCount", "DrawCount", DEF_DRAW_COUNT, t_Int},\r
+ {&camera_speed, "cameraSpeed", "CameraSpeed", DEF_CAMERA_SPEED, t_Int}};\r
+\r
+ENTRYPOINT ModeSpecOpt klondike_opts = {countof(opts), opts, countof(vars), vars, NULL};\r
+\r
+// Local function prototypes\r
+static void initialize_placeholders(klondike_configuration *bp, int width, int height);\r
+static double ease_in_out_quart(double x);\r
+static double ease_out_quart(double x);\r
+static int compare_cards(const void *a, const void *b);\r
+static void animate_board_to_deck(klondike_configuration *bp);\r
+static void animate_initial_board(klondike_configuration *bp);\r
+\r
+// Function to load a texture\r
+// Function to load a texture\r
+static GLuint load_texture(ModeInfo *mi, const char *name,\r
+ const unsigned char *buffer, unsigned length)\r
+{\r
+ GLuint texture_id;\r
+ XImage *image = image_data_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi),\r
+ buffer, length);\r
+\r
+ glGenTextures(1, &texture_id);\r
+ glBindTexture(GL_TEXTURE_2D, texture_id);\r
+\r
+ // Set texture parameters\r
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\r
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\r
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\r
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r
+\r
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);\r
+ /* glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width); */\r
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,\r
+ image->width, image->height, 0,\r
+ GL_RGBA, GL_UNSIGNED_BYTE, image->data);\r
+ check_gl_error("load texture");\r
+ check_gl_error("mipmap");\r
+ XDestroyImage(image);\r
+ return texture_id;\r
+}\r
+\r
+/* Window management, etc\r
+*/\r
+ENTRYPOINT void\r
+reshape_klondike(ModeInfo *mi, int width, int height)\r
+{\r
+ int y = 0;\r
+\r
+ if (width > height * 5)\r
+ { /* tiny window: show middle */\r
+ height = width * 9 / 16;\r
+ y = -height / 2;\r
+ }\r
+\r
+ glViewport(0, y, (GLint)width, (GLint)height);\r
+\r
+ // Enable blending\r
+ glEnable(GL_BLEND);\r
+\r
+ // Set the blending function\r
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r
+\r
+ // Enable multisampling\r
+ glEnable(GL_MULTISAMPLE);\r
+\r
+ glEnable(GL_DEPTH_TEST);\r
+ glDepthFunc(GL_LESS);\r
+\r
+ {\r
+ GLfloat s = (MI_WIDTH(mi) < MI_HEIGHT(mi)\r
+ ? (MI_WIDTH(mi) / (GLfloat)MI_HEIGHT(mi))\r
+ : 1);\r
+ glScalef(s, s, s);\r
+ }\r
+\r
+ initialize_placeholders(&bps[MI_SCREEN(mi)], width, height);\r
+\r
+ glClear(GL_COLOR_BUFFER_BIT);\r
+}\r
+\r
+// initialize the placeholders for the foundation and tableau\r
+static void initialize_placeholders(klondike_configuration *bp, int width, int height)\r
+{\r
+ float xscale = width > height ? 0.53 * height / width : 0.53;\r
+\r
+ if (width < height)\r
+ {\r
+ xscale *= width / 1280.0f;\r
+ }\r
+\r
+ for (int i = 0; i < 4; i++)\r
+ {\r
+ bp->foundation_placeholders[i].x = 0.15 + -0.4f + (0.075 + 0.15 * i * xscale / 0.3f) * bp->scale;\r
+ bp->foundation_placeholders[i].y = 0.7f * bp->scale;\r
+ }\r
+\r
+ for (int i = 0; i < 7; i++)\r
+ {\r
+ bp->tableau_placeholders[i].x = 0.15 + -0.55f + 0.15 * i * xscale / 0.3f * bp->scale;\r
+ bp->tableau_placeholders[i].y = 0.3f * bp->scale;\r
+ }\r
+\r
+ bp->waste_x = bp->tableau_placeholders[0].x;\r
+ bp->waste_y = -0.65f * bp->scale;\r
+ bp->deck_x = bp->tableau_placeholders[6].x;\r
+ bp->deck_y = -0.65f * bp->scale;\r
+}\r
+\r
+// animate the initial board\r
+static void animate_initial_board(klondike_configuration *bp)\r
+{\r
+ int n = 0;\r
+ for (int i = 0; i < 7; i++)\r
+ {\r
+ for (int j = 0; j < 7; j++)\r
+ {\r
+ if (i < bp->game_state->tableau_size[j])\r
+ {\r
+ card_struct *card = &bp->game_state->tableau[j][i];\r
+ card->start_frame = 10 + n * animation_ticks / 4;\r
+ card->end_frame = card->start_frame + animation_ticks;\r
+ card->start_x = bp->deck_x;\r
+ card->start_y = bp->deck_y;\r
+ card->dest_x = bp->tableau_placeholders[j].x;\r
+ card->dest_y = bp->tableau_placeholders[j].y;\r
+ card->angle = 0.0f;\r
+ card->start_angle = 0.0f;\r
+ card->is_face_up = i == bp->game_state->tableau_size[j] - 1;\r
+ card->end_angle = card->is_face_up ? 180.0f : 0.0f;\r
+\r
+ n++;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+ENTRYPOINT Bool\r
+klondike_handle_event(ModeInfo *mi, XEvent *event)\r
+{\r
+ klondike_configuration *bp = &bps[MI_SCREEN(mi)];\r
+ if (gltrackball_event_handler (event, bp->trackball,\r
+ MI_WIDTH (mi), MI_HEIGHT (mi),\r
+ &bp->button_down_p))\r
+ return True;\r
+ \r
+ return False;\r
+}\r
+\r
+ENTRYPOINT void\r
+init_klondike(ModeInfo *mi)\r
+{\r
+ klondike_configuration *bp;\r
+ int wire = MI_IS_WIREFRAME(mi);\r
+\r
+ MI_INIT(mi, bps);\r
+ bp = &bps[MI_SCREEN(mi)];\r
+\r
+ bp->glx_context = init_GL(mi);\r
+\r
+ reshape_klondike(mi, MI_WIDTH(mi), MI_HEIGHT(mi));\r
+\r
+ if (!wire)\r
+ {\r
+ GLfloat pos[4] = {0.0, 0.0, 1.0, 0.0}; // Changed light position to be in front\r
+ GLfloat amb[4] = {0.8, 0.8, 0.8, 1.0}; // Increased ambient light\r
+ GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};\r
+ GLfloat spc[4] = {0.0, 0.0, 0.0, 1.0}; // Removed specular highlight\r
+\r
+ // enable lighting, depth testing, normaliztion, culling, texture mapping, blending\r
+ glEnable(GL_LIGHTING);\r
+ glEnable(GL_LIGHT0);\r
+ glEnable(GL_DEPTH_TEST);\r
+ glEnable(GL_NORMALIZE);\r
+ glEnable(GL_CULL_FACE);\r
+ glEnable(GL_TEXTURE_2D);\r
+ glEnable(GL_BLEND);\r
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r
+\r
+ // Set up material properties\r
+ GLfloat mat_ambient[] = {1.0, 1.0, 1.0, 1.0};\r
+ GLfloat mat_diffuse[] = {1.0, 1.0, 1.0, 1.0};\r
+ GLfloat mat_specular[] = {0.0, 0.0, 0.0, 1.0};\r
+ GLfloat mat_shininess[] = {0.0};\r
+ \r
+ // set the material properties\r
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);\r
+ glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);\r
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);\r
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);\r
+\r
+ // set the light position\r
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);\r
+ glLightfv(GL_LIGHT0, GL_AMBIENT, amb);\r
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);\r
+ glLightfv(GL_LIGHT0, GL_SPECULAR, spc);\r
+\r
+ // Load textures\r
+ unsigned int n = 0; \r
+ \r
+ bp->fronts[n++] = load_texture(mi, "DA", DA_png, sizeof(DA_png));\r
+ bp->fronts[n++] = load_texture(mi, "D2", D2_png, sizeof(D2_png));\r
+ bp->fronts[n++] = load_texture(mi, "D3", D3_png, sizeof(D3_png));\r
+ bp->fronts[n++] = load_texture(mi, "D4", D4_png, sizeof(D4_png));\r
+ bp->fronts[n++] = load_texture(mi, "D5", D5_png, sizeof(D5_png));\r
+ bp->fronts[n++] = load_texture(mi, "D6", D6_png, sizeof(D6_png));\r
+ bp->fronts[n++] = load_texture(mi, "D7", D7_png, sizeof(D7_png));\r
+ bp->fronts[n++] = load_texture(mi, "D8", D8_png, sizeof(D8_png));\r
+ bp->fronts[n++] = load_texture(mi, "D9", D9_png, sizeof(D9_png));\r
+ bp->fronts[n++] = load_texture(mi, "DT", DT_png, sizeof(DT_png));\r
+ bp->fronts[n++] = load_texture(mi, "DJ", DJ_png, sizeof(DJ_png));\r
+ bp->fronts[n++] = load_texture(mi, "DQ", DQ_png, sizeof(DQ_png));\r
+ bp->fronts[n++] = load_texture(mi, "DK", DK_png, sizeof(DK_png));\r
+ \r
+ bp->fronts[n++] = load_texture(mi, "CA", CA_png, sizeof(CA_png));\r
+ bp->fronts[n++] = load_texture(mi, "C2", C2_png, sizeof(C2_png));\r
+ bp->fronts[n++] = load_texture(mi, "C3", C3_png, sizeof(C3_png));\r
+ bp->fronts[n++] = load_texture(mi, "C4", C4_png, sizeof(C4_png));\r
+ bp->fronts[n++] = load_texture(mi, "C5", C5_png, sizeof(C5_png));\r
+ bp->fronts[n++] = load_texture(mi, "C6", C6_png, sizeof(C6_png));\r
+ bp->fronts[n++] = load_texture(mi, "C7", C7_png, sizeof(C7_png));\r
+ bp->fronts[n++] = load_texture(mi, "C8", C8_png, sizeof(C8_png));\r
+ bp->fronts[n++] = load_texture(mi, "C9", C9_png, sizeof(C9_png));\r
+ bp->fronts[n++] = load_texture(mi, "CT", CT_png, sizeof(CT_png));\r
+ bp->fronts[n++] = load_texture(mi, "CJ", CJ_png, sizeof(CJ_png));\r
+ bp->fronts[n++] = load_texture(mi, "CQ", CQ_png, sizeof(CQ_png));\r
+ bp->fronts[n++] = load_texture(mi, "CK", CK_png, sizeof(CK_png));\r
+ \r
+ bp->fronts[n++] = load_texture(mi, "HA", HA_png, sizeof(HA_png));\r
+ bp->fronts[n++] = load_texture(mi, "H2", H2_png, sizeof(H2_png));\r
+ bp->fronts[n++] = load_texture(mi, "H3", H3_png, sizeof(H3_png));\r
+ bp->fronts[n++] = load_texture(mi, "H4", H4_png, sizeof(H4_png));\r
+ bp->fronts[n++] = load_texture(mi, "H5", H5_png, sizeof(H5_png));\r
+ bp->fronts[n++] = load_texture(mi, "H6", H6_png, sizeof(H6_png));\r
+ bp->fronts[n++] = load_texture(mi, "H7", H7_png, sizeof(H7_png));\r
+ bp->fronts[n++] = load_texture(mi, "H8", H8_png, sizeof(H8_png));\r
+ bp->fronts[n++] = load_texture(mi, "H9", H9_png, sizeof(H9_png));\r
+ bp->fronts[n++] = load_texture(mi, "HT", HT_png, sizeof(HT_png));\r
+ bp->fronts[n++] = load_texture(mi, "HJ", HJ_png, sizeof(HJ_png));\r
+ bp->fronts[n++] = load_texture(mi, "HQ", HQ_png, sizeof(HQ_png));\r
+ bp->fronts[n++] = load_texture(mi, "HK", HK_png, sizeof(HK_png));\r
+\r
+ bp->fronts[n++] = load_texture(mi, "SA", SA_png, sizeof(SA_png));\r
+ bp->fronts[n++] = load_texture(mi, "S2", S2_png, sizeof(S2_png));\r
+ bp->fronts[n++] = load_texture(mi, "S3", S3_png, sizeof(S3_png));\r
+ bp->fronts[n++] = load_texture(mi, "S4", S4_png, sizeof(S4_png));\r
+ bp->fronts[n++] = load_texture(mi, "S5", S5_png, sizeof(S5_png));\r
+ bp->fronts[n++] = load_texture(mi, "S6", S6_png, sizeof(S6_png));\r
+ bp->fronts[n++] = load_texture(mi, "S7", S7_png, sizeof(S7_png));\r
+ bp->fronts[n++] = load_texture(mi, "S8", S8_png, sizeof(S8_png));\r
+ bp->fronts[n++] = load_texture(mi, "S9", S9_png, sizeof(S9_png));\r
+ bp->fronts[n++] = load_texture(mi, "ST", ST_png, sizeof(ST_png));\r
+ bp->fronts[n++] = load_texture(mi, "SJ", SJ_png, sizeof(SJ_png));\r
+ bp->fronts[n++] = load_texture(mi, "SQ", SQ_png, sizeof(SQ_png));\r
+ bp->fronts[n++] = load_texture(mi, "SK", SK_png, sizeof(SK_png));\r
+\r
+ bp->back = load_texture(mi, "back", back_png, sizeof(back_png)); \r
+ }\r
+\r
+ bp->scale = 1.1f;\r
+\r
+ initialize_placeholders(bp, MI_WIDTH(mi), MI_HEIGHT(mi));\r
+\r
+ bp->trackball = gltrackball_init (True);\r
+\r
+ // initialize the game state\r
+ bp->game_state = (game_state_struct *)malloc(sizeof(game_state_struct));\r
+ bp->tick = 0;\r
+ bp->universe_tick = 0;\r
+\r
+ bp->camera_phase = (random() / (float)RAND_MAX) * 0.2 * M_PI;\r
+\r
+ bp->redeal = 0;\r
+ bp->final_animation = 0;\r
+\r
+ // set the draw count to 3 if it is not 1 or 3\r
+ if (draw_count != 1 && draw_count != 3)\r
+ {\r
+ draw_count = 3;\r
+ }\r
+\r
+ bp->animation_ticks = animation_ticks;\r
+ bp->draw_count = draw_count;\r
+ bp->camera_speed = camera_speed;\r
+ bp->sloppy = sloppy;\r
+}\r
+\r
+// ease in out quartic\r
+static double ease_in_out_quart(double x)\r
+{\r
+ if (x < 0.5)\r
+ {\r
+ return 8 * x * x * x * x;\r
+ }\r
+ else\r
+ {\r
+ return 1 - pow(-2 * x + 2, 4) / 2;\r
+ }\r
+}\r
+\r
+// ease out quartic\r
+static double ease_out_quart(double x)\r
+{\r
+ return 1 - pow(1 - x, 4);\r
+}\r
+\r
+// sort cards by z position then by end_frame\r
+static int compare_cards(const void *a, const void *b)\r
+{\r
+ card_struct **ca = (card_struct **)a;\r
+ card_struct **cb = (card_struct **)b;\r
+\r
+ // sort by z position then by end_frame\r
+ if ((*ca)->z != (*cb)->z)\r
+ {\r
+ return ((*ca)->z > (*cb)->z) ? 1 : -1;\r
+ }\r
+\r
+ // sort by end_frame\r
+ return (*ca)->end_frame - (*cb)->end_frame;\r
+}\r
+\r
+// collect the cards from the board back to the deck to be redealt\r
+static void animate_board_to_deck(klondike_configuration *bp)\r
+{\r
+ int n = 0;\r
+ for (int i = 0; i < 7; i++)\r
+ {\r
+ for (int j = 0; j < bp->game_state->tableau_size[i]; j++)\r
+ {\r
+ card_struct *card = &bp->game_state->tableau[i][j];\r
+ card->start_frame = bp->tick + n * animation_ticks / 12;\r
+ card->end_frame = card->start_frame + animation_ticks;\r
+ card->start_x = card->x;\r
+ card->start_y = card->y;\r
+ card->dest_x = bp->deck_x;\r
+ card->dest_y = bp->deck_y;\r
+ card->start_angle = card->angle;\r
+ card->end_angle = 360.0f;\r
+ card->is_face_up = 0;\r
+ n++;\r
+ }\r
+ }\r
+\r
+ for (int i = 0; i < 4; i++)\r
+ {\r
+ for (int j = 0; j < bp->game_state->foundation_size[i]; j++)\r
+ {\r
+ card_struct *card = &bp->game_state->foundation[i][j];\r
+ card->start_frame = bp->tick + n * animation_ticks / 12;\r
+ card->end_frame = card->start_frame + animation_ticks;\r
+ card->start_x = card->x;\r
+ card->start_y = card->y;\r
+ card->dest_x = bp->deck_x;\r
+ card->dest_y = bp->deck_y;\r
+ card->start_angle = card->angle;\r
+ card->end_angle = 360.0f;\r
+ card->is_face_up = 0;\r
+ n++;\r
+ }\r
+ }\r
+\r
+ for (int i = 0; i < bp->game_state->waste_size; i++)\r
+ {\r
+ card_struct *card = &bp->game_state->waste[i];\r
+ card->start_frame = bp->tick + n * animation_ticks / 12;\r
+ card->end_frame = card->start_frame + animation_ticks;\r
+ card->start_x = card->x;\r
+ card->start_y = card->y;\r
+ card->dest_x = bp->deck_x;\r
+ card->dest_y = bp->deck_y;\r
+ card->start_angle = card->angle;\r
+ card->end_angle = 360.0f;\r
+ card->is_face_up = 0;\r
+ n++;\r
+ }\r
+\r
+ bp->final_animation = bp->tick + n * animation_ticks / 12 + animation_ticks;\r
+}\r
+\r
+ENTRYPOINT void\r
+draw_klondike(ModeInfo *mi)\r
+{\r
+ klondike_configuration *bp = &bps[MI_SCREEN(mi)];\r
+ Display *dpy = MI_DISPLAY(mi);\r
+ Window window = MI_WINDOW(mi);\r
+ card_struct *renderCards[52];\r
+\r
+ if (!bp->glx_context)\r
+ {\r
+ fprintf(stderr, "%s: no graphics context\n", progname);\r
+ return;\r
+ }\r
+\r
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);\r
+\r
+ glShadeModel(GL_SMOOTH);\r
+ glEnable(GL_DEPTH_TEST);\r
+ glEnable(GL_NORMALIZE);\r
+ glEnable(GL_CULL_FACE);\r
+ glEnable(GL_TEXTURE_2D);\r
+ glEnable(GL_BLEND);\r
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\r
+ glEnable(GL_LIGHTING);\r
+ glEnable(GL_LIGHT0);\r
+\r
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\r
+\r
+ int width = MI_WIDTH(mi);\r
+ int height = MI_HEIGHT(mi);\r
+\r
+ glScalef(1.1, 1.1, 1.1);\r
+\r
+ float offset_x = 0.0f;\r
+ float offset_y = 0.0f;\r
+ float scale = 1.0f;\r
+\r
+ if (bp->tick == 0)\r
+ {\r
+ initialize_placeholders(bp, MI_WIDTH(mi), MI_HEIGHT(mi));\r
+\r
+ klondike_initialize_deck(bp);\r
+ klondike_shuffle_deck(bp->game_state->deck);\r
+\r
+ klondike_deal_cards(bp);\r
+ animate_initial_board(bp);\r
+ }\r
+\r
+ bp->tick++;\r
+ bp->universe_tick++;\r
+ int animatedCardCount = 0;\r
+\r
+ int last_animation = 0;\r
+\r
+ // render the tableaus\r
+ for (int i = 0; i < 7; i++)\r
+ {\r
+ for (int j = 0; j < bp->game_state->tableau_size[i]; j++)\r
+ {\r
+ card_struct *card = &(bp->game_state->tableau[i][j]);\r
+ renderCards[animatedCardCount++] = card;\r
+ }\r
+ }\r
+\r
+ // render the foundations\r
+ for (int i = 0; i < 4; i++)\r
+ {\r
+ for (int j = 0; j < bp->game_state->foundation_size[i]; j++)\r
+ {\r
+ card_struct *card = &(bp->game_state->foundation[i][j]);\r
+ renderCards[animatedCardCount++] = card;\r
+\r
+ card->z = j;\r
+ }\r
+ }\r
+\r
+ // render the waste pile\r
+ for (int j = 0; j < bp->game_state->waste_size; j++)\r
+ {\r
+ card_struct *card = &(bp->game_state->waste[j]);\r
+ card->z = j;\r
+ renderCards[animatedCardCount++] = card;\r
+ }\r
+\r
+ // render the deck\r
+ int ds = klondike_deck_size(bp->game_state);\r
+ for (int j = ds - 1; j >= 0; j--)\r
+ {\r
+ card_struct *card = &(bp->game_state->deck[j]);\r
+ // Only set z position, don't modify x,y here\r
+ card->z = ds - 1 - j;\r
+ renderCards[animatedCardCount + j] = card;\r
+ }\r
+\r
+ animatedCardCount += ds;\r
+\r
+ for (int i = 0; i < animatedCardCount; i++)\r
+ {\r
+ card_struct *card = renderCards[i];\r
+\r
+ card->z = i / 10.0f;\r
+\r
+ if (bp->tick >= card->start_frame && bp->tick < card->end_frame)\r
+ {\r
+ float n = ((float)bp->tick - (float)card->start_frame) / (card->end_frame - card->start_frame);\r
+ float eased2 = ease_in_out_quart(n);\r
+ float eased_card_z = card->start_z * (1.0f - eased2);\r
+ card->z += eased_card_z;\r
+ card->z += 8 * sin(n * M_PI);\r
+ }\r
+ else {\r
+ card->start_z = 0;\r
+ }\r
+ \r
+ }\r
+\r
+\r
+\r
+ // qsort the rendercards by animation order\r
+ qsort(renderCards, animatedCardCount, sizeof(card_struct *), compare_cards);\r
+\r
+ // camera\r
+ \r
+ float speed_factor = camera_speed / 100.0f;\r
+ float camera_theta = M_PI / 2 + (sin((bp->universe_tick + bp->camera_phase) * 0.0065 * speed_factor) * 0.225);\r
+ float camera_phi = -0.55 + (sin((bp->universe_tick + bp->camera_phase) * 0.008 * speed_factor) * 0.25);\r
+ float camera_d = 3.5 + (sin((bp->universe_tick + bp->camera_phase) * 0.013 * speed_factor));\r
+ float camera_x = camera_d * cos(camera_theta) * sin(camera_phi);\r
+ float camera_y = camera_d * sin(camera_theta) * sin(camera_phi);\r
+ float camera_z = camera_d * cos(camera_phi);\r
+ \r
+ // Use immediate mode rendering instead of shaders\r
+ glMatrixMode(GL_MODELVIEW);\r
+ glLoadIdentity();\r
+ gluPerspective (30.0, 1.0, 1.0, 200);\r
+ gluLookAt( camera_x, camera_y, camera_z, // Camera position (eye point)\r
+ 0.1, -0.0, 0, // Look-at point (center)\r
+ 0, 0, 1); // Up vector\r
+\r
+ glRotatef (current_device_rotation(), 0, 0, 1);\r
+ gltrackball_rotate (bp->trackball);\r
+\r
+ for (int i = 0; i < animatedCardCount; i++)\r
+ {\r
+ glPushMatrix();\r
+\r
+ card_struct *card = renderCards[i];\r
+\r
+ float tx, ty, tz;\r
+\r
+ if (card->end_frame > last_animation)\r
+ {\r
+ last_animation = card->end_frame;\r
+ }\r
+\r
+ // card->z = i / 10.0f;\r
+\r
+ if (bp->tick >= card->start_frame && bp->tick < card->end_frame)\r
+ {\r
+ float n = ((float)bp->tick - (float)card->start_frame) / (card->end_frame - card->start_frame);\r
+ float eased = ease_out_quart(n);\r
+ float eased2 = ease_in_out_quart(n);\r
+\r
+ if (card->dest_x != card->start_x)\r
+ {\r
+ card->x = card->start_x + eased * (card->dest_x - card->start_x);\r
+ }\r
+ else\r
+ {\r
+ card->x = card->start_x;\r
+ }\r
+\r
+ if (card->dest_y != card->start_y)\r
+ {\r
+ card->y = card->start_y + eased * (card->dest_y - card->start_y);\r
+ }\r
+ else\r
+ {\r
+ card->y = card->start_y;\r
+ }\r
+\r
+ // card->z += 25 * sin(n * M_PI);\r
+\r
+ if (card->end_angle != card->start_angle)\r
+ {\r
+ card->angle = card->start_angle + eased2 * (card->end_angle - card->start_angle);\r
+ }\r
+ else\r
+ {\r
+ card->angle = card->start_angle;\r
+ }\r
+ }\r
+ // Make sure card positions are finalized after animation completes\r
+ else if (bp->tick >= card->end_frame)\r
+ {\r
+ card->x = card->dest_x;\r
+ card->y = card->dest_y;\r
+ card->angle = card->end_angle;\r
+ }\r
+ \r
+ float s = 1.0f;\r
+ GLuint current_texture = -1;\r
+ \r
+ if (card->angle > 90.0f && card->angle < 270.0f && (bp->tick > card->start_frame || bp->tick < card->end_frame))\r
+ {\r
+ int n = card->suit * 13 + card->rank - 1;\r
+ current_texture = bp->fronts[n];\r
+ s = -1.0f;\r
+ }\r
+ else\r
+ {\r
+ current_texture = bp->back;\r
+ }\r
+\r
+ tx = card->x;\r
+ ty = card->y;\r
+ tz = card->z * 0.025f;\r
+\r
+ float translateX = tx + offset_x;\r
+ float translateY = ty + offset_y;\r
+ float translateZ = tz;\r
+\r
+ float horiz_card_aspect_scale = 0.53 * height / width;\r
+ float vert_card_aspect_scale = 0.53;\r
+\r
+ if (width < height)\r
+ {\r
+ horiz_card_aspect_scale *= width / 1280.0f;\r
+ vert_card_aspect_scale *= width / 1280.0f;\r
+ }\r
+\r
+ float scaleX = s * horiz_card_aspect_scale * 0.45f * scale;\r
+ float scaleY = vert_card_aspect_scale * 0.45f * scale;\r
+ float scaleZ = horiz_card_aspect_scale * 0.45f * scale;\r
+\r
+ glTranslatef(translateX, translateY, translateZ);\r
+ glRotatef(card->angle, 0.0f, 1.0f, 0.0f);\r
+ glScalef(scaleX, scaleY, scaleZ);\r
+\r
+ // Bind the appropriate texture\r
+ glBindTexture(GL_TEXTURE_2D, current_texture);\r
+\r
+ // Draw the card quad with proper texture coordinates\r
+ glBegin(GL_QUADS);\r
+ // Front face\r
+ glNormal3f(0.0f, 0.0f, 1.0f);\r
+ glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.75f, 0.0f);\r
+ glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.75f, 0.0f);\r
+ glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.75f, 0.0f);\r
+ glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.75f, 0.0f);\r
+ glEnd();\r
+\r
+ glPopMatrix();\r
+ }\r
+\r
+ if (mi->fps_p) do_fps (mi);\r
+ \r
+ glFinish();\r
+ glXSwapBuffers(dpy, window);\r
+\r
+ if (bp->tick >= last_animation)\r
+ {\r
+ game_state_struct *n = NULL;\r
+ if (bp->redeal)\r
+ {\r
+ n = (game_state_struct *)malloc(sizeof(game_state_struct));\r
+ bp->tick = 0;\r
+ bp->final_animation = 0;\r
+ bp->redeal = 0;\r
+ }\r
+ else if (bp->final_animation == 0)\r
+ {\r
+ n = klondike_next_move(bp);\r
+ }\r
+\r
+ if (bp->final_animation != 0 && bp->tick >= bp->final_animation)\r
+ {\r
+ // resetBoard(game_state);\r
+ bp->redeal = 1;\r
+ }\r
+ else if (bp->final_animation == 0 && n == NULL)\r
+ {\r
+ animate_board_to_deck(bp);\r
+\r
+ // Check for win\r
+# if 0 // unused \r
+ int won_game = 0;\r
+ for (int i = 0; i < 4; i++)\r
+ {\r
+ won_game &= (bp->game_state->foundation_size[i] == 13);\r
+ }\r
+# endif \r
+ }\r
+\r
+ if (n != NULL)\r
+ {\r
+ klondike_free_game_state(bp->game_state);\r
+ bp->game_state = n;\r
+ }\r
+ }\r
+}\r
+\r
+ENTRYPOINT void\r
+free_klondike(ModeInfo *mi)\r
+{\r
+ klondike_configuration *bp = &bps[MI_SCREEN(mi)];\r
+ if (!bp->glx_context)\r
+ return;\r
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);\r
+\r
+ // Free the textures\r
+ for (int i = 0; i < 52; i++)\r
+ {\r
+ glDeleteTextures(1, &bp->fronts[i]);\r
+ }\r
+\r
+ glDeleteTextures(1, &bp->back);\r
+\r
+ klondike_free_game_state(bp->game_state);\r
+}\r
+\r
+XSCREENSAVER_MODULE_2("Klondike", klondike, klondike)\r
+\r
+#endif /* USE_GL */\r
--- /dev/null
+/* platonicfolding --- Displays the unfolding and folding of the Platonic
+ solids. */
+
+#if 0
+static const char sccsid[] = "@(#)platonicfolding.c 1.1 25/03/18 xlockmore";
+#endif
+
+/* Copyright (c) 2025 Carsten Steger <carsten@mirsanmir.org>. */
+
+/*
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * REVISION HISTORY:
+ * C. Steger - 25/03/18: Initial version
+ */
+
+/*
+ * This program shows the unfolding and folding of the Platonic
+ * solids. For the five Platonic solids (the tetrahedron, cube,
+ * octahedron, dodecahedron, and icosahedron), all unfoldings of its
+ * faces are non-overlapping: they form a net. The tetrahedron has 16
+ * unfoldings, of which two are essentially different
+ * (non-isomorphic), the cube and octahedron each have 384 unfoldings,
+ * of which eleven are non-isomorphic, and the dodecahedron and
+ * icosahedron each have 5,184,000 unfoldings, of which 43,380 are
+ * non-isomorphic. This program displays randomly selected unfoldings
+ * for the five Platonic solids. Note that while it is guaranteed
+ * that the nets of the Platonic solids are non-overlapping, their
+ * faces occasionally intersect during the unfolding and folding.
+ *
+ * The program displays the Platonic solids either using different
+ * colors for each face (face colors) or with a illuminated view of
+ * the earth (earth colors). When using face colors, the colors of
+ * the faces are randomly chosen each time a new Platonic solid is
+ * selected. When using earth colors, the Platonic solid is displayed
+ * as if the sphere of the earth were illuminated with the current
+ * position of the sun at the time the program is run. The hemisphere
+ * the sun is currently illuminating is displayed with a satellite
+ * image of the earth by day and the other hemisphere is displayed
+ * with a satellite image of the earth by night. The specular
+ * highlight on the illuminated hemisphere (which is only shown over
+ * bodies of water) is the subsolar point (the point on earth above
+ * which the sun is perpendicular). The earth's sphere is then
+ * projected onto the Platonic solid via a gnomonic projection. The
+ * program randomly selects whether the north pole or the south pole
+ * is facing upwards. The inside of the earth is displayed with a
+ * magma-like texture.
+ *
+ * At the beginning of each cycle, the program selects one of the five
+ * Platonic solids randomly and moves it to the center of the screen.
+ * It then repeatedly selects a random net of the polyhedron and
+ * unfolds and folds the polyhedron. The unfolding and folding can
+ * occur around each edge of the net successively or around all edges
+ * simultaneously. At the end of each cycle, the Platonic solid is
+ * moved offscreen and the next cycle begins.
+ *
+ * While the Platonic solid is moved on the screen or is unfolded or
+ * folded, it is rotated by default. If earth colors are used, the
+ * rotation is always performed in the direction the earth is rotating
+ * (counterclockwise as viewed from the north pole towards the center
+ * of the earth). This rotation optionally can be switched off.
+ */
+
+
+#define DEF_ROTATE "True"
+#define DEF_COLORS "random"
+#define DEF_FOLDINGS "random"
+
+#define COLORS_FACE 0
+#define COLORS_EARTH 1
+#define NUM_COLORS 2
+
+#define RANDOM_NUM_FOLDINGS -1
+
+#ifdef STANDALONE
+# define DEFAULTS "*delay: 25000 \n" \
+ "*showFPS: False \n" \
+ "*prefersGLSL: True \n" \
+
+# define release_platonicfolding 0
+# include "xlockmore.h" /* from the xscreensaver distribution */
+#else /* !STANDALONE */
+# include "xlock.h" /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+#ifdef USE_GL
+
+/* The possible animation states. */
+#define ANIM_INIT 0
+#define ANIM_APPEAR 1
+#define ANIM_DISAPPEAR 2
+#define ANIM_UNFOLD_JNT 3
+#define ANIM_FOLD_JNT 4
+#define ANIM_UNFOLD_SEP 5
+#define ANIM_FOLD_SEP 6
+
+/* The possible easing functions. */
+#define EASING_QUINTIC 0
+#define EASING_ACCEL 1
+#define EASING_DECEL 2
+
+/* Hash size for the Perlin noise generator. */
+#define HASH_SIZE 256
+
+/* Number of octaves of the Perlin noise generator to use for the magma
+ noise texture. */
+#define NUM_OCTAVES 4
+
+/* Frequency of the first octave of the Perlin noise generator to use for
+ the magma noise texture. */
+#define START_FREQUENCY 4
+
+/* Size of one dimension of the 3D magma noise texture. */
+#define MAGMA_TEX_SIZE 128
+
+/* Gamma value to use for the magma look-up table. */
+#define MAGMA_LUT_GAMMA (1.0f/1.5f)
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+#define M_PI_F 3.14159265359f
+#define SQRT_2_2_F 0.70710678119f
+#define SQRT_2_4_F 0.35355339059f
+#define SQRT_3_2_F 0.86602540379f
+#define COS_36_F 0.80901699438f
+#define SIN_36_F 0.58778525229f
+#define COS_72_F 0.30901699438f
+#define SIN_72_F 0.95105651630f
+#define DODECA_IN_RAD_F 1.30901699437f
+#define ICOSA_IN_RAD_F 1.30901699437f
+
+/* The maximum stack size for the traversal of the polygon tree that
+ represents the folding and unfolding of a Platonic solid. */
+#define MAX_STACK 20
+
+/* The maximum number of folding angles. */
+#define NUM_ANGLES 19
+
+/* The maximum number of vertices over all polygons of any unfolding. This
+ is achieved for the dodecahedron (12*5 vertices) and the icosahedron
+ (20*3 vertices). */
+#define NUM_VERTEX 60
+
+/* Constants to label vertices during the computation of the minimum
+ spanning tree of an unfolding of a Platonic solid. */
+#define UNSEEN (-1)
+#define SEEN (-2)
+
+/* The different Platonic solid types. */
+#define TETRAHEDRON 0
+#define HEXAHEDRON 1
+#define OCTAHEDRON 2
+#define DODECAHEDRON 3
+#define ICOSAHEDRON 4
+
+/* The number of faces and edges for each Platonic solid. */
+#define TETRAHEDRON_NUM_FACES 4
+#define TETRAHEDRON_NUM_EDGES 6
+#define HEXAHEDRON_NUM_FACES 6
+#define HEXAHEDRON_NUM_EDGES 12
+#define OCTAHEDRON_NUM_FACES 8
+#define OCTAHEDRON_NUM_EDGES 12
+#define DODECAHEDRON_NUM_FACES 12
+#define DODECAHEDRON_NUM_EDGES 30
+#define ICOSAHEDRON_NUM_FACES 20
+#define ICOSAHEDRON_NUM_EDGES 30
+
+/* The maximum rotation angle for a triangle in the folding of a
+ tetrahedron. This is 180° minus the dihedral angle of the faces, which
+ is acos(1/3) = atan(2*sqrt(2)). Hence, this angle is acos(-1/3) =
+ 2*atan(sqrt(2)). */
+#define TETRAHEDRON_MAX_ANGLE 109.4712206344907f
+#define TETRAHEDRON_DELTA_ANGLE (TETRAHEDRON_MAX_ANGLE/30.0f)
+
+/* The maximum rotation angle for a square in the folding of a hexahedron
+ (cube). This is 180° minus the dihedral angle of the faces, which
+ obviously is 90°. */
+#define HEXAHEDRON_MAX_ANGLE 90.0f
+#define HEXAHEDRON_DELTA_ANGLE (HEXAHEDRON_MAX_ANGLE/30.0f)
+
+/* The maximum rotation angle for a triangle in the folding of an
+ octahedron. This is 180° minus the dihedral angle of the faces, which
+ is acos(-1/3) = 2*atan(sqrt(2)). Hence, this angle is acos(1/3) =
+ atan(2*sqrt(2)). */
+#define OCTAHEDRON_MAX_ANGLE 70.5287793655093f
+#define OCTAHEDRON_DELTA_ANGLE (OCTAHEDRON_MAX_ANGLE/30.0f)
+
+/* The maximum rotation angle for a pentagon in the folding of a
+ dodecahedron. This is 180° minus the dihedral angle of the faces, which
+ is 180°-2*atan((sqrt(5)-1)/2). Hence, this angle is
+ 2*atan((sqrt(5)-1)/2). */
+#define DODECAHEDRON_MAX_ANGLE 63.4349488229220f
+#define DODECAHEDRON_DELTA_ANGLE (DODECAHEDRON_MAX_ANGLE/30.0f)
+
+/* The maximum rotation angle for a triangle in the folding of an
+ icosahedron. This is 180° minus the dihedral angle of the faces, which
+ is 180°-2*atan((3-sqrt(5))/2). Hence, this angle is
+ 2*atan((3-sqrt(5))/2). */
+#define ICOSAHEDRON_MAX_ANGLE 41.8103148957786f
+#define ICOSAHEDRON_DELTA_ANGLE (ICOSAHEDRON_MAX_ANGLE/30.0f)
+
+
+#include <stdbool.h>
+#include "glsl-utils.h"
+#include "gltrackball.h"
+
+#include "images/gen/earth_png.h"
+#include "images/gen/earth_night_png.h"
+#include "images/gen/earth_water_png.h"
+#include "ximage-loader.h"
+
+
+#ifdef USE_MODULES
+ModStruct platonicfolding_description =
+{"platonicfolding", "init_platonicfolding", "draw_platonicfolding",
+ NULL, "draw_platonicfolding", "change_platonicfolding",
+ "free_platonicfolding", &platonicfolding_opts, 20000, 1, 1, 1, 1.0, 4, "",
+ "Display the unfolding and folding of Platonic solids",
+ 0, NULL};
+#endif
+
+
+static Bool rotate;
+static char *color_mode;
+static char *foldings;
+
+
+static XrmOptionDescRec opts[] =
+{
+ {"-rotate", ".rotate", XrmoptionNoArg, "on"},
+ {"+rotate", ".rotate", XrmoptionNoArg, "off"},
+ {"-colors", ".colors", XrmoptionSepArg, 0 },
+ {"-face-colors", ".colors", XrmoptionNoArg, "face" },
+ {"-earth-colors", ".colors", XrmoptionNoArg, "earth" },
+ {"-foldings", ".foldings", XrmoptionSepArg, 0 },
+};
+
+static argtype vars[] =
+{
+ { &rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool },
+ { &color_mode, "colors", "Colors", DEF_COLORS, t_String },
+ { &foldings, "foldings", "Foldings", DEF_FOLDINGS, t_String }
+};
+
+ENTRYPOINT ModeSpecOpt platonicfolding_opts =
+{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL};
+
+
+/* ------------------------------------------------------------------------- */
+
+
+typedef float vertex[4];
+typedef float normal[4];
+typedef float color[4];
+typedef float texcoord[3];
+typedef float matrix[4][4];
+
+/* Data structure to represent an edge of a graph that represents the
+ adjacency of polygons in a polyhedron. The variables src and dst
+ describe which polygon faces are adjacent. The variables src_edge and
+ dst_edge describe which edges of the respective faces are touching.
+ The variable weight is set to a random value to create the minimum
+ spanning tree. */
+typedef struct {
+ int src, dst;
+ int src_edge, dst_edge;
+ float weight;
+} edge;
+
+/* Data structure to represent an edge-based graph with num_v vertices,
+ num_e edges, and an array of edges. The graph represents the adjacency
+ of faces in a polyhedron via the array edges. */
+typedef struct {
+ int num_v, num_e;
+ edge *edges;
+} edge_graph;
+
+/* Disjoint set union (DSU) data structure with parent and rank. This is
+ used in the algorithm that determines the edges of a minimum spanning
+ tree of a polyhedron. */
+typedef struct {
+ int *parent;
+ int *rank;
+} dsu;
+
+/* Data structure for an adjacency-list-based graph. This is used to
+ convert the edges of the minimum spanning tree into a graph that is then
+ traversed to create the actual minimum spanning tree. The variable v
+ stores the face number of the polygon in the polyhedron. The variables
+ edge_parent and edge_self store the edges by which a polygon that is
+ adjacent to v is connected to the polygon v. The value of edge_parent
+ refers to the edge in the parent polygon of v in the graph, while
+ edge_self refers to the edge in the polygon v. */
+typedef struct graph_node {
+ struct graph_node *next;
+ int v;
+ int edge_parent, edge_self;
+} graph_node;
+
+/* The data structure for a tree that describes an unfolded and folded
+ polyhedron. The pointer next points to the next base polygon on the
+ same tree level. The pointer child points to the first polygon on the
+ next lower tree level. Hence, the unfolding is described by a general
+ tree. The index of the polygon in the polyhedron is given by
+ polygon_index. This is used to color the polygons in different foldings
+ consistently. The indices edge_parent and edge_self contain the indices
+ of the edge in the parent polygon and the edge in the polygon described
+ by the current node that are attached to each other. Since it is assumed
+ that the base polygon is oriented counterclockwise, the edges point in
+ opposite directions. The root of the tree has no parent. Therefore,
+ both indices must be set to -1 for the root node. The matrix
+ unfold_pose describes the pose of the polygon described by the node with
+ respect to its parent polygon in the unfolded state. Hence, it is a
+ relative pose and not an absolute pose. The variable fold_angle_index
+ is used to determine which angle to use for the rotation of the polygon
+ described by a node with respect to its parent polygon. The values of
+ unfold_pose and fold_angle_index are determined dynamically by the order
+ of the tree traversal in the initialization function
+ determine_unfolding_poses. The variable fold_angle stores the current
+ fold angle of the polygon described by the node with respect to its
+ parent. The matrix fold_pose stores the relative pose obtained by
+ rotating the polygon described by a node with respect to its parent
+ polygon around the edge described by edge_parent and edge_self by the
+ angle fold_angle. The values of fold_angle and fold_pose are typically
+ determined when the folded polyhedron is drawn. */
+typedef struct tree_node {
+ struct tree_node *next;
+ struct tree_node *child;
+ int polygon_index;
+ int edge_parent, edge_self;
+ matrix unfold_pose;
+ int fold_angle_index;
+ float fold_angle;
+ matrix fold_pose;
+} tree_node;
+
+/* The data structure for a polygon consisting of num vertices v, a single
+ normal vector n, num texture coordinates t (one per vertex), and a
+ color c. */
+typedef struct {
+ int num;
+ vertex *v;
+ normal n;
+ texcoord *t;
+ color c;
+} polygon;
+
+/* The data structure for a set of polygons with polygon-specific data such
+ as texture coordinates for each vertex. */
+typedef struct {
+ int num;
+ polygon *poly;
+} polygons;
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* The edge-based adjacency graph of a tetrahedron. */
+static edge tetrahedron_edges[TETRAHEDRON_NUM_EDGES] = {
+ { 0, 1, 0, 0, 0.0f },
+ { 0, 2, 2, 0, 0.0f },
+ { 0, 3, 1, 0, 0.0f },
+ { 1, 2, 1, 2, 0.0f },
+ { 1, 3, 2, 1, 0.0f },
+ { 2, 3, 1, 2, 0.0f }
+};
+
+/* The edge-based adjacency graph of a hexahedron. */
+static edge hexahedron_edges[HEXAHEDRON_NUM_EDGES] = {
+ { 0, 1, 0, 0, 0.0f },
+ { 0, 2, 1, 0, 0.0f },
+ { 0, 3, 2, 0, 0.0f },
+ { 0, 4, 3, 0, 0.0f },
+ { 1, 2, 3, 1, 0.0f },
+ { 1, 4, 1, 3, 0.0f },
+ { 1, 5, 2, 0, 0.0f },
+ { 2, 3, 3, 1, 0.0f },
+ { 2, 5, 2, 3, 0.0f },
+ { 3, 4, 3, 1, 0.0f },
+ { 3, 5, 2, 2, 0.0f },
+ { 4, 5, 2, 1, 0.0f }
+};
+
+/* The edge-based adjacency graph of an octahedron. */
+static edge octahedron_edges[OCTAHEDRON_NUM_EDGES] = {
+ { 0, 1, 0, 0, 0.0f },
+ { 0, 2, 1, 0, 0.0f },
+ { 0, 3, 2, 0, 0.0f },
+ { 1, 4, 1, 2, 0.0f },
+ { 1, 5, 2, 1, 0.0f },
+ { 2, 5, 1, 0, 0.0f },
+ { 2, 6, 2, 0, 0.0f },
+ { 3, 4, 2, 0, 0.0f },
+ { 3, 6, 1, 2, 0.0f },
+ { 4, 7, 1, 1, 0.0f },
+ { 5, 7, 2, 0, 0.0f },
+ { 6, 7, 1, 2, 0.0f }
+};
+
+/* The edge-based adjacency graph of a dodecahedron. */
+static edge dodecahedron_edges[DODECAHEDRON_NUM_EDGES] = {
+ { 0, 1, 0, 0, 0.0f },
+ { 0, 2, 1, 4, 0.0f },
+ { 0, 3, 2, 0, 0.0f },
+ { 0, 4, 3, 3, 0.0f },
+ { 0, 5, 4, 1, 0.0f },
+ { 1, 2, 4, 0, 0.0f },
+ { 1, 5, 1, 0, 0.0f },
+ { 1, 6, 2, 0, 0.0f },
+ { 1, 7, 3, 0, 0.0f },
+ { 2, 3, 3, 1, 0.0f },
+ { 2, 7, 1, 4, 0.0f },
+ { 2, 8, 2, 2, 0.0f },
+ { 3, 4, 4, 4, 0.0f },
+ { 3, 8, 2, 1, 0.0f },
+ { 3, 9, 3, 0, 0.0f },
+ { 4, 5, 2, 2, 0.0f },
+ { 4, 9, 0, 4, 0.0f },
+ { 4, 10, 1, 4, 0.0f },
+ { 5, 6, 4, 1, 0.0f },
+ { 5, 10, 3, 3, 0.0f },
+ { 6, 7, 4, 1, 0.0f },
+ { 6, 10, 2, 2, 0.0f },
+ { 6, 11, 3, 3, 0.0f },
+ { 7, 8, 3, 3, 0.0f },
+ { 7, 11, 2, 2, 0.0f },
+ { 8, 9, 0, 1, 0.0f },
+ { 8, 11, 4, 1, 0.0f },
+ { 9, 10, 3, 0, 0.0f },
+ { 9, 11, 2, 0, 0.0f },
+ { 10, 11, 1, 4, 0.0f }
+};
+
+/* The edge-based adjacency graph of an icosahedron. */
+static edge icosahedron_edges[ICOSAHEDRON_NUM_EDGES] = {
+ { 0, 1, 2, 0, 0.0f },
+ { 0, 2, 0, 0, 0.0f },
+ { 0, 3, 1, 0, 0.0f },
+ { 1, 4, 2, 0, 0.0f },
+ { 1, 9, 1, 0, 0.0f },
+ { 2, 5, 1, 2, 0.0f },
+ { 2, 6, 2, 1, 0.0f },
+ { 3, 7, 1, 0, 0.0f },
+ { 3, 8, 2, 0, 0.0f },
+ { 4, 5, 2, 0, 0.0f },
+ { 4, 10, 1, 0, 0.0f },
+ { 5, 15, 1, 2, 0.0f },
+ { 6, 7, 0, 1, 0.0f },
+ { 6, 14, 2, 1, 0.0f },
+ { 7, 13, 2, 0, 0.0f },
+ { 8, 9, 2, 1, 0.0f },
+ { 8, 12, 1, 2, 0.0f },
+ { 9, 11, 2, 1, 0.0f },
+ { 10, 11, 1, 0, 0.0f },
+ { 10, 18, 2, 0, 0.0f },
+ { 11, 17, 2, 1, 0.0f },
+ { 12, 13, 0, 2, 0.0f },
+ { 12, 17, 1, 2, 0.0f },
+ { 13, 16, 1, 0, 0.0f },
+ { 14, 15, 2, 1, 0.0f },
+ { 14, 16, 0, 1, 0.0f },
+ { 15, 18, 0, 2, 0.0f },
+ { 16, 19, 2, 0, 0.0f },
+ { 17, 19, 0, 2, 0.0f },
+ { 18, 19, 1, 1, 0.0f }
+};
+
+/* The vertices of a tetrahedron triangle. All vertices lie on the unit
+ circle. */
+static vertex tetrahedron_triangle_vert[3] = {
+ { 1.0f, 0.0f, -SQRT_2_4_F, 1.0f },
+ { -0.5f, SQRT_3_2_F, -SQRT_2_4_F, 1.0f },
+ { -0.5f, -SQRT_3_2_F, -SQRT_2_4_F, 1.0f }
+};
+
+/* The triangle that is used for the tetrahedron. */
+static polygon tetrahedron_triangle = {
+ 3,
+ tetrahedron_triangle_vert,
+ { 0.0f, 0.0f, 1.0f, 0.0f },
+ NULL,
+ { 0.0f, 0.0f, 0.0f, 1.0f }
+};
+
+/* The eye position for a terahedron. */
+static const GLfloat tetrahedron_eye_pos[3] = { 0.0f, 0.0f, 6.0f };
+
+
+/* The vertices of a hexahedron square. All vertices lie on the unit
+ circle. */
+static vertex hexahedron_square_vert[4] = {
+ { SQRT_2_2_F, -SQRT_2_2_F, -SQRT_2_2_F, 1.0f },
+ { SQRT_2_2_F, SQRT_2_2_F, -SQRT_2_2_F, 1.0f },
+ { -SQRT_2_2_F, SQRT_2_2_F, -SQRT_2_2_F, 1.0f },
+ { -SQRT_2_2_F, -SQRT_2_2_F, -SQRT_2_2_F, 1.0f }
+};
+
+/* The square that is used for the hexahedron (cube). */
+static polygon hexahedron_square = {
+ 4,
+ hexahedron_square_vert,
+ { 0.0f, 0.0f, 1.0f, 0.0f },
+ NULL,
+ { 0.0f, 0.0f, 0.0f, 1.0f }
+};
+
+/* The eye position for a hexahedron. */
+static const GLfloat hexahedron_eye_pos[3] = { 0.0f, 0.0f, 6.0f };
+
+
+/* The vertices of an octahedron triangle. All vertices lie on the unit
+ circle. */
+static vertex octahedron_triangle_vert[3] = {
+ { 1.0f, 0.0f, -SQRT_2_2_F, 1.0f },
+ { -0.5f, SQRT_3_2_F, -SQRT_2_2_F, 1.0f },
+ { -0.5f, -SQRT_3_2_F, -SQRT_2_2_F, 1.0f }
+};
+
+/* The triangle that is used for the octahedron. */
+static polygon octahedron_triangle = {
+ 3,
+ octahedron_triangle_vert,
+ { 0.0f, 0.0f, 1.0f, 0.0f },
+ NULL,
+ { 0.0f, 0.0f, 0.0f, 1.0f }
+};
+
+/* The eye position for an octahedron. */
+static const GLfloat octahedron_eye_pos[3] = { 0.0f, 0.0f, 6.0f };
+
+
+/* The vertices of a dodecahedron pentagon. All vertices lie on the unit
+ circle. */
+static vertex dodecahedron_pentagon_vert[5] = {
+ { 1.0f, 0.0f, -DODECA_IN_RAD_F, 1.0f },
+ { COS_72_F, SIN_72_F, -DODECA_IN_RAD_F, 1.0f },
+ { -COS_36_F, SIN_36_F, -DODECA_IN_RAD_F, 1.0f },
+ { -COS_36_F, -SIN_36_F, -DODECA_IN_RAD_F, 1.0f },
+ { COS_72_F, -SIN_72_F, -DODECA_IN_RAD_F, 1.0f }
+};
+
+/* The pentagon that is used for the dodecahedron. */
+static polygon dodecahedron_pentagon = {
+ 5,
+ dodecahedron_pentagon_vert,
+ { 0.0f, 0.0f, 1.0f, 0.0f },
+ NULL,
+ { 0.0f, 0.0f, 0.0f, 1.0f }
+};
+
+/* The eye position for a dodecahedron. */
+static const GLfloat dodecahedron_eye_pos[3] = { 0.0f, 0.0f, 12.0f };
+
+
+/* The vertices of a icosahedron triangle. All vertices lie on the unit
+ circle. */
+static vertex icosahedron_triangle_vert[3] = {
+ { 1.0f, 0.0f, -ICOSA_IN_RAD_F, 1.0f },
+ { -0.5f, SQRT_3_2_F, -ICOSA_IN_RAD_F, 1.0f },
+ { -0.5f, -SQRT_3_2_F, -ICOSA_IN_RAD_F, 1.0f }
+};
+
+/* The triangle that is used for the icosahedron. */
+static polygon icosahedron_triangle = {
+ 3,
+ icosahedron_triangle_vert,
+ { 0.0f, 0.0f, 1.0f, 0.0f },
+ NULL,
+ { 0.0f, 0.0f, 0.0f, 1.0f }
+};
+
+/* The eye position for an icosahedron. */
+static const GLfloat icosahedron_eye_pos[3] = { 0.0f, 0.0f, 12.0f };
+
+
+/* ------------------------------------------------------------------------- */
+
+
+typedef struct {
+ GLint WindH, WindW;
+ GLXContext *glx_context;
+ /* Options */
+ bool rotate;
+ int colors;
+ bool random_colors;
+ int num_foldings;
+ /* Aspect ratio of the current window */
+ float aspect;
+ /* Trackball states */
+ trackball_state *trackball;
+ Bool button_pressed;
+ /* The folding angle data. */
+ float angle[NUM_ANGLES];
+ float max_angle, delta_angle, angle_dir;
+ /* The index of the current angle that is folded or unfolded. */
+ int fold_angle;
+ /* The number of angles that can be folded for the current polyhedron. */
+ int num_fold_angles;
+ /* The 3D rotation angles. */
+ float alpha, beta, delta, delta_delta;
+ /* The eype position of the current polyhedron. */
+ const GLfloat *eye_pos;
+ /* The unfolding tree of the current polyhedron. */
+ tree_node *polygon_unfolding;
+ /* The base polygons of the current polyhedron. */
+ polygons *base_polygons;
+ /* Should the north pole or the south pole face upwards? */
+ bool north_up;
+ /* Animation state variables. */
+ int anim_state, anim_poly, anim_step, anim_num_steps, anim_remaining;
+ float poly_pos[3], spoly_pos[3], dpoly_pos[3];
+ /* The current color rotation matrix. */
+ matrix color_matrix;
+ /* Buffers for drawing a polyhedron. */
+ vertex vtx[NUM_VERTEX];
+ normal nrm[NUM_VERTEX];
+ color col[NUM_VERTEX];
+ texcoord tex[NUM_VERTEX];
+ int num[NUM_VERTEX];
+#ifdef HAVE_GLSL
+ bool use_shaders, use_mipmaps;
+ GLuint shader_program;
+ GLint pos_index, normal_index, color_index, tex_index;
+ GLint mv_index, proj_index;
+ GLint glbl_ambient_index, lt_ambient_index;
+ GLint lt_diffuse_index, lt_specular_index;
+ GLint lt_direction_index, lt_halfvect_index;
+ GLint ambient_index, diffuse_index;
+ GLint specular_index, shininess_index;
+ GLint bool_textures_index, north_up_index, magma_offs_index;
+ GLint samp_mgm_index, samp_day_index, samp_ngt_index, samp_wtr_index;
+ GLuint vertex_buffer, normal_buffer, color_buffer, tex_buffer;
+ GLuint magma_tex, earth_tex[3];
+ GLubyte *magma_tex_rg;
+ GLfloat magma_tex_offset[3];
+ bool textures_supported, aniso_textures, use_textures;
+#endif /* HAVE_GLSL */
+} platonicfoldingstruct;
+
+static platonicfoldingstruct *platonicfolding = (platonicfoldingstruct *) NULL;
+
+
+/* ------------------------------------------------------------------------- */
+
+
+#ifdef HAVE_GLSL
+
+/* The hash table for the Perlin noise generator. In Perlin's original
+ code, only half of this table is stored and then copied to a hash table
+ twice the size. We directly store the table of twice the size. */
+static GLushort permutation[2*HASH_SIZE] = {
+ 151, 160, 137, 91, 90, 15, 131, 13,
+ 201, 95, 96, 53, 194, 233, 7, 225,
+ 140, 36, 103, 30, 69, 142, 8, 99,
+ 37, 240, 21, 10, 23, 190, 6, 148,
+ 247, 120, 234, 75, 0, 26, 197, 62,
+ 94, 252, 219, 203, 117, 35, 11, 32,
+ 57, 177, 33, 88, 237, 149, 56, 87,
+ 174, 20, 125, 136, 171, 168, 68, 175,
+ 74, 165, 71, 134, 139, 48, 27, 166,
+ 77, 146, 158, 231, 83, 111, 229, 122,
+ 60, 211, 133, 230, 220, 105, 92, 41,
+ 55, 46, 245, 40, 244, 102, 143, 54,
+ 65, 25, 63, 161, 1, 216, 80, 73,
+ 209, 76, 132, 187, 208, 89, 18, 169,
+ 200, 196, 135, 130, 116, 188, 159, 86,
+ 164, 100, 109, 198, 173, 186, 3, 64,
+ 52, 217, 226, 250, 124, 123, 5, 202,
+ 38, 147, 118, 126, 255, 82, 85, 212,
+ 207, 206, 59, 227, 47, 16, 58, 17,
+ 182, 189, 28, 42, 223, 183, 170, 213,
+ 119, 248, 152, 2, 44, 154, 163, 70,
+ 221, 153, 101, 155, 167, 43, 172, 9,
+ 129, 22, 39, 253, 19, 98, 108, 110,
+ 79, 113, 224, 232, 178, 185, 112, 104,
+ 218, 246, 97, 228, 251, 34, 242, 193,
+ 238, 210, 144, 12, 191, 179, 162, 241,
+ 81, 51, 145, 235, 249, 14, 239, 107,
+ 49, 192, 214, 31, 181, 199, 106, 157,
+ 184, 84, 204, 176, 115, 121, 50, 45,
+ 127, 4, 150, 254, 138, 236, 205, 93,
+ 222, 114, 67, 29, 24, 72, 243, 141,
+ 128, 195, 78, 66, 215, 61, 156, 180,
+ 151, 160, 137, 91, 90, 15, 131, 13,
+ 201, 95, 96, 53, 194, 233, 7, 225,
+ 140, 36, 103, 30, 69, 142, 8, 99,
+ 37, 240, 21, 10, 23, 190, 6, 148,
+ 247, 120, 234, 75, 0, 26, 197, 62,
+ 94, 252, 219, 203, 117, 35, 11, 32,
+ 57, 177, 33, 88, 237, 149, 56, 87,
+ 174, 20, 125, 136, 171, 168, 68, 175,
+ 74, 165, 71, 134, 139, 48, 27, 166,
+ 77, 146, 158, 231, 83, 111, 229, 122,
+ 60, 211, 133, 230, 220, 105, 92, 41,
+ 55, 46, 245, 40, 244, 102, 143, 54,
+ 65, 25, 63, 161, 1, 216, 80, 73,
+ 209, 76, 132, 187, 208, 89, 18, 169,
+ 200, 196, 135, 130, 116, 188, 159, 86,
+ 164, 100, 109, 198, 173, 186, 3, 64,
+ 52, 217, 226, 250, 124, 123, 5, 202,
+ 38, 147, 118, 126, 255, 82, 85, 212,
+ 207, 206, 59, 227, 47, 16, 58, 17,
+ 182, 189, 28, 42, 223, 183, 170, 213,
+ 119, 248, 152, 2, 44, 154, 163, 70,
+ 221, 153, 101, 155, 167, 43, 172, 9,
+ 129, 22, 39, 253, 19, 98, 108, 110,
+ 79, 113, 224, 232, 178, 185, 112, 104,
+ 218, 246, 97, 228, 251, 34, 242, 193,
+ 238, 210, 144, 12, 191, 179, 162, 241,
+ 81, 51, 145, 235, 249, 14, 239, 107,
+ 49, 192, 214, 31, 181, 199, 106, 157,
+ 184, 84, 204, 176, 115, 121, 50, 45,
+ 127, 4, 150, 254, 138, 236, 205, 93,
+ 222, 114, 67, 29, 24, 72, 243, 141,
+ 128, 195, 78, 66, 215, 61, 156, 180
+};
+
+
+/* The fade function of Perlin's improved noise generator. */
+static inline float fade(float t)
+{
+ return ((6.0f*t-15.0f)*t+10.0f)*t*t*t;
+}
+
+
+/* The linear interpolation function of Perlin's improved noise generator. */
+static inline float lerp(float t, float a, float b)
+{
+ return a+t*(b-a);
+}
+
+
+/* The gradient noise generator of Perlin's improved noise generator. */
+static float grad(GLushort hash, float x, float y, float z)
+{
+ GLushort h;
+ float u, v;
+
+ /* Convert the low four bits of the hash code into twelve gradient
+ directions. */
+ h = hash&15;
+ u = h<8 ? x : y;
+ v = h<4 ? y : h==12 || h==14 ? x : z;
+ return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
+}
+
+
+/* Perlin's improved noise generator. */
+static float noise3(float x, float y, float z)
+{
+ static const GLushort *p = permutation;
+ float u, v, w;
+ GLushort X, Y, Z, A, B, AA, AB, BA, BB;
+
+ /* Find unit cube that contains the point (x,y,z). */
+ X = (int)floorf(x) & (HASH_SIZE-1);
+ Y = (int)floorf(y) & (HASH_SIZE-1);
+ Z = (int)floorf(z) & (HASH_SIZE-1);
+ /* Find the relative x, y, and z coordinates of the point in the cube. */
+ x -= floorf(x);
+ y -= floorf(y);
+ z -= floorf(z);
+ /* Compute fade curves for each of x, y, and z. */
+ u = fade(x);
+ v = fade(y);
+ w = fade(z);
+ /* Compute the hash coordinates of the eight cube corners. */
+ A = p[X]+Y;
+ AA = p[A]+Z;
+ AB = p[A+1]+Z;
+ B = p[X+1]+Y;
+ BA = p[B]+Z;
+ BB = p[B+1]+Z;
+ /* Add the blended results from the eight corners of the cube. */
+ return lerp(w,lerp(v,lerp(u,grad(p[AA],x,y,z),
+ grad(p[BA],x-1.0f,y,z)),
+ lerp(u,grad(p[AB],x,y-1.0f,z),
+ grad(p[BB],x-1.0f,y-1.0f,z))),
+ lerp(v,lerp(u,grad(p[AA+1],x,y,z-1.0f),
+ grad(p[BA+1],x-1.0f,y,z-1.0f)),
+ lerp(u,grad(p[AB+1],x,y-1.0f,z-1.0f),
+ grad(p[BB+1],x-1.0f,y-1.0f,z-1.0f))));
+}
+
+
+/* The smoothstep function of the OpenGL shading language. */
+static inline float smoothstep(float min, float max, float x)
+{
+ float t;
+
+ if (x < min)
+ {
+ return 0.0f;
+ }
+ else if (x > max)
+ {
+ return 1.0f;
+ }
+ else
+ {
+ t = (x-min)/(max-min);
+ return t*t*(3.0f-2.0f*t);
+ }
+}
+
+
+/* Create a red and green look-up table that simulates the look of a heated
+ material. The blue look-up table would entirely consist of zeros, so we
+ don't create it. */
+static void create_magma_lut_rg(GLubyte lutr[256], GLubyte lutg[256])
+{
+ int i;
+
+ for (i=0; i<256; i++)
+ {
+ lutr[i] = (GLubyte)(255.0f*powf(smoothstep(0.0f,159.0f,(float)i),
+ MAGMA_LUT_GAMMA));
+ lutg[i] = (GLubyte)(255.0f*powf(smoothstep(96.0f,255.0f,(float)i),
+ MAGMA_LUT_GAMMA));
+ }
+}
+
+
+/* Create a 3D magma texture of dimension size×size×size. Since the blue
+ channel would be entirely filled with zeros, we omit this channel and
+ create a texture consisting only of the red and green channels. */
+static GLubyte *gen_3d_magma_texture_rg(int size)
+{
+ const float startx = 0.0f, starty = 0.0f, startz = 0.0f;
+ int f, i, j, k, frequency;
+ float x, y, z, inc[NUM_OCTAVES];
+ float a, amp[NUM_OCTAVES], n;
+ GLubyte *ptr, *tex_ptr, g, lutr[256], lutg[256];
+
+ create_magma_lut_rg(lutr,lutg);
+
+ tex_ptr = malloc(2*size*size*size*sizeof(*tex_ptr));
+ if (tex_ptr == NULL)
+ abort();
+
+ frequency = START_FREQUENCY;
+ a = 196.0f;
+ for (f=0; f<NUM_OCTAVES; f++)
+ {
+ inc[f] = (float)frequency/(float)size;
+ amp[f] = a;
+ frequency *= 2;
+ a *= 0.5f;
+ }
+
+ ptr = tex_ptr;
+ for (i=0; i<size; i++)
+ {
+ for (j=0; j<size; j++)
+ {
+ for (k=0; k<size; k++, ptr+=2)
+ {
+ n = 0.0f;
+ for (f=0; f<NUM_OCTAVES; f++)
+ {
+ x = startx+i*inc[f];
+ y = starty+j*inc[f];
+ z = startz+k*inc[f];
+ n += fabsf(noise3(x,y,z))*amp[f];
+ }
+ if (n > 255.0f)
+ n = 255.0f;
+ g = (unsigned char)floorf(n+0.5f);
+ ptr[0] = lutr[g];
+ ptr[1] = lutg[g];
+ }
+ }
+ }
+
+ return tex_ptr;
+}
+
+#endif /* HAVE_GLSL */
+
+
+/* ------------------------------------------------------------------------- */
+
+
+#ifdef HAVE_GLSL
+
+/* Convert degrees to radians, */
+static inline double rad(double deg)
+{
+ return (M_PI/180.0)*deg;
+}
+
+
+/* Convert radians to degrees, */
+static inline double deg(double rad)
+{
+ return (180.0/M_PI)*rad;
+}
+
+
+/* Compute the Fortran modulo function. */
+static inline double modulo(double a, double p)
+{
+ return a-floor(a/p)*p;
+}
+
+
+/* Get the Julian date based on the current time. */
+static inline double get_current_julian_date(void)
+{
+ return (double)time(NULL)/86400.0+2440587.5;
+}
+
+
+/* Get the direction of the sun on the Julian date given by jd. The
+ function calculates the latitude and longitude of the subsolar point,
+ i.e., the point on earth for which the sun is perpendicular. The
+ longitude and latitude are then converted to a direction vector that
+ points to the sun. See: Taiping Zhang, Paul W. Stackhouse Jr., Bradley
+ Macpherson, and J. Colleen Mikovitz: A solar azimuth formula that
+ renders circumstantial treatment unnecessary without compromising
+ mathematical rigor: Mathematical setup, application and extension of a
+ formula based on the subsolar point and atan2 function. Renewable Energy
+ 172:1333-1340, 2021. */
+static inline void get_sun_direction(double jd, float sun[4])
+{
+ double n, utc;
+ double l, g, gr, lambda, lambdar, epsilon, epsilonr;
+ double alpha, delta, eot, lat, lon;
+ float latr, lonr;
+
+ n = jd-2451545.0;
+ utc = 24.0*modulo(jd-0.5,1.0);
+ l = modulo(280.460+0.9856474*n,360.0);
+ g = modulo(357.528+0.9856003*n,360.0);
+ gr = rad(g);
+ lambda = modulo(l+1.915*sin(gr)+0.020*sin(2.0*gr),360.0);
+ epsilon = 23.439-0.0000004*n;
+ lambdar = rad(lambda);
+ epsilonr = rad(epsilon);
+ alpha = modulo(deg(atan2(cos(epsilonr)*sin(lambdar),cos(lambdar))),360.0);
+ delta = deg(asin(sin(epsilonr)*sin(lambdar)));
+ eot = modulo(l-alpha+180.0,360.0)-180.0;
+ lat = delta;
+ lon = -15.0*(utc-12.0+eot*(4.0/60.0));
+ latr = (float)rad(lat);
+ lonr = (float)rad(lon);
+ sun[0] = cosf(latr)*cosf(lonr);
+ sun[1] = cosf(latr)*sinf(lonr);
+ sun[2] = sinf(latr);
+ sun[3] = 0.0f;
+}
+
+#endif /* HAVE_GLSL */
+
+
+/* ------------------------------------------------------------------------- */
+
+
+#ifdef HAVE_GLSL
+
+/* The GLSL versions that correspond to different versions of OpenGL. */
+static const GLchar *shader_version_2_1 =
+ "#version 120\n";
+static const GLchar *shader_version_3_0 =
+ "#version 130\n";
+static const GLchar *shader_version_3_0_es =
+ "#version 300 es\n"
+ "precision highp float;\n"
+ "precision highp int;\n"
+ "precision highp sampler2D;\n"
+ "precision highp sampler3D;\n";
+
+/* The vertex shader code is composed of code fragments that depend on
+ the OpenGL version and code fragments that are version-independent.
+ They are concatenated by glShaderSource in the function init_glsl(). */
+static const GLchar *vertex_shader_attribs_2_1 =
+ "attribute vec4 VertexPosition;\n"
+ "attribute vec4 VertexNormal;\n"
+ "attribute vec4 VertexColor;\n"
+ "attribute vec3 VertexTexCoord;\n"
+ "\n"
+ "varying vec3 Normal;\n"
+ "varying vec4 Color;\n"
+ "varying vec3 EarthTexCoord;\n"
+ "varying vec3 LightTexCoord;\n"
+ "varying vec3 MagmaTexCoord;\n"
+ "\n";
+static const GLchar *vertex_shader_attribs_3_0 =
+ "in vec4 VertexPosition;\n"
+ "in vec4 VertexNormal;\n"
+ "in vec4 VertexColor;\n"
+ "in vec3 VertexTexCoord;\n"
+ "\n"
+ "out vec3 Normal;\n"
+ "out vec4 Color;\n"
+ "out vec3 EarthTexCoord;\n"
+ "out vec3 LightTexCoord;\n"
+ "out vec3 MagmaTexCoord;\n"
+ "\n";
+static const GLchar *vertex_shader_main =
+ "uniform mat4 MatModelView;\n"
+ "uniform mat4 MatProj;\n"
+ "uniform vec3 TexOffsetMagma;\n"
+ "uniform bool NorthUp;\n"
+ "\n"
+ "void main (void)\n"
+ "{\n"
+ " Color = VertexColor;\n"
+ " Normal = normalize(MatModelView*VertexNormal).xyz;\n"
+ " vec4 Position = MatModelView*VertexPosition;\n"
+ " gl_Position = MatProj*Position;\n"
+ " if (NorthUp)\n"
+ " EarthTexCoord = VertexTexCoord;\n"
+ " else\n"
+ " EarthTexCoord = vec3(-1.0f,1.0f,-1.0f)*VertexTexCoord;\n"
+ " LightTexCoord = (MatModelView*vec4(VertexTexCoord,0.0f)).xyz;\n"
+ " MagmaTexCoord = 0.4f*(VertexTexCoord+1.0f)+TexOffsetMagma;\n"
+ "}\n";
+
+/* The fragment shader code is composed of code fragments that depend on
+ the OpenGL version and code fragments that are version-independent.
+ They are concatenated by glsl_CompileAndLinkShaders in the function
+ init_glsl(). */
+static const GLchar *fragment_shader_attribs_2_1 =
+ "varying vec3 Normal;\n"
+ "varying vec4 Color;\n"
+ "varying vec3 EarthTexCoord;\n"
+ "varying vec3 LightTexCoord;\n"
+ "varying vec3 MagmaTexCoord;\n"
+ "\n";
+static const GLchar *fragment_shader_attribs_3_0 =
+ "in vec3 Normal;\n"
+ "in vec4 Color;\n"
+ "in vec3 EarthTexCoord;\n"
+ "in vec3 LightTexCoord;\n"
+ "in vec3 MagmaTexCoord;\n"
+ "\n"
+ "out vec4 FragColor;\n"
+ "\n";
+static const GLchar *fragment_shader_main =
+ "const float I_M_PI_F = 0.318309886184f;\n"
+ "const float I_M_2PI_F = 0.159154943092f;\n"
+ "uniform vec4 LtGlblAmbient;\n"
+ "uniform vec4 LtAmbient, LtDiffuse, LtSpecular;\n"
+ "uniform vec3 LtDirection, LtHalfVector;\n"
+ "uniform vec4 MatAmbient;\n"
+ "uniform vec4 MatDiffuse;\n"
+ "uniform vec4 MatSpecular;\n"
+ "uniform float MatShininess;\n"
+ "uniform bool BoolTextures;\n"
+ "uniform sampler3D TextureSamplerMagma;\n"
+ "uniform sampler2D TextureSamplerDay;\n"
+ "uniform sampler2D TextureSamplerNight;\n"
+ "uniform sampler2D TextureSamplerWater;\n"
+ "\n"
+ "void main (void)\n"
+ "{\n"
+ " vec3 normalDirection;\n"
+ " vec4 ambientColor, diffuseColor, sceneColor;\n"
+ " vec4 ambientLighting, diffuseReflection, specularReflection;\n"
+ " vec4 color, colord, colorn, specd, specn;\n"
+ " vec3 normTexCoord, normLightCoord\n;"
+ " vec2 texCoor;\n"
+ " float ndotl, ldotls, ndoth, attd, attn, pf, lat, lon;\n"
+ " \n"
+ " if (!BoolTextures)\n"
+ " {\n"
+ " if (gl_FrontFacing)\n"
+ " {\n"
+ " normalDirection = normalize(Normal);\n"
+ " sceneColor = Color*MatAmbient*LtGlblAmbient;\n"
+ " ambientColor = Color*MatAmbient;\n"
+ " diffuseColor = Color*MatDiffuse;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " normalDirection = -normalize(Normal);\n"
+ " sceneColor = Color*MatAmbient*LtGlblAmbient;\n"
+ " ambientColor = Color*MatAmbient;\n"
+ " diffuseColor = Color*MatDiffuse;\n"
+ " }\n"
+ " \n"
+ " ndotl = max(0.0f,dot(normalDirection,LtDirection));\n"
+ " ndoth = max(0.0f,dot(normalDirection,LtHalfVector));\n"
+ " if (ndotl == 0.0f)\n"
+ " pf = 0.0f;\n"
+ " else\n"
+ " pf = pow(ndoth,MatShininess);\n"
+ " ambientLighting = ambientColor*LtAmbient;\n"
+ " diffuseReflection = LtDiffuse*diffuseColor*ndotl;\n"
+ " specularReflection = LtSpecular*MatSpecular*pf;\n"
+ " color = sceneColor+ambientLighting+diffuseReflection+\n"
+ " specularReflection;\n"
+ " color.a = diffuseColor.a;\n"
+ " }\n";
+static const GLchar *fragment_shader_out_2_1 =
+ " else\n"
+ " {\n"
+ " if (gl_FrontFacing)\n"
+ " {\n"
+ " color = texture3D(TextureSamplerMagma,MagmaTexCoord);\n"
+ " sceneColor = color*MatAmbient*LtGlblAmbient;\n"
+ " ambientColor = color*MatAmbient;\n"
+ " diffuseColor = color*MatDiffuse;\n"
+ " specularReflection = vec4(0.0f,0.0f,0.0f,0.0f);\n"
+ " diffuseReflection = LtDiffuse*diffuseColor;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " normTexCoord = normalize(EarthTexCoord);\n"
+ " normLightCoord = normalize(LightTexCoord);\n"
+ " lat = asin(normTexCoord.z);\n"
+ " lon = atan(normTexCoord.y,normTexCoord.x);\n"
+ " texCoor = vec2(0.5f+lon*I_M_2PI_F,0.5f+lat*I_M_PI_F);\n"
+ " ndotl = max(0.0f,dot(normLightCoord,LtDirection));\n"
+ " ndoth = max(0.0f,dot(normLightCoord,LtHalfVector));\n"
+ " if (ndotl == 0.0f)\n"
+ " pf = 0.0f;\n"
+ " else\n"
+ " pf = pow(ndoth,MatShininess);\n"
+ " specularReflection = LtSpecular*MatSpecular*pf;\n"
+ " ldotls = dot(normLightCoord,LtDirection);\n"
+ " colord = texture2D(TextureSamplerDay,texCoor);\n"
+ " attd = smoothstep(-0.2f,0.2f,ldotls);\n"
+ " colorn = texture2D(TextureSamplerNight,texCoor);\n"
+ " attn = smoothstep(-0.2f,0.2f,-ldotls);\n"
+ " color = attd*colord+attn*colorn;\n"
+ " specd = texture2D(TextureSamplerWater,texCoor);\n"
+ " specn = vec4(0.0f,0.0f,0.0f,1.0f);\n"
+ " specularReflection *= attd*specd+attn*specn;\n"
+ " sceneColor = color*MatAmbient*LtGlblAmbient;\n"
+ " ambientColor = color*MatAmbient;\n"
+ " diffuseColor = color*MatDiffuse;\n"
+ " diffuseReflection = LtDiffuse*diffuseColor*ndotl;\n"
+ " }\n"
+ " \n"
+ " ambientLighting = ambientColor*LtAmbient;\n"
+ " color = sceneColor+ambientLighting+diffuseReflection+\n"
+ " specularReflection;\n"
+ " color.a = diffuseColor.a;\n"
+ " }\n"
+ " gl_FragColor = clamp(color,0.0f,1.0f);\n"
+ "}\n";
+static const GLchar *fragment_shader_out_3_0 =
+ " else\n"
+ " {\n"
+ " if (gl_FrontFacing)\n"
+ " {\n"
+ " color = texture(TextureSamplerMagma,MagmaTexCoord);\n"
+ " sceneColor = color*MatAmbient*LtGlblAmbient;\n"
+ " ambientColor = color*MatAmbient;\n"
+ " diffuseColor = color*MatDiffuse;\n"
+ " specularReflection = vec4(0.0f,0.0f,0.0f,0.0f);\n"
+ " diffuseReflection = LtDiffuse*diffuseColor;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " normTexCoord = normalize(EarthTexCoord);\n"
+ " normLightCoord = normalize(LightTexCoord);\n"
+ " lat = asin(normTexCoord.z);\n"
+ " lon = atan(normTexCoord.y,normTexCoord.x);\n"
+ " texCoor = vec2(0.5f+lon*I_M_2PI_F,0.5f+lat*I_M_PI_F);\n"
+ " ndotl = max(0.0f,dot(normLightCoord,LtDirection));\n"
+ " ndoth = max(0.0f,dot(normLightCoord,LtHalfVector));\n"
+ " if (ndotl == 0.0f)\n"
+ " pf = 0.0f;\n"
+ " else\n"
+ " pf = pow(ndoth,MatShininess);\n"
+ " specularReflection = LtSpecular*MatSpecular*pf;\n"
+ " ldotls = dot(normLightCoord,LtDirection);\n"
+ " colord = texture(TextureSamplerDay,texCoor);\n"
+ " attd = smoothstep(-0.2f,0.2f,ldotls);\n"
+ " colorn = texture(TextureSamplerNight,texCoor);\n"
+ " attn = smoothstep(-0.2f,0.2f,-ldotls);\n"
+ " color = attd*colord+attn*colorn;\n"
+ " specd = texture(TextureSamplerWater,texCoor);\n"
+ " specn = vec4(0.0f,0.0f,0.0f,1.0f);\n"
+ " specularReflection *= attd*specd+attn*specn;\n"
+ " sceneColor = color*MatAmbient*LtGlblAmbient;\n"
+ " ambientColor = color*MatAmbient;\n"
+ " diffuseColor = color*MatDiffuse;\n"
+ " diffuseReflection = LtDiffuse*diffuseColor*ndotl;\n"
+ " }\n"
+ " \n"
+ " ambientLighting = ambientColor*LtAmbient;\n"
+ " color = sceneColor+ambientLighting+diffuseReflection+\n"
+ " specularReflection;\n"
+ " color.a = diffuseColor.a;\n"
+ " }\n"
+ " FragColor = clamp(color,0.0f,1.0f);\n"
+ "}\n";
+
+#endif /* HAVE_GLSL */
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Copy a vector c = v. */
+static inline void copy_vector(float c[3], const float v[3])
+{
+ c[0] = v[0];
+ c[1] = v[1];
+ c[2] = v[2];
+}
+
+
+/* Compute the negative of a vector n = -a. */
+static inline void neg(float n[3], const float a[3])
+{
+ n[0] = -a[0];
+ n[1] = -a[1];
+ n[2] = -a[2];
+}
+
+
+/* Scale a vector by a factor of s: w = v * s. */
+static inline void scale(float w[3], const float v[3], float s)
+{
+ w[0] = v[0]*s;
+ w[1] = v[1]*s;
+ w[2] = v[2]*s;
+}
+
+
+/* Compute the vector sum s = a + b. */
+static inline void add(float s[3], const float a[3], const float b[3])
+{
+ s[0] = a[0]+b[0];
+ s[1] = a[1]+b[1];
+ s[2] = a[2]+b[2];
+}
+
+
+/* Compute the vector difference s = a - b. */
+static inline void sub(float s[3], const float a[3], const float b[3])
+{
+ s[0] = a[0]-b[0];
+ s[1] = a[1]-b[1];
+ s[2] = a[2]-b[2];
+}
+
+
+/* Compute the midpoint of two vectors m = (a + b) / 2. */
+static inline void mid(float m[3], const float a[3], const float b[3])
+{
+ m[0] = 0.5f*(a[0]+b[0]);
+ m[1] = 0.5f*(a[1]+b[1]);
+ m[2] = 0.5f*(a[2]+b[2]);
+}
+
+
+/* Compute the dot product of two vectors. */
+static inline float dot(const float v[3], const float w[3])
+{
+ return v[0]*w[0]+v[1]*w[1]+v[2]*w[2];
+}
+
+
+/* Compute the Euclidean norm of a vector. */
+static inline float norm(const float v[3])
+{
+ return sqrtf(dot(v,v));
+}
+
+
+/* Normalize a vector. */
+static inline void normalize(float v[3])
+{
+ float l;
+
+ l = norm(v);
+ if (l > 0.0f)
+ l = 1.0f/l;
+ v[0] *= l;
+ v[1] *= l;
+ v[2] *= l;
+}
+
+
+/* Compute the cross product c = m × n. */
+static inline void cross(float c[3], const float m[3], const float n[3])
+{
+ c[0] = m[1]*n[2]-m[2]*n[1];
+ c[1] = m[2]*n[0]-m[0]*n[2];
+ c[2] = m[0]*n[1]-m[1]*n[0];
+}
+
+
+/* Copy a matrix: c = m. */
+static void copy_matrix(matrix c, matrix m)
+{
+ int i, j;
+
+ for (j=0; j<4; j++)
+ for (i=0; i<4; i++)
+ c[i][j] = m[i][j];
+}
+
+
+/* Compute the identity matrix. */
+static void identity_matrix(matrix m)
+{
+ int i, j;
+
+ for (i=0; i<4; i++)
+ for (j=0; j<4; j++)
+ m[i][j] = (float)(i==j);
+}
+
+
+/* Multiply two matrices: c = c * m. */
+static void mult_matrix(matrix c, matrix m)
+{
+ int i, j;
+ matrix t;
+
+ /* Copy c to t. */
+ copy_matrix(t,c);
+ /* Compute c = t*m. */
+ for (j=0; j<4; j++)
+ for (i=0; i<4; i++)
+ c[i][j] = (t[i][0]*m[0][j]+t[i][1]*m[1][j]+
+ t[i][2]*m[2][j]+t[i][3]*m[3][j]);
+}
+
+
+/* Add a translation matrix from the right to a matrix. */
+static void translate_matrix(matrix m, const float t[3])
+{
+ int i;
+
+ for (i=0; i<4; i++)
+ m[i][3] = (t[0]*m[i][0]+t[1]*m[i][1]+t[2]*m[i][2]+m[i][3]);
+}
+
+
+/* Add a rotation in the xy plane to the matrix m. */
+static void rotate_xy_matrix(matrix m, float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI_F/180.0f;
+ c = cosf(phi);
+ s = sinf(phi);
+ for (i=0; i<4; i++)
+ {
+ u = m[i][0];
+ v = m[i][1];
+ m[i][0] = c*u+s*v;
+ m[i][1] = -s*u+c*v;
+ }
+}
+
+
+/* Multiply a matrix with a vector: o = m * v. */
+static void mult_matrix_vector(float o[4], matrix m, float v[4])
+{
+ int i, j;
+
+ for (i=0; i<4; i++)
+ {
+ o[i] = 0.0f;
+ for (j=0; j<4; j++)
+ o[i] += m[i][j]*v[j];
+ }
+}
+
+
+/* Generate a uniformly distributed random rotation matrix. */
+static void rnd_rot_matrix(matrix m)
+{
+ float theta, phi, z, r, vx, vy, vz, st, ct, sx, sy;
+
+ theta = (float)frand(2.0*M_PI);
+ phi = (float)frand(2.0*M_PI);
+ z = (float)frand(2.0);
+ r = sqrtf(z);
+ vx = r*sinf(phi);
+ vy = r*cosf(phi);
+ vz = sqrtf(2.0f-z);
+ st = sinf(theta);
+ ct = cosf(theta);
+ sx = vx*ct-vy*st;
+ sy = vx*st+vy*ct;
+
+ identity_matrix(m);
+ m[0][0] = vx*sx-ct;
+ m[0][1] = vx*sy-st;
+ m[0][2] = vx*vz;
+ m[1][0] = vy*sx+st;
+ m[1][1] = vy*sy-ct;
+ m[1][2] = vy*vz;
+ m[2][0] = vz*sx;
+ m[2][1] = vz*sy;
+ m[2][2] = 1.0f-z;
+}
+
+
+/* An easing function for values of t between 0 and max. */
+static inline float ease(float t, float max, int easing)
+{
+ float s, e;
+
+ s = t/max;
+ if (easing == EASING_QUINTIC)
+ e = ((6.0f*s-15.0f)*s+10.0f)*s*s*s;
+ else if (easing == EASING_ACCEL)
+ e = s*s*(2.0f-s);
+ else if (easing == EASING_DECEL)
+ e = s*(1.0f+s*(1.0f-s));
+ else
+ e = 0.0f;
+ return max*e;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Add a rotation around the x axis to the matrix m. */
+static void rotatex(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI_F/180.0f;
+ c = cosf(phi);
+ s = sinf(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][1];
+ v = m[i][2];
+ m[i][1] = c*u+s*v;
+ m[i][2] = -s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the y axis to the matrix m. */
+static void rotatey(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI_F/180.0f;
+ c = cosf(phi);
+ s = sinf(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][2];
+ m[i][0] = c*u-s*v;
+ m[i][2] = s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the z axis to the matrix m. */
+static void rotatez(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI_F/180.0f;
+ c = cosf(phi);
+ s = sinf(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][1];
+ m[i][0] = c*u+s*v;
+ m[i][1] = -s*u+c*v;
+ }
+}
+
+
+/* Compute the rotation matrix m from the rotation angles. */
+static void rotateall(float al, float be, float de, float m[3][3])
+{
+ int i, j;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ m[i][j] = (i==j);
+ rotatex(m,al);
+ rotatey(m,be);
+ rotatez(m,de);
+}
+
+
+/* Compute a 4x4 rotation matrix from an xscreensaver unit quaternion. Note
+ that xscreensaver has a different convention for unit quaternions than
+ the one that is used in this hack. */
+static void quat_to_rotmat_trackball(float p[4], float r[16])
+{
+ float al, be, de;
+ float r00, r01, r02, r12, r22;
+ float m[3][3];
+ int i, j;
+
+ r00 = 1.0f-2.0f*(p[1]*p[1]+p[2]*p[2]);
+ r01 = 2.0f*(p[0]*p[1]+p[2]*p[3]);
+ r02 = 2.0f*(p[2]*p[0]-p[1]*p[3]);
+ r12 = 2.0f*(p[1]*p[2]+p[0]*p[3]);
+ r22 = 1.0f-2.0f*(p[1]*p[1]+p[0]*p[0]);
+
+ al = atan2f(-r12,r22)*180.0f/M_PI_F;
+ be = atan2f(r02,sqrtf(r00*r00+r01*r01))*180.0f/M_PI_F;
+ de = atan2f(-r01,r00)*180.0f/M_PI_F;
+ rotateall(al,be,de,m);
+ for (i=0; i<3; i++)
+ {
+ for (j=0; j<3; j++)
+ r[j*4+i] = m[i][j];
+ r[3*4+i] = 0.0f;
+ r[i*4+3] = 0.0f;
+ }
+ r[3*4+3] = 1.0f;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Create an edge-based graph eg with num_v vertices, num_e edges, and the
+ edges passed in edges. */
+static edge_graph *create_edge_graph(int num_v, int num_e, edge *edges)
+{
+ edge_graph *eg;
+
+ eg = malloc(sizeof(*eg));
+ if (eg == NULL)
+ abort();
+ eg->edges = malloc(num_e*sizeof(*eg->edges));
+ if (eg->edges == NULL)
+ abort();
+ eg->num_v = num_v;
+ eg->num_e = num_e;
+ memcpy(eg->edges,edges,num_e*sizeof(*eg->edges));
+
+ return eg;
+}
+
+
+/* Free the edge-based graph eg. */
+static void free_edge_graph(edge_graph *eg)
+{
+ free(eg->edges);
+ free(eg);
+}
+
+
+/* Create a DSU with num_v vertices. */
+static dsu *create_dsu(int num_v)
+{
+ dsu *d;
+ int i;
+
+ d = malloc(sizeof(*d));
+ if (d == NULL)
+ abort();
+ d->parent = malloc(num_v*sizeof(*d->parent));
+ d->rank = malloc(num_v*sizeof(*d->rank));
+ if (d->parent == NULL || d->rank == NULL)
+ abort();
+ for (i=0; i<num_v; i++)
+ {
+ d->parent[i] = i;
+ d->rank[i] = 0;
+ }
+ return d;
+}
+
+
+/* Free a DSU. */
+static void free_dsu(dsu *d)
+{
+ free(d->parent);
+ free(d->rank);
+ free(d);
+}
+
+
+/* Find the representative (root) of a set with path compression. */
+static int find_root(dsu *d, int x)
+{
+ if (d->parent[x] != x)
+ d->parent[x] = find_root(d,d->parent[x]);
+ return d->parent[x];
+}
+
+
+/* Compute the union of two sets by rank. */
+static void union_sets(dsu *d, int x, int y)
+{
+ int root_x, root_y;
+
+ root_x = find_root(d,x);
+ root_y = find_root(d,y);
+ if (root_x != root_y)
+ {
+ if (d->rank[root_x] < d->rank[root_y])
+ {
+ d->parent[root_x] = root_y;
+ }
+ else if (d->rank[root_x] > d->rank[root_y])
+ {
+ d->parent[root_y] = root_x;
+ }
+ else
+ {
+ d->parent[root_y] = root_x;
+ d->rank[root_x]++;
+ }
+ }
+}
+
+
+/* Compare function for sorting edges by weight. */
+static int compare_edges(const void *a, const void *b)
+{
+ const edge *edge_a, *edge_b;
+
+ edge_a = (edge *)a;
+ edge_b = (edge *)b;
+ if (edge_a->weight > edge_b->weight)
+ return 1;
+ else if (edge_a->weight < edge_b->weight)
+ return -1;
+ else
+ return 0;
+}
+
+
+/* Kruskal's algorithm to find the g->num_v-1 edges of the minimum spanning
+ tree of the edge-based graph g. */
+static edge *minimum_spanning_tree_edges(edge_graph *g)
+{
+ edge *mst_edges, next_edge;
+ dsu *d;
+ int edge_count, i;
+ int src_root, dst_root;
+
+ mst_edges = malloc((g->num_v-1)*sizeof(*mst_edges));
+ if (mst_edges == NULL)
+ abort();
+
+ qsort(g->edges,g->num_e,sizeof(*g->edges),compare_edges);
+
+ d = create_dsu(g->num_v);
+
+ edge_count = 0;
+ i = 0;
+ while (edge_count<g->num_v-1 && i<g->num_e)
+ {
+ next_edge = g->edges[i++];
+ src_root = find_root(d,next_edge.src);
+ dst_root = find_root(d,next_edge.dst);
+
+ if (src_root != dst_root)
+ {
+ mst_edges[edge_count++] = next_edge;
+ union_sets(d,src_root,dst_root);
+ }
+ }
+
+ free_dsu(d);
+
+ return mst_edges;
+}
+
+
+/* Create an adjacenct-list-based graph from a set of edges. */
+static graph_node **create_adj_list_graph_from_edges(const edge *e, int num_v,
+ int num_e)
+{
+ graph_node **ag, *n;
+ int i, src, dst, src_edge, dst_edge;
+
+ ag = malloc(num_v*sizeof(*ag));
+ if (ag == NULL)
+ abort();
+ for (i=0; i<num_v; i++)
+ ag[i] = NULL;
+ for (i=0; i<num_e; i++)
+ {
+ src = e[i].src;
+ dst = e[i].dst;
+ src_edge = e[i].src_edge;
+ dst_edge = e[i].dst_edge;
+ n = malloc(sizeof(*n));
+ if (n == NULL)
+ abort();
+ n->v = src;
+ n->edge_parent = dst_edge;
+ n->edge_self = src_edge;
+ n->next = ag[dst];
+ ag[dst] = n;
+ n = malloc(sizeof(*n));
+ if (n == NULL)
+ abort();
+ n->v = dst;
+ n->edge_parent = src_edge;
+ n->edge_self = dst_edge;
+ n->next = ag[src];
+ ag[src] = n;
+ }
+ return ag;
+}
+
+
+/* Free an adjacenct-list-based graph. */
+static void free_adj_list_graph(graph_node **ag, int num_v)
+{
+ graph_node *a, *an;
+ int i;
+
+ for (i=0; i<num_v; i++)
+ {
+ a = ag[i];
+ while (a != NULL)
+ {
+ an = a->next;
+ free(a);
+ a = an;
+ }
+ }
+ free(ag);
+}
+
+
+/* Create a minimum spanning tree from a adjacency-list-based graph that
+ represents the minimum spanning tree. */
+static tree_node *create_minimum_spanning_tree(graph_node **ag, int num_v)
+{
+ int i, k, id, sp;
+ int *val, *vstack;
+ tree_node **tstack, *t, *tp, *tn, *tree;
+ graph_node *n;
+
+ val = malloc(num_v*sizeof(*val));
+ vstack = malloc(num_v*sizeof(*vstack));
+ tstack = malloc(num_v*sizeof(*tstack));
+ if (val == NULL || vstack == NULL || tstack == NULL)
+ abort();
+
+ for (i=0; i<num_v; i++)
+ val[i] = UNSEEN;
+
+ tree = NULL;
+ id = 0;
+ sp = 0;
+ for (i=0; i<num_v; i++)
+ {
+ if (val[i] == UNSEEN)
+ {
+ t = malloc(sizeof(*t));
+ if (t == NULL)
+ abort();
+ t->next = NULL;
+ t->child = NULL;
+ t->polygon_index = i;
+ t->edge_parent = -1;
+ t->edge_self = -1;
+ tree = t;
+ vstack[sp] = i;
+ tstack[sp] = t;
+ while (sp >= 0)
+ {
+ k = vstack[sp];
+ tp = tstack[sp];
+ sp--;
+ val[k] = id++;
+ tn = NULL;
+ for (n=ag[k]; n!=NULL; n=n->next)
+ {
+ if (val[n->v] == UNSEEN)
+ {
+ t = malloc(sizeof(*t));
+ if (t == NULL)
+ abort();
+ t->next = NULL;
+ t->child = NULL;
+ t->polygon_index = n->v;
+ t->edge_parent = n->edge_parent;
+ t->edge_self = n->edge_self;
+ if (tp->child == NULL)
+ {
+ tp->child = t;
+ tn = t;
+ }
+ else
+ {
+ tn->next = t;
+ tn = t;
+ }
+ sp++;
+ vstack[sp] = n->v;
+ tstack[sp] = t;
+ val[n->v] = SEEN;
+ }
+ }
+ }
+ }
+ }
+
+ free(tstack);
+ free(vstack);
+ free(val);
+
+ return tree;
+}
+
+
+/* Free a tree. */
+static void free_tree(tree_node *t)
+{
+ if (t->child != NULL)
+ free_tree(t->child);
+ if (t->next != NULL)
+ free_tree(t->next);
+ free(t);
+}
+
+
+/* Generate a random polyhedron unfolding of the polyhedron with num_faces
+ faces and num_edges edges represented by the adjacency information of the
+ faces given by adj. */
+static tree_node *create_random_polyhedron_unfolding(int num_faces,
+ int num_edges,
+ edge *adj)
+{
+ edge_graph *eg;
+ edge *mst_edges;
+ graph_node **ag;
+ tree_node *mst;
+ int i;
+
+ /* Create an edge-based adjacency graph from adj. */
+ eg = create_edge_graph(num_faces,num_edges,adj);
+
+ /* Set random weights for the edges in eg. */
+ for (i=0; i<num_edges; i++)
+ eg->edges[i].weight = (float)frand(1.0);
+
+ /* Compute the edges of the minimum spanning tree. */
+ mst_edges = minimum_spanning_tree_edges(eg);
+
+ /* Create an adjacency-list-based graph from the edges of the minimum
+ spanning tree. */
+ ag = create_adj_list_graph_from_edges(mst_edges,num_faces,num_faces-1);
+
+ /* Create an actual minimum spanning tree from the adjacency-list-based
+ graph. */
+ mst = create_minimum_spanning_tree(ag,num_faces);
+
+ free_adj_list_graph(ag,num_faces);
+ free(mst_edges);
+ free_edge_graph(eg);
+
+ return mst;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Compute the fold pose of the base polygon of the node given by node and
+ the angle given by angles at the index node->fold_angle_index. The
+ function assumes that node->unfold_pose is relative to its parent node
+ has been set by the caller before the call. Furthermore, the function
+ assumes that node->fold_pose has been initialized with its parent's fold
+ pose. On output, the function modifies node->fold_pose to store the
+ fold pose of the polygon described by the node. */
+static void compute_fold_pose(tree_node *node, const polygon *base_polygon,
+ const float *angles, float max_angle)
+{
+ int i, i1, i2;
+ float t[3], mt[3], a[3], b[3], c[3], phi;
+ vertex t1, t2;
+ matrix matr, mats, matst;
+
+ if (node->fold_angle_index >= 0)
+ {
+ phi = angles[node->fold_angle_index];
+ node->fold_angle = ease(phi,max_angle,EASING_QUINTIC);
+
+ i1 = node->edge_self;
+ i2 = (i1+1)%base_polygon->num;
+
+ /* Compute the translation vector. This is the midpoint of the edge of
+ the polygon that is adjacent to the parent polygon. */
+ mult_matrix_vector(t1,node->unfold_pose,base_polygon->v[i1]);
+ mult_matrix_vector(t2,node->unfold_pose,base_polygon->v[i2]);
+ mid(t,t1,t2);
+ neg(mt,t);
+
+ /* Compute the direction that remains fixed during the rotation. This
+ is the normalized vector that points from the first to the second
+ vertex of the polygon that is adjacent to the parent polygon. */
+ sub(c,t2,t1);
+ normalize(c);
+
+ /* Compute the first basis vector of the plane in which the rotation is
+ performed. This is the normalized version of the translation vector
+ that was computed above, except its z coordinate is 0. */
+ a[0] = t[0];
+ a[1] = t[1];
+ a[2] = 0.0f;
+ normalize(a);
+
+ /* Compute the second basis vector of the plane in which the rotation
+ is performed. This is simply the z direction. */
+ b[0] = 0.0f;
+ b[1] = 0.0f;
+ b[2] = 1.0f;
+
+ /* Insert the three vectors into the matrix mats and its transpose
+ matst. */
+ identity_matrix(mats);
+ identity_matrix(matst);
+ for (i=0; i<3; i++)
+ {
+ mats[i][0] = a[i];
+ mats[i][1] = b[i];
+ mats[i][2] = c[i];
+ matst[0][i] = a[i];
+ matst[1][i] = b[i];
+ matst[2][i] = c[i];
+ }
+
+ /* Compute the rotation matrix that rotates the polygon. */
+ identity_matrix(matr);
+ translate_matrix(matr,t);
+ mult_matrix(matr,mats);
+ rotate_xy_matrix(matr,node->fold_angle);
+ mult_matrix(matr,matst);
+ translate_matrix(matr,mt);
+
+ /* Add the fold angle transformation. */
+ mult_matrix(node->fold_pose,matr);
+ }
+
+ /* Add the unfolding pose to the current polygon. */
+ mult_matrix(node->fold_pose,node->unfold_pose);
+}
+
+
+/* Determine the color data of the polygons, i.e., the colors of the faces
+ and the texture coordinates of its vertices, based on the folded
+ polyhedron. */
+static void determine_polygon_color_data(tree_node *unfolding,
+ const polygons *base_polygons,
+ float max_angle, matrix color_matrix)
+{
+ float angles[NUM_ANGLES];
+ int i, sp, pindex;
+ vertex tv, c, cr;
+ matrix matp;
+ tree_node *node_stack[MAX_STACK], *node;
+ polygon *poly;
+
+ for (i=0; i<NUM_ANGLES; i++)
+ angles[i] = max_angle;
+
+ sp = 0;
+ node_stack[sp] = unfolding;
+ while (sp >= 0)
+ {
+ node = node_stack[sp--];
+ pindex = node->polygon_index;
+ poly = &base_polygons->poly[pindex];
+
+ /* Initialize the fold pose when processing the root of the tree. */
+ if (node->edge_parent < 0 || node->edge_self < 0)
+ identity_matrix(node->fold_pose);
+
+ /* The parent fold pose is initially stored in the current node. Save
+ it for later. */
+ copy_matrix(matp,node->fold_pose);
+
+ /* Compute the folding transformation to the current polygon. */
+ compute_fold_pose(node,poly,angles,max_angle);
+
+ /* Transform the polygon, compute the center of the transformed polygon,
+ and normalize it to length 1. */
+ for (i=0; i<3; i++)
+ c[i] = 0.0f;
+ c[3] = 1.0f;
+ for (i=0; i<poly->num; i++)
+ {
+ /* Transform the vertices of the base polygon with the fold
+ transformation. */
+ mult_matrix_vector(tv,node->fold_pose,poly->v[i]);
+ add(c,c,tv);
+ /* Set the 3D texture coordinate of the current vertex to the position
+ of the vertex on a unit sphere. */
+ normalize(tv);
+ copy_vector(poly->t[i],tv);
+ }
+ mult_matrix_vector(cr,color_matrix,c);
+ normalize(cr);
+
+ /* Set the color of the polygon. */
+ for (i=0; i<3; i++)
+ poly->c[i] = 0.5f*(cr[i]+1.0f);
+ poly->c[3] = 1.0f;
+
+ if (node->child != NULL)
+ {
+ copy_matrix(node->child->fold_pose,node->fold_pose);
+ node_stack[++sp] = node->child;
+ }
+ if (node->next != NULL)
+ {
+ copy_matrix(node->next->fold_pose,matp);
+ node_stack[++sp] = node->next;
+ }
+ }
+}
+
+
+/* Determine the poses of the polygons in the unfolding. */
+static void determine_unfolding_poses(tree_node *unfolding,
+ const polygons *base_polygons)
+{
+ tree_node *node_stack[MAX_STACK], *node;
+ int sp, p1, p2, s1, s2, ai, pindex;
+ float vp[3], vs[3], c[3], m[3], t[3], dp, phi;
+ polygon *poly;
+
+ ai = 0;
+ sp = 0;
+ node_stack[sp] = unfolding;
+ while (sp >= 0)
+ {
+ node = node_stack[sp--];
+ pindex = node->polygon_index;
+ poly = &base_polygons->poly[pindex];
+
+ if (node->edge_parent >= 0 && node->edge_self >= 0)
+ {
+ node->fold_angle_index = ai++;
+
+ /* The unfold poses are all relative to its parent node. Therefore,
+ they must be initialized with the identity matrix. */
+ identity_matrix(node->unfold_pose);
+
+ /* The rotation angle phi is determined from the vectors corresponding
+ to the edges that are adjacent to each other in the polyhedron,
+ i.e., the edges that should align in the unfolded state after the
+ transformation of the base polygon. */
+ p1 = node->edge_parent;
+ p2 = (p1+1)%poly->num;
+ s1 = node->edge_self;
+ s2 = (s1+1)%poly->num;
+
+ /* We have to take into account that, since both polygons must be
+ oriented counterclockwise, the edge in the transformed polygon
+ must be taken in the opposite direction to compute the correct
+ rotation angle. */
+ sub(vp,poly->v[p2],poly->v[p1]);
+ sub(vs,poly->v[s1],poly->v[s2]);
+ normalize(vp);
+ normalize(vs);
+ cross(c,vs,vp);
+ dp = dot(vs,vp);
+ phi = (180.0f/M_PI_F)*atan2f(c[2],dp);
+
+ /* Compute the translation vector. This is the midpoint of the edge
+ in the parent polygon that is adjacent to the transformed polygon,
+ scaled by a factor of 2. However, since all polygons must lie in
+ one plane in the unfolded state, we must set the z coordinate of
+ the translation to 0. */
+ mid(m,poly->v[p1],poly->v[p2]);
+ m[2] = 0.0f;
+ scale(t,m,2.0f);
+
+ /* Compute the transformation matrix of the current polygon with
+ respect to its parent. */
+ translate_matrix(node->unfold_pose,t);
+ rotate_xy_matrix(node->unfold_pose,phi);
+ }
+ else
+ {
+ node->fold_angle_index = -1;
+
+ /* The unfold pose for the root of the tree is the identity matrix by
+ definition. */
+ identity_matrix(node->unfold_pose);
+ }
+
+ if (node->child != NULL)
+ node_stack[++sp] = node->child;
+ if (node->next != NULL)
+ node_stack[++sp] = node->next;
+ }
+}
+
+
+/* Initialize the base polygons. */
+static polygons *init_base_polygons(tree_node *unfolding,
+ const polygon *base_polygon)
+{
+ tree_node *node_stack[MAX_STACK], *node;
+ int i, num_poly, sp;
+ polygon *poly;
+ polygons *polys;
+
+ /* Count the number of polygons in the unfolding. */
+ num_poly = 0;
+ sp = 0;
+ node_stack[sp] = unfolding;
+ while (sp >= 0)
+ {
+ node = node_stack[sp--];
+ num_poly++;
+ if (node->child != NULL)
+ node_stack[++sp] = node->child;
+ if (node->next != NULL)
+ node_stack[++sp] = node->next;
+ }
+
+ polys = malloc(sizeof(*polys));
+ poly = malloc(num_poly*sizeof(*poly));
+ if (polys == NULL || poly == NULL)
+ abort();
+
+ polys->poly = poly;
+ polys->num = num_poly;
+
+ /* Copy the base polygon to all base polygons and set the texture
+ coordinates and color to 0. */
+ for (i=0; i<num_poly; i++)
+ {
+ poly[i].v = malloc(base_polygon->num*sizeof(*poly[i].v));
+ poly[i].t = malloc(base_polygon->num*sizeof(*poly[i].t));
+ if (poly[i].v == NULL || poly[i].t == NULL)
+ abort();
+ poly[i].num = base_polygon->num;
+ memcpy(poly[i].v,base_polygon->v,base_polygon->num*sizeof(*poly[i].v));
+ memcpy(poly[i].n,base_polygon->n,sizeof(poly[i].n));
+ memset(poly[i].t,0,base_polygon->num*sizeof(*poly[i].t));
+ memset(poly[i].c,0,sizeof(poly[i].c));
+ }
+
+ return polys;
+}
+
+
+/* Free the base polygons. */
+static void free_base_polygons(polygons *base_polygons)
+{
+ int i;
+
+ for (i=0; i<base_polygons->num; i++)
+ {
+ free(base_polygons->poly[i].v);
+ free(base_polygons->poly[i].t);
+ }
+ free(base_polygons->poly);
+ free(base_polygons);
+}
+
+
+/* Initialize the color matrix that determines the random face colors. */
+static void init_color_matrix(matrix color_matrix)
+{
+ rnd_rot_matrix(color_matrix);
+}
+
+
+/* Initialize the polygon unfolding for a polyhedron of type poly. */
+static void init_polygon_unfolding(ModeInfo *mi, int poly)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+
+ switch (poly)
+ {
+ case TETRAHEDRON:
+ free_base_polygons(pf->base_polygons);
+ free_tree(pf->polygon_unfolding);
+ pf->polygon_unfolding =
+ create_random_polyhedron_unfolding(TETRAHEDRON_NUM_FACES,
+ TETRAHEDRON_NUM_EDGES,
+ tetrahedron_edges);
+ pf->max_angle = TETRAHEDRON_MAX_ANGLE;
+ pf->delta_angle = TETRAHEDRON_DELTA_ANGLE;
+ pf->num_fold_angles = TETRAHEDRON_NUM_FACES-1;
+ pf->eye_pos = tetrahedron_eye_pos;
+ pf->base_polygons = init_base_polygons(pf->polygon_unfolding,
+ &tetrahedron_triangle);
+ determine_unfolding_poses(pf->polygon_unfolding,pf->base_polygons);
+ determine_polygon_color_data(pf->polygon_unfolding,pf->base_polygons,
+ pf->max_angle,pf->color_matrix);
+ break;
+ case HEXAHEDRON:
+ free_base_polygons(pf->base_polygons);
+ free_tree(pf->polygon_unfolding);
+ pf->polygon_unfolding =
+ create_random_polyhedron_unfolding(HEXAHEDRON_NUM_FACES,
+ HEXAHEDRON_NUM_EDGES,
+ hexahedron_edges);
+ pf->max_angle = HEXAHEDRON_MAX_ANGLE;
+ pf->delta_angle = HEXAHEDRON_DELTA_ANGLE;
+ pf->num_fold_angles = HEXAHEDRON_NUM_FACES-1;
+ pf->eye_pos = hexahedron_eye_pos;
+ pf->base_polygons = init_base_polygons(pf->polygon_unfolding,
+ &hexahedron_square);
+ determine_unfolding_poses(pf->polygon_unfolding,pf->base_polygons);
+ determine_polygon_color_data(pf->polygon_unfolding,pf->base_polygons,
+ pf->max_angle,pf->color_matrix);
+ break;
+ case OCTAHEDRON:
+ free_base_polygons(pf->base_polygons);
+ free_tree(pf->polygon_unfolding);
+ pf->polygon_unfolding =
+ create_random_polyhedron_unfolding(OCTAHEDRON_NUM_FACES,
+ OCTAHEDRON_NUM_EDGES,
+ octahedron_edges);
+ pf->max_angle = OCTAHEDRON_MAX_ANGLE;
+ pf->delta_angle = OCTAHEDRON_DELTA_ANGLE;
+ pf->num_fold_angles = OCTAHEDRON_NUM_FACES-1;
+ pf->eye_pos = octahedron_eye_pos;
+ pf->base_polygons = init_base_polygons(pf->polygon_unfolding,
+ &octahedron_triangle);
+ determine_unfolding_poses(pf->polygon_unfolding,pf->base_polygons);
+ determine_polygon_color_data(pf->polygon_unfolding,pf->base_polygons,
+ pf->max_angle,pf->color_matrix);
+ break;
+ case DODECAHEDRON:
+ free_base_polygons(pf->base_polygons);
+ free_tree(pf->polygon_unfolding);
+ pf->polygon_unfolding =
+ create_random_polyhedron_unfolding(DODECAHEDRON_NUM_FACES,
+ DODECAHEDRON_NUM_EDGES,
+ dodecahedron_edges);
+ pf->max_angle = DODECAHEDRON_MAX_ANGLE;
+ pf->delta_angle = DODECAHEDRON_DELTA_ANGLE;
+ pf->num_fold_angles = DODECAHEDRON_NUM_FACES-1;
+ pf->eye_pos = dodecahedron_eye_pos;
+ pf->base_polygons = init_base_polygons(pf->polygon_unfolding,
+ &dodecahedron_pentagon);
+ determine_unfolding_poses(pf->polygon_unfolding,pf->base_polygons);
+ determine_polygon_color_data(pf->polygon_unfolding,pf->base_polygons,
+ pf->max_angle,pf->color_matrix);
+ break;
+ case ICOSAHEDRON:
+ free_base_polygons(pf->base_polygons);
+ free_tree(pf->polygon_unfolding);
+ pf->polygon_unfolding =
+ create_random_polyhedron_unfolding(ICOSAHEDRON_NUM_FACES,
+ ICOSAHEDRON_NUM_EDGES,
+ icosahedron_edges);
+ pf->max_angle = ICOSAHEDRON_MAX_ANGLE;
+ pf->delta_angle = ICOSAHEDRON_DELTA_ANGLE;
+ pf->num_fold_angles = ICOSAHEDRON_NUM_FACES-1;
+ pf->eye_pos = icosahedron_eye_pos;
+ pf->base_polygons = init_base_polygons(pf->polygon_unfolding,
+ &icosahedron_triangle);
+ determine_unfolding_poses(pf->polygon_unfolding,pf->base_polygons);
+ determine_polygon_color_data(pf->polygon_unfolding,pf->base_polygons,
+ pf->max_angle,pf->color_matrix);
+ break;
+ }
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Generate the vertex, normal, color, and texture buffers for a polygon
+ folding. */
+static int gen_polygon_folding_data(tree_node *unfolding,
+ const polygons *base_polygons,
+ const float *angles, float max_angle,
+ vertex *vtx, normal *nrm, color *col,
+ texcoord *tex, int *num)
+{
+ int i, j, n, num_poly, sp, pindex;
+ vertex tv, tn;
+ matrix matp;
+ tree_node *node_stack[MAX_STACK], *node;
+ polygon *poly;
+
+ num_poly = 0;
+ n = 0;
+ sp = 0;
+ node_stack[sp] = unfolding;
+ while (sp >= 0)
+ {
+ node = node_stack[sp--];
+ pindex = node->polygon_index;
+ poly = &base_polygons->poly[pindex];
+
+ /* Initialize the fold pose when processing the root of the tree. */
+ if (node->edge_parent < 0 || node->edge_self < 0)
+ identity_matrix(node->fold_pose);
+
+ /* The parent's fold pose is initially stored in the current node. Save
+ it for later. */
+ copy_matrix(matp,node->fold_pose);
+
+ /* Compute the folding transformation to the current polygon. */
+ compute_fold_pose(node,poly,angles,max_angle);
+
+ /* Transform the normal vector of the polygon. Note that we assume
+ that node->fold_pose is a rigid transformation. Otherwise, we would
+ have to compute and use its inverse transpose. */
+ mult_matrix_vector(tn,node->fold_pose,poly->n);
+
+ for (i=0; i<poly->num; i++)
+ {
+ /* Transform the vertices of the base polygon with the fold
+ transformation. */
+ mult_matrix_vector(tv,node->fold_pose,poly->v[i]);
+
+ for (j=0; j<4; j++)
+ vtx[n][j] = tv[j];
+ for (j=0; j<4; j++)
+ nrm[n][j] = tn[j];
+ for (j=0; j<4; j++)
+ col[n][j] = poly->c[j];
+ for (j=0; j<3; j++)
+ tex[n][j] = poly->t[i][j];
+ n++;
+ }
+
+ num[num_poly] = poly->num;
+ num_poly++;
+
+ if (node->child != NULL)
+ {
+ copy_matrix(node->child->fold_pose,node->fold_pose);
+ node_stack[++sp] = node->child;
+ }
+ if (node->next != NULL)
+ {
+ copy_matrix(node->next->fold_pose,matp);
+ node_stack[++sp] = node->next;
+ }
+ }
+
+ return num_poly;
+}
+
+
+/* Draw the polygon folding using OpenGL's fixed functionality. */
+static int polygon_folding_ff(ModeInfo *mi)
+{
+ static const GLfloat light_ambient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
+ static const GLfloat light_diffuse[] = { 0.7f, 0.7f, 0.7f, 1.0f };
+ static const GLfloat light_specular[] = { 0.75f, 0.75f, 0.75f, 1.0f };
+ static const GLfloat light_pos[] = { 1.0f, 1.0f, 1.0f, 0.0f };
+ static const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+ float qu[4], qr[16];
+ int i, j, n, num_poly;
+
+ /* Collect the polygon data to draw. */
+ num_poly = gen_polygon_folding_data(pf->polygon_unfolding,pf->base_polygons,
+ pf->angle,pf->max_angle,pf->vtx,
+ pf->nrm,pf->col,pf->tex,pf->num);
+
+ glViewport(0,0,pf->WindW,pf->WindH);
+
+ glClearColor(0.0f,0.0f,0.0f,0.0f);
+ glClearDepth(1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ if (pf->aspect >= 1.0f)
+ gluPerspective(45.0,pf->aspect,0.1,30.0);
+ else
+ gluPerspective(360.0/M_PI*atan(tan(45.0*M_PI/360.0)/pf->aspect),
+ pf->aspect,0.1,30.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ /* Note: in the unfolded state, all visible, i.e, front-facing, triangles
+ are oriented counterclockwise. This means that in the folded state,
+ all visible triangles, i.e., the triangles that describe the surface of
+ the polyhedron, are back-facing. Therefore, we must disable face
+ culling. */
+ glFrontFace(GL_CCW);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ glShadeModel(GL_SMOOTH);
+ glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
+ glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
+ glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
+ glLightfv(GL_LIGHT0,GL_POSITION,light_pos);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
+ glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,30.0f);
+
+ gltrackball_get_quaternion(pf->trackball,qu);
+ quat_to_rotmat_trackball(qu,qr);
+
+ gluLookAt(pf->eye_pos[0],pf->eye_pos[1],pf->eye_pos[2],0.0,0.0,0.0,
+ 0.0,1.0,0.0);
+ glTranslatef(pf->poly_pos[0],pf->poly_pos[1],pf->poly_pos[2]);
+ glMultMatrixf(qr);
+ glRotatef(pf->alpha,1.0f,0.0f,0.0f);
+ glRotatef(pf->beta,0.0f,1.0f,0.0f);
+ glRotatef(pf->delta,0.0f,0.0f,1.0f);
+
+ /* Draw the collected polygons. */
+ n = 0;
+ for (i=0; i<num_poly; i++)
+ {
+ glBegin(GL_TRIANGLE_FAN);
+ for (j=0; j<pf->num[i]; j++)
+ {
+ glColor3fv(pf->col[n]);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,pf->col[n]);
+ glNormal3fv(pf->nrm[n]);
+ glVertex4fv(pf->vtx[n]);
+ n++;
+ }
+ glEnd();
+ }
+
+ return num_poly;
+}
+
+
+#ifdef HAVE_GLSL
+
+/* Draw the polygon folding using OpenGL's programmable functionality. */
+static int polygon_folding_pf(ModeInfo *mi)
+{
+ static const GLfloat light_model_amb[] = { 0.2f, 0.2f, 0.2f, 1.0f };
+ static const GLfloat light_ambient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
+ static const GLfloat light_diffuse[] = { 0.7f, 0.7f, 0.7f, 1.0f };
+ static const GLfloat light_model_amb_earth[] = { 0.3f, 0.3f, 0.3f, 1.0f };
+ static const GLfloat light_ambient_earth[] = { 0.5f, 0.5f, 0.5f, 1.0f };
+ static const GLfloat light_diffuse_earth[] = { 0.5f, 0.5f, 0.5f, 1.0f };
+ static const GLfloat light_specular[] = { 0.75f, 0.75f, 0.75f, 1.0f };
+ static const GLfloat light_pos[] = { 1.0f, 1.0f, 1.0f, 0.0f };
+ static const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ static const GLfloat mat_diff_white[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+ float p_mat[16], mv_mat[16], sun_pos[4];
+ float qu[4], qr[16];
+ float light_direction[4], half_vector[3];
+ int i, n, num_poly;
+
+ /* Collect the polygon data to draw. */
+ num_poly = gen_polygon_folding_data(pf->polygon_unfolding,pf->base_polygons,
+ pf->angle,pf->max_angle,pf->vtx,
+ pf->nrm,pf->col,pf->tex,pf->num);
+
+ /* Count the number of elements in the polygon data. */
+ n = 0;
+ for (i=0; i<num_poly; i++)
+ n += pf->num[i];
+
+ /* Copy the collected polygon data to the respective buffer. */
+ glBindBuffer(GL_ARRAY_BUFFER,pf->vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER,4*n*sizeof(GLfloat),pf->vtx,GL_STREAM_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+
+ glBindBuffer(GL_ARRAY_BUFFER,pf->normal_buffer);
+ glBufferData(GL_ARRAY_BUFFER,4*n*sizeof(GLfloat),pf->nrm,GL_STREAM_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+
+ glBindBuffer(GL_ARRAY_BUFFER,pf->color_buffer);
+ glBufferData(GL_ARRAY_BUFFER,4*n*sizeof(GLfloat),pf->col,GL_STREAM_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+
+ glBindBuffer(GL_ARRAY_BUFFER,pf->tex_buffer);
+ glBufferData(GL_ARRAY_BUFFER,3*n*sizeof(GLfloat),pf->tex,GL_STREAM_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
+
+ glsl_Identity(p_mat);
+ if (pf->aspect >= 1.0f)
+ glsl_Perspective(p_mat,45.0f,pf->aspect,0.1f,30.0f);
+ else
+ glsl_Perspective(p_mat,
+ 360.0f/M_PI_F*atanf(tanf(45.0f*M_PI_F/360.0f)/
+ pf->aspect),
+ pf->aspect,0.1f,30.0f);
+
+ gltrackball_get_quaternion(pf->trackball,qu);
+ quat_to_rotmat_trackball(qu,qr);
+
+ glsl_Identity(mv_mat);
+ glsl_LookAt(mv_mat,pf->eye_pos[0],pf->eye_pos[1],pf->eye_pos[2],
+ 0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
+ glsl_Translate(mv_mat,pf->poly_pos[0],pf->poly_pos[1],pf->poly_pos[2]);
+ glsl_MultMatrix(mv_mat,qr);
+ glsl_Rotate(mv_mat,pf->alpha,1.0f,0.0f,0.0f);
+ glsl_Rotate(mv_mat,pf->beta,0.0f,1.0f,0.0f);
+ glsl_Rotate(mv_mat,pf->delta,0.0f,0.0f,1.0f);
+
+ if (!pf->use_textures)
+ {
+ light_direction[0] = light_pos[0];
+ light_direction[1] = light_pos[1];
+ light_direction[2] = light_pos[2];
+ light_direction[3] = 0.0f;
+ normalize(light_direction);
+ half_vector[0] = light_direction[0];
+ half_vector[1] = light_direction[1];
+ half_vector[2] = light_direction[2]+1.0f;
+ normalize(half_vector);
+ }
+ else
+ {
+ get_sun_direction(get_current_julian_date(),sun_pos);
+ if (!pf->north_up)
+ {
+ sun_pos[0] = -sun_pos[0];
+ sun_pos[2] = -sun_pos[2];
+ }
+ glsl_MultMatrixVector(light_direction,mv_mat,sun_pos);
+ normalize(light_direction);
+ half_vector[0] = light_direction[0];
+ half_vector[1] = light_direction[1];
+ half_vector[2] = light_direction[2];
+ normalize(half_vector);
+ }
+
+ glUseProgram(pf->shader_program);
+
+ glViewport(0,0,pf->WindW,pf->WindH);
+
+ glClearColor(0.0f,0.0f,0.0f,0.0f);
+ glClearDepth(1.0f);
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+ glFrontFace(GL_CCW);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+
+ if (!pf->use_textures)
+ {
+ glUniform4fv(pf->glbl_ambient_index,1,light_model_amb);
+ glUniform4fv(pf->lt_ambient_index,1,light_ambient);
+ glUniform4fv(pf->lt_diffuse_index,1,light_diffuse);
+ }
+ else
+ {
+ glUniform4fv(pf->glbl_ambient_index,1,light_model_amb_earth);
+ glUniform4fv(pf->lt_ambient_index,1,light_ambient_earth);
+ glUniform4fv(pf->lt_diffuse_index,1,light_diffuse_earth);
+ }
+ glUniform4fv(pf->lt_specular_index,1,light_specular);
+ glUniform3fv(pf->lt_direction_index,1,light_direction);
+ glUniform3fv(pf->lt_halfvect_index,1,half_vector);
+ glUniform4fv(pf->ambient_index,1,mat_diff_white);
+ glUniform4fv(pf->diffuse_index,1,mat_diff_white);
+ glUniform4fv(pf->specular_index,1,mat_specular);
+ glUniform1f(pf->shininess_index,30.0f);
+ glUniform1i(pf->north_up_index,pf->north_up);
+ glUniform1i(pf->bool_textures_index,pf->use_textures);
+ glUniform3fv(pf->magma_offs_index,1,pf->magma_tex_offset);
+
+ glUniformMatrix4fv(pf->proj_index,1,GL_FALSE,p_mat);
+ glUniformMatrix4fv(pf->mv_index,1,GL_FALSE,mv_mat);
+
+ if (pf->textures_supported)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_3D,pf->magma_tex);
+ glUniform1i(pf->samp_mgm_index,0);
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D,pf->earth_tex[0]);
+ glUniform1i(pf->samp_day_index,1);
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D,pf->earth_tex[1]);
+ glUniform1i(pf->samp_ngt_index,2);
+
+ glActiveTexture(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_2D,pf->earth_tex[2]);
+ glUniform1i(pf->samp_wtr_index,3);
+ }
+
+ /* Draw the collected polygons. */
+ glEnableVertexAttribArray(pf->pos_index);
+ glBindBuffer(GL_ARRAY_BUFFER,pf->vertex_buffer);
+ glVertexAttribPointer(pf->pos_index,4,GL_FLOAT,GL_FALSE,0,0);
+
+ glEnableVertexAttribArray(pf->normal_index);
+ glBindBuffer(GL_ARRAY_BUFFER,pf->normal_buffer);
+ glVertexAttribPointer(pf->normal_index,4,GL_FLOAT,GL_FALSE,0,0);
+
+ glEnableVertexAttribArray(pf->color_index);
+ glBindBuffer(GL_ARRAY_BUFFER,pf->color_buffer);
+ glVertexAttribPointer(pf->color_index,4,GL_FLOAT,GL_FALSE,0,0);
+
+ glEnableVertexAttribArray(pf->tex_index);
+ glBindBuffer(GL_ARRAY_BUFFER,pf->tex_buffer);
+ glVertexAttribPointer(pf->tex_index,3,GL_FLOAT,GL_FALSE,0,0);
+
+ n = 0;
+ for (i=0; i<num_poly; i++)
+ {
+ glDrawArrays(GL_TRIANGLE_FAN,n,pf->num[i]);
+ n += pf->num[i];
+ }
+
+ glDisableVertexAttribArray(pf->pos_index);
+ glDisableVertexAttribArray(pf->normal_index);
+ glDisableVertexAttribArray(pf->color_index);
+ glDisableVertexAttribArray(pf->tex_index);
+ glBindBuffer(GL_ARRAY_BUFFER,0);
+
+ glUseProgram(0);
+
+ return num_poly;
+}
+
+#endif /* HAVE_GLSL */
+
+
+/* Display the Platonic solid unfolding and folding. */
+static void display_platonicfolding(ModeInfo *mi)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+ int i, poly;
+ bool change_dir;
+ float t;
+
+ if (!pf->button_pressed)
+ {
+ if (pf->anim_state == ANIM_INIT)
+ {
+#ifdef HAVE_GLSL
+ /* Select whether to use textures. Textures are used in one third of
+ the animations if the user has selected random colors. */
+ if (pf->random_colors)
+ pf->use_textures = pf->textures_supported && frand(1.0) < 1.0/3.0;
+ else
+ pf->use_textures = (pf->colors == COLORS_EARTH);
+
+ /* Select a random offset for the magma texture. */
+ pf->magma_tex_offset[0] = 0.2f*((float)frand(1.0)-0.5f);
+ pf->magma_tex_offset[1] = 0.2f*((float)frand(1.0)-0.5f);
+ pf->magma_tex_offset[2] = 0.2f*((float)frand(1.0)-0.5f);
+#endif /* HAVE_GLSL */
+
+ /* Select whether the north or south pole are facing upwards in the
+ texture. Both cases occur with equal probability. */
+ pf->north_up = frand(1.0) < 0.5;
+
+ /* Select whether to fold upwards or downwards. Both cases occur with
+ equal probability. */
+ pf->alpha = frand(1.0) < 0.5 ? 300.0f : 120.0f;
+
+ /* Set the rotation around the y axis to 0. */
+ pf->beta = 0.0f;
+
+ /* Initialize the rotation around the z axis to a random angle. */
+ pf->delta = (float)frand(360.0);
+
+ /* Set the angle increment for the rotation around the z axis. */
+ if (pf->north_up)
+ pf->delta_delta = 0.5f;
+ else
+ pf->delta_delta = -0.5f;
+
+ /* Select a random polyhedron that is different from the last
+ polyhedron. */
+ do
+ poly = (int)floor(frand(5.0));
+ while (poly == pf->anim_poly);
+ pf->anim_poly = poly;
+ init_color_matrix(pf->color_matrix);
+ init_polygon_unfolding(mi,pf->anim_poly);
+ if (pf->num_foldings == RANDOM_NUM_FOLDINGS)
+ {
+ if (pf->anim_poly == TETRAHEDRON)
+ pf->anim_remaining = 3+(int)floor(frand(3.0));
+ else if (pf->anim_poly == HEXAHEDRON || pf->anim_poly == OCTAHEDRON)
+ pf->anim_remaining = 4+(int)floor(frand(5.0));
+ else /* pf->anim_poly==DODECAHEDRON || pf->anim_poly==ICOSAHEDRON */
+ pf->anim_remaining = 6+(int)floor(frand(7.0));
+ }
+ else
+ {
+ pf->anim_remaining = pf->num_foldings;
+ }
+
+ /* Set all angles to the folded state. */
+ for (i=0; i<pf->num_fold_angles; i++)
+ pf->angle[i] = pf->max_angle;
+ for (; i<NUM_ANGLES; i++)
+ pf->angle[i] = 0.0f;
+
+ /* Initialize the fold angle index. */
+ pf->fold_angle = 0;
+
+ /* Set the appear and disappear parameters of the polyhedron. */
+ if (pf->aspect >= 1.0f)
+ {
+ pf->spoly_pos[0] = 0.0f;
+ pf->spoly_pos[1] = -pf->eye_pos[2]*(2.0f/3.0f);
+ pf->spoly_pos[2] = 0.0f;
+ pf->dpoly_pos[0] = 0.0f;
+ pf->dpoly_pos[1] = pf->eye_pos[2]*(2.0f/3.0f);
+ pf->dpoly_pos[2] = 0.0f;
+ }
+ else
+ {
+ pf->spoly_pos[0] = -pf->eye_pos[2]*(2.0f/3.0f);
+ pf->spoly_pos[1] = 0.0f;
+ pf->spoly_pos[2] = 0.0f;
+ pf->dpoly_pos[0] = pf->eye_pos[2]*(2.0f/3.0f);
+ pf->dpoly_pos[1] = 0.0f;
+ pf->dpoly_pos[2] = 0.0f;
+ }
+ pf->poly_pos[0] = pf->spoly_pos[0];
+ pf->poly_pos[1] = pf->spoly_pos[1];
+ pf->poly_pos[2] = pf->spoly_pos[2];
+
+ pf->anim_num_steps = 180;
+ pf->anim_step = 0;
+ pf->anim_state = ANIM_APPEAR;
+ }
+
+ if (pf->anim_state == ANIM_APPEAR)
+ {
+ t = (float)pf->anim_step/(float)pf->anim_num_steps;
+ t = ease(t,1.0f,EASING_DECEL);
+ pf->poly_pos[0] = pf->spoly_pos[0]+t*pf->dpoly_pos[0];
+ pf->poly_pos[1] = pf->spoly_pos[1]+t*pf->dpoly_pos[1];
+ pf->poly_pos[2] = pf->spoly_pos[2]+t*pf->dpoly_pos[2];
+
+ if (pf->rotate)
+ pf->delta += pf->delta_delta;
+
+ pf->anim_step++;
+ if (pf->anim_step > pf->anim_num_steps)
+ {
+ /* Select the unfolding mode. Separate unfolding is used in one
+ fourth of the cases. */
+ pf->anim_state =
+ frand(1.0) < 0.25 ? ANIM_UNFOLD_SEP : ANIM_UNFOLD_JNT;
+ }
+ }
+ else if (pf->anim_state == ANIM_DISAPPEAR)
+ {
+ t = (float)pf->anim_step/(float)pf->anim_num_steps;
+ t = 1.0f+ease(t,1.0f,EASING_ACCEL);
+ pf->poly_pos[0] = pf->spoly_pos[0]+t*pf->dpoly_pos[0];
+ pf->poly_pos[1] = pf->spoly_pos[1]+t*pf->dpoly_pos[1];
+ pf->poly_pos[2] = pf->spoly_pos[2]+t*pf->dpoly_pos[2];
+
+ if (pf->rotate)
+ pf->delta += pf->delta_delta;
+
+ pf->anim_step++;
+ if (pf->anim_step > pf->anim_num_steps)
+ pf->anim_state = ANIM_INIT;
+ }
+ else if (pf->anim_state == ANIM_UNFOLD_JNT)
+ {
+ change_dir = false;
+ for (i=0; i<pf->num_fold_angles; i++)
+ {
+ pf->angle[i] -= pf->delta_angle/3.0f;
+ if (pf->angle[i] < 0.0f)
+ {
+ pf->angle[i] = 0.0f;
+ change_dir = true;
+ }
+ }
+
+ if (pf->rotate)
+ pf->delta += pf->delta_delta;
+
+ if (change_dir)
+ pf->anim_state = ANIM_FOLD_JNT;
+ }
+ else if (pf->anim_state == ANIM_FOLD_JNT)
+ {
+ change_dir = false;
+ for (i=0; i<pf->num_fold_angles; i++)
+ {
+ pf->angle[i] += pf->delta_angle/3.0f;
+ if (pf->angle[i] > pf->max_angle)
+ {
+ pf->angle[i] = pf->max_angle;
+ change_dir = true;
+ }
+ }
+
+ if (pf->rotate)
+ pf->delta += pf->delta_delta;
+
+ if (change_dir)
+ {
+ pf->anim_remaining--;
+ if (pf->anim_remaining > 0)
+ {
+ /* Create a new unfolding of the current polhedron type. */
+ init_polygon_unfolding(mi,pf->anim_poly);
+ /* Select the unfolding mode. Separate unfolding is used in one
+ fourth of the cases. */
+ pf->anim_state =
+ frand(1.0) < 0.25 ? ANIM_UNFOLD_SEP : ANIM_UNFOLD_JNT;
+ }
+ else
+ {
+ pf->anim_num_steps = 180;
+ pf->anim_step = 0;
+ pf->anim_state = ANIM_DISAPPEAR;
+ }
+ }
+ }
+ else if (pf->anim_state == ANIM_UNFOLD_SEP)
+ {
+ change_dir = false;
+ pf->angle[pf->fold_angle] -= pf->delta_angle;
+ if (pf->angle[pf->fold_angle] < 0.0f)
+ {
+ pf->angle[pf->fold_angle] = 0.0f;
+ pf->fold_angle++;
+ if (pf->fold_angle >= pf->num_fold_angles)
+ {
+ pf->fold_angle = pf->num_fold_angles-1;
+ change_dir = true;
+ }
+ }
+
+ if (pf->rotate)
+ pf->delta += pf->delta_delta;
+
+ if (change_dir)
+ pf->anim_state = ANIM_FOLD_SEP;
+ }
+ else if (pf->anim_state == ANIM_FOLD_SEP)
+ {
+ change_dir = false;
+ pf->angle[pf->fold_angle] += pf->delta_angle;
+ if (pf->angle[pf->fold_angle] > pf->max_angle)
+ {
+ pf->angle[pf->fold_angle] = pf->max_angle;
+ pf->fold_angle--;
+ if (pf->fold_angle < 0)
+ {
+ pf->fold_angle = 0;
+ change_dir = true;
+ }
+ }
+
+ if (pf->rotate)
+ pf->delta += pf->delta_delta;
+
+ if (change_dir)
+ {
+ pf->anim_remaining--;
+ if (pf->anim_remaining > 0)
+ {
+ /* Create a new unfolding of the current polhedron type. */
+ init_polygon_unfolding(mi,pf->anim_poly);
+ /* Select the unfolding mode. Separate unfolding is used in one
+ fourth of the cases. */
+ pf->anim_state =
+ frand(1.0) < 0.25 ? ANIM_UNFOLD_SEP : ANIM_UNFOLD_JNT;
+ }
+ else
+ {
+ pf->anim_num_steps = 180;
+ pf->anim_step = 0;
+ pf->anim_state = ANIM_DISAPPEAR;
+ }
+ }
+ }
+ }
+
+ gltrackball_rotate(pf->trackball);
+
+#ifdef HAVE_GLSL
+ if (pf->use_shaders)
+ mi->polygon_count = polygon_folding_pf(mi);
+ else
+#endif /* HAVE_GLSL */
+ mi->polygon_count = polygon_folding_ff(mi);
+}
+
+
+
+/* ------------------------------------------------------------------------- */
+
+
+#ifdef HAVE_GLSL
+
+
+/* Set up and enable texturing on our object */
+static void setup_xpm_texture(ModeInfo *mi, const unsigned char *data,
+ unsigned long size)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+ XImage *image;
+ GLint max_aniso, aniso;
+
+ image = image_data_to_ximage(MI_DISPLAY(mi),MI_VISUAL(mi),data,size);
+#ifndef HAVE_JWZGLES
+ glEnable(GL_TEXTURE_2D);
+#endif
+ glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+ glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,image->width,image->height,0,GL_RGBA,
+ GL_UNSIGNED_BYTE,image->data);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ if (pf->use_mipmaps)
+ {
+ glGenerateMipmap(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,4);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,
+ GL_LINEAR_MIPMAP_LINEAR);
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ }
+ if (pf->aniso_textures)
+ {
+ glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&max_aniso);
+ aniso = MIN(max_aniso,10);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_ANISOTROPY_EXT,aniso);
+ }
+ XDestroyImage(image);
+}
+
+
+/* Generate the textures that show the the earth by day and night. */
+static void gen_earth_textures(ModeInfo *mi)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+
+ glGenTextures(3,pf->earth_tex);
+
+ /* Set up the earth by day texture. */
+ glBindTexture(GL_TEXTURE_2D,pf->earth_tex[0]);
+ setup_xpm_texture(mi,earth_png,sizeof(earth_png));
+
+ /* Set up the earth by night texture. */
+ glBindTexture(GL_TEXTURE_2D,pf->earth_tex[1]);
+ setup_xpm_texture(mi,earth_night_png,sizeof(earth_night_png));
+
+ /* Set up the earth water texture. */
+ glBindTexture(GL_TEXTURE_2D,pf->earth_tex[2]);
+ setup_xpm_texture(mi,earth_water_png,sizeof(earth_water_png));
+
+ glBindTexture(GL_TEXTURE_2D,0);
+}
+
+
+/* Generate the magma texture. */
+static void gen_magma_texture(ModeInfo *mi)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+ GLint max_aniso, aniso;
+
+ pf->magma_tex_rg = gen_3d_magma_texture_rg(MAGMA_TEX_SIZE);
+
+#ifndef HAVE_JWZGLES
+ glEnable(GL_TEXTURE_3D);
+#endif
+ glGenTextures(1,&pf->magma_tex);
+ glBindTexture(GL_TEXTURE_3D,pf->magma_tex);
+ glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+#ifdef HAVE_JWZGLES
+ glTexImage3D(GL_TEXTURE_3D,0,GL_RG8,MAGMA_TEX_SIZE,MAGMA_TEX_SIZE,
+ MAGMA_TEX_SIZE,0,GL_RG,GL_UNSIGNED_BYTE,pf->magma_tex_rg);
+#else
+ glTexImage3D(GL_TEXTURE_3D,0,GL_RG,MAGMA_TEX_SIZE,MAGMA_TEX_SIZE,
+ MAGMA_TEX_SIZE,0,GL_RG,GL_UNSIGNED_BYTE,pf->magma_tex_rg);
+#endif
+ glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_WRAP_R,GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ if (pf->use_mipmaps)
+ {
+ glGenerateMipmap(GL_TEXTURE_3D);
+ glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MIN_FILTER,
+ GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MAX_LEVEL,4);
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ }
+ if (pf->aniso_textures)
+ {
+ glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&max_aniso);
+ aniso = MIN(max_aniso,10);
+ glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MAX_ANISOTROPY_EXT,aniso);
+ }
+ glBindTexture(GL_TEXTURE_2D,0);
+}
+
+
+/* Initialize the programmable OpenGL functionality. */
+static void init_glsl(ModeInfo *mi)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+ GLint gl_major, gl_minor, glsl_major, glsl_minor;
+ GLboolean gl_gles3;
+ const char *gl_ext;
+ const GLchar *vertex_shader_source[3];
+ const GLchar *fragment_shader_source[4];
+
+ pf->use_textures = false;
+ pf->use_mipmaps = false;
+
+ pf->magma_tex_offset[0] = 0.0f;
+ pf->magma_tex_offset[1] = 0.0f;
+ pf->magma_tex_offset[2] = 0.0f;
+
+ pf->use_shaders = false;
+ pf->textures_supported = false;
+ pf->aniso_textures = false;
+ pf->shader_program = 0;
+
+ if (!glsl_GetGlAndGlslVersions(&gl_major,&gl_minor,&glsl_major,&glsl_minor,
+ &gl_gles3))
+ return;
+
+ if (!gl_gles3)
+ {
+ if (gl_major < 3 ||
+ (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 30)))
+ {
+ if ((gl_major < 2 || (gl_major == 2 && gl_minor < 1)) ||
+ (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 20)))
+ return;
+ /* We have at least OpenGL 2.1 and at least GLSL 1.20. */
+ vertex_shader_source[0] = shader_version_2_1;
+ vertex_shader_source[1] = vertex_shader_attribs_2_1;
+ vertex_shader_source[2] = vertex_shader_main;
+ fragment_shader_source[0] = shader_version_2_1;
+ fragment_shader_source[1] = fragment_shader_attribs_2_1;
+ fragment_shader_source[2] = fragment_shader_main;
+ fragment_shader_source[3] = fragment_shader_out_2_1;
+ }
+ else
+ {
+ /* We have at least OpenGL 3.0 and at least GLSL 1.30. */
+ vertex_shader_source[0] = shader_version_3_0;
+ vertex_shader_source[1] = vertex_shader_attribs_3_0;
+ vertex_shader_source[2] = vertex_shader_main;
+ fragment_shader_source[0] = shader_version_3_0;
+ fragment_shader_source[1] = fragment_shader_attribs_3_0;
+ fragment_shader_source[2] = fragment_shader_main;
+ fragment_shader_source[3] = fragment_shader_out_3_0;
+ }
+ }
+ else /* gl_gles3 */
+ {
+ if (gl_major < 3 || glsl_major < 3)
+ return;
+ /* We have at least OpenGL ES 3.0 and at least GLSL ES 3.0. */
+ vertex_shader_source[0] = shader_version_3_0_es;
+ vertex_shader_source[1] = vertex_shader_attribs_3_0;
+ vertex_shader_source[2] = vertex_shader_main;
+ fragment_shader_source[0] = shader_version_3_0_es;
+ fragment_shader_source[1] = fragment_shader_attribs_3_0;
+ fragment_shader_source[2] = fragment_shader_main;
+ fragment_shader_source[3] = fragment_shader_out_3_0;
+ }
+
+ if (!glsl_CompileAndLinkShaders(3,vertex_shader_source,
+ 4,fragment_shader_source,
+ &pf->shader_program))
+ return;
+
+ pf->pos_index =
+ glGetAttribLocation(pf->shader_program,"VertexPosition");
+ pf->normal_index =
+ glGetAttribLocation(pf->shader_program,"VertexNormal");
+ pf->color_index =
+ glGetAttribLocation(pf->shader_program,"VertexColor");
+ pf->tex_index =
+ glGetAttribLocation(pf->shader_program,"VertexTexCoord");
+ pf->mv_index =
+ glGetUniformLocation(pf->shader_program,"MatModelView");
+ pf->proj_index =
+ glGetUniformLocation(pf->shader_program,"MatProj");
+ pf->magma_offs_index =
+ glGetUniformLocation(pf->shader_program,"TexOffsetMagma");
+ pf->north_up_index =
+ glGetUniformLocation(pf->shader_program,"NorthUp");
+ pf->glbl_ambient_index =
+ glGetUniformLocation(pf->shader_program,"LtGlblAmbient");
+ pf->lt_ambient_index =
+ glGetUniformLocation(pf->shader_program,"LtAmbient");
+ pf->lt_diffuse_index =
+ glGetUniformLocation(pf->shader_program,"LtDiffuse");
+ pf->lt_specular_index =
+ glGetUniformLocation(pf->shader_program,"LtSpecular");
+ pf->lt_direction_index =
+ glGetUniformLocation(pf->shader_program,"LtDirection");
+ pf->lt_halfvect_index =
+ glGetUniformLocation(pf->shader_program,"LtHalfVector");
+ pf->ambient_index =
+ glGetUniformLocation(pf->shader_program,"MatAmbient");
+ pf->diffuse_index =
+ glGetUniformLocation(pf->shader_program,"MatDiffuse");
+ pf->specular_index =
+ glGetUniformLocation(pf->shader_program,"MatSpecular");
+ pf->shininess_index =
+ glGetUniformLocation(pf->shader_program,"MatShininess");
+ pf->bool_textures_index =
+ glGetUniformLocation(pf->shader_program,"BoolTextures");
+ pf->samp_mgm_index =
+ glGetUniformLocation(pf->shader_program,"TextureSamplerMagma");
+ pf->samp_day_index =
+ glGetUniformLocation(pf->shader_program,"TextureSamplerDay");
+ pf->samp_ngt_index =
+ glGetUniformLocation(pf->shader_program,"TextureSamplerNight");
+ pf->samp_wtr_index =
+ glGetUniformLocation(pf->shader_program,"TextureSamplerWater");
+ if (pf->pos_index == -1 ||
+ pf->normal_index == -1 ||
+ pf->color_index == -1 ||
+ pf->tex_index == -1 ||
+ pf->mv_index == -1 ||
+ pf->proj_index == -1 ||
+ pf->magma_offs_index == -1 ||
+ pf->north_up_index == -1 ||
+ pf->glbl_ambient_index == -1 ||
+ pf->lt_ambient_index == -1 ||
+ pf->lt_diffuse_index == -1 ||
+ pf->lt_specular_index == -1 ||
+ pf->lt_direction_index == -1 ||
+ pf->lt_halfvect_index == -1 ||
+ pf->ambient_index == -1 ||
+ pf->diffuse_index == -1 ||
+ pf->specular_index == -1 ||
+ pf->shininess_index == -1 ||
+ pf->bool_textures_index == -1 ||
+ pf->samp_mgm_index == -1 ||
+ pf->samp_day_index == -1 ||
+ pf->samp_ngt_index == -1 ||
+ pf->samp_wtr_index == -1)
+ {
+ glDeleteProgram(pf->shader_program);
+ return;
+ }
+
+ glGenBuffers(1,&pf->vertex_buffer);
+ glGenBuffers(1,&pf->normal_buffer);
+ glGenBuffers(1,&pf->color_buffer);
+ glGenBuffers(1,&pf->tex_buffer);
+
+ pf->textures_supported = true;
+ if (!gl_gles3 && gl_major < 3)
+ {
+ gl_ext = (const char *)glGetString(GL_EXTENSIONS);
+ if (gl_ext == NULL)
+ pf->textures_supported = false;
+ else
+ pf->textures_supported = (strstr(gl_ext,"GL_ARB_texture_rg") != NULL);
+ }
+
+ pf->magma_tex_rg = NULL;
+ if (pf->textures_supported)
+ {
+ gl_ext = (char *)glGetString(GL_EXTENSIONS);
+ if (gl_ext == NULL)
+ gl_ext = "";
+#if defined(HAVE_IPHONE)
+ /* Don't use anisotropic texture filtering on iOS since it leads to
+ artifacts at the ±180° meridian. */
+ pf->aniso_textures = false;
+#else
+ pf->aniso_textures =
+ ((strstr(gl_ext,"GL_EXT_texture_filter_anisotropic") != NULL) ||
+ (strstr(gl_ext,"GL_ARB_texture_filter_anisotropic") != NULL));
+#endif
+
+ if (!gl_gles3 && gl_major < 3)
+ {
+ /* On OpenGL 2.1 systems, such as the native macOS OpenGL version used
+ by XScreenSaver, we need the GL_ARB_framebuffer_object extension
+ to be able to use the glGenerateMipmap function. */
+ if (strstr(gl_ext,"GL_ARB_framebuffer_object") != NULL)
+ pf->use_mipmaps = true;
+ }
+ else if ((!gl_gles3 && gl_major >= 3) || gl_gles3)
+ {
+ pf->use_mipmaps = true;
+ }
+
+ gen_magma_texture(mi);
+ gen_earth_textures(mi);
+ }
+
+ pf->use_shaders = true;
+#if 0
+ fprintf(stderr,"Use shaders = %d\n",pf->use_shaders);
+ fprintf(stderr,"Textures supported = %d\n",pf->textures_supported);
+ fprintf(stderr,"Error = %d\n",glGetError());
+#endif
+}
+
+#endif /* HAVE_GLSL */
+
+
+/* Initialize the data structures. */
+static void init(ModeInfo *mi)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+ int i;
+
+#ifdef HAVE_GLSL
+ init_glsl(mi);
+#endif /* HAVE_GLSL */
+
+ pf->anim_state = ANIM_INIT;
+ pf->anim_poly = -1;
+ pf->fold_angle = 0;
+ pf->north_up = true;
+
+ pf->alpha = 300.0f;
+ pf->beta = 0.0f;
+ pf->delta = 270.0f;
+ pf->delta_delta = 0.0f;
+
+ pf->poly_pos[0] = 0.0f;
+ pf->poly_pos[1] = 0.0f;
+ pf->poly_pos[2] = 0.0f;
+
+ for (i=0; i<NUM_ANGLES; i++)
+ pf->angle[i] = 0.0f;
+ pf->angle_dir = 1.0f;
+
+ init_color_matrix(pf->color_matrix);
+
+ pf->polygon_unfolding =
+ create_random_polyhedron_unfolding(TETRAHEDRON_NUM_FACES,
+ TETRAHEDRON_NUM_EDGES,
+ tetrahedron_edges);
+ pf->max_angle = TETRAHEDRON_MAX_ANGLE;
+ pf->delta_angle = TETRAHEDRON_DELTA_ANGLE;
+ pf->num_fold_angles = TETRAHEDRON_NUM_FACES-1;
+ pf->eye_pos = tetrahedron_eye_pos;
+ pf->base_polygons = init_base_polygons(pf->polygon_unfolding,
+ &tetrahedron_triangle);
+ determine_unfolding_poses(pf->polygon_unfolding,pf->base_polygons);
+ determine_polygon_color_data(pf->polygon_unfolding,pf->base_polygons,
+ pf->max_angle,pf->color_matrix);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ * Xlock hooks.
+ *-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ */
+
+
+ENTRYPOINT void reshape_platonicfolding(ModeInfo *mi, int width, int height)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+
+ pf->WindW = (GLint)width;
+ pf->WindH = (GLint)height;
+ pf->aspect = (GLfloat)width/(GLfloat)height;
+
+ /* Set the appear and disappear parameters of the polyhedron. */
+ if (pf->eye_pos != NULL)
+ {
+ if (pf->aspect >= 1.0f)
+ {
+ pf->spoly_pos[0] = 0.0f;
+ pf->spoly_pos[1] = -pf->eye_pos[2]*(2.0f/3.0f);
+ pf->spoly_pos[2] = 0.0f;
+ pf->dpoly_pos[0] = 0.0f;
+ pf->dpoly_pos[1] = pf->eye_pos[2]*(2.0f/3.0f);
+ pf->dpoly_pos[2] = 0.0f;
+ }
+ else
+ {
+ pf->spoly_pos[0] = -pf->eye_pos[2]*(2.0f/3.0f);
+ pf->spoly_pos[1] = 0.0f;
+ pf->spoly_pos[2] = 0.0f;
+ pf->dpoly_pos[0] = pf->eye_pos[2]*(2.0f/3.0f);
+ pf->dpoly_pos[1] = 0.0f;
+ pf->dpoly_pos[2] = 0.0f;
+ }
+ }
+}
+
+
+ENTRYPOINT Bool platonicfolding_handle_event(ModeInfo *mi, XEvent *event)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+
+ if (event->xany.type == ButtonPress &&
+ event->xbutton.button == Button1)
+ {
+ pf->button_pressed = True;
+ gltrackball_start(pf->trackball,event->xbutton.x,event->xbutton.y,
+ MI_WIDTH(mi),MI_HEIGHT(mi));
+ return True;
+ }
+ else if (event->xany.type == ButtonRelease &&
+ event->xbutton.button == Button1)
+ {
+ pf->button_pressed = False;
+ gltrackball_stop(pf->trackball);
+ return True;
+ }
+ else if (event->xany.type == MotionNotify && pf->button_pressed)
+ {
+ gltrackball_track(pf->trackball,event->xmotion.x,event->xmotion.y,
+ MI_WIDTH(mi),MI_HEIGHT(mi));
+ return True;
+ }
+ else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
+ {
+ pf->anim_state = ANIM_INIT;
+ return True;
+ }
+
+ return False;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ * Xlock hooks.
+ *-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ */
+
+/*
+ *-----------------------------------------------------------------------------
+ * Initialize platonicfolding. Called each time the window changes.
+ *-----------------------------------------------------------------------------
+ */
+
+ENTRYPOINT void init_platonicfolding(ModeInfo *mi)
+{
+ platonicfoldingstruct *pf;
+ int n;
+
+ MI_INIT(mi,platonicfolding);
+ pf = &platonicfolding[MI_SCREEN(mi)];
+
+ pf->rotate = rotate;
+
+ /* Set the color mode. */
+ if (!strcasecmp(color_mode,"face"))
+ {
+ pf->colors = COLORS_FACE;
+ pf->random_colors = false;
+ }
+ else if (!strcasecmp(color_mode,"earth"))
+ {
+ pf->colors = COLORS_EARTH;
+ pf->random_colors = false;
+ }
+ else
+ {
+ pf->colors = random() % NUM_COLORS;
+ pf->random_colors = true;
+ }
+#ifndef HAVE_GLSL
+ if (pf->colors == COLORS_EARTH)
+ pf->colors = COLORS_FACE;
+#endif
+
+ /* Set the number of foldings. */
+ pf->num_foldings = RANDOM_NUM_FOLDINGS;
+ if (!strcasecmp(foldings,"random"))
+ {
+ pf->num_foldings = RANDOM_NUM_FOLDINGS;
+ }
+ else
+ {
+ n = sscanf(foldings,"%d",&pf->num_foldings);
+ if (n != 1)
+ pf->num_foldings = RANDOM_NUM_FOLDINGS;
+ else if (pf->num_foldings < 1)
+ pf->num_foldings = 1;
+ else if (pf->num_foldings > 20)
+ pf->num_foldings = 20;
+ }
+
+ pf->trackball = gltrackball_init(False);
+ pf->button_pressed = False;
+
+ if ((pf->glx_context = init_GL(mi)) != NULL)
+ {
+ pf->eye_pos = NULL;
+ reshape_platonicfolding(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
+ init(mi);
+ }
+ else
+ {
+ MI_CLEARWINDOW(mi);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Called by the mainline code periodically to update the display.
+ *-----------------------------------------------------------------------------
+ */
+ENTRYPOINT void draw_platonicfolding(ModeInfo *mi)
+{
+ Display *display = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ platonicfoldingstruct *pf;
+
+ if (platonicfolding == NULL)
+ return;
+ pf = &platonicfolding[MI_SCREEN(mi)];
+
+ MI_IS_DRAWN(mi) = True;
+ if (!pf->glx_context)
+ return;
+
+ glXMakeCurrent(display,window,*pf->glx_context);
+
+ display_platonicfolding(mi);
+
+ if (MI_IS_FPS(mi))
+ do_fps (mi);
+
+ glFlush();
+
+ glXSwapBuffers(display,window);
+}
+
+
+#ifndef STANDALONE
+ENTRYPOINT void change_platonicfolding(ModeInfo *mi)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+
+ if (!pf->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*pf->glx_context);
+ init(mi);
+}
+#endif /* !STANDALONE */
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * The display is being taken away from us. Free up malloc'ed
+ * memory and X resources that we've alloc'ed.
+ *-----------------------------------------------------------------------------
+ */
+
+ENTRYPOINT void free_platonicfolding(ModeInfo *mi)
+{
+ platonicfoldingstruct *pf = &platonicfolding[MI_SCREEN(mi)];
+
+ if (!pf->glx_context) return;
+ glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*pf->glx_context);
+
+ gltrackball_free(pf->trackball);
+
+ free_base_polygons(pf->base_polygons);
+
+#ifdef HAVE_GLSL
+ if (pf->use_shaders)
+ {
+ glUseProgram(0);
+ if (pf->shader_program != 0)
+ glDeleteProgram(pf->shader_program);
+ glDeleteBuffers(1,&pf->vertex_buffer);
+ glDeleteBuffers(1,&pf->normal_buffer);
+ glDeleteBuffers(1,&pf->color_buffer);
+ glDeleteBuffers(1,&pf->tex_buffer);
+ if (pf->textures_supported)
+ {
+ glDeleteTextures(1,&pf->magma_tex);
+ free(pf->magma_tex_rg);
+ glDeleteTextures(3,pf->earth_tex);
+ }
+ }
+#endif /* HAVE_GLSL */
+}
+
+
+XSCREENSAVER_MODULE ("PlatonicFolding", platonicfolding)
+
+#endif /* USE_GL */
--- /dev/null
+.TH XScreenSaver 1 "" "X Version 11"
+.SH NAME
+platonicfolding \- Draws the unfolding and folding of the Platonic solids
+.SH SYNOPSIS
+.B platonicfolding
+[\-\-display \fIhost:display.screen\fP]
+[\-\-install]
+[\-\-visual \fIvisual\fP]
+[\-\-window]
+[\-\-root]
+[\-\-window\-id \fInumber\fP]
+[\-\-delay \fIusecs\fP]
+[\-\-fps]
+[\-\-rotate]
+[\-\-colors \fIcolor-scheme\fP]
+[\-\-face-colors]
+[\-\-earth-colors]
+[\-\-foldings \fInum-foldings\fP]
+.SH DESCRIPTION
+The \fIplatonicfolding\fP program shows the unfolding and folding of
+the Platonic solids. For the five Platonic solids (the tetrahedron,
+cube, octahedron, dodecahedron, and icosahedron), all unfoldings of
+its faces are non-overlapping: they form a net. The tetrahedron has 16
+unfoldings, of which two are essentially different (non-isomorphic),
+the cube and octahedron each have 384 unfoldings, of which eleven are
+non-isomorphic, and the dodecahedron and icosahedron each have
+5,184,000 unfoldings, of which 43,380 are non-isomorphic. This program
+displays randomly selected unfoldings for the five Platonic solids.
+Note that while it is guaranteed that the nets of the Platonic solids
+are non-overlapping, their faces occasionally intersect during the
+unfolding and folding.
+.PP
+The program displays the Platonic solids either using different colors
+for each face (face colors) or with a illuminated view of the earth
+(earth colors). When using face colors, the colors of the faces are
+randomly chosen each time a new Platonic solid is selected. When
+using earth colors, the Platonic solid is displayed as if the sphere
+of the earth were illuminated with the current position of the sun at
+the time the program is run. The hemisphere the sun is currently
+illuminating is displayed with a satellite image of the earth by day
+and the other hemisphere is displayed with a satellite image of the
+earth by night. The specular highlight on the illuminated hemisphere
+(which is only shown over bodies of water) is the subsolar point (the
+point on earth above which the sun is perpendicular). The earth's
+sphere is then projected onto the Platonic solid via a gnomonic
+projection. The program randomly selects whether the north pole or
+the south pole is facing upwards. The inside of the earth is
+displayed with a magma-like texture.
+.PP
+At the beginning of each cycle, the program selects one of the five
+Platonic solids randomly and moves it to the center of the screen. It
+then repeatedly selects a random net of the polyhedron and unfolds and
+folds the polyhedron. The unfolding and folding can occur around each
+edge of the net successively or around all edges simultaneously. At
+the end of each cycle, the Platonic solid is moved offscreen and the
+next cycle begins.
+.PP
+While the Platonic solid is moved on the screen or is unfolded or
+folded, it is rotated by default. If earth colors are used, the
+rotation is always performed in the direction the earth is rotating
+(counterclockwise as viewed from the north pole towards the center of
+the earth). This rotation optionally can be switched off.
+.SH OPTIONS
+.I platonicfolding
+accepts the following options:
+.TP 8
+.B \-\-window
+Draw on a newly-created window. This is the default.
+.TP 8
+.B \-\-root
+Draw on the root window.
+.TP 8
+.B \-\-window\-id \fInumber\fP
+Draw on the specified window.
+.TP 8
+.B \-\-install
+Install a private colormap for the window.
+.TP 8
+.B \-\-visual \fIvisual\fP
+Specify which visual to use. Legal values are the name of a visual
+class, or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-\-delay \fImicroseconds\fP
+How much of a delay should be introduced between steps of the
+animation. Default 25000, or 1/40th second.
+.PP
+The following options determine whether the Platonic solid is being
+rotated.
+.TP 8
+.B \-\-rotate
+Rotate the Platonic solid (default).
+.TP 8
+.B \-\-no-rotate
+Do not rotate the Platonic solid.
+.PP
+The following three options are mutually exclusive. They determine
+how to color the Platonic solid.
+.TP 8
+.B \-\-colors random
+Display the Platonic solid with a random color scheme (default).
+.TP 8
+.B \-\-colors face \fP(Shortcut: \fB\-\-face-colors\fP)
+Display the Platonic solid with different colors for each face. The
+colors of the faces are identical on the inside and outside of the
+Platonic solid.
+.TP 8
+.B \-\-colors earth \fP(Shortcut: \fB\-\-earth-colors\fP)
+Display the Platonic solid with a texture of earth as illuminated by
+the sun at the time the program is run.
+.PP
+The following option determines how many unfoldings and foldings to
+perform per cycle.
+.TP 8
+.B \-\-foldings random
+Use a random number of unfoldings and foldings per cycle (default).
+.TP 8
+.B \-\-foldings \fIint\fP
+If an integer number is specified, it is clipped to the range 1...20
+and the clipped number is used as the number of unfoldings and
+foldings per cycle.
+.SH INTERACTION
+If you run this program in standalone mode, you can rotate the
+Platonic solid by dragging the mouse while pressing the left mouse
+button.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.TP 8
+.B XSCREENSAVER_WINDOW
+The window ID to use with \fI\-\-root\fP.
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1),
+.SH FURTHER INFORMATION
+Takashi Horiyama, Wataru Shoji: Edge Unfoldings of Platonic Solids
+Never Overlap. In: 23rd Canadian Conference on Computational
+Geometry, 2011.
+.PP
+Takashi Horiyama, Wataru Shoji: The Number of Different Unfoldings of
+Polyhedra. In: 24th International Symposium on Algorithms and
+Computation, pp. 623-633, 2013.
+.PP
+Taiping Zhang, Paul W. Stackhouse Jr., Bradley Macpherson, J. Colleen
+Mikovitz: A solar azimuth formula that renders circumstantial
+treatment unnecessary without compromising mathematical rigor:
+Mathematical setup, application and extension of a formula based on
+the subsolar point and atan2 function. Renewable Energy
+172:1333-1340, 2021.
+.SH COPYRIGHT
+Copyright \(co 2025 by Carsten Steger. Permission to use, copy,
+modify, distribute, and sell this software and its documentation for
+any purpose is hereby granted without fee, provided that the above
+copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation.
+No representations are made about the suitability of this software for
+any purpose. It is provided "as is" without express or implied
+warranty.
+.SH AUTHOR
+Carsten Steger <carsten@mirsanmir.org>, 18-mar-2025.
Malloc (result, last_uniform * 2 + 3, polyhedron*);
while (index < last_uniform) {
- char sym[4];
+ char sym[20];
Polyhedron *P;
sprintf(sym, "#%d", index + 1);
-/* sonar, Copyright © 1998-2021 Jamie Zawinski and Stephen Martin
+/* sonar, Copyright © 1998-2025 Jamie Zawinski and Stephen Martin
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#endif
{
char *str_error = strerror(errno);
- fprintf(stderr, "%s: %s: %s", progname, filename, str_error);
+ fprintf(stderr, "%s: %s: %s\n", progname, filename, str_error);
}
return 0;
}
}
+/* Reorder the host list randomly.
+ */
+static
+sonar_bogie *
+shuffle_list (sonar_bogie *list)
+{
+ int count, i;
+ sonar_bogie **a, *n;
+ if (!list) return list;
+
+ /* Convert the linked list to an array. */
+ for (n = list, count = 0; n; n = n->next)
+ count++;
+ a = (void *) malloc (count * sizeof(*a));
+ for (n = list, i = 0; n; n = n->next)
+ a[i++] = n;
+
+ /* Fisher–Yates shuffle. */
+ for (i = count-1; i > 0; i--)
+ {
+ int j = random() % i;
+ void *s = a[i];
+ a[i] = a[j];
+ a[j] = s;
+ }
+
+ /* Back to a list. */
+ a[0]->next = NULL;
+ for (i = 1; i < count; i++)
+ a[i]->next = a[i-1];
+ list = a[count-1];
+ free (a);
+
+ return list;
+}
+
+
/* Returns a list of hosts to ping based on the "-ping" argument.
*/
static sonar_bogie *
char *source, *token, *end, dummy;
sonar_bogie *hostlist = 0;
const char *fallback = "subnet";
+ Bool shuffle_p = False;
AGAIN:
{
# ifdef READ_FILES
new = read_hosts_file (ssd, token);
+ shuffle_p = True;
# else
if (pd->debug_p) fprintf (stderr, "%s: skipping file\n", progname);
# endif
else if (!stat (token, &st))
{
new = read_hosts_file (ssd, token);
+ shuffle_p = True;
}
# endif /* READ_FILES */
else
/* not an existant file - must be a host name
*/
new = bogie_for_host (ssd, token, NULL);
+ shuffle_p = True;
}
if (new)
progname, fallback);
ping_arg = fallback;
fallback = 0;
+ shuffle_p = False;
goto AGAIN;
}
+ if (shuffle_p)
+ hostlist = shuffle_list (hostlist);
+
return hostlist;
}
/* Disavow privs */
if (setuid(getuid()) == -1) abort();
+
+ /* Feb 2025: On macOS 14.7, sometimes sendto() gives us a free SIGPIPE,
+ as a treat.
+ */
+ {
+# ifdef HAVE_SIGACTION
+ struct sigaction a;
+ a.sa_handler = SIG_IGN;
+ sigemptyset (&a.sa_mask);
+ a.sa_flags = 0;
+ if (sigaction (SIGPIPE, &a, 0) < 0)
+# else /* !HAVE_SIGACTION */
+ if (((long) signal (SIGPIPE, SIG_IGN)) == -1L)
+# endif /* !HAVE_SIGACTION */
+ {
+ char buf [255];
+ sprintf (buf, "%s: couldn't block SIGPIPE", progname);
+ perror (buf);
+ /* abort(); */
+ }
+ }
+
pd->pid = getpid() & 0xFFFF;
pd->seq = 0;
pd->timeout = timeout;
/* xlock-gl.c --- xscreensaver compatibility layer for xlockmore GL modules.
- * xscreensaver, Copyright © 1997-2024 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 1997-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#endif /* !HAVE_EGL */
-#undef glEnable
+#ifndef HAVE_JWZGLES
+# undef glEnable
void (* glEnable_fn) (GLuint) = glEnable;
-#if defined(__linux__) && (defined(__arm__) || defined(__ARM_ARCH))
-# define PI_LIKE /* Raspberry Pi-adjacent */
+# if defined(__linux__) && (defined(__arm__) || defined(__ARM_ARCH))
+# define PI_LIKE /* Raspberry Pi-adjacent */
static void
glEnable_bad_line_smooth (GLuint cap)
{
if (cap != GL_LINE_SMOOTH)
glEnable (cap);
}
-#endif
+# endif /* PI_LIKE */
+#endif /* HAVE_JWZGLES */
GLXContext *
/* This is re-used, no need to close it. */
d->egl_display = eglGetPlatformDisplay (EGL_PLATFORM_X11_KHR,
- (EGLNativeDisplayType) dpy, NULL);
+ (void *) dpy, NULL);
if (!d->egl_display)
{
fprintf (stderr, "%s: eglGetPlatformDisplay failed\n", progname);
--- /dev/null
+# Attributions of images for the klondike screensaver
+
+The card images in this file are derived from the following sources:
+
+## Card Fronts
+https://commons.wikimedia.org/wiki/Category:SVG_English_pattern_playing_cards
+Author: Дмитрий Фомин (Dmitry Fomin)
+License: CC0 (public domain)
+
+## Card Back
+https://commons.wikimedia.org/wiki/File:Reverso_baraja_española_rojo.svg
+Author: Germarquezm
+License: Creative Commons Attribution-Share Alike 3.0 Unported
+
+## Modifications
+The png files in this folder were made from the original SVG files by rendering the SVG to PNG with Inkscape, then padded with a drop shadow using ImageMagick.
+
+
-/* xscreensaver, Copyright © 1999-2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1999-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
*
* Phosphor -- simulate a glass tty with long-sustain phosphor.
* Written by Jamie Zawinski <jwz@jwz.org>
- * Pty and vt100 emulation by Fredrik Tolf <fredrik@dolda2000.com>
*/
#include "screenhack.h"
#include "textclient.h"
+#include "ansi-tty.h"
#include "ximage-loader.h"
#include "utf8wc.h"
#define FADE 3
#define STATE_MAX FADE
-#define CURSOR_INDEX 128
-
#define NPAR 16
+#undef USE_XFT_BITMAP /* Not working reliably */
#define BUILTIN_FONT
#ifdef BUILTIN_FONT
# include "images/gen/6x10font_png.h"
#endif /* BUILTIN_FONT */
+
typedef struct {
- unsigned char name;
+ unsigned long name; /* Unicode character */
int width, height;
Pixmap pixmap;
#ifdef FUZZY_BORDER
Pixmap pixmap2;
+ Pixmap cache;
#endif /* FUZZY_BORDER */
Bool blank_p;
} p_char;
typedef struct {
- p_char *p_char;
+ unsigned char c;
int state;
Bool changed;
+ Bool invert_p;
+ Bool symbol_p;
} p_cell;
typedef struct {
int ticks;
int mode;
- int escstate;
- int csiparam[NPAR];
- int curparam;
- int unicruds; unsigned char unicrud[7];
+ p_char *chars[256];
+ p_char *ichars[256];
+ p_char *schars[256];
+ p_char *sichars[256];
- p_char **chars;
p_cell *cells;
XGCValues gcv;
GC gc0;
GC gc2;
#endif /* FUZZY_BORDER */
GC *gcs;
- XImage *font_bits;
+ XImage *font_bits, *sym_font_bits;
int cursor_x, cursor_y;
+ Bool cursor_on;
XtIntervalId cursor_timer;
Time cursor_blink;
int delay;
- Bool pty_p;
text_data *tc;
-
- char last_c;
- int bk;
+ ansi_tty *tty;
} p_state;
-static void capture_font_bits (p_state *state);
-static p_char *make_character (p_state *state, int c);
-static void char_to_pixmap (p_state *state, p_char *pc, int c);
+static void capture_font_bits (p_state *state, Bool symbol_p);
+static void char_to_pixmap (p_state *state, p_char *pc, unsigned long c,
+ Bool invert_p, Bool symbol_p);
/* About font metrics:
*/
-static void clear (p_state *);
static void set_cursor (p_state *, Bool on);
static unsigned short scale_color_channel (unsigned short ch1, unsigned short ch2)
#define FONT6x10_WIDTH (256*7)
#define FONT6x10_HEIGHT 10
+static void phosphor_tty_send (void *, const char *);
+
static void *
phosphor_init (Display *dpy, Window window)
{
/* XSelectInput (dpy, window, state->xgwa.your_event_mask | ExposureMask);*/
state->delay = get_integer_resource (dpy, "delay", "Integer");
- state->pty_p = get_boolean_resource (dpy, "usePty", "UsePty");
+
+# ifndef HAVE_XFT /* Custom fonts only work under real Xft. */
+ free (fontname);
+ fontname = strdup ("builtin");
+# ifndef BUILTIN_FONT
+# error BUILTIN_FONT is required unless HAVE_XFT.
+# endif
+# endif /* HAVE_XFT */
if (!strcasecmp (fontname, "builtin") ||
!strcasecmp (fontname, "(builtin)"))
{
-#ifndef BUILTIN_FONT
+# ifndef BUILTIN_FONT
fprintf (stderr, "%s: no builtin font\n", progname);
state->font = load_xft_font_retry (dpy,
screen_number (state->xgwa.screen),
"fixed");
-#endif /* !BUILTIN_FONT */
+# endif /* !BUILTIN_FONT */
}
else
{
/* Xft uses 'scale' */
state->scale = get_integer_resource (dpy, "phosphorScale", "Integer");
state->ticks = STATE_MAX + get_integer_resource (dpy, "ticks", "Integer");
- state->escstate = 0;
if (state->scale <= 0) state->scale = 1;
if (state->ticks <= 0) state->ticks = 1;
state->xmargin = 96;
state->ymargin = state->xmargin;
}
-# endif
+# endif /* HAVE_IPHONE */
state->grid_width = ((state->xgwa.width - state->xmargin * 2) /
(state->char_width * state->scale));
(state->char_height * state->scale));
state->cells = (p_cell *) calloc (sizeof(p_cell),
state->grid_width * state->grid_height);
- state->chars = (p_char **) calloc (sizeof(p_char *), 256);
state->gcs = (GC *) calloc (sizeof(GC), state->ticks + 1);
/* Now, GCs all around.
*/
state->gcv.cap_style = CapRound;
-#ifdef FUZZY_BORDER
+# ifdef FUZZY_BORDER
state->gcv.line_width = (int) (((long) state->scale) * 1.3);
if (state->gcv.line_width == state->scale)
state->gcv.line_width++;
-#else /* !FUZZY_BORDER */
+# else /* !FUZZY_BORDER */
state->gcv.line_width = (int) (((long) state->scale) * 0.9);
if (state->gcv.line_width >= state->scale)
state->gcv.line_width = state->scale - 1;
if (state->gcv.line_width < 1)
state->gcv.line_width = 1;
-#endif /* !FUZZY_BORDER */
+# endif /* !FUZZY_BORDER */
flags = (GCForeground | GCBackground | GCCapStyle | GCLineWidth);
free (colors);
}
- capture_font_bits (state);
+ capture_font_bits (state, False);
+ capture_font_bits (state, True);
- set_cursor (state, True);
+ state->tty = ansi_tty_init (state->grid_width, state->grid_height);
+ state->tty->closure = state;
+ state->tty->tty_send = phosphor_tty_send;
-/* clear (state);*/
+ set_cursor (state, True);
state->tc = textclient_open (dpy);
textclient_reshape (state->tc,
state->xgwa.width - state->xmargin * 2,
state->xgwa.height - state->ymargin * 2,
- state->grid_width - 1,
- state->grid_height - 1,
+ state->grid_width,
+ state->grid_height,
0);
return state;
}
+static p_char *
+make_character (p_state *state, unsigned char c, Bool invert_p, Bool symbol_p)
+{
+ p_char *pc = (p_char *) malloc (sizeof (*pc));
+ pc->name = (c + (symbol_p ? 256 : 0)) * (invert_p ? -1 : 1);
+ pc->width = state->scale * state->char_width;
+ pc->height = state->scale * state->char_height;
+ char_to_pixmap (state, pc, c, invert_p, symbol_p);
+ return pc;
+}
+
+
static void
-capture_font_bits (p_state *state)
+capture_font_bits (p_state *state, Bool symbol_p)
{
XftFont *font = state->font;
int safe_width, height;
if (pix_w != FONT6x10_WIDTH) abort();
if (pix_h != FONT6x10_HEIGHT) abort();
+ if (symbol_p)
+ {
+ /* "6x10font.png" has the DEC Special Graphics characters down in
+ the control area, so we just need to shift them into place. */
+ int cw = FONT6x10_WIDTH / 256;
+ int from = cw * 0x01;
+ int to = cw * 0x60;
+ int len = cw * 31;
+ for (y = 0; y < pix_h; y++)
+ for (x = to; x < to + len; x++)
+ XPutPixel (mm, x, y, XGetPixel (mm, x - (to - from), y));
+ }
+
XGetWindowAttributes (state->dpy, state->window, &xgwa);
im2 = XCreateImage (state->dpy, xgwa.visual, 1, XYBitmap, 0, 0,
pix_w, pix_h, 8, 0);
XftTextExtentsUtf8 (state->dpy, state->font,
(FcChar8 *) "N", 1, &overall);
/* #### maybe safe_width should take lbearing into account */
- safe_width = overall.xOff;
+ safe_width = overall.xOff + 1;
state->char_height = state->font->ascent + state->font->descent;
height = state->char_height;
}
GCCapStyle | GCLineWidth),
&state->gcv);
-#ifdef HAVE_JWXYZ
+# ifdef HAVE_JWXYZ
jwxyz_XSetAntiAliasing (state->dpy, state->gc0, False);
jwxyz_XSetAntiAliasing (state->dpy, state->gc1, False);
-#endif
+# endif
-#ifdef FUZZY_BORDER
+# ifdef FUZZY_BORDER
{
state->gcv.line_width = (int) (((long) state->scale) * 0.8);
if (state->gcv.line_width >= state->scale)
GCCapStyle | GCLineWidth),
&state->gcv);
}
-#endif /* FUZZY_BORDER */
+# endif /* FUZZY_BORDER */
XFillRectangle (state->dpy, p, state->gc0, 0, 0, (safe_width * 256), height);
else
# endif /* BUILTIN_FONT */
{
+# ifdef USE_XFT_BITMAP
+ Pixmap pm_mono = XCreatePixmap (state->dpy, state->window,
+ (safe_width * 256), height,
+ 1);
+# else /* !USE_XFT_BITMAP */
Pixmap pm_color = XCreatePixmap (state->dpy, state->window,
(safe_width * 256), height,
state->xgwa.depth);
- XImage *xim_color, *xim_mono;
+ XImage *xim_color;
+ int x, y;
+# endif /* !USE_XFT_BITMAP */
+ XImage *xim_mono;
GC text_gc;
XGCValues gcv;
XftDraw *xftdraw;
XftColor xft_fg;
- int x, y;
- /* Maybe this should be using XftDrawCreateBitmap instead of
- XftDrawCreate to render the font with 1-bit hinting? */
+# ifdef USE_XFT_BITMAP
+ /* Create a 1-bit pixmap and draw the text into it, fg/bg 1/0. */
+
+ /* When using XftDrawCreateBitmap, XftDrawStringUtf8 always draws
+ fg = 0 regardless of what is in the XftColor, so the background
+ must be 1. */
+
+ /* This is working (and looks better) on macOS X11, but no characters
+ are showing up at all on "real" X11 on a Raspberry Pi. Sigh.
+ It also doesn't work under Cocoa with fake-Xft. */
+
+ gcv.foreground = 1;
+ xft_fg.pixel = 0;
+ xft_fg.color.red = xft_fg.color.green = xft_fg.color.blue = ~0L;
+
+ text_gc = XCreateGC (state->dpy, pm_mono, GCForeground, &gcv);
+ XFillRectangle (state->dpy, pm_mono, text_gc, 0, 0,
+ (safe_width * 256), height);
+ xftdraw = XftDrawCreateBitmap (state->dpy, pm_mono);
+
+# else /* !USE_XFT_BITMAP */
/* Create a full-depth pixmap and draw the text into it, fg/bg ~0/0. */
- gcv.foreground = 0;
+
+ gcv.foreground =
+ BlackPixelOfScreen (DefaultScreenOfDisplay (state->dpy));
text_gc = XCreateGC (state->dpy, pm_color, GCForeground, &gcv);
XFillRectangle (state->dpy, pm_color, text_gc, 0, 0,
(safe_width * 256), height);
state->xgwa.visual, state->xgwa.colormap);
xft_fg.pixel = ~0L;
xft_fg.color.red = xft_fg.color.green = xft_fg.color.blue = ~0L;
+
+# endif /* !USE_XFT_BITMAP */
+
for (i = 0; i < 256; i++)
- XftDrawStringUtf8 (xftdraw, &xft_fg, state->font,
- i * safe_width, font->ascent,
- (FcChar8 *) (string + i), 1);
+ if (!symbol_p)
+ XftDrawStringUtf8 (xftdraw, &xft_fg, state->font,
+ i * safe_width, font->ascent,
+ (FcChar8 *) (string + i), 1);
+ else
+ {
+ unsigned long uc = ansi_graphics_unicode[i];
+ char ss[10];
+ int L = utf8_encode (uc, ss, sizeof(ss)-1);
+ XftDrawStringUtf8 (xftdraw, &xft_fg, state->font,
+ i * safe_width, font->ascent,
+ (FcChar8 *) ss, L);
+ }
+
+# ifdef USE_XFT_BITMAP
+ /* Retrieve a 1-bit XImage. */
+ xim_mono = XGetImage (state->dpy, pm_mono, 0, 0,
+ i * safe_width, font->ascent,
+ 1, XYPixmap);
+ if (! xim_mono) abort();
+ {
+ int i; /* Invert */
+ for (i = 0; i < xim_mono->width * xim_mono->height / 8; i++)
+ xim_mono->data[i] = ~xim_mono->data[i];
+ }
+
+# else /* !USE_XFT_BITMAP */
/* Retrieve a full-depth XImage. */
xim_color = XGetImage (state->dpy, pm_color, 0, 0,
i * safe_width, font->ascent,
- state->xgwa.depth, ZPixmap);
+ ~0L, ZPixmap);
/* Convert it to a mono XImage. */
xim_mono = XCreateImage (state->dpy, state->xgwa.visual,
for (y = 0; y < xim_color->height; y++)
for (x = 0; x < xim_color->width; x++)
XPutPixel (xim_mono, x, y, XGetPixel (xim_color, x, y) ? 1 : 0);
+ XDestroyImage (xim_color);
+ xim_color = 0;
+# endif /* !USE_XFT_BITMAP */
/* Copy mono ximage to mono pixmap */
XFreeGC (state->dpy, text_gc);
- text_gc = XCreateGC (state->dpy, p, GCForeground, &gcv);
+ gcv.foreground = 1;
+ gcv.background = 0;
+ text_gc = XCreateGC (state->dpy, p, GCForeground|GCBackground, &gcv);
XPutImage (state->dpy, p, text_gc, xim_mono, 0, 0, 0, 0,
(safe_width * 256), height);
XFreeGC (state->dpy, text_gc);
- XDestroyImage (xim_color);
XDestroyImage (xim_mono);
+ }
+
# if 0
- XWriteBitmapFile(state->dpy, "/tmp/tvfont.xbm", p,
- (safe_width * 256), height,
- -1, -1);
+ XWriteBitmapFile(state->dpy,
+ (symbol_p ? "/tmp/tvfont2.xbm" : "/tmp/tvfont.xbm"),
+ p,
+ (safe_width * 256), height,
+ -1, -1);
# endif
- }
- /* Draw the cursor. */
- XFillRectangle (state->dpy, p, state->gc1,
- (CURSOR_INDEX * safe_width), 1,
- state->char_width,
- state->char_height);
+ {
+ XImage *im = XGetImage (state->dpy, p, 0, 0,
+ (safe_width * 256), height, ~0L, XYPixmap);
+ if (symbol_p)
+ state->sym_font_bits = im;
+ else
+ state->font_bits = im;
+ }
- state->font_bits = XGetImage (state->dpy, p, 0, 0,
- (safe_width * 256), height, ~0L, XYPixmap);
XFreePixmap (state->dpy, p);
- for (i = 0; i < 256; i++)
- state->chars[i] = make_character (state, i);
-}
-
-
-static p_char *
-make_character (p_state *state, int c)
-{
- p_char *pc = (p_char *) malloc (sizeof (*pc));
- pc->name = (unsigned char) c;
- pc->width = state->scale * state->char_width;
- pc->height = state->scale * state->char_height;
- char_to_pixmap (state, pc, c);
- return pc;
+ for (i = 0; i < countof(state->chars); i++)
+ {
+ p_char *c1 = make_character (state, i, False, symbol_p);
+ p_char *c2 = make_character (state, i, True, symbol_p);
+ if (symbol_p)
+ {
+ state->schars [i] = c1;
+ state->sichars[i] = c2;
+ }
+ else
+ {
+ state->chars [i] = c1;
+ state->ichars[i] = c2;
+ }
+ }
}
static void
-char_to_pixmap (p_state *state, p_char *pc, int c)
+char_to_pixmap (p_state *state, p_char *pc, unsigned long c,
+ Bool invert_p, Bool symbol_p)
{
Pixmap p = 0;
GC gc;
-#ifdef FUZZY_BORDER
+# ifdef FUZZY_BORDER
Pixmap p2 = 0;
GC gc2;
-#endif /* FUZZY_BORDER */
+# endif /* FUZZY_BORDER */
int from, to;
int x1, y;
+ XImage *font_bits = (symbol_p ? state->sym_font_bits : state->font_bits);
int safe_width = state->char_width + 1;
gc = state->gc1;
p = XCreatePixmap (state->dpy, state->window, width, height, 1);
XFillRectangle (state->dpy, p, state->gc0, 0, 0, width, height);
-#ifdef FUZZY_BORDER
+# ifdef FUZZY_BORDER
gc2 = state->gc2;
p2 = XCreatePixmap (state->dpy, state->window, width, height, 1);
XFillRectangle (state->dpy, p2, state->gc0, 0, 0, width, height);
-#endif /* FUZZY_BORDER */
+# endif /* FUZZY_BORDER */
from = safe_width * c;
to = safe_width * (c + 1);
-#if 0
+# if 0
if (c > 75 && c < 150)
{
- printf ("\n=========== %d (%c)\n", c, c);
+ printf ("\n=========== %lu (%c)\n", c, (char) c);
for (y = 0; y < state->char_height; y++)
{
for (x1 = from; x1 < to; x1++)
- printf (XGetPixel (state->font_bits, x1, y) ? "* " : ". ");
+ printf (XGetPixel (font_bits, x1, y) ? "* " : ". ");
printf ("\n");
}
}
-#endif
+# endif
- pc->blank_p = True;
+ pc->blank_p = !invert_p;
for (y = 0; y < state->char_height; y++)
for (x1 = from; x1 < to; x1++)
- if (XGetPixel (state->font_bits, x1, y))
- {
- int xoff = state->scale / 2;
- int x2;
- for (x2 = x1; x2 < to; x2++)
- if (!XGetPixel (state->font_bits, x2, y))
- break;
- x2--;
- XDrawLine (state->dpy, p, gc,
- (x1 - from) * state->scale + xoff, y * state->scale,
- (x2 - from) * state->scale + xoff, y * state->scale);
-#ifdef FUZZY_BORDER
- XDrawLine (state->dpy, p2, gc2,
- (x1 - from) * state->scale + xoff, y * state->scale,
- (x2 - from) * state->scale + xoff, y * state->scale);
-#endif /* FUZZY_BORDER */
- x1 = x2;
- pc->blank_p = False;
- }
-
- /* if (pc->blank_p && c == CURSOR_INDEX)
- abort();*/
+ {
+ unsigned long pix = XGetPixel (font_bits, x1, y);
+ if (invert_p) pix = !pix;
+ if (pix)
+ {
+ int xoff = state->scale / 2;
+ int x2;
+ for (x2 = x1; x2 < to; x2++)
+ {
+ pix = XGetPixel (font_bits, x2, y);
+ if (invert_p) pix = !pix;
+ if (!pix)
+ break;
+ }
+ x2--;
+ XDrawLine (state->dpy, p, gc,
+ (x1 - from) * state->scale + xoff, y * state->scale,
+ (x2 - from) * state->scale + xoff, y * state->scale);
+# ifdef FUZZY_BORDER
+ XDrawLine (state->dpy, p2, gc2,
+ (x1 - from) * state->scale + xoff, y * state->scale,
+ (x2 - from) * state->scale + xoff, y * state->scale);
+# endif /* FUZZY_BORDER */
+ x1 = x2;
+ pc->blank_p = False;
+ }
+ }
pc->pixmap = p;
-#ifdef FUZZY_BORDER
+# ifdef FUZZY_BORDER
pc->pixmap2 = p2;
-#endif /* FUZZY_BORDER */
+# endif /* FUZZY_BORDER */
+
+# ifdef FUZZY_BORDER
+ pc->cache = 0;
+ if (get_boolean_resource (state->dpy, "cache", "Cache"))
+ {
+ /* Cache every possible frame of this character's fade in a pixmap,
+ because XSetClipMask has become *incredibly* fucking slow on
+ "modern" X11 systems (e.g. Raspbian 11.) I blame compositors. */
+
+ int st;
+ pc->cache = XCreatePixmap (state->dpy, state->window,
+ width * state->ticks, height,
+ state->xgwa.depth);
+ for (st = 0; st < state->ticks; st++)
+ {
+ int tx = st * width;
+ int ty = 0;
+ GC gc1 = state->gcs[st];
+ GC gc2 = ((st + 2) < state->ticks
+ ? state->gcs[st + 2]
+ : 0);
+ GC gc3 = (gc2 ? gc2 : gc1);
+ if (gc3)
+ XCopyPlane (state->dpy, pc->pixmap, pc->cache, gc3,
+ 0, 0, width, height, tx, ty, 1L);
+ if (gc2)
+ {
+ XSetClipMask (state->dpy, gc1, pc->pixmap2);
+ XSetClipOrigin (state->dpy, gc1, tx, ty);
+ XFillRectangle (state->dpy, pc->cache, gc1,
+ tx, ty, width, height);
+ XSetClipMask (state->dpy, gc1, None);
+ }
+ }
+ }
+# endif /* FUZZY_BORDER */
}
-\f
+
/* Managing the display.
*/
static Bool
set_cursor_1 (p_state *state, Bool on)
{
- p_cell *cell = &state->cells[state->grid_width * state->cursor_y
- + state->cursor_x];
- p_char *cursor = state->chars[CURSOR_INDEX];
- int new_state = (on ? NORMAL : FADE);
-
- if (cell->p_char != cursor)
- cell->changed = True;
-
- if (cell->state != new_state)
- cell->changed = True;
-
- cell->p_char = cursor;
- cell->state = new_state;
- return cell->changed;
+ p_cell *cell = &state->cells[state->grid_width * state->cursor_y +
+ state->cursor_x];
+ if (state->cursor_on == on)
+ return False;
+ state->cursor_on = on;
+ cell->changed = True;
+ return True;
}
static void
}
-static void
-clear (p_state *state)
-{
- int x, y;
- state->cursor_x = 0;
- state->cursor_y = 0;
- for (y = 0; y < state->grid_height; y++)
- for (x = 0; x < state->grid_width; x++)
- {
- p_cell *cell = &state->cells[state->grid_width * y + x];
- if (cell->state == FLARE || cell->state == NORMAL)
- {
- cell->state = FADE;
- cell->changed = True;
- }
- }
- set_cursor (state, True);
-}
-
-
static void
decay (p_state *state)
{
{
cell->state++;
if (cell->state >= state->ticks)
- cell->state = BLANK;
+ {
+ cell->state = BLANK;
+ cell->c = ' ';
+ }
cell->changed = True;
}
}
static void
-scroll (p_state *state)
+print_char (p_state *state, int c)
{
int x, y;
+ ansi_tty *tty = state->tty;
+ ansi_tty_print (tty, c);
- for (x = 0; x < state->grid_width; x++)
- {
- p_cell *from = 0, *to = 0;
- for (y = 1; y < state->grid_height; y++)
- {
- from = &state->cells[state->grid_width * y + x];
- to = &state->cells[state->grid_width * (y-1) + x];
+ for (y = 0; y < tty->height; y++)
+ for (x = 0; x < tty->width; x++)
+ {
+ tty_char *tcell = &tty->grid [tty->width * y + x];
+ p_cell *cell = &state->cells [state->grid_width * y + x];
+ Bool inv_p = tcell->flags & (TTY_BOLD | TTY_ITALIC | TTY_INVERSE);
+ Bool sym_p = tcell->flags & (TTY_SYMBOLS);
+ unsigned char latin1 = 0;
+ p_char *pc;
- if ((from->state == FLARE || from->state == NORMAL) &&
- !from->p_char->blank_p)
- {
- *to = *from;
- to->state = NORMAL; /* should be FLARE? Looks bad... */
- }
- else
- {
- if (to->state == FLARE || to->state == NORMAL)
- to->state = FADE;
- }
+ if (tty->inverse_p) inv_p = !inv_p;
- to->changed = True;
- }
+ if ( cell->c == 0) cell->c = ' ';
+ if (tcell->c == 0) tcell->c = ' ';
- to = from;
- if (to && (to->state == FLARE || to->state == NORMAL))
- {
- to->state = FADE;
- to->changed = True;
- }
- }
- set_cursor (state, True);
-}
+ if (tcell->c <= 128)
+ latin1 = tcell->c;
+ else
+ {
+ /* Convert non-ASCII Unicode to closest Latin1. */
+ char utf8[10];
+ if (utf8_encode (tcell->c, utf8, sizeof(utf8)-1))
+ {
+ char *s = utf8_to_latin1 (utf8, FALSE);
+ if (s)
+ {
+ latin1 = s[0];
+ free (s);
+ }
+ }
+ }
+ if (!latin1) latin1 = ' ';
-static int
-process_unicrud (p_state *state, int c)
-{
- if ((c & 0xE0) == 0xC0) { /* 110xxxxx: 11 bits, 2 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 102;
- } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx: 16 bits, 3 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 103;
- } else if ((c & 0xF8) == 0xF0) { /* 11110xxx: 21 bits, 4 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 104;
- } else if ((c & 0xFC) == 0xF8) { /* 111110xx: 26 bits, 5 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 105;
- } else if ((c & 0xFE) == 0xFC) { /* 1111110x: 31 bits, 6 bytes */
- state->unicruds = 1;
- state->unicrud[0] = c;
- state->escstate = 106;
- } else if (state->unicruds == 0) {
- return c;
- } else {
- int total = state->escstate - 100; /* see what I did there */
- if (state->unicruds < total) {
- /* Buffer more bytes of the UTF-8 sequence */
- state->unicrud[state->unicruds++] = c;
- }
+ pc = (cell->symbol_p
+ ? (inv_p ? state->sichars[cell->c] : state->schars[cell->c])
+ : (inv_p ? state->ichars[cell->c] : state->chars[cell->c]));
- if (state->unicruds >= total) {
- /* Done! Convert it to Latin1 and print that. */
- char *s;
- state->unicrud[state->unicruds] = 0;
- s = utf8_to_latin1 ((const char *) state->unicrud, False);
- state->unicruds = 0;
- state->escstate = 0;
- if (s) {
- c = (unsigned char) s[0];
- free (s);
- return c;
+ if (!pc->blank_p &&
+ latin1 == ' ' &&
+ !inv_p)
+ {
+ /* Replacing existing character with a blank:
+ fade out the previous character. */
+ if (cell->state == FLARE || cell->state == NORMAL)
+ cell->state = FADE;
+ cell->changed = True;
+ }
+ else if (cell->c != tcell->c ||
+ cell->state >= FADE ||
+ cell->invert_p != inv_p ||
+ cell->symbol_p != sym_p)
+ {
+ /* Adding a new character: flare it in. */
+ cell->invert_p = inv_p;
+ cell->symbol_p = sym_p;
+ /* cell->state = FLARE; -- Looks bad when scrolling */
+ cell->state = NORMAL;
+ cell->changed = True;
+ cell->c = latin1;
+ }
}
- }
- }
- return 0;
-}
-
-static void
-print_char (p_state *state, int c)
-{
- int cols = state->grid_width;
- int rows = state->grid_height;
- p_cell *cell = &state->cells[state->grid_width * state->cursor_y
- + state->cursor_x];
-
- /* Start the cursor fading (in case we don't end up overwriting it.) */
- if (cell->state == FLARE || cell->state == NORMAL)
- {
- cell->state = FADE;
- cell->changed = True;
- }
-
-#ifdef HAVE_FORKPTY
- if (state->pty_p) /* Only interpret VT100 sequences if running in pty-mode.
- It would be nice if we could just interpret them all
- the time, but that would require subprocesses to send
- CRLF line endings instead of bare LF, so that's no good.
- */
- {
- int i;
- int start, end;
-
- /* Mostly duplicated in apple2-main.c */
-
- switch (state->escstate)
- {
- case 0:
- switch (c)
- {
- case 7: /* BEL */
- /* Dummy case - we don't want the screensaver to beep */
- /* #### But maybe this should flash the screen? */
- break;
- case 8: /* BS */
- if (state->cursor_x > 0)
- state->cursor_x--;
- break;
- case 9: /* HT */
- if (state->cursor_x < cols - 8)
- {
- state->cursor_x = (state->cursor_x & ~7) + 8;
- }
- else
- {
- state->cursor_x = 0;
- if (state->cursor_y < rows - 1)
- state->cursor_y++;
- else
- scroll (state);
- }
- break;
- case 10: /* LF */
-# ifndef HAVE_FORKPTY
- state->cursor_x = 0; /* No ptys on iPhone; assume CRLF. */
-# endif
- case 11: /* VT */
- case 12: /* FF */
- if(state->last_c == 13)
- {
- cell->state = NORMAL;
- cell->p_char = state->chars[state->bk];
- cell->changed = True;
- }
- if (state->cursor_y < rows - 1)
- state->cursor_y++;
- else
- scroll (state);
- break;
- case 13: /* CR */
- state->cursor_x = 0;
- cell = &state->cells[cols * state->cursor_y];
- if((cell->p_char == NULL) || (cell->p_char->name == CURSOR_INDEX))
- state->bk = ' ';
- else
- state->bk = cell->p_char->name;
- break;
- case 14: /* SO */
- case 15: /* SI */
- /* Dummy case - there is one and only one font. */
- break;
- case 24: /* CAN */
- case 26: /* SUB */
- /* Dummy case - these interrupt escape sequences, so
- they don't do anything in this state */
- break;
- case 27: /* ESC */
- state->escstate = 1;
- break;
- case 127: /* DEL */
- /* Dummy case - this is supposed to be ignored */
- break;
- case 155: /* CSI */
- state->escstate = 2;
- for(i = 0; i < NPAR; i++)
- state->csiparam[i] = 0;
- state->curparam = 0;
- break;
- default:
-
- PRINT: /* Come from states 102-106 */
- c = process_unicrud (state, c);
- if (! c)
- break;
-
- /* If the cursor is in column 39 and we print a character, then
- that character shows up in column 39, and the cursor is no
- longer visible on the screen (it's in "column 40".) If
- another character is printed, then that character shows up in
- column 0, and the cursor moves to column 1.
-
- This is empirically what xterm and gnome-terminal do, so that
- must be the right thing. (In xterm, the cursor vanishes,
- whereas; in gnome-terminal, the cursor overprints the
- character in col 39.)
- */
- cell->state = FLARE;
- cell->p_char = state->chars[c];
- cell->changed = True;
- state->cursor_x++;
-
- if (c != ' ' && cell->p_char->blank_p)
- cell->p_char = state->chars[CURSOR_INDEX];
-
- if (state->cursor_x >= cols - 1 /*####*/)
- {
- state->cursor_x = 0;
- if (state->cursor_y >= rows - 1)
- scroll (state);
- else
- state->cursor_y++;
- }
- break;
- }
- break;
- case 1:
- switch (c)
- {
- case 24: /* CAN */
- case 26: /* SUB */
- state->escstate = 0;
- break;
- case 'c': /* Reset */
- clear (state);
- state->escstate = 0;
- break;
- case 'D': /* Linefeed */
- if (state->cursor_y < rows - 1)
- state->cursor_y++;
- else
- scroll (state);
- state->escstate = 0;
- break;
- case 'E': /* Newline */
- state->cursor_x = 0;
- state->escstate = 0;
- break;
- case 'M': /* Reverse newline */
- if (state->cursor_y > 0)
- state->cursor_y--;
- state->escstate = 0;
- break;
- case '7': /* Save state */
- state->saved_x = state->cursor_x;
- state->saved_y = state->cursor_y;
- state->escstate = 0;
- break;
- case '8': /* Restore state */
- state->cursor_x = state->saved_x;
- state->cursor_y = state->saved_y;
- state->escstate = 0;
- break;
- case '[': /* CSI */
- state->escstate = 2;
- for(i = 0; i < NPAR; i++)
- state->csiparam[i] = 0;
- state->curparam = 0;
- break;
- case '%': /* Select charset */
- /* @: Select default (ISO 646 / ISO 8859-1)
- G: Select UTF-8
- 8: Select UTF-8 (obsolete)
-
- We can just ignore this and always process UTF-8, I think?
- We must still catch the last byte, though.
- */
- case '(':
- case ')':
- /* I don't support different fonts either - see above
- for SO and SI */
- state->escstate = 3;
- break;
- default:
- /* Escape sequences not supported:
- *
- * H - Set tab stop
- * Z - Terminal identification
- * > - Keypad change
- * = - Other keypad change
- * ] - OS command
- */
- state->escstate = 0;
- break;
- }
- break;
- case 2:
- switch (c)
- {
- case 24: /* CAN */
- case 26: /* SUB */
- state->escstate = 0;
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- if (state->curparam < NPAR)
- state->csiparam[state->curparam] = (state->csiparam[state->curparam] * 10) + (c - '0');
- break;
- case ';':
- state->csiparam[++state->curparam] = 0;
- break;
- case '[':
- state->escstate = 3;
- break;
- case '@':
- for (i = 0; i < state->csiparam[0]; i++)
- {
- if(++state->cursor_x > cols)
- {
- state->cursor_x = 0;
- if (state->cursor_y < rows - 1)
- state->cursor_y++;
- else
- scroll (state);
- }
- cell = &state->cells[cols * state->cursor_y + state->cursor_x];
- if (cell->state == FLARE || cell->state == NORMAL)
- {
- cell->state = FADE;
- cell->changed = True;
- }
- }
- state->escstate = 0;
- break;
- case 'F':
- state->cursor_x = 0;
- case 'A':
- if (state->csiparam[0] == 0)
- state->csiparam[0] = 1;
- if ((state->cursor_y -= state->csiparam[0]) < 0)
- state->cursor_y = 0;
- state->escstate = 0;
- break;
- case 'E':
- state->cursor_x = 0;
- case 'e':
- case 'B':
- if (state->csiparam[0] == 0)
- state->csiparam[0] = 1;
- if ((state->cursor_y += state->csiparam[0]) >= rows - 1 /*####*/)
- state->cursor_y = rows - 1;
- state->escstate = 0;
- break;
- case 'a':
- case 'C':
- if (state->csiparam[0] == 0)
- state->csiparam[0] = 1;
- if ((state->cursor_x += state->csiparam[0]) >= cols - 1 /*####*/)
- state->cursor_x = cols - 1;
- state->escstate = 0;
- break;
- case 'D':
- if (state->csiparam[0] == 0)
- state->csiparam[0] = 1;
- if ((state->cursor_x -= state->csiparam[0]) < 0)
- state->cursor_x = 0;
- state->escstate = 0;
- break;
- case 'd':
- if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/)
- state->cursor_y = rows - 1;
- state->escstate = 0;
- break;
- case '`':
- case 'G':
- if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols - 1 /*####*/)
- state->cursor_x = cols - 1;
- state->escstate = 0;
- break;
- case 'f':
- case 'H':
- if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/)
- state->cursor_y = rows - 1;
- if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols - 1 /*####*/)
- state->cursor_x = cols - 1;
- if(state->cursor_y < 0)
- state->cursor_y = 0;
- if(state->cursor_x < 0)
- state->cursor_x = 0;
- state->escstate = 0;
- break;
- case 'J':
- start = 0;
- end = rows * cols;
- if (state->csiparam[0] == 0)
- start = cols * state->cursor_y + state->cursor_x;
- if (state->csiparam[0] == 1)
- end = cols * state->cursor_y + state->cursor_x;
- for (i = start; i < end; i++)
- {
- cell = &state->cells[i];
- if (cell->state == FLARE || cell->state == NORMAL)
- {
- cell->state = FADE;
- cell->changed = True;
- }
- }
- set_cursor (state, True);
- state->escstate = 0;
- break;
- case 'K':
- start = 0;
- end = cols;
- if (state->csiparam[0] == 0)
- start = state->cursor_x;
- if (state->csiparam[1] == 1)
- end = state->cursor_x;
- for (i = start; i < end; i++)
- {
- if (cell->state == FLARE || cell->state == NORMAL)
- {
- cell->state = FADE;
- cell->changed = True;
- }
- cell++;
- }
- state->escstate = 0;
- break;
- case 'm': /* Set attributes unimplemented (bold, blink, rev) */
- state->escstate = 0;
- break;
- case 's': /* Save position */
- state->saved_x = state->cursor_x;
- state->saved_y = state->cursor_y;
- state->escstate = 0;
- break;
- case 'u': /* Restore position */
- state->cursor_x = state->saved_x;
- state->cursor_y = state->saved_y;
- state->escstate = 0;
- break;
- case '?': /* DEC Private modes */
- if ((state->curparam != 0) || (state->csiparam[0] != 0))
- state->escstate = 0;
- break;
- default:
- /* Known unsupported CSIs:
- *
- * L - Insert blank lines
- * M - Delete lines (I don't know what this means...)
- * P - Delete characters
- * X - Erase characters (difference with P being...?)
- * c - Terminal identification
- * g - Clear tab stop(s)
- * h - Set mode (Mainly due to its complexity and lack of good
- docs)
- * l - Clear mode
- * m - Set mode (Phosphor is, per defenition, green on black)
- * n - Status report
- * q - Set keyboard LEDs
- * r - Set scrolling region (too exhausting - noone uses this,
- right?)
- */
- state->escstate = 0;
- break;
- }
- break;
- case 3:
- state->escstate = 0;
- break;
-
- case 102: /* states 102-106 are for UTF-8 decoding */
- case 103:
- case 104:
- case 105:
- case 106:
- goto PRINT;
-
- default:
- abort();
- }
- set_cursor (state, True);
- }
- else
-#endif /* HAVE_FORKPTY */
+ /* If the cursor has moved, turn it on and flare it. */
+ if (state->cursor_x != tty->x ||
+ state->cursor_y != tty->y)
{
- if (c == '\t') c = ' '; /* blah. */
-
- if (c == '\r' || c == '\n') /* handle CR, LF, or CRLF as "new line". */
- {
- if (c == '\n' && state->last_c == '\r')
- ; /* CRLF -- do nothing */
- else
- {
- state->cursor_x = 0;
- if (state->cursor_y == rows - 1)
- scroll (state);
- else
- state->cursor_y++;
- }
- }
- else if (c == '\014')
- {
- clear (state);
- }
- else
- {
- c = process_unicrud (state, c);
- if (!c) return;
-
- cell->state = FLARE;
- cell->p_char = state->chars[c];
- cell->changed = True;
- state->cursor_x++;
-
- if (c != ' ' && cell->p_char->blank_p)
- cell->p_char = state->chars[CURSOR_INDEX];
-
- if (state->cursor_x >= cols - 1)
- {
- state->cursor_x = 0;
- if (state->cursor_y >= rows - 1)
- scroll (state);
- else
- state->cursor_y++;
- }
- }
+ p_cell *oc = &state->cells [state->grid_width * state->cursor_y +
+ state->cursor_x];
+ p_cell *nc = &state->cells [state->grid_width * tty->y +
+ tty->x];
+ oc->changed = True;
+ nc->changed = True;
+
+ /* If the new cell is already fading out, do not bring its old
+ character back underneath this new cursor. */
+ if (nc->state >= FADE)
+ nc->c = ' ';
+ nc->state = FLARE;
+ state->cursor_x = tty->x;
+ state->cursor_y = tty->y;
set_cursor (state, True);
}
-
- state->last_c = c;
}
for (x = 0; x < state->grid_width; x++)
{
p_cell *cell = &state->cells[state->grid_width * y + x];
+ int st = cell->state;
+ Bool inv_p = cell->invert_p;
+ Bool sym_p = cell->symbol_p;
+ Bool cursor_p = (x == state->cursor_x && y == state->cursor_y);
+ unsigned char c = cell->c;
+ p_char *pc;
int width, height, tx, ty;
if (changed_only && !cell->changed)
continue;
+ if (cursor_p)
+ {
+ if (state->cursor_on)
+ inv_p = !inv_p;
+ if (st >= FADE)
+ c = ' ';
+ if (st == BLANK || st >= FADE)
+ st = NORMAL;
+ }
+
+ if (!c) c = ' ';
+ pc = (sym_p
+ ? (inv_p ? state->sichars[c] : state->schars[c])
+ : (inv_p ? state->ichars[c] : state->chars[c]));
+
width = state->char_width * state->scale;
height = state->char_height * state->scale;
tx = x * width + state->xmargin;
ty = y * height + state->ymargin;
- if (cell->state == BLANK || cell->p_char->blank_p)
+ if (pc->blank_p || (cell->state == BLANK && !cursor_p))
{
XFillRectangle (state->dpy, state->window, state->gcs[BLANK],
tx, ty, width, height);
}
+# ifdef FUZZY_BORDER
+ else if (pc->cache)
+ {
+ int src_x = st * width;
+ int src_y = 0;
+ XCopyArea (state->dpy, pc->cache, state->window, state->gcs[BLANK],
+ src_x, src_y, width, height, tx, ty);
+ }
+# endif /* FUZZY_BORDER */
else
{
-#ifdef FUZZY_BORDER
- GC gc1 = state->gcs[cell->state];
- GC gc2 = ((cell->state + 2) < state->ticks
- ? state->gcs[cell->state + 2]
+# ifdef FUZZY_BORDER
+ GC gc1 = state->gcs[st];
+ GC gc2 = ((st + 2) < state->ticks
+ ? state->gcs[st + 2]
: 0);
GC gc3 = (gc2 ? gc2 : gc1);
if (gc3)
- XCopyPlane (state->dpy, cell->p_char->pixmap, state->window, gc3,
+ XCopyPlane (state->dpy, pc->pixmap, state->window, gc3,
0, 0, width, height, tx, ty, 1L);
if (gc2)
{
- XSetClipMask (state->dpy, gc1, cell->p_char->pixmap2);
+ XSetClipMask (state->dpy, gc1, pc->pixmap2);
XSetClipOrigin (state->dpy, gc1, tx, ty);
XFillRectangle (state->dpy, state->window, gc1,
tx, ty, width, height);
XSetClipMask (state->dpy, gc1, None);
}
-#else /* !FUZZY_BORDER */
-
+# else /* !FUZZY_BORDER */
XCopyPlane (state->dpy,
- cell->p_char->pixmap, state->window,
- state->gcs[cell->state],
+ pc->pixmap, state->window,
+ state->gcs[st],
0, 0, width, height, tx, ty, 1L);
-
-#endif /* !FUZZY_BORDER */
+# endif /* !FUZZY_BORDER */
}
cell->changed = False;
if (! changed_p) return;
+ ansi_tty_resize (state->tty, state->grid_width, state->grid_height);
+
textclient_reshape (state->tc,
w - state->xmargin * 2,
h - state->ymargin * 2,
- state->grid_width - 1,
- state->grid_height - 1,
+ state->grid_width,
+ state->grid_height,
0);
}
+static void
+phosphor_tty_send (void *closure, const char *text)
+{
+ p_state *state = (p_state *) closure;
+ textclient_puts (state->tc, text);
+}
+
+
static Bool
phosphor_event (Display *dpy, Window window, void *closure, XEvent *event)
{
if (event->xany.type == Expose)
update_display (state, False);
else if (event->xany.type == KeyPress)
- return textclient_putc (state->tc, &event->xkey);
+ return textclient_putc_event (state->tc, &event->xkey);
return False;
}
if (state->cursor_timer)
XtRemoveTimeOut (state->cursor_timer);
+ ansi_tty_free (state->tty);
+
if (state->gc0) XFreeGC (dpy, state->gc0);
if (state->gc1) XFreeGC (dpy, state->gc1);
-#ifdef FUZZY_BORDER
+# ifdef FUZZY_BORDER
if (state->gc2) XFreeGC (dpy, state->gc2);
-#endif /* FUZZY_BORDER */
+# endif /* FUZZY_BORDER */
for (i = 0; i < state->ticks; i++)
if (state->gcs[i]) XFreeGC (dpy, state->gcs[i]);
free (state->gcs);
- for (i = 0; i < 256; i++) {
+ for (i = 0; i < countof(state->chars); i++) {
XFreePixmap (dpy, state->chars[i]->pixmap);
-#ifdef FUZZY_BORDER
+ XFreePixmap (dpy, state->ichars[i]->pixmap);
+# ifdef FUZZY_BORDER
XFreePixmap (dpy, state->chars[i]->pixmap2);
-#endif /* FUZZY_BORDER */
+ XFreePixmap (dpy, state->ichars[i]->pixmap2);
+ if (state->chars[i]->cache)
+ XFreePixmap (dpy, state->chars[i]->cache);
+ if (state->ichars[i]->cache)
+ XFreePixmap (dpy, state->ichars[i]->cache);
+# endif /* FUZZY_BORDER */
free (state->chars[i]);
}
XDestroyImage (state->font_bits);
- free (state->chars);
+ XDestroyImage (state->sym_font_bits);
free (state->cells);
free (state);
}
static const char *phosphor_defaults [] = {
-/* ".lowrez: true", */
+/*".lowrez: true", */
".background: Black",
".foreground: #00FF00",
"*fpsSolid: true",
-#if defined(BUILTIN_FONT)
- "*font: (builtin)",
-#elif defined(HAVE_COCOA)
- "*font: Monaco 15",
-#else
- "*font: fixed",
-#endif
"*phosphorScale: 6",
"*ticks: 20",
"*delay: 50000",
"*relaunch: 5",
"*metaSendsESC: True",
"*swapBSDEL: True",
-#ifdef HAVE_FORKPTY
+
+# if defined(BUILTIN_FONT)
+ "*font: (builtin)",
+# elif defined(HAVE_COCOA)
+ "*font: Monaco 15",
+# else
+ "*font: fixed",
+# endif
+
+# ifdef HAVE_JWXYZ
+ "*cache: False",
+# else
+ "*cache: True",
+# endif
+
+# ifdef HAVE_FORKPTY
"*usePty: True",
-#else /* !HAVE_FORKPTY */
+# else
"*usePty: False",
-#endif /* !HAVE_FORKPTY */
+# endif
+
+ /* For debugging vt100 at 80x24: */
+ /*
+ "*delay: 0",
+ "*phosphorScale: 3",
+ ".geometry: =1470x760",
+ "*program: echo $COLUMNS x $ROWS ; /usr/local/bin/vttest",
+ */
+
0
};
{ "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
{ "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
{ "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
+ { "-cache", ".cache", XrmoptionNoArg, "True" },
+ { "-no-cache", ".cache", XrmoptionNoArg, "False" },
{ 0, 0, 0, 0 }
};
[\-\-display \fIhost:display.screen\fP] [\-\-window] [\-\-root]
[\-\-window\-id \fInumber\fP][\-\-install]
[\-\-visual \fIvisual\fP] [\-\-font \fIfont\fP] [\-\-scale \fIint\fP]
-[\-\-ticks \fIint\fP] [\-\-delay \fIusecs\fP] [\-\-program \fIcommand\fP]
+[\-\-ticks \fIint\fP] [\-\-delay \fIusecs\fP] [\-\-no\-cache]
+[\-\-program \fIcommand\fP]
[\-\-meta] [\-\-esc] [\-\-bs] [\-\-del]
[\-\-fps]
.SH DESCRIPTION
The speed of the terminal: how long to wait between drawing each character.
Default 50000, or about 1/20th second.
.TP 8
+.B \-\-cache | \-\-no-cache
+Speed up rendering at the expense of memory and startup time. Default yes.
+.TP 8
.B \-\-pty
Launch the sub-program under a PTY, so that it can address the screen
directly. This is the default.
-/* xscreensaver, Copyright © 1992-2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1992-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#ifdef HAVE_RECORD_ANIM
{
- int frames = get_integer_resource (dpy, "recordAnim", "Integer");
+ char *str = get_string_resource (dpy, "recordAnim", "Time");
+ int fps = 30;
+ int h = 0, m = 0, s = 0;
+ int frames = 0;
+ char c, suf[20];
+ *suf = 0;
+
+ if (!str || !*str)
+ ;
+ else if (3 == sscanf (str, " %d:%d:%d %c", &h, &m, &s, &c)) /* H:MM:SS */
+ frames = fps * (h*60*60 + m*60 + s);
+ else if (2 == sscanf (str, " %d:%d %c", &m, &s, &c)) /* M:SS */
+ frames = fps * (m*60 + s);
+ else if (1 == sscanf (str, " %d %c", &s, &c)) /* frames */
+ frames = s;
+ else if (2 == sscanf (str, "%d %10s %c", &h, suf, &c))
+ {
+ if (!strcasecmp (suf, "h") || /* 1 H */
+ !strcasecmp (suf, "hour") ||
+ !strcasecmp (suf, "hours"))
+ frames = fps * h*60*60;
+ else if (!strcasecmp (suf, "m") || /* 2 min */
+ !strcasecmp (suf, "min") ||
+ !strcasecmp (suf, "mins") ||
+ !strcasecmp (suf, "minute") ||
+ !strcasecmp (suf, "minutes"))
+ frames = fps * h*60;
+ else if (!strcasecmp (suf, "s") || /* 30 sec */
+ !strcasecmp (suf, "sec") ||
+ !strcasecmp (suf, "secs") ||
+ !strcasecmp (suf, "second") ||
+ !strcasecmp (suf, "seconds"))
+ frames = fps * h;
+ else
+ goto FAIL;
+ }
+ else
+ {
+ FAIL:
+ fprintf (stderr, "%s: unparsable duration: %s\n", progname, str);
+ exit (1);
+ }
+
+ if (str) free (str);
if (frames > 0)
anim_state = screenhack_record_anim_init (xgwa.screen, window, frames);
}
-/* xscreensaver, Copyright © 1992-2023 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1992-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
# include <GL/glx.h>
# endif
+# ifndef HAVE_JWZGLES
/* Sep 2022, Sep 2023: The Raspberry Pi 4b Broadcom driver doesn't do
GL_LINE_SMOOTH properly, so we must disable it. See init_GL(). */
extern void (* glEnable_fn) (GLuint);
# define glEnable (* glEnable_fn)
+# endif /* !HAVE_JWZGLES */
# endif /* real X11 */
#!/usr/bin/perl -w
#
-# webcollage, Copyright © 1999-2024 Jamie Zawinski <jwz@jwz.org>
+# webcollage, Copyright © 1999-2025 Jamie Zawinski <jwz@jwz.org>
# This program decorates the screen with random images from the web.
# One satisfied customer described it as "a nonstop pop culture brainbath."
#
my $progname = $0; $progname =~ s@.*/@@g;
-my ($version) = ('$Revision: 1.193 $' =~ m/\s(\d[.\d]+)\s/s);
+my ($version) = ('$Revision: 1.195 $' =~ m/\s(\d[.\d]+)\s/s);
my $copyright = "WebCollage $version, Copyright © 1999-2024" .
" Jamie Zawinski <jwz\@jwz.org>\n" .
" https://www.jwz.org/webcollage/\n";
# a short-running screen saver, but not as a batch job.
# I haven't found a workaround.
#
- 5, "googlephotos", \&pick_from_google_image_photos,
- 3, "googleimgs", \&pick_from_google_images,
- 3, "googlenums", \&pick_from_google_image_numbers,
+ 4, "googlephotos", \&pick_from_google_image_photos,
+ 2, "googleimgs", \&pick_from_google_images,
+ 2, "googlenums", \&pick_from_google_image_numbers,
# So let's try Bing instead. No rate limiting yet!
#
13, "bingphotos", \&pick_from_bing_image_photos,
- 10, "bingimgs", \&pick_from_bing_images,
- 9, "bingnums", \&pick_from_bing_image_numbers,
+ 9, "bingimgs", \&pick_from_bing_images,
+ 8, "bingnums", \&pick_from_bing_image_numbers,
20, "flickr_recent", \&pick_from_flickr_recent,
15, "flickr_random", \&pick_from_flickr_random,
- 6, "livejournal", \&pick_from_livejournal_images,
+ 10, "flickr_commons",\&pick_from_flickr_commons,
11, "imgur", \&pick_from_imgur,
+ 2, "livejournal", \&pick_from_livejournal_images,
# Tumblr doesn't have an "or" search, so this isn't great.
- 3, "tumblr", \&pick_from_tumblr,
+ 2, "tumblr", \&pick_from_tumblr,
# I ran out of usable access tokens, May 2017
# 0, "instagram", \&pick_from_instagram,
$url =~ m@^https?://random\.yahoo\.com/@s ||
$url =~ m@^https?://[^./]+\.google\.com/@s ||
$url =~ m@^https?://www\.livejournal\.com/@s ||
- $url =~ m@^https?://imgur\.com/@s) {
+ $url =~ m@^https?://([^./]+\.)?imgur\.com/@s) {
# block this, you turkeys.
$user_agent = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.7)' .
' Gecko/20070914 Firefox/2.0.0.7';
print "\n<<[[\n"; $req->dump; print "\n]]\n";
return;
});
+ $ua->add_handler ("response_done",
+ sub($$$) {
+ my ($req, $ua, $h) = @_;
+ print "\n<<[[\n"; $req->dump; print "\n]]\n";
+ return;
+ });
}
if ($verbose_http) {
# random image from it.
# returns the url of the page loaded; the url of the image chosen.
#
-sub pick_image_from_pages($$$$@) {
- my ($base, $total_hit_count, $unfiltered_link_count, $timeout, @pages) = @_;
-
- $total_hit_count = "?" unless defined($total_hit_count);
-
- @pages = depoison (@pages);
- LOG ($verbose_load,
- "" . ($#pages+1) . " candidates of $unfiltered_link_count links" .
- " ($total_hit_count total)");
-
- return () if ($#pages < 0);
-
- my $i = int(rand($#pages+1));
- my $page = $pages[$i];
-
- LOG ($verbose_load, "picked page $page");
-
- $suppress_audit = 1;
-
- my ( $base2, $body2 ) = get_document ($page, $base, $timeout);
-
- if (!$base2 || !$body2) {
- $body2 = undef;
- return ();
- }
-
- my $img = pick_image_from_body ($base2, $body2);
- $body2 = undef;
-
- if ($img) {
- return ($base2, $img);
- } else {
- return ();
- }
-}
+#sub pick_image_from_pages($$$$@) {
+# my ($base, $total_hit_count, $unfiltered_link_count, $timeout, @pages) = @_;
+#
+# $total_hit_count = "?" unless defined($total_hit_count);
+#
+# @pages = depoison (@pages);
+# LOG ($verbose_load,
+# "" . ($#pages+1) . " candidates of $unfiltered_link_count links" .
+# " ($total_hit_count total)");
+#
+# return () if ($#pages < 0);
+#
+# my $i = int(rand($#pages+1));
+# my $page = $pages[$i];
+#
+# LOG ($verbose_load, "picked page $page");
+#
+# $suppress_audit = 1;
+#
+# my ( $base2, $body2 ) = get_document ($page, $base, $timeout);
+#
+# if (!$base2 || !$body2) {
+# $body2 = undef;
+# return ();
+# }
+#
+# my $img = pick_image_from_body ($base2, $body2);
+# $body2 = undef;
+#
+# if ($img) {
+# return ($base2, $img);
+# } else {
+# return ();
+# }
+#}
\f
#############################################################################
return ($base, $img);
}
+\f
+############################################################################
+#
+# Pick images from the Flickr Commons landing page.
+#
+############################################################################
+
+my $flickr_commons = 'https://commons.flickr.org/';
+
+# flickr_commons
+sub pick_from_flickr_commons($) {
+ my $timeout = shift;
+
+ $last_search = $flickr_commons;
+
+ print STDERR "\n\n" if ($verbose_load);
+ LOG ($verbose_load, "URL: $last_search");
+
+ $suppress_audit = 1;
+
+ my ( $base, $body ) = get_document ($last_search, undef, $timeout);
+ if (!$base || !$body) {
+ $body = undef;
+ return;
+ }
+
+ $body =~ s@^ .* "random_photos" (.*?) </section> .* @$1@six;
+ my @imgs = ();
+ foreach my $a ($body =~ m@ <a .*? </a> @gsix) {
+ my ($url) = ($a =~ m@\bHREF=[\"\']([^\'\"<>]+)@gsi);
+ my ($img) = ($a =~ m@\bSRC=[\"\']([^\'\"<>]+)@gsi);
+ next unless ($img && $url);
+ next unless ($url =~ m@/photos/@s);
+ push @imgs, [ $url, $img ];
+ }
+
+ my $n = @imgs;
+ my $i = int(rand($n));
+ my $P = $imgs[$i];
+ return () unless defined ($P);
+
+ $base = $P->[0];
+ my $img = $P->[1];
+
+ LOG ($verbose_load, "redirected to: $base");
+ return ($base, $img);
+}
+
\f
############################################################################
#
#!/usr/bin/perl -w
-# Copyright © 2005-2023 Jamie Zawinski <jwz@jwz.org>
+# Copyright © 2005-2024 Jamie Zawinski <jwz@jwz.org>
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
my $progname = $0; $progname =~ s@.*/@@g;
-my ($version) = ('$Revision: 1.71 $' =~ m/\s(\d[.\d]+)\s/s);
+my ($version) = ('$Revision: 1.72 $' =~ m/\s(\d[.\d]+)\s/s);
my $verbose = 0;
my $http_proxy = undef;
$text =~ s/\xE2\x80\x99/'/gs;
$text =~ s/\xE2\x80\x9C/``/gs;
$text =~ s/\xE2\x80\x9D/'/gs;
- $text =~ s/\xE2\x80\xA2/•/gs;
+ $text =~ s/\xE2\x80\xA2/\xB7/gs; # ·
+ $text =~ s/\xE2\xC2\x80/\xB7/gs; # ·
$text =~ s/\xE2\x80\xA6/.../gs;
$text =~ s/\xE2\x80\xB2/'/gs;
- $text =~ s/\xE2\x84\xA2/™/gs;
- $text =~ s/\xE2\x86\x90/ ← /gs;
+ $text =~ s/\xE2\x84\xA2/[tm]/gs; # ™
+ $text =~ s/\xE2\x86\x90/<=/gs; # ←
+ $text =~ s/\xE2\x86\x92/=>/gs; # →
return $text;
}
s@(<PRE\b[^<>]*>\s*)(.*?)(</PRE)@{
my ($a, $b, $c) = ($1, $2, $3);
$b =~ s/[\r\n]/<BR>/gs;
+ $b =~ s/ /\240/gs; #
$a . $b . $c;
}@gsexi;
}
if (! $rss_p) {
# In HTML, unfold lines.
# In RSS, assume \n means literal line break.
- s@[\r\n]@ @gsi;
+ s/\s+/ /gs;
+ s@ @\240@gs;
}
# This right here is the part where I doom us all to inhuman
s@<!--.*?-->@@gsi; # lose comments
s@<(STYLE|SCRIPT)\b[^<>]*>.*?</\1\s*>@@gsi; # lose css and js
+ s/(<LI>)/$1• /gsi;
s@</?(BR|TR|TD|LI|DIV)\b[^<>]*>@\n@gsi; # line break at BR, TD, DIV, etc
s@</?(P|UL|OL|BLOCKQUOTE)\b[^<>]*>@\n\n@gsi; # two line breaks
s@<[^<>]*>?@@gs; # lose all other HTML tags
$_ = de_entify ($_); # convert HTML entities
+ if (! $rss_p) {
+ s/[ \t]+/ /gs;
+ s/\240/ /gs;
+ }
+
# For Wikipedia: delete anything inside {{ }} and unwrap [[tags]],
# among other things.
#
# elide any remaining non-Latin1 binary data.
if ($latin1_p) {
- utf8::encode ($_); # Unpack Unicode back to multi-byte UTF-8.
+ $_ = utf8_to_latin1 ($_);
s/([^\000-\176]+(\s*[^\000-\176]+)[^a-z\d]*)/\xAB...\xBB /g;
}
s/^(([^\n]*\n){$truncate_lines}).*$/$1/s;
}
- $_ = utf8_to_latin1($_) if ($latin1_p);
y/A-Za-z/N-ZA-Mn-za-m/ if ($nyarlathotep_p);
return $_;
-/* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1991-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#define XK_F10 0xFFC7
#define XK_F11 0xFFC8
#define XK_F12 0xFFC9
+#define XK_F13 0xFFCA
+#define XK_F14 0xFFCB
+#define XK_F15 0xFFCC
+#define XK_F16 0xFFCD
+#define XK_F17 0xFFCE
+#define XK_F18 0xFFCF
+#define XK_F19 0xFFD0
+#define XK_F20 0xFFD1
+
+#define XK_KP_F1 0xFF91
+#define XK_KP_F2 0xFF92
+#define XK_KP_F3 0xFF93
+#define XK_KP_F4 0xFF94
+
+#define XK_KP_Home 0xFF95
+#define XK_KP_Left 0xFF96
+#define XK_KP_Up 0xFF97
+#define XK_KP_Right 0xFF98
+#define XK_KP_Down 0xFF99
+#define XK_KP_Prior 0xFF9A
+#define XK_KP_Page_Up 0xFF9A
+#define XK_KP_Next 0xFF9B
+#define XK_KP_Page_Down 0xFF9B
+#define XK_KP_End 0xFF9C
+#define XK_KP_Begin 0xFF9D
+#define XK_KP_Insert 0xFF9E
+#define XK_KP_Delete 0xFF9F
+#define XK_KP_Equal 0xFFBD
+#define XK_KP_Multiply 0xFFAA
+#define XK_KP_Add 0xFFAB
+#define XK_KP_Separator 0xFFAC
+#define XK_KP_Subtract 0xFFAD
+#define XK_KP_Decimal 0xFFAE
+#define XK_KP_Divide 0xFFAF
#define GXclear 0x0 /* 0 */
if (d2 != data) free (d2);
}
+void
+jwzgles_glTexImage3D (GLenum target,
+ GLint level,
+ GLint internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const GLvoid *data)
+{
+# ifdef HAVE_GLSL
+ glTexImage3D (target, level, internalFormat, width, height, depth, border,
+ format, type, data);
+# endif
+}
+
void
jwzgles_glTexSubImage2D (GLenum target, GLint level,
GLint xoffset, GLint yoffset,
GLenum format,
GLenum type,
const GLvoid *data);
+extern void jwzgles_glTexImage3D (GLenum target,
+ GLint level,
+ GLint internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const GLvoid *data);
extern void jwzgles_glTexSubImage2D (GLenum target, GLint level,
GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height,
-# Auto-generated: Fri Jun 7 22:01:23 PDT 2024
+# Auto-generated: Mon Apr 28 12:45:42 PDT 2025
driver/demo-Gtk-conf.c
driver/demo-Gtk.c
driver/demo.ui
hacks/config/dnalogo.xml
hacks/config/drift.xml
hacks/config/droste.xml
+hacks/config/dumpsterfire.xml
hacks/config/dymaxionmap.xml
hacks/config/endgame.xml
hacks/config/energystream.xml
hacks/config/highvoltage.xml
hacks/config/hilbert.xml
hacks/config/hopalong.xml
+hacks/config/hopffibration.xml
hacks/config/hydrostat.xml
hacks/config/hyperball.xml
hacks/config/hypercube.xml
hacks/config/kaleidocycle.xml
hacks/config/kallisti.xml
hacks/config/klein.xml
+hacks/config/klondike.xml
hacks/config/kumppa.xml
hacks/config/lament.xml
hacks/config/laser.xml
hacks/config/piecewise.xml
hacks/config/pinion.xml
hacks/config/pipes.xml
+hacks/config/platonicfolding.xml
hacks/config/polyhedra.xml
hacks/config/polyominoes.xml
hacks/config/polytopes.xml
#!/bin/sh
-# Copyright © 2018 Jamie Zawinski <jwz@jwz.org>
+# Copyright © 2018-2025 Jamie Zawinski <jwz@jwz.org>
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
-e 's@^.*/@@' \
-e 's/\.[^.]*$//' \
-e 's/[-.]/_/g' \
- -e 's/^\([^a-z]\)/_\1/'`;
+ -e 's/^\([^a-zA-Z]\)/_\1/'`;
if [ x"$PERL" = "x" ]; then PERL=perl ; fi
+# Perl fails if set to run in UTF-8 mode
+unset PERL5OPTS
+unset PERL_UNICODE
+
# On Linux, we could do this and put the raw image into a .o data segment:
# $(LD) -r -b binary $< -o $@
# but that doesn't work on MacOS.
-/* xscreensaver, Copyright (c) 2012-2018 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 2012-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
Bool
-textclient_putc (text_data *d, XKeyEvent *k)
+textclient_puts (text_data *d, const char *s)
+{
+ return False;
+}
+
+Bool
+textclient_putc_event (text_data *d, XKeyEvent *k)
{
return False;
}
-/* xscreensaver, Copyright © 2012-2021 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 2012-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
subproc_cb (XtPointer closure, int *source, XtInputId *id)
{
text_data *d = (text_data *) closure;
-# ifdef DEBUG
+# ifdef _DEBUG
if (! d->input_available_p)
fprintf (stderr, "%s: textclient: input available\n", progname);
# endif
}
-static void start_timer (text_data *d);
+static void start_timer (text_data *, Bool);
static void
launch_text_generator (text_data *d)
{
if (!d->out_buffer || !*d->out_buffer)
d->out_buffer = "Can't exec; Gatekeeper problem?\r\n\r\n";
- start_timer (d);
+ start_timer (d, False);
free (cmd);
return;
}
/* This is the child fork. */
char *av[10];
int i = 0;
- if (putenv ("TERM=vt100"))
- abort();
av[i++] = "/bin/sh";
av[i++] = "-c";
av[i++] = cmd;
static void
-start_timer (text_data *d)
+start_timer (text_data *d, Bool first_time_p)
{
XtAppContext app = XtDisplayToApplicationContext (d->dpy);
if (d->pipe_timer)
XtRemoveTimeOut (d->pipe_timer);
d->pipe_timer =
- XtAppAddTimeOut (app, d->subproc_relaunch_delay,
+ XtAppAddTimeOut (app, (first_time_p
+ ? 250
+ : d->subproc_relaunch_delay),
relaunch_generator_timer,
(XtPointer) d);
}
pix_w, pix_h, char_w, char_h);
# endif
+ {
+ char s1[30];
+ char s2[30];
+ sprintf (s1, "COLUMNS=%d", d->char_w);
+ sprintf (s2, "ROWS=%d", d->char_h);
+ putenv (strdup (s1)); /* Some versions of putenv copy, some don't */
+ putenv (strdup (s2)); /* so we must leak */
+ }
+
if (d->pid && d->pipe)
{
/* Tell the sub-process that the screen size has changed. */
# endif
close_pipe (d);
d->input_available_p = False;
- start_timer (d);
+ start_timer (d, False);
}
}
}
# endif
- start_timer (d);
+ putenv ("TERM=vt100");
+ unsetenv ("TERMCAP");
+ unsetenv ("INSIDE_EMACS");
+
+ start_timer (d, True);
return d;
}
d->out_buffer = "\r\n\r\n";
}
- start_timer (d);
+ start_timer (d, False);
}
d->input_available_p = False;
}
# ifdef DEBUG
if (ret <= 0)
- fprintf (stderr, "%s: textclient: getc: %d\n", progname, ret);
+ /* fprintf (stderr, "%s: textclient: getc: %d\n", progname, ret) */;
else if (ret < ' ')
fprintf (stderr, "%s: textclient: getc: %03o\n", progname, ret);
else
Bool
-textclient_putc (text_data *d, XKeyEvent *k)
+textclient_puts (text_data *d, const char *s)
+{
+ fputs (s, d->pipe);
+ fflush (d->pipe);
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: write: %s\n", progname, s);
+# endif
+ return True;
+}
+
+
+Bool
+textclient_putc_event (text_data *d, XKeyEvent *k)
{
KeySym keysym;
unsigned char c = 0;
+ char *s = 0;
+ char ss[3];
+
+ if (! d->pipe) return False;
+
XLookupString (k, (char *) &c, 1, &keysym, &d->compose);
- if (c != 0 && d->pipe)
+
+# define ESC "\x1B"
+# define CSI ESC "["
+# define SS3 ESC "O"
+
+ /* https://vt100.net/docs/vt220-rm/chapter3.html#S3.2
+ https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ */
+ switch (keysym) {
+ case XK_Up: s = SS3 "A"; break;
+ case XK_Down: s = SS3 "B"; break;
+ case XK_Right: s = SS3 "C"; break;
+ case XK_Left: s = SS3 "D"; break;
+ case XK_Home: s = SS3 "H"; break;
+ case XK_End: s = SS3 "F"; break;
+ case XK_Prior: s = CSI "5~"; break;
+ case XK_Next: s = CSI "6~"; break;
+
+ case XK_F1: s = SS3 "P"; break;
+ case XK_F2: s = SS3 "Q"; break;
+ case XK_F3: s = SS3 "R"; break;
+ case XK_F4: s = SS3 "S"; break;
+ case XK_F5: s = CSI "15~"; break;
+ case XK_F6: s = CSI "17~"; break;
+ case XK_F7: s = CSI "18~"; break;
+ case XK_F8: s = CSI "19~"; break;
+ case XK_F9: s = CSI "20~"; break;
+ case XK_F10: s = CSI "21~"; break;
+ case XK_F11: s = CSI "23~"; break;
+ case XK_F12: s = CSI "24~"; break;
+ case XK_F13: s = CSI "25~"; break;
+ case XK_F14: s = CSI "26~"; break;
+ case XK_F15: s = CSI "28~"; break;
+ case XK_F16: s = CSI "29~"; break;
+ case XK_F17: s = CSI "31~"; break;
+ case XK_F18: s = CSI "32~"; break;
+ case XK_F19: s = CSI "33~"; break;
+ case XK_F20: s = CSI "34~"; break;
+
+ case XK_KP_F1: s = SS3 "P"; break;
+ case XK_KP_F2: s = SS3 "Q"; break;
+ case XK_KP_F3: s = SS3 "R"; break;
+ case XK_KP_F4: s = SS3 "S"; break;
+
+ case XK_KP_Up: s = SS3 "A"; break;
+ case XK_KP_Down: s = SS3 "B"; break;
+ case XK_KP_Right: s = SS3 "C"; break;
+ case XK_KP_Left: s = SS3 "D"; break;
+ case XK_KP_Home: s = SS3 "H"; break;
+ case XK_KP_End: s = SS3 "F"; break;
+ case XK_KP_Prior: s = CSI "5~"; break;
+ case XK_KP_Next: s = CSI "6~"; break;
+
+ case XK_KP_Begin: s = CSI "E"; break;
+ case XK_KP_Insert: s = CSI "2~"; break;
+ case XK_KP_Delete: s = CSI "3~"; break;
+ case XK_KP_Equal: s = SS3 "X"; break;
+ case XK_KP_Multiply: s = SS3 "j"; break;
+ case XK_KP_Add: s = SS3 "k"; break;
+ case XK_KP_Subtract: s = SS3 "m"; break;
+ case XK_KP_Decimal: s = ESC "?n"; break;
+ case XK_KP_Divide: s = ESC "?o"; break;
+ default: break;
+ }
+
+ if (c && !s)
{
if (!d->swap_bs_del_p) ;
else if (c == 127) c = 8;
if (k->state & meta_modifier (d))
{
if (d->meta_sends_esc_p)
- fputc ('\033', d->pipe);
+ {
+ ss[0] = ESC[0];
+ ss[1] = c;
+ ss[2] = 0;
+ }
else
- c |= 0x80;
+ {
+ ss[0] = c | 0x80;
+ ss[1] = 0;
+ }
+ }
+ else
+ {
+ ss[0] = c;
+ ss[1] = 0;
}
- fputc (c, d->pipe);
- fflush (d->pipe);
- k->type = 0; /* don't interpret this event defaultly. */
-
-# ifdef DEBUG
- fprintf (stderr, "%s: textclient: putc '%c'\n", progname, (char) c);
-# endif
+ s = ss;
+ }
+ if (s)
+ {
+ textclient_puts (d, s);
+ k->type = 0;
return True;
}
+
return False;
}
-/* xscreensaver, Copyright (c) 2012-2016 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 2012-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
int char_w, int char_h,
int max_lines);
extern int textclient_getc (text_data *);
-extern Bool textclient_putc (text_data *, XKeyEvent *);
+extern Bool textclient_puts (text_data *, const char *);
+extern Bool textclient_putc_event (text_data *, XKeyEvent *);
# if defined(HAVE_IPHONE) || defined(HAVE_ANDROID)
extern char *textclient_mobile_date_string (void);
static const char screensaver_id[] =
- "@(#)xscreensaver 6.09 (07-Jun-2024), by Jamie Zawinski (jwz@jwz.org)";
-#define XSCREENSAVER_VERSION "6.09"
-#define XSCREENSAVER_RELEASED 1717786800
+ "@(#)xscreensaver 6.10 (28-Apr-2025), by Jamie Zawinski (jwz@jwz.org)";
+#define XSCREENSAVER_VERSION "6.10"
+#define XSCREENSAVER_RELEASED 1745866800
-/* xscreensaver, Copyright © 1999-2021 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1999-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
int egl_major = -1, egl_minor = -1;
/* This is re-used, no need to close it. */
- egl_display = eglGetDisplay ((EGLNativeDisplayType) dpy);
+ egl_display = eglGetPlatformDisplay (EGL_PLATFORM_X11_KHR,
+ (void *) dpy, NULL);
if (!egl_display)
{
- fprintf (stderr, "%s: eglGetDisplay failed\n", progname);
+ fprintf (stderr, "%s: eglGetPlatformDisplay failed\n", progname);
return 0;
}
/* This is re-used, no need to close it. */
egl_display = eglGetPlatformDisplay (EGL_PLATFORM_X11_KHR,
- (EGLNativeDisplayType) dpy, NULL);
+ (void *) dpy, NULL);
if (!egl_display)
{
fprintf (stderr, "%s: eglGetPlatformDisplay failed\n", progname);
else if (fields[i].i == EGL_TRANSPARENT_RED_VALUE && tt != EGL_NONE)
sprintf (s, "%d, %d, %d", tr, tg, tb);
else if (fields[i].i == EGL_CONFIG_CAVEAT)
- strcpy (s, (v == EGL_NONE ? "none" :
- v == EGL_SLOW_CONFIG ? "slow" :
+ {
+ const char *s2 = (v == EGL_NONE ? "none" :
+ v == EGL_SLOW_CONFIG ? "slow" :
# ifdef EGL_NON_CONFORMANT
- v == EGL_NON_CONFORMANT ? "non-conformant" :
+ v == EGL_NON_CONFORMANT ? "non-conformant" :
# endif
- "???"));
+ "???");
+ strcpy (s, s2);
+ }
else if (fields[i].i == EGL_COLOR_BUFFER_TYPE)
strcpy (s, (v == EGL_RGB_BUFFER ? "RGB" :
v == EGL_LUMINANCE_BUFFER ? "luminance" :
-/* xscreensaver, Copyright © 2014-2021 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 2014-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
}
+XftDraw *
+XftDrawCreateBitmap (Display *dpy, Pixmap bitmap)
+{
+ Screen *screen = DefaultScreenOfDisplay (dpy);
+ return XftDrawCreate (dpy, bitmap,
+ DefaultVisualOfScreen (screen),
+ DefaultColormapOfScreen (screen));
+}
+
+
void
XftDrawDestroy (XftDraw *draw)
{
-/* xscreensaver, Copyright © 2014-2022 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 2014-2025 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
Drawable drawable,
Visual *visual,
Colormap colormap);
+XftDraw *XftDrawCreateBitmap (Display *dpy, Pixmap bitmap);
Display *XftDrawDisplay (XftDraw *);
Bool XftDrawSetClipRectangles (XftDraw *, int x, int y,
_Xconst XRectangle *rects, int n);
if (nl) in++;
while (*in == ' ' || *in == '\t') in++;
in--;
+ if (out < line_out) abort();
XftTextExtentsUtf8 (dpy, font,
(FcChar8 *) line_out,
word_out = 0;
if (done) break;
*out++ = *in;
+ if (out < line_out) abort();
}
else
{
*out++ = *in;
if (nl)
{
- line_out = out + 1;
+ line_out = out; /* Why was I adding 1 to this? */
word_out = 0;
}
+ if (out < line_out) abort();
}
if (done) break;
/* Like XftTextExtentsUtf8, but handles multi-line strings.
XGlyphInfo will contain the bounding box that encloses all of the text.
Return value is the number of lines in the text, >= 1.
+
+ overall->y will be the distance from the top of the ink to the origin of
+ the first character in the string, as usual. If there are multiple lines,
+ you can think of them as extremely tall descenders below the origin.
*/
int
XftTextExtentsUtf8_multi (Display *dpy, XftFont *font,
int nx1, ny1, nx2, ny2; /* bbox of 'gi' */
int ux1, uy1, ux2, uy2; /* union */
+ /* Cumulative bbox prior to this line */
ox1 = overall->x;
oy1 = overall->y;
ox2 = ox1 + overall->width;
oy2 = oy1 + overall->height;
- line_y += font->ascent + font->descent; /* advance origin */
+ /* Advance the origin of this line downward. */
+ line_y += font->ascent + font->descent;
+ /* Bounding box of this line, with origin adjusted to be
+ the origin of line 0. */
nx1 = gi.x;
ny1 = gi.y + line_y;
nx2 = nx1 + gi.width;
- ny2 = ny1 + gi.height + line_y;
+ ny2 = ny1 + gi.height;
+ /* Find the union of the two same-origin bboxes. */
ux1 = MIN (ox1, nx1); /* upper left */
uy1 = MIN (oy1, ny1);
ux2 = MAX (ox2, nx2); /* bottom right */
%define name xscreensaver
-%define version 6.09
+%define version 6.10
Summary: X screen saver and locker
Name: %{name}
%description
A modular screen saver and locker for the X Window System.
-More than 250 display modes are included in this package.
+More than 260 display modes are included in this package.
%prep
%setup -q