Instantly share code, notes, and snippets.

Embed
What would you like to do?
Sutherland-hogson polygon clipping javascript
<!--
Sutherland-Hodgson polygon clipping algorithm using html5 canvas and javascript. Click to clip polygon sequentially
Copyright (C) 2014 Kevin Martin Jose
-->
<!--
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-->
<html>
<head>
<title>CG assignment 1 Q2</title>
</head>
<body>
<canvas width="800" height="800" id="canvas"></canvas>
<script type="text/javascript">
/*HOW TO USE :
Open this file in a modern browser (firefox version 26 and above)
Click anywhere on the diagram to start clipping the polygon
To see console log, press ctrl+shift+k
*/
//N.B : All variables declared with the prefix 'var' are global
//viewport is a square of vertices a, b, c and d
var ax = 200;
var ay = 200;
var bx = 500;
var by = 200;
var cx = 500;
var cy = 500;
var dx = 200;
var dy = 500;
//min max values required to know the bounds of the viewport
var xmin = ax
var xmax = cx
var ymin = ay;
var ymax = cy;
//vertices in the polygon and the new clipped polygon
var polygonPath=[];
var clippedPath=[];
//Determines which edge to clip when mouse is clicked
var clipEdge=0;
//Boilerplate code. Required to access the html5 canvas
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
//Google "html5 compositing"
context.globalCompositeOperation = 'source-over';
//The next 4 blocks draw our viewport
//drawing line x=200, of length 300 units (pixels)
context.beginPath();
context.moveTo(ax, ay);
context.lineTo(bx, by);
context.stroke();
//drawing line y=200, length 300 pixels
context.beginPath();
context.moveTo(bx, by);
context.lineTo(cx,cy);
context.stroke();
//drawing line x=500
context.beginPath();
context.moveTo(cx, cy);
context.lineTo(dx, dy);
context.stroke();
//drawing line y=500
context.beginPath();
context.moveTo(dx, dy);
context.lineTo(ax, ay);
context.stroke()
//Put the vertices of our original polygon into polygonPath
polygonPath.push([100, 400]);
polygonPath.push([350, 100]);
polygonPath.push([600, 400]);
polygonPath.push([400, 400]);
polygonPath.push([380, 600]);
polygonPath.push([320, 600]);
polygonPath.push([300, 400]);
polygonPath.push([100, 400]);
//Draw the polygon filled with red
context.fillStyle = "#ff0000";
drawPolygon(polygonPath);
//Executes on every mouse click
canvas.addEventListener('mousedown', function(){
//
clipEdge++;
clip(polygonPath);
});
function clip(path)
{
switch(clipEdge)
{
case 1:
//We draw a white polygon of same dimensions of the original one to erase it
//Then we draw the new clipped polygon with respect to each edge
clip_left(path);
context.fillStyle = "#ffffff";
drawPolygon(path);
context.fillStyle = "#00ff00";
drawPolygon(clippedPath);
break;
case 2:
//slice() clones an array and returns a reference to it
//After each clipping, the clipped polygon becomes the polygon to be clipped next
path = clippedPath.slice();
clippedPath = [];
clip_top(path);
context.fillStyle = "#ffffff";
drawPolygon(path);
context.fillStyle = "#0000ff";
drawPolygon(clippedPath);
break;
case 3:
path = clippedPath.slice();
clippedPath = [];
clip_right(path);
context.fillStyle = "#ffffff";
drawPolygon(path);
context.fillStyle = "#ffff00";
drawPolygon(clippedPath);
break;
case 4:
path = clippedPath.slice();
clippedPath = [];
clip_down(path);
context.fillStyle = "#ffffff";
drawPolygon(path);
context.fillStyle = "#ff0000";
drawPolygon(clippedPath);
break;
}
}
function clip_left(path)
{
console.log("clipping from left");
console.log("path : " + path);
for(i=0; i<path.length-1; i++)
{
//Both points outside
if(!isInside(path[i], 'left') && !isInside(path[i+1], 'left'))
{
console.log('Both ' + path[i] + " and " + path[i+1] + " are outside the clipping area");
//No points are added to the clipped polygon
}
//Both points inside
else if(isInside(path[i], 'left') && isInside(path[i+1], 'left'))
{
console.log('Both ' + path[i] + " and " + path[i+1] + " are inside the clipping area");
//The ith point is added to the clipped polygon
clippedPath.push(path[i]);
}
//ith point is inside and i+1t point is outside
else if(isInside(path[i], 'left'))
{
if(!isInside(path[i+1], 'left'))
{
console.log('Point ' + path[i] + ' is inside and ' + path[i+1] + ' is outside');
//Find the intersection of the point outside left edge and the left edge
endpoints = [path[i], path[i+1]];
intersection = find_intersection(endpoints, 'left');
console.log("intersects at " + intersection);
clippedPath.push(path[i]);
clippedPath.push(intersection);
}
}
else if(!isInside(path[i], 'left'))
{
if(isInside(path[i+1], 'left'))
{
console.log('Point ' + path[i] + ' is outside and ' + path[i+1] + ' is inside');
endpoints = [path[i], path[i+1]];
intersection = find_intersection(endpoints, 'left');
console.log("intersects at " + intersection);
clippedPath.push(intersection);
clippedPath.push(path[i+1]);
}
}
}
//So that the clipped polygon is closed
clippedPath.push(clippedPath[0]);
}
function clip_top(path)
{
for(i=0; i<path.length-1; i++)
{
if(!isInside(path[i], 'top') && !isInside(path[i+1], 'top'))
{
//No points are added to the clipped polygon
}
else if(isInside(path[i], 'top') && isInside(path[i+1], 'top'))
{
clippedPath.push(path[i]);
}
else if(isInside(path[i], 'top'))
{
if(!isInside(path[i+1], 'top'))
{
endpoints = [path[i], path[i+1]];
intersection = find_intersection(endpoints, 'top');
clippedPath.push(path[i]);
clippedPath.push(intersection);
}
}
else if(!isInside(path[i], 'top'))
{
if(isInside(path[i+1], 'top'))
{
endpoints = [path[i], path[i+1]];
intersection = find_intersection(endpoints, 'top');
clippedPath.push(intersection);
clippedPath.push(path[i+1]);
}
}
}
clippedPath.push(clippedPath[0]);
}
function clip_right(path)
{
console.log("clippingfrom right");
for(i=0; i<path.length-1; i++)
{
if(!isInside(path[i], 'right') && !isInside(path[i+1], 'right'))
{
}
else if(isInside(path[i], 'right') && isInside(path[i+1], 'right'))
{
clippedPath.push(path[i]);
}
else if(isInside(path[i], 'right'))
{
if(!isInside(path[i+1], 'right'))
{
endpoints = [path[i], path[i+1]];
intersection = find_intersection(endpoints, 'right');
clippedPath.push(path[i]);
clippedPath.push(intersection);
}
}
else if(!isInside(path[i], 'right'))
{
if(isInside(path[i+1], 'right'))
{
endpoints = [path[i], path[i+1]];
intersection = find_intersection(endpoints, 'right');
clippedPath.push(intersection);
clippedPath.push(path[i+1]);
}
}
}
clippedPath.push(clippedPath[0]);
}
function clip_down(path)
{
console.log("clipping down");
for(i=0; i<path.length-1; i++)
{
if(!isInside(path[i], 'down') && !isInside(path[i+1], 'down'))
{
}
else if(isInside(path[i], 'down') && isInside(path[i+1], 'down'))
{
clippedPath.push(path[i]);
}
else if(isInside(path[i], 'down'))
{
if(!isInside(path[i+1], 'down'))
{
endpoints = [path[i], path[i+1]];
intersection = find_intersection(endpoints, 'down');
clippedPath.push(path[i]);
clippedPath.push(intersection);
}
}
else if(!isInside(path[i], 'down'))
{
if(isInside(path[i+1], 'down'))
{
endpoints = [path[i], path[i+1]];
intersection = find_intersection(endpoints, 'down');
clippedPath.push(intersection);
clippedPath.push(path[i+1]);
}
}
}
clippedPath.push(clippedPath[0]);
}
function find_intersection(endpoints, edge)
{
//endpoints - the end points of an edge of the polygon
//edge - the edge with which we want to calculate the intersection with
intersection = [];
//all lines are of the form y = mx + c
//m = (y2-y1)/(x2-x1)
start = endpoints[0];
end = endpoints[1];
x1 = start[0];
y1 = start[1];
x2 = end[0];
y2 = end[1];
m = (y2-y1)/(x2-x1);
//find the constant c
c = y1 - m*x1;
//To find intersection with left edge
if(edge == 'left')
{
intersection[0] = xmin;
intersection[1] = m*xmin + c;
}
else if(edge == 'right')
{
intersection[0] = xmax;
intersection[1] = m*xmax + c;
}
else if(edge == 'top')
{
intersection[0] = (ymin - c)/m;
intersection[1] = ymin;
}
else if(edge == 'down')
{
intersection[0] = (ymax - c)/m;
intersection[1] = ymax;
}
return intersection;
}
//returns true if the point is inside with respect to a particular edge
function isInside(point, orientation)
{
x = point[0];
y = point[1];
if(orientation == 'left')
{
if(x > xmin)
return true;
else
return false;
}
else if(orientation == 'top')
{
if(y > ymin)
return true;
else
return false;
}
else if(orientation == 'right')
{
if(x < xmax)
return true;
else
return false;
}
else if(orientation == 'down')
{
if(y < ymax)
return true;
else
return false;
}
}
function drawPolygon(path)
{
context.beginPath();
context.moveTo(path[0][0], path[0][1]);
for(i=1; i<path.length; i++)
{
context.lineTo(path[i][0], path[i][1]);
}
context.fill();
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment