// 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
}
}
}