I am trying to grab the RGB value from a click on a colour wheel image.
Now i am trying to grab the RGB from this Colour wheel image below without using canvas getImageData.
How can i do this without canvas?
With canvas you can run.
$('#picker').click(function(e) { // mouse move handler
// get coordinates of current position
var canvasOffset = $(canvas).offset();
var canvasX = Math.floor(e.pageX - canvasOffset.left);
var canvasY = Math.floor(e.pageY - canvasOffset.top);
alert("canvasX " + canvasX + " canvasY " + canvasY);
// get current pixel
var imageData = ctx.getImageData(canvasX, canvasY, 1, 1);
var pixel = imageData.data;
alert(JSON.stringify(pixel));
});
I cannot use canvas for this only javascript can anyone tell me the best way to getImageData from a image would i need to build a rgb grid with a for loop over the top and work out the selected pixel from the x and y value and then match it.
Really stumped on where to start any help?
Here's #Pointy's good idea put to code:
Illustrating with just 1 of your color wheel wedges. You can expand this starting point to include all wedges in your color wheel.
Example code and a Demo: http://jsfiddle.net/m1erickson/9pc4orw8/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#wheel{border:1px solid red;}
#results{width:50px;height:25px;background-color:black;border:1px solid green;}
</style>
<script>
$(function(){
var $wheel=$("#wheel");
var wheelOffset=$wheel.offset();
var offsetX=wheelOffset.left;
var offsetY=wheelOffset.top;
var PI=Math.PI;
var cx=0;
var cy=0;
// define the angle & radius of each color in the color wheel
var colors=[];
colors.push({sAngle:0,eAngle:PI/6,sRadius:0,eRadius:15,color:'rgb(255,255,255)'});
colors.push({sAngle:0,eAngle:PI/6,sRadius:15,eRadius:32,color:'rgb(253,237,238)'});
colors.push({sAngle:0,eAngle:PI/6,sRadius:32,eRadius:53,color:'rgb(251,211,212)'});
colors.push({sAngle:0,eAngle:PI/6,sRadius:53,eRadius:87,color:'rgb(246,145,149)'});
colors.push({sAngle:0,eAngle:PI/6,sRadius:87,eRadius:117,color:'rgb(240,73,80)'});
colors.push({sAngle:0,eAngle:PI/6,sRadius:117,eRadius:138,color:'rgb(237,27,36)'});
$("#wheel").mousedown(function(e){handleMouseDown(e);});
// test the current mouse position against the saved angle & radius of each color
// return the colors index of any color under the mouse
function hit(mx,my){
var hitIndex=-1;
for(var i=0;i<colors.length;i++){
var c=colors[i];
var a0=c.sAngle;
var a1=c.eAngle;
var r0=c.sRadius;
var r1=c.eRadius;
var dx=mx-cx;
var dy=my-cy;
var radius=Math.sqrt(dx*dx+dy*dy);
var angle=Math.atan2(dy,dx);
if(angle>a0 && angle<a1 && radius>r0 && radius<r1){
hitIndex=i;
break;
}
}
return(hitIndex);
}
function handleMouseDown(e){
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
var colorIndex=hit(mx,my);
if(colorIndex>=0){
$('#results').css('background-color',colors[colorIndex].color);
}
}
}); // end $(function(){});
</script>
</head>
<body>
Click to select color:<div id=results></div>
<img id=wheel src='https://dl.dropboxusercontent.com/u/139992952/multple/colorwheel1.png'/>
</body>
</html>
Related
Below is an easy way to put some text on a pixi.js (using WebGL) canvas.
How can we scroll / zoom the displayed part of the canvas ?
(i.e. mouse down + drag should move ...)
Example of what I would like to achieve : http://s419743653.onlinehome.fr/things/test2.htm
<!DOCTYPE HTML>
<html>
<head>
<title>pixi.js example 1</title>
<script src="pixi.js"></script>
</head>
<body>
<script>
var stage = new PIXI.Stage(0xFFFFFF);
var renderer = PIXI.autoDetectRenderer(800, 600);
document.body.appendChild(renderer.view);
var text = new PIXI.Text("Hello World", {font:"50px Arial", fill:"black"});
stage.addChild(text);
renderer.render(stage);
</script>
</body>
</html>
set the scale property on the DisplayObjectContainer. In your case you don't have any so you can scale the stage.
stage.scale.x = 2;
stage.scale.y = 2;
or you can put your objects in a group and scale the group.
var group = new Pixi.DisplayObjectContainer();
group.scale.x = 2;
group.scale.y = 2;
group.add(text);
I want to draw 200 or more(highly fluid) object in canvas.
and add mouse over, mouse click event each of them.
source code like this...
(valiable k is increase)
'
....
....
for( k = 0 ; k < 200; k++){
start = start[k];
end = end[k];
x1 = centerX-radius*Math.sin(-arg*start)*0.9;
y1 = centerY-radius*Math.cos(-arg*start)*0.9;
x2 = centerX-radius*Math.sin(-arg*start)*0.95;
y2 = centerY-radius*Math.cos(-arg*start)*0.95;
x3 = centerX-radius*Math.sin(-arg*end)*0.95;
y3 = centerY-radius*Math.cos(-arg*end)*0.95;
x4 = centerX-radius*Math.sin(-arg*end)*0.9;
y4 = centerY-radius*Math.cos(-arg*end)*0.9;
Shape(ctx, x1,y1,x2,y2,x3,y3,x4,y4,k);
}
function Shape(ctx, x1,y1,x2,y2,x3,y3,x4,y4, k){
ctx.strokeStyle = "black";
ctx.fillStyle = "red";
ctx.globalAlpha = 1.0;
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.lineTo(x3,y3);
ctx.lineTo(x4,y4);
ctx.lineTo(x1,y1);
ctx.lineWidth = 0.5;
ctx.fill();
ctx.stroke();
ctx.fillText(k,(x2+x3)/2,(y2+y3)/2);
}
....
....
my hope is..
if mouse over on shape, display valiable k
if mouse click on shape, go other url with valiable k parameter
but, I did not want to use image.
please help me.
thanks.
If you keep the shape's coordinates in an array, you can loop trough them and check for collision with the mouse's coordinates.
Canvas are drawn like bitmap. All the changes are made to the pixels, and no trace of line or path would be kept. If you want to see whether a click is on or inside certain path, you need to implement you own hit test. If the shapes you draw can overlap each other, you will need to handle the order by yourself. It's doable, but you are on your own.
The other way out is to use SVG instead. Because SVG are objects, browser will keep track of them for you. You can just add onclick to an SVG element like you do to an HTML element.
The easiest solution would be just use library like d3: http://d3js.org/
Since your shapes are irregular, it would be difficult to do hit-testing mathematically.
Fortunately, the context has the isPointInPath method that will test if the supplied mouseX / mouseY is inside the last defined path.
To hit-test your irregular shapes:
Keep enough information to redefine each path in an object.
Add each shape object to an array.
In the mousemove event handler...
Iterate through the array
Redefine each shape (1 at a time). Note: redefining is drawing without stroke/fill.
Use context.isPointInPath(mouseX,mouseY) to hit-test if the mouse is inside the last defined shape.
Here is example code and a Demo: http://jsfiddle.net/m1erickson/o5xp21t2/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
ctx.strokeStyle = "black";
ctx.fillStyle = "red";
ctx.globalAlpha = 1.0;
ctx.lineWidth = 0.5;
ctx.font="14px verdana";
var centerX=150;
var centerY=150;
var radius=120;
var arg=1;
var start=0;
var end=Math.PI/8;
var shapes=[];
for(var k=0;k<10;k++){
start+=Math.PI/8;
end+=Math.PI/8;
x1 = centerX-radius*Math.sin(-arg*start)*0.9;
y1 = centerY-radius*Math.cos(-arg*start)*0.9;
x2 = centerX-radius*Math.sin(-arg*start)*0.95;
y2 = centerY-radius*Math.cos(-arg*start)*0.95;
x3 = centerX-radius*Math.sin(-arg*end)*0.95;
y3 = centerY-radius*Math.cos(-arg*end)*0.95;
x4 = centerX-radius*Math.sin(-arg*end)*0.9;
y4 = centerY-radius*Math.cos(-arg*end)*0.9;
var s={x1:x1,y1:y1,x2:x2,y2:y2,x3:x3,y3:y3,x4:x4,y4:y4,k:k};
shapes.push(s);
Shape(s,k,true);
}
$results=$("#results");
$("#canvas").mousemove(function(e){handleMouseMove(e);});
function handleMouseMove(e){
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
for(var k=0;k<shapes.length;k++){
Shape(shapes[k],k,false);
if(ctx.isPointInPath(mouseX,mouseY)){
$results.text("Last mouseover: "+k);
}
}
}
function Shape(s, k, draw){
ctx.fillStyle="red";
ctx.beginPath();
ctx.moveTo(s.x1,s.y1);
ctx.lineTo(s.x2,s.y2);
ctx.lineTo(s.x3,s.y3);
ctx.lineTo(s.x4,s.y4);
ctx.lineTo(s.x1,s.y1);
if(draw){
ctx.fill();
ctx.stroke();
ctx.fillStyle="blue";
ctx.fillText(k,(s.x2+s.x3)/2,(s.y2+s.y3)/2);
}
}
}); // end $(function(){});
</script>
</head>
<body>
<p id=results>Hover mouse over shapes.</p>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I have a large canvas containing an image, as shown in the example below:
I have the position and rotation angle of the red rectangle:
red : {
top : top,
left : left,
width : width,
height : height,
angle : angle
}
I also have a full set of translated coordinates denoting the actual corner points of the red rotated rectangle.
Finally, I have the position of the blue rectangle relative to the red rectangle:
blue : {
left : left,
top : top,
width : width,
height : height
}
What I need to do is create a new canvas that is the size of the blue rectangle. The new canvas should contain the correctly rotated portion of the image that is contained within the blue rectangle. The resulting image should look like this:
Here is my JavaScript code so far:
var c = getCenterPoint(); // returns center x/y positions of the RED rectangle
canvas.width = blue.width;
canvas.height = blue.height;
var blueX = red.left + blue.left;
var blueY = red.top + blue.top;
var tx = blueX - c.x;
var ty = blueY - c.y;
this.cursorContext.translate(tx, ty);
this.cursorContext.rotate(angle * (Math.PI / 180));
this.cursorContext.translate(-tx, -ty);
this.cursorContext.drawImage(image, -blueX, -blueY, blue.width, blue.height);
The problem I am having is getting the correct portion of the image when the rectangle is rotated. How can I do this?
You can use a temporary canvas to clip and unrotate your blue box
Clip the boundingbox of the blue rectangle from the image
Unrotate the boundingbox so the blue rectangle is unrotated (angle==0)
Clip the extra boundingbox area away to reveal only the blue rectangle
Draw the blue rectangle to the display canvas
Here’s code and a Demo: http://jsfiddle.net/m1erickson/28EkG/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// blue rect's info
var blueX=421;
var blueY=343;
var blueWidth=81;
var blueHeight=44;
var blueAngle=-25.00*Math.PI/180;
// load the image
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp6.jpg";
function start(){
// create 2 temporary canvases
var canvas1=document.createElement("canvas");
var ctx1=canvas1.getContext("2d");
var canvas2=document.createElement("canvas");
var ctx2=canvas2.getContext("2d");
// get the boundingbox of the rotated blue box
var rectBB=getRotatedRectBB(blueX,blueY,blueWidth,blueHeight,blueAngle);
// clip the boundingbox of the rotated blue rect
// to a temporary canvas
canvas1.width=canvas2.width=rectBB.width;
canvas1.height=canvas2.height=rectBB.height;
ctx1.drawImage(img,
rectBB.cx-rectBB.width/2,
rectBB.cy-rectBB.height/2,
rectBB.width,
rectBB.height,
0,0,rectBB.width,rectBB.height
);
// unrotate the blue rect on the temporary canvas
ctx2.translate(canvas1.width/2,canvas1.height/2);
ctx2.rotate(-blueAngle);
ctx2.drawImage(canvas1,-canvas1.width/2,-canvas1.height/2);
// draw the blue rect to the display canvas
var offX=rectBB.width/2-blueWidth/2;
var offY=rectBB.height/2-blueHeight/2;
canvas.width=blueWidth;
canvas.height=blueHeight;
ctx.drawImage(canvas2,-offX,-offY);
} // end start
// Utility: get bounding box of rotated rectangle
function getRotatedRectBB(x,y,width,height,rAngle){
var absCos=Math.abs(Math.cos(rAngle));
var absSin=Math.abs(Math.sin(rAngle));
var cx=x+width/2*Math.cos(rAngle)-height/2*Math.sin(rAngle);
var cy=y+width/2*Math.sin(rAngle)+height/2*Math.cos(rAngle);
var w=width*absCos+height*absSin;
var h=width*absSin+height*absCos;
return({cx:cx,cy:cy,width:w,height:h});
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I tried every example that is on internet and NONE of them is working.
I tried to get a full image into a canvas, but it's zoomed in with really bad quality.
I tried rotate (standing pictures need to be laying) and I need the dataurl back.
Getting the dataurl was not the problem.
The problem is that it doesn't work.
<script>
function readFile(file, callback){
var reader = new FileReader();
reader.onload = callback
reader.readAsDataURL(file);
result = reader.readAsDataURL(file);
console.log(result);
}
function snapitgetFile(){
snapitloadshit();
}
function snapitloadshit(){
$('#snapitUpload').on('change', function(e){
var filesize = this.files[0].size;
var filename = this.files[0].name;
var extension = filename.substring(filename.lastIndexOf('.')+1);
var filePath = $(this).val();
readFile(this.files[0], function(e) {
//console.log(e.target.result);
var canvas = document.getElementById("pic");
var ctx = canvas.getContext("2d");
var image = new Image();
image.onload = function() {
var imgwidth = this.width;
var imgheigt = this.height;
$('#pic').css('height', imgheigt);
$('#pic').css('width', imgwidth);
ctx.drawImage(image, 0,0);
};
image.src = e.target.result;
});
});
}
</script>
demo: http://www.aeglobalresearch.com/demo/rotate.html
Try to select a picture and you see what I mean.
I googled and checked here, but all examples makes the picture look ugly or moves them to weird position.
I just want to get load the picture, rotate 90 degrees so a picture that is standing is laying.
Get the dataurl and than the rest of my script will do the rest.
Keeping all the exif, it just need to rotate 90 degrees right that's all.
What I'm doing wrong
Do the rotation like this:
make the canvas the same size as the image (as #Akaryatrh says),
translate to the center of the image,
rotate by 90 degrees (==90*Math.PI/180)
draw the image offset by -img.width/2 and -img.height/2
Demo: http://jsfiddle.net/m1erickson/NZZ3s/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();img.onload=start;img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/2014.png";
function start(){
canvas.width=img.width;
canvas.height=img.height;
ctx.translate(img.width/2,img.height/2);
ctx.rotate(90 * Math.PI/180);
ctx.drawImage(img,-img.width/2,-img.height/2);
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Replace these lines :
$('#pic').css('height', imgheigt);
$('#pic').css('width', imgwidth)
With this :
canvas.height = imgheigt;
canvas.width = imgwidth;
Actually you were setting width and height css styles to your canvas, while you were supposed to modify element attributes.
[EDIT] Here's a full solution on jsfiddle with 90° rotation : http://jsfiddle.net/Akaryatrh/r6u9c/
I have one image like this and I want to fill with pattern like this to make result like this .
I can fill the pattern using following code but I don't know how to curve pattern properly along the collar shape because it should look like real but my result become like this. .
Here is my sample script
$(function(){
drawCanvas("body","collar","images/collar.png", 180);
function drawCanvas(overlayType, canvasID, imageSource, degreeRotate){
var canvas=document.getElementById(canvasID);
var ctx=canvas.getContext("2d");
var imgBody=new Image();
var imgPattern=new Image();
imgPattern.onload=function(){
imgBody.onload=function(){
start();
}
imgBody.src=imageSource;
}
imgPattern.src="images/pattern.png";
function start(){
ctx.drawImage(imgBody,0,0);
if(overlayType=="body"){
ctx.globalCompositeOperation="source-atop";
ctx.globalAlpha=.85;
var pattern = ctx.createPattern(imgPattern, 'repeat');
ctx.fillStyle = pattern;
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.rotate(degreeRotate * Math.PI/180);
ctx.fill();
ctx.translate(150,0);
ctx.globalAlpha=.1;
ctx.drawImage(imgBody,150,0);
}
}
}});
Can someone guide me to how to manage pattern to curve along side collar shape to look like real?
You can get this close by simply slicing and offsetting your pattern vertically
Original "collar" image:
Collar filled with curved checkered texture
**The Method:*
Create an image tiled with your checkered texture.
Draw that texture image onto a temporary canvas.
Get the image data for the temporary canvas.
For each column of pixels, offset that entire column by the curve of your collar.
A quadratic curve fits the curve of your collar well, so I used that in the example to determin the Y offset.
Put the image data back on the temporary canvas.
(You now have a curved checkered pattern to apply to your collar image).
Draw the collar image on your main canvas.
Set context.globalCompositeOperation=”source-atop”
Draw the texture from the temporary canvas onto the main canvas.
(The compositing mode will paint the texture only on the collar—not the background)
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/hdXyk/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
// get canvas references (canvas=collar, canvas1=texture)
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvas1=document.getElementById("canvas1");
var ctx1=canvas1.getContext("2d");
// preload the texture and collar images before starting
var textureImg,collarImg;
var imageURLs=[];
var imagesOK=0;
var imgs=[];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/checkered.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/collar.png");
loadAllImages();
function loadAllImages(callback){
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
img.crossOrigin="anonymous";
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK==imageURLs.length ) {
textureImg=imgs[0];
collarImg=imgs[1];
start();
}
};
img.src = imageURLs[i];
}
}
function start(){
// set both canvas dimensions
canvas.width=collarImg.width;
canvas.height=collarImg.height+5;
canvas1.width=textureImg.width;
canvas1.height=textureImg.height;
// draw the textureImg on canvas1
ctx1.drawImage(textureImg,0,0,canvas1.width,canvas1.height);
// curve the texture into a collar shaped curved
curveTexture(collarImg.width,collarImg.height);
// draw the collarImg on canvas
ctx.drawImage(collarImg,0,0);
// set compositing to source-atop
// any new drawing will ONLY fill existing non-transparent pixels
ctx.globalCompositeOperation="source-atop";
// draw the curved texture from canvas1 onto the collar of canvas
// (the existing pixels are the collar, so only the collar is filled)
ctx.drawImage(canvas1,0,0);
}
function curveTexture(w,h){
// define a quadratic curve that fits the collar bottom
// These values change if the collar image changes (+5,-32)
var x0=0;
var y0=h+5;
var cx=w/2;
var cy=h-32;
var x1=w;
var y1=h+5;
// get a,b,c for quadratic equation
// equation is used to offset columns of texture pixels
// in the same shape as the collar
var Q=getQuadraticEquation(x0,y0,cx,cy,x1,y1);
// get the texture canvas pixel data
// 2 copies to avoid self-referencing
var imageData0 = ctx1.getImageData(0,0,w,h);
var data0 = imageData0.data;
var imageData1 = ctx1.getImageData(0,0,w,h);
var data1 = imageData1.data;
// loop thru each vertical column of pixels
// Offset the pixel column into the shape of the quad-curve
for(var y=0; y<h; y++) {
for(var x=0; x<w; x++) {
// the pixel to write
var n=((w*y)+x)*4;
// the vertical offset amount
var yy=parseInt(y+h-(Q.a * x*x + Q.b*x + Q.c));
// the offset pixel to read
var nn=((w*yy)+x)*4;
// offset this pixel by the quadCurve Y value (yy)
data0[n+0]=data1[nn+0];
data0[n+1]=data1[nn+1];
data0[n+2]=data1[nn+2];
data0[n+3]=data1[nn+3];
}
}
ctx1.putImageData(imageData0,0,0);
}
// Quadratic Curve: given x coordinate, find y coordinate
function getQuadraticY(x,Q){
return(Q.a * x*x + Q.b*x + Q.c);
}
// Quadratic Curve:
// Given: start,control,end points
// Find: a,b,c in quadratic equation ( y=a*x*x+b*x+c )
function getQuadraticEquation(x0,y0,cx,cy,x2,y2){
// need 1 more point on q-curve, so calc its midpoint XY
// Note: since T=0.5 therefore TT=(1-T)=0.5 also [so could simplify]
var T=0.50;
var TT=1-T;
var x1=TT*TT*x0+2*TT*T*cx+T*T*x2;
var y1=TT*TT*y0+2*TT*T*cy+T*T*y2;
var A = ((y1-y0)*(x0-x2)
+ (y2-y0)*(x1-x0))/((x0-x2)*(x1*x1-x0*x0)
+ (x1-x0)*(x2*x2-x0*x0));
var B = ((y1-y0)-A*(x1*x1-x0*x0))/(x1-x0);
var C = y0-A*x0*x0-B*x0;
return({a:A,b:B,c:C});
}
}); // end $(function(){});
</script>
</head>
<body>
<p>"Curve" a texture by offsetting Y pixels based on Q-curve</p>
<canvas id="canvas" width=300 height=300></canvas>
<p>The temporary texture canvas</p>
<canvas id="canvas1" width=300 height=300></canvas>
</body>
</html>
You can triangulate the polygon and then bend the mesh. Then you can fill the mesh with the pattern. Here is an example of a triangulation in Java: How does this code for delaunay triangulation work?. Here is an example of a triangulation and a bit of work to remove long edges. It's a concave hull of a 2d point set.