Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Voronoi work #696

Merged
merged 14 commits into from
Aug 1, 2023
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.marginallyclever</groupId>
<artifactId>Makelangelo</artifactId>
<version>7.39.9</version>
<version>7.40.0</version>
<name>Makelangelo</name>
<description>Makelangelo Software is a Java program that prepares art for CNC plotters. It is especially designed for the Makelangelo Robot.
It pairs really well with Marlin-polargraph, the code in the brain of the robot that receives instructions and moves the motors.</description>
Expand Down
34 changes: 17 additions & 17 deletions src/main/java/com/marginallyclever/convenience/ColorRGB.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,31 @@ public ColorRGB(int pixel) {
}

public void set(int pixel) {
int r = ((pixel >> 16) & 0xff);
int g = ((pixel >> 8) & 0xff);
int b = ((pixel) & 0xff);
set(r, g, b);
this.red = (pixel >> 16) & 0xff;
this.green = (pixel >> 8) & 0xff;
this.blue = (pixel ) & 0xff;
}

public ColorRGB(Color c) {
red = c.getRed();
red = c.getRed();
green = c.getGreen();
blue = c.getBlue();
blue = c.getBlue();
}

public int toInt() {
return ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff);
return (0xff<<24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff);
}

public ColorRGB set(ColorRGB x) {
set(x.red, x.green, x.blue);
return this;
public void set(ColorRGB x) {
this.red = x.red;
this.green = x.green;
this.blue = x.blue;
}

public void set(int red, int green, int blue) {
this.red = Math.max(0, Math.min(255, red));
this.green = Math.max(0, Math.min(255, green));
this.blue = Math.max(0, Math.min(255, blue));
this.red = red;
this.green = green;
this.blue = blue;
}

public void set(ColorHSB hsb) {
Expand All @@ -82,9 +82,9 @@ public ColorRGB add(ColorRGB x) {

public ColorRGB mul(double f) {
return new ColorRGB(
(int) (this.red * f),
(int) (this.green * f),
(int) (this.blue * f));
(int) (f * this.red),
(int) (f * this.green),
(int) (f * this.blue));
}

public float diffSquared(ColorRGB other) {
Expand All @@ -95,7 +95,7 @@ public float diffSquared(ColorRGB other) {
}

public float diff(ColorRGB other) {
return (float) Math.sqrt(diffSquared(other));
return (float)Math.sqrt(diffSquared(other));
}

public String toString() {
Expand Down
19 changes: 9 additions & 10 deletions src/main/java/com/marginallyclever/convenience/ConvexHull.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
public class ConvexHull {
private static final Logger logger = LoggerFactory.getLogger(ConvexHull.class);
private final ArrayList<Vector2d> points = new ArrayList<Vector2d>();
private final ArrayList<Vector2d> points = new ArrayList<>();

public ConvexHull() {}

Expand All @@ -43,7 +43,7 @@ public void clear() {

public Rectangle2D getBounds() {
Rectangle2D bounds = new Rectangle2D.Double();
if(points.size()>0) {
if(!points.isEmpty()) {
Vector2d p = points.get(0);
bounds.setRect(p.x,p.y,0,0);
for(Vector2d p2 : points) {
Expand Down Expand Up @@ -98,21 +98,20 @@ private boolean pointIsOnTheLeft(Vector2d c,Vector2d a,Vector2d b) {
}

/**
* See https://en.wikipedia.org/wiki/Gift_wrapping_algorithm
* @param p
* @throws Exception
* See <a href="https://en.wikipedia.org/wiki/Gift_wrapping_algorithm">Gift wrapping algorithm</a>
* @param p the point to add
*/
private void addPointCarefully(Vector2d p) throws Exception {
private void addPointCarefully(Vector2d p) {
points.add(p);
rebuildHull();
}

private void rebuildHull() {
ArrayList<Vector2d> toKeep = new ArrayList<>();
int hullSize= points.size();
int hullSize = points.size();
if(hullSize<=3) return;

Vector2d pointOnHull = getPointGuaranteedOnEdgeOfHull(points);
Vector2d pointOnHull = getPointGuaranteedOnEdgeOfHull();
Vector2d firstPoint = pointOnHull;
Vector2d endPoint;
do {
Expand All @@ -135,7 +134,7 @@ private void rebuildHull() {
points.addAll(toKeep);
}

private Vector2d getPointGuaranteedOnEdgeOfHull(ArrayList<Vector2d> hull2) {
private Vector2d getPointGuaranteedOnEdgeOfHull() {
// first is left-most point in the set.
Vector2d pointOnHull = points.get(0);
for( Vector2d n : points) {
Expand All @@ -154,7 +153,7 @@ public Vector2d getCenterOfHull() {
Vector2d center = new Vector2d();

int s = points.size();
for(int i=0;i<s;++i) center.add(points.get(i));
for (Vector2d point : points) center.add(point);
center.scale(1.0/(double)s);

return center;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.marginallyclever.convenience;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;

/**
* Displays an {@link BufferedImage} in a {@link JPanel} and allows the user to zoom and pan.
* @author Dan Royer
* @since 7.39.9
*/
public class ResizableImagePanel extends JPanel {
private final BufferedImage image;
private final Point imagePosition = new Point(0, 0);
private double zoomFactor = 1.0;
private Point lastDragPoint;

public ResizableImagePanel(BufferedImage image) {
this.image = image;

// Enable mouse wheel events for zooming
addMouseWheelListener(e -> {
int rotation = e.getWheelRotation();
zoomFactor += rotation * 0.1; // Adjust the zoom speed here if needed
zoomFactor = Math.max(0.1, zoomFactor); // Ensure zoomFactor doesn't go below 0.1
repaint();
});

// Enable mouse events for dragging the image
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
lastDragPoint = e.getPoint();
}
});

addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
int dx = e.getX() - lastDragPoint.x;
int dy = e.getY() - lastDragPoint.y;

// Adjust the translation based on the zoom factor
dx /= zoomFactor;
dy /= zoomFactor;

// Update the last drag point
lastDragPoint = e.getPoint();

// Update the image position
imagePosition.translate(dx, dy);

repaint();
}
});
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);

if (image != null) {
// Calculate the aspect ratio of the image
double imageAspect = (double) image.getWidth() / image.getHeight();

// Get the size of the panel
int panelWidth = getWidth();
int panelHeight = getHeight();

// Calculate the scaled dimensions based on the zoom factor
int drawWidth = (int) (panelWidth * zoomFactor);
int drawHeight = (int) (panelHeight * zoomFactor);

// Adjust the drawing size to maintain the aspect ratio
if (panelWidth > panelHeight) {
drawHeight = (int) (drawWidth / imageAspect);
} else {
drawWidth = (int) (drawHeight * imageAspect);
}

// Calculate the position to center the image
int x = (panelWidth - drawWidth) / 2;
int y = (panelHeight - drawHeight) / 2;

// Draw the scaled image on the panel
g.drawImage(image, x + imagePosition.x, y + imagePosition.y, drawWidth, drawHeight, this);
}
}

/**
* Displays an image in a resizable window.
*
* @param image The image to display.
* @param title The title of the window.
*/
public static void showImage(BufferedImage image,String title) throws InvalidParameterException {
if (image == null) throw new InvalidParameterException("image cannot be null.");

JFrame frame = new JFrame(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 500);

ResizableImagePanel imagePanel = new ResizableImagePanel(image);
frame.add(imagePanel);

frame.setVisible(true);
imagePanel.revalidate();
imagePanel.repaint();
}

public static void main(String[] args) throws IOException {
// Replace "path_to_your_image.png" with the actual path to your image file
BufferedImage image = ImageIO.read(new File("src/test/resources/test.png"));
showImage(image,"Resizable Image Panel");
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package com.marginallyclever.convenience.voronoi;

import com.marginallyclever.convenience.Point2D;
import org.locationtech.jts.geom.Coordinate;

public class VoronoiCell {
public final Point2D center = new Point2D();
public final Coordinate center = new Coordinate();
public double weight;
public double change;

public VoronoiCell(double x,double y) {
super();
this.center.set(x,y);
set(x,y);
}

public void set(double x, double y) {
this.center.x=x;
this.center.y=y;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,21 @@
* @since 2022-04-08
*/
public class VoronoiTesselator2 {
private final GeometryFactory geometryFactory = new GeometryFactory();
private Coordinate[] coordinates;
private Geometry diagram;

public void setNumHulls(int numHulls) {
coordinates = new Coordinate[numHulls];
}

public void tessellate(List<VoronoiCell> points, Rectangle2D bounds, double tolerance) {
Coordinate[] coordinates = new Coordinate[points.size()];
if(points.size()!=coordinates.length) setNumHulls(points.size());

int i=0;
for(VoronoiCell cell : points) {
coordinates[i++] = new Coordinate(cell.center.x,cell.center.y);
coordinates[i++] = cell.center;
}
GeometryFactory geometryFactory = new GeometryFactory();
MultiPoint multiPoint = geometryFactory.createMultiPointFromCoords(coordinates);

VoronoiDiagramBuilder builder = new VoronoiDiagramBuilder();
Expand All @@ -38,4 +44,5 @@ public int getNumHulls() {
public Polygon getHull(int i) {
return (Polygon)diagram.getGeometryN(i);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public void openFile(String filename) {

if(filename == null || filename.trim().isEmpty()) throw new InvalidParameterException("filename cannot be empty");

if (loader.load(filename)) {
if (loader.onNewFilenameChosen(filename)) {
previewPanel.addListener(loader);
JDialog dialog = new JDialog(mainFrame, Translator.get("LoadFilePanel.title"));
dialog.add(loader);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import javax.swing.event.ChangeListener;
import java.awt.*;

/**
* Slider with two handles.
*/
public class MakeleangeloRangeSlider extends JPanel {
private final RangeSlider rangeSlider;
private final JLabel labelRangeMin = new JLabel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import com.marginallyclever.makelangelo.makeart.imagefilter.ImageFilter;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.util.Arrays;

/**
* TransformedImage is a {@link BufferedImage}, with a transformation matrix on top.
Expand Down Expand Up @@ -119,9 +119,8 @@ public int sample(double x0, double y0, double x1, double y1) {
}

double [] m = new double[w*h];
for(int i=0;i<m.length;++i) {
m[i]=1;
}
Arrays.fill(m, 1);

// bottom edge
if(bottom<y0) {
double yWeightStart = y0-bottom;
Expand Down Expand Up @@ -193,9 +192,8 @@ public int sample1x1Unchecked(double x, double y) {
int sampleX = getTransformedX(x);
int sampleY = getTransformedY(y);

Color c = new Color(sourceImage.getRGB(sampleX, sampleY));
int c2 = ImageFilter.decodeColor(c) & 0xFF; // clamp to 0..255
return c2;
int c2 = sourceImage.getRGB(sampleX, sampleY);
return ImageFilter.decode32bit(c2) & 0xFF;
}

// sample the pixels from x0,y0 (top left) to x1,y1 (bottom right)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public Converter_CMYK_Circles() {
});
add(maxCircleSize);

add(new SelectReadOnlyText("note",Translator.get("ConverterCMYKNote")));
add(new SelectReadOnlyText("note",Translator.get("Converter_CMYK_Crosshatch.Note")));
}

@Override
Expand Down
Loading
Loading