From f07767dcfd21620452d48709352fb339a79a9969 Mon Sep 17 00:00:00 2001 From: philippe lhardy Date: Sun, 11 Oct 2020 22:14:10 +0200 Subject: [PATCH] fix normals for vertices - now stl output can be porcessed directly by slic3r without intermediate fix wihtin blender - created Wall3dStream instead of keeping all within Wall3d - can save only stl output ( 'Save stl' ) --- .../games/maze/gui/Display.java | 51 +++-- .../org/artisanlogiciel/games/stl/Wall3d.java | 184 ++++++++---------- .../games/stl/Wall3dStream.java | 102 ++++++++++ 3 files changed, 216 insertions(+), 121 deletions(-) create mode 100644 java/org/artisanlogiciel/games/stl/Wall3dStream.java diff --git a/java/org/artisanlogiciel/games/maze/gui/Display.java b/java/org/artisanlogiciel/games/maze/gui/Display.java index a25f413..ae7b2be 100644 --- a/java/org/artisanlogiciel/games/maze/gui/Display.java +++ b/java/org/artisanlogiciel/games/maze/gui/Display.java @@ -33,6 +33,7 @@ import javax.swing.event.ChangeListener; import org.artisanlogiciel.games.*; import org.artisanlogiciel.games.stl.Wall3d; +import org.artisanlogiciel.games.stl.Wall3dStream; import org.artisanlogiciel.util.UTF8Control; import org.artisanlogiciel.graphics.Drawing; @@ -332,7 +333,7 @@ public class Display extends JFrame { final JButton saveSvgButton = new JButton(labels.getString("save") + " svg"); Action saveSvgAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { - writeSentence("save png"); + writeSentence("save svg"); setMazeName(saveName.getText()); saveSvg(); } @@ -359,12 +360,25 @@ public class Display extends JFrame { } }; saveImcButton.addActionListener(saveImcAction); + final JButton saveStlButton = new JButton(labels.getString("save") + " stl"); + Action saveStlAction = new AbstractAction() { + public void actionPerformed(ActionEvent evt) { + // + System.out.println("save stl"); + setMazeName(saveName.getText()); + MazeParamsFixed p = (MazeParamsFixed) params; + saveStl(p, model); + } + }; + saveStlButton.addActionListener(saveStlAction); + JMenu saveMenu = new JMenu("Save"); saveMenu.add(saveName); saveMenu.add(saveSvgButton); saveMenu.add(savePngButton); saveMenu.add(saveButton); + saveMenu.add(saveStlButton); saveMenu.add(saveImcButton); return saveMenu; @@ -997,6 +1011,24 @@ public class Display extends JFrame { Display display = new Display(model, W, H, params); } + public static void saveStl(MazeParamsFixed params, LabyModel model) { + File outfile = new File(params.getSaveDir(), params.getName() + ".stl"); + if (!outfile.exists()) { + System.out.println("Saving to " + outfile + " ..."); + try { + FileOutputStream out = new FileOutputStream(outfile); + new Wall3dStream(params.getName(), model, out, false).stream(10,10,10); + out.close(); + System.out.println("... Done."); + } catch (IOException io) { + io.printStackTrace(System.err); + } + } else { + System.out.println("" + outfile + " already exists"); + } + + } + public static void save(MazeParamsFixed params, LabyModel model) { File outfile = new File(params.getSaveDir(), params.getName() + ".raw"); if (!outfile.exists()) { @@ -1013,24 +1045,9 @@ public class Display extends JFrame { } else { System.out.println("" + outfile + " already exists"); } - outfile = new File(params.getSaveDir(), params.getName() + ".stl"); - if (!outfile.exists()) { - System.out.println("Saving to " + outfile + " ..."); - try { - FileOutputStream out = new FileOutputStream(outfile); - Wall3d.streamWallsOut(params.getName(), model, out); - out.flush(); - out.close(); - System.out.println("... Done."); - } catch (IOException io) { - io.printStackTrace(System.err); - } - } else { - System.out.println("" + outfile + " already exists"); - } - } + public static void main(String pArgs[]) { LabyModel model = null; int W = 600; diff --git a/java/org/artisanlogiciel/games/stl/Wall3d.java b/java/org/artisanlogiciel/games/stl/Wall3d.java index 93837b1..7f77f3d 100644 --- a/java/org/artisanlogiciel/games/stl/Wall3d.java +++ b/java/org/artisanlogiciel/games/stl/Wall3d.java @@ -11,25 +11,62 @@ import java.io.IOException; /** * Wall3d to create walls in 3d for stl conversion South, West North East... + * + * wall3D will create a rectangular cuboid with 2 triangle vertex for each face **/ public class Wall3d { + // 4 triangles in 2 dim space reused 3 times - final static int BASE[][][] = {{{0, 0}, {1, 0}, {0, 1}}, {{1, 0}, {1, 1}, {0, 1}}, - {{0, 0}, {1, 0}, {1, 1}}, {{0, 0}, {1, 1}, {0, 1}}}; + // in facts order matters... + final static int BASE[][][] = { + // lower left + {{0, 0}, {1, 0}, {0, 1}}, + // higher right + {{1, 0}, {1, 1}, {0, 1}}, + // lower right + {{0, 0}, {1, 0}, {1, 1}}, + // higher left + {{0, 0}, {1, 1}, {0, 1}}}; final static short X = 1; final static short Y = 2; final static short Z = 4; // final static short AXIS[][]= {{X,Y},{-Z,Y},{X,Y},{Z,Y},{X,-Z},{X,-Z}}; - final static short AXIS[][] = {{X, Y, 0}, {Z, Y, 0}, {X, Y, 1}, {Z, Y, 1}, {X, Z, 0}, {X, Z, 1}}; - public final static Wall3d South = new Wall3d(10, 1, 10, 0, 0, 0); - public final static Wall3d West = new Wall3d(1, 10, 10, 0, 0, 0); - public final static Wall3d North = new Wall3d(10, 1, 10, 0, 10, 0); - public final static Wall3d East = new Wall3d(1, 10, 10, 10, 0, 0); - public final static Wall3d HighGround = new Wall3d(10, 10, 3, 0, 0, 0); - public final static Wall3d LowGround = new Wall3d(10, 10, 2, 0, 0, 0); + // [face][axis] + final static short AXIS[][] = { + // face 0 (x,y) back + {X, Y, 0}, + // face 1 (z,y) left + {Z, Y, 0}, + // face 2 (x,y,1) + {X, Y, 1}, + // face 3 + {Z, Y, 1}, + // face 4 + {X, Z, 0}, + // face 5 + {X, Z, 1}}; + + final static String normalx = "1.0 0.0 0.0"; + final static String normaly = "0.0 1.0 0.0"; + final static String normalz = "0.0 0.0 1.0"; + final static String normalmx = "-1.0 0.0 0.0"; + final static String normalmy = "0.0 -1.0 0.0"; + final static String normalmz = "0.0 0.0 -1.0"; + + // final static short NORMAL[] = {Z,X,Z,X,Y,Y}; + + final static String normalstr[] = + { normalmz, normalx, + normalz, normalmx, + normaly, normalmy + }; + + boolean reverse_vertex[] = { + true,false,false,true,false,true + }; int triangle[][][] = null; @@ -46,18 +83,27 @@ public class Wall3d { triangle = new int[12][3][3]; int[] factor = {xl, yl, zl}; int[] translate = {dx, dy, dz}; - for (int i = 0; i < 12; i++) { - // point in a triangle + for (int facet = 0; facet < 12; facet++) { + // point in a triangle / facet for (int p = 0; p < 3; p++) { short uaxis = 0; + // a square face is two facets. + int face = facet / 2; + // first two axis ( projected on x,y,z depending on AXIS[face][] ) for (int axis = 0; axis < 2; axis++) { - short caxis = AXIS[i / 2][axis]; + short caxis = AXIS[face][axis]; if (caxis > 0) { f = 1; } else if (caxis < 0) { + // wait wait in AXIS there is no negative value ... + // so we never enter here, what was the purpose ? might be history. f = -1; caxis = (short) -caxis; } + // what if caxis == 0 ? f is kept as its previous value ? + // which is 1 in facts due to AXIS[..][0] > 0 + + // uaxis keep track of used axes for this face to find last missing axis uaxis |= caxis; if (caxis == X) { caxis = 0; @@ -66,22 +112,20 @@ public class Wall3d { } else { caxis = 2; } - // if ( f == 0 ) - // { - // System.out.println("ERROR"); - // } - // System.out.println("i " + i + " p " + p + " a " + caxis + - // " , " + BASE[i%4][p][axis] ); - triangle[i][p][caxis] = translate[caxis] + BASE[i % 4][p][axis] * f * factor[caxis]; + triangle[facet][p][caxis] = translate[caxis] + BASE[facet % 4][p][axis] * f * factor[caxis]; } + // last remaining axis if ((uaxis & X) == 0) { + // no X was used => X uaxis = 0; } else if ((uaxis & Y) == 0) { + // X was used byt not Y => Y uaxis = 1; } else { + // default X, and Y were used => Z uaxis = 2; } - triangle[i][p][uaxis] = translate[uaxis] + AXIS[i / 2][2] * factor[uaxis]; + triangle[facet][p][uaxis] = translate[uaxis] + AXIS[facet / 2][2] * factor[uaxis]; } } } @@ -90,11 +134,11 @@ public class Wall3d { int[] translate = {dx, dy, dz}; int t[][][] = new int[12][3][3]; - for (int i = 0; i < 12; i++) { + for (int facet = 0; facet < 12; facet ++) { // point in a triangle for (int p = 0; p < 3; p++) { for (int axis = 0; axis < 3; axis++) { - t[i][p][axis] = translate[axis] + triangle[i][p][axis]; + t[facet][p][axis] = translate[axis] + triangle[facet][p][axis]; } } } @@ -107,14 +151,23 @@ public class Wall3d { **/ public String toString() { String s = ""; - for (int t = 0; t < 12; t++) { - s += "facet normal 0 0 0\nouter loop\n"; + for (int facet = 0; facet < 12; facet++) { + int face = facet / 2; + // most software don't care normal but just rely on vertex order ( right hand ). + String normal = normalstr[face]; + s += "facet normal " + normal + "\nouter loop\n"; + boolean reverse = reverse_vertex[face]; for (int p = 0; p < 3; p++) { s += "vertex"; + // p that accept right hand + int rhp = p; + if ( reverse ) + { + // reverse 2 and 1 ; 0 => 0, 1 => 2 , 2 => 1 + rhp = ( p * 5 ) % 3; + } for (int a = 0; a < 3; a++) { - // s+=" t "+ t + " p " + p + " a " + a + "=" + - // triangle[t][p][a]; - s += " " + triangle[t][p][a]; + s += " " + triangle[facet][rhp][a]; } s += "\n"; } @@ -123,81 +176,4 @@ public class Wall3d { return s; } - public static void prepare() { - System.out.println(South.toString()); - System.out.println(East.toString()); - System.out.println(North.toString()); - System.out.println(West.toString()); - } - - public static void streamWallsOut(String name, LabyModel provider, OutputStream stream) throws IOException { - int width = provider.getWidth(); - int height = provider.getHeight(); - int xl = 10; - int yl = 10; - int zl = 10; - - // WARNING DOWN - UP reversed ( in 2D Y is oriented to lower, in 3D it - // is to upper ). - stream.write(("solid " + name + "\n").getBytes()); - for (int x = 0; x < width; x++) { - short walls = provider.getWalls(x, 0); - if ((walls & Brick.UP) != 0) { - stream.write(new Wall3d(South, x * xl, 0, 0).toString().getBytes()); - } - if ((walls & Brick.LEFT) != 0) { - stream.write(new Wall3d(West, x * xl, 0, 0).toString().getBytes()); - } - } - - for (int y = 0; y < height; y++) { - short walls = provider.getWalls(0, y); - if ((walls & Brick.LEFT) != 0) { - stream.write(new Wall3d(West, 0, y * yl, 0).toString().getBytes()); - } - for (int x = 0; x < width; x++) { - // south and east - walls = provider.getWalls(x, y); - if ((walls & Brick.DOWN) != 0) { - stream.write(new Wall3d(North, x * xl, y * yl, 0).toString().getBytes()); - } - if ((walls & Brick.RIGHT) != 0) { - stream.write(new Wall3d(East, x * xl, y * yl, 0).toString().getBytes()); - } - short path = provider.getPath(x, y); - if ((path & LabyModel.SOLVED) == LabyModel.SOLVED) - // if ( (walls & ( Brick.GOAL | Brick.ENTRY ) ) == 0 ) - { - // if ( ( (x+y) % 2) == 0 ) - - stream.write(new Wall3d(LowGround, x * xl, y * yl, 0).toString().getBytes()); - } else { - stream.write(new Wall3d(HighGround, x * xl, y * yl, 0).toString().getBytes()); - } - } - } - - stream.write("endsolid wall\n\n".getBytes()); - } - - public static void main(String args[]) { - - Scanner console = new Scanner(System.in); - int xl = console.nextInt(); - int yl = console.nextInt(); - int zl = console.nextInt(); - - int dx = console.nextInt(); - int dy = console.nextInt(); - int dz = console.nextInt(); - - String s = "solid wall\n"; - - Wall3d instance = new Wall3d(xl, yl, zl, dx, dy, dz); - - prepare(); - - s += "endsolid wall\n\n"; - System.out.println(s); - } } diff --git a/java/org/artisanlogiciel/games/stl/Wall3dStream.java b/java/org/artisanlogiciel/games/stl/Wall3dStream.java new file mode 100644 index 0000000..a4c4f6d --- /dev/null +++ b/java/org/artisanlogiciel/games/stl/Wall3dStream.java @@ -0,0 +1,102 @@ +package org.artisanlogiciel.games.stl; + +import org.artisanlogiciel.games.Brick; +import org.artisanlogiciel.games.LabyModel; + +import java.io.IOException; +import java.io.OutputStream; + +public class Wall3dStream +{ + + public final static Wall3d South = new Wall3d(10, 1, 10, 0, 0, 0); + public final static Wall3d West = new Wall3d(1, 10, 10, 0, 0, 0); + public final static Wall3d North = new Wall3d(10, 1, 10, 0, 10, 0); + public final static Wall3d East = new Wall3d(1, 10, 10, 10, 0, 0); + public final static Wall3d HighGround = new Wall3d(10, 10, 3, 0, 0, 0); + public final static Wall3d LowGround = new Wall3d(10, 10, 2, 0, 0, 0); + + String name; + LabyModel provider; + OutputStream stream; + boolean reverse; + + /** + * reverse : means that resolved path will be HighGround actual making maze more difficult to play with + * + * @param name + * @param provider + * @param stream + * @param reverse + */ + public Wall3dStream(String name, LabyModel provider, OutputStream stream, boolean reverse) + { + this.name = name; + this.provider = provider; + this.stream = stream; + this.reverse = reverse; + } + + public static void prepare() { + System.out.println(South.toString()); + System.out.println(East.toString()); + System.out.println(North.toString()); + System.out.println(West.toString()); + } + + private void writeWall3D(Wall3d wall3d) + throws IOException + { + stream.write(wall3d.toString().getBytes()); + } + + public void stream(int xl, int yl, int zl) throws IOException { + int width = provider.getWidth(); + int height = provider.getHeight(); + + // WARNING DOWN - UP reversed ( in 2D Y is oriented to lower, in 3D it + // is to upper ). + stream.write(("solid " + name + "\n").getBytes()); + for (int x = 0; x < width; x++) { + short walls = provider.getWalls(x, 0); + if ((walls & Brick.UP) != 0) { + writeWall3D(new Wall3d(South, x * xl, 0, 0)); + } + if ((walls & Brick.LEFT) != 0) { + writeWall3D(new Wall3d(West, x * xl, 0, 0)); + } + } + + for (int y = 0; y < height; y++) { + short walls = provider.getWalls(0, y); + if ((walls & Brick.LEFT) != 0) { + writeWall3D(new Wall3d(West, 0, y * yl, 0)); + } + for (int x = 0; x < width; x++) { + // south and east + walls = provider.getWalls(x, y); + if ((walls & Brick.DOWN) != 0) { + writeWall3D(new Wall3d(North, x * xl, y * yl, 0)); + } + if ((walls & Brick.RIGHT) != 0) { + writeWall3D(new Wall3d(East, x * xl, y * yl, 0)); + } + short path = provider.getPath(x, y); + + // where resolved path is leaked to stl model. + Wall3d ground = reverse ? LowGround : HighGround; + if ((path & LabyModel.SOLVED) == LabyModel.SOLVED) + // if ( (walls & ( Brick.GOAL | Brick.ENTRY ) ) == 0 ) + { + // if ( ( (x+y) % 2) == 0 ) + ground = reverse ? HighGround : LowGround; + } + writeWall3D(new Wall3d(ground, x * xl, y * yl, 0)); + } + } + + stream.write("endsolid wall\n\n".getBytes()); + + stream.flush(); + } +}