// Copyright (C) Stichting Deltares 2024. All rights reserved. // // This file is part of the D-Soil Model application. // // The D-Soil Model application is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Linq; using System.Windows.Forms; using Deltares.DSoilModel.Forms.Properties; using Deltares.Geometry.Forms; using Deltares.Geotechnics.Soils; using Deltares.Standard.EventPublisher; using Deltares.Standard.EventPublisher.Enum; using Deltares.Standard.Forms; using DevExpress.XtraEditors; namespace Deltares.DSoilModel.Forms { /// /// Control to show the Stochastic Soil Profiles of a Stochastic Soil Model /// as a film strip under the Geometry Editor. /// public partial class FilmStrip : XtraUserControl { private const int thumbnailHeightMarginSize = 10; private const int thumbnailWidthMarginSize = 10; private const int thumbnailSubPanelSeparatorSize = 20; private readonly SpatialEditor spatialEditor; private readonly List thumbnailList = new List(); private readonly Color thumbnailSelectedColor; private int startThumbnailIndex; private int thumbnailButtonsShown; /// /// Constructor. /// public FilmStrip(SpatialEditor spatialEditorToClone) { InitializeComponent(); spatialEditor = new SpatialEditor(); spatialEditorToClone.CopyShapeInfos(spatialEditor); spatialEditor.RegisterShapeType(typeof(Rectangle), typeof(DrawingBoundingBox)); spatialEditor.MarginBottom = 55; thumbnailSelectedColor = Color.FromArgb(122, 150, 223); } /// /// Gets or sets the selected Stochastic Soil Model. /// public StochasticSoilModel SelectedStochasticSoilModel { get; set; } /// /// Gets or sets the selected Stochastic Soil Profile. /// public StochasticSoilProfile SelectedStochasticSoilProfile { get; set; } /// /// Method to refresh the filmstrip control and redraw the thumbnails. /// /// Whether the thumbnail to display should start from the first thumbnail. public void Refresh(bool reset = true) { if (reset) { startThumbnailIndex = 0; } ClearThumbnails(); SetupThumbnails(); base.Refresh(); } private void ClearThumbnails() { thumbnailsPanel.Controls.Clear(); } private void SetupThumbnails() { if (SelectedStochasticSoilModel != null && SelectedStochasticSoilModel.StochasticSoilProfiles != null) { Rectangle boundingBox1D; Rectangle boundingBox2D; DetermineBoundingBoxes(SelectedStochasticSoilModel, out boundingBox1D, out boundingBox2D); // Create a list of thumbnails for all profiles in the stochasic soil model thumbnailList.Clear(); foreach (var soilProfile in SelectedStochasticSoilModel.StochasticSoilProfiles.Select(soilProfile => soilProfile.Profile)) { if (soilProfile != null) { var soilProfile1D = soilProfile as SoilProfile1D; if (soilProfile1D != null) { spatialEditor.Clear(); spatialEditor.AddObject(boundingBox1D); spatialEditor.AddObject(soilProfile1D); spatialEditor.AddList(soilProfile1D.Layers); spatialEditor.EmptySelection = new EmptyShape(soilProfile); spatialEditor.Refresh(); spatialEditor.ZoomToExtents(true); thumbnailList.Add(spatialEditor.GetImage(500, 500)); } else { var soilProfile2D = soilProfile as SoilProfile2D; if (soilProfile2D != null && soilProfile2D.Geometry.Curves.Count > 0) { spatialEditor.Clear(); spatialEditor.AddObject(boundingBox2D); spatialEditor.AddObject(soilProfile2D); spatialEditor.AddObject(soilProfile2D.Geometry); spatialEditor.AddList(soilProfile2D.Surfaces); spatialEditor.AddList(soilProfile2D.Geometry.Curves); spatialEditor.EmptySelection = new EmptyShape(soilProfile); spatialEditor.Refresh(); spatialEditor.ZoomToExtents(true); thumbnailList.Add(spatialEditor.GetImage(500, 500)); } else { thumbnailList.Add(Resources.EmptyThumbnail); } } } else { thumbnailList.Add(Resources.EmptyThumbnail); } } // Avoid index out of range if (startThumbnailIndex > SelectedStochasticSoilModel.StochasticSoilProfiles.Count - 1) { startThumbnailIndex = 0; } // Put thumbs from the start of the focused thumbnails thumbnailButtonsShown = 0; for (var i = startThumbnailIndex; i < SelectedStochasticSoilModel.StochasticSoilProfiles.Select(soilProfile => soilProfile.Profile).Count(); i++) { var stochasticSoilProfile = SelectedStochasticSoilModel.StochasticSoilProfiles[i]; var newFilmStripImage = new FilmStripThumbnail(thumbnailList[i], stochasticSoilProfile) { Selected = (stochasticSoilProfile == SelectedStochasticSoilProfile) }; AddTumbnail(newFilmStripImage); } } else { ClearThumbnails(); } SetButtonStates(); } private void DetermineBoundingBoxes(StochasticSoilModel stochasticSoilModel, out Rectangle boundingBox1D, out Rectangle boundingBox2D) { // Determine the boundaries for all profiles in the stochasic soil model var topLevelForAll = double.MinValue; var bottomLevelForAll = double.MaxValue; var leftBoundaryForAll = double.MaxValue; var rightBoundaryForAll = double.MinValue; foreach (var soilProfile in stochasticSoilModel.StochasticSoilProfiles.Select(soilProfile => soilProfile.Profile)) { var soilProfile1D = soilProfile as SoilProfile1D; if (soilProfile1D != null) { topLevelForAll = Math.Max(topLevelForAll, soilProfile1D.TopLevel); bottomLevelForAll = Math.Min(bottomLevelForAll, soilProfile1D.BottomLevel); } var soilProfile2D = soilProfile as SoilProfile2D; if (soilProfile2D != null) { topLevelForAll = Math.Max(topLevelForAll, soilProfile2D.Geometry.MaxGeometryPointsZ); bottomLevelForAll = Math.Min(bottomLevelForAll, soilProfile2D.Geometry.MinGeometryPointsZ); leftBoundaryForAll = Math.Min(leftBoundaryForAll, soilProfile2D.Geometry.MinGeometryPointsX); rightBoundaryForAll = Math.Max(rightBoundaryForAll, soilProfile2D.Geometry.MaxGeometryPointsX); } } // Determine the one height of the bounding box for all thumbnails var boundingBoxHeight = Math.Abs(topLevelForAll - bottomLevelForAll); // Determine the one common bounding box for all 1D profiles const int boundingBoxLeft = -5; var boundingBox1DWidth = Math.Abs(boundingBoxLeft * 2); boundingBox1D = new Rectangle(boundingBoxLeft, (int)topLevelForAll, boundingBox1DWidth, (int)boundingBoxHeight); // Determine the one common bounding box for all 2D profiles var boundingBox2DWidth = Math.Abs(rightBoundaryForAll - leftBoundaryForAll); boundingBox2D = new Rectangle((int)leftBoundaryForAll, (int)topLevelForAll, (int)boundingBox2DWidth, (int)boundingBoxHeight); } private void AddTumbnail(FilmStripThumbnail filmStripThumbnail) { var localIndex = thumbnailsPanel.Controls.Count; var globalIndex = startThumbnailIndex + localIndex; var globalIndexText = globalIndex.ToString(CultureInfo.InvariantCulture); // Create the sub panel to put the button on var thumbnailSubPanelHeight = thumbnailsPanel.Height; var thumbnailSubPanelWidth = thumbnailSubPanelHeight; var thumbnailSubPanel = new Panel { Name = "thumbnailSubPanel" + globalIndexText, Top = 0, Left = thumbnailSubPanelSeparatorSize, Height = thumbnailSubPanelHeight, Width = thumbnailSubPanelWidth, BackColor = filmStripThumbnail.Selected ? thumbnailSelectedColor : thumbnailsPanel.BackColor }; if (globalIndex > 0) { thumbnailSubPanel.Left += ((thumbnailSubPanelWidth + thumbnailSubPanelSeparatorSize)*localIndex); } // Create the button const int spaceAboveAndBelowThumbnail = 2*thumbnailHeightMarginSize; var thumbnailHeight = thumbnailsPanel.Height - spaceAboveAndBelowThumbnail; var thumbnailWidth = thumbnailHeight; var newButton = new Button { Name = "thumbnailButton" + globalIndexText, Tag = filmStripThumbnail, Top = thumbnailHeightMarginSize, Left = thumbnailWidthMarginSize, Height = thumbnailHeight, Width = thumbnailWidth, FlatStyle = FlatStyle.Flat, BackColor = Color.Transparent, Image = ScaleImage(filmStripThumbnail.ThumbnailImage, thumbnailWidth, thumbnailHeight), Visible = true }; newButton.FlatAppearance.BorderSize = 0; newButton.Click += Thumbnail_Click; newButton.TabIndex = buttonRight.TabIndex; buttonRight.TabIndex++; // Create a tooltip for the button var toolTip = new ToolTip(); var toolTipCaption = filmStripThumbnail.StochasticSoilProfile.ToString(); toolTip.SetToolTip(newButton, toolTipCaption); thumbnailSubPanel.Controls.Add(newButton); thumbnailsPanel.Controls.Add(thumbnailSubPanel); // Keep track of how many buttons are actually visible on screen if (thumbnailSubPanel.Right < thumbnailsPanel.Width) { thumbnailButtonsShown++; } } private void SetButtonStates() { if (thumbnailsPanel.Controls.Count > 0) { var lastButtonIndex = thumbnailList.Count - startThumbnailIndex; if (thumbnailList.Count <= thumbnailButtonsShown) { // There aren't enough images (or just enough) to fill the thumbnail panel buttonLeft.Enabled = false; buttonRight.Enabled = false; } else if (startThumbnailIndex == 0) { // We're at the start buttonLeft.Enabled = false; buttonRight.Enabled = true; } else if (thumbnailButtonsShown == lastButtonIndex) { // We're at the end buttonLeft.Enabled = true; buttonRight.Enabled = false; } else { // We're in the middle buttonLeft.Enabled = true; buttonRight.Enabled = true; } } } private void ButtonLeftClick(object sender, EventArgs e) { // Shift the thumbnails to the left one place if (startThumbnailIndex > 0) { startThumbnailIndex--; Refresh(false); } } private void ButtonRightClick(object sender, EventArgs e) { // Shift the thumbnails to the right one place if (thumbnailsPanel.Controls.Count > 0) { startThumbnailIndex++; Refresh(false); } } private void Thumbnail_Click(object sender, EventArgs e) { var boxSender = sender as Button; if (boxSender != null) { var selectedFilmStripImage = boxSender.Tag as FilmStripThumbnail; if (selectedFilmStripImage != null) { var selectedStochasticSoilProfile = selectedFilmStripImage.StochasticSoilProfile; if (selectedStochasticSoilProfile != null) { DataEventPublisher.SelectionChanged(selectedStochasticSoilProfile, PropertyEditorReactionType.Ignore); } } } } private Image ScaleImage(Image image, int width, int height) { var returnImage = image; if ((width > 0) && (height > 0)) { if ((width != image.Width) || (height != image.Height)) { double widthRatio = 1; double heightRatio = 1; if (image.Width > 0) { widthRatio = width/(double) image.Width; } if (image.Height > 0) { heightRatio = height/(double) image.Height; } returnImage = widthRatio < heightRatio ? image.GetThumbnailImage((int) (image.Width*widthRatio), (int) (image.Height*widthRatio), ThumbnailCallback, IntPtr.Zero) : image.GetThumbnailImage((int) (image.Width*heightRatio), (int) (image.Height*heightRatio), ThumbnailCallback, IntPtr.Zero); } } return returnImage; } private bool ThumbnailCallback() { return false; // do nothing } } }