/* * This file is part of the Illarion project. * * Copyright © 2015 - Illarion e.V. * * Illarion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Illarion 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. */ package illarion.mapedit.render; import illarion.mapedit.data.Map; import illarion.mapedit.events.MapScrollEvent; import illarion.mapedit.events.map.RepaintRequestEvent; import illarion.mapedit.events.map.ZoomEvent; import illarion.mapedit.util.Vector2i; import javolution.util.FastTable; import org.bushe.swing.event.EventBus; import org.bushe.swing.event.annotation.AnnotationProcessor; import org.bushe.swing.event.annotation.EventSubscriber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.awt.*; import java.awt.geom.AffineTransform; import java.util.Collections; import java.util.List; /** * This class manages all renderers and enables, and disables them. * * @author Tim */ public class RendererManager { private static final Logger LOGGER = LoggerFactory.getLogger(RendererManager.class); private static final int DEFAULT_TILE_HEIGHT = 16; private static final int DEFAULT_TILE_WIDTH = 32; public static final float DEFAULT_ZOOM = 1f; private static final float MIN_ZOOM = 0.27f; public static final float ZOOM_STEP = .1f; @Nonnull private final List<AbstractMapRenderer> renderers; private float zoom = DEFAULT_ZOOM; private int translationX; private int translationY; private int defaultTranslationX; private int defaultTranslationY; private int actualLevel; private Rectangle panelViewport; public RendererManager() { renderers = new FastTable<>(); AnnotationProcessor.process(this); } public void addRenderer(AbstractMapRenderer r) { renderers.add(r); Collections.sort(renderers); EventBus.publish(new RepaintRequestEvent()); } public void removeRenderer(AbstractMapRenderer r) { renderers.remove(r); EventBus.publish(new RepaintRequestEvent()); } public void render(Map map, @Nonnull Rectangle viewport, @Nonnull Graphics2D g) { Rectangle renderViewport = new Rectangle((int) (viewport.x - (getTileWidth() * getZoom())), (int) (viewport.y - (getTileHeight() * getZoom())), (int) (viewport.width + (2 * getTileWidth() * getZoom())), (int) (viewport.height + (2 * getTileHeight() * getZoom()))); AffineTransform t = g.getTransform(); g.translate(translationX, translationY); g.scale(getZoom(), getZoom()); for (AbstractMapRenderer r : renderers) { r.renderMap(map, renderViewport, actualLevel, g); } g.setTransform(t); } public static float getTileHeight() { return DEFAULT_TILE_HEIGHT; } public static float getTileWidth() { return DEFAULT_TILE_WIDTH; } public void setZoom(float zoom, @Nullable Vector2i zoomPoint) { if (zoomPoint == null) { setZoom(zoom); return; } if ((zoom < .1) || (zoom > 1)) { return; } int viewportWidth = panelViewport.width; int viewportHeight = panelViewport.height; float relativeX = zoomPoint.getX() / (float) viewportWidth; float relativeY = zoomPoint.getY() / (float) viewportHeight; float oldViewportWidth = viewportWidth / this.zoom; float oldViewportHeight = viewportHeight / this.zoom; float newViewportWidth = viewportWidth / zoom; float newViewportHeight = viewportHeight / zoom; int fixX = Math.round((newViewportWidth - oldViewportWidth) * relativeX); int fixY = Math.round((newViewportHeight - oldViewportHeight) * relativeY); translationX /= this.zoom; translationY /= this.zoom; translationX += fixX; translationY += fixY; translationX *= zoom; translationY *= zoom; this.zoom = zoom; EventBus.publish(new RepaintRequestEvent()); } public void setZoom(float zoom) { if ((zoom < .1) || (zoom > 1)) { return; } Vector2i zoomPoint = new Vector2i(panelViewport.width / 2, panelViewport.height / 2); setZoom(zoom, zoomPoint); } public float getZoom() { return zoom; } public int getTranslationX() { return translationX; } public void setTranslationX(int translationX) { this.translationX = translationX; } public int getTranslationY() { return translationY; } public void setTranslationY(int translationY) { this.translationY = translationY; } public void zoomIn(Vector2i pos) { if (zoom < 1) { setZoom(zoom + ZOOM_STEP, pos); } } public void zoomOut(Vector2i pos) { if (zoom > 0) { setZoom(zoom - ZOOM_STEP, pos); } } public void changeZoom(float amount, Vector2i pos) { setZoom(zoom + amount, pos); } public void changeTranslation(int x, int y) { setTranslationX(translationX + x); setTranslationY(translationY + y); } public float getMinZoom() { return MIN_ZOOM; } public void setPanelViewport(@Nullable Rectangle panelViewport) { if (panelViewport == null) { LOGGER.warn("SetPanelViewport: panelViewport is null"); return; } if (this.panelViewport == null) { this.panelViewport = new Rectangle(); } this.panelViewport.setRect(panelViewport.x, panelViewport.y, panelViewport.width, panelViewport.height); } public void setDefaultTranslationY(int defaultTranslationY) { this.defaultTranslationY = defaultTranslationY; } public void setDefaultTranslationX(int defaultTranslationX) { this.defaultTranslationX = defaultTranslationX; } public void setSelectedLevel(int level) { actualLevel = level; } @EventSubscriber public void onZoom(@Nonnull ZoomEvent e) { if (e.isOriginal()) { setZoom(DEFAULT_ZOOM); setTranslationX(defaultTranslationX); setTranslationY(defaultTranslationY); } else { changeZoom(e.getValue(), e.getPos()); } } @EventSubscriber public void onScroll(@Nonnull MapScrollEvent e) { changeTranslation(e.getX(), e.getY()); EventBus.publish(new RepaintRequestEvent()); } }