Cyotek Development Bloghttps://devblog.cyotek.com/tag/slice/atom.xml2013-02-10T08:37:04ZDividing up a rectangle based on pairs of points using C#urn:uuid:ff67a638-dab6-4764-9165-0c2399d4765d2013-02-10T08:37:04Z2013-02-10T08:37:04Z<p>Recently we released the first alpha of our latest product,
<a href="https://cyotek.com/cyotek-slicr">Cyotek Slicr</a>, a tool for slicing up an image. At the heart
of this tool is a series of routines that take a given image and
pairs of input points, from which the image is chopped up
accordingly. This article describes how to break up a rectangle
into smaller parts based on user defined co-ordinates.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rectangle1.png" class="gallery" title="An example of the programs output" ><img src="https://images.cyotek.com/image/thumbnail/devblog/rectangle1.png" alt="An example of the programs output" decoding="async" loading="lazy" /></a><figcaption>An example of the programs output</figcaption></figure><h2 id="caveat-emptor">Caveat Emptor</h2>
<blockquote>
<p>Before I get started, I just want to point out that the code
I'm about to show you is proof of concept code. It doesn't use
algorithms such as <a href="http://en.wikipedia.org/wiki/Bentley%E2%80%93Ottmann_algorithm" rel="external nofollow noopener">Bentley–Ottmann</a>, it's not very
efficient, and there's probably a hundred ways of doing it
better. However, it works, which is pretty much all I care
about at this moment!</p>
</blockquote>
<h2 id="getting-started">Getting Started</h2>
<p>These are the rules for splitting up a rectangle into component
parts:</p>
<ol>
<li>Lines must be straight, so each pair of co-ordinates will
align on one axis</li>
<li>Co-ordinates must start from either an edge, or the
intersection of another pair. The second co-ordinate must end
in a similar fashion. Any &quot;orphan&quot; co-ordinates which don't
start/end on another edge/join are illegal and should be
ignored</li>
<li>Co-ordinates can start and end at any point in the bounds of
the canvas, as long as they follow the previous rules.</li>
</ol>
<p>In order to achieve this, I ended up with creating a number of
support classes</p>
<ul>
<li><code>Segment</code> - this class starts the starting point of a line,
it's length, and it's orientation. Originally I just started
by storing the two pairs of co-ordinates, but in the end it
was easier with the length and orientation.</li>
<li><code>SegmentPoint</code> - this class stores the details of a single
point. An instance of this class is created for each unique
point created by the start and end of a segment, and any
intersections. It also stores the directions of neighbouring
points.</li>
</ul>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">internal</span> <span class="keyword">class</span> Segment
<span class="symbol">{</span>
 <span class="keyword">public</span> Point EndLocation
 <span class="symbol">{</span>
 <span class="keyword">get</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> x<span class="symbol">;</span>
 <span class="keyword">int</span> y<span class="symbol">;</span>

 <span class="keyword">switch</span> <span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Orientation<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> SegmentOrientation<span class="symbol">.</span>Vertical<span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 x <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X <span class="symbol">+</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">;</span>
 y <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> <span class="keyword">new</span> Point<span class="symbol">(</span>x<span class="symbol">,</span> y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> Point Location <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> SegmentOrientation Orientation <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> Size <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>

<span class="keyword">internal</span> <span class="keyword">class</span> SegmentPoint
<span class="symbol">{</span>
 <span class="keyword">public</span> SegmentPointConnections Connections <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Point Location <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> X <span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">int</span> Y <span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>With these helper classes, I can now construct the code to
process them and extract the rectangles.</p>
<h2 id="calculating-points">Calculating Points</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rectangle2.png" class="gallery" title="In this example, a rectangle has been created by segments crossing each other. We need to detect the intersections to find these types of rectangles" ><img src="https://images.cyotek.com/image/thumbnail/devblog/rectangle2.png" alt="In this example, a rectangle has been created by segments crossing each other. We need to detect the intersections to find these types of rectangles" decoding="async" loading="lazy" /></a><figcaption>In this example, a rectangle has been created by segments crossing each other. We need to detect the intersections to find these types of rectangles</figcaption></figure>
<p>The image above demonstrates the first problem. The four
segments intersect with each other, causing a rectangle to be
generated untouched by any user defined point. However, if we
are going to find that rectangle, we need to find any new point
generated by multiple segments intersecting.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> CalculatePoints<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 List<span class="symbol">&lt;</span>Segment<span class="symbol">&gt;</span> segments<span class="symbol">;</span>

 segments <span class="symbol">=</span> <span class="keyword">new</span> List<span class="symbol">&lt;</span>Segment<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Points <span class="symbol">=</span> <span class="keyword">new</span> Dictionary<span class="symbol">&lt;</span>Point<span class="symbol">,</span> SegmentPoint<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add segments representing the edges</span>
 segments<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Segment <span class="symbol">{</span> Location <span class="symbol">=</span> Point<span class="symbol">.</span>Empty<span class="symbol">,</span> Size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">,</span> Orientation <span class="symbol">=</span> SegmentOrientation<span class="symbol">.</span>Horizontal <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 segments<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Segment <span class="symbol">{</span> Location <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Height<span class="symbol">)</span><span class="symbol">,</span> Size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">,</span> Orientation <span class="symbol">=</span> SegmentOrientation<span class="symbol">.</span>Horizontal <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 segments<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Segment <span class="symbol">{</span> Location <span class="symbol">=</span> Point<span class="symbol">.</span>Empty<span class="symbol">,</span> Size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Height<span class="symbol">,</span> Orientation <span class="symbol">=</span> SegmentOrientation<span class="symbol">.</span>Vertical <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>
 segments<span class="symbol">.</span>Add<span class="symbol">(</span><span class="keyword">new</span> Segment <span class="symbol">{</span> Location <span class="symbol">=</span> <span class="keyword">new</span> Point<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Width<span class="symbol">,</span> <span class="number">0</span><span class="symbol">)</span><span class="symbol">,</span> Size <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Size<span class="symbol">.</span>Height<span class="symbol">,</span> Orientation <span class="symbol">=</span> SegmentOrientation<span class="symbol">.</span>Vertical <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// add the rest of the segments</span>
 segments<span class="symbol">.</span>AddRange<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Segments<span class="symbol">)</span><span class="symbol">;</span>

 segments<span class="symbol">.</span>Sort<span class="symbol">(</span><span class="symbol">(</span>a<span class="symbol">,</span> b<span class="symbol">)</span> <span class="symbol">=&gt;</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span> result <span class="symbol">=</span> a<span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">.</span>CompareTo<span class="symbol">(</span>b<span class="symbol">.</span>Location<span class="symbol">.</span>X<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>result <span class="symbol">==</span> <span class="number">0</span><span class="symbol">)</span>
 result <span class="symbol">=</span> a<span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">.</span>CompareTo<span class="symbol">(</span>b<span class="symbol">.</span>Location<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">return</span> result<span class="symbol">;</span>
 <span class="symbol">}</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>Segment segment <span class="keyword">in</span> segments<span class="symbol">)</span>
 <span class="symbol">{</span>
 Segment currentSegment<span class="symbol">;</span>

 <span class="comment">// add the segment points</span>
 <span class="keyword">this</span><span class="symbol">.</span>UpdatePoint<span class="symbol">(</span>segment<span class="symbol">.</span>Location<span class="symbol">,</span> segment<span class="symbol">.</span>Orientation <span class="symbol">==</span> SegmentOrientation<span class="symbol">.</span>Horizontal <span class="symbol">?</span> SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">:</span> SegmentPointConnections<span class="symbol">.</span>Top<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>UpdatePoint<span class="symbol">(</span>segment<span class="symbol">.</span>EndLocation<span class="symbol">,</span> segment<span class="symbol">.</span>Orientation <span class="symbol">==</span> SegmentOrientation<span class="symbol">.</span>Horizontal <span class="symbol">?</span> SegmentPointConnections<span class="symbol">.</span>Right <span class="symbol">:</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// calculate any intersecting points</span>
 currentSegment <span class="symbol">=</span> segment<span class="symbol">;</span>
 <span class="keyword">foreach</span> <span class="symbol">(</span>Segment otherSegment <span class="keyword">in</span> segments<span class="symbol">.</span>Where<span class="symbol">(</span>s <span class="symbol">=&gt;</span> s <span class="symbol">!=</span> currentSegment<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Point intersection<span class="symbol">;</span>

 intersection <span class="symbol">=</span> Intersection<span class="symbol">.</span>FindLineIntersection<span class="symbol">(</span>segment<span class="symbol">.</span>Location<span class="symbol">,</span> segment<span class="symbol">.</span>EndLocation<span class="symbol">,</span> otherSegment<span class="symbol">.</span>Location<span class="symbol">,</span> otherSegment<span class="symbol">.</span>EndLocation<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>intersection<span class="symbol">.</span>IsEmpty<span class="symbol">)</span>
 <span class="symbol">{</span>
 SegmentPointConnections flags<span class="symbol">;</span>

 flags <span class="symbol">=</span> SegmentPointConnections<span class="symbol">.</span>None<span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>intersection <span class="symbol">!=</span> segment<span class="symbol">.</span>Location <span class="symbol">&amp;&amp;</span> intersection <span class="symbol">!=</span> segment<span class="symbol">.</span>EndLocation<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>segment<span class="symbol">.</span>Orientation <span class="symbol">==</span> SegmentOrientation<span class="symbol">.</span>Horizontal<span class="symbol">)</span>
 flags <span class="symbol">|=</span> <span class="symbol">(</span> SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Right<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 flags <span class="symbol">|=</span> <span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Top <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>intersection <span class="symbol">!=</span> otherSegment<span class="symbol">.</span>Location <span class="symbol">&amp;&amp;</span> intersection <span class="symbol">!=</span> otherSegment<span class="symbol">.</span>EndLocation<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span>otherSegment<span class="symbol">.</span>Orientation <span class="symbol">==</span> SegmentOrientation<span class="symbol">.</span>Horizontal<span class="symbol">)</span>
 flags <span class="symbol">|=</span> <span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Right<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 flags <span class="symbol">|=</span> <span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Top <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">if</span> <span class="symbol">(</span>flags <span class="symbol">!=</span> SegmentPointConnections<span class="symbol">.</span>None<span class="symbol">)</span>
 <span class="keyword">this</span><span class="symbol">.</span>UpdatePoint<span class="symbol">(</span>intersection<span class="symbol">,</span> flags<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Breaking the code down, we do the following:</p>
<ul>
<li>Create an additional four segments representing the boundaries
of the canvas</li>
<li>Sort the segments by their starting locations</li>
<li>Cycle each segment and</li>
<li>Create a point for the starting co-ordinate</li>
<li>Create a point for the ending co-ordinate</li>
<li>Cycle each other segment and see if it intersects with the
current segment. If it does, create a new point at the
intersection</li>
</ul>
<p>In any case above where I state to create a point, the code will
either create a point if one doesn't already exist, otherwise it
will update the connections of the existing point.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> UpdatePoint<span class="symbol">(</span>Point location<span class="symbol">,</span> SegmentPointConnections connections<span class="symbol">)</span>
<span class="symbol">{</span>
 SegmentPoint point<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>TryGetValue<span class="symbol">(</span>location<span class="symbol">,</span> <span class="keyword">out</span> point<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 point <span class="symbol">=</span> <span class="keyword">new</span> SegmentPoint <span class="symbol">{</span> Location <span class="symbol">=</span> location<span class="symbol">,</span> Connections <span class="symbol">=</span> connections <span class="symbol">}</span><span class="symbol">;</span>
 <span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>Add<span class="symbol">(</span>point<span class="symbol">.</span>Location<span class="symbol">,</span> point<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>point<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>connections<span class="symbol">)</span><span class="symbol">)</span>
 point<span class="symbol">.</span>Connections <span class="symbol">|=</span> connections<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The <code>CalculatePoints</code> method above is very inefficient, but it
does the job. Once this routine has run, we'll have an array of
co-ordinates and the directions of linked points.</p>
<h2 id="calculating-rectangles">Calculating Rectangles</h2>
<p>Now that we have all points, both user defined, and
intersections, we can use this to generate the actual
rectangles.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">void</span> CalculateRectangles<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 SegmentPoint<span class="symbol">[</span><span class="symbol">]</span> horizontalPoints<span class="symbol">;</span>
 SegmentPoint<span class="symbol">[</span><span class="symbol">]</span> verticalPoints<span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Rectangles <span class="symbol">=</span> <span class="keyword">new</span> HashSet<span class="symbol">&lt;</span>Rectangle<span class="symbol">&gt;</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 horizontalPoints <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>Values<span class="symbol">.</span>OrderBy<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>X<span class="symbol">)</span><span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 verticalPoints <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>Values<span class="symbol">.</span>OrderBy<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">.</span>ToArray<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">foreach</span> <span class="symbol">(</span>SegmentPoint topLeft <span class="keyword">in</span> <span class="keyword">this</span><span class="symbol">.</span>Points<span class="symbol">.</span>Values<span class="symbol">.</span>Where<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Top<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 SegmentPoint topRight<span class="symbol">;</span>
 SegmentPoint bottomLeft<span class="symbol">;</span>

 topRight <span class="symbol">=</span> horizontalPoints<span class="symbol">.</span>FirstOrDefault<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>X <span class="symbol">&gt;</span> topLeft<span class="symbol">.</span>X <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Y <span class="symbol">==</span> topLeft<span class="symbol">.</span>Y <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Right <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Top<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 bottomLeft <span class="symbol">=</span> verticalPoints<span class="symbol">.</span>FirstOrDefault<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>X <span class="symbol">==</span> topLeft<span class="symbol">.</span>X <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Y <span class="symbol">&gt;</span> topLeft<span class="symbol">.</span>Y <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Left <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>topRight <span class="symbol">!=</span> <span class="keyword">null</span> <span class="symbol">&amp;&amp;</span> bottomLeft <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 SegmentPoint bottomRight<span class="symbol">;</span>

 bottomRight <span class="symbol">=</span> horizontalPoints<span class="symbol">.</span>FirstOrDefault<span class="symbol">(</span>p <span class="symbol">=&gt;</span> p<span class="symbol">.</span>X <span class="symbol">==</span> topRight<span class="symbol">.</span>X <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Y <span class="symbol">==</span> bottomLeft<span class="symbol">.</span>Y <span class="symbol">&amp;&amp;</span> p<span class="symbol">.</span>Connections<span class="symbol">.</span>HasFlag<span class="symbol">(</span>SegmentPointConnections<span class="symbol">.</span>Right <span class="symbol">|</span> SegmentPointConnections<span class="symbol">.</span>Bottom<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>bottomRight <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Rectangle rectangle<span class="symbol">;</span>

 rectangle <span class="symbol">=</span> <span class="keyword">new</span> Rectangle<span class="symbol">(</span>topLeft<span class="symbol">.</span>X<span class="symbol">,</span> topLeft<span class="symbol">.</span>Y<span class="symbol">,</span> bottomRight<span class="symbol">.</span>X <span class="symbol">-</span> topLeft<span class="symbol">.</span>X<span class="symbol">,</span> bottomRight<span class="symbol">.</span>Y <span class="symbol">-</span> topLeft<span class="symbol">.</span>Y<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>Rectangles<span class="symbol">.</span>Add<span class="symbol">(</span>rectangle<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In this method, we loop through all our defined points that have
connections in the upper left corner.</p>
<p>For each matching point, we then try and find points with the
following criteria</p>
<ul>
<li>has the same Y co-ordinate and a right or top connection. This
gives us the upper right corner.</li>
<li>has the same X co-ordinate and a left or bottom connection.
This gives us the lower left corner.</li>
<li>if we have both the above corners, we then try and find a
point that has the same X co-ordinate as the upper right
corner, the same Y co-ordinate as the lower left corner, and
right or bottom connections. This gives us the last corner,
lower right</li>
<li>if we have all four corners, and the rectangle. The use of a
<code>HashSet</code> ensures the same rectangle isn't added twice.</li>
</ul>
<p>And that's all there is to it. With these two routines, I can
now break up a rectangle into many smaller pieces just by
defining pairs of points.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/rectangle3.png" class="gallery" title="Another example of the programs output" ><img src="https://images.cyotek.com/image/thumbnail/devblog/rectangle3.png" alt="Another example of the programs output" decoding="async" loading="lazy" /></a><figcaption>Another example of the programs output</figcaption></figure><h2 id="closing-remarks">Closing Remarks</h2>
<p>There are a few things that I'm aware of that the code doesn't
do</p>
<ul>
<li>As mentioned (several times!) none of this code is
particularly efficient. The more segments you add, the slower
it will get. Gareth Rees posted a nice <a href="https://stackoverflow.com/a/13923149/148962" rel="external nofollow noopener">diagram</a> of what I
should be doing, and indeed his pointers help me get this code
working originally</li>
<li>It doesn't handle overlapping segments very well. It will
rerun the point generation for these, adding to the overall
time</li>
<li>Ordering of the output rectangles isn't always what you
expect, it jumps around a bit</li>
<li>The end coordinate must be equal to or greater than the start
(using the sample, providing a negative segment size would
trigger this bug)</li>
</ul>
<p>As always the source code for this sample can be downloaded from
the link below.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2013-02-10 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/dividing-up-a-rectangle-based-on-pairs-of-points-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.com