From b177ea5a381f40fe45237a365ed9a7a432cd8b45 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 22 Sep 2026 13:30:03 +0200 Subject: [PATCH] Update architecture --- src/main/java/app/AppModule.java | 8 +- src/main/java/app/SwingNavigator.java | 10 +- src/main/java/app/state/AppState.java | 28 +- src/main/java/app/state/AppStateListener.java | 9 + .../java/app/state/DoubleMatrixState.java | 46 ++- src/main/java/app/state/FilteringState.java | 57 +++- src/main/java/app/state/State.java | 20 +- .../filtering/HighPassChangedEvent.java | 5 - .../events/filtering/LowPassChangedEvent.java | 5 - .../domain/events/listeners/Listener.java | 5 + .../filtering/GlobalLinearFiltering.java | 285 +++++++++++------- .../java/presenters/FilteringPresenter.java | 55 ---- src/main/java/presenters/MainPresenter.java | 64 ++-- src/main/java/presenters/NavPresenter.java | 29 +- .../doubleMatrix/DoubleMatrixListener.java | 12 + .../DoubleMatrixPresenter.java | 46 +-- .../filtering/FilteringListener.java | 11 + .../filtering/FilteringPresenter.java | 79 +++++ src/main/java/services/FilteringService.java | 7 + src/main/java/services/ImageService.java | 39 +-- .../java/ui/abstractions/AbstractView.java | 7 + .../components/controls/IntensitySlider.java | 17 ++ .../{button => controls}/SaveButton.java | 2 +- .../components/image/TabbedImagePanel.java | 29 ++ .../ui/implementation/views/DoubleMatrix.java | 9 +- ...eringView.java => IdealFilteringView.java} | 92 +++--- .../views/NonIdealFilteringView.java | 190 ++++++++++++ 27 files changed, 792 insertions(+), 374 deletions(-) create mode 100644 src/main/java/app/state/AppStateListener.java delete mode 100644 src/main/java/domain/events/filtering/HighPassChangedEvent.java delete mode 100644 src/main/java/domain/events/filtering/LowPassChangedEvent.java create mode 100644 src/main/java/domain/events/listeners/Listener.java delete mode 100644 src/main/java/presenters/FilteringPresenter.java create mode 100644 src/main/java/presenters/doubleMatrix/DoubleMatrixListener.java rename src/main/java/presenters/{ => doubleMatrix}/DoubleMatrixPresenter.java (75%) create mode 100644 src/main/java/presenters/filtering/FilteringListener.java create mode 100644 src/main/java/presenters/filtering/FilteringPresenter.java create mode 100644 src/main/java/ui/implementation/components/controls/IntensitySlider.java rename src/main/java/ui/implementation/components/{button => controls}/SaveButton.java (87%) create mode 100644 src/main/java/ui/implementation/components/image/TabbedImagePanel.java rename src/main/java/ui/implementation/views/{FilteringView.java => IdealFilteringView.java} (82%) create mode 100644 src/main/java/ui/implementation/views/NonIdealFilteringView.java diff --git a/src/main/java/app/AppModule.java b/src/main/java/app/AppModule.java index 12b015c..7ef01a9 100644 --- a/src/main/java/app/AppModule.java +++ b/src/main/java/app/AppModule.java @@ -5,14 +5,14 @@ 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.FilteringPresenter; +import presenters.doubleMatrix.DoubleMatrixPresenter; +import presenters.filtering.FilteringPresenter; import presenters.MainPresenter; import presenters.NavPresenter; import services.ImageService; import ui.implementation.components.nav.NavBar; import ui.implementation.views.DoubleMatrix; -import ui.implementation.views.FilteringView; +import ui.implementation.views.IdealFilteringView; import ui.implementation.views.MainView; import ui.abstractions.IDoubleMatrix; import ui.abstractions.IFilteringView; @@ -40,7 +40,7 @@ public class AppModule extends AbstractModule { bind(INavBar.class).to(NavBar.class); bind(IMainView.class).to(MainView.class); bind(IDoubleMatrix.class).to(DoubleMatrix.class); - bind(IFilteringView.class).to(FilteringView.class); + bind(IFilteringView.class).to(IdealFilteringView.class); //--- Presentation layer --- bind(MainPresenter.class); diff --git a/src/main/java/app/SwingNavigator.java b/src/main/java/app/SwingNavigator.java index a8f8522..75d44f8 100644 --- a/src/main/java/app/SwingNavigator.java +++ b/src/main/java/app/SwingNavigator.java @@ -3,11 +3,9 @@ package app; import com.google.inject.Inject; import com.google.inject.Provider; import ui.implementation.views.DoubleMatrix; -import ui.implementation.views.FilteringView; +import ui.implementation.views.IdealFilteringView; import ui.implementation.views.ViewInitializer; -import javax.swing.text.View; - /** * Concrete implement of the INavigator interface. * Implements abstract endpoints to displays views in a Swing way @@ -16,12 +14,12 @@ import javax.swing.text.View; public class SwingNavigator implements INavigator { private final Provider doubleMatrixProvider; - private final Provider filteringProvider; + private final Provider filteringProvider; @Inject public SwingNavigator( Provider doubleMatrixProvider, - Provider filteringProvider + Provider filteringProvider ) { this.doubleMatrixProvider = doubleMatrixProvider; this.filteringProvider = filteringProvider; @@ -36,7 +34,7 @@ public class SwingNavigator implements INavigator { @Override public void showFiltering() { - FilteringView filteringView = filteringProvider.get(); + IdealFilteringView filteringView = filteringProvider.get(); ViewInitializer.Initialize(filteringView); filteringView.setVisible(true); } diff --git a/src/main/java/app/state/AppState.java b/src/main/java/app/state/AppState.java index d0ff4fc..a160e2d 100644 --- a/src/main/java/app/state/AppState.java +++ b/src/main/java/app/state/AppState.java @@ -6,35 +6,47 @@ import com.google.inject.Singleton; import domain.common.Mode; import domain.events.ImageChangedEvent; import domain.events.ModeChangedEvent; +import domain.events.listeners.Listener; import domain.image.Image; +import java.util.ArrayList; +import java.util.List; + /** * Represents the global state of the app */ @Singleton public class AppState extends State { - private Mode mode; private Image rgbImage; + private final List listeners = new ArrayList<>(); @Inject - public AppState(EventBus eventBus) { - super(eventBus); + public AppState() {} + + public void subscribe(AppStateListener listener) { + listeners.add(listener); + } + + public void unsubscribe(AppStateListener listener) { + listeners.remove(listener); } public void setMode(Mode mode) { this.mode = mode; - this.eventBus.post(new ModeChangedEvent(mode)); + listeners.forEach(listener -> listener.onModeChanged(mode)); } public void setImage(Image image) { this.rgbImage = image; - this.eventBus.post(new ImageChangedEvent(image)); + listeners.forEach(listener -> listener.onImageChanged(image)); } - public Image getImage(){ - return this.rgbImage; + public Image getImage() { + return rgbImage; } - public Mode getMode() { return this.mode; } + public Mode getMode() { + return mode; + } } diff --git a/src/main/java/app/state/AppStateListener.java b/src/main/java/app/state/AppStateListener.java new file mode 100644 index 0000000..a3b0ecc --- /dev/null +++ b/src/main/java/app/state/AppStateListener.java @@ -0,0 +1,9 @@ +package app.state; + +import domain.common.Mode; +import domain.image.Image; + +public interface AppStateListener { + void onModeChanged(Mode mode); + void onImageChanged(Image image); +} diff --git a/src/main/java/app/state/DoubleMatrixState.java b/src/main/java/app/state/DoubleMatrixState.java index 71815c6..6db72f3 100644 --- a/src/main/java/app/state/DoubleMatrixState.java +++ b/src/main/java/app/state/DoubleMatrixState.java @@ -1,22 +1,32 @@ 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; +import presenters.doubleMatrix.DoubleMatrixListener; + +import java.util.ArrayList; +import java.util.List; public class DoubleMatrixState extends State { - private ComplexMatrix fourier; private GrayScaleMatrix module; private GrayScaleMatrix phase; private GrayScaleMatrix real; private GrayScaleMatrix imaginary; + private final List listeners = new ArrayList<>(); + @Inject - public DoubleMatrixState() { super(new EventBus()); } + public DoubleMatrixState() {} + + public void subscribe(DoubleMatrixListener listener) { + listeners.add(listener); + } + + public void unsubscribe(DoubleMatrixListener listener) { + listeners.remove(listener); + } public ComplexMatrix getFourier() { return fourier; @@ -24,26 +34,42 @@ public class DoubleMatrixState extends State { public void setFourier(ComplexMatrix fourier) { this.fourier = fourier; - this.eventBus.post(new ComplexMatrixChanged(fourier)); + listeners.forEach(listener -> listener.onFourierChanged(fourier)); } public void setModule(GrayScaleMatrix matrix) { this.module = matrix; - this.eventBus.post(new ModuleChangedEvent(matrix)); + listeners.forEach(listener -> listener.onModuleChanged(matrix)); } public void setPhase(GrayScaleMatrix matrix) { this.phase = matrix; - this.eventBus.post(new PhaseChangedEvent(matrix)); + listeners.forEach(listener -> listener.onPhaseChanged(matrix)); } public void setReal(GrayScaleMatrix matrix) { this.real = matrix; - this.eventBus.post(new RealChangedEvent(matrix)); + listeners.forEach(listener -> listener.onRealChanged(matrix)); } public void setImaginary(GrayScaleMatrix matrix) { this.imaginary = matrix; - this.eventBus.post(new ImaginaryChangedEvent(matrix)); + listeners.forEach(listener -> listener.onImaginaryChanged(matrix)); + } + + public GrayScaleMatrix getModule() { + return module; + } + + public GrayScaleMatrix getPhase() { + return phase; + } + + public GrayScaleMatrix getReal() { + return real; + } + + public GrayScaleMatrix getImaginary() { + return imaginary; } } \ No newline at end of file diff --git a/src/main/java/app/state/FilteringState.java b/src/main/java/app/state/FilteringState.java index 03d64ee..f53bce2 100644 --- a/src/main/java/app/state/FilteringState.java +++ b/src/main/java/app/state/FilteringState.java @@ -1,27 +1,64 @@ package app.state; -import com.google.common.eventbus.EventBus; import com.google.inject.Inject; -import domain.events.filtering.HighPassChangedEvent; -import domain.events.filtering.LowPassChangedEvent; -import domain.events.fourier.ModuleChangedEvent; -import domain.image.GrayScaleMatrix; import domain.image.Image; +import presenters.filtering.FilteringListener; + +import java.util.ArrayList; +import java.util.List; public class FilteringState extends State { - private Image lowPass; private Image highPass; + private Image butterworthLowPass; + private Image butterworthHighPass; + + private final List listeners = new ArrayList<>(); @Inject - public FilteringState() { super(new EventBus()); } + public FilteringState() {} + + public void subscribe(FilteringListener listener) { + listeners.add(listener); + } + + public void unsubscribe(FilteringListener listener) { + listeners.remove(listener); + } public void setLowPass(Image image) { - this.eventBus.post(new LowPassChangedEvent(image)); + lowPass = image; + listeners.forEach(listener -> listener.onLowPassChanged(image)); } public void setHighPass(Image image) { - this.eventBus.post(new HighPassChangedEvent(image)); + highPass = image; + listeners.forEach(listener -> listener.onHighPassChanged(image)); } -} + public void setButterworthLowPass(Image image) { + butterworthLowPass = image; + listeners.forEach(listener -> listener.onButterworthLowPassChanged(image)); + } + + public void setButterworthHighPass(Image image) { + butterworthHighPass = image; + listeners.forEach(listener -> listener.onButterworthHighPassChanged(image)); + } + + public Image getLowPass() { + return lowPass; + } + + public Image getHighPass() { + return highPass; + } + + public Image getButterworthLowPass() { + return butterworthLowPass; + } + + public Image getButterworthHighPass() { + return butterworthHighPass; + } +} \ No newline at end of file diff --git a/src/main/java/app/state/State.java b/src/main/java/app/state/State.java index 83f5b35..29cfc3d 100644 --- a/src/main/java/app/state/State.java +++ b/src/main/java/app/state/State.java @@ -2,6 +2,10 @@ package app.state; import com.google.common.eventbus.EventBus; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + /** * Represents any states that could ever exist. * In order to notify the caller about any change made to internal properties of a children state, the caller @@ -9,18 +13,4 @@ import com.google.common.eventbus.EventBus; * @author Laurent Crema */ public abstract class State { - - protected EventBus eventBus; - - public State(EventBus eventBus) { - this.eventBus = eventBus; - } - - /** - * Register the caller so it can be notifed from changes within the state - * @param obj the caller itself - */ - public void register(Object obj) { - eventBus.register(obj); - } -} +} \ No newline at end of file diff --git a/src/main/java/domain/events/filtering/HighPassChangedEvent.java b/src/main/java/domain/events/filtering/HighPassChangedEvent.java deleted file mode 100644 index 4095576..0000000 --- a/src/main/java/domain/events/filtering/HighPassChangedEvent.java +++ /dev/null @@ -1,5 +0,0 @@ -package domain.events.filtering; - -import domain.image.Image; - -public record HighPassChangedEvent(Image image) { } \ No newline at end of file diff --git a/src/main/java/domain/events/filtering/LowPassChangedEvent.java b/src/main/java/domain/events/filtering/LowPassChangedEvent.java deleted file mode 100644 index f5bbca4..0000000 --- a/src/main/java/domain/events/filtering/LowPassChangedEvent.java +++ /dev/null @@ -1,5 +0,0 @@ -package domain.events.filtering; - -import domain.image.Image; - -public record LowPassChangedEvent(Image image) { } \ No newline at end of file diff --git a/src/main/java/domain/events/listeners/Listener.java b/src/main/java/domain/events/listeners/Listener.java new file mode 100644 index 0000000..0fa3d03 --- /dev/null +++ b/src/main/java/domain/events/listeners/Listener.java @@ -0,0 +1,5 @@ +package domain.events.listeners; + +public interface Listener { + +} diff --git a/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java b/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java index d04a52f..dc89576 100644 --- a/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java +++ b/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java @@ -13,136 +13,96 @@ import java.util.List; public class GlobalLinearFiltering { public static GrayScaleMatrix lowPassFiltering(GrayScaleMatrix matrix, double cutoff) { - int width = matrix.getWidth(); int height = matrix.getHeight(); - double minCutoff = 0; - double maxCutoff = Math.sqrt(Math.pow(width / 2.0, 2) + Math.pow(height / 2.0, 2)); + validateCutoff(cutoff, width, height); - if(cutoff < minCutoff || cutoff > maxCutoff){ - throw new IllegalArgumentException( - String.format("Cutoff value is out of bond (%f - %f)", minCutoff, maxCutoff) - ); - } + Mat mat = toMat(matrix); + Mat spectrum = fourierTransform(mat); - Mat mat = new Mat(height, width, CvType.CV_64F); + shiftSpectrum(spectrum); + applyIdealLowPassFilter(spectrum, cutoff); + shiftSpectrum(spectrum); - double[][] data = matrix.getRawData(); + Mat filtered = inverseFourierTransform(spectrum); - for (int y = 0; y < height; y++) { - mat.put(y, 0, data[y]); - } + return toGrayScaleMatrix(filtered); + } - // Fourier transform - Mat spectrum = new Mat(); - Core.dft(mat, spectrum, Core.DFT_COMPLEX_OUTPUT); + public static GrayScaleMatrix highPassFiltering(GrayScaleMatrix matrix, double cutoff) { + int width = matrix.getWidth(); + int height = matrix.getHeight(); + + validateCutoff(cutoff, width, height); + + Mat mat = toMat(matrix); + Mat spectrum = fourierTransform(mat); + + shiftSpectrum(spectrum); + applyIdealHighPassFilter(spectrum, cutoff); + shiftSpectrum(spectrum); + + Mat filtered = inverseFourierTransform(spectrum); + + return toGrayScaleMatrix(filtered); + } + + public static GrayScaleMatrix butterworthLowPassFiltering(GrayScaleMatrix matrix, double cutoff, int order) { + validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight()); + + Mat spectrum = fourierTransform(toMat(matrix)); + + shiftSpectrum(spectrum); + applyButterworthLowPassFilter(spectrum, cutoff, order); + shiftSpectrum(spectrum); + + return toGrayScaleMatrix(inverseFourierTransform(spectrum)); + } + + public static GrayScaleMatrix butterworthHighPassFiltering(GrayScaleMatrix matrix, double cutoff, int order) { + validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight()); + + Mat spectrum = fourierTransform(toMat(matrix)); + + shiftSpectrum(spectrum); + applyButterworthHighPassFilter(spectrum, cutoff, order); + shiftSpectrum(spectrum); + + return toGrayScaleMatrix(inverseFourierTransform(spectrum)); + } + + private static void applyIdealLowPassFilter(Mat spectrum, double cutoff) { + int width = spectrum.cols(); + int height = spectrum.rows(); - // Shift zero frequency to the center int cx = width / 2; int cy = height / 2; - Mat q0 = spectrum.submat(0, cy, 0, cx); - Mat q1 = spectrum.submat(0, cy, cx, width); - Mat q2 = spectrum.submat(cy, height, 0, cx); - Mat q3 = spectrum.submat(cy, height, cx, width); - - Mat tmp = new Mat(); - - q0.copyTo(tmp); - q3.copyTo(q0); - tmp.copyTo(q3); - - q1.copyTo(tmp); - q2.copyTo(q1); - tmp.copyTo(q2); - for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2)); + + double distance = Math.sqrt( + Math.pow(x - cx, 2) + Math.pow(y - cy, 2) + ); if (distance > cutoff) { spectrum.put(y, x, 0, 0); } } } - - // Shift back - q0.copyTo(tmp); - q3.copyTo(q0); - tmp.copyTo(q3); - - q1.copyTo(tmp); - q2.copyTo(q1); - tmp.copyTo(q2); - - // Inverse Fourier transform - Mat filtered = new Mat(); - - Core.idft( - spectrum, - filtered, - Core.DFT_REAL_OUTPUT | Core.DFT_SCALE - ); - - // Convert result back to your Image - double[][] pixels = new double[height][width]; - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - pixels[y][x] = Math.clamp(filtered.get(y, x)[0], 0.0, 255.0); - } - } - - return new GrayScaleMatrix(pixels); } - public static GrayScaleMatrix highPassFiltering(GrayScaleMatrix matrix, double cutoff){ - int width = matrix.getWidth(); - int height = matrix.getHeight(); + private static void applyIdealHighPassFilter(Mat spectrum, double cutoff) { + int width = spectrum.cols(); + int height = spectrum.rows(); - double minCutoff = 0; - double maxCutoff = Math.sqrt(Math.pow(width / 2.0, 2) + Math.pow(height / 2.0, 2)); - - if(cutoff < minCutoff || cutoff > maxCutoff){ - throw new IllegalArgumentException( - String.format("Cutoff value is out of bond (%f - %f)", minCutoff, maxCutoff) - ); - } - - Mat mat = new Mat(height, width, CvType.CV_64F); - - double[][] data = matrix.getRawData(); - - for (int y = 0; y < height; y++) { - mat.put(y, 0, data[y]); - } - - // Fourier transform - Mat spectrum = new Mat(); - Core.dft(mat, spectrum, Core.DFT_COMPLEX_OUTPUT); - - // Shift zero frequency to the center int cx = width / 2; int cy = height / 2; - Mat q0 = spectrum.submat(0, cy, 0, cx); - Mat q1 = spectrum.submat(0, cy, cx, width); - Mat q2 = spectrum.submat(cy, height, 0, cx); - Mat q3 = spectrum.submat(cy, height, cx, width); - - Mat tmp = new Mat(); - - q0.copyTo(tmp); - q3.copyTo(q0); - tmp.copyTo(q3); - - q1.copyTo(tmp); - q2.copyTo(q1); - tmp.copyTo(q2); - for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { + double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2)); if (distance <= cutoff) { @@ -150,8 +110,94 @@ public class GlobalLinearFiltering { } } } + } + + private static void applyButterworthLowPassFilter(Mat spectrum, double cutoff, int order) { + int width = spectrum.cols(); + int height = spectrum.rows(); + + int cx = width / 2; + int cy = height / 2; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + + double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2) ); + + double coefficient = 1.0 / (1.0 + Math.pow(distance / cutoff, 2 * order)); + + double[] value = spectrum.get(y, x); + + spectrum.put(y, x, value[0] * coefficient, value[1] * coefficient); + } + } + } + + private static void applyButterworthHighPassFilter(Mat spectrum, double cutoff, int order) { + int width = spectrum.cols(); + int height = spectrum.rows(); + + int cx = width / 2; + int cy = height / 2; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + + double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2)); + + double coefficient; + + if (distance == 0) { + coefficient = 0; + } else { + coefficient = 1.0 / (1.0 + Math.pow(cutoff / distance, 2 * order)); + } + + double[] value = spectrum.get(y, x); + + spectrum.put(y, x, value[0] * coefficient, value[1] * coefficient); + } + } + } + + private static void validateCutoff(double cutoff, int width, int height) { + double minCutoff = 0; + double maxCutoff = Math.sqrt( + Math.pow(width / 2.0, 2) + + Math.pow(height / 2.0, 2) + ); + + if (cutoff < minCutoff || cutoff > maxCutoff) { + throw new IllegalArgumentException( + String.format( + "Cutoff value is out of bounds (%f - %f)", + minCutoff, + maxCutoff + ) + ); + } + } + + private static Mat fourierTransform(Mat mat) { + Mat spectrum = new Mat(); + Core.dft(mat, spectrum, Core.DFT_COMPLEX_OUTPUT); + return spectrum; + } + + private static void shiftSpectrum(Mat spectrum) { + int width = spectrum.cols(); + int height = spectrum.rows(); + + int cx = width / 2; + int cy = height / 2; + + Mat q0 = spectrum.submat(0, cy, 0, cx); + Mat q1 = spectrum.submat(0, cy, cx, width); + Mat q2 = spectrum.submat(cy, height, 0, cx); + Mat q3 = spectrum.submat(cy, height, cx, width); + + Mat tmp = new Mat(); - // Shift back q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3); @@ -159,8 +205,9 @@ public class GlobalLinearFiltering { q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2); + } - // Inverse Fourier transform + private static Mat inverseFourierTransform(Mat spectrum) { Mat filtered = new Mat(); Core.idft( @@ -169,23 +216,41 @@ public class GlobalLinearFiltering { Core.DFT_REAL_OUTPUT | Core.DFT_SCALE ); - // Convert result back to your Image + return filtered; + } + + private static GrayScaleMatrix toGrayScaleMatrix(Mat filtered) { + int width = filtered.cols(); + int height = filtered.rows(); + double[][] pixels = new double[height][width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - pixels[y][x] = Math.clamp(filtered.get(y, x)[0], 0.0, 255.0); + pixels[y][x] = Math.clamp( + filtered.get(y, x)[0], + 0.0, + 255.0 + ); } } return new GrayScaleMatrix(pixels); } - public static void butterworthLowPassFiltering(){ + private static Mat toMat(GrayScaleMatrix matrix) { + int width = matrix.getWidth(); + int height = matrix.getHeight(); + Mat mat = new Mat(height, width, CvType.CV_64F); + + double[][] data = matrix.getRawData(); + + for (int y = 0; y < height; y++) { + mat.put(y, 0, data[y]); + } + + return mat; } - public static void butterworthHighPassFiltering(){ - - } } diff --git a/src/main/java/presenters/FilteringPresenter.java b/src/main/java/presenters/FilteringPresenter.java deleted file mode 100644 index 51a55c5..0000000 --- a/src/main/java/presenters/FilteringPresenter.java +++ /dev/null @@ -1,55 +0,0 @@ -package presenters; - -import app.state.FilteringState; -import com.google.common.eventbus.Subscribe; -import com.google.inject.Inject; -import domain.events.filtering.HighPassChangedEvent; -import domain.events.filtering.LowPassChangedEvent; -import domain.image.GrayScaleMatrix; -import domain.image.Image; -import services.FilteringService; -import services.ImageService; -import ui.abstractions.IFilteringView; - -public class FilteringPresenter { - - private final FilteringState state; - private final ImageService imageService; - private final FilteringService filteringService; - private IFilteringView view; - - @Inject - public FilteringPresenter(FilteringState state, ImageService imageService, FilteringService filteringService) { - this.state = state; - this.state.register(this); - - this.imageService = imageService; - this.filteringService = filteringService; - } - - public void setView(IFilteringView view){ - this.view = view; - } - - public void computeLowPassFiltering(int percentage){ - GrayScaleMatrix matrix = imageService.getGrayScale(); - Image image = filteringService.lowPassFiltering(matrix, (double) percentage /100); - this.state.setLowPass(image); - } - - public void computeHighPassFiltering(int percentage){ - GrayScaleMatrix matrix = imageService.getGrayScale(); - Image image = filteringService.highPassFiltering(matrix, (double) percentage /100); - this.state.setHighPass(image); - } - - @Subscribe - public void onLowPassUpdate(LowPassChangedEvent event){ - view.updateLowPassFiltering(event.image()); - } - - @Subscribe - public void onHighPassUpdate(HighPassChangedEvent event){ - view.updateHighPassFiltering(event.image()); - } -} diff --git a/src/main/java/presenters/MainPresenter.java b/src/main/java/presenters/MainPresenter.java index 225a5d6..6a06fc8 100644 --- a/src/main/java/presenters/MainPresenter.java +++ b/src/main/java/presenters/MainPresenter.java @@ -1,67 +1,63 @@ package presenters; -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; +import app.state.AppState; +import app.state.AppStateListener; 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.abstractions.IMainView; -/** - * @author Laurent Crema - */ -public class MainPresenter { - +public class MainPresenter implements AppStateListener { private IMainView view; + private final AppState appState; private final ModeService modeService; private final ImageService imageService; @Inject - public MainPresenter(ModeService modeService, ImageService imageService, EventBus eventBus) { + public MainPresenter(AppState appState, ModeService modeService, ImageService imageService) { + this.appState = appState; this.modeService = modeService; this.imageService = imageService; - eventBus.register(this); + this.appState.subscribe(this); } public void setView(IMainView view) { this.view = view; } - @Subscribe - public void onImageChanged(ImageChangedEvent e){ - view.displayImage(e.image()); + @Override + public void onImageChanged(Image image) { + view.displayImage(image); } - @Subscribe - public void onModeChanged(ModeChangedEvent e){ - view.changeMode(e.mode()); + @Override + public void onModeChanged(Mode mode) { + view.changeMode(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(); + public void drawPixel(int x, int y, int red, int green, int blue) { + if (modeService.getMode() != Mode.DRAW_PIXEL) return; + + Image image = appState.getImage(); image.setPixelColor(x, y, red, green, blue); - imageService.setImage(image); + appState.setImage(image); } - public void drawShape(int x1, int x2, int y1, int y2, int red, int green, int blue){ - Image image = imageService.getImage(); + public void drawShape(int x1, int x2, int y1, int y2, int red, int green, int blue) { + Image image = appState.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; + case DRAW_RECTANGLE -> image.setRectangleColor(x1, x2, y1, y2, red, green, blue); + case DRAW_LINE -> image.setLineColor(x1, x2, y1, y2, red, green, blue); + case DRAW_CIRCLE -> {} } - imageService.setImage(image); + + appState.setImage(image); } - -} + public void dispose() { + appState.unsubscribe(this); + } +} \ No newline at end of file diff --git a/src/main/java/presenters/NavPresenter.java b/src/main/java/presenters/NavPresenter.java index bec43f4..9966386 100644 --- a/src/main/java/presenters/NavPresenter.java +++ b/src/main/java/presenters/NavPresenter.java @@ -1,9 +1,11 @@ package presenters; import app.INavigator; +import app.state.AppState; import com.google.inject.Inject; import domain.common.Mode; import domain.image.GrayScaleMatrix; +import domain.image.Image; import services.ImageService; import services.ModeService; import ui.abstractions.INavBar; @@ -11,19 +13,17 @@ import ui.abstractions.INavBar; import java.io.File; import java.io.IOException; -/** - * @author Laurent Crema - */ public class NavPresenter { - private INavBar navBar; - private INavigator navigator; + private final INavigator navigator; + private final AppState appState; private final ImageService imageService; private final ModeService modeService; @Inject - public NavPresenter(INavigator navigator, ImageService imageService, ModeService modeService) { + public NavPresenter(INavigator navigator, AppState appState, ImageService imageService, ModeService modeService) { this.navigator = navigator; + this.appState = appState; this.imageService = imageService; this.modeService = modeService; } @@ -33,27 +33,28 @@ public class NavPresenter { } public void createImage(int red, int green, int blue, int height, int width) { - imageService.createImage(red, green, blue, height, width); + Image image = imageService.createImage(red, green, blue, height, width); + appState.setImage(image); } - public void loadImage(File f) throws IOException { - imageService.loadImage(f); + public void loadImage(File file) throws IOException { + Image image = imageService.loadImage(file); + appState.setImage(image); } - public void goToFourier(){ + public void goToFourier() { navigator.showFourier(); } - public void goToFiltering(){ + public void goToFiltering() { navigator.showFiltering(); } public GrayScaleMatrix getImageGrayMatrix() { - //return ((CImageNG)appState.getImage()).getMatrice(); - return imageService.getImage().toGrayScale(); + return appState.getImage().toGrayScale(); } public void setMode(Mode mode) { modeService.setMode(mode); } -} +} \ No newline at end of file diff --git a/src/main/java/presenters/doubleMatrix/DoubleMatrixListener.java b/src/main/java/presenters/doubleMatrix/DoubleMatrixListener.java new file mode 100644 index 0000000..63dc73b --- /dev/null +++ b/src/main/java/presenters/doubleMatrix/DoubleMatrixListener.java @@ -0,0 +1,12 @@ +package presenters.doubleMatrix; + +import domain.image.GrayScaleMatrix; +import domain.image.processing.complex.ComplexMatrix; + +public interface DoubleMatrixListener { + void onFourierChanged(ComplexMatrix fourier); + void onModuleChanged(GrayScaleMatrix matrix); + void onPhaseChanged(GrayScaleMatrix matrix); + void onRealChanged(GrayScaleMatrix matrix); + void onImaginaryChanged(GrayScaleMatrix matrix); +} \ No newline at end of file diff --git a/src/main/java/presenters/DoubleMatrixPresenter.java b/src/main/java/presenters/doubleMatrix/DoubleMatrixPresenter.java similarity index 75% rename from src/main/java/presenters/DoubleMatrixPresenter.java rename to src/main/java/presenters/doubleMatrix/DoubleMatrixPresenter.java index 5e1e232..0d0e0d4 100644 --- a/src/main/java/presenters/DoubleMatrixPresenter.java +++ b/src/main/java/presenters/doubleMatrix/DoubleMatrixPresenter.java @@ -1,5 +1,6 @@ -package presenters; +package presenters.doubleMatrix; +import app.state.AppState; import app.state.DoubleMatrixState; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; @@ -18,25 +19,26 @@ import java.io.IOException; /** * @author Laurent Crema */ -public class DoubleMatrixPresenter { - +public class DoubleMatrixPresenter implements DoubleMatrixListener { private IDoubleMatrix view; + private final AppState appState; private final DoubleMatrixState state; private final ImageService imageService; @Inject - public DoubleMatrixPresenter(ImageService imageService, DoubleMatrixState state) { + public DoubleMatrixPresenter(AppState appState, ImageService imageService, DoubleMatrixState state) { + this.appState = appState; this.imageService = imageService; this.state = state; - this.state.register(this); + this.state.subscribe(this); } - public void setView(IDoubleMatrix view){ + public void setView(IDoubleMatrix view) { this.view = view; } public void saveImage(Image image) throws IOException { - this.imageService.saveImage(image); + imageService.saveImage(image); } public void loadModule(){ @@ -87,28 +89,32 @@ public class DoubleMatrixPresenter { state.setImaginary(matrix); } - @Subscribe - public void onModuleUpdate(ModuleChangedEvent event) { + @Override + public void onModuleChanged(GrayScaleMatrix matrix) { GrayScaleMatrix module = new GrayScaleMatrix(getFourier().getModule()); - view.updateModule(module, event.matrix()); + view.updateModule(module, matrix); } - @Subscribe - public void onPhaseUpdate(PhaseChangedEvent event) { + @Override + public void onPhaseChanged(GrayScaleMatrix matrix) { GrayScaleMatrix phase = new GrayScaleMatrix(getFourier().getPhase()); - view.updatePhase(phase, event.matrix()); + view.updatePhase(phase, matrix); } - @Subscribe - public void onRealUpdate(RealChangedEvent event) { + @Override + public void onRealChanged(GrayScaleMatrix matrix) { GrayScaleMatrix real = new GrayScaleMatrix(getFourier().getPartieReelle()); - view.updateReal(real, event.matrix()); + view.updateReal(real, matrix); } - @Subscribe - public void onImaginaryUpdate(ImaginaryChangedEvent event) { + @Override + public void onFourierChanged(ComplexMatrix fourier) { + } + + @Override + public void onImaginaryChanged(GrayScaleMatrix matrix) { GrayScaleMatrix imaginary = new GrayScaleMatrix(getFourier().getPartieImaginaire()); - view.updateImaginary(imaginary, event.matrix()); + view.updateImaginary(imaginary, matrix); } private GrayScaleMatrix clip(GrayScaleMatrix matrix, double black, double white){ @@ -119,7 +125,7 @@ public class DoubleMatrixPresenter { private ComplexMatrix getFourier() { if (state.getFourier() == null) { - state.setFourier(imageService.computeFourier()); + state.setFourier(imageService.computeFourier(appState.getImage())); } return state.getFourier(); } diff --git a/src/main/java/presenters/filtering/FilteringListener.java b/src/main/java/presenters/filtering/FilteringListener.java new file mode 100644 index 0000000..8667b6d --- /dev/null +++ b/src/main/java/presenters/filtering/FilteringListener.java @@ -0,0 +1,11 @@ +package presenters.filtering; + +import domain.events.listeners.Listener; +import domain.image.Image; + +public interface FilteringListener extends Listener { + void onLowPassChanged(Image image); + void onHighPassChanged(Image image); + void onButterworthLowPassChanged(Image image); + void onButterworthHighPassChanged(Image image); +} \ No newline at end of file diff --git a/src/main/java/presenters/filtering/FilteringPresenter.java b/src/main/java/presenters/filtering/FilteringPresenter.java new file mode 100644 index 0000000..98f192b --- /dev/null +++ b/src/main/java/presenters/filtering/FilteringPresenter.java @@ -0,0 +1,79 @@ +package presenters.filtering; + +import app.state.AppState; +import app.state.FilteringState; +import com.google.inject.Inject; +import domain.image.GrayScaleMatrix; +import domain.image.Image; +import services.FilteringService; +import services.ImageService; +import ui.abstractions.IFilteringView; + +public class FilteringPresenter implements FilteringListener { + private final AppState appState; + private final FilteringState state; + private final ImageService imageService; + private final FilteringService filteringService; + private IFilteringView view; + + @Inject + public FilteringPresenter(AppState appState, FilteringState state, ImageService imageService, FilteringService filteringService) { + this.appState = appState; + this.state = state; + this.imageService = imageService; + this.filteringService = filteringService; + this.state.subscribe(this); + } + + public void setView(IFilteringView view) { + this.view = view; + } + + public void computeLowPassFiltering(float percentage) { + GrayScaleMatrix matrix = imageService.getGrayScale(appState.getImage()); + Image image = filteringService.lowPassFiltering(matrix, percentage / 100); + state.setLowPass(image); + } + + public void computeHighPassFiltering(float percentage) { + GrayScaleMatrix matrix = imageService.getGrayScale(appState.getImage()); + Image image = filteringService.highPassFiltering(matrix, percentage / 100); + state.setHighPass(image); + } + + public void computeButterworthLowPassFiltering(float percentage) { + GrayScaleMatrix matrix = imageService.getGrayScale(appState.getImage()); + Image image = filteringService.butterworthLowPassFiltering(matrix, percentage / 100); + state.setButterworthLowPass(image); + } + + public void computeButterworthHighPassFiltering(float percentage) { + GrayScaleMatrix matrix = imageService.getGrayScale(appState.getImage()); + Image image = filteringService.butterworthHighPassFiltering(matrix, percentage / 100); + state.setButterworthHighPass(image); + } + + @Override + public void onLowPassChanged(Image image) { + view.updateLowPassFiltering(image); + } + + @Override + public void onHighPassChanged(Image image) { + view.updateHighPassFiltering(image); + } + + @Override + public void onButterworthLowPassChanged(Image image) { + view.updateButterworthLowPassFiltering(image); + } + + @Override + public void onButterworthHighPassChanged(Image image) { + view.updateButterworthHighPassFiltering(image); + } + + public void dispose() { + state.unsubscribe(this); + } +} \ No newline at end of file diff --git a/src/main/java/services/FilteringService.java b/src/main/java/services/FilteringService.java index 0afd7cf..a7e38ea 100644 --- a/src/main/java/services/FilteringService.java +++ b/src/main/java/services/FilteringService.java @@ -22,4 +22,11 @@ public class FilteringService { return Math.sqrt(Math.pow(width / 2.0, 2) + Math.pow(height / 2.0, 2)); } + public Image butterworthLowPassFiltering(GrayScaleMatrix matrix, float v) { + return null; + } + + public Image butterworthHighPassFiltering(GrayScaleMatrix matrix, float v) { + return null; + } } diff --git a/src/main/java/services/ImageService.java b/src/main/java/services/ImageService.java index 76efc87..2bdfaa4 100644 --- a/src/main/java/services/ImageService.java +++ b/src/main/java/services/ImageService.java @@ -1,9 +1,7 @@ package services; -import app.state.AppState; -import com.google.inject.Inject; -import domain.image.Image; import domain.image.GrayScaleMatrix; +import domain.image.Image; import domain.image.processing.complex.ComplexMatrix; import domain.image.processing.fourier.Fourier; import infrastructure.image.io.ImageLoader; @@ -17,42 +15,25 @@ import java.io.IOException; */ public class ImageService { - private final AppState appState; - - @Inject - public ImageService(AppState appState) { - this.appState = appState; + public Image createImage(int red, int green, int blue, int height, int width) { + return new Image(red, green, blue, height, width); } - 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 Image loadImage(File file) throws IOException { + return ImageLoader.loadImage(file); } public void saveImage(Image image) throws IOException { ImageSaver.saveImage(image); } - public ComplexMatrix computeFourier(){ - GrayScaleMatrix matrix = appState.getImage().toGrayScale(); + public ComplexMatrix computeFourier(Image image) { + GrayScaleMatrix matrix = image.toGrayScale(); ComplexMatrix fourier = Fourier.Fourier2D(matrix.getRawData()); return Fourier.decroise(fourier); } - public Image getImage(){ - return appState.getImage(); + public GrayScaleMatrix getGrayScale(Image image) { + return image.toGrayScale(); } - - public void setImage(Image img){ - appState.setImage(img); - } - - public GrayScaleMatrix getGrayScale(){ - return appState.getImage().toGrayScale(); - } -} +} \ No newline at end of file diff --git a/src/main/java/ui/abstractions/AbstractView.java b/src/main/java/ui/abstractions/AbstractView.java index 6145f67..4343d29 100644 --- a/src/main/java/ui/abstractions/AbstractView.java +++ b/src/main/java/ui/abstractions/AbstractView.java @@ -5,4 +5,11 @@ import javax.swing.*; public abstract class AbstractView extends JFrame { public abstract void initComponents(); public abstract void initValues(); + public abstract void disposeCore(); + + @Override + public void dispose() { + disposeCore(); + super.dispose(); + } } diff --git a/src/main/java/ui/implementation/components/controls/IntensitySlider.java b/src/main/java/ui/implementation/components/controls/IntensitySlider.java new file mode 100644 index 0000000..96275e0 --- /dev/null +++ b/src/main/java/ui/implementation/components/controls/IntensitySlider.java @@ -0,0 +1,17 @@ +package ui.implementation.components.controls; + +import javax.swing.*; +import java.awt.event.ActionListener; + +public class IntensitySlider extends JSlider { + + public IntensitySlider() { + super(); + setMinimum(0); + setMaximum(1000); + } + + public float getPercentage(){ + return (float) getValue() /10; + } +} diff --git a/src/main/java/ui/implementation/components/button/SaveButton.java b/src/main/java/ui/implementation/components/controls/SaveButton.java similarity index 87% rename from src/main/java/ui/implementation/components/button/SaveButton.java rename to src/main/java/ui/implementation/components/controls/SaveButton.java index 554319a..9e747ec 100644 --- a/src/main/java/ui/implementation/components/button/SaveButton.java +++ b/src/main/java/ui/implementation/components/controls/SaveButton.java @@ -1,4 +1,4 @@ -package ui.implementation.components.button; +package ui.implementation.components.controls; import javax.swing.*; import java.net.URL; diff --git a/src/main/java/ui/implementation/components/image/TabbedImagePanel.java b/src/main/java/ui/implementation/components/image/TabbedImagePanel.java new file mode 100644 index 0000000..a58fb64 --- /dev/null +++ b/src/main/java/ui/implementation/components/image/TabbedImagePanel.java @@ -0,0 +1,29 @@ +package ui.implementation.components.image; + +import domain.image.Image; +import infrastructure.ui.ImageMapper; + +import javax.swing.*; +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; + +public class TabbedImagePanel extends JTabbedPane { + + private final Map tabs; + + public TabbedImagePanel() { + this.tabs = new HashMap<>(); + } + + public void addPreviewTab(String title) { + ImagePanel panel = new ImagePanel(); + tabs.put(title, panel); + addTab(title, panel); + } + + public void loadImage(String title, Image image) { + BufferedImage bufferedImage = ImageMapper.toBufferedImage(image); + tabs.get(title).loadImage(bufferedImage); + } +} diff --git a/src/main/java/ui/implementation/views/DoubleMatrix.java b/src/main/java/ui/implementation/views/DoubleMatrix.java index 858bff0..e78cc93 100644 --- a/src/main/java/ui/implementation/views/DoubleMatrix.java +++ b/src/main/java/ui/implementation/views/DoubleMatrix.java @@ -2,9 +2,9 @@ package ui.implementation.views; import domain.image.GrayScaleMatrix; import jakarta.inject.Inject; -import presenters.DoubleMatrixPresenter; +import presenters.doubleMatrix.DoubleMatrixPresenter; import ui.abstractions.AbstractView; -import ui.implementation.components.button.SaveButton; +import ui.implementation.components.controls.SaveButton; import ui.implementation.components.image.ImagePanel; import ui.implementation.components.intent.FileChooser; import ui.abstractions.IDoubleMatrix; @@ -96,6 +96,11 @@ public class DoubleMatrix extends AbstractView implements IDoubleMatrix this.whiteTextField.addActionListener(this::jTextFieldBlancActionPerformed); } + @Override + public void disposeCore() { + this.presenter.setView(null); + } + private JSlider buildSlider() { return new JSlider(JSlider.VERTICAL); } diff --git a/src/main/java/ui/implementation/views/FilteringView.java b/src/main/java/ui/implementation/views/IdealFilteringView.java similarity index 82% rename from src/main/java/ui/implementation/views/FilteringView.java rename to src/main/java/ui/implementation/views/IdealFilteringView.java index 70d7718..c726f50 100644 --- a/src/main/java/ui/implementation/views/FilteringView.java +++ b/src/main/java/ui/implementation/views/IdealFilteringView.java @@ -2,39 +2,42 @@ package ui.implementation.views; import com.google.inject.Inject; import domain.image.Image; -import infrastructure.ui.ImageMapper; -import presenters.FilteringPresenter; +import presenters.filtering.FilteringPresenter; import ui.abstractions.AbstractView; -import ui.implementation.components.button.SaveButton; -import ui.implementation.components.image.ImagePanel; +import ui.implementation.components.controls.IntensitySlider; +import ui.implementation.components.controls.SaveButton; import ui.abstractions.IFilteringView; +import ui.implementation.components.image.TabbedImagePanel; import javax.swing.*; import javax.swing.event.ChangeEvent; -public class FilteringView extends AbstractView implements IFilteringView { +public class IdealFilteringView extends AbstractView implements IFilteringView { - private ImagePanel lowPassContainer; - private ImagePanel highPassContainer; - private JTabbedPane modeTab; - private JSlider intensitySlider; + private final static String LOW_PASS_TAB_TITLE = "Passe-haut"; + private final static String HIGH_PASS_TAB_TITLE = "Passe-bas"; + + private TabbedImagePanel modeTab; + private IntensitySlider intensitySlider; private JLabel intensityLabel; private final FilteringPresenter presenter; private boolean isUpdating; @Inject - public FilteringView(FilteringPresenter presenter) { + public IdealFilteringView(FilteringPresenter presenter) { this.presenter = presenter; } @Override public void initComponents(){ - this.lowPassContainer = new ImagePanel(); - this.highPassContainer = new ImagePanel(); - this.modeTab = buildModeTab(); - this.intensitySlider = new JSlider(0,100,0); - this.intensitySlider.setOrientation(JSlider.VERTICAL); this.intensityLabel = new JLabel(); + this.intensitySlider = new IntensitySlider(); + this.intensitySlider.setOrientation(JSlider.VERTICAL); + + this.modeTab = new TabbedImagePanel(); + this.modeTab.addPreviewTab(LOW_PASS_TAB_TITLE); + this.modeTab.addPreviewTab(HIGH_PASS_TAB_TITLE); + JButton saveButton = new SaveButton(); saveButton.addActionListener(e -> {}); @@ -45,33 +48,7 @@ public class FilteringView extends AbstractView implements IFilteringView { pack(); } - @Override - public void initValues(){ - this.presenter.setView(this); - this.intensitySlider.setMinimum(0); - this.intensitySlider.setMaximum(100); - this.intensitySlider.setValue(50); - this.intensitySlider.addChangeListener(this::onCutoffUpdate); - - this.intensityLabel.setText(String.valueOf(intensitySlider.getValue())); - - this.modeTab.addChangeListener(e -> { - switch (modeTab.getSelectedIndex()) { - case 0 -> presenter.computeLowPassFiltering(intensitySlider.getValue()); - case 1 -> presenter.computeHighPassFiltering(intensitySlider.getValue()); - } - }); - - this.presenter.computeLowPassFiltering(intensitySlider.getValue()); - } - - private JTabbedPane buildModeTab(){ - JTabbedPane modeTabs = new JTabbedPane(); - modeTabs.addTab("Passe-bas", lowPassContainer); - modeTabs.addTab("Passe-haut", highPassContainer); - return modeTabs; - } private void buildLayout(JTabbedPane modeTab, JLabel valueTextField, @@ -158,10 +135,10 @@ public class FilteringView extends AbstractView implements IFilteringView { if (isUpdating) return; isUpdating = true; try { - this.intensityLabel.setText(String.valueOf(intensitySlider.getValue())); + this.intensityLabel.setText(String.valueOf(intensitySlider.getPercentage())); switch (modeTab.getSelectedIndex()) { - case 0 -> presenter.computeLowPassFiltering(intensitySlider.getValue()); - case 1 -> presenter.computeHighPassFiltering(intensitySlider.getValue()); + case 0 -> presenter.computeLowPassFiltering(intensitySlider.getPercentage()); + case 1 -> presenter.computeHighPassFiltering(intensitySlider.getPercentage()); } } finally { isUpdating = false; @@ -170,12 +147,12 @@ public class FilteringView extends AbstractView implements IFilteringView { @Override public void updateLowPassFiltering(Image image) { - lowPassContainer.loadImage(ImageMapper.toBufferedImage(image)); + modeTab.loadImage(LOW_PASS_TAB_TITLE, image); } @Override public void updateHighPassFiltering(Image image) { - highPassContainer.loadImage(ImageMapper.toBufferedImage(image)); + modeTab.loadImage(HIGH_PASS_TAB_TITLE, image); } @Override @@ -187,4 +164,27 @@ public class FilteringView extends AbstractView implements IFilteringView { public void updateButterworthLowPassFiltering(Image image) { } + + @Override + public void initValues(){ + this.presenter.setView(this); + + this.intensitySlider.addChangeListener(this::onCutoffUpdate); + this.intensityLabel.setText(String.valueOf(intensitySlider.getValue())); + + this.modeTab.addChangeListener(e -> { + switch (modeTab.getSelectedIndex()) { + case 0 -> presenter.computeLowPassFiltering(intensitySlider.getPercentage()); + case 1 -> presenter.computeHighPassFiltering(intensitySlider.getPercentage()); + } + }); + + this.presenter.computeLowPassFiltering(intensitySlider.getPercentage()); + } + + @Override + public void disposeCore() { + this.presenter.setView(null); + this.presenter.dispose(); + } } diff --git a/src/main/java/ui/implementation/views/NonIdealFilteringView.java b/src/main/java/ui/implementation/views/NonIdealFilteringView.java new file mode 100644 index 0000000..171007e --- /dev/null +++ b/src/main/java/ui/implementation/views/NonIdealFilteringView.java @@ -0,0 +1,190 @@ +package ui.implementation.views; + +import com.google.inject.Inject; +import domain.image.Image; +import presenters.filtering.FilteringPresenter; +import ui.abstractions.AbstractView; +import ui.abstractions.IFilteringView; +import ui.implementation.components.controls.IntensitySlider; +import ui.implementation.components.controls.SaveButton; +import ui.implementation.components.image.TabbedImagePanel; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; + +public class NonIdealFilteringView extends AbstractView implements IFilteringView { + + private final static String LOW_PASS_TAB_TITLE = "Passe-haut"; + private final static String HIGH_PASS_TAB_TITLE = "Passe-bas"; + + private TabbedImagePanel modeTab; + private IntensitySlider intensitySlider; + private JLabel intensityLabel; + private final FilteringPresenter presenter; + private boolean isUpdating; + + @Inject + public NonIdealFilteringView(FilteringPresenter presenter) { + this.presenter = presenter; + } + + + + private void buildLayout(JTabbedPane modeTab, + JLabel valueTextField, + JSlider intensitySlider, + JButton saveButton) { + + JPanel rightPanel = new JPanel(); + GroupLayout rightLayout = new GroupLayout(rightPanel); + rightPanel.setLayout(rightLayout); + + rightLayout.setHorizontalGroup( + rightLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(valueTextField, GroupLayout.PREFERRED_SIZE, 90, GroupLayout.PREFERRED_SIZE) + .addGroup(rightLayout.createSequentialGroup() + .addComponent(intensitySlider) + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) + ) + ); + + rightLayout.setVerticalGroup( + rightLayout.createSequentialGroup() + .addComponent(valueTextField, GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(intensitySlider) + ) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + ); + + // ------------------------- + // MAIN LAYOUT + // ------------------------- + GroupLayout layout = new GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + + layout.setHorizontalGroup( + layout.createParallelGroup(GroupLayout.Alignment.LEADING) + + // TOP BAR + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + //.addComponent(valeurMin) + .addGap(50) + .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(saveButton) + ) + + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + + // MAIN CONTENT + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(modeTab) + .addComponent(rightPanel) + ) + ); + } + + private void onCutoffUpdate(ChangeEvent e) { + if (isUpdating) return; + isUpdating = true; + try { + this.intensityLabel.setText(String.valueOf(intensitySlider.getPercentage())); + switch (modeTab.getSelectedIndex()) { + case 0 -> presenter.computeLowPassFiltering(intensitySlider.getPercentage()); + case 1 -> presenter.computeHighPassFiltering(intensitySlider.getPercentage()); + } + } finally { + isUpdating = false; + } + } + + @Override + public void updateLowPassFiltering(Image image) { + + } + + @Override + public void updateHighPassFiltering(Image image) { + + } + + @Override + public void updateButterworthHighPassFiltering(Image image) { + modeTab.loadImage(LOW_PASS_TAB_TITLE, image); + } + + @Override + public void updateButterworthLowPassFiltering(Image image) { + modeTab.loadImage(HIGH_PASS_TAB_TITLE, image); + } + + @Override + public void initComponents(){ + this.intensityLabel = new JLabel(); + this.intensitySlider = new IntensitySlider(); + this.intensitySlider.setOrientation(JSlider.VERTICAL); + + this.modeTab = new TabbedImagePanel(); + this.modeTab.addPreviewTab(LOW_PASS_TAB_TITLE); + this.modeTab.addPreviewTab(HIGH_PASS_TAB_TITLE); + + JButton saveButton = new SaveButton(); + + saveButton.addActionListener(e -> {}); + + buildLayout(modeTab, intensityLabel, intensitySlider, saveButton); + setTitle("Filtering"); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + pack(); + } + + @Override + public void initValues(){ + this.presenter.setView(this); + + this.intensitySlider.addChangeListener(this::onCutoffUpdate); + this.intensityLabel.setText(String.valueOf(intensitySlider.getValue())); + + this.modeTab.addChangeListener(e -> { + switch (modeTab.getSelectedIndex()) { + case 0 -> presenter.computeLowPassFiltering(intensitySlider.getPercentage()); + case 1 -> presenter.computeHighPassFiltering(intensitySlider.getPercentage()); + } + }); + + this.presenter.computeLowPassFiltering(intensitySlider.getPercentage()); + } + + @Override + public void disposeCore() { + this.presenter.setView(null); + this.presenter.dispose(); + } +}