taskman taskman - 1 month ago 14
Java Question

I want to create a fill pattern for a slice for 3D printing in JavaFX/Java

I create SVG slices from STL files. The SVG files consist of many many lines. The short version is I can't use the built in functionality of JavaFX to create a fill pattern of the SVG. The reason was something to do with the SVG not being created correctly, because the code does a move and then line and then move and then line. To use the fill feature I think you need to have a single move and then many lines. Too long ago to remember the exact issue.

So this is what I am doing now to solve the issue
I store the fill pattern, honeycomb fill pattern, in a file. This can ofcourse be stored in memory, but it is for testing. I then store a the hollow slice in a second file. I need to combine the two so that only the parts of the honeycomb that are inside the layer/slice display and the rest is removed. The honeycomb should be white and the slice should also be white. The plan was to make the honeycomb white when I copy it over to the slice.

I tried two methods.

The first method I tried was to copy pixels from the fill pattern to the layer/slice. I tried to find where the lines are of the layer and then try to figure out where the inside and the outside is. I failed. Code will be attached to the bottom of this question.

The second way I tried was to use the blendMode, but none of the modes seem to do what I need it to do.

First method code
I tried saving the honeycomb and layer in different colours, I tried saving them on top of each other and then remove honeycomb. Here you can see I am trying to figure out what is inside and what is outside, but I couldn't make it work

package javaapplication3;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javax.imageio.ImageIO;

public class JavaApplication3 {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
try {
File file = new File("C:\\Temp\\Mandibular\\MandibularsolidNOreferencepoints.gizmofill0.gizmoslice.png");
BufferedImage bufImage = ImageIO.read(file);

WritableImage writableImage = SwingFXUtils.toFXImage(bufImage, null);

PixelReader pixelReader = writableImage.getPixelReader();
int width = (int) writableImage.getWidth();
int height = (int) writableImage.getHeight();

WritableImage dest = new WritableImage(width, height);
PixelWriter writer = dest.getPixelWriter();
boolean isOnLine = false;
boolean previousIsOnLine = false;
boolean isInside = false;
for (int x = 0; x < width; x++) {
for (int y = 00; y < height; y++) {
// reading a pixel from src image,
// then writing a pixel to dest image
Color color = pixelReader.getColor(x, y);
double red = color.getRed();
double green = color.getGreen();
double blue = color.getBlue();

if (red == 0 && green == 1 && blue == 0) {
//isInside = !isInside;
isOnLine = true;
} else {
previousIsOnLine = isOnLine;
isOnLine = false;
}

if (previousIsOnLine) {
isInside = !isInside;
}

/*if (isOnLine && red == 1 && green == 0 && blue == 0) {
isInside = true;
isOnLine = false;
}*/

if (isOnLine || isInside) {
writer.setColor(x, y, color);
}
/*if (isOnLine || isInside && (red > 0 && green == 0 && blue == 0)) {
writer.setColor(x, y, Color.WHITE);
}*/

}
}


File outputFile = new File("C:\\Temp\\Mandibular\\test.png");
ImageIO.write(SwingFXUtils.fromFXImage(dest, null), "png", outputFile);

} catch (IOException ex) {
Logger.getLogger(JavaApplication3.class.getName()).log(Level.SEVERE, null, ex);
}
}

}


Second method using blend
I tried the different blendModes and ended with the last one layerView.setBlendMode(BlendMode.SRC_ATOP); that clearly doesn't do what I need. I just tested each one to see what might work

package javafxapplication15;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.imageio.ImageIO;

public class JavaFXApplication15 extends Application {
private void doWork() {
try {
// TODO code application logic here
File file = new File("C:\\Temp\\Mandibular\\MandibularsolidNOreferencepoints.gizmofill0.gizmoslice.png");
BufferedImage layerImage = ImageIO.read(file);

file = new File("C:\\Temp\\Mandibular\\MandibularsolidNOreferencepoints.gizmofill-1.gizmoslice.png");
BufferedImage fillImage = ImageIO.read(file);

WritableImage layerWritableImage = SwingFXUtils.toFXImage(layerImage, null);
WritableImage fillWritableImage = SwingFXUtils.toFXImage(fillImage, null);

WritableImage temp = copyImageOntoFillPattern(fillWritableImage, layerWritableImage);


File outputFile = new File("C:\\Temp\\Mandibular\\test.png");
ImageIO.write(SwingFXUtils.fromFXImage(temp, null), "png", outputFile);
} catch (IOException ex) {
Logger.getLogger(JavaFXApplication15.class.getName()).log(Level.SEVERE, null, ex);
}

}
private WritableImage copyImageOntoFillPattern(final WritableImage fillPatternImage, final WritableImage sourceImage) {

ImageView fillPatternView = new ImageView(fillPatternImage);
ImageView layerView = new ImageView(sourceImage);
layerView.setBlendMode(BlendMode.SRC_ATOP);

Group blend = new Group(fillPatternView, layerView);

blend.snapshot(null, sourceImage);
SnapshotParameters param = new SnapshotParameters();
final WritableImage snapshotCombined = blend.snapshot(param, null);
return snapshotCombined;
}

@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {

@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
doWork();
}
});

StackPane root = new StackPane();
root.getChildren().add(btn);

Scene scene = new Scene(root, 300, 250);

primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}

}


Layer

Honeycomb fill pattern and layer

Answer

I clicked around for maybe an hour changing a .fxml file

Here is the contents of the file, I need to convert it to code that I can run, but shouldn't be too hard now.

The layer needed to be filled, the fill colour doesn't seem to matter. I made it green so that it is visible on the website here, but I will probably create it as white when I create it in my slicer. I also made the honeycomb red so that it is easier to see. I am showing an image of SceneBuilder where it worked

I will now just create the honeycomb as white and the layer as solid white and things will be great

Slice or Layer

Honeycomb

Combination in Scene Builder

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>


<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Group blendMode="HARD_LIGHT">
         <children>
            <ImageView blendMode="DIFFERENCE" cache="true" fitHeight="418.0" fitWidth="591.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@outline.png" />
               </image>
            </ImageView>
            <ImageView blendMode="SRC_ATOP" fitHeight="451.0" fitWidth="688.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@honeycomb.png" />
               </image>
               <effect>
                  <Blend mode="ADD" />
               </effect>
            </ImageView>
         </children>
      </Group>
   </children>
</AnchorPane>