From dd3f32b8f1f53869a273bb1fe518f3db0680c2f1 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sun, 30 Mar 2014 12:15:39 -0700 Subject: [PATCH] Moved HeightMap to math.convolution. --- .../worldedit/commands/RegionCommands.java | 1 + .../{ => math/convolution}/HeightMap.java | 360 +++++++++--------- .../worldedit/tools/brushes/SmoothBrush.java | 2 +- 3 files changed, 183 insertions(+), 180 deletions(-) rename src/main/java/com/sk89q/worldedit/{ => math/convolution}/HeightMap.java (95%) diff --git a/src/main/java/com/sk89q/worldedit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldedit/commands/RegionCommands.java index c748a7926..74748c60b 100644 --- a/src/main/java/com/sk89q/worldedit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/RegionCommands.java @@ -28,6 +28,7 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.expression.ExpressionException; import com.sk89q.worldedit.math.convolution.GaussianKernel; +import com.sk89q.worldedit.math.convolution.HeightMap; import com.sk89q.worldedit.math.convolution.HeightMapFilter; import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.generator.FloraGenerator; diff --git a/src/main/java/com/sk89q/worldedit/HeightMap.java b/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java similarity index 95% rename from src/main/java/com/sk89q/worldedit/HeightMap.java rename to src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java index 7ac34b8a8..e1ced85df 100644 --- a/src/main/java/com/sk89q/worldedit/HeightMap.java +++ b/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java @@ -1,179 +1,181 @@ -// $Id$ -/* - * WorldEditLibrary - * Copyright (C) 2010 sk89q and contributors - * - * 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 . -*/ - -package com.sk89q.worldedit; - -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldedit.math.convolution.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, region, false); - } - - /** - * Constructs the HeightMap - * - * @param session - * @param region - * @param naturalOnly ignore non-natural blocks - */ - public HeightMap(EditSession session, Region region, boolean naturalOnly) { - 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, naturalOnly); - } - } - } - - /** - * 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(BlockID.AIR); - - 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() != BlockID.WATER && existing.getType() != BlockID.STATIONARY_WATER - && existing.getType() != BlockID.LAVA && existing.getType() != BlockID.STATIONARY_LAVA) { - 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; - } -} +// $Id$ +/* + * WorldEditLibrary + * Copyright (C) 2010 sk89q and contributors + * + * 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 . +*/ + +package com.sk89q.worldedit.math.convolution; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockID; +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, region, false); + } + + /** + * Constructs the HeightMap + * + * @param session + * @param region + * @param naturalOnly ignore non-natural blocks + */ + public HeightMap(EditSession session, Region region, boolean naturalOnly) { + 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, naturalOnly); + } + } + } + + /** + * 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(BlockID.AIR); + + 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() != BlockID.WATER && existing.getType() != BlockID.STATIONARY_WATER + && existing.getType() != BlockID.LAVA && existing.getType() != BlockID.STATIONARY_LAVA) { + 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; + } +} diff --git a/src/main/java/com/sk89q/worldedit/tools/brushes/SmoothBrush.java b/src/main/java/com/sk89q/worldedit/tools/brushes/SmoothBrush.java index 06911372c..27a41c0c1 100644 --- a/src/main/java/com/sk89q/worldedit/tools/brushes/SmoothBrush.java +++ b/src/main/java/com/sk89q/worldedit/tools/brushes/SmoothBrush.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.tools.brushes; import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.HeightMap; +import com.sk89q.worldedit.math.convolution.HeightMap; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldVector;