Как заполнить пустое пространство прямоугольниками в WPF? - PullRequest
0 голосов
/ 04 марта 2019

Я создаю программу для просмотра и редактирования шкафов / мебели в 3D-программе.

Один запрос был сделан для производственной группы, чтобы иметь возможность просматривать лицо шкафа в 2D и иметь размер отверстий, перечисленных для удобства просмотра.Я выясняю размер моих отверстий и добавляю прямоугольник такого размера.Цель состоит в том, чтобы добавить текст в белый прямоугольник, отображающий размер проема, чтобы они могли создавать предметы, чтобы поместиться внутри него.

Мне удалось получить проемы на шкафу с выдвижными ящиками, см. Ниже..

enter image description here

Для более сложных, таких как приведенный ниже, мне немного сложнее.

enter image description here

Вот свойства, которые находятся на коричневых частях:

public double X {get;задавать;}

public double Y {get;задавать;}

public double Width {get;задавать;}

public double Height {get;задавать;}

Мой XAML прост, просто добавив все прямоугольники к холсту и расположив их с помощью X, Y.

Мой код немного больше беспорядка, но он ниже.

//Filter list of parts to get the frame parts
int Counter = 0;
var frameParts = getFrameParts.Where(p => p.CatalogPartID == 1015 || p.CatalogPartID == 1016 || p.CatalogPartID == 3025).OrderBy(p => p.CatalogPartID).OrderBy(p => p.Y).ToList();
MoFacePart previousFrameRail = new MoFacePart();
MoFacePart previousMidFrameStile = new MoFacePart();

foreach (var part in frameParts)
{
        var totalParts = getFrameParts.Where(p => p.CatalogPartID == 1016).ToList().Count();
        // Adding Horizontal Spaces
        if (part.CatalogPartID == 1016)
        {
            var newOpening = new MoFacePart { Width = part.Width, Height = (previousFrameRail.Y - previousFrameRail.Height) - (130-(part.Y + part.Height)), X = ((80 - (double)SelectedViewerProduct.Width) / 2) + part.X, Y = (previousFrameRail.Y - previousFrameRail.Height), Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(255, 255, 255)) };
            if (Counter > 0 && Counter < (totalParts))
            {
                FaceParts.Add(newOpening);
            }
            Counter++;
        }

        var newPart = new MoFacePart { Width = part.Width, Height = part.Height, X = ((80 - (double)SelectedViewerProduct.Width) / 2) + part.X, Y = 130 - part.Y, Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(210, 180, 140)) };
        FaceParts.Add(newPart);
        if (part.CatalogPartID == 1016)
        {
            previousFrameRail = newPart;
        }

}

Учитывая все это, есть ли лучший способ выяснить все пустые места?

Я прошу прощения за код, вставленный в виде изображения.Я не смог заставить его правильно отформатировать, чтобы я мог оставлять сообщения.

Я уверен, что могу найти решение, которое делает то, что я делаю, но я чувствую, что есть лучший способ, и я скучаю по нему.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

Это выглядело весело, так что вот ответ.Я просто положил прямоугольники на холст для моих исходных данных.Смотрите комментарии в коде для деталей.Может потребоваться некоторая настройка, я протестировал его только с прямоугольниками, которые вы видите в XAML.

Снимок экрана:

enter image description here

XAML

<Window x:Class="StackOverflow54985848.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Canvas x:Name="canvas" Margin="10">
        <Canvas.Resources>
            <Style TargetType="Rectangle">
                <Setter Property="Fill" Value="Tan" />
                <Setter Property="StrokeThickness" Value="1" />
                <Setter Property="Stroke" Value="Black" />
            </Style>
        </Canvas.Resources>
        <!-- Outside frame -->
        <Rectangle Canvas.Left="0" Canvas.Top="0" Width="10" Height="300" />
        <Rectangle Canvas.Left="300" Canvas.Top="0" Width="10" Height="300" />
        <Rectangle Canvas.Left="10" Canvas.Top="0" Width="290" Height="10" />
        <Rectangle Canvas.Left="10" Canvas.Top="290" Width="290" Height="10" />

        <!-- Insides -->
        <Rectangle Canvas.Left="10" Canvas.Top="75" Width="290" Height="10" />
        <Rectangle Canvas.Left="100" Canvas.Top="85" Width="10" Height="205" />
        <Rectangle Canvas.Left="10" Canvas.Top="175" Width="90" Height="10" />
    </Canvas>
</Window>

Код:

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace StackOverflow54985848
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // Get the rectangles from the canvas
            var rects = canvas.Children
                .Cast<Rectangle>()
                .Select(r => new Rect(
                    (double)r.GetValue(Canvas.LeftProperty),
                    (double)r.GetValue(Canvas.TopProperty),
                    r.Width, r.Height))
                    .ToArray();

            // Determine the bounds of the rects
            var minX = rects.Min(r => r.Left);
            var maxX = rects.Max(r => r.Right);
            var minY = rects.Min(r => r.Top);
            var maxY = rects.Max(r => r.Bottom);
            var bounds = new Rect(minX, minY, maxX - minX, maxY - minY);

            // openSpace initially is the entire area
            List<Rect> openSpace = new List<Rect>() { bounds };

            // Remove r from all rects in openSpace
            foreach (var r in rects)
            {
                List<Rect> openSpaceToRemove = new List<Rect>();
                List<Rect> openSpaceToAdd = new List<Rect>();

                foreach (var os in openSpace)
                {
                    if (!os.IntersectsWith(r))
                        continue;
                    var r2 = os;
                    r2.Intersect(r); // result stored in r2, it is the area that isn't open anymore

                    // We will be removing os since it intersects
                    openSpaceToRemove.Add(os);

                    // Remove r2 from os
                    //
                    // Probably a better way to do this...
                    // We have the area that ISNT open (r2) but we want the area that IS open still
                    // Create 4 rects that cover the area OUTSIDE of r2 (to the left, right, above, below)
                    // The intersection of those rects and os is our still open space (subset of os)

                    // Create a rect that is everything to the left of r2 and intersect it with os
                    var rr = new Rect(bounds.Left, bounds.Top, r2.Left, bounds.Height);
                    rr.Intersect(os); // intersection is stored in rr
                    if (rr.Width > 0 && rr.Height > 0)
                        openSpaceToAdd.Add(rr);

                    // Repeat with everything to the right
                    rr = new Rect(r2.Right, bounds.Top, bounds.Right - r2.Right, bounds.Height);
                    rr.Intersect(os); // intersection is stored in rr
                    if (rr.Width > 0 && rr.Height > 0)
                        openSpaceToAdd.Add(rr);

                    // Repeat with everything above the top
                    rr = new Rect(bounds.Left, r2.Top - bounds.Height, bounds.Width, bounds.Height);
                    rr.Intersect(os); // intersection is stored in rr
                    if (rr.Width > 0 && rr.Height > 0)
                        openSpaceToAdd.Add(rr);

                    // Repeat with everything below the bottom
                    rr = new Rect(bounds.Left, r2.Bottom, bounds.Width, bounds.Height);
                    rr.Intersect(os); // intersection is stored in rr
                    if (rr.Width > 0 && rr.Height > 0)
                        openSpaceToAdd.Add(rr);
                }

                // Remove rects we don't want
                foreach (var os in openSpaceToRemove)
                    openSpace.Remove(os);
                // Add rects we do want
                openSpace.AddRange(openSpaceToAdd);
            }

            // Merge openSpace entries
            for (int i = 0; i < openSpace.Count; i++)
            {
                // Get an openSpace rect
                var r = openSpace[i];
                // Loop through the rects that come after it
                for (int j = i + 1; j < openSpace.Count; j++)
                {
                    // Get the next rect
                    var c = openSpace[j];
                    // If c or r contains each other then expand r to contain both and remove c
                    if (r.Contains(c) || c.Contains(r))
                    {
                        r.Union(c);
                        openSpace[i] = r;
                        openSpace.RemoveAt(j);
                        // start over since r changed and we removed openSpace at index j
                        // set j = i so when the loop counter increments, j will equal i + 1
                        j = i;
                    }
                }
            }
            // Remove duplicates?
            openSpace = openSpace.Distinct().ToList();

            // Now that our openspace has been determined, add it to the canvas
            foreach (var r in openSpace)
            {
                var rr = new Rectangle()
                {
                    Width = r.Width,
                    Height = r.Height,
                    Fill = Brushes.Beige,
                    Stroke = Brushes.Red,
                    StrokeThickness = 1.0
                };
                rr.SetValue(Canvas.LeftProperty, r.Left);
                rr.SetValue(Canvas.TopProperty, r.Top);
                canvas.Children.Add(rr);

                // Grid to hold the textblock (more control over width/height)
                var grid = new Grid()
                {
                    Width = r.Width,
                    Height = r.Height,
                };
                grid.SetValue(Canvas.LeftProperty, r.Left);
                grid.SetValue(Canvas.TopProperty, r.Top);
                TextBlock tb = new TextBlock()
                {
                    Text = $"Width: {rr.Width} Height: {rr.Height}",
                    Foreground = Brushes.Red,
                    VerticalAlignment = VerticalAlignment.Center,
                    HorizontalAlignment = HorizontalAlignment.Center,
                    TextWrapping = TextWrapping.Wrap
                };
                grid.Children.Add(tb);
                canvas.Children.Add(grid);
            }
        }
    }
}
0 голосов
/ 04 марта 2019

Вместо того, чтобы пытаться выяснить, где находятся ваши пустые пространства, просто предположите, что везде внутри вашего квадрата кастрации находится пустое пространство, и установите Background контейнера Parant на белый.Предполагая, что у вас есть родитель, содержащий все ваши блоки .. если вы не учитываете это ..

В wpf вы также можете комбинировать gemoetrys, посмотрите эту статью, это может сильно помочь: http://www.blackwasp.co.uk/WPFCombinedGeometry.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...