How to run processing script on multiple frames in a folder - processing

Using processing I am trying to run a script that will process a folder full of frames.
The script is a combination of PixelSortFrames and SortThroughSeamCarving.
I am new to processing and what I want does not seems to be working. I would like the script to run back through and choose the following file in the folder to be processed. At the moment it stops at the end and does not return to start on next file (there are three other modules also involved).
Any help would be much appreciated. :(
/* ASDFPixelSort for video frames v1.0
Original ASDFPixelSort by Kim Asendorf <http://kimasendorf.com>
https://github.com/kimasendorf/ASDFPixelSort
Fork by dx <http://dequis.org> and chinatsu <http://360nosco.pe>
// Main configuration
String basedir = ".../Images/Seq_002"; // Specify the directory in which the frames are located. Use forward slashes.
String fileext = ".jpg"; // Change to the format your images are in.
int resumeprocess = 0; // If you wish to resume a previously stopped process, change this value.
boolean reverseIt = true;
boolean saveIt = true;
int mode = 2; // MODE: 0 = black, 1 = bright, 2 = white
int blackValue = -10000000;
int brightnessValue = -1;
int whiteValue = -6000000;
// -------
PImage img, original;
float[][] sums;
int bottomIndex = 0;
String[] filenames;
int row = 0;
int column = 0;
int i = 0;
java.io.File folder = new java.io.File(dataPath(basedir));
java.io.FilenameFilter extfilter = new java.io.FilenameFilter() {
boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(fileext);
}
};
void setup() {
if (resumeprocess > 0) {i = resumeprocess - 1;frameCount = i;}
size(1504, 1000); // Resolution of the frames. It's likely there's a better way of doing this..
filenames = folder.list(extfilter);
size(1504, 1000);
println(" " + width + " x " + height + " px");
println("Creating buffer images...");
PImage hImg = createImage(1504, 1000, RGB);
PImage vImg = createImage(1504, 1000, RGB);
// draw image and convert to grayscale
if (i +1 > filenames.length) {println("Uh.. Done!"); System.exit(0);}
img = loadImage(basedir+"/"+filenames[i]);
original = loadImage(basedir+"/"+filenames[i]);
image(img, 0, 0);
filter(GRAY);
img.loadPixels(); // updatePixels is in the 'runKernals'
// run kernels to create "energy map"
println("Running kernals on image...");
runKernels(hImg, vImg);
image(img, 0, 0);
// sum pathways through the image
println("Getting sums through image...");
sums = getSumsThroughImage();
image(img, 0, 0);
loadPixels();
// get start point (smallest value) - this is used to find the
// best seam (starting at the lowest energy)
bottomIndex = width/2;
// bottomIndex = findStartPoint(sums, 50);
println("Bottom index: " + bottomIndex);
// find the pathway with the lowest information
int[] path = new int[height];
path = findPath(bottomIndex, sums, path);
for (int bi=0; bi<width; bi++) {
// get the pixels of the path from the original image
original.loadPixels();
color[] c = new color[path.length]; // create array of the seam's color values
for (int i=0; i<c.length; i++) {
try {
c[i] = original.pixels[i*width + path[i] + bi]; // set color array to values from original image
}
catch (Exception e) {
// when we run out of pixels, just ignore
}
}
println(" " + bi);
c = sort(c); // sort (use better algorithm later)
if (reverseIt) {
c = reverse(c);
}
for (int i=0; i<c.length; i++) {
try {
original.pixels[i*width + path[i] + bi] = c[i]; // reverse! set the pixels of the original from sorted array
}
catch (Exception e) {
// when we run out of pixels, just ignore
}
}
original.updatePixels();
}
// when done, update pixels to display
updatePixels();
// display the result!
image(original, 0, 0);
if (saveIt) {
println("Saving file...");
//filenames = stripFileExtension(filenames);
save("results/SeamSort_" + filenames + ".tiff");
}
println("DONE!");
}
// strip file extension for saving and renaming
String stripFileExtension(String s) {
s = s.substring(s.lastIndexOf('/')+1, s.length());
s = s.substring(s.lastIndexOf('\\')+1, s.length());
s = s.substring(0, s.lastIndexOf('.'));
return s;
}
This code works by processing all images in the selected folder
String basedir = "D:/things/pixelsortframes"; // Specify the directory in which the frames are located. Use forward slashes.
String fileext = ".png"; // Change to the format your images are in.
int resumeprocess = 0; // If you wish to resume a previously stopped process, change this value.
int mode = 1; // MODE: 0 = black, 1 = bright, 2 = white
int blackValue = -10000000;
int brightnessValue = -1;
int whiteValue = -6000000;
PImage img;
String[] filenames;
int row = 0;
int column = 0;
int i = 0;
java.io.File folder = new java.io.File(dataPath(basedir));
java.io.FilenameFilter extfilter = new java.io.FilenameFilter() {
boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(fileext);
}
};
void setup() {
if (resumeprocess > 0) {i = resumeprocess - 1;frameCount = i;}
size(1920, 1080); // Resolution of the frames. It's likely there's a better way of doing this..
filenames = folder.list(extfilter);
}
void draw() {
if (i +1 > filenames.length) {println("Uh.. Done!"); System.exit(0);}
row = 0;
column = 0;
img = loadImage(basedir+"/"+filenames[i]);
image(img,0,0);
while(column < width-1) {
img.loadPixels();
sortColumn();
column++;
img.updatePixels();
}
while(row < height-1) {
img.loadPixels();
sortRow();
row++;
img.updatePixels();
}
image(img,0,0);
saveFrame(basedir+"/out/"+filenames[i]);
println("Frames processed: "+frameCount+"/"+filenames.length);
i++;
}
essentially I want to do the same thing only with a different image process but my code is not doing this to all with in the folder... just one file.

You seem to be confused about what the setup() function does. It runs once, and only once, at the beginning of your code's execution. You don't have any looping structure for processing the other files, so it's no wonder that it only processes the first one. Perhaps wrap the entire thing in a for loop? It looks like you kind of thought about this, judging by the global variable i, but you never increment it to go to the next image and you overwrite its value in several for loops later anyway.

Related

saveframe() screenshots into file but cannot read image files in processing

I want to use processing to take screenshots and use the screenshot to generate animations. The way I try to do it is save the frames into a directory and reload the image files into an image String. Not sure if it is the right way to do it, but I get error when I load the images:
error message
String[] listFileNames(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
String names[] = file.list();
return names;
} else {
// If it's not a directory
return null;
}
}
void draw(){
if (frameCount % 60 == 0) screen();
if (screen != null) image(screen, 0, 0, width, height);
if(recording){
saveFrame("output//screenshot_####.png");
}
names = listFileNames("/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/");
print(names);
if(names.length != 0){
for(int i = 0; i < names.length; i = i+1){
img[i] = loadImage(names[i]);
for (int j = 0; j < particles.length; j++) {
particles[j].display();
particles[j].move();
}
}
}
}
I wonder if anyone knows the problem and if there is another way to do the screenshot animations.Thank you!
I think the issue is that listFileNames() simply list the file names, not the absolute paths to the files. Unless those images are in the sketch directory (or within the data directory of the sketch) you will need to use the full path to the images to load them.
One option would be to concatenate the absolute directory path with the filename:
String[] listFileNames(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
String names[] = file.list();
return names;
} else {
// If it's not a directory
return null;
}
}
void draw(){
if (frameCount % 60 == 0) screen();
if (screen != null) image(screen, 0, 0, width, height);
if(recording){
saveFrame("output//screenshot_####.png");
}
String dir = "/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/"
names = listFileNames(dir);
print(names);
if(names.length != 0){
for(int i = 0; i < names.length; i = i+1){
// concatenate the directory with each fine name
// dir inclues / at the end (otherwise you can use File.separator (e.g. dir/image.png = dir + File.separator + "image.png"))
img[i] = loadImage(dir + names[i]);
for (int j = 0; j < particles.length; j++) {
particles[j].display();
particles[j].move();
}
}
}
}
You could use listFiles() instead which returns File objects that do provide the absolute path.
Here's a snippet illustrating usage of listFiles():
File[] listFiles(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
File[] files = file.listFiles();
return files;
} else {
// If it's not a directory
return null;
}
}
File[] files;
void draw(){
if (frameCount % 60 == 0) screen();
if (screen != null) image(screen, 0, 0, width, height);
if(recording){
saveFrame("output//screenshot_####.png");
}
String dir = "/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/"
files = listFiles(dir);
if(files == null) {
println(dir + "is not a directory");
return;
}
for(int i = 0; i < files.length; i = i+1){
// concatenate the directory with each fine name
// dir inclues / at the end (otherwise you can use File.separator (e.g. dir/image.png = dir + File.separator + "image.png"))
img[i] = loadImage(files[i].getAbsolutePath());
for (int j = 0; j < particles.length; j++) {
particles[j].display();
particles[j].move();
}
}
}
You can find this function and a few more under Daniel Shiffman's DirectoryList example via Processing > Examples > Topics > File IO
Additionally, listFiles() / listPaths() are present in Processing already, just a bit tucked away. The nice thing about these variants is that you can filter results to be files with png extension only (e.g. excluding folder, hidden files, etc.).
Here's a modified version of the above snippet:
void draw(){
if (frameCount % 60 == 0) screen();
if (screen != null) image(screen, 0, 0, width, height);
if(recording){
saveFrame("output//screenshot_####.png");
}
String dir = "/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/"
File[] files = listFiles(dir,"files","extension=png")
if(files == null) {
println(dir + "is not a directory");
return;
}
for(int i = 0; i < files.length; i = i+1){
// concatenate the directory with each fine name
// dir inclues / at the end (otherwise you can use File.separator (e.g. dir/image.png = dir + File.separator + "image.png"))
img[i] = loadImage(files[i].getAbsolutePath());
for (int j = 0; j < particles.length; j++) {
particles[j].display();
particles[j].move();
}
}
}
A bit off-topic from your question I am noticing a few confusing things:
you're listing files and loading images multiple times per frame in draw(). You might want to load the images once in setup(), store them in a PImage[], then simply use them in draw()
It appears you're updating particles only while loading images and there doesn't seem to be a connection between the loaded images and the particles from the code you're shared. You might want to separate the particle code from the image loading code
Shameless plug: If you want to easily load and display png sequences in Processing you can use the ImageSequencePlayer Processing library I worked on:
// import library
import com.hirschandmann.image.*;
// create a reference to an image sequence
ISPlayer player;
void setup() {
size(640, 360);
// create instance and load all valid images from the data folder
player = new ISPlayer(this,"/Users/lj/Desktop/JingLang_vis145A_Midterm/screenshot/output/"));
}
void draw() {
// clear background with yellow
background(255, 204, 0);
// render image sequence, ISPlayer extends PImage, use it as such
image(player,mouseX,mouseY);
}
// image sequence events
// all frames loaded
void onSequenceLoaded(ISPlayer player){
println(player+" sequence fully loaded");
player.loop();
}

How to correct loop counters for maze algorithm?

I have figured out how to move my character around the maze using the algorithm I have written, but the count is not figuring correctly. At the end of each row my character moves up and down several times until the count reaches the specified number to exit the loop, then the character moves along the next row down until it reaches the other side and repeats the moving up and down until the count reaches the specified number again. Can anyone help me find why my count keeps getting off? The algorithm and the maze class I am calling from is listed below.
public class P4 {
public static void main(String[] args) {
// Create maze
String fileName = args[3];
Maze maze = new Maze(fileName);
System.out.println("Maze name: " + fileName);
// Get dimensions
int mazeWidth = maze.getWidth();
int mazeHeight = maze.getHeight();
// Print maze size
System.out.println("Maze width: " + mazeWidth);
System.out.println("Maze height: " + mazeHeight);
int r = 0;
int c = 0;
// Move commands
while (true){
for (c = 0; c <= mazeWidth; c++){
if (maze.moveRight()){
maze.isDone();
c++;
}
if (maze.isDone() == true){
System.exit(1);
}
if (maze.moveRight() == false && c != mazeWidth){
maze.moveDown();
maze.moveRight();
maze.moveRight();
maze.moveUp();
c++;
}
}
for (r = 0; r % 2 == 0; r++){
maze.moveDown();
maze.isDone();
if (maze.isDone() == true){
System.exit(1);
}
}
for (c = mazeWidth; c >= 0; c--){
if (maze.moveLeft()){
c--;
maze.isDone();
System.out.println(c);
}
if (maze.isDone() == true){
System.exit(1);
}
if (maze.moveLeft() == false && c != 0){
maze.moveDown();
maze.moveLeft();
maze.moveLeft();
maze.moveUp();
c--;
}
}
for (r = 1; r % 2 != 0; r++){
maze.moveDown();
maze.isDone();
if (maze.isDone() == true){
System.exit(1);
}
}
}
}
}
public class Maze {
// Maze variables
private char mazeData[][];
private int mazeHeight, mazeWidth;
private int finalRow, finalCol;
int currRow;
private int currCol;
private int prevRow = -1;
private int prevCol = -1;
// User interface
private JFrame frame;
private JPanel panel;
private Image java, student, success, donotpass;
private ArrayList<JButton> buttons;
// Maze constructor
public Maze(String fileName) {
// Read maze
readMaze(fileName);
// Graphics setup
setupGraphics();
}
// Get height
public int getHeight() {
return mazeHeight;
}
// Get width
public int getWidth() {
return mazeWidth;
}
// Move right
public boolean moveRight() {
// Legal move?
if (currCol + 1 < mazeWidth) {
// Do not pass?
if (mazeData[currRow][currCol + 1] != 'D')
{
currCol++;
redraw(true);
return true;
}
}
return false;
}
// Move left
public boolean moveLeft() {
// Legal move?
if (currCol - 1 >= 0) {
// Do not pass?
if (mazeData[currRow][currCol - 1] != 'D')
{
currCol--;
redraw(true);
return true;
}
}
return false;
}
// Move up
public boolean moveUp() {
// Legal move?
if (currRow - 1 >= 0) {
// Do not pass?
if (mazeData[currRow - 1][currCol] != 'D')
{
currRow--;
redraw(true);
return true;
}
}
return false;
}
// Move down
public boolean moveDown() {
// Legal move?
if (currRow + 1 < mazeHeight) {
// Do not pass?
if (mazeData[currRow + 1][currCol] != 'D')
{
currRow++;
redraw(true);
return true;
}
}
return false;
}
public boolean isDone() {
// Maze solved?
if ((currRow == finalRow) && (currCol == finalCol))
return true;
else
return false;
}
private void redraw(boolean print) {
// Wait for awhile
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
if (print)
System.out.println("Moved to row " + currRow + ", column " + currCol);
// Compute index and remove icon
int index = (prevRow * mazeWidth) + prevCol;
if ((prevRow >= 0) && (prevCol >= 0)) {
buttons.get(index).setIcon(null);
}
// Compute index and add icon
index = (currRow * mazeWidth) + currCol;
if ((currRow == finalRow) && (currCol == finalCol))
buttons.get(index).setIcon(new ImageIcon(success));
else
buttons.get(index).setIcon(new ImageIcon(student));
// Store previous location
prevRow = currRow;
prevCol = currCol;
}
// Set button
private void setButton(JButton button, int row, int col) {
if (mazeData[row][col] == 'S') {
button.setIcon(new ImageIcon(student));
currRow = row;
currCol = col;
} else if (mazeData[row][col] == 'J') {
button.setIcon(new ImageIcon(java));
finalRow = row;
finalCol = col;
} else if (mazeData[row][col] == 'D') {
button.setIcon(new ImageIcon(donotpass));
}
}
// Read maze
private void readMaze(String filename) {
try {
// Open file
Scanner scan = new Scanner(new File(filename));
// Read numbers
mazeHeight = scan.nextInt();
mazeWidth = scan.nextInt();
// Allocate maze
mazeData = new char[mazeHeight][mazeWidth];
// Read maze
for (int row = 0; row < mazeHeight; row++) {
// Read line
String line = scan.next();
for (int col = 0; col < mazeWidth; col++) {
mazeData[row][col] = line.charAt(col);
}
}
// Close file
scan.close();
} catch (IOException e) {
System.out.println("Cannot read maze: " + filename);
System.exit(0);
}
}
// Setup graphics
private void setupGraphics() {
// Create grid
frame = new JFrame();
panel = new JPanel();
panel.setLayout(new GridLayout(mazeHeight, mazeWidth, 0, 0));
frame.add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH);
frame.add(panel, BorderLayout.CENTER);
// Look and feel
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
// Configure window
frame.setSize(mazeWidth * 100, mazeHeight * 100);
frame.setTitle("Maze");
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setAlwaysOnTop(true);
// Load and scale images
ImageIcon icon0 = new ImageIcon("Java.jpg");
Image image0 = icon0.getImage();
java = image0.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon1 = new ImageIcon("Student.jpg");
Image image1 = icon1.getImage();
student = image1.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon2 = new ImageIcon("Success.jpg");
Image image2 = icon2.getImage();
success = image2.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon3 = new ImageIcon("DoNotPass.jpg");
Image image3 = icon3.getImage();
donotpass = image3.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
// Build panel of buttons
buttons = new ArrayList<JButton>();
for (int row = 0; row < mazeHeight; row++) {
for (int col = 0; col < mazeWidth; col++) {
// Initialize and add button
JButton button = new JButton();
Border border = new LineBorder(Color.darkGray, 4);
button.setOpaque(true);
button.setBackground(Color.gray);
button.setBorder(border);
setButton(button, row, col);
panel.add(button);
buttons.add(button);
}
}
// Show window
redraw(false);
frame.setVisible(true);
}
}
One error I can see in your code is that you're incrementing your c counter more often than you should. You start with it managed by your for loop, which means that it will be incremented (or decremented, for the leftward moving version) at the end of each pass through the loop. However, you also increment it an additional time in two of your if statements. That means that c might increase by two or three on a single pass through the loop, which is probably not what you intend.
Furthermore, the count doesn't necessarily have anything obvious to do with the number of moves you make. The loop code will always increase it by one, even if you're repeatedly trying to move through an impassible wall.
I don't really understand what your algorithm is supposed to be, so I don't have any detailed advice for how to fix your code.
One suggestion I have though is that you probably don't ever want to be calling methods on your Maze class without paying attention to their return values. You have a bunch of places where you call isDone but ignore the return value, which doesn't make any sense. Similarly, you should always be checking the return values from your moveX calls, to see if the move was successful or not. Otherwise you may just blunder around a bunch, without your code having any clue where you are in the maze.

JFreechart multiline Label on Symbolaxis or other way to align charts

i have a lot of charts, each in a different JInternalFrame:
But the horizontal axis should be aligned to the same point(maybe the red line). The problem is, that the space for the label is set automatically by jFreechart.
So i tried to find a solution for multiline ticklabels. I found this:
int optionsCount = state.getStatusOptions().toArray().length;
String[] grade = new String[optionsCount + 1];
grade[0] = "";
for (int x = 1; x < optionsCount + 1; x++) {
//grade[x] ="blaa"+x;//state.getStatusOptions().get(x - 1);
//grade[x]="1.line\n2.line\n3.line";
grade[x] = newLineString(state.getStatusOptions().get(x - 1), 5);
}
// grade[1]="1.line\n2.line";
SymbolAxis rangeAxis;
rangeAxis = new SymbolAxis("", grade){
#Override
protected Rectangle2D getLabelEnclosure(Graphics2D g2, RectangleEdge edge) {
Rectangle2D l = super.getLabelEnclosure(g2, edge);
l.setRect(l.getX(),l.getY(),l.getWidth()*0.5,l.getHeight());
return l;
}
#Override
protected AxisState drawTickMarksAndLabels(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge) {
AxisState state = new AxisState(cursor);
if (isAxisLineVisible()) {
drawAxisLine(g2, cursor, dataArea, edge);
}
double ol = getTickMarkOutsideLength();
double il = getTickMarkInsideLength();
List ticks = refreshTicks(g2, state, dataArea, edge);
state.setTicks(ticks);
g2.setFont(getTickLabelFont());
Iterator iterator = ticks.iterator();
// remember the max number of lines used in any label
int maxLinesUsed = 0;
while (iterator.hasNext()) {
ValueTick tick = (ValueTick) iterator.next();
if (isTickLabelsVisible()) {
g2.setPaint(getTickLabelPaint());
float[] anchorPoint = calculateAnchorPoint(tick, cursor, dataArea, edge);
g2.draw(plotArea);
g2.setPaint(Color.green);
g2.draw(dataArea);
g2.setPaint(getTickLabelPaint());
// split by "\n" and draw text in a new line for each result
String tickText = tick.getText();
int line = 1;
for (String tickTextLine : tickText.split("\n")) {
float x = anchorPoint[0];
// one row down...
float y = anchorPoint[1] + line * g2.getFont().getSize();
TextUtilities.drawRotatedString(tickTextLine, g2, x, y, tick.getTextAnchor(), tick.getAngle(), tick
.getRotationAnchor());
line++;
}
// if we used more lines than any time before remember it
if (line > maxLinesUsed) {
maxLinesUsed = line;
}
}
if (isTickMarksVisible() && tick.getTickType().equals(TickType.MAJOR)) {
float xx = (float) valueToJava2D(tick.getValue(), dataArea, edge);
Line2D mark = null;
g2.setStroke(getTickMarkStroke());
g2.setPaint(getTickMarkPaint());
if (edge == RectangleEdge.LEFT) {
mark = new Line2D.Double(cursor - ol, xx, cursor + il, xx);
} else if (edge == RectangleEdge.RIGHT) {
mark = new Line2D.Double(cursor + ol, xx, cursor - il, xx);
} else if (edge == RectangleEdge.TOP) {
mark = new Line2D.Double(xx, cursor - ol, xx, cursor + il);
} else if (edge == RectangleEdge.BOTTOM) {
mark = new Line2D.Double(xx, cursor + ol, xx, cursor - il);
}
g2.draw(mark);
}
}
// need to work out the space used by the tick labels...
// so we can update the cursor...
// patched using maxLinesUsed => we need more space because of multiple lines
double used = 0.0;
if (isTickLabelsVisible()) {
if (edge == RectangleEdge.LEFT) {
used += findMaximumTickLabelWidth(ticks, g2, plotArea, isVerticalTickLabels()) * maxLinesUsed;
state.cursorLeft(used);
} else if (edge == RectangleEdge.RIGHT) {
used = findMaximumTickLabelWidth(ticks, g2, plotArea, isVerticalTickLabels()) * maxLinesUsed;
state.cursorRight(used);
} else if (edge == RectangleEdge.TOP) {
used = findMaximumTickLabelHeight(ticks, g2, plotArea, isVerticalTickLabels()) * maxLinesUsed;
state.cursorUp(used);
} else if (edge == RectangleEdge.BOTTOM) {
used = findMaximumTickLabelHeight(ticks, g2, plotArea, isVerticalTickLabels()) * maxLinesUsed;
state.cursorDown(used);
}
}
return state;
}
};
As you can see in the picture above, the new line function works, but the spacing for the labels does not work. I tried to override the getLabelEnclosure method, but its given string is just "".
Does anyone know a solution for my problem. Either the multiline or an other way to align the charts?
thanks!
Override the reserveSpace function and in the place where the height is calculated modify it with the number of rows you need:
Rectangle2D labelEnclosure = getLabelEnclosure(g2, edge);
if (RectangleEdge.isTopOrBottom(edge)) {
double labelHeight = labelEnclosure.getHeight();
space.add(labelHeight + **YOUR_NUMBER_OF_ROWS** * tickLabelHeight, edge);
} else if (RectangleEdge.isLeftOrRight(edge)) {
double labelWidth = labelEnclosure.getWidth();
space.add(labelWidth + tickLabelWidth, edge);
}
return space;

Slicing up a Rectangle

I need to get AS3 Rectangle objects from a function receiving other Rectangles as parameters. The result is very similar to the slice tool in Photoshop. It is quite hard to explain, so here is a picture:
(source: free.fr)
The blue squares are the rectangles that are given as parameters and the green ones are the result. Given Rectangles can overlap, as seen on picture 2 or be out of frame.
I don't look for a graphical realisation but for a way to get Rectangle objects as result.
Do you know any lib to do that?
Looked like a fun problem, so I gave it a crack. My idea was to just brute force it by:
Determine which points where the corners of the generated rectangles could be.
Remove all duplicates from this list of points.
Check all rectangles that could theoretically be drawn where the rect would have all 4 corners in the list of point.
Filter out all invalid rectangles (it intersects with one of our original rectangles etc.)
Reduce all valid rectangles to the minimum amount needed (if a valid rectangle contains another valid rectangle the "child" is removed.
It seems to work (although I haven't tested extensively).
Here's a demo. Sorry about the color palette. I was winging it.
Here's the source code (could probably be optimized quite a bit):
package
{
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.getTimer;
public class Main extends Sprite {
private var m_colors : Array = [0xffaaaa, 0x77ff77, 0xaaaaff, 0xffff44, 0xff44ff, 0xaaffff, 0x444444, 0xffaa55, 0xaaff55, 0x55aaff, 0x55ffaa];
private var m_roomRect : Rectangle;
private var m_sourceRects : Vector.<Rectangle> = new Vector.<Rectangle>();
private var m_currentDragRect : Rectangle;
private var m_dragMousePoint : Point = new Point();
private var m_outputTextField : TextField;
public function Main() : void {
m_roomRect = new Rectangle(40, 40, 400, 400);
m_sourceRects.push(new Rectangle(60, 60, 60, 80));
m_sourceRects.push(new Rectangle(130, 220, 70, 80));
m_sourceRects.push(new Rectangle(160, 260, 100, 80));
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
this.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseEvent);
this.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
var tf : TextField = new TextField();
tf.defaultTextFormat = new TextFormat("_sans", 12);
tf.text = "Click and drag blue rectangles to move them";
tf.autoSize = TextFieldAutoSize.LEFT;
tf.x = (m_roomRect.left + m_roomRect.right) / 2 - tf.width / 2;
tf.y = m_roomRect.top - tf.height;
this.stage.addChild(tf);
m_outputTextField = new TextField();
m_outputTextField.defaultTextFormat = tf.defaultTextFormat;
m_outputTextField.width = m_roomRect.width;
m_outputTextField.x = m_roomRect.x;
m_outputTextField.y = m_roomRect.bottom + 5;
this.stage.addChild(m_outputTextField);
redraw();
}
private function onMouseEvent(event : MouseEvent):void {
switch(event.type) {
case MouseEvent.MOUSE_DOWN:
checkMouseDownOnRect();
break;
case MouseEvent.MOUSE_MOVE:
checkMouseDrag();
break;
case MouseEvent.MOUSE_UP:
m_currentDragRect = null;
break;
}
}
private function checkMouseDownOnRect():void {
m_currentDragRect = null;
m_dragMousePoint = new Point(this.stage.mouseX, this.stage.mouseY);
for each(var sourceRect : Rectangle in m_sourceRects) {
if (sourceRect.containsPoint(m_dragMousePoint)) {
m_currentDragRect = sourceRect;
break;
}
}
}
private function checkMouseDrag():void {
if (m_currentDragRect != null) {
m_currentDragRect.x += this.stage.mouseX - m_dragMousePoint.x;
m_currentDragRect.y += this.stage.mouseY - m_dragMousePoint.y;
m_dragMousePoint.x = this.stage.mouseX;
m_dragMousePoint.y = this.stage.mouseY;
redraw();
}
}
private function redraw():void {
// calculate data
var time : int = getTimer();
var data : CalculationData = calculate();
var calcTime : int = getTimer() - time;
// draw room bounds
this.graphics.clear();
this.graphics.lineStyle(3, 0x0);
this.graphics.drawRect(m_roomRect.x, m_roomRect.y, m_roomRect.width, m_roomRect.height);
// draw generated rectangles
for (var i : int = 0; i < data.outputRects.length; i++) {
var color : int = m_colors[i % m_colors.length];
var rect : Rectangle = data.outputRects[i];
this.graphics.lineStyle(2, color, 0.5);
this.graphics.beginFill(color, 0.5);
this.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
this.graphics.endFill();
}
// draw horisontal lines (a line that crosses each red point) for debug purposes
for each (var lineY : int in data.lines) {
this.graphics.lineStyle(1, 0, 0.2);
this.graphics.moveTo(m_roomRect.x, lineY);
this.graphics.lineTo(m_roomRect.x + m_roomRect.width, lineY);
this.graphics.endFill();
}
// the original rectangles
for each (var sourceRect : Rectangle in m_sourceRects) {
this.graphics.lineStyle(2, 0x0);
this.graphics.beginFill(0x0000aa, 0.5);
this.graphics.drawRect(sourceRect.x, sourceRect.y, sourceRect.width, sourceRect.height);
this.graphics.endFill();
}
// draw all points that was used to generate the output rectangles for debug purposes
for each (var p : Point in data.points) {
this.graphics.lineStyle(0, 0, 0);
this.graphics.beginFill(0xff0000, 1);
this.graphics.drawCircle(p.x, p.y, 3);
this.graphics.endFill();
}
m_outputTextField.text = "Rect count: " + data.outputRects.length + " (calculation time: " + calcTime + "ms)";
}
private function calculate(): CalculationData {
// list of y coords for horisontal lines,
// which are interesting when determining which rectangles to generate
var lines : Vector.<int> = new Vector.<int>();
// list of all points which are interesting
// when determining where the corners of the generated rect could be
var points : Vector.<Point> = new Vector.<Point>();
// add the 4 corners of the room to interesting points
points.push(new Point(m_roomRect.left, m_roomRect.top));
points.push(new Point(m_roomRect.right, m_roomRect.top));
points.push(new Point(m_roomRect.left, m_roomRect.bottom));
points.push(new Point(m_roomRect.right, m_roomRect.bottom));
for (var i:int = 0; i < m_sourceRects.length; i++) {
var sourceRect : Rectangle = m_sourceRects[i];
// source rect is completely outside of the room, we shoud ignore it
if (!m_roomRect.containsRect(sourceRect) && !m_roomRect.intersects(sourceRect)) {
continue;
}
// push the y coord of the rect's top edge to the list of lines if it's not already been added
if (lines.indexOf(sourceRect.y) == -1) {
lines.push(sourceRect.y);
}
// push the y coord of the rect's bottom edge to the list of lines if it's not already been added
if (lines.indexOf(sourceRect.bottom) == -1) {
lines.push(sourceRect.bottom);
}
// add the 4 corners of the source rect to the list of interesting points
addCornerPoints(points, sourceRect);
// find the intersections between source rectangles and add those points
for (var j:int = 0; j < m_sourceRects.length; j++) {
if (j != i) {
var intersect : Rectangle = m_sourceRects[i].intersection(m_sourceRects[j]);
if (intersect.width != 0 && intersect.height != 0) {
addCornerPoints(points, intersect);
}
}
}
}
for (i = 0; i < lines.length; i++) {
// add the points where the horisontal lines intersect with the room's left and right edges
points.push(new Point(m_roomRect.x, lines[i]));
points.push(new Point(m_roomRect.right, lines[i]));
var lineRect : Rectangle = new Rectangle(m_roomRect.x, m_roomRect.y,
m_roomRect.width, lines[i] - m_roomRect.y);
// add all points where the horisontal lines intersect with the source rectangles
for (a = 0; a < m_sourceRects.length;a++) {
intersect = m_sourceRects[a].intersection(lineRect);
if (intersect.width != 0 && intersect.height != 0) {
addCornerPoints(points, intersect);
}
}
}
// clamp all points that are outside of the room to the room edges
for (i = 0; i < points.length; i++) {
points[i].x = Math.min(Math.max(m_roomRect.left, points[i].x), m_roomRect.right);
points[i].y = Math.min(Math.max(m_roomRect.top, points[i].y), m_roomRect.bottom);
}
removeDuplicatePoints(points);
var outputRects : Vector.<Rectangle> = new Vector.<Rectangle>();
var pointsHash : Object = { };
for (a = 0; a < points.length; a++) {
pointsHash[points[a].x + "_" + points[a].y] = true;
}
for (var a:int = 0; a < points.length; a++) {
for (var b:int = 0; b < points.length; b++) {
if (b != a && points[b].x > points[a].x && points[b].y == points[a].y) {
for (var c:int = 0; c < points.length; c++) {
// generate a rectangle that has its four corners in our points of interest
if (c != b && c != a && points[c].y > points[b].y && points[c].x == points[b].x) {
var r : Rectangle = new Rectangle(points[a].x, points[a].y, points[b].x - points[a].x, points[c].y - points[b].y);
// make sure the rect has the bottom left corner in one of our points
if (pointsHash[r.left+"_"+r.bottom]) {
var containsOrIntersectsWithSource : Boolean = false;
for (i = 0; i < m_sourceRects.length;i++) {
if (r.containsRect(m_sourceRects[i]) || r.intersects(m_sourceRects[i])) {
containsOrIntersectsWithSource = true;
break;
}
}
// we don't add any rectangles that either intersects with a source rect
// or completely contains a source rect
if (!containsOrIntersectsWithSource) {
outputRects.push(r);
}
}
}
}
}
}
}
trace("outputRects before cleanup:", outputRects.length);
combineOutputRects(outputRects)
trace("outputRects after cleanup", outputRects.length);
var data : CalculationData = new CalculationData();
data.outputRects = outputRects;
data.lines = lines;
data.points = points;
return data;
}
private function addCornerPoints(points : Vector.<Point>, rect : Rectangle) : void {
points.push(new Point(rect.left, rect.top));
points.push(new Point(rect.right, rect.top));
points.push(new Point(rect.left, rect.bottom));
points.push(new Point(rect.right, rect.bottom));
}
// removes all rectangle that are already contained in another rectangle
private function combineOutputRects(outputRects : Vector.<Rectangle>):Boolean {
for (var a : int = 0; a < outputRects.length; a++) {
for (var b : int = 0; b < outputRects.length; b++) {
if (b != a) {
if (outputRects[a].containsRect(outputRects[b])) {
trace("\tremoved rect " + outputRects[b] + ", it was contained in " + outputRects[a]);
outputRects.splice(b, 1);
b--;
a = 0;
}
}
}
}
return false;
}
private function removeDuplicatePoints(points : Vector.<Point>) : void {
var usedPoints : Object = {};
for (var i : int = 0; i < points.length; i++) {
if (usedPoints[points[i].toString()]) {
points.splice(i, 1);
i--;
} else {
usedPoints[points[i].toString()] = true;
}
}
}
}
}
import flash.geom.Point;
import flash.geom.Rectangle;
class CalculationData {
public var outputRects : Vector.<Rectangle> = new Vector.<Rectangle>;
public var lines : Vector.<int> = new Vector.<int>;
public var points : Vector.<Point> = new Vector.<Point>;
}

Wrap AutoCompleteField List Item

My apps has AutoCompleteField that hold long text more than 100 Characters, if I use regular AutoCompleteField I cant read the rest of data.
How can I make the text wrap into 2 or more lines in the autocompletefield options ?
I try using '\r'+'\n' and '\n', its not giving new line. setting it size and also set row height doesnt give me the result I wanted
AutoCompleteField autoCustomer = new AutoCompleteField(custList, style);
autoCustomer.getListField().setSize(20);
autoCustomer.getListField().setRowHeight(100);
If I was you I would override drawListRow and draw the text using drawText which will give me total control on how the row should look. Try adapting your code to behave like this
AutoCompleteField autoCompleteField = new AutoCompleteField(
filterList, AutoCompleteField.LIST_STATIC) {
public void drawListRow(ListField listField, Graphics g,
int index, int y, int width) {
BasicFilteredListResult result = (BasicFilteredListResult) (autoCompleteField
.get(listField, index));
if (result != null)
{
//Draw text here
}
}
public void onSelect(Object selection, int type) {
super.onSelect(selection, type);
if (selection != null) {
BasicFilteredListResult result = (BasicFilteredListResult) this
.getSelectedObject();
handleResult((String) result._object);
} else {
Dialog.alert(Resource
.getString(PLEASE_PICK_A_VALID_NAME));
return;
}
}
};
IF you want to wrap your text you can use the following method
// Handy method to wrap text drawn with the specified font into rows with
// the max width
// Found here:
// http://supportforums.blackberry.com/t5/Java-Development/Can-drawText-wrap-text-into-multiple-lines/m-p/499901
public static String[] wrapText(String text, Font f, int maxWidth) {
Vector result = new Vector();
if (text == null)
return new String[] {};
boolean hasMore = true;
// The current index of the cursor
int current = 0;
// The next line break index
int lineBreak = -1;
// The space after line break
int nextSpace = -1;
while (hasMore) {
// Find the line break
while (true) {
lineBreak = nextSpace;
if (lineBreak == text.length() - 1) {
// We have reached the last line
hasMore = false;
break;
}
nextSpace = text.indexOf(' ', lineBreak + 1);
if (nextSpace == -1)
nextSpace = text.length() - 1;
int linewidth = f
.getAdvance(text, current, nextSpace - current);
// If too long, break out of the find loop
if (linewidth > maxWidth)
break;
}
String line = text.substring(current, lineBreak + 1);
result.addElement(line);
current = lineBreak + 1;
}
String[] resultArray = new String[result.size()];
result.copyInto(resultArray);
return resultArray;
}

Resources