我正在构建一个程序,以在3D程序中查看和编辑橱柜/家具。
一个功能要求是生产团队能够以2D方式查看橱柜的正面,并列出列出的开口尺寸以便于查看。 我正在弄清楚开口的大小,并添加一个该大小的矩形。目标是将文本添加到显示开口大小的白色矩形中,以便他们可以构建适合其内部的项目。
我能够在抽屉柜上找到开口,见下文。
对于像下面这样的更复杂的对象,对我来说有点困难。
以下是棕色部分的属性:
公共双X {组; }
公共双Y {组; }
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;
}
}
鉴于所有这些,是否有更好的方法找出所有空白?
我很抱歉插入作为图像插入的代码。我无法正确设置格式以允许发布。
我有信心我可以找到一种解决方案,但是我觉得有更好的方法了,我很想念它。
谢谢!
答案 0 :(得分:1)
这看起来很有趣,所以这里是一个答案。我只是将矩形放在画布上作为源数据。有关详细信息,请参见代码中的注释。可能需要进行一些调整,我只对您在XAML中看到的矩形进行了测试。
屏幕截图:
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);
}
}
}
}
答案 1 :(得分:0)
与其尝试找出您的空白空间,不如假设您在耕作方块内部的任何地方都是空白空间,然后将Parant容器的Background
设置为白色。假设您有一个包含所有块的父项。如果没有,您应该考虑一下。
在wpf中,您还可以结合使用宝石学来查看本文,这可能会有所帮助: http://www.blackwasp.co.uk/WPFCombinedGeometry.aspx