{"id":303,"date":"2015-11-02T11:37:49","date_gmt":"2015-11-02T09:37:49","guid":{"rendered":"http:\/\/blog.teleranek.org\/?p=303"},"modified":"2025-06-29T12:29:13","modified_gmt":"2025-06-29T10:29:13","slug":"greiner-hormann-clipping-algorithm-for-degenerate-cases-and-curves","status":"publish","type":"post","link":"https:\/\/blog.teleranek.org\/?p=303","title":{"rendered":"Greiner-Hormann clipping algorithm for degenerate cases and curves"},"content":{"rendered":"<p>Today&#8217;s 200th aniversary of <a href=\"https:\/\/en.wikipedia.org\/wiki\/George_Boole\"><strong>Goerge Boole<\/strong>&#8216;s<\/a> birth, so this is a post about polygon clipping &#8211; <a href=\"https:\/\/en.wikipedia.org\/wiki\/Boolean_operations_on_polygons\">boolean operations on polygons<\/a>.<br \/>\nMost popular (and quite simple and fast) algorithm for this task is <a href=\"https:\/\/en.wikipedia.org\/wiki\/Greiner%E2%80%93Hormann_clipping_algorithm\">Greiner-Hormann<\/a>. Unfortunately, as wikipedia says, it has 1 big drawback &#8211; cannot handle shapes with common edges. So XOR boolean operation won&#8217;t work. And we won&#8217;t be able to clip following &#8220;rectangles&#8221; (<strong>AND<\/strong> operation in this example):<\/p>\n<p><span style=\"font-size: 60px;\"><span style=\"color: #00ccff;\">[<\/span><span style=\"color: #ff6600;\">[<\/span><span style=\"color: #00ccff;\">]<\/span><span style=\"color: #ff6600;\">]<\/span><span style=\"color: #ffffff;\">&rarr;<\/span><span style=\"color: #333333;\">[<\/span><span style=\"color: #ffffff;\">[]<\/span><span style=\"color: #333333;\">]<\/span><\/span><\/p>\n<p>Original <em>Greiner-Hormann<\/em> consists of 3 phases:<\/p>\n<ol>\n<li>Compute intersections and add them to polygon<\/li>\n<li>Marking intersections as &#8220;entering&#8221; or &#8220;exiting&#8221;<\/li>\n<li>Final traversal and generating new polygons<\/li>\n<\/ol>\n<p>The improved algorithm involves just adding additional phase of algorithm, just after the 1st one:<br \/>\n<strong>1.5 disabling &#8220;bad&#8221; intersections<\/strong><\/p>\n<p>Of course all intersections shouldn&#8217;t double themselves, so before adding intersections to polygon you should remove duplicate intersections. Doubling intersections are esp. possible when computing bezier intersections using traditional bezier clipping or computing intersections via approximation of bezier curves with polygons &#8211; the approximation leads to finding too many intersections for almost collinear curves.<\/p>\n<p>Disabling bad intersections should mark intersections as &#8220;bad&#8221; and then they should be ignored in further steps of an algorithm.<\/p>\n<p>The following pseudocode explains disabling bad intersections algorithm:<\/p>\n<ol>\n<li>Do this for each intersection of current polygon in intersections:<br \/>\ncompute 2 boolean properties in current intersection: previousInside and nextInside. These properties determine whether the edge that is before the intersection point and the edge that is after the intersection point lies inside the second polygon (not current). In practice, we will be checking whether the point in the middle of the next and previous edge is inside second polygon, using some <a href=\"http:\/\/geomalgorithms.com\/a03-_inclusion.html\">PIP-check algorithm<\/a>, for example winding or odd-even algorithm.<strong>Important remark<\/strong>: if previous or next edge is collinear with corresponding edge in the second polygon, we mark it always as inside, or always as outside. (we could also\u00a0mark it as &#8220;collinear&#8221; and then during second phase use some advanced heuristic how to treat collinear edges, but I won&#8217;t cover that method in this article, maybe later&#8230;).<\/li>\n<li>Now traverse polygon. If we meet an intersection, and when <code>intersection.nextInside == intersection.prevInside<\/code> then disable this intersection<\/li>\n<\/ol>\n<p><strong>Remark:<\/strong> To apply this algorithm to curves, we can treat each curve segment as polygon segment. We just have to change the collinearity check to support curves, and the PIP check algorithm should be changed accordingly (e.g. check insideness through bezier subdivision).<\/p>\n<p>As you can see, this simple change repairs <em>Greiner-Hormann<\/em> and there&#8217;s no need for perturbing vertices, as suggested in the original paper and on Wikipedia. Perturbing could generate random results in some cases.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today&#8217;s 200th aniversary of Goerge Boole&#8216;s birth, so this is a post about polygon clipping &#8211; boolean operations on polygons. Most popular (and quite simple and fast) algorithm for this task is Greiner-Hormann. Unfortunately, as wikipedia says, it has 1 big drawback &#8211; cannot handle shapes with common edges. So XOR boolean operation won&#8217;t work. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[95,92,91,93,94],"class_list":["post-303","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-algorithms","tag-boolean-operations","tag-clipping","tag-greiner-hormann","tag-polygon-clipping"],"_links":{"self":[{"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=\/wp\/v2\/posts\/303","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=303"}],"version-history":[{"count":11,"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=\/wp\/v2\/posts\/303\/revisions"}],"predecessor-version":[{"id":315,"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=\/wp\/v2\/posts\/303\/revisions\/315"}],"wp:attachment":[{"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=303"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=303"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.teleranek.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=303"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}