package oripa.fold; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.swing.JOptionPane; import javax.vecmath.Vector2d; import oripa.ORIPA; import oripa.geom.GeomUtil; import oripa.value.CalculationResource; import oripa.value.OriLine; public class OrigamiModelFactory { final public static int NO_OVERLAP = 0; final public static int UPPER = 1; final public static int LOWER = 2; final public static int UNDEFINED = 9; int debugCount = 0; // should not be in this class //public boolean hasModel = false; private OriVertex addAndGetVertexFromVVec( List<OriVertex> vertices, Vector2d p) { OriVertex vtx = null; for (OriVertex v : vertices) { if (GeomUtil.Distance(v.p, p) < CalculationResource.POINT_EPS) { vtx = v; } } if (vtx == null) { vtx = new OriVertex(p); vertices.add(vtx); } return vtx; } //TODO: change as: throw error if creation failed. public OrigamiModel buildOrigami( Collection<OriLine> creasePattern, double paperSize, boolean needCleanUp) { OrigamiModel origamiModel = new OrigamiModel(paperSize); List<OriFace> faces = origamiModel.getFaces(); List<OriEdge> edges = origamiModel.getEdges(); List<OriVertex> vertices = origamiModel.getVertices(); edges.clear(); vertices.clear(); faces.clear(); for (OriLine l : creasePattern) { if (l.typeVal == OriLine.TYPE_NONE) { continue; } OriVertex sv = addAndGetVertexFromVVec(vertices, l.p0); OriVertex ev = addAndGetVertexFromVVec(vertices, l.p1); OriEdge eg = new OriEdge(sv, ev, l.typeVal); edges.add(eg); sv.addEdge(eg); ev.addEdge(eg); } for (OriVertex v : vertices) { for (OriEdge e : v.edges) { if (e.type == OriLine.TYPE_CUT) { continue; } if (v == e.sv) { if (e.left != null) { continue; } } else { if (e.right != null) { continue; } } OriFace face = new OriFace(); faces.add(face); OriVertex walkV = v; OriEdge walkE = e; debugCount = 0; while (true) { if (debugCount++ > 200) { System.out.println("ERROR"); return origamiModel; } OriHalfedge he = new OriHalfedge(walkV, face); face.halfedges.add(he); he.tmpInt = walkE.type; if (walkE.sv == walkV) { walkE.left = he; } else { walkE.right = he; } walkV = walkE.oppositeVertex(walkV); walkE = walkV.getPrevEdge(walkE); if (walkV == v) { break; } } face.makeHalfedgeLoop(); face.setOutline(); face.setPreOutline(); } } makeEdges(edges, faces); for (OriEdge e : edges) { e.type = e.left.tmpInt; } origamiModel.setHasModel(true); origamiModel.setProbablyFoldable(true); return origamiModel; } public OrigamiModel createOrigamiModel3( Collection<OriLine> creasePattern, double paperSize) { return this.createOrigamiModel3(creasePattern, paperSize, false); } public OrigamiModel createOrigamiModel3NoDuplicateLines( Collection<OriLine> creasePattern, double paperSize) { return this.createOrigamiModel3(creasePattern, paperSize, true); } /** * * @param creasePattern * @param paperSize * @param needCleanUp * @return A model data converted from crease pattern. */ //TODO: change as: return OrigamiModel. throw error if creation failed. public OrigamiModel createOrigamiModel3( Collection<OriLine> creasePattern, double paperSize, boolean needCleanUp) { OrigamiModel origamiModel = new OrigamiModel(paperSize); List<OriFace> faces = origamiModel.getFaces(); List<OriEdge> edges = origamiModel.getEdges(); List<OriVertex> vertices = origamiModel.getVertices(); List<OriFace> sortedFaces = origamiModel.getSortedFaces(); sortedFaces.clear(); edges.clear(); vertices.clear(); faces.clear(); // Remove lines with the same position debugCount = 0; if (needCleanUp) { if (cleanDuplicatedLines(creasePattern)) { JOptionPane.showMessageDialog( ORIPA.mainFrame, "Removing multiples edges with the same position ", "Simplifying CP", JOptionPane.INFORMATION_MESSAGE); } } // Create the edges from the vertexes for (OriLine l : creasePattern) { if (l.typeVal == OriLine.TYPE_NONE) { continue; } OriVertex sv = addAndGetVertexFromVVec(vertices, l.p0); OriVertex ev = addAndGetVertexFromVVec(vertices, l.p1); OriEdge eg = new OriEdge(sv, ev, l.typeVal); edges.add(eg); sv.addEdge(eg); ev.addEdge(eg); } // Check if there are vertexes with just 2 collinear edges with same type // merge the edges and delete the vertex for efficiency ArrayList<OriEdge> eds = new ArrayList<OriEdge>(); ArrayList<OriVertex> tmpVVec = new ArrayList<OriVertex>(); tmpVVec.addAll(vertices); for (OriVertex v : tmpVVec) { eds.clear(); for (OriEdge e : edges) { if (e.sv == v || e.ev == v) { eds.add(e); } } if (eds.size() != 2) { continue; } // If the types of the edges are different, do nothing if (eds.get(0).type != eds.get(1).type) { continue; } OriEdge e0 = eds.get(0); OriEdge e1 = eds.get(1); // Check if they are collinear Vector2d dir0 = new Vector2d(e0.ev.p.x - e0.sv.p.x, e0.ev.p.y - e0.sv.p.y); Vector2d dir1 = new Vector2d(e1.ev.p.x - e1.sv.p.x, e1.ev.p.y - e1.sv.p.y); dir0.normalize(); dir1.normalize(); if (GeomUtil.Distance(dir0, dir1) > 0.001 && Math.abs(GeomUtil.Distance(dir0, dir1) - 2.0) > 0.001) { continue; } // found mergeable edge edges.remove(e0); edges.remove(e1); vertices.remove(v); e0.sv.edges.remove(e0); e0.ev.edges.remove(e0); e1.sv.edges.remove(e1); e1.ev.edges.remove(e1); if (e0.sv == v && e1.sv == v) { OriEdge ne = new OriEdge(e0.ev, e1.ev, e0.type); edges.add(ne); ne.sv.addEdge(ne); ne.ev.addEdge(ne); } else if (e0.sv == v && e1.ev == v) { OriEdge ne = new OriEdge(e0.ev, e1.sv, e0.type); edges.add(ne); ne.sv.addEdge(ne); ne.ev.addEdge(ne); } else if (e0.ev == v && e1.sv == v) { OriEdge ne = new OriEdge(e0.sv, e1.ev, e0.type); edges.add(ne); ne.sv.addEdge(ne); ne.ev.addEdge(ne); } else { OriEdge ne = new OriEdge(e0.sv, e1.sv, e0.type); edges.add(ne); ne.sv.addEdge(ne); ne.ev.addEdge(ne); } } // System.out.println("vnum=" + vertices.size()); // System.out.println("enum=" + edges.size()); // Construct the faces for (OriVertex v : vertices) { for (OriEdge e : v.edges) { if (e.type == OriLine.TYPE_CUT) { continue; } if (v == e.sv) { if (e.left != null) { continue; } } else { if (e.right != null) { continue; } } OriFace face = new OriFace(); faces.add(face); OriVertex walkV = v; OriEdge walkE = e; debugCount = 0; while (true) { if (debugCount++ > 100) { System.out.println("ERROR"); // throw new UnfoldableModelException("algorithmic error"); return origamiModel; } OriHalfedge he = new OriHalfedge(walkV, face); face.halfedges.add(he); he.tmpInt = walkE.type; if (walkE.sv == walkV) { walkE.left = he; } else { walkE.right = he; } walkV = walkE.oppositeVertex(walkV); walkE = walkV.getPrevEdge(walkE); if (walkV == v) { break; } } face.makeHalfedgeLoop(); face.setOutline(); face.setPreOutline(); } } makeEdges(edges, faces); for (OriEdge e : edges) { e.type = e.left.tmpInt; } origamiModel.setHasModel(true); origamiModel.setProbablyFoldable(checkPatternValidity(edges, vertices, faces)); return origamiModel; } private boolean cleanDuplicatedLines(Collection<OriLine> creasePattern) { debugCount = 0; System.out.println("pre cleanDuplicatedLines " + creasePattern.size()); ArrayList<OriLine> tmpLines = new ArrayList<OriLine>(); for (OriLine l : creasePattern) { OriLine ll = l; boolean bSame = false; // Test if the line is already in tmpLines to prevent duplicity for (OriLine line : tmpLines) { if (GeomUtil.isSameLineSegment(line, ll)) { bSame = true; break; } } if (bSame) { continue; } tmpLines.add(ll); } if (creasePattern.size() == tmpLines.size()) { return false; } creasePattern.clear(); creasePattern.addAll(tmpLines); System.out.println("after cleanDuplicatedLines " + creasePattern.size()); return true; } private boolean checkPatternValidity( List<OriEdge> edges, List<OriVertex> vertices, List<OriFace> faces) { boolean isOK = true; // Check if the faces are convex for (OriFace face : faces) { if (face.halfedges.size() == 3) { continue; } OriHalfedge baseHe = face.halfedges.get(0); boolean baseFlg = GeomUtil.CCWcheck(baseHe.prev.vertex.p, baseHe.vertex.p, baseHe.next.vertex.p); for (int i = 1; i < face.halfedges.size(); i++) { OriHalfedge he = face.halfedges.get(i); if (GeomUtil.CCWcheck(he.prev.vertex.p, he.vertex.p, he.next.vertex.p) != baseFlg) { isOK = false; face.hasProblem = true; break; } } } // Check Maekawa's theorem for all vertexes for (OriVertex v : vertices) { int ridgeCount = 0; int valleyCount = 0; boolean isCorner = false; for (OriEdge e : v.edges) { if (e.type == OriLine.TYPE_RIDGE) { ridgeCount++; } else if (e.type == OriLine.TYPE_VALLEY) { valleyCount++; } else if (e.type == OriLine.TYPE_CUT) { isCorner = true; break; } } if (isCorner) { continue; } if (Math.abs(ridgeCount - valleyCount) != 2) { System.out.println("edge type count invalid: "+ v+" "+Math.abs(ridgeCount - valleyCount)); v.hasProblem = true; isOK = false; } } // Check Kawasaki's theorem for every vertex for (OriVertex v : vertices) { if (v.hasProblem) { continue; } Vector2d p = v.p; double oddSum = 0; double evenSum = 0; boolean isCorner = false; for (int i = 0; i < v.edges.size(); i++) { OriEdge e = v.edges.get(i); if (e.type == OriLine.TYPE_CUT) { isCorner = true; break; } Vector2d preP = new Vector2d(v.edges.get(i).oppositeVertex(v).p); Vector2d nxtP = new Vector2d(v.edges.get((i + 1) % v.edges.size()).oppositeVertex(v).p); nxtP.sub(p); preP.sub(p); if (i % 2 == 0) { oddSum += preP.angle(nxtP); } else { evenSum += preP.angle(nxtP); } } if (isCorner) { continue; } //System.out.println("oddSum = " + oddSum + "/ evenSum = " + evenSum); if (Math.abs(oddSum - Math.PI) > Math.PI / 180 / 2) { System.out.println("edge angle sum invalid"); v.hasProblem = true; isOK = false; } } return isOK; } //boolean sortFinished = false; private void makeEdges(List<OriEdge> edges, List<OriFace> faces) { edges.clear(); ArrayList<OriHalfedge> tmpHalfedges = new ArrayList<OriHalfedge>(); // Clear all the Halfedges for (OriFace face : faces) { for (OriHalfedge he : face.halfedges) { he.pair = null; he.edge = null; tmpHalfedges.add(he); } } // Search the halfedge pair int heNum = tmpHalfedges.size(); for (int i = 0; i < heNum; i++) { OriHalfedge he0 = tmpHalfedges.get(i); if (he0.pair != null) { continue; } for (int j = i + 1; j < heNum; j++) { OriHalfedge he1 = tmpHalfedges.get(j); if (he0.vertex == he1.next.vertex && he0.next.vertex == he1.vertex) { OriEdge edge = new OriEdge(); he0.pair = he1; he1.pair = he0; he0.edge = edge; he1.edge = edge; edge.sv = he0.vertex; edge.ev = he1.vertex; edge.left = he0; edge.right = he1; edges.add(edge); edge.type = OriLine.TYPE_NONE;//OriEdge.TYPE_NONE; } } } // If the pair wasnt found it should be an edge for (OriHalfedge he : tmpHalfedges) { if (he.pair == null) { OriEdge edge = new OriEdge(); he.edge = edge; edge.sv = he.vertex; edge.ev = he.next.vertex; edge.left = he; edges.add(edge); edge.type = OriLine.TYPE_CUT; } } } // public void setCrossLine(List<OriLine> crossLines, OriLine line, List<OriFace> sortedFaces) { // crossLines.clear(); // for (OriFace face : sortedFaces) { // ArrayList<Vector2d> vv = new ArrayList<Vector2d>(); // int crossCount = 0; // for (OriHalfedge he : face.halfedges) { // OriLine l = new OriLine(he.positionForDisplay.x, he.positionForDisplay.y, // he.next.positionForDisplay.x, he.next.positionForDisplay.y, PaintConfig.inputLineType); // // double params[] = new double[2]; // boolean res = GeomUtil.getCrossPointParam(line.p0, line.p1, l.p0, l.p1, params); // if (res == true && params[0] > -0.001 && params[1] > -0.001 && params[0] < 1.001 && params[1] < 1.001) { // double param = params[1]; // crossCount++; // // Vector2d crossV = new Vector2d(); // crossV.x = (1.0 - param) * he.vertex.preP.x + param * he.next.vertex.preP.x; // crossV.y = (1.0 - param) * he.vertex.preP.y + param * he.next.vertex.preP.y; // // boolean isNewPoint = true; // for (Vector2d v2d : vv) { // if (GeomUtil.Distance(v2d, crossV) < 1) { // isNewPoint = false; // break; // } // } // if (isNewPoint) { // vv.add(crossV); // } // } // } // // if (vv.size() >= 2) { // crossLines.add(new OriLine(vv.get(0), vv.get(1), PaintConfig.inputLineType)); // } // } // // } }