commit 2ddced7d419e23d282e0dc714f83155e28120603
Author: Laurent <2-naaturel@users.noreply.gitlab.naaturel.be>
Date: Tue Jun 23 20:27:12 2026 +0200
Import from internal git
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1fac4d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+.kotlin
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..bc44e09
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,12 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+/discord.xml
+/material_theme_project_new.xml
+/PMDPlugin.xml
+/.name
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..2a65317
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..f16dea7
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3fccfec
--- /dev/null
+++ b/README.md
@@ -0,0 +1,86 @@
+# Projet TDS MASI 4
+---
+# Ce qui a changé
+
+## Fonctionnalité
+Certaines fonctionnalités ont été déplacées ou légérement adaptée afin de les rendre
+plus facile à prendre en main
+
+---
+
+### Réorganisation des menus
+Certains items du menu tels que le chargement et la création des image,
+les fonctionnalités de dessin et l'affichage des transformées de Fourier ont été rémaniés.
+---
+
+### Chargement et création des images
+Désormais les images sont automatiquement chargée en format RGB.
+Leur réprésentation en niveaux de gris est gardée en mémoire à chaque
+modification de l'image et peut-être utilisée dès que nécessaire.
+
+#### Exemple
+```java
+//Crée une image blanche de 512 pixels par 512 pixels
+Image image = new Image(255,255,255, 512,512);
+
+//Crée une copie de l'image en récupérant son équivalent en niveaus de gris
+GrayScaleMatrix = image.toGrayScale();
+```
+
+---
+
+### Affichage des transformées de fourier
+
+Le module, la phase, la partie réelle et la partie imaginaire sont maintenant
+régroupés dans une seule et même fenêtre. L'affichage des transformées de plusieurs
+images différentes à travers différentes fenêtres reste possible.
+
+
+---
+
+## Architecture
+C'est ici que la majorité des changements ont eus lieu.
+Une architecture et des patterns modernes ont remplacé la majorité
+de la base de code précédente.
+
+### MVP
+Le programme utilise maintenant un design pattern architectural
+Model-View-Presenter afin de séparer au mieux les responsabilités
+des différentes couches et d'imposer un flux d'éxecution strict
+tel que décrit comme suit :
+
+- View : Réceptionne les événements utilisateur et gère l'état visuel de la fenêtre
+- Presenter : Couche d'orchestration entre la vue et la logique métier. Possède un accès aux données locales à la vue courante.
+- Services : Couche tampons faisant le lien entre les presenters, l'infrastructure et la couche domaine. Possède un accès à l'état global de l'application
+- Domaine : Contient la logique métier de l'application. Doit rester totalement agnostique de toute dépendance technologique
+- Infrastructure : Couche contenant un accès à des ressources externes telles que le système de fichier de la machine hôte
+
+
+### Gestion d'état
+La gestion d'état a été remaniée. Plusieurs composants d'état sont maintenant disponabible dans le package *app.state* :
+- Classe abstraite *State* : Classe parent donnant une structure à chaque classe d'état
+- Etat global du programme : Représenté par la classe *AppState*. Cet classe est déclarée comme étant un singleton dans le conteneur Guava
+- Etat local d'une vue : Representé par toutes les autres classes disponibles dans le package. Ne devrait pas être un singleton
+
+### Injection de dépendances
+Le programme utilise maintenant le conteneur d'injection de dépendance de Google, Guava.
+Cet ajout permet d'isoler la logique d'instanciation des différents composants lors de l'éxecution.
+La logique de navigation entre les différentes fenêtres s'en retrouve facilitée.
+Toutes les dépendances sont enregistrées dans la classe *AppModule* au sein du package *app*
+
+### Logique de navigation
+L'injection de dépendance précédement expliquée a permis d'isoler la logique de navigation.
+Désormais, pour naviguer entre différentes vue, il suffit de créer une méthode au sein de
+la classe SwingNavigator, d'injecter l'interface INavigator dans une Presenter et d'appeler la méthode nouvellement créée.
+
+Un exemple de fonctionnement est déjà fourni dans la classe SwingNavigator et peut-être facilement retracé en trouvant
+les usages de la méthode *showFourier()* avec un IDE moderne.
+
+#### Note importante
+Toutes les vues doivent être enregistrée en tant que dépendance au sein du conteneur Guava.
+Pour ce faire, différents exemples sont déjà implémenté dans la classe *AppModule*.
+Cet ajout aura pour conséquence de donner accès à un objet ``Provider`` (où T est le type de la dépendance enregistrée)
+qui pourra être injecté dans la classe SwingNavigator.
+
+Appeler la méthode ``Provider.get()`` aura pour conséquence d'instancier la dépendance désirée, dans notre cas,
+une potentielle vue qui pourra ensuite être affichée.
diff --git a/architecture.png b/architecture.png
new file mode 100644
index 0000000..c1f7d31
Binary files /dev/null and b/architecture.png differ
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..d16acd4
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,24 @@
+plugins {
+ id("java")
+}
+
+group = "be.naaturel"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation("org.jfree:jfreechart:1.5.4")
+ implementation("com.google.inject:guice:7.0.0")
+ implementation("com.google.guava:guava:33.4.0-jre")
+
+ testImplementation(platform("org.junit:junit-bom:5.10.0"))
+ testImplementation("org.junit.jupiter:junit-jupiter")
+ testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+}
+
+tasks.test {
+ useJUnitPlatform()
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..249e583
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a6b75b6
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 08 11:32:29 CEST 2026
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..1b6c787
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/menu_fourier.png b/menu_fourier.png
new file mode 100644
index 0000000..6636abe
Binary files /dev/null and b/menu_fourier.png differ
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..d6c967b
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "TDS"
\ No newline at end of file
diff --git a/src/main/java/Main.java b/src/main/java/Main.java
new file mode 100644
index 0000000..faf60cb
--- /dev/null
+++ b/src/main/java/Main.java
@@ -0,0 +1,28 @@
+import app.AppModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import presenters.MainPresenter;
+import presenters.NavPresenter;
+import ui.implementation.components.nav.NavBar;
+import ui.implementation.views.MainView;
+import ui.interfaces.IMainView;
+
+import javax.swing.*;
+
+/**
+ * @author Laurent Crema
+ */
+public class Main {
+
+ public static void main(String[] args) {
+ Injector injector = Guice.createInjector(new AppModule());
+
+ SwingUtilities.invokeLater(() -> {
+
+ NavBar navBar = injector.getInstance(NavBar.class);
+ MainView view = (MainView) injector.getInstance(IMainView.class);
+ view.setNavBar(navBar);
+ view.setVisible(true);
+ });
+ }
+}
diff --git a/src/main/java/app/AppModule.java b/src/main/java/app/AppModule.java
new file mode 100644
index 0000000..9eff494
--- /dev/null
+++ b/src/main/java/app/AppModule.java
@@ -0,0 +1,49 @@
+package app;
+
+import app.state.AppState;
+import app.state.DoubleMatrixState;
+import com.google.common.eventbus.EventBus;
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+import presenters.DoubleMatrixPresenter;
+import presenters.MainPresenter;
+import presenters.NavPresenter;
+import services.ImageService;
+import ui.implementation.components.nav.NavBar;
+import ui.implementation.views.DoubleMatrix;
+import ui.implementation.views.MainView;
+import ui.interfaces.IDoubleMatrix;
+import ui.interfaces.IMainView;
+import ui.interfaces.INavBar;
+
+/**
+ * @author Laurent Crema
+ */
+public class AppModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+
+ //--- States ---
+ bind(AppState.class).in(Scopes.SINGLETON);
+ bind(DoubleMatrixState.class);
+
+ //--- Application layer ---
+ bind(EventBus.class).in(Scopes.SINGLETON);
+ bind(INavigator.class).to(SwingNavigator.class).in(Scopes.SINGLETON);
+
+ //--- UI layer ---
+ bind(INavBar.class).to(NavBar.class);
+ bind(IMainView.class).to(MainView.class);
+ bind(IDoubleMatrix.class).to(DoubleMatrix.class);
+
+ //--- Presentation layer ---
+ bind(MainPresenter.class);
+ bind(NavPresenter.class);
+ bind(DoubleMatrixPresenter.class);
+
+ //--- Service layer layer ---
+ bind(ImageService.class);
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/app/INavigator.java b/src/main/java/app/INavigator.java
new file mode 100644
index 0000000..49361e3
--- /dev/null
+++ b/src/main/java/app/INavigator.java
@@ -0,0 +1,7 @@
+package app;
+
+public interface INavigator {
+
+ void showFourier();
+
+}
diff --git a/src/main/java/app/SwingNavigator.java b/src/main/java/app/SwingNavigator.java
new file mode 100644
index 0000000..a264057
--- /dev/null
+++ b/src/main/java/app/SwingNavigator.java
@@ -0,0 +1,23 @@
+package app;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import ui.implementation.views.DoubleMatrix;
+
+public class SwingNavigator implements INavigator {
+
+ private final Provider doubleMatrixProvider;
+
+ @Inject
+ public SwingNavigator(
+ Provider doubleMatrixProvider
+ ) {
+ this.doubleMatrixProvider = doubleMatrixProvider;
+ }
+
+ @Override
+ public void showFourier() {
+ DoubleMatrix doubleMatrix = doubleMatrixProvider.get();
+ doubleMatrix.setVisible(true);
+ }
+}
diff --git a/src/main/java/app/state/AppState.java b/src/main/java/app/state/AppState.java
new file mode 100644
index 0000000..4d7e9af
--- /dev/null
+++ b/src/main/java/app/state/AppState.java
@@ -0,0 +1,37 @@
+package app.state;
+
+import com.google.common.eventbus.EventBus;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import domain.common.Mode;
+import domain.events.ImageChangedEvent;
+import domain.events.ModeChangedEvent;
+import domain.image.Image;
+
+@Singleton
+public class AppState extends State {
+
+ private Mode mode;
+ private Image rgbImage;
+
+ @Inject
+ public AppState(EventBus eventBus) {
+ super(eventBus);
+ }
+
+ public void setMode(Mode mode) {
+ this.mode = mode;
+ this.eventBus.post(new ModeChangedEvent(mode));
+ }
+
+ public void setImage(Image image) {
+ this.rgbImage = image;
+ this.eventBus.post(new ImageChangedEvent(image));
+ }
+
+ public Image getImage(){
+ return this.rgbImage;
+ }
+
+ public Mode getMode() { return this.mode; }
+}
diff --git a/src/main/java/app/state/DoubleMatrixState.java b/src/main/java/app/state/DoubleMatrixState.java
new file mode 100644
index 0000000..71815c6
--- /dev/null
+++ b/src/main/java/app/state/DoubleMatrixState.java
@@ -0,0 +1,49 @@
+package app.state;
+
+import com.google.common.eventbus.EventBus;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import domain.events.fourier.*;
+import domain.image.GrayScaleMatrix;
+import domain.image.processing.complex.ComplexMatrix;
+
+public class DoubleMatrixState extends State {
+
+ private ComplexMatrix fourier;
+ private GrayScaleMatrix module;
+ private GrayScaleMatrix phase;
+ private GrayScaleMatrix real;
+ private GrayScaleMatrix imaginary;
+
+ @Inject
+ public DoubleMatrixState() { super(new EventBus()); }
+
+ public ComplexMatrix getFourier() {
+ return fourier;
+ }
+
+ public void setFourier(ComplexMatrix fourier) {
+ this.fourier = fourier;
+ this.eventBus.post(new ComplexMatrixChanged(fourier));
+ }
+
+ public void setModule(GrayScaleMatrix matrix) {
+ this.module = matrix;
+ this.eventBus.post(new ModuleChangedEvent(matrix));
+ }
+
+ public void setPhase(GrayScaleMatrix matrix) {
+ this.phase = matrix;
+ this.eventBus.post(new PhaseChangedEvent(matrix));
+ }
+
+ public void setReal(GrayScaleMatrix matrix) {
+ this.real = matrix;
+ this.eventBus.post(new RealChangedEvent(matrix));
+ }
+
+ public void setImaginary(GrayScaleMatrix matrix) {
+ this.imaginary = matrix;
+ this.eventBus.post(new ImaginaryChangedEvent(matrix));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/app/state/State.java b/src/main/java/app/state/State.java
new file mode 100644
index 0000000..cded0ad
--- /dev/null
+++ b/src/main/java/app/state/State.java
@@ -0,0 +1,16 @@
+package app.state;
+
+import com.google.common.eventbus.EventBus;
+
+public abstract class State {
+
+ protected EventBus eventBus;
+
+ public State(EventBus eventBus) {
+ this.eventBus = eventBus;
+ }
+
+ public void register(Object obj) {
+ eventBus.register(obj);
+ }
+}
diff --git a/src/main/java/domain/common/Mode.java b/src/main/java/domain/common/Mode.java
new file mode 100644
index 0000000..f81bb93
--- /dev/null
+++ b/src/main/java/domain/common/Mode.java
@@ -0,0 +1,9 @@
+package domain.common;
+
+public enum Mode {
+ DISABLED,
+ DRAW_PIXEL,
+ DRAW_LINE,
+ DRAW_RECTANGLE,
+ DRAW_CIRCLE,
+}
diff --git a/src/main/java/domain/events/ImageChangedEvent.java b/src/main/java/domain/events/ImageChangedEvent.java
new file mode 100644
index 0000000..124fb89
--- /dev/null
+++ b/src/main/java/domain/events/ImageChangedEvent.java
@@ -0,0 +1,5 @@
+package domain.events;
+
+import domain.image.Image;
+
+public record ImageChangedEvent(Image image) { }
diff --git a/src/main/java/domain/events/MatrixChangedEvent.java b/src/main/java/domain/events/MatrixChangedEvent.java
new file mode 100644
index 0000000..997e5c9
--- /dev/null
+++ b/src/main/java/domain/events/MatrixChangedEvent.java
@@ -0,0 +1,6 @@
+package domain.events;
+
+import domain.image.GrayScaleMatrix;
+
+public record MatrixChangedEvent(GrayScaleMatrix matrix) {
+}
diff --git a/src/main/java/domain/events/ModeChangedEvent.java b/src/main/java/domain/events/ModeChangedEvent.java
new file mode 100644
index 0000000..ed93ed3
--- /dev/null
+++ b/src/main/java/domain/events/ModeChangedEvent.java
@@ -0,0 +1,5 @@
+package domain.events;
+
+import domain.common.Mode;
+
+public record ModeChangedEvent(Mode mode) { }
diff --git a/src/main/java/domain/events/fourier/ComplexMatrixChanged.java b/src/main/java/domain/events/fourier/ComplexMatrixChanged.java
new file mode 100644
index 0000000..4cd7c45
--- /dev/null
+++ b/src/main/java/domain/events/fourier/ComplexMatrixChanged.java
@@ -0,0 +1,6 @@
+package domain.events.fourier;
+
+import domain.image.processing.complex.ComplexMatrix;
+
+public record ComplexMatrixChanged(ComplexMatrix matrix) {
+}
diff --git a/src/main/java/domain/events/fourier/ImaginaryChangedEvent.java b/src/main/java/domain/events/fourier/ImaginaryChangedEvent.java
new file mode 100644
index 0000000..dc980d3
--- /dev/null
+++ b/src/main/java/domain/events/fourier/ImaginaryChangedEvent.java
@@ -0,0 +1,6 @@
+package domain.events.fourier;
+
+import domain.image.GrayScaleMatrix;
+
+public record ImaginaryChangedEvent(GrayScaleMatrix matrix) {
+}
diff --git a/src/main/java/domain/events/fourier/ModuleChangedEvent.java b/src/main/java/domain/events/fourier/ModuleChangedEvent.java
new file mode 100644
index 0000000..e9c7c56
--- /dev/null
+++ b/src/main/java/domain/events/fourier/ModuleChangedEvent.java
@@ -0,0 +1,6 @@
+package domain.events.fourier;
+
+import domain.image.GrayScaleMatrix;
+
+public record ModuleChangedEvent(GrayScaleMatrix matrix) {
+}
diff --git a/src/main/java/domain/events/fourier/PhaseChangedEvent.java b/src/main/java/domain/events/fourier/PhaseChangedEvent.java
new file mode 100644
index 0000000..4192da8
--- /dev/null
+++ b/src/main/java/domain/events/fourier/PhaseChangedEvent.java
@@ -0,0 +1,6 @@
+package domain.events.fourier;
+
+import domain.image.GrayScaleMatrix;
+
+public record PhaseChangedEvent(GrayScaleMatrix matrix) {
+}
diff --git a/src/main/java/domain/events/fourier/RealChangedEvent.java b/src/main/java/domain/events/fourier/RealChangedEvent.java
new file mode 100644
index 0000000..9e97e81
--- /dev/null
+++ b/src/main/java/domain/events/fourier/RealChangedEvent.java
@@ -0,0 +1,6 @@
+package domain.events.fourier;
+
+import domain.image.GrayScaleMatrix;
+
+public record RealChangedEvent(GrayScaleMatrix matrix) {
+}
diff --git a/src/main/java/domain/image/GrayScaleMatrix.java b/src/main/java/domain/image/GrayScaleMatrix.java
new file mode 100644
index 0000000..b40b0a3
--- /dev/null
+++ b/src/main/java/domain/image/GrayScaleMatrix.java
@@ -0,0 +1,130 @@
+package domain.image;
+
+import infrastructure.image.io.ImageSaver;
+
+import java.util.Arrays;
+
+/**
+ * @author Jean-Marc Wagner, Laurent Crema
+ */
+public class GrayScaleMatrix {
+
+ private final double[][] matrix;
+
+ private final double maxValue;
+ private final double minValue;
+
+ public GrayScaleMatrix(double[][] matrix) {
+ this.matrix = matrix;
+ this.maxValue = computeMax();
+ this.minValue = computeMin();
+ }
+
+ /**
+ * Returns a copy of the actual content of this matrix.
+ * Since a copy is returned, no change that will be applied on it is going to affect the original matrix
+ * @return a copy of the actual content
+ */
+ public double[][] getRawData() {
+ return Arrays.copyOf(matrix, matrix.length);
+ }
+
+ public int getWidth(){
+ return matrix[0].length;
+ }
+
+ public int getHeight(){
+ return matrix.length;
+ }
+
+ public double getMaxValue() {
+ return maxValue;
+ }
+
+ public double getMinValue() {
+ return minValue;
+ }
+
+ public double getValueAt(double normalized) {
+ if(normalized <= 0) { return minValue; }
+ if(normalized >= 1) { return maxValue; }
+ return minValue + (maxValue - minValue) * normalized;
+ }
+
+ private double computeMax(){
+ double res = matrix[0][0];
+ for(int y = 0; y < getHeight(); y++) {
+ for (int x = 0; x < getWidth(); x++) {
+ double value = matrix[y][x];
+ if (value > res) res = matrix[y][x];
+ }
+ }
+ return res;
+ }
+
+ private double computeMin(){
+ double res = matrix[0][0];
+ for(int y = 0; y < getHeight(); y++) {
+ for (int x = 0; x < getWidth(); x++) {
+ double value = matrix[y][x];
+ if (value < res) res = matrix[y][x];
+ }
+ }
+ return res;
+ }
+
+ public GrayScaleMatrix clip() {
+ return clip(minValue, maxValue);
+ }
+
+ public GrayScaleMatrix clip(double lowerBound, double upperBound) {
+
+ int height = getHeight();
+ int width = getWidth();
+
+ double[][] res = new double[height][width];
+
+ double range = upperBound - lowerBound;
+ if (range == 0) range = 1;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+
+ double v = matrix[y][x];
+ double out;
+
+ if (v >= upperBound) {
+ out = 255;
+ } else if (v <= lowerBound) {
+ out = 0;
+ } else {
+ out = (v - lowerBound) / range * 255.0;
+ }
+
+ res[y][x] = Math.clamp(out, 0, 255);
+ }
+ }
+
+ return new GrayScaleMatrix(res);
+ }
+
+ public Image toImage() {
+
+ Pixel[][] res = new Pixel[getHeight()][getWidth()];
+
+ double range = maxValue - minValue;
+ if (range == 0) range = 1;
+
+ for (int y = 0; y < getHeight(); y++) {
+ for (int x = 0; x < getWidth(); x++) {
+
+ double normalized = (matrix[y][x] - minValue) / range;
+ int gray = (int) (normalized * 255);
+
+ res[y][x] = new Pixel(gray, gray, gray);
+ }
+ }
+
+ return new Image(res);
+ }
+}
diff --git a/src/main/java/domain/image/Image.java b/src/main/java/domain/image/Image.java
new file mode 100644
index 0000000..db2bb86
--- /dev/null
+++ b/src/main/java/domain/image/Image.java
@@ -0,0 +1,126 @@
+package domain.image;
+
+/**
+ * @author Laurent Crema
+ */
+public class Image {
+
+ private final Pixel[][] pixels;
+ private GrayScaleMatrix grayScaleEquivalent;
+
+ public Image(Pixel[][] pixels) {
+ if(pixels == null || pixels.length == 0 || pixels[0].length == 0)
+ throw new IllegalArgumentException("Invalid dimensions");
+ this.pixels = pixels;
+ }
+
+ public Image(int red, int green, int blue, int height, int width) {
+ pixels = new Pixel[height][width];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ pixels[y][x] = new Pixel(red, green, blue);
+ }
+ }
+ }
+
+ public int getWidth(){
+ return pixels[0].length;
+ }
+
+ public int getHeight(){
+ return pixels.length;
+ }
+
+ public Pixel getPixel(int x, int y){
+ if(x < 0 || y < 0 || x >= getWidth() || y >= getHeight())
+ throw new IllegalArgumentException("Invalid coordinates");
+ return pixels[y][x];
+ }
+
+ public void setPixelColor(int x, int y, int red, int green, int blue){
+ if(x < 0 || y < 0 || x >= getWidth() || y >= getHeight())
+ throw new IllegalArgumentException("Invalid coordinates");
+ Pixel p = new Pixel(red, green, blue);
+ pixels[y][x] = p;
+ grayScaleEquivalent = toGrayScale();
+ }
+
+ public void setRectangleColor(int x1, int x2, int y1, int y2,
+ int red, int green, int blue) {
+
+ int minX = Math.max(0, Math.min(x1, x2));
+ int maxX = Math.min(pixels[0].length - 1, Math.max(x1, x2));
+
+ int minY = Math.max(0, Math.min(y1, y2));
+ int maxY = Math.min(pixels.length - 1, Math.max(y1, y2));
+
+ Pixel p = new Pixel(red, green, blue);
+
+ for (int y = minY; y <= maxY; y++) {
+ for (int x = minX; x <= maxX; x++) {
+ pixels[y][x] = p;
+ }
+ }
+
+ grayScaleEquivalent = toGrayScale();
+ }
+
+ public void setLineColor(int x1, int x2, int y1, int y2,
+ int red, int green, int blue) {
+
+ int dx = Math.abs(x2 - x1);
+ int dy = Math.abs(y2 - y1);
+
+ int sx = x1 < x2 ? 1 : -1;
+ int sy = y1 < y2 ? 1 : -1;
+
+ int err = dx - dy;
+
+ Pixel p = new Pixel(red, green, blue);
+
+ while (true) {
+ if (x1 >= 0 && y1 >= 0 && x1 < getWidth() && y1 < getHeight()) {
+ pixels[y1][x1] = p;
+ }
+
+ if (x1 == x2 && y1 == y2) break;
+
+ int e2 = 2 * err;
+
+ if (e2 > -dy) {
+ err -= dy;
+ x1 += sx;
+ }
+
+ if (e2 < dx) {
+ err += dx;
+ y1 += sy;
+ }
+ }
+
+ grayScaleEquivalent = toGrayScale();
+ }
+
+ /**
+ * Return the corresponding gray scale matrix. Since this operation is destructive, the original image is not modified.
+ * Instead, a new one is returned.
+ * @return A newly converted image
+ */
+ public GrayScaleMatrix toGrayScale() {
+
+ if(grayScaleEquivalent != null) return this.grayScaleEquivalent;
+
+ double[][] res = new double[getHeight()][getWidth()];
+
+ for (int y = 0; y < getHeight(); y++) {
+ for (int x = 0; x < getWidth(); x++) {
+
+ Pixel p = pixels[y][x];
+ res[y][x] = p.grayValue();
+ }
+ }
+
+ this.grayScaleEquivalent = new GrayScaleMatrix(res);
+ return this.grayScaleEquivalent;
+ }
+}
diff --git a/src/main/java/domain/image/Pixel.java b/src/main/java/domain/image/Pixel.java
new file mode 100644
index 0000000..5af15f6
--- /dev/null
+++ b/src/main/java/domain/image/Pixel.java
@@ -0,0 +1,62 @@
+package domain.image;
+
+/**
+ * @author Laurent Crema
+ */
+public class Pixel {
+
+ private int red, green, blue;
+
+ public Pixel(int red, int green, int blue) {
+
+ if(checkColorValidity(red))
+ throw new IllegalArgumentException("Invalid red value"); // <-- here
+ if(checkColorValidity(green)) throw new IllegalArgumentException("Invalid green value");
+ if(checkColorValidity(blue)) throw new IllegalArgumentException("Invalid blue value");
+
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ }
+
+ public int getRed() {
+ return red;
+ }
+
+ public void setRed(int value) {
+ if(checkColorValidity(value)) throw new IllegalArgumentException("Invalid value");
+ this.red = value;
+ }
+
+ public int getGreen() {
+ return green;
+ }
+
+ public void setGreen(int value) {
+ if(checkColorValidity(value)) throw new IllegalArgumentException("Invalid value");
+ this.green = value;
+ }
+
+ public int getBlue() {
+ return blue;
+ }
+
+ public void setBlue(int value) {
+ if(checkColorValidity(value)) throw new IllegalArgumentException("Invalid value");
+ this.blue = value;
+ }
+
+ public boolean checkColorValidity(int color) {
+ return !(color >= 0 && color <= 255);
+ }
+
+ /**
+ * Convert the current RGB combination into a gray value according to UIT-R BT 709 standard.
+ * The current RGB values are not modified.
+ * @return the corresponding gray scale value
+ */
+ public int grayValue(){
+ return (int) (0.2126 * red + 0.7152 * green + 0.0722 * blue);
+ }
+
+}
diff --git a/src/main/java/domain/image/processing/complex/ComplexMatrix.java b/src/main/java/domain/image/processing/complex/ComplexMatrix.java
new file mode 100644
index 0000000..f45b103
--- /dev/null
+++ b/src/main/java/domain/image/processing/complex/ComplexMatrix.java
@@ -0,0 +1,80 @@
+package domain.image.processing.complex;
+
+public class ComplexMatrix
+{
+ private Complexe m[][];
+ private int lignes;
+ private int colonnes;
+
+ /** Creates a new instance of MatriceComplexe */
+ public ComplexMatrix(int l, int c)
+ {
+ lignes = l;
+ colonnes = c;
+ m = new Complexe[l][c];
+ for(int i=0 ; i v **********
+ double pr1[][] = new double[M][N];
+ double pi1[][] = new double[M][N];
+
+ for(int m=0 ; m 0)
+ {
+ pr1[m][N-v] = pr1[m][v];
+ pi1[m][N-v] = -pi1[m][v];
+ }
+ }
+ }
+
+ //********** m --> u **********
+ double pr2[][] = new double[M][N];
+ double pi2[][] = new double[M][N];
+
+ for(int v=0 ; v n **********
+ double pr1[][] = new double[M][N];
+ double pi1[][] = new double[M][N];
+
+ for(int u=0 ; u m **********
+ double pr2[][] = new double[M][N];
+ double pi2[][] = new double[M][N];
+
+ for(int n=0 ; n= 0) && (pixelGrayValue <= 255)) {
+ histo[pixelGrayValue]++;
+ }
+ }
+ }
+ return histo;
+ }
+}
diff --git a/src/main/java/domain/image/processing/lineaire/FiltrageLinaireGlobal.java b/src/main/java/domain/image/processing/lineaire/FiltrageLinaireGlobal.java
new file mode 100644
index 0000000..14c95ea
--- /dev/null
+++ b/src/main/java/domain/image/processing/lineaire/FiltrageLinaireGlobal.java
@@ -0,0 +1,20 @@
+package domain.image.processing.lineaire;
+
+import jdk.jshell.spi.ExecutionControl;
+
+public class FiltrageLinaireGlobal {
+
+ public static int[][] filtrePasseBasIdeal(int[][] image,int frequenceCoupure){
+ return null;
+ }
+ public static int[][] filtrePasseHautIdeal(int[][] image,int frequenceCoupure){
+ return null;
+ }
+ public static int[][] filtrePasseBasButterworth(int[][] image,int frequenceCoupure,int ordre){
+ return null;
+ }
+ public static int[][] filtrePasseHautButterworth(int[][] image,int frequenceCoupure, int ordre) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/infrastructure/image/io/ImageLoader.java b/src/main/java/infrastructure/image/io/ImageLoader.java
new file mode 100644
index 0000000..259b427
--- /dev/null
+++ b/src/main/java/infrastructure/image/io/ImageLoader.java
@@ -0,0 +1,39 @@
+package infrastructure.image.io;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import domain.image.Image;
+import domain.image.Pixel;
+
+public class ImageLoader {
+
+ public static Image loadImage(File f) throws IOException {
+
+ BufferedImage buffered = ImageIO.read(f);
+
+ if (buffered == null) throw new IOException("Unsupported or invalid image file: " + f);
+
+ int width = buffered.getWidth();
+ int height = buffered.getHeight();
+
+ Pixel[][] pixels = new Pixel[height][width];
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+
+ int rgb = buffered.getRGB(x, y);
+
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = rgb & 0xFF;
+
+ pixels[y][x] = new Pixel(r, g, b);
+ }
+ }
+
+ return new Image(pixels);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/infrastructure/image/io/ImageSaver.java b/src/main/java/infrastructure/image/io/ImageSaver.java
new file mode 100644
index 0000000..fae14b0
--- /dev/null
+++ b/src/main/java/infrastructure/image/io/ImageSaver.java
@@ -0,0 +1,12 @@
+package infrastructure.image.io;
+
+import domain.image.Image;
+
+import java.io.IOException;
+
+public class ImageSaver {
+
+ public static void saveImage(Image image) throws IOException {
+ }
+
+}
diff --git a/src/main/java/infrastructure/ui/ImageMapper.java b/src/main/java/infrastructure/ui/ImageMapper.java
new file mode 100644
index 0000000..7eb2245
--- /dev/null
+++ b/src/main/java/infrastructure/ui/ImageMapper.java
@@ -0,0 +1,55 @@
+package infrastructure.ui;
+
+import domain.image.Image;
+import domain.image.Pixel;
+
+import java.awt.image.BufferedImage;
+
+public final class ImageMapper {
+
+ public static BufferedImage toBufferedImage(Image image) {
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ BufferedImage out = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+
+ Pixel p = image.getPixel(x, y);
+
+ int rgb = (p.getRed() << 16)
+ | (p.getGreen() << 8)
+ | p.getBlue();
+
+ out.setRGB(x, y, rgb);
+ }
+ }
+
+ return out;
+ }
+
+ public static Image fromBufferedImage(BufferedImage img) {
+
+ int width = img.getWidth();
+ int height = img.getHeight();
+
+ Pixel[][] pixels = new Pixel[height][width];
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+
+ int rgb = img.getRGB(x, y);
+
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = rgb & 0xFF;
+
+ pixels[y][x] = new Pixel(r, g, b);
+ }
+ }
+
+ return new Image(pixels);
+ }
+}
diff --git a/src/main/java/presenters/DoubleMatrixPresenter.java b/src/main/java/presenters/DoubleMatrixPresenter.java
new file mode 100644
index 0000000..b21a629
--- /dev/null
+++ b/src/main/java/presenters/DoubleMatrixPresenter.java
@@ -0,0 +1,126 @@
+package presenters;
+
+import app.state.DoubleMatrixState;
+import com.google.common.eventbus.Subscribe;
+import com.google.inject.Inject;
+import domain.events.fourier.ImaginaryChangedEvent;
+import domain.events.fourier.ModuleChangedEvent;
+import domain.events.fourier.PhaseChangedEvent;
+import domain.events.fourier.RealChangedEvent;
+import domain.image.GrayScaleMatrix;
+import domain.image.Image;
+import domain.image.processing.complex.ComplexMatrix;
+import services.ImageService;
+import ui.interfaces.IDoubleMatrix;
+
+import java.io.IOException;
+
+/**
+ * @author Laurent Crema
+ */
+public class DoubleMatrixPresenter {
+
+ private IDoubleMatrix view;
+ private final DoubleMatrixState state;
+ private final ImageService imageService;
+
+ @Inject
+ public DoubleMatrixPresenter(ImageService imageService, DoubleMatrixState state) {
+ this.imageService = imageService;
+ this.state = state;
+ this.state.register(this);
+ }
+
+ public void setView(IDoubleMatrix view){
+ this.view = view;
+ }
+
+ public void saveImage(Image image) throws IOException {
+ this.imageService.saveImage(image);
+ }
+
+ public void loadModule(){
+ double[][] module = getFourier().getModule();
+ state.setModule(new GrayScaleMatrix(module));
+ }
+
+ public void loadPhase(){
+ double[][] phase = getFourier().getPhase();
+ state.setPhase(new GrayScaleMatrix(phase));
+ }
+
+ public void loadReal(){
+ double[][] real = getFourier().getPartieReelle();
+ state.setReal(new GrayScaleMatrix(real));
+ }
+
+ public void loadImaginary(){
+ double[][] imaginary = getFourier().getPartieImaginaire();
+ state.setImaginary(new GrayScaleMatrix(imaginary));
+ }
+
+ public double getValueAt(GrayScaleMatrix matrix, double normalized){
+ return matrix.getValueAt(normalized);
+ }
+
+ public void clipModule(double black, double white){
+ GrayScaleMatrix module = new GrayScaleMatrix(getFourier().getModule());
+ GrayScaleMatrix matrix = clip(module, black, white);
+ state.setModule(matrix);
+ }
+
+ public void clipPhase(double black, double white){
+ GrayScaleMatrix phase = new GrayScaleMatrix(getFourier().getPhase());
+ GrayScaleMatrix matrix = clip(phase, black, white);
+ state.setPhase(matrix);
+ }
+
+ public void clipReal(double black, double white){
+ GrayScaleMatrix real = new GrayScaleMatrix(getFourier().getPartieReelle());
+ GrayScaleMatrix matrix = clip(real, black, white);
+ state.setReal(matrix);
+ }
+
+ public void clipImaginary(double black, double white){
+ GrayScaleMatrix imaginary = new GrayScaleMatrix(getFourier().getPartieImaginaire());
+ GrayScaleMatrix matrix = clip(imaginary, black, white);
+ state.setImaginary(matrix);
+ }
+
+ @Subscribe
+ public void onModuleUpdate(ModuleChangedEvent event) {
+ GrayScaleMatrix module = new GrayScaleMatrix(getFourier().getModule());
+ view.updateModule(module, event.matrix());
+ }
+
+ @Subscribe
+ public void onPhaseUpdate(PhaseChangedEvent event) {
+ GrayScaleMatrix phase = new GrayScaleMatrix(getFourier().getPhase());
+ view.updatePhase(phase, event.matrix());
+ }
+
+ @Subscribe
+ public void onRealUpdate(RealChangedEvent event) {
+ GrayScaleMatrix real = new GrayScaleMatrix(getFourier().getPartieReelle());
+ view.updateReal(real, event.matrix());
+ }
+
+ @Subscribe
+ public void onImaginaryUpdate(ImaginaryChangedEvent event) {
+ GrayScaleMatrix imaginary = new GrayScaleMatrix(getFourier().getPartieImaginaire());
+ view.updateImaginary(imaginary, event.matrix());
+ }
+
+ private GrayScaleMatrix clip(GrayScaleMatrix matrix, double black, double white){
+ double lowerBound = matrix.getValueAt(black);
+ double upperBound = matrix.getValueAt(white);
+ return matrix.clip(lowerBound, upperBound);
+ }
+
+ private ComplexMatrix getFourier() {
+ if (state.getFourier() == null) {
+ state.setFourier(imageService.computeFourier());
+ }
+ return state.getFourier();
+ }
+}
diff --git a/src/main/java/presenters/MainPresenter.java b/src/main/java/presenters/MainPresenter.java
new file mode 100644
index 0000000..ca33501
--- /dev/null
+++ b/src/main/java/presenters/MainPresenter.java
@@ -0,0 +1,67 @@
+package presenters;
+
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+import com.google.inject.Inject;
+import domain.common.Mode;
+import domain.events.ImageChangedEvent;
+import domain.events.ModeChangedEvent;
+import domain.image.Image;
+import services.ImageService;
+import services.ModeService;
+import ui.interfaces.IMainView;
+
+/**
+ * @author Laurent Crema
+ */
+public class MainPresenter {
+
+ private IMainView view;
+ private final ModeService modeService;
+ private final ImageService imageService;
+
+ @Inject
+ public MainPresenter(ModeService modeService, ImageService imageService, EventBus eventBus) {
+ this.modeService = modeService;
+ this.imageService = imageService;
+ eventBus.register(this);
+ }
+
+ public void setView(IMainView view) {
+ this.view = view;
+ }
+
+ @Subscribe
+ public void onImageChanged(ImageChangedEvent e){
+ view.displayImage(e.image());
+ }
+
+ @Subscribe
+ public void onModeChanged(ModeChangedEvent e){
+ view.changeMode(e.mode());
+ }
+
+ public void drawPixel(int x, int y, int red, int green, int blue){
+ if(modeService.getMode() != Mode.DRAW_PIXEL) return;
+ Image image = imageService.getImage();
+ image.setPixelColor(x, y, red, green, blue);
+ imageService.setImage(image);
+ }
+
+ public void drawShape(int x1, int x2, int y1, int y2, int red, int green, int blue){
+ Image image = imageService.getImage();
+ switch (modeService.getMode()) {
+ case DRAW_RECTANGLE:
+ image.setRectangleColor(x1, x2, y1, y2, red, green, blue);
+ break;
+ case DRAW_LINE:
+ image.setLineColor(x1, x2, y1, y2, red, green, blue);
+ break;
+ case DRAW_CIRCLE:
+ break;
+ }
+ imageService.setImage(image);
+ }
+
+
+}
diff --git a/src/main/java/presenters/NavPresenter.java b/src/main/java/presenters/NavPresenter.java
new file mode 100644
index 0000000..9d1162b
--- /dev/null
+++ b/src/main/java/presenters/NavPresenter.java
@@ -0,0 +1,55 @@
+package presenters;
+
+import app.INavigator;
+import com.google.inject.Inject;
+import domain.common.Mode;
+import domain.image.GrayScaleMatrix;
+import services.ImageService;
+import services.ModeService;
+import ui.interfaces.INavBar;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author Laurent Crema
+ */
+public class NavPresenter {
+
+ private INavBar navBar;
+ private INavigator navigator;
+ private final ImageService imageService;
+ private final ModeService modeService;
+
+ @Inject
+ public NavPresenter(INavigator navigator, ImageService imageService, ModeService modeService) {
+ this.navigator = navigator;
+ this.imageService = imageService;
+ this.modeService = modeService;
+ }
+
+ public void setView(INavBar navBar) {
+ this.navBar = navBar;
+ }
+
+ public void createImage(int red, int green, int blue, int height, int width) {
+ imageService.createImage(red, green, blue, height, width);
+ }
+
+ public void loadImage(File f) throws IOException {
+ imageService.loadImage(f);
+ }
+
+ public void goToFourier(){
+ navigator.showFourier();
+ }
+
+ public GrayScaleMatrix getImageGrayMatrix() {
+ //return ((CImageNG)appState.getImage()).getMatrice();
+ return imageService.getImage().toGrayScale();
+ }
+
+ public void setMode(Mode mode) {
+ modeService.setMode(mode);
+ }
+}
diff --git a/src/main/java/services/ImageService.java b/src/main/java/services/ImageService.java
new file mode 100644
index 0000000..f9a7f2b
--- /dev/null
+++ b/src/main/java/services/ImageService.java
@@ -0,0 +1,55 @@
+package services;
+
+import app.state.AppState;
+import com.google.inject.Inject;
+import domain.image.Image;
+import domain.image.GrayScaleMatrix;
+import domain.image.processing.complex.ComplexMatrix;
+import domain.image.processing.fourier.Fourier;
+import infrastructure.image.io.ImageLoader;
+import infrastructure.image.io.ImageSaver;
+
+import java.io.File;
+import java.io.IOException;
+
+public class ImageService {
+
+ private final AppState appState;
+
+ @Inject
+ public ImageService(AppState appState) {
+ this.appState = appState;
+ }
+
+ public void createImage(int red, int green, int blue, int height, int width) {
+ Image image = new Image(red, green, blue, height, width);
+ appState.setImage(image);
+ }
+
+ public void loadImage(File f) throws IOException {
+ Image image = ImageLoader.loadImage(f);
+ appState.setImage(image);
+ }
+
+ public void saveImage(Image image) throws IOException {
+ ImageSaver.saveImage(image);
+ }
+
+ public ComplexMatrix computeFourier(){
+ GrayScaleMatrix matrix = appState.getImage().toGrayScale();
+ ComplexMatrix fourier = Fourier.Fourier2D(matrix.getRawData());
+ return Fourier.decroise(fourier);
+ }
+
+ public Image getImage(){
+ return appState.getImage();
+ }
+
+ public void setImage(Image img){
+ appState.setImage(img);
+ }
+
+ public GrayScaleMatrix getGrayScale(){
+ return appState.getImage().toGrayScale();
+ }
+}
diff --git a/src/main/java/services/ModeService.java b/src/main/java/services/ModeService.java
new file mode 100644
index 0000000..922bc7a
--- /dev/null
+++ b/src/main/java/services/ModeService.java
@@ -0,0 +1,22 @@
+package services;
+
+import app.state.AppState;
+import com.google.inject.Inject;
+import domain.common.Mode;
+
+public class ModeService {
+
+ private final AppState appState;
+
+ @Inject
+ public ModeService(AppState appState) {
+ this.appState = appState;
+ }
+
+ public void setMode(Mode m){
+ appState.setMode(m);
+ }
+
+ public Mode getMode() { return this.appState.getMode(); }
+
+}
diff --git a/src/main/java/ui/implementation/components/image/ImagePanel.java b/src/main/java/ui/implementation/components/image/ImagePanel.java
new file mode 100644
index 0000000..28e1434
--- /dev/null
+++ b/src/main/java/ui/implementation/components/image/ImagePanel.java
@@ -0,0 +1,79 @@
+package ui.implementation.components.image;
+
+import domain.image.GrayScaleMatrix;
+import domain.image.Image;
+import infrastructure.ui.ImageMapper;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+
+public class ImagePanel extends JPanel {
+ private BufferedImage image;
+ private double scale;
+ private int drawW;
+ private int drawH;
+ private int offsetX;
+ private int offsetY;
+
+ public ImagePanel() { }
+
+ public ImagePanel(BufferedImage image) {
+ loadImage(image);
+ }
+
+ public void loadImage(BufferedImage image){
+ this.image = image;
+ setPreferredSize(new Dimension(image.getWidth(null), image.getHeight(null)));
+ repaint();
+ }
+
+ public void loadImage(GrayScaleMatrix matrix){
+ this.image = ImageMapper.toBufferedImage(matrix.toImage());
+ setPreferredSize(new Dimension(this.image.getWidth(null), this.image.getHeight(null)));
+ repaint();
+ }
+
+ public Point toImageCoordinates(int x, int y) {
+ if (image == null) return null;
+
+ if (x < offsetX || x >= offsetX + drawW ||
+ y < offsetY || y >= offsetY + drawH) {
+ return null;
+ }
+
+ int imgX = (int) ((x - offsetX) / scale);
+ int imgY = (int) ((y - offsetY) / scale);
+
+ return new Point(imgX, imgY);
+ }
+
+ private void computeViewport() {
+ if (image == null) return;
+
+ int imgW = image.getWidth();
+ int imgH = image.getHeight();
+
+ scale = Math.min(
+ (double) getWidth() / imgW,
+ (double) getHeight() / imgH
+ );
+
+ drawW = (int) (imgW * scale);
+ drawH = (int) (imgH * scale);
+
+ offsetX = (getWidth() - drawW) / 2;
+ offsetY = (getHeight() - drawH) / 2;
+ }
+
+
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (image == null) return;
+ computeViewport();
+ g.drawImage(image, offsetX, offsetY, drawW, drawH, null);
+ }
+}
diff --git a/src/main/java/ui/implementation/components/intent/FileChooser.java b/src/main/java/ui/implementation/components/intent/FileChooser.java
new file mode 100644
index 0000000..dfcea99
--- /dev/null
+++ b/src/main/java/ui/implementation/components/intent/FileChooser.java
@@ -0,0 +1,9 @@
+package ui.implementation.components.intent;
+
+import javax.swing.*;
+public class FileChooser extends JFileChooser {
+
+ public FileChooser(String initialLocation) {
+ super(initialLocation);
+ }
+}
diff --git a/src/main/java/ui/implementation/components/nav/Menu.java b/src/main/java/ui/implementation/components/nav/Menu.java
new file mode 100644
index 0000000..4b5e58b
--- /dev/null
+++ b/src/main/java/ui/implementation/components/nav/Menu.java
@@ -0,0 +1,32 @@
+package ui.implementation.components.nav;
+
+import javax.swing.*;
+import java.net.URL;
+
+public class Menu extends JMenu {
+
+ public Menu(String title, JMenuItem... items)
+ {
+ this(title,null, items);
+ }
+
+ public Menu(String title, String iconPath, JMenuItem... items)
+ {
+ super(title);
+
+ if (iconPath != null) {
+ URL url = getClass().getResource(iconPath);
+ if (url != null) {
+ setIcon(new ImageIcon(url));
+ }
+ }
+
+ if (items != null) {
+ for (JMenuItem item : items) {
+ if (item != null) {
+ this.add(item);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/ui/implementation/components/nav/MenuItem.java b/src/main/java/ui/implementation/components/nav/MenuItem.java
new file mode 100644
index 0000000..6277f52
--- /dev/null
+++ b/src/main/java/ui/implementation/components/nav/MenuItem.java
@@ -0,0 +1,25 @@
+package ui.implementation.components.nav;
+
+import javax.swing.*;
+import java.awt.event.ActionListener;
+import java.net.URL;
+
+public class MenuItem extends JMenuItem {
+
+ public MenuItem(String title, ActionListener listener) {
+ this(title, null, listener);
+ }
+
+ public MenuItem(String title, String iconPath, ActionListener listener) {
+ super(title);
+ if(iconPath != null) {
+ URL url = getClass().getResource(iconPath);
+ if (url != null) {
+ super.setIcon(new ImageIcon(url));
+ }
+ }
+ if (listener != null) {
+ addActionListener(listener);
+ }
+ }
+}
diff --git a/src/main/java/ui/implementation/components/nav/NavBar.java b/src/main/java/ui/implementation/components/nav/NavBar.java
new file mode 100644
index 0000000..6b14a40
--- /dev/null
+++ b/src/main/java/ui/implementation/components/nav/NavBar.java
@@ -0,0 +1,301 @@
+package ui.implementation.components.nav;
+
+import domain.image.GrayScaleMatrix;
+import domain.image.processing.histogram.Histogramme;
+import domain.common.Mode;
+import jakarta.inject.Inject;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartFrame;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import presenters.NavPresenter;
+import ui.implementation.components.intent.FileChooser;
+import ui.implementation.dialogs.ImageCreatorDialog;
+import ui.implementation.dialogs.GreyScaleImageCreatorDialog;
+import ui.implementation.dialogs.RGBImageCreatorDialog;
+import ui.interfaces.INavBar;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author Jean-Marc Wagner, Laurent Crema
+ */
+public class NavBar extends JMenuBar implements INavBar {
+
+ private JMenu imageMenu;
+ private JMenu drawingMenu;
+ private JMenu fourierMenu;
+ private JMenu linearMenu;
+ private JMenu histogramMenu;
+
+ private NavPresenter presenter;
+
+ @Inject
+ public NavBar(NavPresenter presenter) {
+ this.presenter = presenter;
+ this.presenter.setView(this);
+ initComponents();
+ }
+
+ private void initComponents(){
+
+ imageMenu = new Menu("Image","/net_13_p1.jpg",
+ new Menu("Nouvelle", "/file_65_p3.jpg",
+ new MenuItem("RGB", e -> this.createImage(e, new RGBImageCreatorDialog(new JFrame(),true))),
+ new MenuItem("NG", e -> this.createImage(e, new GreyScaleImageCreatorDialog(new JFrame(),true)))
+ ),
+ new MenuItem("Ouvrir...", "/folder_036_p3.jpg", this::loadImage),
+ new MenuItem("Enregistrer sous...","/dd_27_p3.jpg", e -> {}),
+ new MenuItem("Quitter","/cp_59_p3.jpg", this::quit)
+ );
+
+ drawingMenu = new Menu("Editer","/dd_28_p1.jpg",
+ new Menu("Dessiner", "/display_14_p3.jpg",
+ new MenuItem("Couleur", this::chooseColor),
+ new Menu("Formes",
+ new MenuItem("Pixel", e -> this.setMode(e, Mode.DRAW_PIXEL)),
+ new MenuItem("Ligne", e -> this.setMode(e, Mode.DRAW_LINE)),
+ new MenuItem("Rectangle",e -> this.setMode(e, Mode.DRAW_RECTANGLE)),
+ new MenuItem("Cercle", e -> this.setMode(e, Mode.DRAW_CIRCLE))
+ )
+ ),
+ new Menu("Convertir",
+ new MenuItem("RGB", e -> {}),
+ new MenuItem("NG", e -> {})
+ )
+ );
+
+ fourierMenu = new Menu("Fourier","/cp_51_p1.jpg",
+ new MenuItem("Afficher", "/cp_51_p3.jpg", this::displayFourier)
+ );
+
+ linearMenu = new Menu("Linéaire","/cp_51_p1.jpg",
+ new Menu("Global",
+ new Menu("Formes",
+ new MenuItem("PasseBasIdeal", e -> this.setMode(e, Mode.DRAW_PIXEL)),
+ new MenuItem("PasseHautIdeal", e -> this.setMode(e, Mode.DRAW_LINE)),
+ new MenuItem("PasseBasButterworth",e -> this.setMode(e, Mode.DRAW_RECTANGLE)),
+ new MenuItem("PasseHautButterworth", e -> this.setMode(e, Mode.DRAW_CIRCLE))
+ )
+ )
+ );
+
+ histogramMenu = new Menu("Histogramme","/report_48_hot.jpg",
+ new MenuItem("Afficher", "/report_32_hot.jpg",this::displayHistogram)
+ );
+
+ this.add(imageMenu);
+ this.add(drawingMenu);
+ this.add(fourierMenu);
+ this.add(linearMenu);
+ this.add(histogramMenu);
+
+ drawingMenu.setEnabled(false);
+ fourierMenu.setEnabled(false);
+ linearMenu.setEnabled(false);
+ histogramMenu.setEnabled(false);
+ }
+
+ //####################################################
+
+ private File chooseFile(){
+ FileChooser chooser = new FileChooser("./");
+
+ int dialogResult = chooser.showOpenDialog(this);
+ if (dialogResult != JFileChooser.APPROVE_OPTION) return null;
+
+ return chooser.getSelectedFile();
+ }
+
+ private void loadImage(ActionEvent e) {
+ try
+ {
+ File file = chooseFile();
+ if(file == null) return;
+ presenter.loadImage(file);
+ activeMenusRGB();
+ activeMenusNG();
+ }
+ catch (IOException ex)
+ {
+ System.err.println("Erreur I/O : " + ex.getMessage());
+ }
+ }
+
+ private void quit(ActionEvent e) {
+ System.exit(0);
+ }
+
+ private void createImage(ActionEvent e, ImageCreatorDialog dialog) {
+ dialog.setVisible(true);
+ presenter.createImage(
+ dialog.getRed(), dialog.getGreen(), dialog.getBlue(),
+ dialog.getImageHeight(), dialog.getImageWidth());
+ activeMenusRGB();
+ activeMenusNG();
+ }
+
+ private void setMode(ActionEvent e, Mode mode) {
+ presenter.setMode(mode);
+ }
+
+ private void chooseColor(ActionEvent e) {
+ /*if (imageRGB != null)
+ {
+ Color newC = JColorChooser.showDialog(this,"Couleur du pinceau", couleurPinceauRGB);
+ if (newC != null) couleurPinceauRGB = newC;
+ observer.setCouleurPinceau(couleurPinceauRGB);
+ }
+
+ if (imageNG != null)
+ {
+ GreyScalePicker dialog = new GreyScalePicker(new JFrame(),true, couleurPinceauNG);
+ dialog.setVisible(true);
+ couleurPinceauNG = dialog.getCouleur();
+ }*/
+ }
+
+
+ private void activeMenusNG()
+ {
+ drawingMenu.setEnabled(true);
+ fourierMenu.setEnabled(true);
+ linearMenu.setEnabled(true);
+ histogramMenu.setEnabled(true);
+ }
+
+ private void activeMenusRGB()
+ {
+ drawingMenu.setEnabled(true);
+ fourierMenu.setEnabled(false);
+ linearMenu.setEnabled(false);
+ histogramMenu.setEnabled(false);
+ }
+
+ //####################################################
+
+ private void displayHistogram(ActionEvent e) {
+ GrayScaleMatrix i_graymatrix = presenter.getImageGrayMatrix();
+ int[] histo = Histogramme.Histogramme256(i_graymatrix);
+
+ //Création du dataset
+ XYSeries serie = new XYSeries("Histo");
+ for(int i=0 ; i<256 ; i++) serie.add(i,histo[i]);
+ XYSeriesCollection dataset = new XYSeriesCollection();
+ dataset.addSeries(serie);
+
+ // Creation du chart
+ JFreeChart chart = ChartFactory.createHistogram("Histogramme","Niveaux de gris",
+ "Nombre de pixels", dataset, PlotOrientation.VERTICAL,false,false,false);
+
+ XYPlot plot = chart.getXYPlot();
+ ValueAxis axeX = plot.getDomainAxis();
+ axeX.setRange(0,255);
+ plot.setDomainAxis(axeX);
+
+ // creation d'une frame
+ ChartFrame frame = new ChartFrame("Histogramme de l'image",chart);
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+ private void jMenuItemFourierAfficherPartieImaginaireActionPerformed(ActionEvent e) {
+// try
+// {
+// int f_int[][] = imageNG.getMatrice();
+// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()];
+// for(int i=0 ; i//GEN-BEGIN:initComponents
+ private void initComponents() {
+ jSlider = new javax.swing.JSlider();
+ jPanel = new javax.swing.JPanel();
+ jButtonOk = new javax.swing.JButton();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ setTitle("Choix du niveau de gris");
+ setResizable(false);
+ jSlider.setMajorTickSpacing(255);
+ jSlider.setMaximum(255);
+ jSlider.setMinorTickSpacing(15);
+ jSlider.setPaintLabels(true);
+ jSlider.setPaintTicks(true);
+ jSlider.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSliderStateChanged(evt);
+ }
+ });
+
+ jPanel.setBorder(javax.swing.BorderFactory.createCompoundBorder(null, javax.swing.BorderFactory.createLineBorder(new Color(255, 0, 0))));
+ javax.swing.GroupLayout jPanelLayout = new javax.swing.GroupLayout(jPanel);
+ jPanel.setLayout(jPanelLayout);
+ jPanelLayout.setHorizontalGroup(
+ jPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(0, 62, Short.MAX_VALUE)
+ );
+ jPanelLayout.setVerticalGroup(
+ jPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(0, 45, Short.MAX_VALUE)
+ );
+
+ jButtonOk.setText("Ok");
+ jButtonOk.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButtonOkActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(19, 19, 19)
+ .addComponent(jPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE)
+ .addComponent(jButtonOk, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jButtonOk, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 47, Short.MAX_VALUE)
+ .addComponent(jPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jSlider, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ pack();
+ }// //GEN-END:initComponents
+
+ private void jButtonOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonOkActionPerformed
+ couleur = jSlider.getValue();
+ setVisible(false);
+ dispose();
+ }//GEN-LAST:event_jButtonOkActionPerformed
+
+ private void jSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSliderStateChanged
+ int valeur = jSlider.getValue();
+ Color c = new Color(valeur,valeur,valeur);
+ jPanel.setBackground(c);
+ }//GEN-LAST:event_jSliderStateChanged
+
+ public int getCouleur() { return couleur; }
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String args[]) {
+ java.awt.EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ new GreyScalePicker(new javax.swing.JFrame(), true, 128).setVisible(true);
+ }
+ });
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton jButtonOk;
+ private javax.swing.JPanel jPanel;
+ private javax.swing.JSlider jSlider;
+ // End of variables declaration//GEN-END:variables
+
+}
diff --git a/src/main/java/ui/implementation/dialogs/ImageCreatorDialog.java b/src/main/java/ui/implementation/dialogs/ImageCreatorDialog.java
new file mode 100644
index 0000000..e12a37d
--- /dev/null
+++ b/src/main/java/ui/implementation/dialogs/ImageCreatorDialog.java
@@ -0,0 +1,167 @@
+package ui.implementation.dialogs;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+
+public abstract class ImageCreatorDialog extends JDialog
+{
+ protected Color color;
+ protected int height;
+ protected int width;
+
+ protected JButton confirmButton;
+ protected JPanel colorVisualizer;
+ protected JComponent colorPickerContainer;
+
+ protected JTextField heightInput;
+ protected JTextField widthInput;
+
+ public ImageCreatorDialog(Frame parent, boolean modal)
+ {
+ super(parent, modal);
+ initComponents();
+
+ color = new Color(255,255,255);
+ width = 256;
+ height = 256;
+ }
+
+ private void initComponents() {
+ colorVisualizer = new JPanel();
+ confirmButton = new JButton();
+ widthInput = new JTextField();
+ heightInput = new JTextField();
+
+ setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ setTitle("Choix de la couleur");
+ setResizable(false);
+
+ GroupLayout jPanelLayout = new GroupLayout(colorVisualizer);
+ colorVisualizer.setBorder(
+ BorderFactory.createCompoundBorder(null,
+ BorderFactory.createLineBorder(new Color(255, 0, 0)))
+ );
+ colorVisualizer.setLayout(jPanelLayout);
+
+ jPanelLayout.setHorizontalGroup(
+ jPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGap(0, 62, Short.MAX_VALUE)
+ );
+ jPanelLayout.setVerticalGroup(
+ jPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGap(0, 55, Short.MAX_VALUE)
+ );
+
+ confirmButton.setText("Ok");
+ confirmButton.addActionListener(this::onConfirmClicked);
+
+ widthInput.setHorizontalAlignment(JTextField.CENTER);
+
+ heightInput.setHorizontalAlignment(JTextField.CENTER);
+
+ JLabel widthLabel = new JLabel("Largeur");
+ JLabel heightLabel = new JLabel("Hauteur");
+
+ colorPickerContainer = new JPanel(new BorderLayout());
+
+ GroupLayout layout = new GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(27, 27, 27)
+ .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(widthLabel)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(widthInput, GroupLayout.PREFERRED_SIZE, 97, GroupLayout.PREFERRED_SIZE)
+ .addGap(37, 37, 37)
+ .addComponent(heightLabel)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(heightInput, GroupLayout.PREFERRED_SIZE, 96, GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(51, Short.MAX_VALUE))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(colorVisualizer, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+ .addGap(25, 25, 25)
+ .addComponent(colorPickerContainer)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 135, Short.MAX_VALUE)
+ .addComponent(confirmButton, GroupLayout.PREFERRED_SIZE, 65, GroupLayout.PREFERRED_SIZE)
+ .addGap(26, 26, 26))))
+ );
+
+ layout.linkSize(SwingConstants.HORIZONTAL, heightInput, widthInput);
+
+ layout.setVerticalGroup(
+ layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGap(24, 24, 24)
+ .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(heightInput, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+ .addComponent(heightLabel)
+ .addComponent(widthInput, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+ .addComponent(widthLabel)
+ )
+ .addGap(37, 37, 37)
+ .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(confirmButton, GroupLayout.DEFAULT_SIZE, 57, Short.MAX_VALUE)
+ .addComponent(colorPickerContainer, GroupLayout.DEFAULT_SIZE, 57, Short.MAX_VALUE))
+ .addComponent(colorVisualizer, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ pack();
+ colorVisualizer.setBackground(color);
+ widthInput.setText("256");
+ heightInput.setText("256");
+ }
+
+ protected void onColorChosen(ActionEvent e) {
+ Color color = JColorChooser.showDialog(this,"Couleur de fond", this.color);
+ if (color == null) return;
+ this.color = color;
+ colorVisualizer.setBackground(this.color);
+ }
+
+ protected void onConfirmClicked(ActionEvent e) {
+ try
+ {
+ height = Integer.parseInt(widthInput.getText());
+ width = Integer.parseInt(heightInput.getText());
+
+ setVisible(false);
+ dispose();
+ } catch(NumberFormatException nfe)
+ {
+ JOptionPane.showMessageDialog(this,
+ "Hauteur et Largeur doivent etre entiers !","Erreur",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ public Color getImageColor() {
+ return color;
+ }
+
+ public int getRed() {
+ return color.getRed();
+ }
+
+ public int getGreen() {
+ return color.getGreen();
+ }
+
+ public int getBlue() {
+ return color.getBlue();
+ }
+
+ public int getImageWidth(){
+ return width;
+ }
+
+ public int getImageHeight(){
+ return height;
+ }
+
+ protected abstract void createColorPicker();
+}
diff --git a/src/main/java/ui/implementation/dialogs/RGBImageCreatorDialog.java b/src/main/java/ui/implementation/dialogs/RGBImageCreatorDialog.java
new file mode 100644
index 0000000..8adca0e
--- /dev/null
+++ b/src/main/java/ui/implementation/dialogs/RGBImageCreatorDialog.java
@@ -0,0 +1,24 @@
+package ui.implementation.dialogs;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class RGBImageCreatorDialog extends ImageCreatorDialog
+{
+
+ private JButton colorPickerButton;
+
+ public RGBImageCreatorDialog(Frame parent, boolean modal)
+ {
+ super(parent, modal);
+ createColorPicker();
+ }
+
+ @Override
+ protected void createColorPicker() {
+ colorPickerButton = new JButton();
+ colorPickerButton.setText("Choisir");
+ colorPickerButton.addActionListener(this::onColorChosen);
+ colorPickerContainer.add(colorPickerButton);
+ }
+}
diff --git a/src/main/java/ui/implementation/views/DoubleMatrix.java b/src/main/java/ui/implementation/views/DoubleMatrix.java
new file mode 100644
index 0000000..f7cd691
--- /dev/null
+++ b/src/main/java/ui/implementation/views/DoubleMatrix.java
@@ -0,0 +1,374 @@
+package ui.implementation.views;
+
+import domain.image.GrayScaleMatrix;
+import domain.image.Image;
+import jakarta.inject.Inject;
+import presenters.DoubleMatrixPresenter;
+import ui.implementation.components.image.ImagePanel;
+import ui.implementation.components.intent.FileChooser;
+import ui.interfaces.IDoubleMatrix;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.io.*;
+import java.util.Objects;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+
+/**
+ * @author Jean-Marc Wagner, Laurent Crema
+ */
+public class DoubleMatrix extends JFrame implements IDoubleMatrix
+{
+ private double normalizedWhite;
+ private double normalizedBlack;
+
+ private boolean isUpdating = false;
+
+ private JTabbedPane modeTab;
+ private ImagePanel modulePreviewContainer;
+ private ImagePanel phasePreviewContainer;
+ private ImagePanel realPreviewContainer;
+ private ImagePanel imaginaryPreviewContainer;
+
+ private JSlider whiteSlider;
+ private JTextField whiteTextField;
+ private JLabel maxLabel;
+
+ private JSlider blackSlider;
+ private JTextField blackTextField;
+ private JLabel minLabel;
+
+ private final DoubleMatrixPresenter presenter;
+
+ @Inject
+ public DoubleMatrix(DoubleMatrixPresenter presenter)
+ {
+ this.presenter = presenter;
+ initComponents();
+ presenter.setView(this);
+ initValues();
+ }
+
+ private void initComponents() {
+
+ modulePreviewContainer = new ImagePanel();
+ phasePreviewContainer = new ImagePanel();
+ realPreviewContainer = new ImagePanel();
+ imaginaryPreviewContainer = new ImagePanel();
+
+ blackSlider = buildSlider();
+ whiteSlider = buildSlider();
+
+ blackTextField = buildTextField(Color.BLACK);
+ whiteTextField = buildTextField(null);
+
+ minLabel = buildValueLabel();
+ maxLabel = buildValueLabel();
+ JLabel minValueText = new JLabel("Valeur MIN :");
+ JLabel maxValueText = new JLabel("Valeur MAX :");
+
+ JButton saveButton = new JButton(new ImageIcon(Objects.requireNonNull(getClass().getResource("/dd_27_p3.jpg"))));
+ saveButton.addActionListener(this::saveImage);
+
+ blackSlider.addChangeListener(this::onValueUpdate);
+ whiteSlider.addChangeListener(this::onValueUpdate);
+
+ blackTextField.addActionListener(this::jTextFieldNoirActionPerformed);
+ whiteTextField.addActionListener(this::jTextFieldBlancActionPerformed);
+
+ modeTab = buildModeTab();
+ buildLayout(modeTab, maxValueText, minValueText, minLabel, maxLabel, saveButton);
+
+ setTitle("Fourier");
+ setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ pack();
+ }
+
+ private void initValues(){
+ modeTab.setSelectedIndex(1);
+
+ blackSlider.setValue(0);
+ whiteSlider.setValue(1000);
+ }
+
+ private JSlider buildSlider() {
+ return new JSlider(JSlider.VERTICAL);
+ }
+
+ private JTextField buildTextField(Color background) {
+ JTextField field = new JTextField();
+ field.setFont(new Font("Tahoma", Font.BOLD, 11));
+ field.setForeground(Color.RED);
+ field.setHorizontalAlignment(JTextField.CENTER);
+ if (background != null) field.setBackground(background);
+ return field;
+ }
+
+ private JLabel buildValueLabel() {
+ JLabel label = new JLabel("0.0");
+ label.setFont(new Font("Tahoma", Font.BOLD, 11));
+ label.setForeground(Color.BLUE);
+ return label;
+ }
+
+ private JTabbedPane buildModeTab(){
+
+ JTabbedPane modeTabs = new JTabbedPane();
+ modeTabs.addTab("Module", modulePreviewContainer);
+ modeTabs.addTab("Phase", phasePreviewContainer);
+ modeTabs.addTab("Partie réelle", realPreviewContainer);
+ modeTabs.addTab("Partie imaginaire", imaginaryPreviewContainer);
+ modeTabs.addChangeListener(e -> {
+ switch (modeTabs.getSelectedIndex()) {
+ case 0 -> presenter.loadModule();
+ case 1 -> presenter.loadPhase();
+ case 2 -> presenter.loadReal();
+ case 3 -> presenter.loadImaginary();
+ }
+ });
+ return modeTabs;
+ }
+
+ private void buildLayout(JTabbedPane modeTab,
+ JLabel labelMax, JLabel labelMin,
+ JLabel valeurMin, JLabel valeurMax,
+ JButton saveButton) {
+
+ JPanel rightPanel = new JPanel();
+ GroupLayout rightLayout = new GroupLayout(rightPanel);
+ rightPanel.setLayout(rightLayout);
+
+ rightLayout.setHorizontalGroup(
+ rightLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(whiteTextField, GroupLayout.PREFERRED_SIZE, 90, GroupLayout.PREFERRED_SIZE)
+ .addGroup(rightLayout.createSequentialGroup()
+ .addComponent(blackSlider)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(whiteSlider)
+ )
+ .addComponent(blackTextField, GroupLayout.PREFERRED_SIZE, 90, GroupLayout.PREFERRED_SIZE)
+ );
+
+ rightLayout.setVerticalGroup(
+ rightLayout.createSequentialGroup()
+ .addComponent(whiteTextField, GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER)
+ .addComponent(blackSlider)
+ .addComponent(whiteSlider)
+ )
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(blackTextField, GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+ );
+
+ // -------------------------
+ // MAIN LAYOUT
+ // -------------------------
+ GroupLayout layout = new GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+
+ // TOP BAR
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(labelMin)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(valeurMin)
+ .addGap(50)
+ .addComponent(labelMax)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(valeurMax)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED,
+ GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(saveButton)
+ .addContainerGap()
+ )
+
+ // MAIN ROW
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(modeTab, 400, 400, Short.MAX_VALUE)
+ .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(rightPanel, GroupLayout.PREFERRED_SIZE,
+ GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+ .addContainerGap()
+ )
+ );
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+
+ // TOP BAR
+ .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(labelMin)
+ .addComponent(valeurMin)
+ .addComponent(labelMax)
+ .addComponent(valeurMax)
+ .addComponent(saveButton)
+ )
+
+ .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+
+ // MAIN CONTENT
+ .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addComponent(modeTab)
+ .addComponent(rightPanel)
+ )
+ );
+ }
+
+ private File chooseFile(){
+ FileChooser chooser = new FileChooser("./");
+
+ int dialogResult = chooser.showOpenDialog(this);
+ if (dialogResult != JFileChooser.APPROVE_OPTION) return null;
+
+ return chooser.getSelectedFile();
+ }
+
+ private void onValueUpdate(ChangeEvent e) {
+ if (isUpdating) return;
+ isUpdating = true;
+
+ try {
+ int black = blackSlider.getValue();
+ int white = whiteSlider.getValue();
+
+ int minGap = 1;
+ if (white - black < minGap) {
+ if (blackSlider.getValueIsAdjusting()) {
+ black = white - minGap;
+ blackSlider.setValue(black);
+ } else {
+ white = black + minGap;
+ whiteSlider.setValue(white);
+ }
+ }
+
+ normalizedBlack = (double) black / blackSlider.getMaximum();
+ normalizedWhite = (double) white / whiteSlider.getMaximum();
+
+ switch (modeTab.getSelectedIndex()) {
+ case 0 -> presenter.clipModule(normalizedBlack, normalizedWhite);
+ case 1 -> presenter.clipPhase(normalizedBlack, normalizedWhite);
+ case 2 -> presenter.clipReal(normalizedBlack, normalizedWhite);
+ case 3 -> presenter.clipImaginary(normalizedBlack, normalizedWhite);
+ }
+
+ } finally {
+ isUpdating = false;
+ }
+ }
+
+
+ @Override
+ public void updateModule(GrayScaleMatrix source, GrayScaleMatrix clipped) {
+ updatePreview(modulePreviewContainer, source, clipped);
+ }
+
+ @Override
+ public void updatePhase(GrayScaleMatrix source, GrayScaleMatrix clipped) {
+ updatePreview(phasePreviewContainer, source, clipped);
+ }
+
+ @Override
+ public void updateReal(GrayScaleMatrix source, GrayScaleMatrix clipped) {
+ updatePreview(realPreviewContainer, source, clipped);
+ }
+
+ @Override
+ public void updateImaginary(GrayScaleMatrix source, GrayScaleMatrix clipped) {
+ updatePreview(imaginaryPreviewContainer, source, clipped);
+ }
+
+ private void updatePreview(ImagePanel imagePanel, GrayScaleMatrix source, GrayScaleMatrix matrix) {
+ try {
+ imagePanel.loadImage(matrix);
+ updateLabels(source.getMinValue(), source.getMaxValue());
+ updateTextFields(
+ presenter.getValueAt(source, normalizedBlack),
+ presenter.getValueAt(source, normalizedWhite)
+ );
+ } catch (Exception e){
+ displayErrorMessage("Oups !", e.getMessage());
+ }
+ }
+
+ private void updateTextFields(double black, double white) {
+ blackTextField.setText(String.format("%.5f", black));
+ whiteTextField.setText(String.format("%.5f", white));
+ }
+
+ private void updateLabels(double black, double white) {
+ minLabel.setText(String.format("%.5f", black));
+ maxLabel.setText(String.format("%.5f", white));
+ }
+
+ private void saveImage(ActionEvent evt) {
+
+ File file = chooseFile();
+ if (file != null) {
+ try {
+ presenter.saveImage(null);
+ //image.enregistreFormatPNG(fichier);
+ } catch (IOException ex) {
+ System.err.println("Erreur I/O : " + ex.getMessage());
+ }
+ }
+ }
+
+ private void displayErrorMessage(String title, String message) {
+ JOptionPane.showMessageDialog(
+ this,
+ message,
+ title,
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
+
+ private void jTextFieldNoirActionPerformed(ActionEvent evt) {
+// double val = Double.parseDouble(jTextFieldNoir.getText());
+//
+// double black = presenter.getBlackLevel();
+// double white = presenter.getWhiteLevel();
+//
+// if (val < black)
+// {
+// jSliderNoir.setValue(0);
+// return;
+// }
+// if (val >= normalizedWhite)
+// {
+// jSliderNoir.setValue(jSliderBlanc.getValue()-1);
+// return;
+// }
+// int s = (int)((double)D*(val-black)/(white-black)+0.5);
+// jSliderNoir.setValue(s);
+
+ }
+
+ private void jTextFieldBlancActionPerformed(ActionEvent evt) {
+// double val = Double.parseDouble(jTextFieldBlanc.getText());
+// double black = presenter.getBlackLevel();
+// double white = presenter.getWhiteLevel();
+//
+// if (val > white)
+// {
+// jSliderBlanc.setValue(D);
+// return;
+// }
+// if (val <= normalizedBlack)
+// {
+// jSliderBlanc.setValue(jSliderNoir.getValue()+1);
+// return;
+// }
+// int s = (int)((double)D*(val-black)/(white-black)+0.5);
+// jSliderBlanc.setValue(s);
+ }
+}
diff --git a/src/main/java/ui/implementation/views/MainView.java b/src/main/java/ui/implementation/views/MainView.java
new file mode 100644
index 0000000..03afaa0
--- /dev/null
+++ b/src/main/java/ui/implementation/views/MainView.java
@@ -0,0 +1,201 @@
+package ui.implementation.views;
+
+import com.google.inject.Inject;
+import domain.common.Mode;
+import domain.image.Image;
+import infrastructure.ui.ImageMapper;
+import presenters.MainPresenter;
+import ui.implementation.components.image.ImagePanel;
+import ui.implementation.components.nav.NavBar;
+import ui.interfaces.IMainView;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionAdapter;
+import java.awt.image.BufferedImage;
+
+/**
+ * @author Jean-Marc Wagner, Laurent Crema
+ */
+public class MainView extends JFrame implements IMainView
+{
+ private Mode currentMode;
+ private ImagePanel imagePreviewContainer;
+ private final MainPresenter presenter;
+
+ @Inject
+ public MainView(MainPresenter presenter){
+ this.presenter = presenter;
+ this.presenter.setView(this);
+ initComponents();
+ }
+
+ public void setNavBar(NavBar navBar) {
+ setJMenuBar(navBar);
+ }
+
+ private void initComponents() {
+ imagePreviewContainer = new ImagePanel();
+
+ imagePreviewContainer.addMouseMotionListener(new MouseMotionAdapter() {
+ @Override
+ public void mouseDragged(MouseEvent e) {
+ drawPixel(e.getX(), e.getY(), 255, 0, 0);
+ }
+ });
+
+ imagePreviewContainer.addMouseListener(new MouseAdapter() {
+
+ private Point startPoint;
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ startPoint = new Point(e.getX(), e.getY());
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ Point endPoint = new Point(e.getX(), e.getY());
+ if (startPoint == null) return;
+
+ drawShape(
+ startPoint.x,
+ endPoint.x,
+ startPoint.y,
+ endPoint.y,
+ 255, 0, 0
+ );
+
+ startPoint = null;
+ }
+ });
+
+ GroupLayout layout = new GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(imagePreviewContainer)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(imagePreviewContainer)
+ .addContainerGap())
+ );
+
+ setSize(new Dimension(500, 400));
+ setLocationRelativeTo(null);
+ }
+
+ public void drawPixel(int x, int y, int red, int green, int blue){
+ try {
+ Point p = imagePreviewContainer.toImageCoordinates(x, y);
+ if(p == null) return;
+ presenter.drawPixel((int)p.getX(), (int)p.getY(), red, green, blue);
+ } catch (Exception e) {
+ displayErrorMessage("Oups !",
+ "Impossible de dessiner sur l'image pour le moment : " + e.getMessage());
+ }
+ }
+
+ public void drawShape(int x1, int x2, int y1, int y2, int red, int green, int blue){
+ try {
+ Point p1 = imagePreviewContainer.toImageCoordinates(x1, y1);
+ Point p2 = imagePreviewContainer.toImageCoordinates(x2, y2);
+ if(p1 == null || p2 == null) return;
+ presenter.drawShape(
+ (int)p1.getX(), (int)p2.getX(), (int) p1.getY(), (int) p2.getY(),
+ red, green, blue);
+
+ } catch (Exception e) {
+ displayErrorMessage("Oups !",
+ "Impossible de dessiner sur l'image pour le moment : " + e.getMessage());
+ }
+ }
+
+
+// public void SelectCercleDetected(DeuxClicsEvent e)
+// {
+// if (presenter.isCircleModeActive())
+// {
+// try
+// {
+// if (imageRGB != null)
+// imageRGB.DessineCercle(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauRGB);
+// if (imageNG != null)
+// imageNG.DessineCercle(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauNG);
+// }
+// catch (CImageRGBException ex)
+// { System.out.println("Erreur RGB : " + ex.getMessage()); }
+// catch (CImageNGException ex)
+// { System.out.println("Erreur NG : " + ex.getMessage()); }
+// }
+// }
+//
+// public void SelectCercleFillDetected(DeuxClicsEvent e)
+// {
+// if (presenter.isPlainCircleModeActive())
+// {
+// try
+// {
+// if (imageRGB != null)
+// imageRGB.RemplitCercle(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauRGB);
+// if (imageNG != null)
+// imageNG.RemplitCercle(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauNG);
+// }
+// catch (CImageRGBException ex)
+// { System.out.println("Erreur RGB : " + ex.getMessage()); }
+// catch (CImageNGException ex)
+// { System.out.println("Erreur NG : " + ex.getMessage()); }
+// }
+// }
+//
+// public void SelectRectFillDetected(DeuxClicsEvent e)
+// {
+// if (presenter.isPlainRectangleActive())
+// {
+// try
+// {
+// if (imageRGB != null)
+// imageRGB.RemplitRect(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauRGB);
+// if (imageNG != null)
+// imageNG.RemplitRect(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauNG);
+// }
+// catch (CImageRGBException ex)
+// { System.out.println("Erreur RGB : " + ex.getMessage()); }
+// catch (CImageNGException ex)
+// { System.out.println("Erreur NG : " + ex.getMessage()); }
+// }
+// }
+
+ @Override
+ public void displayImage(Image image) {
+ try {
+ BufferedImage bufferedImage = ImageMapper.toBufferedImage(image);
+ imagePreviewContainer.loadImage(bufferedImage);
+ } catch (Exception ex) {
+ displayErrorMessage("Oups !", ex.getMessage());
+ }
+ }
+
+ @Override
+ public void changeMode(Mode m) {
+
+ }
+
+ private void displayErrorMessage(String title, String message) {
+ JOptionPane.showMessageDialog(
+ this,
+ message,
+ title,
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
+
+}
diff --git a/src/main/java/ui/interfaces/IDoubleMatrix.java b/src/main/java/ui/interfaces/IDoubleMatrix.java
new file mode 100644
index 0000000..42ec9c3
--- /dev/null
+++ b/src/main/java/ui/interfaces/IDoubleMatrix.java
@@ -0,0 +1,11 @@
+package ui.interfaces;
+
+import domain.image.GrayScaleMatrix;
+
+public interface IDoubleMatrix {
+
+ void updateModule(GrayScaleMatrix source, GrayScaleMatrix clipped);
+ void updatePhase(GrayScaleMatrix source, GrayScaleMatrix clipped);
+ void updateReal(GrayScaleMatrix source, GrayScaleMatrix clipped);
+ void updateImaginary(GrayScaleMatrix source, GrayScaleMatrix clipped);
+}
diff --git a/src/main/java/ui/interfaces/IMainView.java b/src/main/java/ui/interfaces/IMainView.java
new file mode 100644
index 0000000..54ed61b
--- /dev/null
+++ b/src/main/java/ui/interfaces/IMainView.java
@@ -0,0 +1,15 @@
+package ui.interfaces;
+
+import domain.common.Mode;
+import domain.image.Image;
+import presenters.DoubleMatrixPresenter;
+import presenters.MainPresenter;
+
+import java.awt.image.BufferedImage;
+
+public interface IMainView {
+
+ void changeMode(Mode m);
+ void displayImage(Image image);
+
+}
diff --git a/src/main/java/ui/interfaces/INavBar.java b/src/main/java/ui/interfaces/INavBar.java
new file mode 100644
index 0000000..7f9b597
--- /dev/null
+++ b/src/main/java/ui/interfaces/INavBar.java
@@ -0,0 +1,8 @@
+package ui.interfaces;
+
+import presenters.NavPresenter;
+
+public interface INavBar {
+
+
+}
diff --git a/src/main/resources/Thumbs.db b/src/main/resources/Thumbs.db
new file mode 100644
index 0000000..79d32e6
Binary files /dev/null and b/src/main/resources/Thumbs.db differ
diff --git a/src/main/resources/cp_51_p1.jpg b/src/main/resources/cp_51_p1.jpg
new file mode 100644
index 0000000..5cf394a
Binary files /dev/null and b/src/main/resources/cp_51_p1.jpg differ
diff --git a/src/main/resources/cp_51_p3.jpg b/src/main/resources/cp_51_p3.jpg
new file mode 100644
index 0000000..afd1968
Binary files /dev/null and b/src/main/resources/cp_51_p3.jpg differ
diff --git a/src/main/resources/cp_59_p3.jpg b/src/main/resources/cp_59_p3.jpg
new file mode 100644
index 0000000..a73120a
Binary files /dev/null and b/src/main/resources/cp_59_p3.jpg differ
diff --git a/src/main/resources/dd_27_p3.jpg b/src/main/resources/dd_27_p3.jpg
new file mode 100644
index 0000000..50859c3
Binary files /dev/null and b/src/main/resources/dd_27_p3.jpg differ
diff --git a/src/main/resources/dd_28_p1.jpg b/src/main/resources/dd_28_p1.jpg
new file mode 100644
index 0000000..4b09e7b
Binary files /dev/null and b/src/main/resources/dd_28_p1.jpg differ
diff --git a/src/main/resources/display_14_p3.jpg b/src/main/resources/display_14_p3.jpg
new file mode 100644
index 0000000..3ffc98d
Binary files /dev/null and b/src/main/resources/display_14_p3.jpg differ
diff --git a/src/main/resources/file_65_p3.jpg b/src/main/resources/file_65_p3.jpg
new file mode 100644
index 0000000..7ed010b
Binary files /dev/null and b/src/main/resources/file_65_p3.jpg differ
diff --git a/src/main/resources/folder_036_p3.jpg b/src/main/resources/folder_036_p3.jpg
new file mode 100644
index 0000000..3498887
Binary files /dev/null and b/src/main/resources/folder_036_p3.jpg differ
diff --git a/src/main/resources/net_13_p1.jpg b/src/main/resources/net_13_p1.jpg
new file mode 100644
index 0000000..1c7f09c
Binary files /dev/null and b/src/main/resources/net_13_p1.jpg differ
diff --git a/src/main/resources/report_32_hot.jpg b/src/main/resources/report_32_hot.jpg
new file mode 100644
index 0000000..e95b666
Binary files /dev/null and b/src/main/resources/report_32_hot.jpg differ
diff --git a/src/main/resources/report_48_hot.jpg b/src/main/resources/report_48_hot.jpg
new file mode 100644
index 0000000..019ca82
Binary files /dev/null and b/src/main/resources/report_48_hot.jpg differ