Calcolo numerico per la generazione di immagini fotorealistiche
Maurizio Tomasi maurizio.tomasi@unimi.it
Communicate to the teacher by the next exercise the composition of your group and the chosen language
The exercises end at 12:30, but if you complete the work early you can leave earlier
In this course we will develop a complex program to generate photorealistic images;
Managing complex programs requires a series of measures:
Automatic code quality checks
Change monitoring
Code visibility to other users
Access to documentation
A version control system (VCS) records changes made to the code;
Possibility to undo changes;
Release of “releases” (e.g., 1.0, 1.1, 1.2) with the possibility of retrieving older ones;
Ensures that multiple programmers can modify the code simultaneously (with some caveats).
A VCS manages a directory, with all its subdirectories;
When creating/modifying a file within the directory, you ask the VCS to record the change;
The VCS takes “snapshots” of the directory, which it saves in its own database.
I create a directory hello_world
and a file
hello_world/hello.py
:
I invoke the VCS to “save” a snapshot of the
hello_world
directory
I modify the file hello_world/hello.py
to correct
the message:
I invoke the VCS again to “save” a new snapshot of the directory
At the end of the example, the VCS database contains two snapshots:
File hello_world/hello.py
with this content:
File hello_world/hello.py
with this content:
Each snapshot is always associated by VCS with some additional information:
In VCS jargon, a snapshot is called a commit.
We can create a simple VCS in Linux/Mac OS X using the Bash/Zsh
shell and two command-line programs: date
(prints date and
time) and whoami
(prints the user’s name).
We use the ability of shells like Bash to substitute
commands using $()
:
This command creates a backup copy of the files in the current directory:
The command creates a .tar
file in a
/vcsdatabase
folder containing all the files of the current
directory.
The file name contains the user’s name and the date; the latter
is encoded as a long number (e.g., 20240926155130
for the
date 2024-09-26, 15:51:30)
It is always useful to associate a brief comment with a commit. We
extend our idea into a shell script called my_vcs.sh
:
#!/bin/bash
readonly destpath="$1"
readonly tag="$2"
if [ "$tag" == "" ]; then
echo "Usage: $0 PATH TAG"
exit 1
fi
# Create the folder, if it does not exist
mkdir -p "${destpath}"
readonly filename="${destpath}/$(date +%Y%m%d%H%M%S)-$(whoami)-${tag}.tar"
tar -c -f "$filename" *
echo "File \"$filename\" created successfully"
We have a backup of the code: if we accidentally delete a source
file from the working directory, we can recover it from
/vcsdatabase
.
If we realize that a modification does not work, we can restore the previous version.
We can reconstruct the history of the code development simply by
looking at the list of files in /vcsdatabase
:
20240926153856-tomasi-first-release.tar
20240926155130-tomasi-fix-bug.tar
If we discover the existence of a bug, we can check backwards to determine when the bug was introduced.
If a VCS is being used, it is probably because the project is complex and has many files.
Usually, modifications affect one or just a few files at a time.
However, our implementation with tar
saves
all files every time: this risks occupying a lot of
space, and it is not necessary!
There is also another issue: if the database contained the files
20240926153856-tomasi-first-release.tar
20240926155130-tomasi-fix-bug.tar
and we wanted to understand what the “bug” was and how it was fixed,
we would have to compare the files in the latest .tar
one
by one with their counterparts in the previous .tar
to see
what changed.
tar
, saving
only the files that were actually modified (for example, by checking the
modification date of each file with ls -l
).Complex modifications are usually implemented gradually; for example:
A modification that adds the ability to save work to a file;
A modification that adds the ability to load a file.
If each of these tasks required a week of work, the programmer might want to perform a backup after completing the first step, before moving on to the second.
Our system does not allow logically related modifications to be
grouped together: each tar
file is independent of the
others!
Our system does not provide any control when multiple people work on the project.
Consider this situation:
A starts from the .tar
with the latest version of
the code to fix a bug.
B starts from the same .tar
to add a new feature to
the program.
A uses my_vcs.sh
to save their version with the bug
fixed.
B uses my_vcs.sh
to save their version with the new
feature.
In the end, there will be a .tar
file with the bug fixed
but without the new feature, and a .tar
file with the new
feature but where the bug is still present.
There are solutions for each of the problems we identified in our VCS.
Modern VCSs all have these features:
They save only the parts of files that have changed (using tools
similar to the diff
command found in Linux and Mac OS
X).
They allow logically related commits to be grouped together (e.g., saving/loading files).
When multiple programmers work on the same file, they check the consistency of modifications.
/vcsdatabase
directory) resides on a
remote computer that all programmers access.
Name | Kind | Example |
---|---|---|
CVS | Centralized | OpenBSD (link) |
Subversion | Centralized | FreePascal (until 2021), GCC (until 2019) |
GNU Bazaar | Distributed | Ubuntu Linux (until 2018) |
Mercurial | Distributed | Facebook, Mozilla, GNU Octave (link) |
Fossil | Distributed | SQLite (link) |
BitKeeper | Distributed | Kernel Linux (until 2005) |
Git | Distributed | Too many! |
On Ubuntu/Mint Linux systems, install Git with
sudo apt install git
As soon as it is installed, you need to configure Git with your identity. Run these commands:
git config --global user.email "YOUREMAIL@BLABLA"
git config --global user.name "First Last"
This allows Git to associate your name with the actions you perform on the repository. (Obviously, this is unnecessary if you know you’ll always be the only one working on the repository, but Git is designed to be a collaborative tool.)
To create a database in a directory, run
git init
This will create a hidden .git
directory (equivalent to
our /vcsdatabase
).
The first time you run git init
, it may ask you to
specify your name and email address.
When you want to make a commit, you must perform two operations:
git add FILENAME1 FILENAME2…
git commit
The first command prepares the files for “taking the snapshot,” copying them to the staging area, while the second performs the actual snapshot.
The git commit
command opens an editor to enter a
description.
Each commit/snapshot is identified by a long hexadecimal number
called a hash (e.g.,
2f2f2cb36bbf02eaf5629b6295e9a47684c16905
).
Each commit has two associated hashes:
The hash of the latest commit is called HEAD
, and it
can be viewed using the command
git rev-parse HEAD
When you run git commit
, the following happens:
git
analyzes which files have been modified compared to
the last commit (indicated by HEAD
);HEAD
commit;HEAD
value in the commit as the “previous
hash”;HEAD
to point to the new hash.git status
shows the status of the repository
(extremely useful!)git log
prints the list of commits starting from the
most recent one (HEAD
) and going backwards in timegit diff
shows which changes have been made since the
last commitgit mv
rename a filegit rm
delete a fileAutomatically generated files should not be included in a
repository (e.g., *.o
files, backups, executables,
etc.).
If you create a text file named .gitignore
, you can
list the files to exclude inside it. For example:
*~
*.o
build/
The .gitignore
file should be added to the
repository (git add .gitignore
, followed by
git commit
).
You can generate this file using the website gitignore.io or your IDE.
Since Git is a distributed system, when you connect to a remote server you need to sync your database. These are the most important commands:
git clone
creates a new directory based on a remote
database, and downloads the entire database into .git
;git pull
syncs your database in .git
by
requesting changes from a remote one;git push
sends your local changes in .git
to a remote database.GitHub makes Git “a bit more centralized” and “less distributed”:
https://github.com/name/project
);It is interesting to note that GitHub could provide all these features based on any other VCS that is not Git!
Create your own account on GitHub (if you don’t already have one)
Create an empty project and add .gitignore
Write a program (in your chosen programming language) that prints
Hello, wold!
[without r
], make a commit (1)
and publish it on GitHub
Fix the error in the text and make a commit (2)
Add the ability to specify a name and make a commit (3):
Install CMake; on Linux Debian/Ubuntu/Mint just run
sudo apt install cmake
Create an application that produces an executable. Structure the code like this:
CMakeLists.txt
file in the root directorysrc
directory that contains the main.cpp
fileIn .gitignore
list *.o
, the executable
name (e.g. hello_world
), any backup files
(*.bak
, *~
depending on the editor you use)
and the build
directory (or use gitignore.io indicating
c++
and cmake
).
cmake_minimum_required(VERSION 3.12)
# Define a "project", providing a description and a programming language
project(hello_world
VERSION 1.0
DESCRIPTION "Hello world in C++"
LANGUAGES CXX
)
set(CMAKE_CXX_STANDARD 20) # Pick the standard you like
# Our "project" will be able to build an executable out of a C++ source file
add_executable(hello_world src/main.cpp)
(In your case, CMake might output build.ninja
instead of
Makefile
: in this case, run ninja
instead of
make
.)
If you use CLion (highly recommended!), you can format the code using the Code/Reformat code command (Shift+Alt+L)
Otherwise, there is the command-line program
clang-format
; install it with
sudo apt install clang-format
. If you write this:
then clang-format
transforms it into
The clang-format
program is used from the command
line:
If you do not use CLion, it should be possible
to configure your editor to automatically invoke
clang-format
on every save. Some development environments
like Qt Creator
can do this automatically on every save.
These tools are very useful for keeping the code clean and clear to read: try to configure them to the best of your ability and learn to use them right from the start.
Create an empty application and the .gitignore
file;
if you use dotnet
from the command line, run
If you use Rider, make sure to enable Git when you create the project.
The application already prints Hello World!
: change
the message to Hello, wold!
(otherwise today’s exercise
makes no sense!)
Compile and run; from the command line, run
dotnet run
If you are using Rider, just press Shift+F10.
To automatically format the code in Rider, run Code/Reformat code (Shift+Alt+L)
In Visual Studio Code, install the C# package.
To format the code from the command line, install
dotnet-format
:
Create a package using the Julia manual (see the example in the next slide)
Create a hello_world
application (in the directory
where Project.toml
is located) like this:
Once you have completed the exercise, the directory should have this shape:
$ tree hello_world
hello_world/
├── hello_world
├── Project.toml
└── src
└── hello_world.jl
The logic of this structure is that the function library is
implemented inside src
, while the code related to the
executable part (e.g., command-line parameter interpretation) goes into
hello_world
.
If you use Visual Studio Code, there is the julia-vscode package.
It should guarantee the ability to format the code, but it is good to verify that it works.
There is also an independent package, Runic.jl.
Fundamental aspect of Julia!
They correspond to Python’s virtual environments
With Pkg.generate
you create a new package, with
Pkg.activate
you activate the package
The hello_world
script shown before activates the
package and invokes it:
Create a Kotlin or Java application in IntelliJ IDEA:
The empty application prints Hello World!
: first,
change the message to Hello, wold!
.
To use Git, you can also rely on IntelliJ’s “VCS” menu (it
automatically manages .gitignore
). It’s very convenient,
sometimes perhaps too much…
The directory containing the project has an executable,
gradlew
, which can be used to produce a
distribution in the ./build/distributions
directory:
gradlew assembleDist
Since it is a very useful function, explore it! Create a distribution of your program and try to understand how to install and use it.
In Java and Kotlin, there is great reliance on the integrated development environment (IDE). Learn to know IntelliJ IDEA well!
Get used to regularly invoking the “Code | Reformat code” command (Ctrl+Alt+L).
Create an empty application using your language’s package
manager. Nim uses nimble
:
$ nimble init helloworld
D uses dub
:
$ dub init helloworld
Rust uses cargo
:
$ cargo init helloworld
With both Nim and D you will have to answer some questions. If
possible, choose the default (but for Nim make sure to specify that you
want a binary
).
The application already prints Hello World!
: change
the message to Hello, wold!
(otherwise today’s exercise
makes no sense!)
To compile and run, just use the run
command
(identical in nimble
, dub
and
cargo
):
$ cd helloworld
$ nimble run # Or: dub run, or: cargo run
For both D and Nim there are plugins for IntelliJ IDEA, JetBrains’ Java IDE. For Rust, you can use CLion with the Rust plugin.