I'm building a program to view and edit Cabinets/furniture in a 3D program.
One feature request was for the production team to be able to view the face of the cabinet in 2D and have the size of the openings listed for easy viewing. I'm figuring out the size of my openings and adding a rectangle of that size. The goal is to add text to the white rectangle displaying the size of the opening so they can build items to fit inside of it.
I was able to get the openings on a drawer cabinet, see below.
For the more complicated ones like the one below it is a bit more difficult for me.
Here are the properties that are on the brown parts:
public double X { get; set; }
public double Y { get; set; }
public double Width { get; set; }
public double Height { get; set; }
My XAML is simple, just adding all the rectangles to a canvas and positioning with X,Y.
My code is a bit more of a mess but it is below.
//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;
}
}
Given all of this, is there a better way to figure out all the empty spaces?
I apologize for the code being inserted as an image. I could not get it to properly format to allow me to post.
I'm confident I can find a solution doing what i'm doing but I feel like there is a better way and i'm missing it.
Thanks!
This looked like fun so here is an answer. I just put rectangles on a canvas for my source data. See the comments in the code for details. It may need some tweaking, I only tested it with the rectangles you see in the XAML.
Screenshot:
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>
Code:
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);
}
}
}
}
Instead of trying to figure out where your empty spaces are just assume everywhere inside of your custruction square is empty space and set the Background
of the Parant container to white. assuming you have a parent containing all your blocks..if not you should consider it..
In wpf you can aslo combine gemoetrys check out this article it may help a lot: http://www.blackwasp.co.uk/WPFCombinedGeometry.aspx
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.