package com.sk89q.worldedit; // $Id$ /* * WorldEditLibrary * Copyright (C) 2010 sk89q * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.filtering.HeightMapFilter; import com.sk89q.worldedit.regions.Region; /** * Allows applications of Kernels onto the region's heightmap. * Currently only used for smoothing (with a GaussianKernel). * * @author Grum */ public class HeightMap { private int[] data; private int width; private int height; private Region region; private EditSession session; /** * Constructs the HeightMap * * @param session * @param region */ public HeightMap(EditSession session, Region region) { this.session = session; this.region = region; this.width = region.getWidth(); this.height = region.getLength(); int minX = region.getMinimumPoint().getBlockX(); int minY = region.getMinimumPoint().getBlockY(); int minZ = region.getMinimumPoint().getBlockZ(); int maxY = region.getMaximumPoint().getBlockY(); // Store current heightmap data data = new int[width * height]; for (int z = 0; z < height; z++) { for (int x = 0; x < width; x++) { data[z * width + x] = session.getHighestTerrainBlock(x + minX, z + minZ, minY, maxY); } } } /** * Apply the filter 'iterations' amount times. * * @param filter * @param iterations * @return number of blocks affected * @throws MaxChangedBlocksException */ public int applyFilter(HeightMapFilter filter, int iterations) throws MaxChangedBlocksException { int[] newData = new int[data.length]; System.arraycopy(data, 0, newData, 0, data.length); for (int i = 0; i < iterations; i++) newData = filter.filter(newData, width, height); return apply(newData); } /** * Apply a raw heightmap to the region * * @param data * @return number of blocks affected * @throws MaxChangedBlocksException */ public int apply(int[] data) throws MaxChangedBlocksException { Vector minY = region.getMinimumPoint(); int originX = minY.getBlockX(); int originY = minY.getBlockY(); int originZ = minY.getBlockZ(); int maxY = region.getMaximumPoint().getBlockY(); BaseBlock fillerAir = new BaseBlock(0); int blocksChanged = 0; // Apply heightmap for (int z = 0; z < height; z++) { for (int x = 0; x < width; x++) { int index = z * width + x; int curHeight = this.data[index]; // Clamp newHeight within the selection area int newHeight = Math.min(maxY, data[index]); // Offset x,z to be 'real' coordinates int X = x + originX; int Z = z + originZ; // We are keeping the topmost blocks so take that in account for the scale double scale = (double) (curHeight - originY) / (double) (newHeight - originY); // Depending on growing or shrinking we need to start at the bottom or top if (newHeight > curHeight) { // Set the top block of the column to be the same type (this might go wrong with rounding) BaseBlock existing = session.getBlock(new Vector(X, curHeight, Z)); // Skip water/lava if (existing.getType() < 8 || existing.getType() > 11) { session.setBlock(new Vector(X, newHeight, Z), existing); blocksChanged++; // Grow -- start from 1 below top replacing airblocks for (int y = newHeight - 1 - originY; y >= 0; y--) { int copyFrom = (int) (y * scale); session.setBlock(new Vector(X, originY + y, Z), session.getBlock(new Vector(X, originY + copyFrom, Z))); blocksChanged++; } } } else if (curHeight > newHeight) { // Shrink -- start from bottom for (int y = 0; y < newHeight - originY; y++) { int copyFrom = (int) (y * scale); session.setBlock(new Vector(X, originY + y, Z), session.getBlock(new Vector(X, originY + copyFrom, Z))); blocksChanged++; } // Set the top block of the column to be the same type // (this could otherwise go wrong with rounding) session.setBlock(new Vector(X, newHeight, Z), session.getBlock(new Vector(X, curHeight, Z))); blocksChanged++; // Fill rest with air for (int y = newHeight + 1; y <= curHeight; y++) { session.setBlock(new Vector(X, y, Z), fillerAir); blocksChanged++; } } } } // Drop trees to the floor -- TODO return blocksChanged; } }