Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* "Concave" hulls by Glenn Hudson and Matt Duckham
*
* Source code downloaded from https://archive.md/l3Un5#selection-571.0-587.218 on 3rd November 2021.
*
* - This software is Copyright (C) 2008 Glenn Hudson released under Gnu Public License (GPL). Under
* GPL you are free to use, modify, and redistribute the software. Please acknowledge Glenn Hudson
* and Matt Duckham as the source of this software if you do use or adapt the code in further research
* or other work. For full details of GPL see http://www.gnu.org/licenses/gpl-3.0.txt.
* - This software comes with no warranty of any kind, expressed or implied.
*
* A paper with full details of the characteristic hulls algorithm is published in Pattern Recognition.
* Duckham, M., Kulik, L., Worboys, M.F., Galton, A. (2008) Efficient generation of simple polygons for
* characterizing the shape of a set of points in the plane. Pattern Recognition v41, 3224-3236
*
* The software was developed by Glenn Hudson while working with me as an RA. The characteristic shapes
* algorithm is collaborative work between Matt Duckham, Lars Kulik, Antony Galton, and Mike Worboys.
*
*/
package signalprocesser.voronoi.shapegeneration;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.util.ArrayList;
import signalprocesser.voronoi.VPoint;
public class ShapeGeneration {
/* ********************************************************* */
// Constants
private static final int POINTSLEFT_BEFORE_CUTOFF = 100;
private static final FontRenderContext FONT_RENDER = new FontRenderContext(null, true, true);
/* ********************************************************* */
// Helpful Methods (for debugging)
public static Shape createShape(Area area) {
return new PathIteratorWrapper(area.getPathIterator(null));
}
public static Shape createShape(PathIterator pathiter) {
return new PathIteratorWrapper(pathiter);
}
public static Shape createShape(ArrayList points) {
return new PathIteratorWrapper(new ListPathIterator(points));
}
public static Area createArea(ArrayList points) {
return new Area(new PathIteratorWrapper(new ListPathIterator(points)));
}
/* ********************************************************* */
// Fill with points
public static ArrayList addRandomPoints(ArrayList points_original,
boolean splitlonglines,
int shapepoints, int shapepoint_mindensity,
int internalpoints, int internal_mindensity) throws ShapeGenerationException {
// Protect the original border points - we don't want them to be changed
ArrayList points = new ArrayList();
points.addAll( points_original );
// Create a new array that contains the same boundary points as before
ArrayList randompoints = new ArrayList();
// Can't add points to a line, just return
if ( points.size()<=2 ) {
// Add points to random points
randompoints.addAll(points);
// Return list
return randompoints;
}
// Determine boundary of points
VPoint first = points.get(0);
int min_x = first.x, max_x = first.x;
int min_y = first.y, max_y = first.y;
for ( VPoint point : points ) {
// Check x value
if ( point.xmax_x ) {
max_x = point.x;
}
// Check y value
if ( point.ymax_y ) {
max_y = point.y;
}
}
// Calculate size of image
int width = max_x - min_x + 1;
int height = max_y - min_y + 1;
// Verify width/height (just in case! - below will fail badly if not positive)
if ( width<=0 ) {
throw new ShapeGenerationException("Width of shape is zero - cannot add random points");
} else if ( height<=0 ) {
throw new ShapeGenerationException("Height of shape is zero - cannot add random points");
}
// Initialise array entirely to TRUE values
int x=0, y=0;
boolean[][] array = new boolean[width][height];
for ( x=0 ; xshapepoints ) {
// Determine a random point to remove
int index = (int) ( Math.random() * (points.size() - 1) );
// Remove that index
points.remove(index);
}
// Clear around the radius of shape points - set radius around corner
// points to FALSE, everything else at the end will be TRUE (including
// those points inside AND outside of the shape required)
int index;
VPoint currpoint = null;
for ( index=0 ; index=2 ) {
currpoint = points.get(0);
VPoint prevpoint;
for ( index=1 ; index2*shapepoint_mindensity ) {
index = addPointsToLine(index, points, randompoints, prevpoint, currpoint, min_x, min_y,
array, width, height, shapepoint_circle, shapepoint_mindensity);
}
}
// As we've been removing points from the points list we may not have
// a complete shape - make sure the point is closed, if not close it
VPoint firstpoint = points.get(0);
VPoint lastpoint = points.get(points.size()-1);
if (!( firstpoint.x==lastpoint.x && firstpoint.y==lastpoint.y )) {
// Check if the line needs to be split
if ( splitlonglines && lastpoint!=null && lastpoint.distanceTo(firstpoint)>2*shapepoint_mindensity ) {
addPointsToLine(points.size(), points, randompoints, lastpoint, firstpoint, min_x, min_y,
array, width, height, shapepoint_circle, shapepoint_mindensity);
}
// Add in the final line segment
points.add( first );
}
}
// Reset array back to true and put on circles at internal_mindensity
// rather than shapepoint_mindensity
for ( x=0 ; x0 ; internalpoints-- ) {
// Return if no more positions left
if ( pointsleft<=POINTSLEFT_BEFORE_CUTOFF ) {
return randompoints;
}
// Decide on the position of the next point
int point = (int)( Math.random() * (pointsleft - 1) + 1 );
{foundxycoord:
for ( x=0 ; x points, ArrayList randompoints, VPoint prevpoint, VPoint currpoint, int min_x, int min_y, boolean[][] array, int width, int height, boolean[][] circle, int maxdensity) {
// Determine skip
double length = prevpoint.distanceTo(currpoint);
int pointstoadd = (int)( length/(maxdensity+1) );
// Otherwise consider whether x/y axis has the lower gradient
double grad = (double)(prevpoint.y-currpoint.y) / (double)(prevpoint.x-currpoint.x);
double skip_x = Math.sqrt( ( (length*length) / (pointstoadd*pointstoadd) ) / ( 1 + grad*grad ) );
double skip_y = Math.sqrt( ( (length*length) / (pointstoadd*pointstoadd) ) / ( 1 + 1/(grad*grad) ) );
if ( prevpoint.x>currpoint.x ) skip_x *= -1;
if ( prevpoint.y>currpoint.y ) skip_y *= -1;
// Consider pixel by pixel
int x, y;
for ( int pointnum=1 ; pointnum<=pointstoadd ; pointnum++ ) {
// Determine coordinates
x = prevpoint.x-min_x + (int)( skip_x * (double)pointnum );
y = prevpoint.y-min_y + (int)( skip_y * (double)pointnum );
// If position is not taken then take it
if ( array[x][y] ) {
// Unset the position and the pieces around this position
unsetCircle(-1, x, y,
array, width, height, circle, maxdensity);
// Add this point to both the points list and the
// random points list
VPoint point = new VPoint(x+min_x, y+min_y);
points.add(index, point );
randompoints.add( point );
// Increment index so as to skip over this new point
index++;
}
}
// Return final index
return index;
}
static private int unsetCircle(int pointsleft, int x, int y, boolean[][] array, int width, int height, boolean[][] circle, int maxdensity ) {
int index_x, index_y;
for ( int x2=0 ; x2=width ) {
return pointsleft;
} else if ( index_x<0 || index_y>=height ) {
break;
} else if ( index_y<0 ) {
continue;
} else if ( array[index_x][index_y] ) {
pointsleft--;
array[index_x][index_y] = false;
}
}
}
}
return pointsleft;
}
/* ********************************************************* */
// Create Shape Outline
public static ArrayList createShapeOutline(String text, Rectangle bounds, Font font) throws ShapeGenerationException {
// Create text layout
TextLayout textlayout = new TextLayout(text, font, FONT_RENDER);
// Get the bounds of the shape
Rectangle2D shapebounds = textlayout.getBounds();
// Tranform to the correct location
double scale_x = (double)bounds.width / (double)shapebounds.getWidth();
double scale_y = (double)bounds.height / (double)shapebounds.getHeight();
double translate_x = bounds.x/scale_x - shapebounds.getX();
double translate_y = bounds.y/scale_y - shapebounds.getY();
AffineTransform transform = AffineTransform.getScaleInstance(scale_x, scale_y);
transform.translate(translate_x, translate_y);
// Get the outline of the shape
Shape outline = textlayout.getOutline(transform);
PathIterator pathiter = outline.getPathIterator(null, 0.0);
// Collect the points that form the shape
ArrayList points = new ArrayList();
double currpoint[] = new double[2];
while (!( pathiter.isDone() )) {
// Consider the current element
int type = pathiter.currentSegment(currpoint);
if ( type==PathIterator.SEG_MOVETO ) {
points.add( new VPoint((int)currpoint[0], (int)currpoint[1]) );
} else if ( type==PathIterator.SEG_LINETO ) {
points.add( new VPoint((int)currpoint[0], (int)currpoint[1]) );
} else if ( type==PathIterator.SEG_CLOSE ) {
break;
} else {
throw new RuntimeException("Unexpected type " + type + " returned");
}
// Go to the next element
pathiter.next();
}
// Return points
return points;
}
/* ********************************************************* */
// Classes used by the above
private static class PathIteratorWrapper implements Shape {
private PathIterator iter;
public PathIteratorWrapper(PathIterator _iter) {
this.iter = _iter;
}
public PathIterator getPathIterator(AffineTransform at) { return iter; }
public PathIterator getPathIterator(AffineTransform at, double flatness) { return iter; }
public boolean contains(double x, double y) { throw new RuntimeException("Unimplemented method"); }
public boolean contains(double x, double y, double w, double h) { throw new RuntimeException("Unimplemented method"); }
public boolean contains(Point2D p) { throw new RuntimeException("Unimplemented method"); }
public boolean contains(Rectangle2D r) { throw new RuntimeException("Unimplemented method"); }
public Rectangle getBounds() { throw new RuntimeException("Unimplemented method"); }
public Rectangle2D getBounds2D() { throw new RuntimeException("Unimplemented method"); }
public boolean intersects(double x, double y, double w, double h) { throw new RuntimeException("Unimplemented method"); }
public boolean intersects(Rectangle2D r) { throw new RuntimeException("Unimplemented method"); }
}
private static class ListPathIterator implements PathIterator {
private int index = 0;
private java.util.List points;
private int offset_x;
private int offset_y;
public ListPathIterator(java.util.List points) {
this(points, 0, 0);
}
public ListPathIterator(java.util.List _points, int _offset_x, int _offset_y) {
this.points = _points;
this.offset_x = _offset_x;
this.offset_y = _offset_y;
}
public void resetIterator() {
index = 0;
}
public boolean isDone() { return ( index>=points.size() ); }
public void next() { index++; }
public int currentSegment(double[] coords) {
VPoint point = points.get(index);
coords[0] = point.x - offset_x;
coords[1] = point.y - offset_y;
return getReturnValue();
}
public int currentSegment(float[] coords) {
VPoint point = points.get(index);
coords[0] = point.x - offset_x;
coords[1] = point.y - offset_y;
return getReturnValue();
}
private int getReturnValue() {
if ( index==0 ) {
return SEG_MOVETO;
} else if ( index<(points.size()-1) ) {
return SEG_LINETO;
} else {
return SEG_CLOSE;
}
}
/**
* WIND_NON_ZERO appears to be the only value ever returned
* - but their may be instances where this should not be the case
*/
public int getWindingRule() {
return WIND_NON_ZERO;
}
}
/* ********************************************************* */
}