299 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /*
 | |
|  * Copyright (C) 2008 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
|  
 | |
|  /**
 | |
|  * @fileoverview This implements a Photoshop script that can be used to generate
 | |
|  * collision information for the AndouKun game engine.  This tool walks over
 | |
|  * each path in the current document and generates a list of edges and normals
 | |
|  * in a new document.  It is intended to be used on a file containing
 | |
|  * graphical representations of the collision tiles used by the engine.  Each
 | |
|  * path in the file must be closed and may not contain any curved points 
 | |
|  * (the tool assumes that the line between any two points in a given path is
 | |
|  * straight).  Only one shape may be contained per path layer (each path must go
 | |
|  * in its own path layer).  This tool can also output a graphical version of its
 | |
|  * edge calculation for debugging purposes.
 | |
|  */
 | |
|  
 | |
| /* If set to true, the computation will be rendered graphically to the output
 | |
|    file */
 | |
| var drawOutput = false;
 | |
| /* If true, the computation will be printed in a text layer in the
 | |
|    output file.*/
 | |
| var printOutput = true;
 | |
| 
 | |
| // Back up the ruler units that this file uses before switching to pixel units.
 | |
| var defaultRulerUnits = app.preferences.rulerUnits;
 | |
| app.preferences.rulerUnits = Units.PIXELS;
 | |
| 
 | |
| var tileSizeX = prompt("Tile pixel width:");
 | |
| var tileSizeY = prompt("Tile pixel height:");
 | |
| 
 | |
| var documentWidth = app.activeDocument.width;
 | |
| var documentHeight = app.activeDocument.height;
 | |
| 
 | |
| var tilesPerRow = documentWidth / tileSizeX;
 | |
| var tilesPerColumn = documentHeight / tileSizeY;
 | |
| 
 | |
| var tiles = new Array();
 | |
| tiles.length = tilesPerRow * tilesPerColumn;
 | |
| 
 | |
| // Walk the list of paths and extract edges and normals.  Store these in
 | |
| // an array by tile.
 | |
| var pathList = app.activeDocument.pathItems;
 | |
| for (pathIndex = 0; pathIndex < pathList.length; pathIndex++) {
 | |
|   var main_path = pathList[pathIndex];
 | |
|   if (main_path) {
 | |
|     var itemList = main_path.subPathItems;
 | |
|     if (!itemList) {
 | |
|       alert("Path has no sub items!");
 | |
|     } else {
 | |
|       for (var x = 0; x < itemList.length; x++) {
 | |
|         var item = itemList[x];
 | |
|         var points = item.pathPoints;
 | |
|         var tile = new Object;
 | |
|         tile.edges = new Array();
 | |
|         
 | |
|         var totalX = 0;
 | |
|         var totalY = 0;
 | |
|         for (var y = 0; y < points.length; y++) {
 | |
|           var firstPoint = points[y];
 | |
|           var lastPoint = points[(y + 1) % points.length];
 | |
| 
 | |
|           var edge = new Object;
 | |
|           
 | |
|           edge.startX = firstPoint.anchor[0];
 | |
|           edge.startY = firstPoint.anchor[1];
 | |
|           
 | |
|           edge.endX = lastPoint.anchor[0];
 | |
|           edge.endY = lastPoint.anchor[1];
 | |
|           
 | |
|           var normalX = -(edge.endY - edge.startY);
 | |
|           var normalY = edge.endX - edge.startX;
 | |
|           
 | |
|           var normalLength = Math.sqrt((normalX * normalX) + (normalY * normalY));
 | |
|           normalX /= normalLength;
 | |
|           normalY /= normalLength;
 | |
|           
 | |
|           edge.normalX = normalX;
 | |
|           edge.normalY = normalY;
 | |
|           
 | |
|           if (normalX == 0 && normalY == 0) {
 | |
|             alert("Zero length normal calculated at path " + pathIndex);
 | |
|           }
 | |
|           
 | |
|           var normalLength2 = Math.sqrt((normalX * normalX) + (normalY * normalY));
 | |
|           if (normalLength2 > 1 || normalLength2 < 0.9) {
 | |
|             alert("Normal of invalid length (" + normalLength2 + ") found at path " + pathIndex);
 | |
|           }
 | |
|           
 | |
|           totalX += edge.endX;
 | |
|           totalY += edge.endY;
 | |
|           
 | |
|           var width = edge.endX - edge.startX;
 | |
|           var height = edge.endY - edge.startY;
 | |
|           
 | |
|           edge.centerX = edge.endX - (width / 2);
 | |
|           edge.centerY = edge.endY - (height / 2);
 | |
|           
 | |
|           tile.edges.push(edge);
 | |
|         }
 | |
|         
 | |
|         totalX /= points.length;
 | |
|         totalY /= points.length;
 | |
|         tile.centerX = totalX;
 | |
|         tile.centerY = totalY; 
 | |
|         
 | |
|         var column = Math.floor(tile.centerX / tileSizeX);
 | |
|         var row = Math.floor(tile.centerY / tileSizeY);
 | |
|         
 | |
|         tile.xOffset = column * tileSizeX;
 | |
|         tile.yOffset = row * tileSizeY;
 | |
|         
 | |
|         tile.centerX -= tile.xOffset;
 | |
|         tile.centerY -= tile.yOffset;
 | |
|         
 | |
|         var tileIndex = Math.floor(row * tilesPerRow + column);
 | |
|         tiles[tileIndex] = tile;
 | |
|         
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| var outputString = "";
 | |
| 
 | |
| // For each tile print the edges to a string.
 | |
| for (var x = 0; x < tiles.length; x++) {
 | |
|   if (tiles[x]) {
 | |
|     var tile = tiles[x];
 | |
|     for (var y = 0; y < tile.edges.length; y++) {
 | |
|       var edge = tile.edges[y];
 | |
|       
 | |
|       // convert to tile space
 | |
|       edge.startX -= tile.xOffset;
 | |
|       edge.startY -= tile.yOffset;
 | |
|       edge.endX -= tile.xOffset;
 | |
|       edge.endY -= tile.yOffset;
 | |
|       edge.centerX -= tile.xOffset;
 | |
|       edge.centerY -= tile.yOffset;
 | |
|       
 | |
|       // The normals that we calculated previously might be facing the wrong
 | |
|       // direction.  Detect this case and correct it by checking to see if
 | |
|       // adding the normal to a point on the edge moves the point closer or
 | |
|       // further from the center of the shape.
 | |
|       if (Math.abs(edge.centerX - tile.centerX) > 
 | |
|             Math.abs((edge.centerX + edge.normalX) - tile.centerX)) {
 | |
|         edge.normalX *= -1;
 | |
|         edge.normalY *= -1;
 | |
|       }
 | |
|       
 | |
|       if (Math.abs(edge.centerY - tile.centerY) > 
 | |
|             Math.abs((edge.centerY + edge.normalY) - tile.centerY)) {
 | |
|         edge.normalX *= -1;
 | |
|         edge.normalY *= -1;
 | |
|       }
 | |
|       
 | |
|        
 | |
|       // Convert to left-handed GL space (the origin is at the bottom-left).
 | |
|       edge.normalY *= -1;
 | |
|       edge.startY = tileSizeY - edge.startY;
 | |
|       edge.endY = tileSizeY - edge.endY;
 | |
|       edge.centerY = tileSizeY - edge.centerY;
 | |
|      
 | |
|       outputString += x + ":" + Math.floor(edge.startX) + "," + 
 | |
|           Math.floor(edge.startY) + ":" + Math.floor(edge.endX) + "," + 
 | |
|           Math.floor(edge.endY) + ":" + edge.normalX + "," + edge.normalY +
 | |
|           "\r";
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| if (outputString.length > 0) {
 | |
|     
 | |
|     var newDoc = app.documents.add(600, 700, 72.0, "Edge Output", 
 | |
|         NewDocumentMode.RGB);
 | |
|     
 | |
|     if (drawOutput) {
 | |
|       // Render the edges and normals to the new document.
 | |
|       var pathLayer = newDoc.artLayers.add();
 | |
|       newDoc.activeLayer = pathLayer;
 | |
|       
 | |
|       // draw the edges to make sure everything works
 | |
|       var black = new SolidColor;
 | |
|       black.rgb.red = 0;
 | |
|       black.rgb.blue = 0;
 | |
|       black.rgb.green = 0;
 | |
|       
 | |
|       var redColor = new SolidColor;
 | |
|       redColor.rgb.red = 255;
 | |
|       redColor.rgb.blue = 0;
 | |
|       redColor.rgb.green = 0;
 | |
|       
 | |
|       var greenColor = new SolidColor;
 | |
|       greenColor.rgb.red = 0;
 | |
|       greenColor.rgb.blue = 0;
 | |
|       greenColor.rgb.green = 255;
 | |
|       
 | |
|       var blueColor = new SolidColor;
 | |
|       blueColor.rgb.red = 0;
 | |
|       blueColor.rgb.blue = 255;
 | |
|       blueColor.rgb.green = 0;
 | |
|       
 | |
|       var lineIndex = 0;
 | |
|       for (var x = 0; x < tiles.length; x++) {
 | |
|         if (tiles[x]) {
 | |
|           var tile = tiles[x];
 | |
|           var lineArray = new Array();
 | |
|           var offsetX = Math.floor(x % tilesPerRow) * tileSizeX;
 | |
|           var offsetY = Math.floor(x / tilesPerRow) * tileSizeY;
 | |
|             
 | |
|           for (var y = 0; y < tile.edges.length; y++) {
 | |
|             var edge = tile.edges[y];
 | |
|            
 | |
|             lineArray[y] = Array(offsetX + edge.startX, offsetY + edge.startY);
 | |
|           }
 | |
|           
 | |
|           // I tried to do this by stroking paths, but the documentation 
 | |
|           // provided by Adobe is faulty (their sample code doesn't run).  The 
 | |
|           // same thing can be accomplished with selections instead.
 | |
|           newDoc.selection.select(lineArray);
 | |
|           newDoc.selection.stroke(black, 2);
 | |
|          
 | |
|           for (var y = 0; y < tile.edges.length; y++) {
 | |
|             var edge = tile.edges[y];
 | |
|       
 | |
|              var normalX = Math.round(tile.centerX + 
 | |
|                 (edge.normalX * (tileSizeX / 2)));
 | |
|              var normalY = Math.round(tile.centerY + 
 | |
|                 (edge.normalY * (tileSizeY / 2)));
 | |
|              
 | |
|              var tileCenterArray = new Array();
 | |
|              tileCenterArray[0] = new Array(offsetX + tile.centerX - 1, 
 | |
|                 offsetY + tile.centerY - 1);
 | |
|              tileCenterArray[1] = new Array(offsetX + tile.centerX - 1, 
 | |
|                 offsetY + tile.centerY + 1);
 | |
|              tileCenterArray[2] = new Array(offsetX + tile.centerX + 1, 
 | |
|                 offsetY + tile.centerY + 1);
 | |
|              tileCenterArray[3] = new Array(offsetX + tile.centerX + 1, 
 | |
|                 offsetY + tile.centerY - 1);
 | |
|              tileCenterArray[4] = new Array(offsetX + normalX - 1, 
 | |
|                 offsetY + normalY - 1);
 | |
|              tileCenterArray[5] = new Array(offsetX + normalX + 1, 
 | |
|                 offsetY + normalY + 1);
 | |
|              tileCenterArray[6] = new Array(offsetX + tile.centerX, 
 | |
|                 offsetY + tile.centerY);
 | |
|               
 | |
|              newDoc.selection.select(tileCenterArray);
 | |
|              newDoc.selection.fill(redColor);
 | |
|              
 | |
|              var centerArray = new Array();
 | |
|              centerArray[0] = new Array(offsetX + edge.centerX - 1, 
 | |
|                 offsetY + edge.centerY - 1);
 | |
|              centerArray[1] = new Array(offsetX + edge.centerX - 1, 
 | |
|                 offsetY + edge.centerY + 1);
 | |
|              centerArray[2] = new Array(offsetX + edge.centerX + 1, 
 | |
|                 offsetY + edge.centerY + 1);
 | |
|              centerArray[3] = new Array(offsetX + edge.centerX + 1, 
 | |
|                 offsetY + edge.centerY - 1);
 | |
|              
 | |
|              newDoc.selection.select(centerArray);
 | |
|              newDoc.selection.fill(greenColor);
 | |
|              
 | |
|           }
 | |
|          
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     
 | |
|     if (printOutput) {
 | |
|       var textLayer = newDoc.artLayers.add();
 | |
|       textLayer.kind = LayerKind.TEXT;
 | |
|       textLayer.textItem.contents = outputString;
 | |
|     }
 | |
| }
 | |
| 
 | |
| preferences.rulerUnits = defaultRulerUnits;
 | |
| 
 | |
| // Convenience function for clamping negative values to zero.  Trying to select
 | |
| // areas outside the canvas causes Bad Things.
 | |
| function clamp(input) {
 | |
|   if (input < 0) {
 | |
|     return 0;
 | |
|   }
 | |
|   return input;
 | |
| }
 |