├── .classpath
├── .project
├── README
├── build.xml
└── src
└── daid
└── sliceAndDaid
├── Layer.java
├── LayerPart.java
├── Model.java
├── Polygon.java
├── Segment2D.java
├── SliceAndDaidMain.java
├── config
├── CraftConfig.java
├── CraftConfigLoader.java
└── Setting.java
├── tool
├── GCodeTool.java
├── PathTool.java
├── PerimeterTool.java
├── SliceTool.java
└── SpeedTool.java
├── ui
├── ConfigWindow.java
├── LogWindow.java
└── PreviewFrame.java
└── util
├── AABBTree.java
├── AABBrect.java
├── GCodeFile.java
├── Logger.java
├── LoggingInterface.java
├── Triangle.java
├── Vector2.java
└── Vector3.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | SliceAndDaid
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | THIS PROJECT IS LONG DEAD AND REPLACED BY CURA, kept for historical reasons!
2 |
3 |
4 |
5 |
6 | WHAT IS IT?
7 | ===========
8 | SliceAndDaid is a 3D slicer written in Java. It's a much faster replacement
9 | for skeinforge.
10 |
11 | It processes STL input files into GCode. Right now it only creates the outline.
12 | It does NOT do infills yet. Which will be the next step.
13 |
14 | However, it currently does so in seconds instead of minutes compared to Skeinforge.
15 |
16 | HOW TO BUILD?
17 | =============
18 | I've provided a simple ant build script.
19 | So running with ant installed should be a simple:
20 | $ ant
21 | $ java -jar SliceAndDaid.jar
22 |
23 | There are no extra dependences, just java. Eclipse project files are also supplied.
24 |
25 | HOW TO USE?
26 | ===========
27 | Change the settings you want, and slice ahead. The default settings should work
28 | on an Ultimaker. These settings are conservative.
29 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/Layer.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid;
2 |
3 | import java.util.HashSet;
4 | import java.util.Vector;
5 |
6 | import daid.sliceAndDaid.config.CraftConfig;
7 | import daid.sliceAndDaid.util.AABBTree;
8 | import daid.sliceAndDaid.util.AABBrect;
9 | import daid.sliceAndDaid.util.Logger;
10 |
11 | public class Layer
12 | {
13 | public int layerNr;
14 |
15 | public Vector modelSegmentList = new Vector();
16 |
17 | public Segment2D pathStart;
18 | public LayerPart skirt = null;
19 | public LayerPart modelPart = new LayerPart(this);
20 | public LayerPart[] outlinePart;
21 | private AABBTree modelSegmentTree = new AABBTree();
22 |
23 | public Layer(int layerNr, double minX, double minY, double maxX, double maxY)
24 | {
25 | this.layerNr = layerNr;
26 | this.outlinePart = new LayerPart[CraftConfig.perimeterCount];
27 | }
28 |
29 | public void addModelSegment(Segment2D segment)
30 | {
31 | if (segment.start.asGoodAsEqual(segment.end))
32 | return;
33 | modelSegmentTree.insert(segment);
34 | modelSegmentList.add(segment);
35 | }
36 |
37 | private void removeModelSegment(Segment2D segment)
38 | {
39 | modelSegmentList.remove(segment);
40 | modelSegmentTree.remove(segment);
41 | }
42 |
43 | public boolean optimize()
44 | {
45 | // Link up the segments with start/ends, so polygons are created.
46 | for (Segment2D s1 : modelSegmentList)
47 | {
48 | if (s1.getPrev() == null)
49 | {
50 | Segment2D best = null;
51 | double bestDist2 = 0.01;
52 | for (Segment2D s2 : modelSegmentTree.query(new AABBrect(s1.start, s1.start)))
53 | {
54 | if (s1 != s2 && s2.getNext() == null && s1.start.asGoodAsEqual(s2.end) && s1.start.sub(s2.end).vSize2() < bestDist2)
55 | {
56 | best = s2;
57 | bestDist2 = s1.start.sub(s2.end).vSize2();
58 | break;
59 | }
60 | }
61 | if (best != null)
62 | {
63 | s1.start = best.end;
64 | best.setNext(s1);
65 | }
66 | }
67 | if (s1.getNext() == null)
68 | {
69 | Segment2D best = null;
70 | double bestDist2 = 0.01;
71 | for (Segment2D s2 : modelSegmentTree.query(new AABBrect(s1.end, s1.end)))
72 | {
73 | if (s1 != s2 && s2.getPrev() == null && s1.end.asGoodAsEqual(s2.start) && s1.end.sub(s2.start).vSize2() < bestDist2)
74 | {
75 | best = s2;
76 | bestDist2 = s1.end.sub(s2.start).vSize2();
77 | break;
78 | }
79 | }
80 | if (best != null)
81 | {
82 | s1.end = best.start;
83 | s1.setNext(best);
84 | }
85 | }
86 | }
87 |
88 | for (Segment2D s : modelSegmentList)
89 | {
90 | if (s.getPrev() != null && s.getPrev().getNext() != s)
91 | throw new RuntimeException();
92 | if (s.getNext() != null && s.getNext().getPrev() != s)
93 | throw new RuntimeException();
94 | if (s.getNext() != null && !modelSegmentList.contains(s.getNext()))
95 | throw new RuntimeException();
96 | if (s.getPrev() != null && !modelSegmentList.contains(s.getPrev()))
97 | throw new RuntimeException();
98 | }
99 |
100 | boolean manifoldErrorReported = false;
101 | HashSet tmpSet = new HashSet(modelSegmentList);
102 | while (tmpSet.size() > 0)
103 | {
104 | Segment2D start = tmpSet.iterator().next();
105 | boolean manifold = false;
106 | for (Segment2D s = start; s != null; s = s.getNext())
107 | {
108 | if (!tmpSet.contains(s))
109 | {
110 | Logger.warning("Problem in layer: " + layerNr + "\nTried to create a segment link from links that where already used...");
111 | break;
112 | }
113 | if (s.getNext() == start)
114 | {
115 | manifold = true;
116 | break;
117 | }
118 | }
119 | if (manifold)
120 | {
121 | Polygon poly = new Polygon(start);
122 | for (Segment2D s : poly)
123 | {
124 | tmpSet.remove(s);
125 | }
126 | addModelPolygon(poly);
127 | } else
128 | {
129 | if (!manifoldErrorReported)
130 | Logger.warning("Object not manifold in layer: " + layerNr);
131 | manifoldErrorReported = true;
132 | for (Segment2D s = start; s != null; s = s.getNext())
133 | {
134 | tmpSet.remove(s);
135 | s.setType(Segment2D.TYPE_ERROR);
136 | if (s.getNext() == start)
137 | break;
138 | }
139 | for (Segment2D s = start; s != null; s = s.getPrev())
140 | {
141 | tmpSet.remove(s);
142 | s.setType(Segment2D.TYPE_ERROR);
143 | if (s.getPrev() == start)
144 | break;
145 | }
146 | }
147 | }
148 | return manifoldErrorReported;
149 | }
150 |
151 | private void addModelPolygon(Polygon poly)
152 | {
153 | for (Segment2D s : poly)
154 | {
155 | if (s.getNormal().dot(s.getNext().getNormal()) > CraftConfig.joinMinCosAngle)
156 | {
157 | removeModelSegment(s);
158 | Segment2D next = s.getNext();
159 | modelSegmentTree.remove(next);
160 | poly.remove(s);
161 | modelSegmentTree.insert(next);
162 | }
163 | }
164 | modelPart.addPolygon(poly);
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/LayerPart.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid;
2 |
3 | import java.util.Iterator;
4 | import java.util.Vector;
5 |
6 | import daid.sliceAndDaid.util.AABBTree;
7 | import daid.sliceAndDaid.util.AABBrect;
8 | import daid.sliceAndDaid.util.Vector2;
9 |
10 | public class LayerPart implements Iterable
11 | {
12 | private Layer layer;
13 | private Vector polygons = new Vector();
14 |
15 | public LayerPart(Layer layer)
16 | {
17 | this.layer = layer;
18 | }
19 |
20 | public LayerPart(LayerPart layerPart)
21 | {
22 | this.layer = layerPart.layer;
23 | }
24 |
25 | public Polygon getLargestPolygon()
26 | {
27 | Polygon largestPoly = null;
28 | double largestPolySize = 0;
29 | for (Polygon poly : polygons)
30 | {
31 | AABBrect polygonRect = poly.getAABB();
32 |
33 | if (polygonRect.getPerimeter() > largestPolySize)
34 | {
35 | largestPolySize = polygonRect.getPerimeter();
36 | largestPoly = poly;
37 | }
38 | }
39 | return largestPoly;
40 | }
41 |
42 | public void addPolygon(Polygon poly)
43 | {
44 | poly.check();
45 | if (poly.empty())
46 | {
47 | return;
48 | }
49 | polygons.add(poly);
50 | }
51 |
52 | /**
53 | * makeConvex is used to generate a single convex polygon from the existing polygon set.
54 | *
55 | * This is used for the skirt. Right now it creates a square box around the object.
56 | */
57 | public LayerPart makeConvex()
58 | {
59 | LayerPart ret = new LayerPart(this);
60 | double minX = Double.MAX_VALUE;
61 | double maxX = Double.MIN_VALUE;
62 | double minY = Double.MAX_VALUE;
63 | double maxY = Double.MIN_VALUE;
64 | for (Polygon p : polygons)
65 | {
66 | for (Segment2D s : p)
67 | {
68 | if (s.start.x < minX)
69 | minX = s.start.x;
70 | if (s.start.y < minY)
71 | minY = s.start.y;
72 | if (s.start.x > maxX)
73 | maxX = s.start.x;
74 | if (s.start.y > maxY)
75 | maxY = s.start.y;
76 | }
77 | }
78 | Polygon p = new Polygon();
79 | p.addEnd(new Segment2D(Segment2D.TYPE_PERIMETER, new Vector2(minX, minY), new Vector2(minX, maxY)));
80 | p.addEnd(new Segment2D(Segment2D.TYPE_PERIMETER, new Vector2(minX, maxY), new Vector2(maxX, maxY)));
81 | p.addEnd(new Segment2D(Segment2D.TYPE_PERIMETER, new Vector2(maxX, maxY), new Vector2(maxX, minY)));
82 | p.addEnd(new Segment2D(Segment2D.TYPE_PERIMETER, new Vector2(maxX, minY), new Vector2(minX, minY)));
83 | p.close();
84 | ret.addPolygon(p);
85 | return ret;
86 | }
87 |
88 | public Iterator iterator()
89 | {
90 | return polygons.iterator();
91 | }
92 |
93 | public Vector getPolygonListClone()
94 | {
95 | return new Vector(polygons);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/Model.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.FileReader;
5 | import java.io.IOException;
6 | import java.io.RandomAccessFile;
7 | import java.util.Vector;
8 |
9 | import daid.sliceAndDaid.util.Logger;
10 | import daid.sliceAndDaid.util.Triangle;
11 | import daid.sliceAndDaid.util.Vector3;
12 |
13 | public class Model
14 | {
15 | public Vector triangles;
16 |
17 | public Model(String filename) throws IOException
18 | {
19 | Logger.updateStatus("Loading: " + filename);
20 |
21 | if (filename.toLowerCase().endsWith(".stl"))
22 | {
23 | char[] buf = new char[5];
24 | BufferedReader br = new BufferedReader(new FileReader(filename));
25 | br.mark(5);
26 | br.read(buf);
27 | br.close();
28 | String header = new String(buf);
29 |
30 | if (header.equals("solid"))
31 | readAsciiSTL(filename);
32 | else
33 | readBinarySTL(filename);
34 | } else
35 | {
36 | new RuntimeException("Unknown model format: " + filename);
37 | }
38 | Logger.message("Triangle count: " + triangles.size());
39 | }
40 |
41 | public Vector3 getMin()
42 | {
43 | Vector3 ret = new Vector3();
44 | ret.x = Double.MAX_VALUE;
45 | ret.y = Double.MAX_VALUE;
46 | ret.z = Double.MAX_VALUE;
47 | for (Triangle t : triangles)
48 | {
49 | for (int i = 0; i < 3; i++)
50 | {
51 | if (ret.x > t.point[i].x)
52 | ret.x = t.point[i].x;
53 | if (ret.y > t.point[i].y)
54 | ret.y = t.point[i].y;
55 | if (ret.z > t.point[i].z)
56 | ret.z = t.point[i].z;
57 | }
58 | }
59 | return ret;
60 | }
61 |
62 | public Vector3 getMax()
63 | {
64 | Vector3 ret = new Vector3();
65 | ret.x = Double.MIN_VALUE;
66 | ret.y = Double.MIN_VALUE;
67 | ret.z = Double.MIN_VALUE;
68 | for (Triangle t : triangles)
69 | {
70 | for (int i = 0; i < 3; i++)
71 | {
72 | if (ret.x < t.point[i].x)
73 | ret.x = t.point[i].x;
74 | if (ret.y < t.point[i].y)
75 | ret.y = t.point[i].y;
76 | if (ret.z < t.point[i].z)
77 | ret.z = t.point[i].z;
78 | }
79 | }
80 | return ret;
81 | }
82 |
83 | private void readBinarySTL(String filename) throws IOException
84 | {
85 | RandomAccessFile raf = new RandomAccessFile(filename, "r");
86 | byte[] header = new byte[80];
87 | raf.read(header);
88 | int triangleCount = Integer.reverseBytes(raf.readInt());
89 | triangles = new Vector();
90 | for (int i = 0; i < triangleCount; i++)
91 | {
92 | Logger.setProgress(i, triangleCount);
93 | for (int j = 0; j < 3; j++)
94 | raf.readFloat();
95 |
96 | Triangle t = new Triangle();
97 | float x = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
98 | float y = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
99 | float z = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
100 | t.point[0] = new Vector3(x, y, z);
101 | x = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
102 | y = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
103 | z = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
104 | t.point[1] = new Vector3(x, y, z);
105 | x = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
106 | y = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
107 | z = Float.intBitsToFloat(Integer.reverseBytes(raf.readInt()));
108 | t.point[2] = new Vector3(x, y, z);
109 | raf.readShort();// flags
110 | triangles.add(t);
111 | }
112 | System.out.println(getMin());
113 | }
114 |
115 | private void readAsciiSTL(String filename) throws IOException
116 | {
117 | BufferedReader br = new BufferedReader(new FileReader(filename));
118 | String line;
119 | int i = 0;
120 | Vector3 normal = null;
121 | Triangle nextTri = new Triangle();
122 | triangles = new Vector();
123 | while ((line = br.readLine()) != null)
124 | {
125 | line = line.trim();
126 | if (line.startsWith("facet normal"))
127 | {
128 | String[] parts = line.split(" +");
129 | normal = new Vector3(Double.parseDouble(parts[2]), Double.parseDouble(parts[3]), Double.parseDouble(parts[4]));
130 | }
131 | if (line.startsWith("vertex"))
132 | {
133 | String[] parts = line.split(" +");
134 | nextTri.point[i] = new Vector3(Double.parseDouble(parts[1]), Double.parseDouble(parts[2]), Double.parseDouble(parts[3]));
135 | i++;
136 | if (i == 3)
137 | {
138 | if (normal.vSize2() > 0.1 && nextTri.getNormal().dot(normal) < 0.5)
139 | {
140 | // Triangle winding order and normal don't point in the same direction...
141 | // Flip the triangle?
142 | }
143 | triangles.add(nextTri);
144 | nextTri = new Triangle();
145 | i = 0;
146 | }
147 | }
148 | }
149 | br.close();
150 | }
151 |
152 | public void center()
153 | {
154 | Vector3 min = getMin();
155 | Vector3 max = getMax();
156 | Vector3 translate = new Vector3();
157 | translate.z = -min.z;
158 | translate.x = -(max.x + min.x) / 2;
159 | translate.y = -(max.y + min.y) / 2;
160 |
161 | move(translate);
162 | }
163 |
164 | private void move(Vector3 translate)
165 | {
166 | for (Triangle t : triangles)
167 | {
168 | for (int i = 0; i < 3; i++)
169 | {
170 | t.point[i].addToSelf(translate);
171 | }
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/Polygon.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid;
2 |
3 | import java.util.Iterator;
4 |
5 | import daid.sliceAndDaid.util.AABBrect;
6 | import daid.sliceAndDaid.util.Vector2;
7 |
8 | public class Polygon implements Iterable
9 | {
10 | private Segment2D first = null;
11 | private Segment2D last = null;
12 | private boolean enclosed = false;
13 |
14 | public Polygon()
15 | {
16 | }
17 |
18 | public Polygon(Segment2D segment)
19 | {
20 | first = segment;
21 | if (first == null)
22 | return;
23 | last = first;
24 | for (Segment2D s = first.getNext(); s != null; s = s.getNext())
25 | {
26 | if (s == first)
27 | {
28 | enclosed = true;
29 | break;
30 | }
31 | last = s;
32 | }
33 | }
34 |
35 | /**
36 | * Get the closest segment in this segment loop
37 | */
38 | public Segment2D closestTo(Vector2 p)
39 | {
40 | Segment2D best = first;
41 | double bestDist = 99999;
42 | for (Segment2D s : this)
43 | {
44 | if (s.start.sub(p).vSize2() < bestDist)
45 | {
46 | bestDist = s.start.sub(p).vSize2();
47 | best = s;
48 | }
49 | }
50 | return best;
51 | }
52 |
53 | public AABBrect getAABB()
54 | {
55 | AABBrect ret = new AABBrect(first);
56 | for (Segment2D s : this)
57 | {
58 | ret.addAABB(s);
59 | }
60 | return ret;
61 | }
62 |
63 | public void addEnd(Segment2D s)
64 | {
65 | if (enclosed)
66 | throw new RuntimeException();
67 | if (first == null)
68 | {
69 | first = s;
70 | }
71 | if (last != null)
72 | last.setNext(s);
73 | last = s;
74 | }
75 |
76 | /**
77 | * removeEnd removes this segment from the segment list, and links up the next segment to the previous. Removing 1 point in the polygon. The point removed
78 | * is the endpoint of this segment.
79 | */
80 | public void remove(Segment2D s)
81 | {
82 | if (s == first)
83 | {
84 | first = s.getNext();
85 | //In case we are enclosed with a single segment, the next is the same one. So we are back to an empty polygon.
86 | if (first == s)
87 | first = null;
88 | }
89 | if (s == last)
90 | {
91 | last = last.getPrev();
92 | //In case we are enclosed with a single segment, the prev is the same one. So we are back to an empty polygon.
93 | if (last == s)
94 | last = null;
95 | }
96 |
97 | if (s.getNext() == null)
98 | {
99 | if (enclosed)
100 | throw new RuntimeException();
101 | // Remove 's' from the linked list.
102 | s.getPrev().setNext(null);
103 | } else
104 | {
105 | // Update the start point of s.next to the end of the previous point. Effectively removing
106 | // s.end from the polygon.
107 | s.getNext().update(s.getPrev().end, s.getNext().end);
108 | // Remove 's' from the linked list.
109 | // We can set 's.next' to null here, even if we are iterating over 's',
110 | // because the next point of iteration has already been stored by the iterator.
111 | Segment2D prev = s.getPrev();
112 | Segment2D next = s.getNext();
113 | prev.setNext(null);
114 | s.setNext(null);
115 | prev.setNext(next);
116 | }
117 | }
118 |
119 | public void close()
120 | {
121 | if (enclosed)
122 | throw new UnsupportedOperationException();
123 | check();
124 | enclosed = true;
125 | last.setNext(first);
126 | check();
127 | }
128 |
129 | public Segment2D cutPoly(Segment2D s)
130 | {
131 | if (!enclosed)
132 | throw new UnsupportedOperationException();
133 | enclosed = false;
134 | Segment2D ret = s.getPrev();
135 | ret.setNext(null);
136 | return ret;
137 | }
138 |
139 | public void check()
140 | {
141 | if (first == null)
142 | return;
143 | if (enclosed)
144 | {
145 | if (first.getPrev() == null)
146 | throw new RuntimeException();
147 | if (last.getNext() == null)
148 | throw new RuntimeException();
149 | if (last.getNext() != first)
150 | throw new RuntimeException();
151 | if (first.getPrev() != last)
152 | throw new RuntimeException();
153 | for (Segment2D s = first.getNext(); s != first; s = s.getNext())
154 | {
155 | if (s == null)
156 | throw new RuntimeException();
157 | if (s.getPrev().getNext() != s)
158 | throw new RuntimeException();
159 | }
160 | } else
161 | {
162 | if (first.getPrev() != null)
163 | throw new RuntimeException();
164 | if (last.getNext() != null)
165 | throw new RuntimeException();
166 | }
167 | }
168 |
169 | public boolean empty()
170 | {
171 | return first == null;
172 | }
173 |
174 | public Iterator iterator()
175 | {
176 | return new Segment2DIterator();
177 | }
178 |
179 | private class Segment2DIterator implements Iterator
180 | {
181 | private Segment2D next;
182 |
183 | public Segment2DIterator()
184 | {
185 | this.next = first;
186 | }
187 |
188 | public boolean hasNext()
189 | {
190 | return next != null;
191 | }
192 |
193 | public Segment2D next()
194 | {
195 | Segment2D ret = next;
196 | next = next.getNext();
197 | if (next == first)
198 | next = null;
199 | return ret;
200 | }
201 |
202 | public void remove()
203 | {
204 | throw new UnsupportedOperationException();
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/Segment2D.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid;
2 |
3 | import daid.sliceAndDaid.util.AABBrect;
4 | import daid.sliceAndDaid.util.Vector2;
5 |
6 | /**
7 | * Segment2D represents a line in 2D space.
8 | */
9 | public class Segment2D extends AABBrect
10 | {
11 | public final static int TYPE_MODEL_SLICE = 0;
12 | public final static int TYPE_PERIMETER = 1;
13 | public final static int TYPE_MOVE = 2;
14 | public final static int TYPE_FILL = 3;
15 | public final static int TYPE_ERROR = 0xFFFF;
16 |
17 | public Vector2 start;
18 | public Vector2 end;
19 | private Vector2 normal;
20 | private Segment2D next, prev;
21 |
22 | public double lineWidth;
23 | public double feedRate;
24 | private int type;
25 |
26 | public Segment2D(int type, Vector2 start, Vector2 end)
27 | {
28 | // Make the AABB 1mm larger then the actual segment, to account for inaccuracies and moving
29 | // around the segment ends a bit.
30 | super(start, end, 1.0);
31 |
32 | this.type = type;
33 | this.lineWidth = -1;
34 | update(start, end);
35 | }
36 |
37 | public Segment2D(int type, Segment2D prev, Segment2D next)
38 | {
39 | super(prev.end, next.start, 1.0);
40 | this.type = type;
41 | this.start = prev.end;
42 | this.end = next.start;
43 |
44 | if (prev.next != null)
45 | prev.next.prev = null;
46 | prev.next = this;
47 | if (next.prev != null)
48 | next.prev.next = null;
49 | next.prev = this;
50 |
51 | this.prev = prev;
52 | this.next = next;
53 |
54 | update(this.start, this.end);
55 | }
56 |
57 | /**
58 | * For large updates we need to fix the normal, and the AABB. Only call this when the segment is
59 | * not in a Tree2D
60 | */
61 | public void update(Vector2 start, Vector2 end)
62 | {
63 | this.start = start;
64 | this.end = end;
65 | this.normal = end.sub(start).crossZ().normal();
66 | updateAABB(start, end, 1.0);
67 | }
68 |
69 | public String toString()
70 | {
71 | return "Segment:" + start + " " + end;
72 | }
73 |
74 | public Vector2 getIntersectionPoint(Segment2D other)
75 | {
76 | double x12 = start.x - end.x;
77 | double x34 = other.start.x - other.end.x;
78 | double y12 = start.y - end.y;
79 | double y34 = other.start.y - other.end.y;
80 |
81 | // Calculate the intersection of the 2 segments.
82 | double c = x12 * y34 - y12 * x34;
83 | if (Math.abs(c) < 0.0001)
84 | {
85 | return null;
86 | } else
87 | {
88 | double a = start.x * end.y - start.y * end.x;
89 | double b = other.start.x * other.end.y - other.start.y * other.end.x;
90 |
91 | return new Vector2((a * x34 - b * x12) / c, (a * y34 - b * y12) / c);
92 | }
93 | }
94 |
95 | public Vector2 getCollisionPoint(Segment2D other)
96 | {
97 | Vector2 p = getIntersectionPoint(other);
98 | if (p == null)
99 | return null;
100 | if ((p.x >= start.x && p.x <= end.x) || (p.x >= end.x && p.x <= start.x))
101 | {
102 | if ((p.y >= start.y && p.y <= end.y) || (p.y >= end.y && p.y <= start.y))
103 | return p;
104 | }
105 | return null;
106 | }
107 |
108 | public Vector2 getNormal()
109 | {
110 | return normal;
111 | }
112 |
113 | public int getType()
114 | {
115 | return type;
116 | }
117 |
118 | public void setType(int type)
119 | {
120 | this.type = type;
121 | }
122 |
123 | public void setNext(Segment2D newNext)
124 | {
125 | if (newNext == null)
126 | {
127 | if (next == null)
128 | throw new UnsupportedOperationException();
129 | next.prev = null;
130 | next = null;
131 | } else
132 | {
133 | if (next != null)
134 | throw new UnsupportedOperationException();
135 | if (newNext.prev != null)
136 | throw new UnsupportedOperationException();
137 | next = newNext;
138 | next.prev = this;
139 | }
140 | }
141 |
142 | public Segment2D getNext()
143 | {
144 | return next;
145 | }
146 |
147 | public Segment2D getPrev()
148 | {
149 | return prev;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/SliceAndDaidMain.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid;
2 |
3 | import java.io.FileWriter;
4 | import java.io.IOException;
5 | import java.util.Vector;
6 |
7 | import javax.swing.SwingUtilities;
8 |
9 | import daid.sliceAndDaid.config.CraftConfig;
10 | import daid.sliceAndDaid.config.CraftConfigLoader;
11 | import daid.sliceAndDaid.tool.GCodeTool;
12 | import daid.sliceAndDaid.tool.PerimeterTool;
13 | import daid.sliceAndDaid.tool.PathTool;
14 | import daid.sliceAndDaid.tool.SliceTool;
15 | import daid.sliceAndDaid.tool.SpeedTool;
16 | import daid.sliceAndDaid.ui.ConfigWindow;
17 | import daid.sliceAndDaid.ui.PreviewFrame;
18 | import daid.sliceAndDaid.util.GCodeFile;
19 | import daid.sliceAndDaid.util.Logger;
20 | import daid.sliceAndDaid.util.Vector2;
21 |
22 | public class SliceAndDaidMain
23 | {
24 | public static void main(String[] args)
25 | {
26 | CraftConfigLoader.loadConfig(null);
27 |
28 | if (args.length < 1)
29 | {
30 |
31 | SwingUtilities.invokeLater(new Runnable()
32 | {
33 | public void run()
34 | {
35 | try
36 | {
37 | new ConfigWindow();
38 | } catch (Exception e)
39 | {
40 | // We sometimes get a "Cannot write XdndAware property" exception in Java
41 | // 1.6.0_22 in Linux. Seems to be a java bug related to the text areas.
42 |
43 | // Just retry and hope for the best.
44 | if (e.getMessage().equals("Cannot write XdndAware property"))
45 | {
46 | new ConfigWindow();
47 | return;
48 | }
49 | e.printStackTrace();
50 | System.exit(-1);
51 | }
52 | }
53 | });
54 | } else
55 | {
56 | for (int i = 0; i < args.length; i++)
57 | sliceModel(args[i]);
58 | }
59 | }
60 |
61 | public static void sliceModel(String filename)
62 | {
63 | long startTime = System.currentTimeMillis();
64 | CraftConfig.lastSlicedFile = filename;
65 | CraftConfigLoader.saveConfig(null);
66 |
67 | Model m;
68 | try
69 | {
70 | m = new Model(filename);
71 | } catch (IOException e)
72 | {
73 | e.printStackTrace();
74 | Logger.error("Failed to load model");
75 | return;
76 | }
77 | m.center();
78 | SliceTool slicer = new SliceTool(m);
79 | final Vector layers = slicer.sliceModel(CraftConfig.startLayerNr, CraftConfig.endLayerNr, 0.0);
80 | Logger.updateStatus("Creating skirt");
81 | if (CraftConfig.skirtDistance > 0)
82 | {
83 | layers.get(0).skirt = new PerimeterTool(layers.get(0).modelPart, -CraftConfig.skirtDistance).createPerimeter().makeConvex();
84 | }
85 | Logger.updateStatus("Creating outlines");
86 | for (int i = 0; i < layers.size(); i++)
87 | {
88 | Logger.setProgress(i, layers.size());
89 | LayerPart prevPart = layers.get(i).modelPart;
90 | for (int c = 0; c < CraftConfig.perimeterCount; c++)
91 | {
92 | if (c == 0)
93 | prevPart = new PerimeterTool(prevPart, CraftConfig.perimeterWidth * 0.5).createPerimeter();
94 | else
95 | prevPart = new PerimeterTool(prevPart, CraftConfig.perimeterWidth).createPerimeter();
96 | layers.get(i).outlinePart[c] = prevPart;
97 | }
98 | }
99 | Logger.updateStatus("Generating paths");
100 | Vector2 startPoint = new Vector2(0, 0);
101 | for (int i = 0; i < layers.size(); i++)
102 | {
103 | Logger.setProgress(i, layers.size());
104 | new PathTool(layers.get(i)).generatePath(startPoint);
105 | if (layers.get(i).pathStart != null)
106 | startPoint = layers.get(i).pathStart.start;
107 | }
108 | Logger.updateStatus("Setting speeds");
109 | for (int i = 0; i < layers.size(); i++)
110 | {
111 | Logger.setProgress(i, layers.size());
112 | new SpeedTool(layers.get(i)).updateSpeed();
113 | }
114 | Logger.updateStatus("Generating GCode");
115 | try
116 | {
117 | GCodeFile gcodeFile = new GCodeFile(new FileWriter(filename + "_export.gcode"));
118 | gcodeFile.writeComment("GCode generated by SliceAndDaid:" + CraftConfig.VERSION);
119 |
120 | for (String line : CraftConfig.startGCode.split("\n"))
121 | gcodeFile.write(line);
122 | // gcodeFile.write("M101; extruder on (to get skeinlayer working)");
123 |
124 | for (int i = 0; i < layers.size(); i++)
125 | {
126 | Logger.setProgress(i, layers.size());
127 | new GCodeTool(layers.get(i), gcodeFile).generateGCode();
128 | }
129 |
130 | // gcodeFile.write("M103; extruder off (to get skeinlayer working)");
131 | for (String line : CraftConfig.endGCode.split("\n"))
132 | gcodeFile.write(line);
133 | gcodeFile.close();
134 | Logger.message("Expected print time: " + ((int) gcodeFile.getBuildTime() / 60) + " minutes");
135 | } catch (IOException e)
136 | {
137 | e.printStackTrace();
138 | }
139 |
140 | /* Post slicing */
141 | long sliceTime = System.currentTimeMillis() - startTime;
142 | Logger.message("Slice time: " + (((double) sliceTime) / 1000) + " seconds");
143 | SwingUtilities.invokeLater(new Runnable()
144 | {
145 | public void run()
146 | {
147 | new PreviewFrame(layers);
148 | }
149 | });
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/config/CraftConfig.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.config;
2 |
3 | /**
4 | * The CraftConfig class contains the configurable
5 | * settings for the slicer. Reflection and annotations
6 | * are used to make it easy to generate the configuration
7 | * dialog.
8 | * NOTE: Do not auto format this file. Manual format keeps it readable!
9 | */
10 | public class CraftConfig
11 | {
12 | public static final String VERSION = "Dev-Prerelease";
13 |
14 | public static final int GCODE_FULL = 0;
15 | public static final int GCODE_COMPACT = 1;
16 | public static final int GCODE_TINY_COMPACT = 2;
17 |
18 | @Setting(level = Setting.LEVEL_STARTER, group = "Layers",
19 | title = "Layer height (mm)",
20 | description = "Height of each sliced layer",
21 | minValue = 0.0, maxValue = 1.0)
22 | public static double layerHeight = 0.2;
23 |
24 | @Setting(level = Setting.LEVEL_STARTER, group = "Perimeter",
25 | title = "Width of the perimeter lines",
26 | description = "The width of the perimeter lines, a good value is the inner radius of your nozzle tip.",
27 | minValue = 0, maxValue = Integer.MAX_VALUE)
28 | public static double perimeterWidth = 0.4;
29 |
30 | @Setting(level = Setting.LEVEL_NORMAL, group = "Perimeter",
31 | title = "Perimeter line count",
32 | description = "Amount of perimeter walls",
33 | minValue = 1, maxValue = Integer.MAX_VALUE)
34 | public static int perimeterCount = 3;
35 |
36 | @Setting(level = Setting.LEVEL_STARTER, group = "Speed",
37 | title = "Print speed (mm/s)",
38 | description = "Speed at which the head is moved while it's printing",
39 | minValue = 0, maxValue = 10000)
40 | public static double printSpeed = 40.0;
41 |
42 | @Setting(level = Setting.LEVEL_NORMAL, group = "Speed",
43 | title = "First layer print speed (mm/s)",
44 | description = "Speed at which the head is moved while it's printing the first layer",
45 | minValue = 0, maxValue = 10000)
46 | public static double layerZeroPrintSpeed = 20.0;
47 |
48 | @Setting(level = Setting.LEVEL_NORMAL, group = "Speed",
49 | title = "Max speedup per layer (mm/s)",
50 | description = "Amount of speed increase per layer after the first layer, until it reaches the print speed.",
51 | minValue = 0, maxValue = 10000)
52 | public static double layerPrintSpeedIncrease = 10.0;
53 |
54 | @Setting(level = Setting.LEVEL_NORMAL, group = "Speed",
55 | title = "Travel speed (mm/s)",
56 | description = "Speed at which the head is moved while it's not printing",
57 | minValue = 0, maxValue = 10000)
58 | public static double travelSpeed = 150.0;
59 |
60 | @Setting(level = Setting.LEVEL_STARTER, group = "Dimensions",
61 | title = "Filament diameter (mm)",
62 | description = "The diameter of the filament, as accurate as possible.\n"+
63 | "If you cannot measure it accurate then manually tweak it.\n"+
64 | "If you get to little extrusion reduce this number, if you get to much, increase this number.",
65 | minValue = 0, maxValue = 10)
66 | public static double filamentDiameter = 2.89;
67 |
68 | @Setting(level = Setting.LEVEL_ADVANCED, group = "Speed",
69 | title = "Minimum layer time (s)",
70 | description = "The minimal amount of time spend to print a single layer.\n" +
71 | "Gives time to cool the layer before the next one is printed.",
72 | minValue = 0, maxValue = 200)
73 | public static double minLayerTime = 10;
74 |
75 | @Setting(level = Setting.LEVEL_NORMAL, group = "Skirt",
76 | title = "Skirt distance (mm)",
77 | description = "Distance of the skirt (outline around layer 0) from the model. Use 0 to disable.",
78 | minValue = 0, maxValue = 10)
79 | public static double skirtDistance = 6.0;
80 |
81 | @Setting(level = Setting.LEVEL_KITCHENSINK, group = "Layers",
82 | title = "First layer slice height (%)",
83 | description = "Starting height of the first slice in the model. 50% is the default.",
84 | minValue = 0, maxValue = 200)
85 | public static int firstLayerHeightPercent = 50;
86 |
87 | @Setting(level = Setting.LEVEL_ADVANCED, group = "Perimeter",
88 | title = "Minimal line segment cosinus value",
89 | description = "If the cosinus of the line angle difference is higher then this value then 2 lines are joined into 1.\nSpeeding up the slicing, and creating less gcode commands. Lower values makes circles less round,\nfor a faster slicing and less GCode. A value of 1.0 leaves every line intact.",
90 | minValue = 0.95, maxValue = 1.0)
91 | public static double joinMinCosAngle = 0.995;
92 |
93 | @Setting(level = Setting.LEVEL_KITCHENSINK, group = "Layers",
94 | title = "Start layer number",
95 | description = "First layer that is sliced, can be used to remove the bottom X layers",
96 | minValue = 0, maxValue = Integer.MAX_VALUE)
97 | public static int startLayerNr = 0;
98 |
99 | @Setting(level = Setting.LEVEL_KITCHENSINK, group = "Layers",
100 | title = "Final layer number",
101 | description = "Last layer that is sliced, can be used to remove the top X layers.",
102 | minValue = 0, maxValue = Integer.MAX_VALUE)
103 | public static int endLayerNr = Integer.MAX_VALUE;
104 |
105 | @Setting(level = Setting.LEVEL_KITCHENSINK, group = "Perimeter",
106 | title = "Cap perimeter corners",
107 | description = "Cap off tight corners in the perimeter.")
108 | public static boolean perimeterCap = true;
109 | @Setting(level = Setting.LEVEL_KITCHENSINK,
110 | title = "Minimum segment length (mm)",
111 | description = "Remove segments shorter then this length.")
112 | public static double minSegmentLength = 0.1;
113 |
114 | @Setting(level = Setting.LEVEL_ADVANCED, group = "GCode",
115 | title = "GCode format",
116 | description = "Different GCode exports types are supported.\n"+
117 | "Full: exports everything with comments. Use this for debugging, or post processing of the GCode.\n" +
118 | "Compact: removes the comments, and assumes the last feedrate will be reused. (About 25% smaller then full)\n" +
119 | "Tiny compact: tries to export the minimum amount of GCode required. Not all firmwares and parsers will work with this (About 10% smaller then compact).",
120 | enumName = "GCODE")
121 | public static int gcodeType = GCODE_COMPACT;
122 |
123 | @Setting(level = Setting.LEVEL_HIDDEN)
124 | public static String startGCode = "M98 E926; Set the number of steps per E mm\n" +
125 | "G28; Move to origin\n" +
126 | "G92 X-105 Y-105 Z0; Put the 'origin' on the center of the platform\n" +
127 | "G1 Z5 F180; Move the head up a bit\n" +
128 | "G1 X0 Y0; Move to the center of the platfrom\n"+
129 | "M106 S255; Turn on the fan\n" +
130 | "G1 Z0 F180; Move the head down for printing of layer 0";
131 |
132 | @Setting(level = Setting.LEVEL_HIDDEN)
133 | public static String endGCode = "G1 X-200 Y-200; Move the X/Y away from the printed object\n" +
134 | "M104 S0; Turn off the extruder temperature";
135 |
136 | @Setting(level = Setting.LEVEL_HIDDEN,
137 | minValue = Setting.LEVEL_STARTER, maxValue = Setting.LEVEL_KITCHENSINK)
138 | public static int showLevel = Setting.LEVEL_STARTER;
139 | @Setting(level = Setting.LEVEL_HIDDEN)
140 | public static String lastSlicedFile = "";
141 | }
142 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/config/CraftConfigLoader.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.config;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.BufferedWriter;
5 | import java.io.FileNotFoundException;
6 | import java.io.FileReader;
7 | import java.io.FileWriter;
8 | import java.io.IOException;
9 | import java.lang.reflect.Field;
10 |
11 | import daid.sliceAndDaid.util.Logger;
12 |
13 | public class CraftConfigLoader
14 | {
15 | /***************************
16 | * Load and save functions
17 | ***************************/
18 |
19 | /**
20 | * loadConfig
21 | *
22 | * Loads the configuration from a file, use 'null' for the default config file.
23 | */
24 | public static void loadConfig(String filename)
25 | {
26 | if (filename == null)
27 | filename = System.getProperty("user.home") + "/.SliceAndDaid.conf";
28 | BufferedReader br = null;
29 | try
30 | {
31 | br = new BufferedReader(new FileReader(filename));
32 | } catch (FileNotFoundException e)
33 | {
34 | return;
35 | }
36 | String line;
37 | String section = null;
38 | try
39 | {
40 | while ((line = br.readLine()) != null)
41 | {
42 | if (line.startsWith(";"))
43 | continue;
44 | if (line.startsWith("[") && line.endsWith("]"))
45 | {
46 | section = line;
47 | continue;
48 | }
49 | if (line.indexOf('=') < 0)
50 | continue;
51 | String key = line.substring(0, line.indexOf('='));
52 | String value = line.substring(line.indexOf('=') + 1);
53 | if ("[SliceAndDaid config]".equals(section))
54 | {
55 | setField(key, value);
56 | }
57 | }
58 | } catch (IOException e)
59 | {
60 | Logger.error("IOException during loading of config file...");
61 | }
62 | }
63 |
64 | private static void setField(String key, String value)
65 | {
66 | Class> c = CraftConfig.class;
67 | Field f = null;
68 | try
69 | {
70 | f = c.getField(key);
71 | if (f == null)
72 | return;
73 | Setting s = f.getAnnotation(Setting.class);
74 | if (f.getType() == Double.TYPE)
75 | {
76 | double v = Double.parseDouble(value);
77 | if (s != null && v < s.minValue())
78 | v = s.minValue();
79 | if (s != null && v > s.maxValue())
80 | v = s.maxValue();
81 | f.setDouble(null, v);
82 | } else if (f.getType() == Integer.TYPE)
83 | {
84 | int v = Integer.parseInt(value);
85 | if (s != null && v < s.minValue())
86 | v = (int) s.minValue();
87 | if (s != null && v > s.maxValue())
88 | v = (int) s.maxValue();
89 | f.setInt(null, v);
90 | } else if (f.getType() == Boolean.TYPE)
91 | {
92 | f.setBoolean(null, Boolean.parseBoolean(value));
93 | } else if (f.getType() == String.class)
94 | {
95 | f.set(null, value.toString().replace("\\n", "\n"));
96 | } else
97 | {
98 | throw new RuntimeException("Unknown config type: " + f.getType());
99 | }
100 | } catch (IllegalArgumentException e)
101 | {
102 | e.printStackTrace();
103 | } catch (IllegalAccessException e)
104 | {
105 | e.printStackTrace();
106 | } catch (SecurityException e)
107 | {
108 | e.printStackTrace();
109 | } catch (NoSuchFieldException e)
110 | {
111 | Logger.warning("Found: " + key + " in the configuration, but I don't know this setting");
112 | }
113 |
114 | }
115 |
116 | /**
117 | * saveConfig
118 | *
119 | * Saves the configuration to a file, use 'null' for the default config file.
120 | */
121 | public static void saveConfig(String filename)
122 | {
123 | if (filename == null)
124 | filename = System.getProperty("user.home") + "/.SliceAndDaid.conf";
125 | try
126 | {
127 | BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
128 | bw.write(";Saved with version: " + CraftConfig.VERSION + "\n");
129 | bw.write("[SliceAndDaid config]\n");
130 | Class configClass = CraftConfig.class;
131 | for (final Field f : configClass.getFields())
132 | {
133 | Setting s = f.getAnnotation(Setting.class);
134 | if (s == null)
135 | continue;
136 | try
137 | {
138 | bw.write(f.getName() + "=" + f.get(null).toString().replace("\n", "\\n") + "\n");
139 | } catch (IllegalArgumentException e)
140 | {
141 | e.printStackTrace();
142 | } catch (IllegalAccessException e)
143 | {
144 | e.printStackTrace();
145 | }
146 | }
147 | bw.close();
148 | } catch (IOException e1)
149 | {
150 | // TODO Auto-generated catch block
151 | e1.printStackTrace();
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/config/Setting.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.config;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target(ElementType.FIELD)
10 | public @interface Setting
11 | {
12 | int LEVEL_STARTER = 0;
13 | int LEVEL_NORMAL = 1;
14 | int LEVEL_ADVANCED = 2;
15 | int LEVEL_KITCHENSINK = 3;
16 | int LEVEL_HIDDEN = 4;
17 |
18 | public String title() default "";
19 |
20 | public String description() default "";
21 |
22 | public double minValue() default Double.MIN_VALUE;
23 |
24 | public double maxValue() default Double.MAX_VALUE;
25 |
26 | public int level() default Setting.LEVEL_NORMAL;
27 |
28 | public String enumName() default "";
29 |
30 | public String group() default "";
31 | }
32 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/tool/GCodeTool.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.tool;
2 |
3 | import java.io.IOException;
4 |
5 | import daid.sliceAndDaid.Layer;
6 | import daid.sliceAndDaid.Segment2D;
7 | import daid.sliceAndDaid.config.CraftConfig;
8 | import daid.sliceAndDaid.util.GCodeFile;
9 |
10 | public class GCodeTool
11 | {
12 | private Layer layer;
13 | private GCodeFile file;
14 |
15 | public GCodeTool(Layer layer, GCodeFile file)
16 | {
17 | this.layer = layer;
18 | this.file = file;
19 | }
20 |
21 | public void generateGCode() throws IOException
22 | {
23 | double filamentMM3PerMM = Math.PI * (CraftConfig.filamentDiameter / 2) * (CraftConfig.filamentDiameter / 2);
24 |
25 | file.writeComment("LAYER:" + layer.layerNr);
26 | file.writeMoveZ((double) (layer.layerNr + 1) * CraftConfig.layerHeight, CraftConfig.travelSpeed, "Move to layer: " + layer.layerNr);
27 | if (layer.pathStart == null)
28 | return;
29 | file.writeMoveXY(layer.pathStart.start.x, layer.pathStart.start.y, CraftConfig.travelSpeed, "");
30 | for (Segment2D s = layer.pathStart; s != null; s = s.getNext())
31 | {
32 | if (s.lineWidth < 0)
33 | {
34 | file.writeMoveXY(s.end.x, s.end.y, s.feedRate, "");
35 | } else
36 | {
37 | // First calculate the amount of filament we need in mm3
38 | double filamentAmount = s.end.sub(s.start).vSize() * s.lineWidth * CraftConfig.layerHeight;
39 | // Then divide this by the amount of mm3 we have per mm filament, so we get the
40 | // amount of mm of filament we need to extrude.
41 | filamentAmount = filamentAmount / filamentMM3PerMM;
42 | file.writeMoveXYE(s.end.x, s.end.y, filamentAmount, s.feedRate, "");
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/tool/PathTool.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.tool;
2 |
3 | import java.util.Vector;
4 |
5 | import daid.sliceAndDaid.Layer;
6 | import daid.sliceAndDaid.Polygon;
7 | import daid.sliceAndDaid.Segment2D;
8 | import daid.sliceAndDaid.util.Vector2;
9 |
10 | /**
11 | * The path tool is the final tool run before the GCode is generated.
12 | *
13 | * It takes all the separate lines and links those into a single large path, ready for GCode generation.
14 | */
15 | public class PathTool
16 | {
17 | private Layer layer;
18 |
19 | public PathTool(Layer layer)
20 | {
21 | this.layer = layer;
22 | }
23 |
24 | public void generatePath(Vector2 bestStartPoint)
25 | {
26 | Segment2D prev = null;
27 | if (layer.skirt != null)
28 | {
29 | Polygon poly = layer.skirt.getLargestPolygon();
30 | if (poly != null)
31 | {
32 | prev = poly.closestTo(bestStartPoint);
33 | layer.pathStart = prev;
34 | prev = poly.cutPoly(prev);
35 | }
36 | }
37 |
38 | for (int i = 0; i < layer.outlinePart.length; i++)
39 | {
40 | // Find the largest polygon. So we start with the biggest outline first.
41 | Polygon nextPoly = layer.outlinePart[i].getLargestPolygon();
42 | if (nextPoly == null)
43 | return;
44 |
45 | Vector polys = layer.outlinePart[i].getPolygonListClone();
46 | polys.remove(nextPoly);
47 | polys.add(0, nextPoly);
48 |
49 | while (polys.size() > 0)
50 | {
51 | nextPoly = polys.get(0);
52 | if (prev != null)
53 | {
54 | for (int n = 0; n < polys.size(); n++)
55 | {
56 | if (polys.get(n).getAABB().getAABBDist(prev) < nextPoly.getAABB().getAABBDist(prev))
57 | nextPoly = polys.get(n);
58 | }
59 | }
60 | polys.remove(nextPoly);
61 | if (prev == null)
62 | {
63 | Segment2D startSegment = nextPoly.closestTo(bestStartPoint);
64 | layer.pathStart = startSegment;
65 | prev = nextPoly.cutPoly(startSegment);
66 | } else
67 | {
68 | Segment2D startSegment = nextPoly.closestTo(prev.end);
69 | Segment2D newPrev = nextPoly.cutPoly(startSegment);
70 | new Segment2D(Segment2D.TYPE_MOVE, prev, startSegment);
71 | prev = newPrev;
72 | }
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/tool/PerimeterTool.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.tool;
2 |
3 | import sun.reflect.generics.tree.Tree;
4 | import daid.sliceAndDaid.LayerPart;
5 | import daid.sliceAndDaid.Polygon;
6 | import daid.sliceAndDaid.Segment2D;
7 | import daid.sliceAndDaid.config.CraftConfig;
8 | import daid.sliceAndDaid.util.AABBTree;
9 | import daid.sliceAndDaid.util.Vector2;
10 |
11 | /**
12 | * The perimeter is the outer lines of the object, the "walls" so to say.
13 | */
14 | public class PerimeterTool
15 | {
16 | private LayerPart layerPart;
17 | private double distance;
18 |
19 | public PerimeterTool(LayerPart layerPart, double distance)
20 | {
21 | this.layerPart = layerPart;
22 | this.distance = distance;
23 | }
24 |
25 | public LayerPart createPerimeter()
26 | {
27 | LayerPart ret = new LayerPart(layerPart);
28 | AABBTree tree = new AABBTree();
29 | for (Polygon poly : layerPart)
30 | {
31 | Polygon newPoly = new Polygon();
32 | Segment2D first = null, prev = null;
33 | for (Segment2D s : poly)
34 | {
35 | Vector2 start = s.start.sub(s.getNormal().mul(distance));
36 | Vector2 end = s.end.sub(s.getNormal().mul(distance));
37 | Segment2D newSeg = new Segment2D(Segment2D.TYPE_PERIMETER, start, end);
38 | newSeg.lineWidth = CraftConfig.perimeterWidth;
39 |
40 | newPoly.addEnd(newSeg);
41 | if (prev == null)
42 | {
43 | first = newSeg;
44 | } else
45 | {
46 | linkUp(ret, prev, newSeg);
47 | }
48 |
49 | prev = newSeg;
50 | }
51 |
52 | if (!newPoly.empty())
53 | {
54 | newPoly.close();
55 | linkUp(ret, prev, first);
56 | for (Segment2D s : newPoly)
57 | {
58 | tree.insert(s);
59 | }
60 | ret.addPolygon(newPoly);
61 | }
62 | }
63 |
64 | return ret;
65 | }
66 |
67 | /**
68 | * Link up the 2 segments to each other, this will extend the segment so that the 2 segments cross, unless the extend it longer then the 'distance', at
69 | * which point an extra segment is created. This will help with very high angle corners.
70 | */
71 | private void linkUp(LayerPart ret, Segment2D prev, Segment2D next)
72 | {
73 | Vector2 p = prev.getIntersectionPoint(next);
74 | if (p == null)
75 | p = prev.end.add(next.start).div(2);
76 | // If the intersection point between the 2 moved lines is a bit further away then the line
77 | // distance, then we are a tight corner and we need to be capped.
78 | if (CraftConfig.perimeterCap && prev.end.sub(p).vSize2() > distance * 1.1 * distance * 1.1)
79 | {
80 | Vector2 p1 = prev.end.add(p.sub(prev.end).normal().mul(distance));
81 | Vector2 p2 = next.start.add(p.sub(next.start).normal().mul(distance));
82 |
83 | prev.end = p1;
84 | next.start = p2;
85 | Segment2D newSeg = new Segment2D(Segment2D.TYPE_PERIMETER, p1, p2);
86 | newSeg.lineWidth = CraftConfig.perimeterWidth;
87 | prev.setNext(null);
88 | prev.setNext(newSeg);
89 | newSeg.setNext(next);
90 | } else
91 | {
92 | prev.end = p;
93 | next.start = p;
94 |
95 | //prev.setNext(next);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/tool/SliceTool.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.tool;
2 |
3 | import java.util.Vector;
4 |
5 | import daid.sliceAndDaid.Layer;
6 | import daid.sliceAndDaid.Model;
7 | import daid.sliceAndDaid.Segment2D;
8 | import daid.sliceAndDaid.config.CraftConfig;
9 | import daid.sliceAndDaid.util.Logger;
10 | import daid.sliceAndDaid.util.Triangle;
11 | import daid.sliceAndDaid.util.Vector3;
12 |
13 | /**
14 | * The slice tool slices the model into layers, it does so by going trough all model triangles and
15 | * slice those into 2D lines.
16 | */
17 | public class SliceTool
18 | {
19 | private Model model;
20 |
21 | public SliceTool(Model model)
22 | {
23 | this.model = model;
24 | }
25 |
26 | public Vector sliceModel(int startLayer, int endLayer, double extraLayerOffset)
27 | {
28 | Vector layers = new Vector();
29 |
30 | double layerHeight = CraftConfig.layerHeight;
31 | Vector3 modelMin = model.getMin();
32 | Vector3 modelMax = model.getMax();
33 | double firstLayerHeight = ((double) CraftConfig.firstLayerHeightPercent) / 100.0 + extraLayerOffset;
34 | int layerCount = (int) (modelMax.z / layerHeight + firstLayerHeight);
35 |
36 | int firstLayer = startLayer;
37 | int lastLayer = endLayer;
38 | if (lastLayer > layerCount)
39 | lastLayer = layerCount;
40 | Logger.updateStatus("Slicing layers");
41 | Logger.message("Slicing " + (lastLayer - firstLayer) + " layers");
42 | for (int i = firstLayer; i < lastLayer; i++)
43 | {
44 | layers.add(new Layer(i, modelMin.x, modelMin.y, modelMax.x, modelMax.y));
45 | }
46 | int n = 0;
47 | for (Triangle t : model.triangles)
48 | {
49 | Logger.setProgress(n++, model.triangles.size());
50 |
51 | double zMin = t.point[0].z;
52 | double zMax = t.point[0].z;
53 | if (t.point[1].z < zMin)
54 | zMin = t.point[1].z;
55 | if (t.point[2].z < zMin)
56 | zMin = t.point[2].z;
57 | if (t.point[1].z > zMax)
58 | zMax = t.point[1].z;
59 | if (t.point[2].z > zMax)
60 | zMax = t.point[2].z;
61 | for (int i = (int) (zMin / layerHeight + firstLayerHeight); i <= (int) (zMax / layerHeight + firstLayerHeight); i++)
62 | {
63 | if (i >= firstLayer && i < lastLayer)
64 | {
65 | double layerZ = (((double) i) + firstLayerHeight) * layerHeight;
66 | Segment2D s = t.project2D(layerZ);
67 | if (s != null)
68 | layers.get(i - firstLayer).addModelSegment(s);
69 | }
70 | }
71 | }
72 |
73 | Logger.updateStatus("Optimizing layers");
74 | for (int i = 0; i < layers.size(); i++)
75 | {
76 | Logger.setProgress(i, layers.size());
77 | layers.get(i).optimize();
78 | }
79 | return layers;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/tool/SpeedTool.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.tool;
2 |
3 | import daid.sliceAndDaid.Layer;
4 | import daid.sliceAndDaid.Segment2D;
5 | import daid.sliceAndDaid.config.CraftConfig;
6 |
7 | public class SpeedTool
8 | {
9 | private Layer layer;
10 |
11 | public SpeedTool(Layer layer)
12 | {
13 | this.layer = layer;
14 | }
15 |
16 | public void updateSpeed()
17 | {
18 | double layerTime = 0;
19 | for (Segment2D s = layer.pathStart; s != null; s = s.getNext())
20 | {
21 | if (s.lineWidth < 0)
22 | {
23 | s.feedRate = CraftConfig.travelSpeed;
24 | } else
25 | {
26 | s.feedRate = CraftConfig.layerZeroPrintSpeed + CraftConfig.layerPrintSpeedIncrease * layer.layerNr;
27 | if (s.feedRate > CraftConfig.printSpeed)
28 | s.feedRate = CraftConfig.printSpeed;
29 | }
30 | layerTime += s.start.sub(s.end).vSize() / s.feedRate;
31 | }
32 |
33 | if (layerTime < CraftConfig.minLayerTime)
34 | {
35 | double multiply = layerTime / CraftConfig.minLayerTime;
36 | for (Segment2D s = layer.pathStart; s != null; s = s.getNext())
37 | {
38 | s.feedRate *= multiply;
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/ui/ConfigWindow.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.ui;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Color;
5 | import java.awt.Component;
6 | import java.awt.Dimension;
7 | import java.awt.Font;
8 | import java.awt.GridBagConstraints;
9 | import java.awt.GridBagLayout;
10 | import java.awt.Insets;
11 | import java.awt.event.ActionEvent;
12 | import java.awt.event.ActionListener;
13 | import java.awt.event.FocusAdapter;
14 | import java.awt.event.FocusEvent;
15 | import java.io.File;
16 | import java.lang.reflect.Field;
17 | import java.util.HashSet;
18 | import java.util.Vector;
19 |
20 | import javax.swing.BorderFactory;
21 | import javax.swing.BoxLayout;
22 | import javax.swing.DefaultListCellRenderer;
23 | import javax.swing.JButton;
24 | import javax.swing.JCheckBox;
25 | import javax.swing.JComboBox;
26 | import javax.swing.JFileChooser;
27 | import javax.swing.JFrame;
28 | import javax.swing.JLabel;
29 | import javax.swing.JList;
30 | import javax.swing.JOptionPane;
31 | import javax.swing.JPanel;
32 | import javax.swing.JScrollPane;
33 | import javax.swing.JSpinner;
34 | import javax.swing.JTabbedPane;
35 | import javax.swing.JTextArea;
36 | import javax.swing.SpinnerNumberModel;
37 | import javax.swing.event.ChangeEvent;
38 | import javax.swing.event.ChangeListener;
39 | import javax.swing.filechooser.FileFilter;
40 |
41 | import daid.sliceAndDaid.SliceAndDaidMain;
42 | import daid.sliceAndDaid.config.CraftConfig;
43 | import daid.sliceAndDaid.config.CraftConfigLoader;
44 | import daid.sliceAndDaid.config.Setting;
45 | import daid.sliceAndDaid.util.Logger;
46 |
47 | /**
48 | * The ConfigWindow class generates a JFrame window with the configurable options.
49 | *
50 | * It uses reflection to get the configurable settings. This makes adding new settings easy.
51 | *
52 | * NOTE: I suck at UI coding.
53 | */
54 | public class ConfigWindow extends JFrame
55 | {
56 | private static final long serialVersionUID = 1L;
57 |
58 | private JPanel configSettingsPanel;
59 | private JPanel actionPanel;
60 |
61 | public ConfigWindow()
62 | {
63 | this.setTitle("SliceAndDaid - " + CraftConfig.VERSION);
64 | this.setResizable(false);
65 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
66 |
67 | JTabbedPane tabbedPane = new JTabbedPane();
68 | this.configSettingsPanel = new JPanel();
69 | this.configSettingsPanel.setLayout(new BoxLayout(this.configSettingsPanel, BoxLayout.Y_AXIS));
70 | this.actionPanel = new JPanel(new GridBagLayout());
71 |
72 | final JTextArea startCodeTextArea = new JTextArea(CraftConfig.startGCode);
73 | final JTextArea endCodeTextArea = new JTextArea(CraftConfig.endGCode);
74 | startCodeTextArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
75 | endCodeTextArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
76 |
77 | startCodeTextArea.addFocusListener(new FocusAdapter()
78 | {
79 | public void focusLost(FocusEvent e)
80 | {
81 | CraftConfig.startGCode = startCodeTextArea.getText();
82 | CraftConfigLoader.saveConfig(null);
83 | }
84 | });
85 | endCodeTextArea.addFocusListener(new FocusAdapter()
86 | {
87 | public void focusLost(FocusEvent e)
88 | {
89 | CraftConfig.endGCode = endCodeTextArea.getText();
90 | CraftConfigLoader.saveConfig(null);
91 | }
92 | });
93 |
94 | tabbedPane.addTab("Settings", this.configSettingsPanel);
95 | tabbedPane.addTab("Start GCode", new JScrollPane(startCodeTextArea));
96 | tabbedPane.addTab("End GCode", new JScrollPane(endCodeTextArea));
97 |
98 | GridBagConstraints c = new GridBagConstraints();
99 | c.gridy = 0;
100 | c.anchor = GridBagConstraints.WEST;
101 | c.insets = new Insets(1, 1, 1, 1);
102 | c.fill = GridBagConstraints.HORIZONTAL;
103 |
104 | JButton sliceButton = new JButton("Slice");
105 | sliceButton.addActionListener(new ActionListener()
106 | {
107 | public void actionPerformed(ActionEvent e)
108 | {
109 | final JFileChooser fc = new JFileChooser();
110 | fc.setFileFilter(new FileFilter()
111 | {
112 | public boolean accept(File f)
113 | {
114 | if (f.isDirectory())
115 | return true;
116 | return f.getName().endsWith(".stl");
117 | }
118 |
119 | public String getDescription()
120 | {
121 | return null;
122 | }
123 |
124 | });
125 | fc.setSelectedFile(new File(CraftConfig.lastSlicedFile));
126 | int returnVal = fc.showOpenDialog(null);
127 | if (returnVal == JFileChooser.APPROVE_OPTION)
128 | {
129 | final LogWindow logWindow = new LogWindow();
130 | new Thread(new Runnable()
131 | {
132 | public void run()
133 | {
134 | try
135 | {
136 | SliceAndDaidMain.sliceModel(fc.getSelectedFile().toString());
137 | logWindow.dispose();
138 | } catch (Exception e)
139 | {
140 | e.printStackTrace();
141 | logWindow.dispose();
142 | StringBuilder sb = new StringBuilder();
143 | sb.append(e.toString());
144 | sb.append("\n");
145 | for (StackTraceElement el : e.getStackTrace())
146 | {
147 | sb.append(el.toString());
148 | sb.append("\n");
149 | }
150 | JOptionPane.showMessageDialog(null, sb, "Exception", JOptionPane.ERROR_MESSAGE);
151 | }
152 | }
153 | }).start();
154 | ConfigWindow.this.dispose();
155 | }
156 | }
157 | });
158 | this.actionPanel.add(sliceButton, c);
159 |
160 | final JComboBox levelSelect = new JComboBox(new String[] { "Starter", "Normal", "Advance", "+Kitchen sink" });
161 | levelSelect.addActionListener(new ActionListener()
162 | {
163 | public void actionPerformed(ActionEvent e)
164 | {
165 | CraftConfig.showLevel = levelSelect.getSelectedIndex();
166 | Color buttonBgColor = null;
167 | for (Component c : levelSelect.getComponents())
168 | {
169 | if (c instanceof JButton)
170 | buttonBgColor = ((JButton) c).getBackground();
171 | }
172 | levelSelect.setBackground(levelColor(CraftConfig.showLevel));
173 | for (Component c : levelSelect.getComponents())
174 | {
175 | if (c instanceof JButton)
176 | ((JButton) c).setBackground(buttonBgColor);
177 | }
178 | CraftConfigLoader.saveConfig(null);
179 | createConfigFields();
180 | }
181 | });
182 | levelSelect.setSelectedIndex(CraftConfig.showLevel);
183 | levelSelect.setRenderer(new DefaultListCellRenderer()
184 | {
185 | private static final long serialVersionUID = 1L;
186 |
187 | public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
188 | {
189 | super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
190 | if (isSelected)
191 | this.setBackground(levelColor(index).darker());
192 | else
193 | this.setBackground(levelColor(index));
194 | return this;
195 | }
196 | });
197 | this.actionPanel.add(levelSelect, c);
198 |
199 | this.add(tabbedPane);
200 | this.add(actionPanel, BorderLayout.SOUTH);
201 |
202 | createConfigFields();
203 | this.setVisible(true);
204 | }
205 |
206 | private void createConfigFields()
207 | {
208 | configSettingsPanel.removeAll();
209 |
210 | HashSet doneGroups = new HashSet();
211 |
212 | for (final Field f : CraftConfig.class.getFields())
213 | {
214 | final Setting s = f.getAnnotation(Setting.class);
215 | if (s == null)
216 | continue;
217 | if (doneGroups.contains(s.group()))
218 | continue;
219 | doneGroups.add(s.group());
220 | JPanel p = new JPanel(new GridBagLayout());
221 | p.setBorder(BorderFactory.createTitledBorder(s.group()));
222 | if (addConfigFields(p, s.group()) > 0)
223 | configSettingsPanel.add(p);
224 | }
225 |
226 | this.pack();
227 | this.setLocationRelativeTo(null);
228 | }
229 |
230 | private int addConfigFields(JPanel p, String groupName)
231 | {
232 | GridBagConstraints c = new GridBagConstraints();
233 | c.gridy = 0;
234 | c.anchor = GridBagConstraints.WEST;
235 | c.insets = new Insets(1, 1, 1, 1);
236 | c.fill = GridBagConstraints.HORIZONTAL;
237 | c.weighty = 1;
238 | for (final Field f : CraftConfig.class.getFields())
239 | {
240 | final Setting s = f.getAnnotation(Setting.class);
241 | Object obj = null;
242 |
243 | try
244 | {
245 | obj = f.get(null).toString();
246 |
247 | if (s == null || obj == null)
248 | continue;
249 | if (!s.group().equals(groupName))
250 | continue;
251 | if (s.level() > CraftConfig.showLevel)
252 | continue;
253 | final Component comp = getSwingComponentForField(f, s);
254 |
255 | if (comp == null)
256 | continue;
257 |
258 | final JLabel label = new JLabel(s.title() + ":");
259 | JButton helpButton = null;
260 |
261 | if (!s.description().equals(""))
262 | {
263 | helpButton = new JButton("?");
264 | helpButton.setMargin(new java.awt.Insets(0, 1, 0, 1));
265 | helpButton.addActionListener(new ActionListener()
266 | {
267 | public void actionPerformed(ActionEvent e)
268 | {
269 | JOptionPane.showMessageDialog(label, s.description());
270 | }
271 | });
272 | helpButton.setBackground(levelColor(s.level()));
273 | }
274 |
275 | comp.setPreferredSize(new Dimension(100, 25));
276 | c.weightx = 0;
277 | c.gridx = 0;
278 | p.add(helpButton, c);
279 | c.weightx = 1;
280 | c.gridx = 1;
281 | p.add(label, c);
282 | c.gridx = 2;
283 | p.add(comp, c);
284 | c.gridy++;
285 | } catch (IllegalArgumentException e)
286 | {
287 | e.printStackTrace();
288 | } catch (IllegalAccessException e)
289 | {
290 | e.printStackTrace();
291 | }
292 | }
293 | return c.gridy;
294 | }
295 |
296 | private Color levelColor(int level)
297 | {
298 | switch (level)
299 | {
300 | case Setting.LEVEL_NORMAL:
301 | return Color.ORANGE;
302 | case Setting.LEVEL_ADVANCED:
303 | return Color.YELLOW;
304 | case Setting.LEVEL_KITCHENSINK:
305 | return Color.RED;
306 | }
307 | return Color.WHITE;
308 | }
309 |
310 | private Component getSwingComponentForField(final Field f, Setting s) throws IllegalArgumentException, IllegalAccessException
311 | {
312 | if (f.getType() == Integer.TYPE)
313 | {
314 | if (s.enumName().equals(""))
315 | {
316 | JSpinner spinner = new JSpinner(new SpinnerNumberModel(f.getInt(null), (int) s.minValue(), (int) s.maxValue(), 1));
317 | spinner.addChangeListener(new ChangeListener()
318 | {
319 | public void stateChanged(ChangeEvent e)
320 | {
321 | try
322 | {
323 | f.setInt(null, ((Integer) ((JSpinner) e.getSource()).getValue()).intValue());
324 | CraftConfigLoader.saveConfig(null);
325 | } catch (Exception e1)
326 | {
327 | e1.printStackTrace();
328 | }
329 | }
330 | });
331 | return spinner;
332 | } else
333 | {
334 | Vector items = new Vector();
335 | for (final Field enumField : CraftConfig.class.getFields())
336 | {
337 | String name = enumField.getName();
338 | if (name.startsWith(s.enumName() + "_"))
339 | {
340 | name = name.substring(name.indexOf("_") + 1);
341 | name = name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
342 | name = name.replace('_', ' ');
343 | items.add(name);
344 | }
345 | }
346 | final JComboBox combo = new JComboBox(items);
347 | combo.setSelectedIndex(f.getInt(null));
348 | combo.addActionListener(new ActionListener()
349 | {
350 | public void actionPerformed(ActionEvent e)
351 | {
352 | try
353 | {
354 | f.setInt(null, combo.getSelectedIndex());
355 | CraftConfigLoader.saveConfig(null);
356 | } catch (Exception e1)
357 | {
358 | e1.printStackTrace();
359 | }
360 | }
361 | });
362 | return combo;
363 | }
364 | } else if (f.getType() == Double.TYPE)
365 | {
366 | JSpinner spinner = new JSpinner(new SpinnerNumberModel(f.getDouble(null), s.minValue(), s.maxValue(), 0.01));
367 | spinner.addChangeListener(new ChangeListener()
368 | {
369 | public void stateChanged(ChangeEvent e)
370 | {
371 | try
372 | {
373 | f.setDouble(null, ((Double) ((JSpinner) e.getSource()).getValue()).doubleValue());
374 | CraftConfigLoader.saveConfig(null);
375 | } catch (Exception e1)
376 | {
377 | e1.printStackTrace();
378 | }
379 | }
380 | });
381 | return spinner;
382 | } else if (f.getType() == Boolean.TYPE)
383 | {
384 | JCheckBox checkbox = new JCheckBox();
385 | checkbox.setSelected(f.getBoolean(null));
386 | checkbox.addActionListener(new ActionListener()
387 | {
388 | public void actionPerformed(ActionEvent e)
389 | {
390 | try
391 | {
392 | f.setBoolean(null, ((JCheckBox) e.getSource()).isSelected());
393 | CraftConfigLoader.saveConfig(null);
394 | } catch (Exception e1)
395 | {
396 | e1.printStackTrace();
397 | }
398 | }
399 | });
400 | return checkbox;
401 | } else
402 | {
403 | Logger.error("Unknown field type for config window: " + f.getType());
404 | }
405 | return null;
406 | }
407 | }
408 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/ui/LogWindow.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.ui;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 |
6 | import javax.swing.JFrame;
7 | import javax.swing.JLabel;
8 | import javax.swing.JProgressBar;
9 | import javax.swing.SwingUtilities;
10 |
11 | import daid.sliceAndDaid.config.CraftConfig;
12 | import daid.sliceAndDaid.util.Logger;
13 | import daid.sliceAndDaid.util.LoggingInterface;
14 |
15 | public class LogWindow extends JFrame implements LoggingInterface
16 | {
17 | private static final long serialVersionUID = 1L;
18 |
19 | private JLabel statusLabel;
20 | private JProgressBar progressBar;
21 |
22 | public LogWindow()
23 | {
24 | this.setTitle("SliceAndDaid - " + CraftConfig.VERSION);
25 | this.setResizable(false);
26 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
27 |
28 | this.setLayout(new BorderLayout());
29 |
30 | statusLabel = new JLabel("SliceAndDaidSliceAndDaidSliceAndDaidSliceAndDaidSliceAndDaid");
31 | statusLabel.setMinimumSize(new Dimension(200, statusLabel.getHeight()));
32 | this.add(statusLabel, BorderLayout.NORTH);
33 |
34 | progressBar = new JProgressBar(0, 2);
35 | progressBar.setIndeterminate(true);
36 | progressBar.setStringPainted(false);
37 | this.add(progressBar, BorderLayout.CENTER);
38 |
39 | this.pack();
40 | this.setLocationRelativeTo(null);
41 | this.setVisible(true);
42 |
43 | Logger.register(this);
44 | }
45 |
46 | public void error(String error)
47 | {
48 | }
49 |
50 | public void message(String message)
51 | {
52 | }
53 |
54 | public void updateStatus(final String status)
55 | {
56 | SwingUtilities.invokeLater(new Runnable()
57 | {
58 | public void run()
59 | {
60 | statusLabel.setText(status);
61 | progressBar.setIndeterminate(true);
62 | progressBar.setStringPainted(false);
63 | LogWindow.this.repaint();
64 | }
65 | });
66 | }
67 |
68 | public void warning(String warning)
69 | {
70 | }
71 |
72 | public void dispose()
73 | {
74 | Logger.unRegister(this);
75 | super.dispose();
76 | }
77 |
78 | public void setProgress(final int value, final int max)
79 | {
80 | SwingUtilities.invokeLater(new Runnable()
81 | {
82 | public void run()
83 | {
84 | progressBar.setIndeterminate(false);
85 | progressBar.setStringPainted(true);
86 | progressBar.setValue(value);
87 | progressBar.setMaximum(max);
88 | LogWindow.this.repaint();
89 | }
90 | });
91 |
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/ui/PreviewFrame.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.ui;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Color;
5 | import java.awt.Graphics;
6 | import java.awt.event.MouseEvent;
7 | import java.awt.event.MouseMotionListener;
8 | import java.util.Vector;
9 |
10 | import javax.swing.BoxLayout;
11 | import javax.swing.JFrame;
12 | import javax.swing.JLabel;
13 | import javax.swing.JPanel;
14 | import javax.swing.JSpinner;
15 | import javax.swing.SpinnerNumberModel;
16 | import javax.swing.event.ChangeEvent;
17 | import javax.swing.event.ChangeListener;
18 |
19 | import daid.sliceAndDaid.Layer;
20 | import daid.sliceAndDaid.Segment2D;
21 | import daid.sliceAndDaid.util.Vector2;
22 |
23 | public class PreviewFrame extends JFrame
24 | {
25 | private static final long serialVersionUID = 1L;
26 | private Vector layers;
27 |
28 | public class PreviewPanel extends JPanel implements MouseMotionListener
29 | {
30 | private static final long serialVersionUID = 1L;
31 |
32 | public int showLayer = 0;
33 | public double drawScale = 5.0;
34 | public double viewOffsetX, viewOffsetY;
35 |
36 | private int oldX, oldY;
37 |
38 | public PreviewPanel()
39 | {
40 | addMouseMotionListener(this);
41 | }
42 |
43 | public void paint(Graphics g)
44 | {
45 | super.paint(g);
46 | for (Segment2D s : layers.get(showLayer).modelSegmentList)
47 | {
48 | drawSegment(g, s);
49 | }
50 | for (Segment2D s = layers.get(showLayer).pathStart; s != null; s = s.getNext())
51 | {
52 | drawSegment(g, s);
53 | }
54 | }
55 |
56 | private void drawSegment(Graphics g, Segment2D s)
57 | {
58 | switch (s.getType())
59 | {
60 | case Segment2D.TYPE_MODEL_SLICE:
61 | g.setColor(Color.GREEN);
62 | break;
63 | case Segment2D.TYPE_PERIMETER:
64 | g.setColor(Color.BLACK);
65 | break;
66 | case Segment2D.TYPE_FILL:
67 | g.setColor(Color.YELLOW);
68 | break;
69 | case Segment2D.TYPE_MOVE:
70 | g.setColor(Color.BLUE);
71 | break;
72 | default:
73 | g.setColor(Color.RED);
74 | break;
75 | }
76 | drawModelLine(g, s.start, s.end);
77 | Vector2 center = s.start.add(s.end).div(2);
78 | Vector2 normal = center.add(s.getNormal().div(drawScale / 5));
79 | drawModelLine(g, center, normal);
80 | drawModelLine(g, s.start, normal);
81 | if (s.getPrev() == null)
82 | drawModelCircle(g, s.start, 10);
83 | if (s.getNext() == null)
84 | drawModelCircle(g, s.end, 10);
85 | }
86 |
87 | private void drawModelLine(Graphics g, Vector2 start, Vector2 end)
88 | {
89 | g.drawLine((int) ((start.x + viewOffsetX) * drawScale) + this.getWidth() / 2, (int) ((start.y + viewOffsetY) * drawScale) + this.getHeight() / 2, (int) ((end.x + viewOffsetX) * drawScale) + this.getWidth() / 2, (int) ((end.y + viewOffsetY) * drawScale) + this.getHeight() / 2);
90 | }
91 |
92 | private void drawModelCircle(Graphics g, Vector2 center, int radius)
93 | {
94 | g.drawOval((int) ((center.x + viewOffsetX) * drawScale) + this.getWidth() / 2 - radius / 2, (int) ((center.y + viewOffsetY) * drawScale) + this.getHeight() / 2 - radius / 2, radius, radius);
95 | }
96 |
97 | public void mouseDragged(MouseEvent e)
98 | {
99 | viewOffsetX += (double) (e.getX() - oldX) / drawScale;
100 | viewOffsetY += (double) (e.getY() - oldY) / drawScale;
101 | repaint();
102 | oldX = e.getX();
103 | oldY = e.getY();
104 | }
105 |
106 | public void mouseMoved(MouseEvent e)
107 | {
108 | oldX = e.getX();
109 | oldY = e.getY();
110 | }
111 | }
112 |
113 | public PreviewFrame(Vector layers)
114 | {
115 | final PreviewPanel viewPanel = new PreviewPanel();
116 | JPanel actionPanel = new JPanel();
117 | actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.X_AXIS));
118 | this.setTitle("Preview");
119 | this.layers = layers;
120 |
121 | final JSpinner layerSpinner = new JSpinner(new SpinnerNumberModel(viewPanel.showLayer, 0, layers.size() - 1, 1));
122 | layerSpinner.addChangeListener(new ChangeListener()
123 | {
124 | public void stateChanged(ChangeEvent e)
125 | {
126 | viewPanel.showLayer = ((Integer) layerSpinner.getValue()).intValue();
127 | viewPanel.repaint();
128 | }
129 | });
130 | final JSpinner zoomSpinner = new JSpinner(new SpinnerNumberModel(viewPanel.drawScale, 1.0, 200.0, 1.0));
131 | zoomSpinner.addChangeListener(new ChangeListener()
132 | {
133 | public void stateChanged(ChangeEvent e)
134 | {
135 | viewPanel.drawScale = ((Double) zoomSpinner.getValue()).doubleValue();
136 | viewPanel.repaint();
137 | }
138 | });
139 |
140 | actionPanel.add(new JLabel("Layer:"));
141 | actionPanel.add(layerSpinner);
142 | actionPanel.add(new JLabel("Zoom:"));
143 | actionPanel.add(zoomSpinner);
144 |
145 | this.setLayout(new BorderLayout());
146 | this.add(viewPanel, BorderLayout.CENTER);
147 | this.add(actionPanel, BorderLayout.SOUTH);
148 |
149 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
150 | this.pack();
151 | this.setSize(600, 600);
152 | this.setVisible(true);
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/util/AABBTree.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.util;
2 |
3 | import java.util.Iterator;
4 | import java.util.Stack;
5 |
6 | /**
7 | * The Tree2D stores a binary AABB tree. AABB are rectangle 2D 'objects' The Tree2D allows a fast
8 | * query of all objects in an area.
9 | */
10 | public class AABBTree
11 | {
12 | class TreeNode
13 | {
14 | public TreeNode parent;
15 | public TreeNode child1, child2;
16 | public AABBrect aabb;
17 | public int height;
18 |
19 | public TreeNode(AABBrect aabb)
20 | {
21 | this.aabb = aabb;
22 | if (aabb.node != null)
23 | throw new RuntimeException();
24 | aabb.node = this;
25 | }
26 |
27 | public boolean isLeaf()
28 | {
29 | return child1 == null;
30 | }
31 | }
32 |
33 | private TreeNode root = null;
34 |
35 | public void insert(T e)
36 | {
37 | TreeNode leaf = new TreeNode(e);
38 | if (root == null)
39 | {
40 | root = leaf;
41 | return;
42 | }
43 |
44 | // Find the best sibling for this node
45 | TreeNode node = root;
46 | while (node.isLeaf() == false)
47 | {
48 | TreeNode child1 = node.child1;
49 | TreeNode child2 = node.child2;
50 |
51 | double area = node.aabb.getPerimeter();
52 |
53 | AABBrect combinedAABB = node.aabb.combine(e);
54 | double combinedArea = combinedAABB.getPerimeter();
55 |
56 | // Cost of creating a new parent for this node and the new leaf
57 | double cost = 2.0f * combinedArea;
58 |
59 | // Minimum cost of pushing the leaf further down the tree
60 | double inheritanceCost = 2.0f * (combinedArea - area);
61 |
62 | // Cost of descending into child1
63 | double cost1;
64 | if (child1.isLeaf())
65 | {
66 | AABBrect aabb = e.combine(child1.aabb);
67 | cost1 = aabb.getPerimeter() + inheritanceCost;
68 | } else
69 | {
70 | AABBrect aabb = e.combine(child1.aabb);
71 | double oldArea = child1.aabb.getPerimeter();
72 | double newArea = aabb.getPerimeter();
73 | cost1 = (newArea - oldArea) + inheritanceCost;
74 | }
75 |
76 | // Cost of descending into child2
77 | double cost2;
78 | if (child2.isLeaf())
79 | {
80 | AABBrect aabb = e.combine(child2.aabb);
81 | cost2 = aabb.getPerimeter() + inheritanceCost;
82 | } else
83 | {
84 | AABBrect aabb = e.combine(child2.aabb);
85 | double oldArea = child2.aabb.getPerimeter();
86 | double newArea = aabb.getPerimeter();
87 | cost2 = (newArea - oldArea) + inheritanceCost;
88 | }
89 |
90 | // Descend according to the minimum cost.
91 | if (cost < cost1 && cost < cost2)
92 | {
93 | break;
94 | }
95 |
96 | // Descend
97 | if (cost1 < cost2)
98 | {
99 | node = child1;
100 | } else
101 | {
102 | node = child2;
103 | }
104 | }
105 |
106 | TreeNode sibling = node;
107 |
108 | // Create a new parent.
109 | TreeNode oldParent = sibling.parent;
110 | TreeNode newParent = new TreeNode(e.combine(sibling.aabb));
111 | newParent.parent = oldParent;
112 | newParent.height = sibling.height + 1;
113 |
114 | if (oldParent != null)
115 | {
116 | // The sibling was not the root.
117 | if (oldParent.child1 == sibling)
118 | {
119 | oldParent.child1 = newParent;
120 | } else
121 | {
122 | oldParent.child2 = newParent;
123 | }
124 |
125 | newParent.child1 = sibling;
126 | newParent.child2 = leaf;
127 | sibling.parent = newParent;
128 | leaf.parent = newParent;
129 | } else
130 | {
131 | // The sibling was the root.
132 | newParent.child1 = sibling;
133 | newParent.child2 = leaf;
134 | sibling.parent = newParent;
135 | leaf.parent = newParent;
136 | root = newParent;
137 | }
138 |
139 | // Walk back up the tree fixing heights and AABBs
140 | node = leaf.parent;
141 | while (node != null)
142 | {
143 | node = balance(node);
144 |
145 | TreeNode child1 = node.child1;
146 | TreeNode child2 = node.child2;
147 |
148 | node.height = 1 + Math.max(child1.height, child2.height);
149 | node.aabb = child1.aabb.combine(child2.aabb);
150 |
151 | node = node.parent;
152 | }
153 | }
154 |
155 | @SuppressWarnings("unchecked")
156 | public void remove(T e)
157 | {
158 | TreeNode leaf = e.node;
159 | e.node = null;
160 | if (leaf == root)
161 | {
162 | root = null;
163 | return;
164 | }
165 |
166 | TreeNode parent = leaf.parent;
167 | TreeNode grandParent = parent.parent;
168 | TreeNode sibling;
169 | if (parent.child1 == leaf)
170 | {
171 | sibling = parent.child2;
172 | } else
173 | {
174 | sibling = parent.child1;
175 | }
176 |
177 | if (grandParent != null)
178 | {
179 | // Destroy parent and connect sibling to grandParent.
180 | if (grandParent.child1 == parent)
181 | {
182 | grandParent.child1 = sibling;
183 | } else
184 | {
185 | grandParent.child2 = sibling;
186 | }
187 | sibling.parent = grandParent;
188 |
189 | // Adjust ancestor bounds.
190 | TreeNode index = grandParent;
191 | while (index != null)
192 | {
193 | index = balance(index);
194 |
195 | TreeNode child1 = index.child1;
196 | TreeNode child2 = index.child2;
197 |
198 | index.aabb = child1.aabb.combine(child2.aabb);
199 | index.height = 1 + Math.max(child1.height, child2.height);
200 |
201 | index = index.parent;
202 | }
203 | } else
204 | {
205 | root = sibling;
206 | sibling.parent = null;
207 | }
208 | }
209 |
210 | private TreeNode balance(TreeNode A)
211 | {
212 | if (A.isLeaf() || A.height < 2)
213 | {
214 | return A;
215 | }
216 |
217 | TreeNode B = A.child1;
218 | TreeNode C = A.child2;
219 |
220 | int balance = C.height - B.height;
221 |
222 | // Rotate C up
223 | if (balance > 1)
224 | {
225 | TreeNode F = C.child1;
226 | TreeNode G = C.child2;
227 |
228 | // Swap A and C
229 | C.child1 = A;
230 | C.parent = A.parent;
231 | A.parent = C;
232 |
233 | // A's old parent should point to C
234 | if (C.parent != null)
235 | {
236 | if (C.parent.child1 == A)
237 | {
238 | C.parent.child1 = C;
239 | } else
240 | {
241 | C.parent.child2 = C;
242 | }
243 | } else
244 | {
245 | root = C;
246 | }
247 |
248 | // Rotate
249 | if (F.height > G.height)
250 | {
251 | C.child2 = F;
252 | A.child2 = G;
253 | G.parent = A;
254 | A.aabb = B.aabb.combine(G.aabb);
255 | C.aabb = A.aabb.combine(F.aabb);
256 |
257 | A.height = 1 + Math.max(B.height, G.height);
258 | C.height = 1 + Math.max(A.height, F.height);
259 | } else
260 | {
261 | C.child2 = G;
262 | A.child2 = F;
263 | F.parent = A;
264 | A.aabb = B.aabb.combine(F.aabb);
265 | C.aabb = A.aabb.combine(G.aabb);
266 |
267 | A.height = 1 + Math.max(B.height, F.height);
268 | C.height = 1 + Math.max(A.height, G.height);
269 | }
270 |
271 | return C;
272 | }
273 |
274 | // Rotate B up
275 | if (balance < -1)
276 | {
277 | TreeNode D = B.child1;
278 | TreeNode E = B.child2;
279 |
280 | // Swap A and B
281 | B.child1 = A;
282 | B.parent = A.parent;
283 | A.parent = B;
284 |
285 | // A's old parent should point to B
286 | if (B.parent != null)
287 | {
288 | if (B.parent.child1 == A)
289 | {
290 | B.parent.child1 = B;
291 | } else
292 | {
293 | B.parent.child2 = B;
294 | }
295 | } else
296 | {
297 | root = B;
298 | }
299 |
300 | // Rotate
301 | if (D.height > E.height)
302 | {
303 | B.child2 = D;
304 | A.child1 = E;
305 | E.parent = A;
306 | A.aabb = C.aabb.combine(E.aabb);
307 | B.aabb = A.aabb.combine(D.aabb);
308 |
309 | A.height = 1 + Math.max(C.height, E.height);
310 | B.height = 1 + Math.max(A.height, D.height);
311 | } else
312 | {
313 | B.child2 = E;
314 | A.child1 = D;
315 | D.parent = A;
316 | A.aabb = C.aabb.combine(D.aabb);
317 | B.aabb = A.aabb.combine(E.aabb);
318 |
319 | A.height = 1 + Math.max(C.height, D.height);
320 | B.height = 1 + Math.max(A.height, E.height);
321 | }
322 |
323 | return B;
324 | }
325 |
326 | return A;
327 | }
328 |
329 | public class Tree2Dquery implements Iterable
330 | {
331 | private AABBrect area;
332 |
333 | public Tree2Dquery(AABBrect area)
334 | {
335 | this.area = area;
336 | }
337 |
338 | public Iterator iterator()
339 | {
340 | return new Tree2Diterator(area);
341 | }
342 | }
343 |
344 | private class Tree2Diterator implements Iterator
345 | {
346 | private AABBrect area;
347 | private Stack stack;
348 | private T ret;
349 |
350 | public Tree2Diterator(AABBrect area)
351 | {
352 | this.area = area;
353 | stack = new Stack();
354 | if (root != null)
355 | stack.add(root);
356 | }
357 |
358 | @SuppressWarnings("unchecked")
359 | public boolean hasNext()
360 | {
361 | if (ret != null)
362 | return true;
363 | while (stack.size() > 0)
364 | {
365 | TreeNode n = stack.pop();
366 | if (n.aabb.overlap(area))
367 | {
368 | if (n.isLeaf())
369 | {
370 | ret = (T) n.aabb;
371 | return true;
372 | } else
373 | {
374 | stack.push(n.child1);
375 | stack.push(n.child2);
376 | }
377 | }
378 | }
379 | return false;
380 | }
381 |
382 | public T next()
383 | {
384 | if (!hasNext())
385 | return null;
386 | T r = ret;
387 | ret = null;
388 | return r;
389 | }
390 |
391 | public void remove()
392 | {
393 | }
394 | }
395 |
396 | public Tree2Dquery query(AABBrect treeAABB)
397 | {
398 | return new Tree2Dquery(treeAABB);
399 | }
400 | }
401 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/util/AABBrect.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.util;
2 |
3 | public class AABBrect
4 | {
5 | private Vector2 lowerBound, upperBound;
6 |
7 | @SuppressWarnings("unchecked")
8 | public AABBTree.TreeNode node;
9 |
10 | public AABBrect(Vector2 p1, Vector2 p2)
11 | {
12 | lowerBound = p1;
13 | upperBound = p2;
14 | }
15 |
16 | public AABBrect(AABBrect aabb)
17 | {
18 | lowerBound = aabb.lowerBound;
19 | upperBound = aabb.upperBound;
20 | }
21 |
22 | public AABBrect(Vector2 p1, Vector2 p2, double extend)
23 | {
24 | lowerBound = new Vector2(Math.min(p1.x, p2.x) - extend, Math.min(p1.y, p2.y) - extend);
25 | upperBound = new Vector2(Math.max(p1.x, p2.x) + extend, Math.max(p1.y, p2.y) + extend);
26 | }
27 |
28 | public double getPerimeter()
29 | {
30 | double w = upperBound.x - lowerBound.x;
31 | double h = upperBound.y - lowerBound.y;
32 | return (w + h) * 2.0;
33 | }
34 |
35 | public void updateAABB(Vector2 p1, Vector2 p2, double extend)
36 | {
37 | if (node != null)
38 | throw new UnsupportedOperationException("Update on AABBrect while in a AABBTree");
39 | lowerBound = new Vector2(Math.min(p1.x, p2.x) - extend, Math.min(p1.y, p2.y) - extend);
40 | upperBound = new Vector2(Math.max(p1.x, p2.x) + extend, Math.max(p1.y, p2.y) + extend);
41 | }
42 |
43 | public AABBrect combine(AABBrect e)
44 | {
45 | return new AABBrect(new Vector2(Math.min(lowerBound.x, e.lowerBound.x), Math.min(lowerBound.y, e.lowerBound.y)), new Vector2(Math.max(upperBound.x, e.upperBound.x), Math.max(upperBound.y, e.upperBound.y)));
46 | }
47 |
48 | public void addAABB(AABBrect a)
49 | {
50 | if (node != null)
51 | throw new UnsupportedOperationException("addAABB on AABBrect while in a AABBTree");
52 | lowerBound = new Vector2(Math.min(lowerBound.x, a.lowerBound.x), Math.min(lowerBound.y, a.lowerBound.y));
53 | upperBound = new Vector2(Math.max(upperBound.x, a.upperBound.x), Math.max(upperBound.y, a.upperBound.y));
54 | }
55 |
56 | public boolean overlap(AABBrect t)
57 | {
58 | if (t.lowerBound.x - upperBound.x > 0.0f || t.lowerBound.y - upperBound.y > 0.0f)
59 | return false;
60 |
61 | if (lowerBound.x - t.upperBound.x > 0.0f || lowerBound.y - t.upperBound.y > 0.0f)
62 | return false;
63 |
64 | return true;
65 | }
66 |
67 | public double getAABBDist(AABBrect t)
68 | {
69 | return upperBound.add(lowerBound).div(2).sub(t.upperBound.add(t.lowerBound).div(2)).vSize();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/util/GCodeFile.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.util;
2 |
3 | import java.io.FileWriter;
4 | import java.io.IOException;
5 | import java.text.DecimalFormat;
6 |
7 | import daid.sliceAndDaid.config.CraftConfig;
8 |
9 | public class GCodeFile
10 | {
11 | private FileWriter fileWriter;
12 | private double totalExtruderValue = 0;
13 | private double buildTime = 0;
14 | private double lastFeedrate = -1;
15 | private Vector3 oldPos = new Vector3();
16 | private DecimalFormat xyzFormat, eFormat, fFormat;
17 |
18 | public GCodeFile(FileWriter fileWriter)
19 | {
20 | this.fileWriter = fileWriter;
21 | xyzFormat = new DecimalFormat("#.##");
22 | eFormat = new DecimalFormat("#.###");
23 | fFormat = new DecimalFormat("#.#");
24 | }
25 |
26 | public void writeMoveZ(double z, double feedRate, String comment) throws IOException
27 | {
28 | switch (CraftConfig.gcodeType)
29 | {
30 | case CraftConfig.GCODE_FULL:
31 | fileWriter.write("G1 Z" + xyzFormat.format(z) + " F" + fFormat.format(feedRate * 60) + "; " + comment + "\n");
32 | break;
33 | case CraftConfig.GCODE_COMPACT:
34 | if (feedRate != lastFeedrate)
35 | fileWriter.write("G1 Z" + xyzFormat.format(z) + " F" + fFormat.format(feedRate * 60) + "\n");
36 | else
37 | fileWriter.write("G1 Z" + xyzFormat.format(z) + "\n");
38 | break;
39 | case CraftConfig.GCODE_TINY_COMPACT:
40 | if (feedRate != lastFeedrate)
41 | fileWriter.write("G1 Z" + xyzFormat.format(z) + " F" + fFormat.format(feedRate * 60) + "\n");
42 | else
43 | fileWriter.write("G1 Z" + xyzFormat.format(z) + "\n");
44 | break;
45 | }
46 |
47 | doMove(oldPos.x, oldPos.y, z, feedRate);
48 | }
49 |
50 | public void writeMoveXY(double x, double y, double feedRate, String comment) throws IOException
51 | {
52 | switch (CraftConfig.gcodeType)
53 | {
54 | case CraftConfig.GCODE_FULL:
55 | fileWriter.write("G1 X" + xyzFormat.format(x) + " Y" + xyzFormat.format(y) + " F" + fFormat.format(feedRate * 60) + "; " + comment + "\n");
56 | break;
57 | case CraftConfig.GCODE_COMPACT:
58 | if (feedRate != lastFeedrate)
59 | fileWriter.write("G1 X" + xyzFormat.format(x) + " Y" + xyzFormat.format(y) + " F" + fFormat.format(feedRate * 60) + "\n");
60 | else
61 | fileWriter.write("G1 X" + xyzFormat.format(x) + " Y" + xyzFormat.format(y) + "\n");
62 | break;
63 | case CraftConfig.GCODE_TINY_COMPACT:
64 | if (feedRate != lastFeedrate)
65 | fileWriter.write("G1X" + xyzFormat.format(x) + "Y" + xyzFormat.format(y) + "F" + fFormat.format(feedRate * 60) + "\n");
66 | else
67 | fileWriter.write("G1X" + xyzFormat.format(x) + "Y" + xyzFormat.format(y) + "\n");
68 | break;
69 | }
70 |
71 | doMove(x, y, oldPos.z, feedRate);
72 | }
73 |
74 | public void writeMoveXYE(double x, double y, double e, double feedRate, String comment) throws IOException
75 | {
76 | totalExtruderValue += e;
77 | switch (CraftConfig.gcodeType)
78 | {
79 | case CraftConfig.GCODE_FULL:
80 | fileWriter.write("G1 X" + xyzFormat.format(x) + " Y" + xyzFormat.format(y) + " E" + eFormat.format(totalExtruderValue) + " F" + fFormat.format(feedRate * 60) + "; " + comment + "\n");
81 | break;
82 | case CraftConfig.GCODE_COMPACT:
83 | if (lastFeedrate != feedRate)
84 | fileWriter.write("G1 X" + xyzFormat.format(x) + " Y" + xyzFormat.format(y) + " E" + eFormat.format(totalExtruderValue) + " F" + fFormat.format(feedRate * 60) + "\n");
85 | else
86 | fileWriter.write("G1 X" + xyzFormat.format(x) + " Y" + xyzFormat.format(y) + " E" + eFormat.format(totalExtruderValue) + "\n");
87 | break;
88 | case CraftConfig.GCODE_TINY_COMPACT:
89 | if (lastFeedrate != feedRate)
90 | fileWriter.write("G1X" + xyzFormat.format(x) + "Y" + xyzFormat.format(y) + "E" + eFormat.format(totalExtruderValue) + "F" + fFormat.format(feedRate * 60) + "\n");
91 | else
92 | fileWriter.write("G1X" + xyzFormat.format(x) + "Y" + xyzFormat.format(y) + "E" + eFormat.format(totalExtruderValue) + "\n");
93 | break;
94 | }
95 |
96 | doMove(x, y, oldPos.z, feedRate);
97 | }
98 |
99 | public void writeComment(String string) throws IOException
100 | {
101 | switch (CraftConfig.gcodeType)
102 | {
103 | case CraftConfig.GCODE_FULL:
104 | fileWriter.write("; " + string + "\n");
105 | break;
106 | case CraftConfig.GCODE_COMPACT:
107 | break;
108 | case CraftConfig.GCODE_TINY_COMPACT:
109 | break;
110 | }
111 | }
112 |
113 | public void write(String string) throws IOException
114 | {
115 | fileWriter.write(string + "\n");
116 | }
117 |
118 | public void close() throws IOException
119 | {
120 | fileWriter.close();
121 | }
122 |
123 | private void doMove(double x, double y, double z, double feedRate)
124 | {
125 | double dist = oldPos.sub(new Vector3(x, y, z)).vSize();
126 | buildTime += dist / feedRate;
127 | lastFeedrate = feedRate;
128 | }
129 |
130 | public double getBuildTime()
131 | {
132 | return buildTime;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/util/Logger.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.util;
2 |
3 | import java.util.HashSet;
4 |
5 | /**
6 | * Logging class, has static functions for logging.
7 | *
8 | * TODO: Different log listeners can connect to this logging service. So the GUI version can show a nice progress dialog.
9 | */
10 | public class Logger
11 | {
12 | private static HashSet loggers = new HashSet();
13 |
14 | public static void updateStatus(String status)
15 | {
16 | System.out.println(status);
17 | for (LoggingInterface li : loggers)
18 | li.updateStatus(status);
19 | }
20 |
21 | public static void message(String message)
22 | {
23 | System.out.println(message);
24 | for (LoggingInterface li : loggers)
25 | li.message(message);
26 | }
27 |
28 | public static void warning(String warning)
29 | {
30 | System.err.println(warning);
31 | for (LoggingInterface li : loggers)
32 | li.warning(warning);
33 | }
34 |
35 | public static void error(String error)
36 | {
37 | System.err.println(error);
38 | for (LoggingInterface li : loggers)
39 | li.error(error);
40 | }
41 |
42 | public static void setProgress(int value, int max)
43 | {
44 | // System.out.println(value + "/" + max);
45 | for (LoggingInterface li : loggers)
46 | li.setProgress(value, max);
47 | }
48 |
49 | public static void register(LoggingInterface obj)
50 | {
51 | loggers.add(obj);
52 | }
53 |
54 | public static void unRegister(LoggingInterface obj)
55 | {
56 | loggers.remove(obj);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/util/LoggingInterface.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.util;
2 |
3 | public interface LoggingInterface
4 | {
5 | void updateStatus(String status);
6 | void message(String message);
7 | void warning(String warning);
8 | void error(String error);
9 | void setProgress(int value, int max);
10 | }
11 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/util/Triangle.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.util;
2 |
3 | import daid.sliceAndDaid.Segment2D;
4 |
5 | /**
6 | * The triangle class represents a 3D triangle in a 3D model
7 | */
8 | public class Triangle
9 | {
10 | public Vector3[] point = new Vector3[3];
11 |
12 | public Segment2D project2D(double layerZ)
13 | {
14 | Segment2D ret = null;
15 |
16 | if (point[0].z < layerZ && point[1].z >= layerZ && point[2].z >= layerZ)
17 | ret = setSegment(ret, layerZ, point[0], point[2], point[1]);
18 | else if (point[0].z > layerZ && point[1].z <= layerZ && point[2].z <= layerZ)
19 | ret = setSegment(ret, layerZ, point[0], point[1], point[2]);
20 |
21 | else if (point[1].z < layerZ && point[0].z >= layerZ && point[2].z >= layerZ)
22 | ret = setSegment(ret, layerZ, point[1], point[0], point[2]);
23 | else if (point[1].z > layerZ && point[0].z <= layerZ && point[2].z <= layerZ)
24 | ret = setSegment(ret, layerZ, point[1], point[2], point[0]);
25 |
26 | else if (point[2].z < layerZ && point[1].z >= layerZ && point[0].z >= layerZ)
27 | ret = setSegment(ret, layerZ, point[2], point[1], point[0]);
28 | else if (point[2].z > layerZ && point[1].z <= layerZ && point[0].z <= layerZ)
29 | ret = setSegment(ret, layerZ, point[2], point[0], point[1]);
30 | else
31 | {
32 | // Logger.error("Cannot handle triangle:\n" + point[0] + "\n" + point[1] + "\n" +
33 | // point[2] + "\non Z: " + layerZ);
34 | return null;
35 | }
36 | if (Double.isNaN(ret.start.x) || Double.isNaN(ret.end.x))
37 | {
38 | Logger.error("Error on triangle:\n" + point[0] + "\n" + point[1] + "\n" + point[2] + "\non Z: " + layerZ);
39 | }
40 |
41 | return ret;
42 | }
43 |
44 | private Segment2D setSegment(Segment2D ret, double layerZ, Vector3 v0, Vector3 v1, Vector3 v2)
45 | {
46 | double a1 = (layerZ - v0.z) / (v1.z - v0.z);
47 | double a2 = (layerZ - v0.z) / (v2.z - v0.z);
48 | Vector2 start = new Vector2(v0.x + (v1.x - v0.x) * a1, v0.y + (v1.y - v0.y) * a1);
49 | Vector2 end = new Vector2(v0.x + (v2.x - v0.x) * a2, v0.y + (v2.y - v0.y) * a2);
50 | return new Segment2D(Segment2D.TYPE_MODEL_SLICE, start, end);
51 | }
52 |
53 | public Vector3 getNormal()
54 | {
55 | return point[1].sub(point[0]).cross(point[2].sub(point[0])).normal();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/util/Vector2.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.util;
2 |
3 | public class Vector2
4 | {
5 | public final double x, y;
6 |
7 | public Vector2(double x, double y)
8 | {
9 | this.x = x;
10 | this.y = y;
11 | if (Double.isNaN(x) || Double.isNaN(y))
12 | throw new RuntimeException("Vector has NaN component...");
13 | }
14 |
15 | public Vector2 add(Vector2 v)
16 | {
17 | return new Vector2(x + v.x, y + v.y);
18 | }
19 |
20 | public Vector2 sub(Vector2 v)
21 | {
22 | return new Vector2(x - v.x, y - v.y);
23 | }
24 |
25 | public Vector2 div(double f)
26 | {
27 | return new Vector2(x / f, y / f);
28 | }
29 |
30 | public Vector2 mul(double f)
31 | {
32 | return new Vector2(x * f, y * f);
33 | }
34 |
35 | public Vector2 crossZ()
36 | {
37 | return new Vector2(y, -x);
38 | }
39 |
40 | public double dot(Vector2 v)
41 | {
42 | return x * v.x + y * v.y;
43 | }
44 |
45 | public boolean asGoodAsEqual(Vector2 v)
46 | {
47 | return (Math.abs(x - v.x) + Math.abs(y - v.y)) < 0.00001;
48 | }
49 |
50 | public String toString()
51 | {
52 | return x + "," + y;
53 | }
54 |
55 | /**
56 | * Returns a normalized vector with a length of 1, having the same direction as the origonal vector.
57 | */
58 | public Vector2 normal()
59 | {
60 | double d = vSize();
61 | if (d < 0.0000001)
62 | return new Vector2(0, 0);
63 | return new Vector2(x / d, y / d);
64 | }
65 |
66 | /**
67 | * Returns the length of the vector.
68 | */
69 | public double vSize()
70 | {
71 | return Math.sqrt(x * x + y * y);
72 | }
73 |
74 | /**
75 | * Returns the squared length of the vector (faster then vSize())
76 | */
77 | public double vSize2()
78 | {
79 | return x * x + y * y;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/daid/sliceAndDaid/util/Vector3.java:
--------------------------------------------------------------------------------
1 | package daid.sliceAndDaid.util;
2 |
3 | public class Vector3
4 | {
5 | public double x, y, z;
6 |
7 | public Vector3()
8 | {
9 | }
10 |
11 | public Vector3(double x, double y, double z)
12 | {
13 | this.x = x;
14 | this.y = y;
15 | this.z = z;
16 | }
17 |
18 | public String toString()
19 | {
20 | return x + "," + y + "," + z;
21 | }
22 |
23 | public void addToSelf(Vector3 v)
24 | {
25 | x += v.x;
26 | y += v.y;
27 | z += v.z;
28 | }
29 |
30 | public Vector3 sub(Vector3 v)
31 | {
32 | return new Vector3(x - v.x, y - v.y, z - v.z);
33 | }
34 |
35 | public Vector3 cross(Vector3 v)
36 | {
37 | return new Vector3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
38 | }
39 |
40 | public double dot(Vector3 v)
41 | {
42 | return x * v.x + y * v.y + z * v.z;
43 | }
44 |
45 | public Vector3 normal()
46 | {
47 | return div(vSize());
48 | }
49 |
50 | public Vector3 div(double f)
51 | {
52 | return new Vector3(x / f, y / f, z / f);
53 | }
54 |
55 | public double vSize()
56 | {
57 | return Math.sqrt(x * x + y * y + z * z);
58 | }
59 |
60 | public double vSize2()
61 | {
62 | return x * x + y * y + z * z;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------