Skip to content

Instantly share code, notes, and snippets.

@markusl
Created July 31, 2012 17:04
Show Gist options
  • Save markusl/3218518 to your computer and use it in GitHub Desktop.
Save markusl/3218518 to your computer and use it in GitHub Desktop.
FSharp translation of the Cohen–Sutherland 2D line clipping algorithm
/// FSharp translation of the Cohen–Sutherland 2D line clipping algorithm
/// http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
///
/// Implemented in purely functional form which means there are no mutable variables.
/// No guarantees are given on the performance or reliability, it's not
/// thoroughly tested so feel free to submit unit tests. :-)
///
/// Markus Lindqvist 07/2012
module CohenSutherland
// Enumeration which defines if the point lies inside or outside the given area
type OutCode = Inside = 0 | Left = 1 | Right = 2 | Bottom = 4 | Top = 8
/// Defines the clipping functionality
type CohenSutherland() =
/// Clips the given line (p1 - p2) with the given rectangle (from min to max)
/// Returns None if line is not within the given rectangle or Some(p1, p2) for clipped line
static member clip (min:vector, max:vector) (p1:vector) (p2:vector) =
// Get the status code for given point (check if point is inside or not)
let getOutcode (min:vector) (max:vector) (point:vector) =
let getXAxisCode =
if point.[0] < min.[0] then OutCode.Left
else if point.[0] > max.[0] then OutCode.Right
else OutCode.Inside
let getYAxisCode =
if point.[1] < min.[1] then OutCode.Bottom
else if point.[1] > max.[1] then OutCode.Top
else OutCode.Inside
getXAxisCode ||| getYAxisCode
// Recursive function to clip next part of line
let rec clipNext outcode1 outcode2 (p1:vector) (p2:vector) =
// Completely inside, return points as such
if outcode1 ||| outcode2 = OutCode.Inside then
Some(p1, p2)
// Completely outside, stop here
else if outcode1 &&& outcode2 <> OutCode.Inside then
None
// Process next point, from outcode1 first
else
let currentCode = if outcode1 <> OutCode.Inside then outcode1
else outcode2
// Extract points to be more readable in equations
let x0, y0, x1, y1 = p1.[0], p1.[1], p2.[0], p2.[1]
// Clip next point based on outcode1
let getNextPoint =
if (currentCode &&& OutCode.Top) <> OutCode.Inside then
let x = x0 + (x1 - x0) * (max.[1] - y0) / (y1 - y0)
vector [x; max.[1]]
else if (currentCode &&& OutCode.Bottom) <> OutCode.Inside then
let x = x0 + (x1 - x0) * (min.[1] - y0) / (y1 - y0)
vector [x; min.[1]]
else if (currentCode &&& OutCode.Right) <> OutCode.Inside then
let y = y0 + (y1 - y0) * (max.[0] - x0) / (x1 - x0)
vector [max.[0]; y]
else if (currentCode &&& OutCode.Left) <> OutCode.Inside then
let y = y0 + (y1 - y0) * (min.[0] - x0) / (x1 - x0)
vector [min.[0]; y]
else
vector [System.Double.NaN; System.Double.NaN]
// If we processed outcode1, check it again and continue recursively
if currentCode = outcode1 then
clipNext (getOutcode min max getNextPoint) outcode2 getNextPoint p2
else
clipNext outcode1 (getOutcode min max getNextPoint) p1 getNextPoint
// Do the first round here and continue recursively
let outcode1 = getOutcode min max p1
let outcode2 = getOutcode min max p2
clipNext outcode1 outcode2 p1 p2
@markusl
Copy link
Author

markusl commented Jul 31, 2012

Uses Microsoft.FSharp.Math.Vector (add reference to FSharp.PowerPack)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment