@SuppressWarnings("unchecked") private void updatePointList() { for (SmoothPathSegment sps : spsMap.values()) sps.remove(); spsMap.clear(); for (LinearPathSegment lps : lpsMap.values()) lps.remove(); lpsMap.clear(); if (arrow != null) arrow.remove(); Set<PathPoint> pps = ((HashMap<PathPoint, PointVisual>) pvMap.clone()).keySet(); int s = pvList.size(); ActiveArrayList<PathPoint> pp = path.points; int s2 = pp.size(); while (s > s2) pvList.remove(--s); pvList.ensureCapacity(s2); for (int i = 0; i < s2; i++) { PathPoint p = pp.get(i); PointVisual v = pvMap.get(p); if (v == null) { v = new PointVisual(p); pvMap.put(p, v); } else { pps.remove(p); } if (i >= s) pvList.add(v); else pvList.set(i, v); } if (pps.contains(properties.get(PPathEditor.SELECTED_POINT))) properties.put(PPathEditor.SELECTED_POINT, null); for (PathPoint pathPoint : pps) pvMap.remove(pathPoint).remove(); if (path.get(PPath.SMOOTH)) { boolean closed = path.properties.get(PPath.CLOSED); if (s2 >= 3) { PathPoint[] rpp = new PathPoint[4]; for (int i = 0; i < 4; i++) rpp[i] = pp.get(i % s2); int i2 = closed ? s2 + 1 : s2 - 2; for (int i = 1; i < i2; i++) { PathPoint p = rpp[1]; spsMap.put(p, new SmoothPathSegment(rpp)); rpp = Arrays.copyOfRange(rpp, 1, 5); rpp[3] = pp.get((i + 3) % s2); } if (!closed) { PathPoint p = pp.get(0); spsMap.put(p, new SmoothPathSegment(null, p, pp.get(1), pp.get(2))); p = pp.get(s2 - 2); spsMap.put(p, new SmoothPathSegment(pp.get(s2 - 3), p, pp.get(s2 - 1), null)); } arrow = new PathArrow(spsMap.get(pp.get(0))); } } else { if (s2 >= 2) { for (int i = 0; i < s2 - 1; i++) { PathPoint p = path.points.get(i); lpsMap.put(p, new LinearPathSegment(p, path.points.get(i + 1))); } if (path.properties.get(PPath.CLOSED)) { PathPoint p = path.points.get(s2 - 1); lpsMap.put(p, new LinearPathSegment(p, path.points.get(0))); } arrow = new PathArrow(null); } } }
private void mouseEvent(MouseEvent e) { Point p = e.getPoint().getLocation(); componentToVisual(p); int s = POINT_MOUSE_RANGE - POINT_SIZE / 2 + 1; Iterator<Visual> vi = binVisual.intersect(new Rectangle(p.x - s, p.y - s, 2 * s, 2 * s)); PathPoint ppOver = null; int posd = POINT_MOUSE_RANGE * POINT_MOUSE_RANGE; while (vi.hasNext()) { Visual v = vi.next(); if (v instanceof PointVisual) { PathPoint pp = ((PointVisual) v).point; int xd = pp.getX() - p.x; int yd = pp.getY() - p.y; int sd = xd * xd + yd * yd; if (sd <= posd) { ppOver = pp; posd = sd; break; } } } switch (e.getID()) { case MouseEvent.MOUSE_PRESSED: ppPressed = ppOver; switch (e.getButton()) { case MouseEvent.BUTTON1: if (ppOver == null) { PathPoint pp = properties.get(PPathEditor.SELECTED_POINT); ppOver = new PathPoint(p.x, p.y, pp == null ? 100 : pp.getSpeed()); if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) movePathPoint(ppOver, p.x, p.y, true); path.points.add(ppOver); ppPressed = ppOver; } else if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) { int i = path.points.indexOf(ppOver); ppOver = new PathPoint(ppOver.getX(), ppOver.getY(), ppOver.getSpeed()); path.points.add(i, ppOver); ppPressed = ppOver; } properties.put(PPathEditor.SELECTED_POINT, ppOver); dragging = true; dragOffset = p.getLocation(); dragOffset.translate(-ppOver.getX(), -ppOver.getY()); break; } break; case MouseEvent.MOUSE_DRAGGED: if (dragging) { lockBounds(); PathPoint pp = properties.get(PPathEditor.SELECTED_POINT); if (pp == null) { ppPressed = null; dragging = false; unlockBounds(); break; } movePathPoint( pp, p.x - dragOffset.x, p.y - dragOffset.y, (e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0); } else if (ppPressed != ppOver) ppPressed = null; break; case MouseEvent.MOUSE_RELEASED: switch (e.getButton()) { case MouseEvent.BUTTON1: dragging = false; unlockBounds(); break; case MouseEvent.BUTTON3: if (dragging) { dragging = false; unlockBounds(); } else if (ppPressed != null) path.points.remove(ppPressed); break; } ppPressed = null; } }
public class PathEditor extends VisualPanel implements UpdateListener { private static final int ROOM_LAYER = -1; private static final int BIN_LAYER = 0; private static final int GRID_LAYER = 1; private static final int POINT_SIZE = 8; private static final int POINT_MOUSE_RANGE = 8; private static final int ARROW_SIZE = 10; private static final int LINE_WIDTH = 5; private static final long serialVersionUID = 1L; private final Path path; private RoomVisual roomVisual; private final BinVisual binVisual; private final GridVisual gridVisual; private final PathPropertyListener ppl = new PathPropertyListener(); private final PointListListener pll = new PointListListener(); public final PropertyMap<PPathEditor> properties; private final PathEditorPropertyValidator pepv = new PathEditorPropertyValidator(); public enum PPathEditor { SHOW_GRID, SELECTED_POINT } private static final EnumMap<PPathEditor, Object> DEFS = PropertyMap.makeDefaultMap(PPathEditor.class, true, null); private final ArrayList<PointVisual> pvList; private final HashMap<PathPoint, PointVisual> pvMap; private PathArrow arrow; public PathEditor(Path p) { enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); properties = new PropertyMap<PPathEditor>(PPathEditor.class, pepv, DEFS); binVisual = new BinVisual(container, 128, 512, 512); put(BIN_LAYER, binVisual); int sx = p.get(PPath.SNAP_X); int sy = p.get(PPath.SNAP_Y); gridVisual = new GridVisual(false, sx, sy); put(GRID_LAYER, gridVisual); path = p; path.reference.updateSource.addListener(this); path.properties.updateSource.addListener(ppl); path.points.updateSource.addListener(pll); int s = path.points.size(); pvList = new ArrayList<PointVisual>(s); pvMap = new HashMap<PathPoint, PointVisual>(Math.max((int) (s / .75f) + 1, 16)); updatePointList(); ResourceReference<Room> r = path.get(PPath.BACKGROUND_ROOM); setRoom(r == null ? null : r.get()); } public void updated(UpdateEvent e) { invalidate(); repaint(); } private boolean dragging = false; private Point dragOffset; private PathPoint ppPressed; private void mouseEvent(MouseEvent e) { Point p = e.getPoint().getLocation(); componentToVisual(p); int s = POINT_MOUSE_RANGE - POINT_SIZE / 2 + 1; Iterator<Visual> vi = binVisual.intersect(new Rectangle(p.x - s, p.y - s, 2 * s, 2 * s)); PathPoint ppOver = null; int posd = POINT_MOUSE_RANGE * POINT_MOUSE_RANGE; while (vi.hasNext()) { Visual v = vi.next(); if (v instanceof PointVisual) { PathPoint pp = ((PointVisual) v).point; int xd = pp.getX() - p.x; int yd = pp.getY() - p.y; int sd = xd * xd + yd * yd; if (sd <= posd) { ppOver = pp; posd = sd; break; } } } switch (e.getID()) { case MouseEvent.MOUSE_PRESSED: ppPressed = ppOver; switch (e.getButton()) { case MouseEvent.BUTTON1: if (ppOver == null) { PathPoint pp = properties.get(PPathEditor.SELECTED_POINT); ppOver = new PathPoint(p.x, p.y, pp == null ? 100 : pp.getSpeed()); if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) movePathPoint(ppOver, p.x, p.y, true); path.points.add(ppOver); ppPressed = ppOver; } else if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) { int i = path.points.indexOf(ppOver); ppOver = new PathPoint(ppOver.getX(), ppOver.getY(), ppOver.getSpeed()); path.points.add(i, ppOver); ppPressed = ppOver; } properties.put(PPathEditor.SELECTED_POINT, ppOver); dragging = true; dragOffset = p.getLocation(); dragOffset.translate(-ppOver.getX(), -ppOver.getY()); break; } break; case MouseEvent.MOUSE_DRAGGED: if (dragging) { lockBounds(); PathPoint pp = properties.get(PPathEditor.SELECTED_POINT); if (pp == null) { ppPressed = null; dragging = false; unlockBounds(); break; } movePathPoint( pp, p.x - dragOffset.x, p.y - dragOffset.y, (e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0); } else if (ppPressed != ppOver) ppPressed = null; break; case MouseEvent.MOUSE_RELEASED: switch (e.getButton()) { case MouseEvent.BUTTON1: dragging = false; unlockBounds(); break; case MouseEvent.BUTTON3: if (dragging) { dragging = false; unlockBounds(); } else if (ppPressed != null) path.points.remove(ppPressed); break; } ppPressed = null; } } private void movePathPoint(PathPoint pp, int x, int y, boolean snap) { if (snap) { int sx = path.get(PPath.SNAP_X); int sy = path.get(PPath.SNAP_Y); pp.setX(negDiv(x + sx / 2, sx) * sx); pp.setY(negDiv(y + sy / 2, sy) * sy); } else { pp.setX(x); pp.setY(y); } } @Override protected void processMouseMotionEvent(MouseEvent e) { mouseEvent(e); super.processMouseMotionEvent(e); } @Override protected void processMouseEvent(MouseEvent e) { mouseEvent(e); super.processMouseEvent(e); } static final int HPS = POINT_SIZE >> 1; final BufferedImage[] pointImage = new BufferedImage[2]; private class PointVisual extends PathVisual { final PathPoint point; final Rectangle bounds = new Rectangle(POINT_SIZE, POINT_SIZE); final PointPositionListener ppl = new PointPositionListener(); private boolean selected; public PointVisual(PathPoint p) { point = p; binVisual.setDepth(this, 0); p.properties.getUpdateSource(PPathPoint.X).addListener(ppl); p.properties.getUpdateSource(PPathPoint.Y).addListener(ppl); validate(); } public void setSelected(boolean s) { selected = s; binVisual.setDepth(this, s ? -2 : 0); } protected void calculateBounds() { bounds.setLocation(point.getX() - HPS, point.getY() - HPS); } protected void validate() { calculateBounds(); setBounds(bounds); } public void paint(Graphics g) { int i = selected ? 1 : 0; if (pointImage[i] == null) { pointImage[i] = getGraphicsConfiguration() .createCompatibleImage(POINT_SIZE, POINT_SIZE, Transparency.BITMASK); Graphics2D g2 = pointImage[i].createGraphics(); g2.setColor(selected ? Color.RED : Color.BLUE); g2.fillOval(0, 0, POINT_SIZE - 1, POINT_SIZE - 1); g2.setColor(Color.BLACK); g2.drawOval(0, 0, POINT_SIZE - 1, POINT_SIZE - 1); } g.drawImage(pointImage[i], 0, 0, null); } private class PointPositionListener extends PropertyUpdateListener<PPathPoint> { @Override public void updated(PropertyUpdateEvent<PPathPoint> e) { invalidate(); } } } static final int HLW = LINE_WIDTH + 1 >> 1; static final Stroke STROKE_OUTER = new BasicStroke(LINE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); static final Stroke STROKE_INNER = new BasicStroke(LINE_WIDTH - 2.83f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); private int spPrecision; private float[] spBlending; private float getBlending(int p, int t) { if (p != spPrecision) { spPrecision = p; spBlending = new float[1 + 3 * p]; float isp = 1f / (p * p); float ip = 1f / p; for (int i = 0; i < p; i++) spBlending[i] = i * i * isp * 0.5f; for (int i = p; i < 2 * p; i++) spBlending[i] = (-2 * i * i * isp + 6 * i * ip - 3) * 0.5f; for (int i = 2 * p; i <= 3 * p; i++) spBlending[i] = (i * i * isp - 6 * i * ip + 9) * 0.5f; } return spBlending[t]; } private class LinearPathSegment extends PathVisual { final PathPoint[] pp = new PathPoint[2]; final Rectangle bounds = new Rectangle(); final PointPositionListener ppl = new PointPositionListener(); int px0, py0, px1, py1; public LinearPathSegment(PathPoint... p) { if (p.length != 2) throw new IllegalArgumentException(); System.arraycopy(p, 0, pp, 0, 2); binVisual.setDepth(this, 1); for (PathPoint point : p) { point.properties.getUpdateSource(PPathPoint.X).addListener(ppl); point.properties.getUpdateSource(PPathPoint.Y).addListener(ppl); } validate(); } protected void calculateBounds() { px0 = pp[0].getX(); py0 = pp[0].getY(); px1 = pp[1].getX(); py1 = pp[1].getY(); bounds.setBounds(0, 0, -1, -1); bounds.add(px0, py0); bounds.add(px1, py1); bounds.grow(HLW, HLW); px0 -= bounds.x; py0 -= bounds.y; px1 -= bounds.x; py1 -= bounds.y; } @Override protected void validate() { calculateBounds(); setBounds(bounds); } public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(Color.BLACK); g2.setStroke(STROKE_OUTER); g2.drawLine(px0, py0, px1, py1); g2.setColor(Color.WHITE); g2.setStroke(STROKE_INNER); g2.drawLine(px0, py0, px1, py1); } private class PointPositionListener extends PropertyUpdateListener<PPathPoint> { @Override public void updated(PropertyUpdateEvent<PPathPoint> e) { invalidate(); } } } private class SmoothPathSegment extends PathVisual { final PathPoint[] pp = new PathPoint[4]; final Rectangle bounds = new Rectangle(); final PointPositionListener ppl = new PointPositionListener(); final InnerSegment innerSegment = new InnerSegment(); int[] px, py; public SmoothPathSegment(PathPoint... p) { if (p.length != 4) throw new IllegalArgumentException(); System.arraycopy(p, 0, pp, 0, 4); binVisual.setDepth(this, 2); for (PathPoint point : p) { if (point == null) continue; point.properties.getUpdateSource(PPathPoint.X).addListener(ppl); point.properties.getUpdateSource(PPathPoint.Y).addListener(ppl); } validate(); } @Override public void remove() { innerSegment.remove(); super.remove(); } private void pBlend(int i, int n, int t, int[] x, int[] y) { float w0 = getBlending(n, t + 1 + 2 * n); float w2 = getBlending(n, t + 1); px[i] = x[1] + Math.round(w0 * (x[0] - x[1]) + w2 * (x[2] - x[1])); py[i] = y[1] + Math.round(w0 * (y[0] - y[1]) + w2 * (y[2] - y[1])); } protected void calculateBounds() { bounds.setBounds(0, 0, -1, -1); int[] x = new int[4]; int[] y = new int[4]; for (int i = 0; i < 4; i++) { if (pp[i] == null) continue; x[i] = pp[i].getX(); y[i] = pp[i].getY(); } int p = path.properties.get(PPath.PRECISION); int n = (1 << p); if (pp[0] == null) { px = new int[2]; py = new int[2]; px[0] = x[1]; py[0] = y[1]; bounds.add(x[1], y[1]); pBlend(1, n, 0, Arrays.copyOfRange(x, 1, 4), Arrays.copyOfRange(y, 1, 4)); bounds.add(px[1], py[1]); } else { px = new int[n]; py = new int[n]; for (int t = 0; t < n - 1; t++) { pBlend(t, n, t, x, y); bounds.add(px[t], py[t]); } if (pp[3] == null) { px[n - 1] = x[2]; py[n - 1] = y[2]; } else pBlend(n - 1, n, 0, Arrays.copyOfRange(x, 1, 4), Arrays.copyOfRange(y, 1, 4)); bounds.add(px[n - 1], py[n - 1]); } bounds.grow(HLW, HLW); for (int t = 0; t < px.length; t++) { px[t] -= bounds.x; py[t] -= bounds.y; } } @Override protected void validate() { calculateBounds(); setBounds(bounds); innerSegment.setBounds(bounds); } public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(Color.BLACK); g2.setStroke(STROKE_OUTER); g2.drawPolyline(px, py, px.length); } private class InnerSegment extends VisualBox { public InnerSegment() { super(binVisual); binVisual.setDepth(this, 1); } @Override protected void setBounds(Rectangle b) { super.setBounds(b); } public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(Color.WHITE); g2.setStroke(STROKE_INNER); g2.drawPolyline(px, py, px.length); } } private class PointPositionListener extends PropertyUpdateListener<PPathPoint> { @Override public void updated(PropertyUpdateEvent<PPathPoint> e) { invalidate(); } } } private static final double ARROW_ANGLE = 2 * Math.PI / 3; private class PathArrow extends PathVisual { final PointPositionListener ppl = new PointPositionListener(); final SmoothPathSegment segment; final int[] px = new int[4]; final int[] py = new int[4]; public PathArrow(SmoothPathSegment s) { segment = s; binVisual.setDepth(this, -1); int i2 = s == null ? 2 : path.get(PPath.CLOSED) ? 4 : 3; for (int i = 0; i < i2; i++) { PathPoint p = path.points.get(i == 3 ? path.points.size() - 1 : i); p.properties.getUpdateSource(PPathPoint.X).addListener(ppl); p.properties.getUpdateSource(PPathPoint.Y).addListener(ppl); } validate(); } private void calculatePoints(int x, int y, double d) { px[0] = x + (int) Math.round(ARROW_SIZE * Math.cos(d)); py[0] = y - (int) Math.round(ARROW_SIZE * Math.sin(d)); px[1] = x + (int) Math.round(ARROW_SIZE * Math.cos(d + ARROW_ANGLE)); py[1] = y - (int) Math.round(ARROW_SIZE * Math.sin(d + ARROW_ANGLE)); px[2] = x; py[2] = y; px[3] = x + (int) Math.round(ARROW_SIZE * Math.cos(d - ARROW_ANGLE)); py[3] = y - (int) Math.round(ARROW_SIZE * Math.sin(d - ARROW_ANGLE)); } private int sqrdist(int x, int y) { return x * x + y * y; } @Override protected void validate() { if (segment == null) { PathPoint p = path.points.get(0); PathPoint p2 = path.points.get(1); int x = p.getX(); int y = p.getY(); calculatePoints(x, y, Math.atan2(y - p2.getY(), p2.getX() - x)); } else { segment.validate(); if (path.get(PPath.CLOSED)) { int i = segment.px.length - 1; int x = segment.px[i]; int y = segment.py[i]; int i2 = i - 1; while (i2 > 0 && sqrdist(segment.px[i2] - x, segment.py[i2] - y) < 4) i2--; calculatePoints( x + segment.bounds.x, y + segment.bounds.y, Math.atan2(segment.py[i2] - y, x - segment.px[i2])); } else { int x = segment.px[0]; int y = segment.py[0]; int i = 1; while (i < segment.px.length - 1 && sqrdist(segment.px[i] - x, segment.py[i] - y) < 4) i++; calculatePoints( x + segment.bounds.x, y + segment.bounds.y, Math.atan2(y - segment.py[i], segment.px[i] - x)); } } Rectangle bounds = new Rectangle(-1, -1); for (int i = 0; i < 4; i++) bounds.add(px[i], py[i]); for (int i = 0; i < 4; i++) { px[i] -= bounds.x; py[i] -= bounds.y; } setBounds(bounds); } public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(new Color(0, 224, 0)); g2.fillPolygon(px, py, 4); g2.setColor(Color.BLACK); g2.drawPolygon(px, py, 4); } private class PointPositionListener extends PropertyUpdateListener<PPathPoint> { @Override public void updated(PropertyUpdateEvent<PPathPoint> e) { invalidate(); } } } private abstract class PathVisual extends VisualBox { private boolean invalid; public PathVisual() { super(binVisual); } protected abstract void validate(); protected final void invalidate() { if (invalid) return; invalid = true; SwingUtilities.invokeLater( new Runnable() { public void run() { try { if (invalid) validate(); } finally { invalid = false; } } }); } } final HashMap<PathPoint, SmoothPathSegment> spsMap = new HashMap<PathPoint, SmoothPathSegment>(); final HashMap<PathPoint, LinearPathSegment> lpsMap = new HashMap<PathPoint, LinearPathSegment>(); @SuppressWarnings("unchecked") private void updatePointList() { for (SmoothPathSegment sps : spsMap.values()) sps.remove(); spsMap.clear(); for (LinearPathSegment lps : lpsMap.values()) lps.remove(); lpsMap.clear(); if (arrow != null) arrow.remove(); Set<PathPoint> pps = ((HashMap<PathPoint, PointVisual>) pvMap.clone()).keySet(); int s = pvList.size(); ActiveArrayList<PathPoint> pp = path.points; int s2 = pp.size(); while (s > s2) pvList.remove(--s); pvList.ensureCapacity(s2); for (int i = 0; i < s2; i++) { PathPoint p = pp.get(i); PointVisual v = pvMap.get(p); if (v == null) { v = new PointVisual(p); pvMap.put(p, v); } else { pps.remove(p); } if (i >= s) pvList.add(v); else pvList.set(i, v); } if (pps.contains(properties.get(PPathEditor.SELECTED_POINT))) properties.put(PPathEditor.SELECTED_POINT, null); for (PathPoint pathPoint : pps) pvMap.remove(pathPoint).remove(); if (path.get(PPath.SMOOTH)) { boolean closed = path.properties.get(PPath.CLOSED); if (s2 >= 3) { PathPoint[] rpp = new PathPoint[4]; for (int i = 0; i < 4; i++) rpp[i] = pp.get(i % s2); int i2 = closed ? s2 + 1 : s2 - 2; for (int i = 1; i < i2; i++) { PathPoint p = rpp[1]; spsMap.put(p, new SmoothPathSegment(rpp)); rpp = Arrays.copyOfRange(rpp, 1, 5); rpp[3] = pp.get((i + 3) % s2); } if (!closed) { PathPoint p = pp.get(0); spsMap.put(p, new SmoothPathSegment(null, p, pp.get(1), pp.get(2))); p = pp.get(s2 - 2); spsMap.put(p, new SmoothPathSegment(pp.get(s2 - 3), p, pp.get(s2 - 1), null)); } arrow = new PathArrow(spsMap.get(pp.get(0))); } } else { if (s2 >= 2) { for (int i = 0; i < s2 - 1; i++) { PathPoint p = path.points.get(i); lpsMap.put(p, new LinearPathSegment(p, path.points.get(i + 1))); } if (path.properties.get(PPath.CLOSED)) { PathPoint p = path.points.get(s2 - 1); lpsMap.put(p, new LinearPathSegment(p, path.points.get(0))); } arrow = new PathArrow(null); } } } private static final EnumSet<Show> ROOM_SHOW = EnumSet.of(Show.BACKGROUNDS, Show.INSTANCES, Show.TILES, Show.FOREGROUNDS); private void setRoom(Room r) { if (roomVisual == null ? r == null : roomVisual.room == r) return; roomVisual = r == null ? null : new RoomVisual(container, r, ROOM_SHOW); put(ROOM_LAYER, roomVisual); } private class PathPropertyListener extends PropertyUpdateListener<PPath> { @Override public void updated(PropertyUpdateEvent<PPath> e) { switch (e.key) { case SNAP_X: gridVisual.setWidth((Integer) path.get(PPath.SNAP_X)); repaint(); break; case SNAP_Y: gridVisual.setHeight((Integer) path.get(PPath.SNAP_Y)); repaint(); break; case PRECISION: for (SmoothPathSegment s : spsMap.values()) s.validate(); arrow.validate(); break; case CLOSED: case SMOOTH: // TODO: Optimize updatePointList(); break; case BACKGROUND_ROOM: ResourceReference<Room> r = path.get(PPath.BACKGROUND_ROOM); setRoom(r == null ? null : r.get()); } } } private class PointListListener implements UpdateListener { public void updated(UpdateEvent e) { ListUpdateEvent lue = (ListUpdateEvent) e; switch (lue.type) { case ADDED: // for (int i = lue.fromIndex; i <= lue.toIndex; i++) // { // PathPoint p = path.points.get(i); // PointVisual v = new PointVisual(p); // pvMap.put(p,v); // pvList.add(i,v); // } // break; case REMOVED: // for (int i = lue.toIndex; i <= lue.fromIndex; i++) // { // PathPoint p = path.points.get(i); // pvMap.remove(p); // pvList.remove(i).box.remove(); // } // break; case CHANGED: updatePointList(); } } } private class PathEditorPropertyValidator implements PropertyValidator<PPathEditor> { public Object validate(PPathEditor k, Object v) { switch (k) { case SELECTED_POINT: PointVisual pv = pvMap.get(properties.get(k)); if (pv != null) { if (v == pv.point) break; pv.setSelected(false); } pv = pvMap.get(v); if (pv == null) { if (pvList.size() < 1) return null; pv = pvList.get(0); } pv.setSelected(true); return pv.point; case SHOW_GRID: if (v instanceof Boolean) put(GRID_LAYER, (Boolean) v ? gridVisual : null); else return properties.get(k); break; } return v; } } }
public class Background extends Resource<Background, Background.PBackground> { private BufferedImage backgroundImage = null; private SoftReference<BufferedImage> imageCache = null; public enum PBackground { TRANSPARENT, SMOOTH_EDGES, PRELOAD, USE_AS_TILESET, TILE_WIDTH, TILE_HEIGHT, H_OFFSET, V_OFFSET, H_SEP, V_SEP } private static final EnumMap<PBackground, Object> DEFS = PropertyMap.makeDefaultMap(PBackground.class, false, false, false, false, 16, 16, 0, 0, 0, 0); public Background() { this(null); } public Background(ResourceReference<Background> r) { super(r); setName(Prefs.prefixes.get(Kind.BACKGROUND)); } public Background makeInstance(ResourceReference<Background> r) { return new Background(r); } public BufferedImage getDisplayImage() { if (backgroundImage == null) return null; BufferedImage bi; if (imageCache != null) { bi = imageCache.get(); if (bi != null) { return bi; } } bi = backgroundImage; if (get(PBackground.TRANSPARENT)) bi = Util.getTransparentIcon(bi); imageCache = new SoftReference<BufferedImage>(bi); return bi; } protected void postCopy(Background dest) { dest.backgroundImage = Util.cloneImage(backgroundImage); } public Kind getKind() { return Kind.BACKGROUND; } @Override protected void fireUpdate() { if (imageCache != null) imageCache.clear(); super.fireUpdate(); } public BufferedImage getBackgroundImage() { return backgroundImage; } public void setBackgroundImage(BufferedImage backgroundImage) { this.backgroundImage = backgroundImage; fireUpdate(); } public int getWidth() { return backgroundImage == null ? 0 : backgroundImage.getWidth(); } public int getHeight() { return backgroundImage == null ? 0 : backgroundImage.getHeight(); } @Override protected PropertyMap<PBackground> makePropertyMap() { return new PropertyMap<PBackground>(PBackground.class, this, DEFS); } }