Disabled external gits
0
cs440-acg/results/homework-1/.gitkeep
Normal file
BIN
cs440-acg/results/homework-1/bunny.png
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
cs440-acg/results/homework-1/mine.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
cs440-acg/results/homework-1/reference.jpg
Normal file
After Width: | Height: | Size: 104 KiB |
25
cs440-acg/results/homework-1/report.html
Normal file
@@ -0,0 +1,25 @@
|
||||
**Homework 1**
|
||||
|
||||
Student name: Cedric Hölzl
|
||||
|
||||
Sciper number: 257844
|
||||
|
||||
|
||||
Feedback
|
||||
========
|
||||
|
||||
Tutorial and explanations are straight forward. The bunny took some seconds to be rendered, I was unsure if my computer is slow or if it is expected (since it may only get worse with more complex renders).
|
||||
|
||||
Below you will find my resulting render of the "bunny.xml" scene.
|
||||
<img src="bunny.png" alt="Bunny Render">
|
||||
|
||||
<!-- Slider -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="../resources/jquery.event.move.js"></script>
|
||||
<script src="../resources/jquery.twentytwenty.js"></script>
|
||||
<link href="../resources/offcanvas.css" rel="stylesheet">
|
||||
<link href="../resources/twentytwenty.css" rel="stylesheet" type="text/css" />
|
||||
<script>var markdeepOptions = {onLoad: function() {$(".twentytwenty-container").twentytwenty({default_offset_pct: 0.5, move_slider_on_hover: true});} };</script>
|
||||
<!-- Markdeep: -->
|
||||
<script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js?" charset="utf-8"></script>
|
||||
<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>
|
BIN
cs440-acg/results/homework-2/ajax-normals-ref.png
Normal file
After Width: | Height: | Size: 380 KiB |
BIN
cs440-acg/results/homework-2/ajax-normals.exr
Normal file
BIN
cs440-acg/results/homework-2/ajax-normals.png
Normal file
After Width: | Height: | Size: 379 KiB |
94
cs440-acg/results/homework-2/report.html
Normal file
@@ -0,0 +1,94 @@
|
||||
**Homework 2**
|
||||
|
||||
Student name: Cédric Hölzl
|
||||
|
||||
Sciper number: 257844
|
||||
|
||||
Octree construction (50 pts)
|
||||
============================
|
||||
|
||||
We store the following information for each octre node:
|
||||
* List of Triangles (inside a vector to be able to add elements easily),
|
||||
* List of ChildNodes pointers (inside a fixed size array since we have at most 8 of them),
|
||||
* Bounding box of the node
|
||||
|
||||
This structures takes up 112 Bytes for each node, composed as follows:
|
||||
* List of Triangles: 24,
|
||||
* List of ChildNodes: 8*8 = 64,
|
||||
* Bounding Box: 24,
|
||||
|
||||
We could have used three pointers to have a smaller structure of only 24 bytes (3*8), but this would result in more memory overhead. Hence, we chose to use a bit larger stucture.
|
||||
|
||||
We collected the following statistics (max_depth=10 and triangle per leaf=8):
|
||||
* Internal Nodes: 284'253,
|
||||
* Leaf Nodes: 1'989'772,
|
||||
* Triangles (accumulated from all leafs): 20'526'613,
|
||||
* Triangles Per Leaf: 10.3161,
|
||||
* Construction Time: 1.0s
|
||||
|
||||
|
||||
Ray traversal (25 pts)
|
||||
======================
|
||||
|
||||
As implementation, we just use a queue, where we add recursivly nodes of our Octree, whenever the ray intersects them. If the node is a leaf, we check the intersection with the ray and only keep the closest intersection.
|
||||
|
||||
We collected the following statistic (image_size=24x24, samples_per_pixel=32):
|
||||
* Render Time Default: 8.5min
|
||||
* Render Time Octree: 114.0ms (default size took us 4.8s to render)
|
||||
|
||||
We can see a speedup of over 4400%.
|
||||
|
||||
|
||||
Improved ray traversal (25 pts)
|
||||
===============================
|
||||
|
||||
From the previous implementation, we replaced the queue with a priority_queue using the distance to push/pop elements from it in the desired order.
|
||||
|
||||
We collected the following statistics:
|
||||
* Render Time: 4.8s
|
||||
* Render Time Improved: 4.1s
|
||||
|
||||
We can see a minor improvement (~16%)
|
||||
|
||||
Below you can see the difference between our render and the reference one, it is relativly clear that our changes provided the same results with better performances.
|
||||
|
||||
Surface normal visualization of the Ajax bust:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="ajax-normals-ref.png" alt="Reference">
|
||||
<img src="ajax-normals.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Note: <a href="ajax-normals.exr">ajax-normals.exr</a>
|
||||
|
||||
|
||||
Bonus: Improved Octree construction (10 pts)
|
||||
=============================================
|
||||
|
||||
From the initial version, we parallelized the child node construction up to a certain depth using `parallel_for`. Each internal node will run its 8 child node constructors in parallel.
|
||||
|
||||
We collected the following statistics:
|
||||
* Construction Time: 1.0s
|
||||
* Construction Time Improved: 422.0ms
|
||||
|
||||
We can see a major improvement (~200%).
|
||||
|
||||
Feedback
|
||||
========
|
||||
|
||||
Time Spent Breakdown:
|
||||
* Designing: ~1h30
|
||||
* Coding: ~5h (had some minor issues with tbb)
|
||||
* Testing: ~3h (renders are slow and forgot to disable debug options)
|
||||
|
||||
I believed no further advice should have been needed, instructions were clear and helpfull. The difficult part of the assignment was understanding the structure of the provided code in order to know where to do what changes. Maybe having a small doc generate from the headers (doxygen) might improve workflow.
|
||||
|
||||
<!-- Slider -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="../resources/jquery.event.move.js"></script>
|
||||
<script src="../resources/jquery.twentytwenty.js"></script>
|
||||
<link href="../resources/offcanvas.css" rel="stylesheet">
|
||||
<link href="../resources/twentytwenty.css" rel="stylesheet" type="text/css" />
|
||||
<script>var markdeepOptions = {onLoad: function() {$(".twentytwenty-container").twentytwenty({default_offset_pct: 0.5, move_slider_on_hover: true});} };</script>
|
||||
<!-- Markdeep: -->
|
||||
<script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js?" charset="utf-8"></script>
|
||||
<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>
|
0
cs440-acg/results/homework-3/.gitkeep
Normal file
BIN
cs440-acg/results/homework-3/HSW.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
cs440-acg/results/homework-3/ajax-ao-ref.png
Normal file
After Width: | Height: | Size: 182 KiB |
BIN
cs440-acg/results/homework-3/ajax-ao.exr
Normal file
BIN
cs440-acg/results/homework-3/ajax-ao.png
Normal file
After Width: | Height: | Size: 167 KiB |
BIN
cs440-acg/results/homework-3/ajax-simple-ref.png
Normal file
After Width: | Height: | Size: 157 KiB |
BIN
cs440-acg/results/homework-3/ajax-simple.exr
Normal file
BIN
cs440-acg/results/homework-3/ajax-simple.png
Normal file
After Width: | Height: | Size: 157 KiB |
BIN
cs440-acg/results/homework-3/beckmann005.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
cs440-acg/results/homework-3/beckmann05.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
cs440-acg/results/homework-3/beckmann1.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
cs440-acg/results/homework-3/coshsphere.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
cs440-acg/results/homework-3/disk.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
cs440-acg/results/homework-3/hsphere.png
Normal file
After Width: | Height: | Size: 23 KiB |
191
cs440-acg/results/homework-3/report.html
Normal file
@@ -0,0 +1,191 @@
|
||||
**Homework 3**
|
||||
|
||||
Student name: Cedric Hölzl
|
||||
|
||||
Sciper number: 257844
|
||||
|
||||
Monte Carlo Sampling (60 pts)
|
||||
=============================
|
||||
|
||||
Tent
|
||||
----
|
||||
$$
|
||||
p(x, y)=(p_1(x)\text{,}
|
||||
p_1(y))
|
||||
\\
|
||||
p_1(t) = \begin{cases}
|
||||
1-|t|, & -1\le t\le 1\\
|
||||
0,&\text{otherwise}\\
|
||||
\end{cases}
|
||||
\\
|
||||
P_1(t) = \begin{cases}
|
||||
1, & t \gt 1\\
|
||||
\frac{1}{2}(t+1)^2 + 1, & 0 \le t \le 1\\
|
||||
\frac{1}{2}(t+1)^2, & -1 \le t \lt 0\\
|
||||
0, & -1 \gt t\\
|
||||
\end{cases}
|
||||
\\
|
||||
P_1^{-1}(t) = \begin{cases}
|
||||
\sqrt{2t}-1, & 0 \le t \lt 0.5\\
|
||||
1 - \sqrt{2(1-t)}, & 0.5 \le t \le 1\\
|
||||
\end{cases}
|
||||
$$
|
||||
<img src="tent.png" alt="Tent X2">
|
||||
|
||||
|
||||
Uniform disk
|
||||
------------
|
||||
$$
|
||||
p(x, y)= (p_1(\sqrt{x}, 2\pi y)\text{,}
|
||||
p_2(\sqrt{x}, 2\pi y))
|
||||
\\
|
||||
p_i(r, \theta) = \begin{cases}
|
||||
r * \cos(\theta), & i = 1\\
|
||||
r * \sin(\theta), & i = 2\\
|
||||
\end{cases}
|
||||
\\
|
||||
P_i^{-1}(x, y) = \begin{cases}
|
||||
0, & x^2 + y^2 \gt 1\\
|
||||
\frac{1}{\pi}, & otherwhise\\
|
||||
\end{cases}
|
||||
$$
|
||||
<img src="disk.png" alt="Disk X2">
|
||||
|
||||
Uniform sphere
|
||||
--------------
|
||||
$$
|
||||
p(x, y)= (p_1(\arccos(1-2x), 2\pi y)\text{,}
|
||||
p_2(\arccos(1-2x), 2\pi y)\text{,}
|
||||
p_3(\arccos(1-2x), 2\pi y))
|
||||
\\
|
||||
p_i(\theta, \phi) = \begin{cases}
|
||||
\sin(\theta)\cos(\phi), & i = 1\\
|
||||
\sin(\theta)\sin(\phi), & i = 2\\
|
||||
\cos(\theta), & i = 3\\
|
||||
\end{cases}
|
||||
\\
|
||||
P_i^{-1}(x, y, z) = \frac{1}{4\pi}
|
||||
$$
|
||||
<img src="sphere.png" alt="Sphere X2">
|
||||
|
||||
Uniform hemisphere
|
||||
------------------
|
||||
$$
|
||||
p(x, y) = (p_1(\arccos(1-x), 2\pi y)\text{,}
|
||||
p_2(\arccos(1-x), 2\pi y)\text{,}
|
||||
p_3(\arccos(1-x), 2\pi y))
|
||||
\\
|
||||
p_i(\theta, \phi) = \begin{cases}
|
||||
\sin(\theta)\cos(\phi), & i = 1\\
|
||||
\sin(\theta)\sin(\phi), & i = 2\\
|
||||
\cos(\theta), & i = 3\\
|
||||
\end{cases}
|
||||
\\
|
||||
P_i^{-1}(x, y, z) = \begin{cases}
|
||||
\frac{1}{2\pi}, & z \ge 0\\
|
||||
0, & otherwhise\\
|
||||
\end{cases}
|
||||
$$
|
||||
<img src="hsphere.png" alt="HemiSphere X2">
|
||||
|
||||
Cosine hemisphere
|
||||
-----------------
|
||||
$$
|
||||
p(x, y)= (p_1(\sqrt{x}, 2\pi y)\text{,}
|
||||
p_2(\sqrt{x}, 2\pi y)\text{,}
|
||||
p_3(\sqrt{x}, 2\pi y))
|
||||
\\
|
||||
p_i(r, \theta) = \begin{cases}
|
||||
r * \cos(\theta), & i = 1\\
|
||||
r * \sin(\theta), & i = 2\\
|
||||
\sqrt{1 - r^2(\cos^2(\theta)-\sin^2(\theta))}, & i = 3\\
|
||||
\end{cases}
|
||||
\\
|
||||
P_i^{-1}(x, y, z) = \begin{cases}
|
||||
\frac{z}{2\pi}, & z \ge 0\\
|
||||
0, & otherwhise\\
|
||||
\end{cases}
|
||||
$$
|
||||
<img src="coshsphere.png" alt="Cosine HemiSphere X2">
|
||||
|
||||
Beckmann distribution
|
||||
---------------------
|
||||
We start with de provided formulas and can build the PDF formula from it (thanks to the mapping hint):
|
||||
$$
|
||||
D(\theta, \phi) = \frac{1}{2\pi}\ \cdot\ \frac{2 e^{\frac{-\tan^2{\theta}}{\alpha^2}}}{\alpha^2 \cos^3 \theta}
|
||||
\\
|
||||
P_i^{-1}(x, y, z, a) = \frac{e^{\frac{-((x^2 + y^2)/(z^2))}{a^2}}}{\pi a^2 z^3}
|
||||
$$
|
||||
To find the warp method, we multiply it by $2\pi$ (equivalent to integrating over $\phi$) and integrate it (as seen in the explanation) to find the following mappings for $\phi$ and $\theta$:
|
||||
$$
|
||||
\theta = \arctan(\sqrt{(-a^2 \log(1-y))})
|
||||
\\
|
||||
\phi = 2\pi x
|
||||
$$
|
||||
So finaly, we can build our warp using those new mappings:
|
||||
$$
|
||||
p(x, y, a) = (p_1(\arctan(\sqrt{(-a^2 \log(1-y))}), 2\pi x)\text{,}\\
|
||||
p_2(\arctan(\sqrt{(-a^2 \log(1-y))}), 2\pi x)\text{,}\\
|
||||
p_3(\arctan(\sqrt{(-a^2 \log(1-y))}), 2\pi x))
|
||||
\\
|
||||
p_i(\theta, \phi) = \begin{cases}
|
||||
\sin(\theta)\cos(\phi), & i = 1\\
|
||||
\sin(\theta)\sin(\phi), & i = 2\\
|
||||
\cos(\theta), & i = 3\\
|
||||
\end{cases}
|
||||
|
||||
$$
|
||||
<img src="beckmann1.png" alt="Beckmann (coef 1.0) X2">
|
||||
<img src="beckmann05.png" alt="Beckmann (coef 0.5) X2">
|
||||
<img src="beckmann005.png" alt="Beckmann (coef 0.05) X2">
|
||||
|
||||
Two simple rendering algorithms (40 pts)
|
||||
========================================
|
||||
|
||||
Point lights
|
||||
------------
|
||||
|
||||
Ajax bust illuminated by a point light source:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="ajax-simple-ref.png" alt="Reference">
|
||||
<img src="ajax-simple.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
We can see that the pictures match.
|
||||
|
||||
Ambient occlusion
|
||||
-----------------
|
||||
|
||||
Ajax bust rendered using ambient occlusion:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="ajax-ao-ref.png" alt="Reference">
|
||||
<img src="ajax-ao.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
We can see that the pictures match.
|
||||
|
||||
Hacker Points
|
||||
-------------
|
||||
I tried implementing the Hierarchical Sample Warping, I couldn't find the issue with the PDF function. Also the matrix to be used is currently entered by hand and not parsed from a file. We use a single array containing the Mipmap until we have only a 2x2 image. I couldnt make it function for a 512x512 image(matrix). Unfortunately I didn't have more time to debug and resolve those issues.
|
||||
|
||||
<img src="HSW.png" alt="Cosine HemiSphere X2">
|
||||
|
||||
|
||||
Feedback
|
||||
========
|
||||
|
||||
* Time taken: 6h designing, had some trouble with some PDF functions, especialy for the HSW. Coding from this was relativly quick and took 4h. Finaly testing was fast since I just needed to run executables.
|
||||
* Sources or documents to help with HSW would have been great, I tried to look on the internet and in the book of the course but found not a great amount of information.
|
||||
* The hardest part was the math. The integrators were easier to implement than expected thanks to the warp functions we made.
|
||||
* I enjoyed seing the points build a grid with disformation(warp).
|
||||
|
||||
<!-- Slider -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="../resources/jquery.event.move.js"></script>
|
||||
<script src="../resources/jquery.twentytwenty.js"></script>
|
||||
<link href="../resources/offcanvas.css" rel="stylesheet">
|
||||
<link href="../resources/twentytwenty.css" rel="stylesheet" type="text/css" />
|
||||
<script>var markdeepOptions = {onLoad: function() {$(".twentytwenty-container").twentytwenty({default_offset_pct: 0.5, move_slider_on_hover: true});} };</script>
|
||||
<!-- Markdeep: -->
|
||||
<script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js?" charset="utf-8"></script>
|
||||
<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>
|
BIN
cs440-acg/results/homework-3/sphere.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
cs440-acg/results/homework-3/tent.png
Normal file
After Width: | Height: | Size: 33 KiB |
0
cs440-acg/results/homework-4/.gitkeep
Normal file
BIN
cs440-acg/results/homework-4/cbox-distributed-ref.png
Normal file
After Width: | Height: | Size: 331 KiB |
BIN
cs440-acg/results/homework-4/cbox-distributed.exr
Normal file
BIN
cs440-acg/results/homework-4/cbox-distributed.png
Normal file
After Width: | Height: | Size: 331 KiB |
BIN
cs440-acg/results/homework-4/cbox-whitted-ref.png
Normal file
After Width: | Height: | Size: 386 KiB |
BIN
cs440-acg/results/homework-4/cbox-whitted.exr
Normal file
BIN
cs440-acg/results/homework-4/cbox-whitted.png
Normal file
After Width: | Height: | Size: 386 KiB |
BIN
cs440-acg/results/homework-4/city.exr
Normal file
BIN
cs440-acg/results/homework-4/city.png
Normal file
After Width: | Height: | Size: 713 KiB |
BIN
cs440-acg/results/homework-4/logo-dielectric-ref.png
Normal file
After Width: | Height: | Size: 432 KiB |
BIN
cs440-acg/results/homework-4/logo-dielectric.exr
Normal file
BIN
cs440-acg/results/homework-4/logo-dielectric.png
Normal file
After Width: | Height: | Size: 432 KiB |
BIN
cs440-acg/results/homework-4/logo-diffuse-ref.png
Normal file
After Width: | Height: | Size: 432 KiB |
BIN
cs440-acg/results/homework-4/logo-diffuse.exr
Normal file
BIN
cs440-acg/results/homework-4/logo-diffuse.png
Normal file
After Width: | Height: | Size: 433 KiB |
94
cs440-acg/results/homework-4/report.html
Normal file
@@ -0,0 +1,94 @@
|
||||
**Homework 4**
|
||||
|
||||
Student name: Cedric Hölzl
|
||||
|
||||
Sciper number: 257844
|
||||
|
||||
Area lights (25 pts)
|
||||
====================
|
||||
|
||||
I used as template other clases, having usualy a `sample()` function along a `eval()` function. Initialy I wasnt using the `eval()` but during refactoring I moved some code into it.
|
||||
|
||||
|
||||
Distribution Ray Tracing (40 pts)
|
||||
=================================
|
||||
|
||||
We can see the images match. There was at one point an issue with the shadows that was tedious to solve, it turned out to be me not using the bsdf sampling properly. Another issue are the furnace tests not passing, I managed to isolate the issue, being the way I handle intersections with "emiters", I didn't take into account the fact that an emiter can be diffuse and I just returned the `radiance` of the latter. Ignoring this special case, results are correct.
|
||||
|
||||
Concerning design decisions, I chose to use the PDF structure to be able to sample emitters easily, the other option being the one one coding myself a way to randomly select one.
|
||||
|
||||
Diffuse logo:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="logo-diffuse-ref.png" alt="Reference">
|
||||
<img src="logo-diffuse.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Cornell box (distributed):
|
||||
<div class="twentytwenty-container">
|
||||
<img src="cbox-distributed-ref.png" alt="Reference">
|
||||
<img src="cbox-distributed.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
|
||||
Dielectrics (25 pts)
|
||||
====================
|
||||
|
||||
No issues or major design choises had to be made to implement dielectrics.
|
||||
|
||||
|
||||
Whitted-style ray tracing (10 pts)
|
||||
==================================
|
||||
|
||||
Dielectric logo:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="logo-dielectric-ref.png" alt="Reference">
|
||||
<img src="logo-dielectric.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Cornell box (Whitted):
|
||||
<div class="twentytwenty-container">
|
||||
<img src="cbox-whitted-ref.png" alt="Reference">
|
||||
<img src="cbox-whitted.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
|
||||
Artist Points (5 pts)
|
||||
=====================
|
||||
|
||||
|
||||
Metropolis (The City):
|
||||
|
||||
<img src="city.png" alt="City Render">
|
||||
|
||||
The objective was to create a minimalistic city with the goal of expressing the `feeling of urbanisation`, using grey as only color with `concrete`, `glass` and `metalic` textures for the towers. The whole scene and 3D models is created by `C. Hölzl` (myself).
|
||||
|
||||
|
||||
Hacker Points (15 pts)
|
||||
======================
|
||||
|
||||
Being Stuck on trying to fix the `furnace` test left no time to work on it.
|
||||
|
||||
Feedback
|
||||
========
|
||||
|
||||
This part of the project is relativly time consuming and leaves space for many small errors that are unfortunately difficult and time-consuming to solve.
|
||||
|
||||
I spent in total 4h designing, 4h coding, 10h testing & debuging.
|
||||
|
||||
It would have been great to have a `wiki` containign images with the common issues with small hints to avoid spamming assistants with questions and have easier time debuging.
|
||||
|
||||
The hard part was mostly finding the small typos I made at multiple places. The ok-part was the angle calculation and logic implementation, which wasnt trivial but still manageable.
|
||||
|
||||
I enjoyed the artist points providing a more fun task in a workload heavy assignment.
|
||||
|
||||
|
||||
<!-- Slider -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="../resources/jquery.event.move.js"></script>
|
||||
<script src="../resources/jquery.twentytwenty.js"></script>
|
||||
<link href="../resources/offcanvas.css" rel="stylesheet">
|
||||
<link href="../resources/twentytwenty.css" rel="stylesheet" type="text/css" />
|
||||
<script>var markdeepOptions = {onLoad: function() {$(".twentytwenty-container").twentytwenty({default_offset_pct: 0.5, move_slider_on_hover: true});} };</script>
|
||||
<!-- Markdeep: -->
|
||||
<script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js?" charset="utf-8"></script>
|
||||
<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>
|
0
cs440-acg/results/homework-5/.gitkeep
Normal file
BIN
cs440-acg/results/homework-5/ajax-rough-ref.png
Normal file
After Width: | Height: | Size: 273 KiB |
BIN
cs440-acg/results/homework-5/ajax-rough.exr
Normal file
BIN
cs440-acg/results/homework-5/ajax-rough.png
Normal file
After Width: | Height: | Size: 273 KiB |
BIN
cs440-acg/results/homework-5/ajax-smooth-ref.png
Normal file
After Width: | Height: | Size: 272 KiB |
BIN
cs440-acg/results/homework-5/ajax-smooth.exr
Normal file
BIN
cs440-acg/results/homework-5/ajax-smooth.png
Normal file
After Width: | Height: | Size: 272 KiB |
BIN
cs440-acg/results/homework-5/cbox_ems-ref.png
Normal file
After Width: | Height: | Size: 967 KiB |
BIN
cs440-acg/results/homework-5/cbox_ems.exr
Normal file
BIN
cs440-acg/results/homework-5/cbox_ems.png
Normal file
After Width: | Height: | Size: 952 KiB |
BIN
cs440-acg/results/homework-5/cbox_mats-ref.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
cs440-acg/results/homework-5/cbox_mats.exr
Normal file
BIN
cs440-acg/results/homework-5/cbox_mats.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
cs440-acg/results/homework-5/cbox_mis-ref.png
Normal file
After Width: | Height: | Size: 1018 KiB |
BIN
cs440-acg/results/homework-5/cbox_mis.exr
Normal file
BIN
cs440-acg/results/homework-5/cbox_mis.png
Normal file
After Width: | Height: | Size: 1005 KiB |
145
cs440-acg/results/homework-5/report.html
Normal file
@@ -0,0 +1,145 @@
|
||||
**Homework 5**
|
||||
|
||||
Student name: Cédric Hölzl
|
||||
|
||||
Sciper number: 257844
|
||||
|
||||
Microfacet BRDF (30 points)
|
||||
===========================
|
||||
|
||||
Evaluating the Microfacet BRDF
|
||||
------------------------------
|
||||
|
||||
In this section, I initialy, implemented the various equation with minimal optimisations, aiming to preserve understandability over optimisations. In a second time I made minor optimisations for exaple avoiding unecessary multiplications in the computation of $X^+$.
|
||||
|
||||
|
||||
Sampling the Microfacet BRDF
|
||||
------------------------------
|
||||
|
||||
The sampling was rather straight forward to implement, with two different cases to handle, diffuse or specular reflection. They were both implemented according to instructions.
|
||||
|
||||
Validation
|
||||
----------
|
||||
|
||||
For the validation both the ajax render match and the $X^2$ warptest test passes.
|
||||
|
||||
Ajax (smooth):
|
||||
<div class="twentytwenty-container">
|
||||
<img src="ajax-smooth-ref.png" alt="Reference">
|
||||
<img src="ajax-smooth.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Ajax (rough):
|
||||
<div class="twentytwenty-container">
|
||||
<img src="ajax-rough-ref.png" alt="Reference">
|
||||
<img src="ajax-rough.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
|
||||
<img src="warptest.png" alt="Mine">
|
||||
|
||||
|
||||
Brute force path tracer (15 points)
|
||||
===================================
|
||||
|
||||
For the implementation, I followed the advice to copy the whitted sampling, made it recurse with a loop (looping while we intersect the scene, and with a russian roulette exit condition). We simplified it by removing some restrictions of the whitted algorithm, only accounting the emitter for the resulting "light" value.
|
||||
|
||||
Validation
|
||||
----------
|
||||
|
||||
We can see that with the exception of the noise, the renders match.
|
||||
|
||||
Cornell box:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="cbox_mats-ref.png" alt="Reference">
|
||||
<img src="cbox_mats.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Veach material test scene:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="veach_mats-ref.png" alt="Reference">
|
||||
<img src="veach_mats.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Table test scene:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="table_mats-ref.png" alt="Reference">
|
||||
<img src="table_mats.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Path tracer with next event estimation (25 points)
|
||||
==================================================
|
||||
|
||||
For the implementation, similar as for the MATS, I followed the advice to copy the whitted sampling, made it recurse with a loop (looping while we intersect the scene, and with similar a russian roulette exit condition). This time we kept and differentiated the diffuse and specular.
|
||||
|
||||
Validation
|
||||
----------
|
||||
|
||||
We can see that with the exception of the noise, the renders match.
|
||||
|
||||
Cornell box:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="cbox_ems-ref.png" alt="Reference">
|
||||
<img src="cbox_ems.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Veach material test scene:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="veach_ems-ref.png" alt="Reference">
|
||||
<img src="veach_ems.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Table test scene:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="table_ems-ref.png" alt="Reference">
|
||||
<img src="table_ems.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Path tracer with Multiple Importance Sampling (30 points)
|
||||
=========================================================
|
||||
|
||||
For the implementation, I merge both the MATS and EMS strategies. I then added the two weight factors and checked that when having one at 0 and the other at 1, I had the results of the MATS and MIS renders respectivly. I then added the proper computation using the required probability functions. I decided to add a PDF function to the mesh to have the probability of sampling a point on that surface.
|
||||
|
||||
Validation
|
||||
----------
|
||||
|
||||
We can see that with the exception of the noise, the renders match.
|
||||
|
||||
Cornell box:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="cbox_mis-ref.png" alt="Reference">
|
||||
<img src="cbox_mis.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Veach material test scene:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="veach_mis-ref.png" alt="Reference">
|
||||
<img src="veach_mis.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Table test scene:
|
||||
<div class="twentytwenty-container">
|
||||
<img src="table_mis-ref.png" alt="Reference">
|
||||
<img src="table_mis.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
Feedback
|
||||
========
|
||||
|
||||
Time spent:
|
||||
* Designing (~5h): Laying out the formulas and thinking on how to write things was relativly straight forward, figuring out how the MIS works took some time
|
||||
* Coding (~5h): Coding was relativly straight forward from the design and formula
|
||||
* Testing (~10h): I had issues with my the weights in my MIS implementation
|
||||
|
||||
It would have been great to have explicitly mentionned that switching the weight from 1.0 & 0.0 would result in having the MATS and EMS render. Figuring out and adding the required PDF used for the weight computation took some time. I found it relativly interesting to write the MATS and EMS from the Whitted implementation, and then mergin them into the MITS. Workload wise it was mostly fine, the main issue was the MIS implementation that took a lot of time to debug.
|
||||
|
||||
<!-- Slider -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="../resources/jquery.event.move.js"></script>
|
||||
<script src="../resources/jquery.twentytwenty.js"></script>
|
||||
<link href="../resources/offcanvas.css" rel="stylesheet">
|
||||
<link href="../resources/twentytwenty.css" rel="stylesheet" type="text/css" />
|
||||
<script>var markdeepOptions = {onLoad: function() {$(".twentytwenty-container").twentytwenty({default_offset_pct: 0.5, move_slider_on_hover: true});} };</script>
|
||||
<!-- Markdeep: -->
|
||||
<script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js?" charset="utf-8"></script>
|
||||
<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>
|
BIN
cs440-acg/results/homework-5/table_ems-ref.png
Normal file
After Width: | Height: | Size: 883 KiB |
BIN
cs440-acg/results/homework-5/table_ems.exr
Normal file
BIN
cs440-acg/results/homework-5/table_ems.png
Normal file
After Width: | Height: | Size: 882 KiB |
BIN
cs440-acg/results/homework-5/table_mats-ref.png
Normal file
After Width: | Height: | Size: 868 KiB |
BIN
cs440-acg/results/homework-5/table_mats.exr
Normal file
BIN
cs440-acg/results/homework-5/table_mats.png
Normal file
After Width: | Height: | Size: 867 KiB |
BIN
cs440-acg/results/homework-5/table_mis-ref.png
Normal file
After Width: | Height: | Size: 841 KiB |
BIN
cs440-acg/results/homework-5/table_mis.exr
Normal file
BIN
cs440-acg/results/homework-5/table_mis.png
Normal file
After Width: | Height: | Size: 836 KiB |
BIN
cs440-acg/results/homework-5/veach_ems-ref.png
Normal file
After Width: | Height: | Size: 616 KiB |
BIN
cs440-acg/results/homework-5/veach_ems.exr
Normal file
BIN
cs440-acg/results/homework-5/veach_ems.png
Normal file
After Width: | Height: | Size: 648 KiB |
BIN
cs440-acg/results/homework-5/veach_mats-ref.png
Normal file
After Width: | Height: | Size: 764 KiB |
BIN
cs440-acg/results/homework-5/veach_mats.exr
Normal file
BIN
cs440-acg/results/homework-5/veach_mats.png
Normal file
After Width: | Height: | Size: 763 KiB |
BIN
cs440-acg/results/homework-5/veach_mis-ref.png
Normal file
After Width: | Height: | Size: 642 KiB |
BIN
cs440-acg/results/homework-5/veach_mis.exr
Normal file
BIN
cs440-acg/results/homework-5/veach_mis.png
Normal file
After Width: | Height: | Size: 678 KiB |
BIN
cs440-acg/results/homework-5/warptest.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
cs440-acg/results/project-proposal/matterhorn.jpg
Normal file
After Width: | Height: | Size: 722 KiB |
83
cs440-acg/results/project-proposal/proposal.html
Normal file
@@ -0,0 +1,83 @@
|
||||
**Final Project Proposal**
|
||||
|
||||
Student name: Cedric Hölzl
|
||||
|
||||
Sciper number: 257844
|
||||
|
||||
|
||||
Scene description and motivation
|
||||
================================
|
||||
|
||||
The idea is to render the famous "Matterhorn", viewed from the "Riffelsee" during the night, with Stone Balancing on one another in the foreground
|
||||
The is achieved by combining/composing the following 3 pictures (taken from Wikipedia):
|
||||
|
||||
<img src="matterhorn.jpg"/>
|
||||
<a href="https://commons.wikimedia.org/wiki/File:Matterhorn%2BRiffelsee.jpg">Source</a>
|
||||
|
||||
|
||||
<img src="sky.jpg"/>
|
||||
<a href="https://commons.wikimedia.org/wiki/File:ESO-VLT-Laser-phot-33a-07.jpg">Source</a>
|
||||
|
||||
<img src="rocks.jpg"/>
|
||||
<a href="https://commons.wikimedia.org/wiki/File:Stone_Balancing_Sunset.jpg">Source</a>
|
||||
|
||||
|
||||
Feature list
|
||||
============
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Feature</th>
|
||||
<th>Standard point count</th>
|
||||
<th>Adjusted point count</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Public Dataset</td>
|
||||
<td>10</td>
|
||||
<td>10</td>
|
||||
<td>
|
||||
<a href="https://svs.gsfc.nasa.gov/4851">NASA Star Map</a>,
|
||||
<a href="https://www.swisstopo.admin.ch/de/geodata/height/dhm25.html">Swiss Height Map</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Textures</td>
|
||||
<td>10</td>
|
||||
<td>10</td>
|
||||
<td>Skybox, Stone, Ground</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Image-Based Illumination</td>
|
||||
<td>30</td>
|
||||
<td>30</td>
|
||||
<td>Skybox Star Light (Infinite Area Light)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Advanced BRDF/BSDF</td>
|
||||
<td>30</td>
|
||||
<td>30</td>
|
||||
<td>Disney BSDF</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><strong>Total</strong></td>
|
||||
<td>80</td>
|
||||
<td>80</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<!-- Slider -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="../resources/jquery.event.move.js"></script>
|
||||
<script src="../resources/jquery.twentytwenty.js"></script>
|
||||
<link href="../resources/offcanvas.css" rel="stylesheet">
|
||||
<link href="../resources/twentytwenty.css" rel="stylesheet" type="text/css" />
|
||||
<script>var markdeepOptions = {onLoad: function() {$(".twentytwenty-container").twentytwenty({default_offset_pct: 0.5, move_slider_on_hover: true});} };</script>
|
||||
<!-- Markdeep: -->
|
||||
<script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js?" charset="utf-8"></script>
|
||||
<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>
|
BIN
cs440-acg/results/project-proposal/rocks.jpg
Normal file
After Width: | Height: | Size: 3.9 MiB |
BIN
cs440-acg/results/project-proposal/sky.jpg
Normal file
After Width: | Height: | Size: 244 KiB |
BIN
cs440-acg/results/project/disney.png
Normal file
After Width: | Height: | Size: 1.9 MiB |
BIN
cs440-acg/results/project/envmap.png
Normal file
After Width: | Height: | Size: 703 KiB |
BIN
cs440-acg/results/project/envmap_ref.png
Normal file
After Width: | Height: | Size: 2.1 MiB |
BIN
cs440-acg/results/project/final-A.png
Normal file
After Width: | Height: | Size: 6.4 MiB |
BIN
cs440-acg/results/project/hsw_panorama.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
cs440-acg/results/project/hsw_panorama_2.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
cs440-acg/results/project/hsw_panorama_orig.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
cs440-acg/results/project/hsw_sample2.png
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
cs440-acg/results/project/os.png
Normal file
After Width: | Height: | Size: 875 KiB |
107
cs440-acg/results/project/report.html
Normal file
@@ -0,0 +1,107 @@
|
||||
**Final Report**
|
||||
|
||||
Student name: Cédric Hölzl
|
||||
|
||||
Sciper number: 257844
|
||||
|
||||
|
||||
Public Dataset (10 points)
|
||||
==========================
|
||||
|
||||
We used high resolution skymap image available from the NASA website. We also used the elevation map of switzerland availabe from OpenTopography. For the elevation map, we used the BlenderGIS plugin that can generate a mesh with a texture from an elevation map and google satelite images.
|
||||
|
||||
We can see that the terrain is properly generated with a mostly correct shape. We had to make slight modifications and corrections to "smoothe" and "rougthen" the terrain, since the elevation data does not have high resolution (30m). For instance the peak of the Matterhorn has invalid or missing data and had to be reconstructed by hand.
|
||||
|
||||
<img src="os.png" alt="Terrain">
|
||||
|
||||
|
||||
Textures (10 points)
|
||||
====================
|
||||
|
||||
To implement textures, we added a new object type to Nori, namely ETexture, and a class implementing it where we can sample a point on the image at the given coordinates.
|
||||
|
||||
We modified the BSDF record structure to hold the UV coordinates, in order to avoid having to pass a new argument when we sample a BSDF. We modified the various BSDF allowing them to take an image path instead of a color (albedo), they instantiate the texture if an image is passed as property and sample it instead of using the color.
|
||||
|
||||
We modified the table render from homework-5 and added a wood texture (from GIMP) to the table object.
|
||||
|
||||
<img src="table_mis.png" alt="Texture">
|
||||
|
||||
Image-Based Illumination (30 points)
|
||||
====================================
|
||||
|
||||
Sample Warping
|
||||
--------------
|
||||
|
||||
We implemented sample warping to adjust for the image provided. We sample the whole image and use linear interpolations to warp samples, and use a mipmap to store the image.
|
||||
|
||||
<img src="hsw_panorama.png" alt="WarpA">
|
||||
<img src="hsw_panorama_2.png" alt="WarpB">
|
||||
|
||||
We can see that the warping even when passing the tests, mostly match.
|
||||
|
||||
Sampling
|
||||
--------
|
||||
We modified the various Integrators we built previously, in order to add support for Image-Based Illumination, when a ray exits the scene, we sample the envmap, similarly to a texture projected on a sphere. We added a infinite_area (envmap) class for this task.
|
||||
|
||||
Results
|
||||
-------
|
||||
We rendered a scene with a diffuse sphere on the left (using disney BSDF for our render and roughtconductor from the mitsuba reference render), followed by a dielectric ajax head and finaly a mirror ball to show that the skybox is also present behind the camera and matches.
|
||||
|
||||
<div class="twentytwenty-container">
|
||||
<img src="envmap_ref.png" alt="Reference">
|
||||
<img src="envmap.png" alt="Mine">
|
||||
</div>
|
||||
|
||||
We can see that the images match, except for the Disney BSDF and RoughConductor balls are slightly different (due to disney BSDF not being perfectly equivalent to a rougth conductor in mitsuba).
|
||||
|
||||
Disney BRDF (30 points)
|
||||
=========================
|
||||
|
||||
We used multiple references to help us with implementing the BRDF.
|
||||
[Disney2012](https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf)
|
||||
[Disney2015](https://blog.selfshadow.com/publications/s2015-shading-course/burley/s2015_pbs_disney_bsdf_notes.pdf)
|
||||
|
||||
Sample Warping
|
||||
--------------
|
||||
|
||||
We implemented two warping functions for the Disney BRDF, GTR (Generalized-Trowbridge-Reitz) with $\sigma=1$ and $\sigma=2$ as seen in the disney paper.
|
||||
|
||||
<img src="warp_gtr1.png" alt="WarpGTR1">
|
||||
<img src="warp_gtr2.png" alt="WarpGTR2">
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
The implementation resides in a single BSDF file. We compute the resulting "color" similarly to how the paper presented it. We encountered some issues, where we missed a multiplication by cosTheta resulting in dark areas (for specular) where light is supposed to be (and similarly, a multiplication using a pdf was also missing). Those issues are however fixed now.
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Unfortunately, I was unable to get the anisotropic factor working, it would have been interesting to use it on the water in the final render. The sheen should be implemented properly, I cross checked with the paper and PBRT but was unable to see why it would not display for the test scene below.
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
We tried to create a scene similar to the one on page 13 of the provided document ([Disney2012](https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf)). In the Image below each column makes the value of a given parameter fluctuate from 0.1 to 1.0 by increments of 0.1. The rows are in order as follows: subsurface, metallic, specular, specular tint, roughness, sheen, sheen tint, clearcoat, clearcoat gloss.
|
||||
|
||||
<img src="disney.png" alt="Disney">
|
||||
|
||||
When comparing both images, we can clearly see that roughness and metallic render properly. A bit less clearly that specular, specular tint, clearcoat, clearcoat gloss, also seems to produce the desired effect. However, neither sheen nor sheen tint show any clear difference.
|
||||
|
||||
Unfortunately neither blender or Mitsuba have a disney BSDF implemented which would have been useful for more precise validation and comparison. As seen previously with the envmap, using an other bsdf with similar parameters results in a very different result.
|
||||
....
|
||||
|
||||
Final Scene
|
||||
========
|
||||
|
||||
<img src="final-A.png" alt="Final Render">
|
||||
|
||||
<!-- Slider -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="../resources/jquery.event.move.js"></script>
|
||||
<script src="../resources/jquery.twentytwenty.js"></script>
|
||||
<link href="../resources/offcanvas.css" rel="stylesheet">
|
||||
<link href="../resources/twentytwenty.css" rel="stylesheet" type="text/css" />
|
||||
<script>var markdeepOptions = {onLoad: function() {$(".twentytwenty-container").twentytwenty({default_offset_pct: 0.5, move_slider_on_hover: true});} };</script>
|
||||
<!-- Markdeep: -->
|
||||
<script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js?" charset="utf-8"></script>
|
||||
<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>
|
BIN
cs440-acg/results/project/table_mis.exr
Normal file
BIN
cs440-acg/results/project/table_mis.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
cs440-acg/results/project/warp_gtr1.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
cs440-acg/results/project/warp_gtr2.png
Normal file
After Width: | Height: | Size: 20 KiB |
599
cs440-acg/results/resources/jquery.event.move.js
Normal file
@@ -0,0 +1,599 @@
|
||||
// DOM.event.move
|
||||
//
|
||||
// 2.0.0
|
||||
//
|
||||
// Stephen Band
|
||||
//
|
||||
// Triggers 'movestart', 'move' and 'moveend' events after
|
||||
// mousemoves following a mousedown cross a distance threshold,
|
||||
// similar to the native 'dragstart', 'drag' and 'dragend' events.
|
||||
// Move events are throttled to animation frames. Move event objects
|
||||
// have the properties:
|
||||
//
|
||||
// pageX:
|
||||
// pageY: Page coordinates of pointer.
|
||||
// startX:
|
||||
// startY: Page coordinates of pointer at movestart.
|
||||
// distX:
|
||||
// distY: Distance the pointer has moved since movestart.
|
||||
// deltaX:
|
||||
// deltaY: Distance the finger has moved since last event.
|
||||
// velocityX:
|
||||
// velocityY: Average velocity over last few events.
|
||||
|
||||
|
||||
(function(fn) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define([], fn);
|
||||
} else if ((typeof module !== "undefined" && module !== null) && module.exports) {
|
||||
module.exports = fn;
|
||||
} else {
|
||||
fn();
|
||||
}
|
||||
})(function(){
|
||||
var assign = Object.assign || window.jQuery && jQuery.extend;
|
||||
|
||||
// Number of pixels a pressed pointer travels before movestart
|
||||
// event is fired.
|
||||
var threshold = 8;
|
||||
|
||||
// Shim for requestAnimationFrame, falling back to timer. See:
|
||||
// see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
||||
var requestFrame = (function(){
|
||||
return (
|
||||
window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function(fn, element){
|
||||
return window.setTimeout(function(){
|
||||
fn();
|
||||
}, 25);
|
||||
}
|
||||
);
|
||||
})();
|
||||
|
||||
// Shim for customEvent
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||
(function () {
|
||||
if ( typeof window.CustomEvent === "function" ) return false;
|
||||
function CustomEvent ( event, params ) {
|
||||
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
||||
var evt = document.createEvent( 'CustomEvent' );
|
||||
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
|
||||
return evt;
|
||||
}
|
||||
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
window.CustomEvent = CustomEvent;
|
||||
})();
|
||||
|
||||
var ignoreTags = {
|
||||
textarea: true,
|
||||
input: true,
|
||||
select: true,
|
||||
button: true
|
||||
};
|
||||
|
||||
var mouseevents = {
|
||||
move: 'mousemove',
|
||||
cancel: 'mouseup dragstart',
|
||||
end: 'mouseup'
|
||||
};
|
||||
|
||||
var touchevents = {
|
||||
move: 'touchmove',
|
||||
cancel: 'touchend',
|
||||
end: 'touchend'
|
||||
};
|
||||
|
||||
var rspaces = /\s+/;
|
||||
|
||||
|
||||
// DOM Events
|
||||
|
||||
var eventOptions = { bubbles: true, cancelable: true };
|
||||
|
||||
var eventsSymbol = typeof Symbol === "function" ? Symbol('events') : {};
|
||||
|
||||
function createEvent(type) {
|
||||
return new CustomEvent(type, eventOptions);
|
||||
}
|
||||
|
||||
function getEvents(node) {
|
||||
return node[eventsSymbol] || (node[eventsSymbol] = {});
|
||||
}
|
||||
|
||||
function on(node, types, fn, data, selector) {
|
||||
types = types.split(rspaces);
|
||||
|
||||
var events = getEvents(node);
|
||||
var i = types.length;
|
||||
var handlers, type;
|
||||
|
||||
function handler(e) { fn(e, data); }
|
||||
|
||||
while (i--) {
|
||||
type = types[i];
|
||||
handlers = events[type] || (events[type] = []);
|
||||
handlers.push([fn, handler]);
|
||||
node.addEventListener(type, handler);
|
||||
}
|
||||
}
|
||||
|
||||
function off(node, types, fn, selector) {
|
||||
types = types.split(rspaces);
|
||||
|
||||
var events = getEvents(node);
|
||||
var i = types.length;
|
||||
var type, handlers, k;
|
||||
|
||||
if (!events) { return; }
|
||||
|
||||
while (i--) {
|
||||
type = types[i];
|
||||
handlers = events[type];
|
||||
if (!handlers) { continue; }
|
||||
k = handlers.length;
|
||||
while (k--) {
|
||||
if (handlers[k][0] === fn) {
|
||||
node.removeEventListener(type, handlers[k][1]);
|
||||
handlers.splice(k, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function trigger(node, type, properties) {
|
||||
// Don't cache events. It prevents you from triggering an event of a
|
||||
// given type from inside the handler of another event of that type.
|
||||
var event = createEvent(type);
|
||||
if (properties) { assign(event, properties); }
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
function Timer(fn){
|
||||
var callback = fn,
|
||||
active = false,
|
||||
running = false;
|
||||
|
||||
function trigger(time) {
|
||||
if (active){
|
||||
callback();
|
||||
requestFrame(trigger);
|
||||
running = true;
|
||||
active = false;
|
||||
}
|
||||
else {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.kick = function(fn) {
|
||||
active = true;
|
||||
if (!running) { trigger(); }
|
||||
};
|
||||
|
||||
this.end = function(fn) {
|
||||
var cb = callback;
|
||||
|
||||
if (!fn) { return; }
|
||||
|
||||
// If the timer is not running, simply call the end callback.
|
||||
if (!running) {
|
||||
fn();
|
||||
}
|
||||
// If the timer is running, and has been kicked lately, then
|
||||
// queue up the current callback and the end callback, otherwise
|
||||
// just the end callback.
|
||||
else {
|
||||
callback = active ?
|
||||
function(){ cb(); fn(); } :
|
||||
fn ;
|
||||
|
||||
active = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Functions
|
||||
|
||||
function noop() {}
|
||||
|
||||
function preventDefault(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function isIgnoreTag(e) {
|
||||
return !!ignoreTags[e.target.tagName.toLowerCase()];
|
||||
}
|
||||
|
||||
function isPrimaryButton(e) {
|
||||
// Ignore mousedowns on any button other than the left (or primary)
|
||||
// mouse button, or when a modifier key is pressed.
|
||||
return (e.which === 1 && !e.ctrlKey && !e.altKey);
|
||||
}
|
||||
|
||||
function identifiedTouch(touchList, id) {
|
||||
var i, l;
|
||||
|
||||
if (touchList.identifiedTouch) {
|
||||
return touchList.identifiedTouch(id);
|
||||
}
|
||||
|
||||
// touchList.identifiedTouch() does not exist in
|
||||
// webkit yet… we must do the search ourselves...
|
||||
|
||||
i = -1;
|
||||
l = touchList.length;
|
||||
|
||||
while (++i < l) {
|
||||
if (touchList[i].identifier === id) {
|
||||
return touchList[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changedTouch(e, data) {
|
||||
var touch = identifiedTouch(e.changedTouches, data.identifier);
|
||||
|
||||
// This isn't the touch you're looking for.
|
||||
if (!touch) { return; }
|
||||
|
||||
// Chrome Android (at least) includes touches that have not
|
||||
// changed in e.changedTouches. That's a bit annoying. Check
|
||||
// that this touch has changed.
|
||||
if (touch.pageX === data.pageX && touch.pageY === data.pageY) { return; }
|
||||
|
||||
return touch;
|
||||
}
|
||||
|
||||
|
||||
// Handlers that decide when the first movestart is triggered
|
||||
|
||||
function mousedown(e){
|
||||
// Ignore non-primary buttons
|
||||
if (!isPrimaryButton(e)) { return; }
|
||||
|
||||
// Ignore form and interactive elements
|
||||
if (isIgnoreTag(e)) { return; }
|
||||
|
||||
on(document, mouseevents.move, mousemove, e);
|
||||
on(document, mouseevents.cancel, mouseend, e);
|
||||
}
|
||||
|
||||
function mousemove(e, data){
|
||||
checkThreshold(e, data, e, removeMouse);
|
||||
}
|
||||
|
||||
function mouseend(e, data) {
|
||||
removeMouse();
|
||||
}
|
||||
|
||||
function removeMouse() {
|
||||
off(document, mouseevents.move, mousemove);
|
||||
off(document, mouseevents.cancel, mouseend);
|
||||
}
|
||||
|
||||
function touchstart(e) {
|
||||
// Don't get in the way of interaction with form elements
|
||||
if (ignoreTags[e.target.tagName.toLowerCase()]) { return; }
|
||||
|
||||
var touch = e.changedTouches[0];
|
||||
|
||||
// iOS live updates the touch objects whereas Android gives us copies.
|
||||
// That means we can't trust the touchstart object to stay the same,
|
||||
// so we must copy the data. This object acts as a template for
|
||||
// movestart, move and moveend event objects.
|
||||
var data = {
|
||||
target: touch.target,
|
||||
pageX: touch.pageX,
|
||||
pageY: touch.pageY,
|
||||
identifier: touch.identifier,
|
||||
|
||||
// The only way to make handlers individually unbindable is by
|
||||
// making them unique.
|
||||
touchmove: function(e, data) { touchmove(e, data); },
|
||||
touchend: function(e, data) { touchend(e, data); }
|
||||
};
|
||||
|
||||
on(document, touchevents.move, data.touchmove, data);
|
||||
on(document, touchevents.cancel, data.touchend, data);
|
||||
}
|
||||
|
||||
function touchmove(e, data) {
|
||||
var touch = changedTouch(e, data);
|
||||
if (!touch) { return; }
|
||||
checkThreshold(e, data, touch, removeTouch);
|
||||
}
|
||||
|
||||
function touchend(e, data) {
|
||||
var touch = identifiedTouch(e.changedTouches, data.identifier);
|
||||
if (!touch) { return; }
|
||||
removeTouch(data);
|
||||
}
|
||||
|
||||
function removeTouch(data) {
|
||||
off(document, touchevents.move, data.touchmove);
|
||||
off(document, touchevents.cancel, data.touchend);
|
||||
}
|
||||
|
||||
function checkThreshold(e, data, touch, fn) {
|
||||
var distX = touch.pageX - data.pageX;
|
||||
var distY = touch.pageY - data.pageY;
|
||||
|
||||
// Do nothing if the threshold has not been crossed.
|
||||
if ((distX * distX) + (distY * distY) < (threshold * threshold)) { return; }
|
||||
|
||||
triggerStart(e, data, touch, distX, distY, fn);
|
||||
}
|
||||
|
||||
function triggerStart(e, data, touch, distX, distY, fn) {
|
||||
var touches = e.targetTouches;
|
||||
var time = e.timeStamp - data.timeStamp;
|
||||
|
||||
// Create a movestart object with some special properties that
|
||||
// are passed only to the movestart handlers.
|
||||
var template = {
|
||||
altKey: e.altKey,
|
||||
ctrlKey: e.ctrlKey,
|
||||
shiftKey: e.shiftKey,
|
||||
startX: data.pageX,
|
||||
startY: data.pageY,
|
||||
distX: distX,
|
||||
distY: distY,
|
||||
deltaX: distX,
|
||||
deltaY: distY,
|
||||
pageX: touch.pageX,
|
||||
pageY: touch.pageY,
|
||||
velocityX: distX / time,
|
||||
velocityY: distY / time,
|
||||
identifier: data.identifier,
|
||||
targetTouches: touches,
|
||||
finger: touches ? touches.length : 1,
|
||||
enableMove: function() {
|
||||
this.moveEnabled = true;
|
||||
this.enableMove = noop;
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
// Trigger the movestart event.
|
||||
trigger(data.target, 'movestart', template);
|
||||
|
||||
// Unbind handlers that tracked the touch or mouse up till now.
|
||||
fn(data);
|
||||
}
|
||||
|
||||
|
||||
// Handlers that control what happens following a movestart
|
||||
|
||||
function activeMousemove(e, data) {
|
||||
var timer = data.timer;
|
||||
|
||||
data.touch = e;
|
||||
data.timeStamp = e.timeStamp;
|
||||
timer.kick();
|
||||
}
|
||||
|
||||
function activeMouseend(e, data) {
|
||||
var target = data.target;
|
||||
var event = data.event;
|
||||
var timer = data.timer;
|
||||
|
||||
removeActiveMouse();
|
||||
|
||||
endEvent(target, event, timer, function() {
|
||||
// Unbind the click suppressor, waiting until after mouseup
|
||||
// has been handled.
|
||||
setTimeout(function(){
|
||||
off(target, 'click', preventDefault);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function removeActiveMouse() {
|
||||
off(document, mouseevents.move, activeMousemove);
|
||||
off(document, mouseevents.end, activeMouseend);
|
||||
}
|
||||
|
||||
function activeTouchmove(e, data) {
|
||||
var event = data.event;
|
||||
var timer = data.timer;
|
||||
var touch = changedTouch(e, event);
|
||||
|
||||
if (!touch) { return; }
|
||||
|
||||
// Stop the interface from gesturing
|
||||
e.preventDefault();
|
||||
|
||||
event.targetTouches = e.targetTouches;
|
||||
data.touch = touch;
|
||||
data.timeStamp = e.timeStamp;
|
||||
|
||||
timer.kick();
|
||||
}
|
||||
|
||||
function activeTouchend(e, data) {
|
||||
var target = data.target;
|
||||
var event = data.event;
|
||||
var timer = data.timer;
|
||||
var touch = identifiedTouch(e.changedTouches, event.identifier);
|
||||
|
||||
// This isn't the touch you're looking for.
|
||||
if (!touch) { return; }
|
||||
|
||||
removeActiveTouch(data);
|
||||
endEvent(target, event, timer);
|
||||
}
|
||||
|
||||
function removeActiveTouch(data) {
|
||||
off(document, touchevents.move, data.activeTouchmove);
|
||||
off(document, touchevents.end, data.activeTouchend);
|
||||
}
|
||||
|
||||
|
||||
// Logic for triggering move and moveend events
|
||||
|
||||
function updateEvent(event, touch, timeStamp) {
|
||||
var time = timeStamp - event.timeStamp;
|
||||
|
||||
event.distX = touch.pageX - event.startX;
|
||||
event.distY = touch.pageY - event.startY;
|
||||
event.deltaX = touch.pageX - event.pageX;
|
||||
event.deltaY = touch.pageY - event.pageY;
|
||||
|
||||
// Average the velocity of the last few events using a decay
|
||||
// curve to even out spurious jumps in values.
|
||||
event.velocityX = 0.3 * event.velocityX + 0.7 * event.deltaX / time;
|
||||
event.velocityY = 0.3 * event.velocityY + 0.7 * event.deltaY / time;
|
||||
event.pageX = touch.pageX;
|
||||
event.pageY = touch.pageY;
|
||||
}
|
||||
|
||||
function endEvent(target, event, timer, fn) {
|
||||
timer.end(function(){
|
||||
trigger(target, 'moveend', event);
|
||||
return fn && fn();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Set up the DOM
|
||||
|
||||
function movestart(e) {
|
||||
if (e.defaultPrevented) { return; }
|
||||
if (!e.moveEnabled) { return; }
|
||||
|
||||
var event = {
|
||||
startX: e.startX,
|
||||
startY: e.startY,
|
||||
pageX: e.pageX,
|
||||
pageY: e.pageY,
|
||||
distX: e.distX,
|
||||
distY: e.distY,
|
||||
deltaX: e.deltaX,
|
||||
deltaY: e.deltaY,
|
||||
velocityX: e.velocityX,
|
||||
velocityY: e.velocityY,
|
||||
identifier: e.identifier,
|
||||
targetTouches: e.targetTouches,
|
||||
finger: e.finger
|
||||
};
|
||||
|
||||
var data = {
|
||||
target: e.target,
|
||||
event: event,
|
||||
timer: new Timer(update),
|
||||
touch: undefined,
|
||||
timeStamp: e.timeStamp
|
||||
};
|
||||
|
||||
function update(time) {
|
||||
updateEvent(event, data.touch, data.timeStamp);
|
||||
trigger(data.target, 'move', event);
|
||||
}
|
||||
|
||||
if (e.identifier === undefined) {
|
||||
// We're dealing with a mouse event.
|
||||
// Stop clicks from propagating during a move
|
||||
on(e.target, 'click', preventDefault);
|
||||
on(document, mouseevents.move, activeMousemove, data);
|
||||
on(document, mouseevents.end, activeMouseend, data);
|
||||
}
|
||||
else {
|
||||
// In order to unbind correct handlers they have to be unique
|
||||
data.activeTouchmove = function(e, data) { activeTouchmove(e, data); };
|
||||
data.activeTouchend = function(e, data) { activeTouchend(e, data); };
|
||||
|
||||
// We're dealing with a touch.
|
||||
on(document, touchevents.move, data.activeTouchmove, data);
|
||||
on(document, touchevents.end, data.activeTouchend, data);
|
||||
}
|
||||
}
|
||||
|
||||
on(document, 'mousedown', mousedown);
|
||||
on(document, 'touchstart', touchstart);
|
||||
on(document, 'movestart', movestart);
|
||||
|
||||
|
||||
// jQuery special events
|
||||
//
|
||||
// jQuery event objects are copies of DOM event objects. They need
|
||||
// a little help copying the move properties across.
|
||||
|
||||
if (!window.jQuery) { return; }
|
||||
|
||||
var properties = ("startX startY pageX pageY distX distY deltaX deltaY velocityX velocityY").split(' ');
|
||||
|
||||
function enableMove1(e) { e.enableMove(); }
|
||||
function enableMove2(e) { e.enableMove(); }
|
||||
function enableMove3(e) { e.enableMove(); }
|
||||
|
||||
function add(handleObj) {
|
||||
var handler = handleObj.handler;
|
||||
|
||||
handleObj.handler = function(e) {
|
||||
// Copy move properties across from originalEvent
|
||||
var i = properties.length;
|
||||
var property;
|
||||
|
||||
while(i--) {
|
||||
property = properties[i];
|
||||
e[property] = e.originalEvent[property];
|
||||
}
|
||||
|
||||
handler.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
jQuery.event.special.movestart = {
|
||||
setup: function() {
|
||||
// Movestart must be enabled to allow other move events
|
||||
on(this, 'movestart', enableMove1);
|
||||
|
||||
// Do listen to DOM events
|
||||
return false;
|
||||
},
|
||||
|
||||
teardown: function() {
|
||||
off(this, 'movestart', enableMove1);
|
||||
return false;
|
||||
},
|
||||
|
||||
add: add
|
||||
};
|
||||
|
||||
jQuery.event.special.move = {
|
||||
setup: function() {
|
||||
on(this, 'movestart', enableMove2);
|
||||
return false;
|
||||
},
|
||||
|
||||
teardown: function() {
|
||||
off(this, 'movestart', enableMove2);
|
||||
return false;
|
||||
},
|
||||
|
||||
add: add
|
||||
};
|
||||
|
||||
jQuery.event.special.moveend = {
|
||||
setup: function() {
|
||||
on(this, 'movestart', enableMove3);
|
||||
return false;
|
||||
},
|
||||
|
||||
teardown: function() {
|
||||
off(this, 'movestart', enableMove3);
|
||||
return false;
|
||||
},
|
||||
|
||||
add: add
|
||||
};
|
||||
});
|
151
cs440-acg/results/resources/jquery.twentytwenty.js
Normal file
@@ -0,0 +1,151 @@
|
||||
(function($){
|
||||
|
||||
$.fn.twentytwenty = function(options) {
|
||||
var options = $.extend({
|
||||
default_offset_pct: 0.5,
|
||||
orientation: 'horizontal',
|
||||
before_label: 'Before',
|
||||
after_label: 'After',
|
||||
no_overlay: false,
|
||||
move_slider_on_hover: false,
|
||||
move_with_handle_only: true,
|
||||
click_to_move: false
|
||||
}, options);
|
||||
|
||||
return this.each(function() {
|
||||
|
||||
var sliderPct = options.default_offset_pct;
|
||||
var container = $(this);
|
||||
var sliderOrientation = options.orientation;
|
||||
var beforeDirection = (sliderOrientation === 'vertical') ? 'down' : 'left';
|
||||
var afterDirection = (sliderOrientation === 'vertical') ? 'up' : 'right';
|
||||
|
||||
|
||||
container.wrap("<div class='twentytwenty-wrapper twentytwenty-" + sliderOrientation + "'></div>");
|
||||
if(!options.no_overlay) {
|
||||
container.append("<div class='twentytwenty-overlay'></div>");
|
||||
var overlay = container.find(".twentytwenty-overlay");
|
||||
overlay.append("<div class='twentytwenty-before-label' data-content='"+options.before_label+"'></div>");
|
||||
overlay.append("<div class='twentytwenty-after-label' data-content='"+options.after_label+"'></div>");
|
||||
}
|
||||
var beforeImg = container.find("img:first");
|
||||
var afterImg = container.find("img:last");
|
||||
container.append("<div class='twentytwenty-handle'></div>");
|
||||
var slider = container.find(".twentytwenty-handle");
|
||||
slider.append("<span class='twentytwenty-" + beforeDirection + "-arrow'></span>");
|
||||
slider.append("<span class='twentytwenty-" + afterDirection + "-arrow'></span>");
|
||||
container.addClass("twentytwenty-container");
|
||||
beforeImg.addClass("twentytwenty-before");
|
||||
afterImg.addClass("twentytwenty-after");
|
||||
|
||||
var calcOffset = function(dimensionPct) {
|
||||
var w = beforeImg.width();
|
||||
var h = beforeImg.height();
|
||||
return {
|
||||
w: w+"px",
|
||||
h: h+"px",
|
||||
cw: (dimensionPct*w)+"px",
|
||||
ch: (dimensionPct*h)+"px"
|
||||
};
|
||||
};
|
||||
|
||||
var adjustContainer = function(offset) {
|
||||
if (sliderOrientation === 'vertical') {
|
||||
beforeImg.css("clip", "rect(0,"+offset.w+","+offset.ch+",0)");
|
||||
afterImg.css("clip", "rect("+offset.ch+","+offset.w+","+offset.h+",0)");
|
||||
}
|
||||
else {
|
||||
beforeImg.css("clip", "rect(0,"+offset.cw+","+offset.h+",0)");
|
||||
afterImg.css("clip", "rect(0,"+offset.w+","+offset.h+","+offset.cw+")");
|
||||
}
|
||||
container.css("height", offset.h);
|
||||
};
|
||||
|
||||
var adjustSlider = function(pct) {
|
||||
var offset = calcOffset(pct);
|
||||
slider.css((sliderOrientation==="vertical") ? "top" : "left", (sliderOrientation==="vertical") ? offset.ch : offset.cw);
|
||||
adjustContainer(offset);
|
||||
};
|
||||
|
||||
// Return the number specified or the min/max number if it outside the range given.
|
||||
var minMaxNumber = function(num, min, max) {
|
||||
return Math.max(min, Math.min(max, num));
|
||||
};
|
||||
|
||||
// Calculate the slider percentage based on the position.
|
||||
var getSliderPercentage = function(positionX, positionY) {
|
||||
var sliderPercentage = (sliderOrientation === 'vertical') ?
|
||||
(positionY-offsetY)/imgHeight :
|
||||
(positionX-offsetX)/imgWidth;
|
||||
|
||||
return minMaxNumber(sliderPercentage, 0, 1);
|
||||
};
|
||||
|
||||
|
||||
$(window).on("resize.twentytwenty", function(e) {
|
||||
adjustSlider(sliderPct);
|
||||
});
|
||||
|
||||
var offsetX = 0;
|
||||
var offsetY = 0;
|
||||
var imgWidth = 0;
|
||||
var imgHeight = 0;
|
||||
var onMoveStart = function(e) {
|
||||
if (((e.distX > e.distY && e.distX < -e.distY) || (e.distX < e.distY && e.distX > -e.distY)) && sliderOrientation !== 'vertical') {
|
||||
e.preventDefault();
|
||||
}
|
||||
else if (((e.distX < e.distY && e.distX < -e.distY) || (e.distX > e.distY && e.distX > -e.distY)) && sliderOrientation === 'vertical') {
|
||||
e.preventDefault();
|
||||
}
|
||||
container.addClass("active");
|
||||
offsetX = container.offset().left;
|
||||
offsetY = container.offset().top;
|
||||
imgWidth = beforeImg.width();
|
||||
imgHeight = beforeImg.height();
|
||||
};
|
||||
var onMove = function(e) {
|
||||
if (container.hasClass("active")) {
|
||||
sliderPct = getSliderPercentage(e.pageX, e.pageY);
|
||||
adjustSlider(sliderPct);
|
||||
}
|
||||
};
|
||||
var onMoveEnd = function() {
|
||||
container.removeClass("active");
|
||||
};
|
||||
|
||||
var moveTarget = options.move_with_handle_only ? slider : container;
|
||||
moveTarget.on("movestart",onMoveStart);
|
||||
moveTarget.on("move",onMove);
|
||||
moveTarget.on("moveend",onMoveEnd);
|
||||
|
||||
if (options.move_slider_on_hover) {
|
||||
container.on("mouseenter", onMoveStart);
|
||||
container.on("mousemove", onMove);
|
||||
container.on("mouseleave", onMoveEnd);
|
||||
}
|
||||
|
||||
slider.on("touchmove", function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
container.find("img").on("mousedown", function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
if (options.click_to_move) {
|
||||
container.on('click', function(e) {
|
||||
offsetX = container.offset().left;
|
||||
offsetY = container.offset().top;
|
||||
imgWidth = beforeImg.width();
|
||||
imgHeight = beforeImg.height();
|
||||
|
||||
sliderPct = getSliderPercentage(e.pageX, e.pageY);
|
||||
adjustSlider(sliderPct);
|
||||
});
|
||||
}
|
||||
|
||||
$(window).trigger("resize.twentytwenty");
|
||||
});
|
||||
};
|
||||
|
||||
})(jQuery);
|
79
cs440-acg/results/resources/offcanvas.css
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Style tweaks
|
||||
* --------------------------------------------------
|
||||
*/
|
||||
html,
|
||||
body {
|
||||
overflow-x: hidden; /* Prevent scroll on narrow devices */
|
||||
}
|
||||
body {
|
||||
padding-top: 70px;
|
||||
}
|
||||
footer {
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border: 1px solid gray;
|
||||
text-align: center;
|
||||
}
|
||||
table tr {
|
||||
border-bottom: 1px solid gray;
|
||||
}
|
||||
table th {
|
||||
padding: 0.5em;
|
||||
border-bottom: 1px solid gray;
|
||||
}
|
||||
table td:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
table td {
|
||||
padding: 1em;
|
||||
border-right: 1px solid gray;
|
||||
}
|
||||
|
||||
/*
|
||||
* Off Canvas
|
||||
* --------------------------------------------------
|
||||
*/
|
||||
@media screen and (max-width: 767px) {
|
||||
.row-offcanvas {
|
||||
position: relative;
|
||||
-webkit-transition: all .25s ease-out;
|
||||
-moz-transition: all .25s ease-out;
|
||||
transition: all .25s ease-out;
|
||||
}
|
||||
|
||||
.row-offcanvas-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.row-offcanvas-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.row-offcanvas-right
|
||||
.sidebar-offcanvas {
|
||||
right: -80%; /* 6 columns */
|
||||
}
|
||||
|
||||
.row-offcanvas-left
|
||||
.sidebar-offcanvas {
|
||||
left: -80%; /* 6 columns */
|
||||
}
|
||||
|
||||
.row-offcanvas-right.active {
|
||||
right: 80%; /* 6 columns */
|
||||
}
|
||||
|
||||
.row-offcanvas-left.active {
|
||||
left: 80%; /* 6 columns */
|
||||
}
|
||||
|
||||
.sidebar-offcanvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 80%; /* 6 columns */
|
||||
}
|
||||
}
|
206
cs440-acg/results/resources/twentytwenty.css
Normal file
@@ -0,0 +1,206 @@
|
||||
.twentytwenty-horizontal .twentytwenty-handle:before, .twentytwenty-horizontal .twentytwenty-handle:after, .twentytwenty-vertical .twentytwenty-handle:before, .twentytwenty-vertical .twentytwenty-handle:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
background: white;
|
||||
position: absolute;
|
||||
z-index: 30;
|
||||
-webkit-box-shadow: 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
-moz-box-shadow: 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
box-shadow: 0px 0px 12px rgba(51, 51, 51, 0.5); }
|
||||
|
||||
.twentytwenty-horizontal .twentytwenty-handle:before, .twentytwenty-horizontal .twentytwenty-handle:after {
|
||||
width: 3px;
|
||||
height: 9999px;
|
||||
left: 50%;
|
||||
margin-left: -1.5px; }
|
||||
|
||||
.twentytwenty-vertical .twentytwenty-handle:before, .twentytwenty-vertical .twentytwenty-handle:after {
|
||||
width: 9999px;
|
||||
height: 3px;
|
||||
top: 50%;
|
||||
margin-top: -1.5px; }
|
||||
|
||||
.twentytwenty-before-label, .twentytwenty-after-label, .twentytwenty-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%; }
|
||||
|
||||
.twentytwenty-before-label, .twentytwenty-after-label, .twentytwenty-overlay {
|
||||
-webkit-transition-duration: 0.5s;
|
||||
-moz-transition-duration: 0.5s;
|
||||
transition-duration: 0.5s; }
|
||||
|
||||
.twentytwenty-before-label, .twentytwenty-after-label {
|
||||
-webkit-transition-property: opacity;
|
||||
-moz-transition-property: opacity;
|
||||
transition-property: opacity; }
|
||||
|
||||
.twentytwenty-before-label:before, .twentytwenty-after-label:before {
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.1em; }
|
||||
|
||||
.twentytwenty-before-label:before, .twentytwenty-after-label:before {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
line-height: 38px;
|
||||
padding: 0 20px;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px; }
|
||||
|
||||
.twentytwenty-horizontal .twentytwenty-before-label:before, .twentytwenty-horizontal .twentytwenty-after-label:before {
|
||||
top: 50%;
|
||||
margin-top: -19px; }
|
||||
|
||||
.twentytwenty-vertical .twentytwenty-before-label:before, .twentytwenty-vertical .twentytwenty-after-label:before {
|
||||
left: 50%;
|
||||
margin-left: -45px;
|
||||
text-align: center;
|
||||
width: 90px; }
|
||||
|
||||
.twentytwenty-left-arrow, .twentytwenty-right-arrow, .twentytwenty-up-arrow, .twentytwenty-down-arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 6px inset transparent;
|
||||
position: absolute; }
|
||||
|
||||
.twentytwenty-left-arrow, .twentytwenty-right-arrow {
|
||||
top: 50%;
|
||||
margin-top: -6px; }
|
||||
|
||||
.twentytwenty-up-arrow, .twentytwenty-down-arrow {
|
||||
left: 50%;
|
||||
margin-left: -6px; }
|
||||
|
||||
.twentytwenty-container {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none; }
|
||||
.twentytwenty-container img {
|
||||
max-width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: block; }
|
||||
.twentytwenty-container.active .twentytwenty-overlay, .twentytwenty-container.active :hover.twentytwenty-overlay {
|
||||
background: rgba(0, 0, 0, 0); }
|
||||
.twentytwenty-container.active .twentytwenty-overlay .twentytwenty-before-label,
|
||||
.twentytwenty-container.active .twentytwenty-overlay .twentytwenty-after-label, .twentytwenty-container.active :hover.twentytwenty-overlay .twentytwenty-before-label,
|
||||
.twentytwenty-container.active :hover.twentytwenty-overlay .twentytwenty-after-label {
|
||||
opacity: 0; }
|
||||
.twentytwenty-container * {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box; }
|
||||
|
||||
.twentytwenty-before-label {
|
||||
opacity: 0; }
|
||||
.twentytwenty-before-label:before {
|
||||
content: attr(data-content); }
|
||||
|
||||
.twentytwenty-after-label {
|
||||
opacity: 0; }
|
||||
.twentytwenty-after-label:before {
|
||||
content: attr(data-content); }
|
||||
|
||||
.twentytwenty-horizontal .twentytwenty-before-label:before {
|
||||
left: 10px; }
|
||||
|
||||
.twentytwenty-horizontal .twentytwenty-after-label:before {
|
||||
right: 10px; }
|
||||
|
||||
.twentytwenty-vertical .twentytwenty-before-label:before {
|
||||
top: 10px; }
|
||||
|
||||
.twentytwenty-vertical .twentytwenty-after-label:before {
|
||||
bottom: 10px; }
|
||||
|
||||
.twentytwenty-overlay {
|
||||
-webkit-transition-property: background;
|
||||
-moz-transition-property: background;
|
||||
transition-property: background;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
z-index: 25; }
|
||||
.twentytwenty-overlay:hover {
|
||||
background: rgba(0, 0, 0, 0.5); }
|
||||
.twentytwenty-overlay:hover .twentytwenty-after-label {
|
||||
opacity: 1; }
|
||||
.twentytwenty-overlay:hover .twentytwenty-before-label {
|
||||
opacity: 1; }
|
||||
|
||||
.twentytwenty-before {
|
||||
z-index: 20; }
|
||||
|
||||
.twentytwenty-after {
|
||||
z-index: 10; }
|
||||
|
||||
.twentytwenty-handle {
|
||||
height: 38px;
|
||||
width: 38px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -22px;
|
||||
margin-top: -22px;
|
||||
border: 3px solid white;
|
||||
-webkit-border-radius: 1000px;
|
||||
-moz-border-radius: 1000px;
|
||||
border-radius: 1000px;
|
||||
-webkit-box-shadow: 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
-moz-box-shadow: 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
box-shadow: 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
z-index: 40;
|
||||
cursor: pointer; }
|
||||
|
||||
.twentytwenty-horizontal .twentytwenty-handle:before {
|
||||
bottom: 50%;
|
||||
margin-bottom: 22px;
|
||||
-webkit-box-shadow: 0 3px 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
-moz-box-shadow: 0 3px 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
box-shadow: 0 3px 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5); }
|
||||
.twentytwenty-horizontal .twentytwenty-handle:after {
|
||||
top: 50%;
|
||||
margin-top: 22px;
|
||||
-webkit-box-shadow: 0 -3px 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
-moz-box-shadow: 0 -3px 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
box-shadow: 0 -3px 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5); }
|
||||
|
||||
.twentytwenty-vertical .twentytwenty-handle:before {
|
||||
left: 50%;
|
||||
margin-left: 22px;
|
||||
-webkit-box-shadow: 3px 0 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
-moz-box-shadow: 3px 0 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
box-shadow: 3px 0 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5); }
|
||||
.twentytwenty-vertical .twentytwenty-handle:after {
|
||||
right: 50%;
|
||||
margin-right: 22px;
|
||||
-webkit-box-shadow: -3px 0 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
-moz-box-shadow: -3px 0 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5);
|
||||
box-shadow: -3px 0 0 white, 0px 0px 12px rgba(51, 51, 51, 0.5); }
|
||||
|
||||
.twentytwenty-left-arrow {
|
||||
border-right: 6px solid white;
|
||||
left: 50%;
|
||||
margin-left: -17px; }
|
||||
|
||||
.twentytwenty-right-arrow {
|
||||
border-left: 6px solid white;
|
||||
right: 50%;
|
||||
margin-right: -17px; }
|
||||
|
||||
.twentytwenty-up-arrow {
|
||||
border-bottom: 6px solid white;
|
||||
top: 50%;
|
||||
margin-top: -17px; }
|
||||
|
||||
.twentytwenty-down-arrow {
|
||||
border-top: 6px solid white;
|
||||
bottom: 50%;
|
||||
margin-bottom: -17px; }
|