From 525071fca4fe5ddd5aa83de6d57476b723cc7011 Mon Sep 17 00:00:00 2001 From: Speiger Date: Tue, 5 Aug 2025 00:20:21 +0200 Subject: [PATCH] Small fixes and additions - Added: Bulk Marking via Defining an area. - Changed: Moved the Application Starter to a dedicated class - Fixed a bug introduced by Meduris --- build.gradle | 2 +- .../speiger/src/MarioKartWorldTracker.java | 210 ++++++++++++++ src/main/java/speiger/src/data/Registry.java | 4 +- src/main/java/speiger/src/ui/MapPanel.java | 262 +++++------------- src/main/java/speiger/src/ui/Marker.java | 6 + 5 files changed, 292 insertions(+), 192 deletions(-) create mode 100644 src/main/java/speiger/src/MarioKartWorldTracker.java diff --git a/build.gradle b/build.gradle index 69db43f..e7994ab 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { archivesBaseName = "Mario Kart World Tracker" version = '1.0.1' -var mainClassName = 'speiger.src.ui.MapPanel' +var mainClassName = 'speiger.src.MarioKartWorldTracker' repositories { mavenCentral() diff --git a/src/main/java/speiger/src/MarioKartWorldTracker.java b/src/main/java/speiger/src/MarioKartWorldTracker.java new file mode 100644 index 0000000..6bc9973 --- /dev/null +++ b/src/main/java/speiger/src/MarioKartWorldTracker.java @@ -0,0 +1,210 @@ +package speiger.src; + +import java.awt.BorderLayout; +import java.awt.Checkbox; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.awt.event.ActionListener; +import java.awt.event.ItemListener; +import java.io.BufferedReader; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.time.Duration; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.prefs.Preferences; + +import javax.swing.ImageIcon; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.KeyStroke; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +import speiger.src.data.CollectableType; +import speiger.src.data.Registry; +import speiger.src.ui.DownloadTask; +import speiger.src.ui.MapPanel; +import speiger.src.ui.VerticalFlowLayout; + +public class MarioKartWorldTracker { + private static final JFileChooser CHOOSER = new JFileChooser(new File(Preferences.userRoot().node(MapPanel.class.getName()).get("Last Folder", new File(".").getAbsolutePath()))); + private static Checkbox P_SWITCH; + private static Checkbox MEDALS; + private static Checkbox PANEL; + private static Checkbox CHUCKS; + + public static void main(String...args) { + Toolkit.getDefaultToolkit().setDynamicLayout(false); + JFrame frame = new JFrame(); + frame.setIconImage(new ImageIcon(MapPanel.class.getResource("/assets/images/icon.png")).getImage()); + frame.setLayout(new BorderLayout()); + frame.setBounds(0, 0, 800, 600); + frame.setTitle("Mario Kart World Progress Tracker"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setLocationRelativeTo(null); + MapPanel panel = new MapPanel(frame); + frame.add(panel, BorderLayout.CENTER); + JMenuBar bar = new JMenuBar(); + frame.setJMenuBar(bar); + JMenu menu = bar.add(new JMenu("File")); + menu.add(item("Import Save", T -> { + Preferences prefs = Preferences.userRoot().node(MapPanel.class.getName()); + if(CHOOSER.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) { + File file = CHOOSER.getSelectedFile(); + if(!file.getName().endsWith(".json")) return; + try(BufferedReader reader = Files.newBufferedReader(file.toPath())) { + List ids = new ArrayList<>(); + for(JsonElement element : JsonParser.parseReader(reader).getAsJsonObject().getAsJsonArray("completed")) { + ids.add(UUID.fromString(element.getAsString())); + } + Registry.INSTANCE.importSave(ids); + panel.onImported(); + } + catch(Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(frame, "Importing caused an error \n"+e.toString(), "Importing Error", JOptionPane.ERROR_MESSAGE); + } + + prefs.put("Last Folder", file.getParent()); + } + }, null)); + menu.add(item("Export Save", T -> { + Preferences prefs = Preferences.userRoot().node(MapPanel.class.getName()); + if(CHOOSER.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) { + File file = CHOOSER.getSelectedFile(); + String name = file.getName(); + int index = name.lastIndexOf("."); + if(index > -1) name = name.substring(0, index); + name += ".json"; + Path save = Registry.getOrigin().resolve("data/save.json"); + if(Files.notExists(save)) return; + + try { Files.copy(save, file.toPath().getParent().resolve(name), StandardCopyOption.REPLACE_EXISTING); } + catch(Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(frame, "Exporting caused an error \n"+e.toString(), "Exporting Error", JOptionPane.ERROR_MESSAGE); + } + + prefs.put("Last Folder", file.getParent()); + + } + }, null)); + menu.add(item("Download all Previews", T -> { + if(JOptionPane.showConfirmDialog(frame, "Are you sure you want to download all missing images?", "Download Images", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + new DownloadTask(frame); + } + }, null)); + menu.add(item("Credits", T -> { + JOptionPane.showMessageDialog(frame, "Credits Go to:\nhttps://mkw.techtangents.net and\nhttps://www.gamerguides.com/mario-kart-world/maps/world \nwhich are used as Resource Providers", "Credits", JOptionPane.INFORMATION_MESSAGE); + }, null)); + + JMenu controls = new JMenu("Controls"); + controls.add("Left Button: Toggle Completion/Finish Bulk Action"); + controls.add("Right Button: Show Location"); + controls.add("Left Button Dragging: Move Map"); + controls.add("Mouse Scrolling: Zoom"); + controls.add("Press 'm' when mouse is on map to start bulk action"); + + + JMenu bulk = new JMenu("Bulk Actions"); + bulk.add(item("Complete Visible", T -> { + panel.setCompletionState(true); + }, null)); + bulk.add(item("Uncomplete Visible", T -> { + panel.setCompletionState(false); + }, null)); + bulk.add(item("Bulk Mark", T -> { + panel.startBulkMarking(panel.getMousePosition()); + }, KeyStroke.getKeyStroke('m'))); + bar.add(bulk); + JMenu fuckups = new JMenu("I Fucked up"); + fuckups.add(item("Reset Zoom", T -> { + panel.resetZoom(); + }, KeyStroke.getKeyStroke('z'))); + fuckups.add(item("Reset Offset", T -> { + panel.resetMap(); + }, KeyStroke.getKeyStroke('r'))); + + bar.add(fuckups); + bar.add(controls); + + JPanel sideMenu = new JPanel(); + sideMenu.setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 5, 0)); + + sideMenu.add(new JLabel("Filter")); + P_SWITCH = (Checkbox)sideMenu.add(box("P-Switches", T -> onFilterChanged(panel::updateFilter))); + MEDALS = (Checkbox)sideMenu.add(box("Medals", T -> onFilterChanged(panel::updateFilter))); + PANEL = (Checkbox)sideMenu.add(box("Panels", T -> onFilterChanged(panel::updateFilter))); + CHUCKS = (Checkbox)sideMenu.add(box("Chucks", T -> onFilterChanged(panel::updateFilter))); + JLabel PSwitchProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.PSWITCH)+" / "+Registry.INSTANCE.total(CollectableType.PSWITCH)); + JLabel MedalsProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.MEDAL)+" / "+Registry.INSTANCE.total(CollectableType.MEDAL)); + JLabel PanelProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.PANEL)+" / "+Registry.INSTANCE.total(CollectableType.PANEL)); + JLabel ChucksProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.CHUCKS)+" / "+Registry.INSTANCE.total(CollectableType.CHUCKS)); + panel.setProgressListener(() -> { + PSwitchProgress.setText(Registry.INSTANCE.completed(CollectableType.PSWITCH)+" / "+Registry.INSTANCE.total(CollectableType.PSWITCH)); + MedalsProgress.setText(Registry.INSTANCE.completed(CollectableType.MEDAL)+" / "+Registry.INSTANCE.total(CollectableType.MEDAL)); + PanelProgress.setText(Registry.INSTANCE.completed(CollectableType.PANEL)+" / "+Registry.INSTANCE.total(CollectableType.PANEL)); + ChucksProgress.setText(Registry.INSTANCE.completed(CollectableType.CHUCKS)+" / "+Registry.INSTANCE.total(CollectableType.CHUCKS)); + EventQueue.invokeLater(sideMenu::repaint); + }); + + sideMenu.add(new JLabel("P-Switches:")); + sideMenu.add(PSwitchProgress); + sideMenu.add(new JLabel("Medals:")); + sideMenu.add(MedalsProgress); + sideMenu.add(new JLabel("Panels:")); + sideMenu.add(PanelProgress); + sideMenu.add(new JLabel("Chucks:")); + sideMenu.add(ChucksProgress); + + frame.add(sideMenu, BorderLayout.WEST); + + panel.resetMap(); + frame.setVisible(true); + + while(true) { + try { + Registry.INSTANCE.processQueue(); + Thread.sleep(Duration.ofMillis(50)); + } + catch(Exception e) { + e.printStackTrace(); + } + } + } + + private static Checkbox box(String name, ItemListener listener) { + Checkbox box = new Checkbox(name, true); + box.addItemListener(listener); + return box; + } + + private static void onFilterChanged(Consumer> consumer) { + EnumSet types = EnumSet.noneOf(CollectableType.class); + if(P_SWITCH.getState()) types.add(CollectableType.PSWITCH); + if(MEDALS.getState()) types.add(CollectableType.MEDAL); + if(PANEL.getState()) types.add(CollectableType.PANEL); + if(CHUCKS.getState()) types.add(CollectableType.CHUCKS); + consumer.accept(types); + } + + private static JMenuItem item(String name, ActionListener action, KeyStroke stroke) { + JMenuItem item = new JMenuItem(name); + item.addActionListener(action); + item.setAccelerator(stroke); + return item; + } +} diff --git a/src/main/java/speiger/src/data/Registry.java b/src/main/java/speiger/src/data/Registry.java index 3021e6d..5327047 100644 --- a/src/main/java/speiger/src/data/Registry.java +++ b/src/main/java/speiger/src/data/Registry.java @@ -203,12 +203,12 @@ public class Registry { public void markComplete(UUID id) { completed.add(id); - if (bulkOperationActive) save(); + if (!bulkOperationActive) save(); } public void unmarkComplete(UUID id) { completed.remove(id); - if (bulkOperationActive) save(); + if (!bulkOperationActive) save(); } public void setBulkOperation(boolean bulk) { diff --git a/src/main/java/speiger/src/ui/MapPanel.java b/src/main/java/speiger/src/ui/MapPanel.java index 069ebfc..3d46d93 100644 --- a/src/main/java/speiger/src/ui/MapPanel.java +++ b/src/main/java/speiger/src/ui/MapPanel.java @@ -1,64 +1,39 @@ package speiger.src.ui; -import java.awt.BorderLayout; -import java.awt.Checkbox; +import java.awt.Color; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; +import java.awt.Rectangle; import java.awt.RenderingHints; -import java.awt.Toolkit; -import java.awt.event.ActionListener; -import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.geom.AffineTransform; -import java.io.BufferedReader; -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.time.Duration; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; -import java.util.UUID; -import java.util.function.Consumer; -import java.util.prefs.Preferences; import javax.swing.ImageIcon; -import javax.swing.JFileChooser; import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; -import javax.swing.KeyStroke; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; import speiger.src.data.Collectable; import speiger.src.data.CollectableType; import speiger.src.data.Registry; public class MapPanel extends JPanel { - private static final JFileChooser CHOOSER = new JFileChooser(new File(Preferences.userRoot().node(MapPanel.class.getName()).get("Last Folder", new File(".").getAbsolutePath()))); private static final long serialVersionUID = 7639401429824622357L; - private static Checkbox P_SWITCH; - private static Checkbox MEDALS; - private static Checkbox PANEL; - private static Checkbox CHUCKS; - JFrame owner; Runnable progressUpdate; float zoom = 1F; Point offset = new Point(-568, -271); Point lastDrag = null; + Point startPos; + Point lastPos; Image map = new ImageIcon(MapPanel.class.getResource("/assets/images/map.png")).getImage(); Image[] medal = new Image[] { new ImageIcon(MapPanel.class.getResource("/assets/images/medal.png")).getImage(), @@ -80,163 +55,6 @@ public class MapPanel extends JPanel { List visibleMarkers = new ArrayList(); Marker lastMarker = null; - public static void main(String...args) { - Toolkit.getDefaultToolkit().setDynamicLayout(false); - JFrame frame = new JFrame(); - frame.setIconImage(new ImageIcon(MapPanel.class.getResource("/assets/images/icon.png")).getImage()); - frame.setLayout(new BorderLayout()); - frame.setBounds(0, 0, 800, 600); - frame.setTitle("Mario Kart World Progress Tracker"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setLocationRelativeTo(null); - MapPanel panel = new MapPanel(frame); - frame.add(panel, BorderLayout.CENTER); - JMenuBar bar = new JMenuBar(); - frame.setJMenuBar(bar); - JMenu menu = bar.add(new JMenu("File")); - menu.add(item("Import Save", T -> { - Preferences prefs = Preferences.userRoot().node(MapPanel.class.getName()); - if(CHOOSER.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) { - File file = CHOOSER.getSelectedFile(); - if(!file.getName().endsWith(".json")) return; - try(BufferedReader reader = Files.newBufferedReader(file.toPath())) { - List ids = new ArrayList<>(); - for(JsonElement element : JsonParser.parseReader(reader).getAsJsonObject().getAsJsonArray("completed")) { - ids.add(UUID.fromString(element.getAsString())); - } - Registry.INSTANCE.importSave(ids); - panel.onImported(); - } - catch(Exception e) { - e.printStackTrace(); - JOptionPane.showMessageDialog(frame, "Importing caused an error \n"+e.toString(), "Importing Error", JOptionPane.ERROR_MESSAGE); - } - - prefs.put("Last Folder", file.getParent()); - } - }, null)); - menu.add(item("Export Save", T -> { - Preferences prefs = Preferences.userRoot().node(MapPanel.class.getName()); - if(CHOOSER.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) { - File file = CHOOSER.getSelectedFile(); - String name = file.getName(); - int index = name.lastIndexOf("."); - if(index > -1) name = name.substring(0, index); - name += ".json"; - Path save = Registry.getOrigin().resolve("data/save.json"); - if(Files.notExists(save)) return; - - try { Files.copy(save, file.toPath().getParent().resolve(name), StandardCopyOption.REPLACE_EXISTING); } - catch(Exception e) { - e.printStackTrace(); - JOptionPane.showMessageDialog(frame, "Exporting caused an error \n"+e.toString(), "Exporting Error", JOptionPane.ERROR_MESSAGE); - } - - prefs.put("Last Folder", file.getParent()); - - } - }, null)); - menu.add(item("Download all Previews", T -> { - if(JOptionPane.showConfirmDialog(frame, "Are you sure you want to download all missing images?", "Download Images", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - new DownloadTask(frame); - } - }, null)); - menu.add(item("Credits", T -> { - JOptionPane.showMessageDialog(frame, "Credits Go to:\nhttps://mkw.techtangents.net and\nhttps://www.gamerguides.com/mario-kart-world/maps/world \nwhich are used as Resource Providers", "Credits", JOptionPane.INFORMATION_MESSAGE); - }, null)); - - JMenu controls = new JMenu("Controls"); - controls.add("Left Button: Toggle Completion"); - controls.add("Right Button: Show Location"); - controls.add("Left Button Dragging: Move Map"); - controls.add("Mouse Scrolling: Zoom"); - - - JMenu bulk = new JMenu("Bulk Actions"); - bulk.add(item("Complete Visible", T -> { - panel.setCompletionState(true); - }, null)); - bulk.add(item("Uncomplete Visible", T -> { - panel.setCompletionState(false); - }, null)); - bar.add(bulk); - JMenu fuckups = new JMenu("I Fucked up"); - fuckups.add(item("Reset Zoom", T -> { - panel.resetZoom(); - }, KeyStroke.getKeyStroke('z'))); - fuckups.add(item("Reset Offset", T -> { - panel.resetMap(); - }, KeyStroke.getKeyStroke('r'))); - - bar.add(fuckups); - bar.add(controls); - - JPanel sideMenu = new JPanel(); - sideMenu.setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 5, 0)); - - sideMenu.add(new JLabel("Filter")); - P_SWITCH = (Checkbox)sideMenu.add(box("P-Switches", T -> onFilterChanged(panel::updateFilter))); - MEDALS = (Checkbox)sideMenu.add(box("Medals", T -> onFilterChanged(panel::updateFilter))); - PANEL = (Checkbox)sideMenu.add(box("Panels", T -> onFilterChanged(panel::updateFilter))); - CHUCKS = (Checkbox)sideMenu.add(box("Chucks", T -> onFilterChanged(panel::updateFilter))); - JLabel PSwitchProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.PSWITCH)+" / "+Registry.INSTANCE.total(CollectableType.PSWITCH)); - JLabel MedalsProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.MEDAL)+" / "+Registry.INSTANCE.total(CollectableType.MEDAL)); - JLabel PanelProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.PANEL)+" / "+Registry.INSTANCE.total(CollectableType.PANEL)); - JLabel ChucksProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.CHUCKS)+" / "+Registry.INSTANCE.total(CollectableType.CHUCKS)); - panel.progressUpdate = () -> { - PSwitchProgress.setText(Registry.INSTANCE.completed(CollectableType.PSWITCH)+" / "+Registry.INSTANCE.total(CollectableType.PSWITCH)); - MedalsProgress.setText(Registry.INSTANCE.completed(CollectableType.MEDAL)+" / "+Registry.INSTANCE.total(CollectableType.MEDAL)); - PanelProgress.setText(Registry.INSTANCE.completed(CollectableType.PANEL)+" / "+Registry.INSTANCE.total(CollectableType.PANEL)); - ChucksProgress.setText(Registry.INSTANCE.completed(CollectableType.CHUCKS)+" / "+Registry.INSTANCE.total(CollectableType.CHUCKS)); - EventQueue.invokeLater(sideMenu::repaint); - }; - - sideMenu.add(new JLabel("P-Switches:")); - sideMenu.add(PSwitchProgress); - sideMenu.add(new JLabel("Medals:")); - sideMenu.add(MedalsProgress); - sideMenu.add(new JLabel("Panels:")); - sideMenu.add(PanelProgress); - sideMenu.add(new JLabel("Chucks:")); - sideMenu.add(ChucksProgress); - - frame.add(sideMenu, BorderLayout.WEST); - - panel.resetMap(); - frame.setVisible(true); - - while(true) { - try { - Registry.INSTANCE.processQueue(); - Thread.sleep(Duration.ofMillis(50)); - } - catch(Exception e) { - e.printStackTrace(); - } - } - } - - private static Checkbox box(String name, ItemListener listener) { - Checkbox box = new Checkbox(name, true); - box.addItemListener(listener); - return box; - } - - private static void onFilterChanged(Consumer> consumer) { - EnumSet types = EnumSet.noneOf(CollectableType.class); - if(P_SWITCH.getState()) types.add(CollectableType.PSWITCH); - if(MEDALS.getState()) types.add(CollectableType.MEDAL); - if(PANEL.getState()) types.add(CollectableType.PANEL); - if(CHUCKS.getState()) types.add(CollectableType.CHUCKS); - consumer.accept(types); - } - - private static JMenuItem item(String name, ActionListener action, KeyStroke stroke) { - JMenuItem item = new JMenuItem(name); - item.addActionListener(action); - item.setAccelerator(stroke); - return item; - } private Image[] fromType(CollectableType type) { return switch(type) { @@ -275,8 +93,9 @@ public class MapPanel extends JPanel { @Override public void mouseMoved(MouseEvent e) { - Point hover = screenToMap(e.getPoint()); - Marker marker = findMarker(hover, zoom); + lastPos = screenToMap(e.getPoint()); + if(startPos != null) repaint(); + Marker marker = findMarker(lastPos, zoom); if(marker == lastMarker) return; if(lastMarker != null) lastMarker.setHovered(false); lastMarker = marker; @@ -295,6 +114,22 @@ public class MapPanel extends JPanel { @Override public void mouseClicked(MouseEvent e) { + if(startPos != null) { + if(e.getButton() == 1) { + int result = JOptionPane.showOptionDialog(frame, "What do you want to do", "Bulk Marking", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] {"Complete", "Uncomplete", "Cancel"}, "Complete"); + if(result == 2) return; + List markers = getMarkers(getMarkedArea(), zoom); + if(markers.isEmpty()) return; + boolean completion = result == 0; + Registry.INSTANCE.setBulkOperation(true); + markers.forEach(T -> T.setCompletion(completion)); + Registry.INSTANCE.setBulkOperation(false); + startPos = null; + repaint(); + } + return; + } + Point hover = screenToMap(e.getPoint()); Marker marker = findMarker(hover, zoom); if(marker == null) return; @@ -314,6 +149,14 @@ public class MapPanel extends JPanel { addMouseMotionListener(adapter); } + public void setProgressListener(Runnable run) { + this.progressUpdate = run; + } + + public void startBulkMarking(Point point) { + this.startPos = this.screenToMap(point); + } + public void onImported() { markers.forEach(Marker::updateCompletion); EventQueue.invokeLater(this::repaint); @@ -369,7 +212,48 @@ public class MapPanel extends JPanel { } for(Marker marker : visibleMarkers) { marker.drawHover(g2, (float)zoom); - } + } + if(startPos != null && lastPos != null) { + Point start = new Point(startPos); + Point end = new Point(lastPos); + if(start.x > end.x) { + int x = start.x; + start.x = end.x; + end.x = x; + } + if(start.y > end.y) { + int y = start.y; + start.y = end.y; + end.y = y; + } + g2.setColor(Color.BLUE); + g2.drawRect(start.x, start.y, end.x-start.x, end.y-start.y); + } + } + + private Rectangle getMarkedArea() { + Point start = new Point(startPos); + Point end = new Point(lastPos); + if(start.x > end.x) { + int x = start.x; + start.x = end.x; + end.x = x; + } + if(start.y > end.y) { + int y = start.y; + start.y = end.y; + end.y = y; + } + return new Rectangle(start.x, start.y, end.x-start.x, end.y-start.y); + } + + private List getMarkers(Rectangle rect, float scale) { + List markers = new ArrayList<>(); + for(int i = 0,m=visibleMarkers.size();i= point.x - rad && mouse.x <= point.x + rad && mouse.y >= point.y - rad && mouse.y <= point.y + rad; } + public boolean collides(Rectangle rect, float scale) { + int rad = Math.clamp((int)(32 / scale), 5, 32)>>1; + return rect.intersects(point.x-rad, point.y-rad, rad*2, rad*2); + } + public boolean isHovered() { return hovered; }