Commits

Karsten Schmidt committed f5f5d52

initial checkin of entire eclipse project incl. all classes, 3rd party jars and test app

Comments (0)

Files changed (13)

+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="src.test"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/toxiclibs"/>
+	<classpathentry kind="lib" path="lib/core.jar"/>
+	<classpathentry kind="lib" path="lib/itext.jar"/>
+	<classpathentry kind="lib" path="lib/pdf.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>flatworld</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

.settings/org.eclipse.jdt.ui.prefs

+#Sun Jun 28 19:28:27 BST 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+internal.default.compliance=default
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=true
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=true
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
Binary file added.

lib/itext.jar

Binary file added.
Binary file added.

src.test/flatworld/test/UnwrapTest.java

+package flatworld.test;
+
+import java.util.Iterator;
+
+import processing.core.PApplet;
+import processing.pdf.PGraphicsPDF;
+import toxi.geom.AABB;
+import toxi.geom.mesh.WETriangleMesh;
+import toxi.math.conversion.UnitTranslator;
+import toxi.util.DateUtils;
+import flatworld.EdgeRenderStrategy;
+import flatworld.GlueTabEdgeStrategy;
+import flatworld.UnwrapSheet;
+import flatworld.Unwrapper;
+
+public class UnwrapTest extends PApplet {
+
+    public static void main(String[] args) {
+        PApplet.main(new String[] { "flatworld.test.UnwrapTest" });
+    }
+
+    private Unwrapper unwrap;
+    private int currSheetID;
+    private EdgeRenderStrategy edgeStrategy;
+    private boolean useLabels;
+
+    private int WIDTH = (int) UnitTranslator.millisToPoints(270);
+    private int HEIGHT = (int) UnitTranslator.millisToPoints(190);
+    private int TAB_WIDTH = (int) UnitTranslator.millisToPoints(6);
+
+    private String pdfPath;
+    private PGraphicsPDF pdf;
+
+    public void draw() {
+        if (pdfPath != null) {
+            pdf = (PGraphicsPDF) beginRecord(PDF, pdfPath);
+            pdf.textMode(SHAPE);
+            pdf.textFont(createFont("Arial", 9));
+            pdf.textAlign(CENTER);
+        }
+        background(255);
+        if (pdfPath != null) {
+            for (Iterator<UnwrapSheet> i = unwrap.getSheets().iterator(); i
+                    .hasNext();) {
+                i.next().draw(pdf, useLabels);
+                if (i.hasNext()) {
+                    pdf.nextPage();
+                }
+            }
+            endRecord();
+            pdfPath = null;
+            println("done...");
+        } else {
+            unwrap.getSheets().get(currSheetID).draw(g, useLabels);
+        }
+    }
+
+    public void keyPressed() {
+        if (key == '[' && currSheetID > 0) {
+            currSheetID--;
+        }
+        if (key == ']' && currSheetID < unwrap.getSheets().size() - 1) {
+            currSheetID++;
+        }
+        if (key == 'r') {
+            reset();
+        }
+        if (key == 'l') {
+            useLabels = !useLabels;
+        }
+        if (key == 's') {
+            pdfPath = "flatworld-" + DateUtils.timeStamp() + ".pdf";
+        }
+    }
+
+    private void reset() {
+        // WETriangleMesh mesh =
+        // new WETriangleMesh().addMesh(new Plane().toMesh(300));
+        // WETriangleMesh mesh =
+        // new WETriangleMesh().addMesh(new Sphere(300).toMesh(5));
+        WETriangleMesh mesh =
+                new WETriangleMesh().addMesh(new AABB(200).toMesh());
+        unwrap = new Unwrapper(width, height);
+        unwrap.unwrapMesh(mesh, 0.5f, edgeStrategy);
+        currSheetID = 0;
+    }
+
+    public void setup() {
+        size(WIDTH, HEIGHT);
+        smooth();
+        textFont(createFont("SansSerif", 9));
+        textAlign(CENTER);
+        edgeStrategy = new GlueTabEdgeStrategy(TAB_WIDTH, 0.1f);
+        reset();
+    }
+}

src/flatworld/EdgeRenderStrategy.java

+package flatworld;
+
+import processing.core.PGraphics;
+import toxi.geom.Vec2D;
+
+public abstract class EdgeRenderStrategy {
+
+    public abstract void drawEdge(PGraphics g, UnwrappedFace face, UnwrapEdge edge, Vec2D a, Vec2D b, boolean useLabels);
+    
+    public void drawLabel(PGraphics g, UnwrapEdge edge, Vec2D pos) {
+        g.fill(0);
+        g.text(edge.getID(), pos.x, pos.y);
+    }
+
+    public abstract float getWidthForEdge(UnwrapEdge edge);
+}

src/flatworld/GlueTabEdgeStrategy.java

+package flatworld;
+
+import processing.core.PGraphics;
+import toxi.geom.Line2D;
+import toxi.geom.Vec2D;
+
+public class GlueTabEdgeStrategy extends EdgeRenderStrategy {
+
+    public float width;
+    public float insetPos;
+
+    public GlueTabEdgeStrategy(int w, float inset) {
+        this.width = w;
+        this.insetPos = inset;
+    }
+
+    public void drawEdge(PGraphics g, UnwrappedFace face, UnwrapEdge edge,
+            Vec2D a, Vec2D b, boolean useLabels) {
+        Line2D l = new Line2D(a, b);
+        Vec2D m = l.getMidPoint();
+        Vec2D n = l.getDirection().perpendicular();
+        if (m.sub(face.sheetPos).dot(n) < 0) {
+            n.invert();
+        }
+        g.stroke(0);
+        if (1 == edge.getUseCount() % 2) {
+            Vec2D off = n.normalizeTo(width);
+            g.noFill();
+            g.beginShape();
+            g.vertex(a.x, a.y);
+            Vec2D aa = a.interpolateTo(b, insetPos);
+            g.vertex(aa.x + off.x, aa.y + off.y);
+            Vec2D bb = b.interpolateTo(a, insetPos);
+            g.vertex(bb.x + off.x, bb.y + off.y);
+            g.vertex(b.x, b.y);
+            g.endShape();
+        }
+        g.line(a.x, a.y, b.x, b.y);
+        if (useLabels) {
+            m.subSelf(n.normalizeTo(8));
+            drawLabel(g, edge, m);
+        }
+    }
+
+    @Override
+    public float getWidthForEdge(UnwrapEdge edge) {
+        return 1 == edge.getUseCount() % 2 ? width : 0;
+    }
+
+}

src/flatworld/UnwrapEdge.java

+package flatworld;
+
+import processing.core.PGraphics;
+import toxi.geom.Vec2D;
+import toxi.geom.mesh.WingedEdge;
+
+public class UnwrapEdge {
+
+    protected WingedEdge edge;
+    protected int useCount;
+    protected final EdgeRenderStrategy strategy;
+    
+    public UnwrapEdge(WingedEdge e, int use, EdgeRenderStrategy strategy) {
+        this.edge=e;
+        this.useCount=use;
+        this.strategy = strategy;
+    }
+    
+    public int getID() {
+        return edge.id;
+    }
+
+    public int getUseCount() {
+        return useCount;
+    }
+
+    public void reuse() {
+        useCount++;
+    }
+
+    public void draw(PGraphics g, UnwrappedFace face, Vec2D p, Vec2D q, boolean useLabels) {
+        strategy.drawEdge(g, face, this, p, q, useLabels);
+    }
+
+    public float getOffset() {
+        return strategy.getWidthForEdge(this);
+    }
+}

src/flatworld/UnwrapSheet.java

+package flatworld;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import processing.core.PGraphics;
+import toxi.geom.Rect;
+import toxi.geom.Vec2D;
+import toxi.math.MathUtils;
+
+public class UnwrapSheet {
+
+    protected Rect bounds;
+
+    public UnwrapSheet(int w, int h) {
+        this.bounds = new Rect(bleed, bleed, w-2*bleed, h-2*bleed);
+        this.totalArea = w * h;
+    }
+
+    protected List<UnwrappedFace> faces = new ArrayList<UnwrappedFace>();
+
+    protected float usedArea;
+    protected float totalArea;
+
+    protected float bleed=10;
+    protected float faceBleed = 20;
+
+    public float getFreeArea() {
+        return totalArea - usedArea;
+    }
+
+    public float getFillRatio() {
+        return usedArea / totalArea;
+    }
+
+    public Vec2D getRandomPos() {
+        return new Vec2D(MathUtils.random(bounds.width),
+                MathUtils.random(bounds.height));
+    }
+
+    public boolean isFacePlacable(UnwrappedFace face) {
+        Rect b = face.getBounds();
+        if (bounds.containsPoint(b.getTopLeft())
+                && bounds.containsPoint(b.getBottomRight())) {
+            for (UnwrappedFace f : faces) {
+                if (f.intersectsFace(face))
+                    return false;
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public void add(UnwrappedFace face) {
+        faces.add(face);
+        usedArea += face.getArea();
+    }
+
+    public void draw(PGraphics g, boolean useLabels) {
+        for (UnwrappedFace f : faces) {
+            f.draw(g,useLabels);
+        }
+    }
+}

src/flatworld/UnwrappedFace.java

+package flatworld;
+
+import processing.core.PConstants;
+import processing.core.PGraphics;
+import toxi.geom.Matrix4x4;
+import toxi.geom.Quaternion;
+import toxi.geom.Rect;
+import toxi.geom.Triangle2D;
+import toxi.geom.Vec2D;
+import toxi.geom.Vec3D;
+import toxi.geom.mesh.WEFace;
+import toxi.geom.mesh.WingedEdge;
+
+public class UnwrappedFace {
+
+    private static final Matrix4x4 matrix = new Matrix4x4();
+
+    protected WEFace face;
+    protected Triangle2D tri;
+    protected Triangle2D collTri;
+
+    public Vec2D a, b, c;
+
+    public WingedEdge[] edges = new WingedEdge[3];
+
+    public Vec2D sheetPos;
+
+    public final int id;
+
+    private UnwrapEdge edgeAB;
+    private UnwrapEdge edgeBC;
+    private UnwrapEdge edgeCA;
+
+    public UnwrappedFace(WEFace f, int id, float scale, UnwrapEdge eab,
+            UnwrapEdge ebc, UnwrapEdge eca) {
+        this.id = id;
+        this.edgeAB = eab;
+        this.edgeBC = ebc;
+        this.edgeCA = eca;
+        Quaternion.getAlignmentQuat(Vec3D.Z_AXIS, f.normal).toMatrix4x4(matrix);
+        Vec3D centroid = f.getCentroid();
+        Vec3D aa = matrix.applyToSelf(f.a.sub(centroid));
+        Vec3D bb = matrix.applyToSelf(f.b.sub(centroid));
+        Vec3D cc = matrix.applyToSelf(f.c.sub(centroid));
+        a = aa.to2DXY().scaleSelf(scale);
+        b = bb.to2DXY().scaleSelf(scale);
+        c = cc.to2DXY().scaleSelf(scale);
+    }
+
+    public void setPosition(final Vec2D pos, final float theta) {
+        sheetPos = pos;
+        Vec2D sa = a.getRotated(theta).addSelf(pos);
+        Vec2D sb = b.getRotated(theta).addSelf(pos);
+        Vec2D sc = c.getRotated(theta).addSelf(pos);
+        tri = new Triangle2D(sa, sb, sc);
+        if (tri.isClockwise()) {
+            tri.flipVertexOrder();
+        }
+        collTri =
+                tri.copy().adjustTriangleSizeBy(edgeAB.getOffset(),
+                        edgeBC.getOffset(), edgeCA.getOffset());
+    }
+
+    public boolean intersectsFace(UnwrappedFace f) {
+        return collTri.intersectsTriangle(f.collTri);
+    }
+
+    public float getArea() {
+        return tri.getArea();
+    }
+
+    public Rect getBounds() {
+        return tri.getBounds();
+    }
+
+    public void draw(PGraphics g, boolean useLabels) {
+        edgeAB.draw(g, this, tri.a, tri.b, useLabels);
+        edgeBC.draw(g, this, tri.b, tri.c, useLabels);
+        edgeCA.draw(g, this, tri.c, tri.a, useLabels);
+        // g.stroke(255, 0, 0);
+        // g.noFill();
+        // g.beginShape(PConstants.TRIANGLES);
+        // g.vertex(collTri.a.x, collTri.a.y);
+        // g.vertex(collTri.b.x, collTri.b.y);
+        // g.vertex(collTri.c.x, collTri.c.y);
+        // g.endShape();
+        if (useLabels) {
+            g.fill(0);
+            g.text(id, sheetPos.x, sheetPos.y);
+        }
+    }
+}

src/flatworld/Unwrapper.java

+package flatworld;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import toxi.geom.Vec2D;
+import toxi.geom.mesh.Face;
+import toxi.geom.mesh.Vertex;
+import toxi.geom.mesh.WEFace;
+import toxi.geom.mesh.WETriangleMesh;
+import toxi.geom.mesh.WingedEdge;
+import toxi.math.MathUtils;
+
+public class Unwrapper {
+
+    protected int sheetWidth, sheetHeight;
+
+    protected List<UnwrapSheet> sheets = new ArrayList<UnwrapSheet>();
+    protected int[] usedEdges;
+
+    protected EdgeRenderStrategy edgeStrategy;
+
+    private int maxSearchIterations=100;
+
+    public Unwrapper(int width, int height) {
+        this.sheetWidth = width;
+        this.sheetHeight = height;
+    }
+
+    public void unwrapMesh(WETriangleMesh mesh, float scale, EdgeRenderStrategy edgeStrategy) {
+        this.edgeStrategy=edgeStrategy;
+        sheets.clear();
+        int id = 1;
+        usedEdges = new int[mesh.edges.size()];
+        for (Face f : mesh.getFaces()) {
+            WEFace ff = (WEFace) f;
+            UnwrapEdge eab = getEdgeFor(ff, ff.a, ff.b);
+            UnwrapEdge ebc = getEdgeFor(ff, ff.b, ff.c);
+            UnwrapEdge eca = getEdgeFor(ff, ff.c, ff.a);
+            UnwrappedFace face =
+                    new UnwrappedFace(ff, id++, scale, eab, ebc, eca);
+            boolean isPlaced = attemptToAddFaceToSheet(face, sheets);
+            if (!isPlaced) {
+                UnwrapSheet sheet = new UnwrapSheet(sheetWidth, sheetHeight);
+                attemptToAddFaceToSheet(face, Collections.singletonList(sheet));
+                sheets.add(sheet);
+            }
+        }
+    }
+
+    private UnwrapEdge getEdgeFor(WEFace f, Vertex v, Vertex w) {
+        UnwrapEdge edge = null;
+        for (WingedEdge e : f.edges) {
+            if ((e.a == v && e.b == w) || (e.a == w && e.b == v)) {
+                if (e.faces.size() > 1) {
+                    usedEdges[e.id]++;
+                }
+                edge = new UnwrapEdge(e, usedEdges[e.id],edgeStrategy);
+                break;
+            }
+        }
+        return edge;
+    }
+
+    private boolean attemptToAddFaceToSheet(UnwrappedFace face,
+            List<UnwrapSheet> sheets) {
+        UnwrapSheet bestSheet = null;
+        Vec2D bestPos = null;
+        float bestTheta = 0;
+        float minDist = Float.MAX_VALUE;
+        face.setPosition(new Vec2D(), 0);
+        float faceArea = face.getArea();
+        boolean isPlaced = false;
+        for (UnwrapSheet sheet : sheets) {
+            if (sheet.getFreeArea() >= faceArea) {
+                for (int i = 0; i < maxSearchIterations; i++) {
+                    Vec2D pos = sheet.getRandomPos();
+                    float theta = MathUtils.random(MathUtils.TWO_PI);
+                    face.setPosition(pos, theta);
+                    if (sheet.isFacePlacable(face)) {
+                        float dist = getMinDistanceOnSheet(face, sheet);
+                        if (dist <= minDist) {
+                            bestSheet = sheet;
+                            bestPos = pos;
+                            bestTheta = theta;
+                            minDist = dist;
+                        }
+                    }
+                }
+            }
+        }
+        if (bestSheet != null) {
+            face.setPosition(bestPos, bestTheta);
+            bestSheet.add(face);
+            isPlaced = true;
+        }
+        return isPlaced;
+    }
+
+    private float getMinDistanceOnSheet(UnwrappedFace face, UnwrapSheet sheet) {
+        float minDist = Float.MAX_VALUE;
+        for (UnwrappedFace f : sheet.faces) {
+            float d = face.sheetPos.distanceToSquared(f.sheetPos);
+            if (d < minDist) {
+                minDist = d;
+            }
+        }
+        return minDist;
+    }
+
+    public List<UnwrapSheet> getSheets() {
+        return sheets;
+    }
+}