use of org.twak.utils.collections.Loop in project chordatlas by twak.
the class CutHoles method cutHoles.
public static void cutHoles(LoopL<Point2d> out, double tol, Map<Point2d, Line> created) {
MultiMap<Boolean, Loop<Point2d>> holeToLoop = new MultiMap<>();
Iterator<Loop<Point2d>> lit = out.iterator();
while (lit.hasNext()) {
// a hole can be a backwards loop...
Loop<Point2d> loop = lit.next();
double area = Loopz.area(loop);
if (Math.abs(area) < tol * tol)
lit.remove();
boolean isHole = area > 0;
holeToLoop.put(isHole, loop);
for (Loop<Point2d> h : loop.holes) {
if (Loopz.area(h) > 0)
h.reverse();
holeToLoop.put(false, h);
}
}
for (Loop<Point2d> hole : holeToLoop.get(false)) {
Point2d origin = new Point2d(Double.MAX_VALUE, 0);
Loopable<Point2d> originL = null;
for (Loopable<Point2d> p : hole.loopableIterator()) {
if (p.get().x < origin.x) {
originL = p;
origin = originL.get();
}
}
LinearForm ray = new LinearForm(0, 1);
ray.findC(origin);
double nearestD = Double.MAX_VALUE;
Loopable<Point2d> nearestL = null;
Point2d nearestH = null;
for (Loop<Point2d> loop : out) {
for (Loopable<Point2d> line : loop.loopableIterator()) {
Point2d a = line.get(), b = line.getNext().get();
if (a.y > origin.y && b.y < origin.y || a.y < origin.y && b.y > origin.y) {
Point2d hit = new Line(a, b).intersects(ray);
if (hit != null && hit.x < origin.x && (origin.x - hit.x < nearestD)) {
nearestD = origin.x - hit.x;
nearestL = line;
nearestH = hit;
}
}
}
}
if (nearestH == null)
System.err.println("failed to find outer ring for hole");
else {
if (created != null)
created.put(nearestH, new Line(nearestL.get(), nearestL.getNext().get()));
Loopable<Point2d> hitPtF = new Loopable<Point2d>(nearestH), hitPtS = new Loopable<Point2d>(nearestH), originL2 = new Loopable(origin);
hitPtS.setNext(nearestL.getNext());
hitPtF.setPrev(nearestL);
hitPtS.getNext().setPrev(hitPtS);
hitPtF.getPrev().setNext(hitPtF);
originL.getNext().setPrev(originL2);
originL2.setNext(originL.getNext());
originL.setNext(hitPtS);
hitPtS.setPrev(originL);
hitPtF.setNext(originL2);
originL2.setPrev(hitPtF);
}
out.remove(hole);
}
}
use of org.twak.utils.collections.Loop in project chordatlas by twak.
the class GreebleSkel method mapTo2d.
protected void mapTo2d(Face f, Loop<LPoint3d> ll, MiniFacade mf, WallTag wallTag, Set<QuadF> features, MatMeshBuilder faceMaterial) {
Matrix4d to2dXY = new Matrix4d();
Vector3d up = f.edge.uphill, along = f.edge.direction(), out = f.edge.getPlaneNormal();
along.normalize();
to2dXY.setRow(2, up.x, up.y, up.z, 0);
to2dXY.setRow(1, out.x, out.y, out.z, 0);
to2dXY.setRow(0, -along.x, -along.y, -along.z, 0);
Point3d bottomS = f.definingSE.iterator().next().getStart(f), bottomE = f.definingSE.iterator().next().getEnd(f);
Point3d start = new Point3d(bottomS);
Point3d end = new Point3d(bottomE);
to2dXY.m33 = 1;
to2dXY.transform(start);
to2dXY.m03 = -start.x;
to2dXY.m13 = -start.y;
to2dXY.m23 = -start.z;
start = new Point3d(bottomS);
to2dXY.transform(start);
to2dXY.transform(end);
Loop<LPoint2d> flat = GreebleHelper.to2dLoop(GreebleHelper.transform(ll, to2dXY), 1);
Matrix4d to3d = new Matrix4d(to2dXY);
to3d.invert();
{
// face in z-up, we're in y-up
double[] one = new double[4], two = new double[4];
to3d.getRow(1, one);
to3d.getRow(2, two);
to3d.setRow(1, two);
to3d.setRow(2, one);
}
// now in jme space
Matrix4d to2d = new Matrix4d(to3d);
to2d.invert();
MiniFacade forFace = null;
if (mf != null) {
forFace = new MiniFacade(mf);
forFace.rects.clear();
}
LinearForm3D facePlane = new LinearForm3D(new Vector3d(out.x, out.z, out.y), new Point3d(bottomS.x, bottomS.z, bottomS.y));
LoopL<Point2d> sides = null;
DRectangle facadeRect = null;
if (wallTag != null) {
sides = findRectagle(flat, Pointz.to2(start), Pointz.to2(end));
if (sides != null)
facadeRect = findRect(sides.remove(0));
}
List<DRectangle> floors = new ArrayList();
List<MeshBuilder> materials = new ArrayList();
if (wallTag != null && facadeRect != null && mf != null && wallTag.isGroundFloor && mf.groundFloorHeight > 0 && wallTag.groundFloorColor != null && facadeRect.x < mf.groundFloorHeight && facadeRect.getMaxX() > mf.groundFloorHeight) {
floors.addAll(facadeRect.splitY(mf.groundFloorHeight));
MatMeshBuilder gfm = greebleGrid.mbs.get("brick", wallTag.groundFloorColor);
for (Loop<Point2d> loop : sides) {
Loop<Point2d>[] cut = Loopz.cutConvex(loop, new LinearForm(0, 1, mf.groundFloorHeight));
faceMaterial.add(cut[1].singleton(), to3d);
gfm.add(cut[0].singleton(), to3d);
}
materials.add(gfm);
materials.add(faceMaterial);
} else {
floors.add(facadeRect);
materials.add(faceMaterial);
if (sides != null)
faceMaterial.add(sides, to3d);
}
for (int j = 0; j < floors.size(); j++) {
DRectangle floorRect = floors.get(j);
MeshBuilder m = materials.get(j);
Iterator<QuadF> quit = features.iterator();
while (quit.hasNext()) {
QuadF n = quit.next();
if (n.project(to2d, to3d, flat, facePlane, new Vector3d(along.y, 0, -along.x)) && wallTag != null && floorRect != null && forFace != null) {
// set the vertical bounds, so we can just render in 2d
FRect bounds = new FRect(n.original);
n.setBounds(to2d, bounds);
if (floorRect.contains(bounds)) {
forFace.rects.put(n.original.f, bounds);
quit.remove();
}
}
}
if (wallTag == null || forFace == null || floorRect == null) {
m.add(flat.singleton(), to3d);
return;
}
List<DRectangle> occlusions = new ArrayList<>();
for (LineHeight lh : wallTag.occlusions) {
Point3d s = new Point3d(lh.start.x, lh.start.y, lh.min), e = new Point3d(lh.end.x, lh.end.y, lh.max);
to2dXY.transform(s);
to2dXY.transform(e);
occlusions.add(new DRectangle(Math.min(s.x, e.x), s.z, Math.abs(e.x - s.x), Math.abs(e.z - s.z)));
}
if (mf.texture == null)
greebleGrid.buildGrid(floorRect, to3d, forFace, m, wallTag);
else
greebleGrid.textureGrid(floorRect, to3d, mf);
}
}
use of org.twak.utils.collections.Loop in project chordatlas by twak.
the class GISGen method importMesh.
private void importMesh(int index) {
LoopL<Point3d> polies = blocks.get(index);
List<Vector2D> verts = polies.stream().flatMap(ll -> ll.streamAble()).map(x -> {
Line3d l = new Line3d(x.get(), x.getNext().get());
l.move(perp(l.dir(), EXPAND_MESH));
return new Vector2D(l.start.x, l.start.z);
}).collect(Collectors.toList());
double tol = 0.0001;
ConvexHull2D chull = null;
while (tol < 10) {
try {
chull = new MonotoneChain(false, tol).generate(verts);
tol = 1000;
} catch (ConvergenceException e) {
tol *= 10;
}
}
if (chull == null) {
System.out.println("unable to find hull");
return;
}
Loop<Point3d> hull = new Loop<Point3d>((Arrays.stream(chull.getLineSegments()).map(x -> new Point3d(x.getStart().getX(), 0, x.getStart().getY())).collect(Collectors.toList())));
File root = new File(Tweed.SCRATCH + "meshes" + File.separator);
int i = 0;
File l;
while ((l = new File(root, "" + i)).exists()) i++;
l.mkdirs();
File croppedFile = new File(l, CROPPED_OBJ);
boolean found = false;
for (Gen gen : tweed.frame.gens(MiniGen.class)) {
// minigen == optimised obj
((MiniGen) gen).clip(hull, croppedFile);
found = true;
}
if (!found)
for (Gen gen : tweed.frame.gens(MeshGen.class)) {
// obj == just import whole obj
ObjGen objg = (ObjGen) gen;
try {
Files.asByteSource(objg.getFile()).copyTo(Files.asByteSink(croppedFile));
objg.setVisible(false);
found = true;
} catch (IOException e) {
e.printStackTrace();
}
}
if (found) {
Graph2D g2 = new Graph2D();
polies.stream().flatMap(ll -> ll.streamAble()).forEach(x -> g2.add(new Point2d(x.get().x, x.get().z), new Point2d(x.getNext().get().x, x.getNext().get().z)));
g2.removeInnerEdges();
// new Plot (true, g2 );
UnionWalker uw = new UnionWalker();
for (Point2d p : g2.map.keySet()) for (Line line : g2.map.get(p)) uw.addEdge(line.end, line.start);
// new Plot (true, new ArrayList( uw.map.keySet()) );
Loopz.writeXZObj(uw.findAll(), new File(l, "gis.obj"), true);
Loopz.writeXZObj(Loopz.to2dLoop(polies, 1, null), new File(l, "gis_footprints.obj"), false);
BlockGen bg = new BlockGen(l, tweed, polies);
lastMesh.put(index, bg);
tweed.frame.addGen(bg, true);
tweed.frame.setSelected(bg);
} else
JOptionPane.showMessageDialog(tweed.frame(), "Failed to find mesh from minimesh or gml layers");
}
use of org.twak.utils.collections.Loop in project chordatlas by twak.
the class LineGen3d method calculate.
@Override
public void calculate() {
for (Spatial s : gNode.getChildren()) s.removeFromParent();
{
Geometry geom;
Mesh m = new Mesh();
m.setMode(Mesh.Mode.Lines);
List<Float> coords = new ArrayList();
List<Integer> inds = new ArrayList();
for (Line3d l : getLines()) {
inds.add(inds.size());
inds.add(inds.size());
coords.add((float) l.start.x);
coords.add((float) l.start.y);
coords.add((float) l.start.z);
coords.add((float) l.end.x);
coords.add((float) l.end.y);
coords.add((float) l.end.z);
}
m.setBuffer(VertexBuffer.Type.Position, 3, Arrayz.toFloatArray(coords));
m.setBuffer(VertexBuffer.Type.Index, 2, Arrayz.toIntArray(inds));
geom = new Geometry(filename, m);
geom.setCullHint(CullHint.Never);
Material lineMaterial = new Material(tweed.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
lineMaterial.setColor("Color", new ColorRGBA(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 1f));
geom.setMaterial(lineMaterial);
geom.setLocalTranslation(0, 0, 0);
gNode.attachChild(geom);
}
// int c = 0;
VertexBuffer emptyVB = new VertexBuffer(Type.Index);
emptyVB.setupData(Usage.Static, 3, Format.UnsignedShort, BufferUtils.createShortBuffer(0));
{
Geometry geom;
Random randy = new Random();
for (Map.Entry<Loop<Point3d>, Integer> e : getFaces().entrySet()) {
Loop<Point3d> p = e.getKey();
final int callbackI = e.getValue();
Mesh m = Jme3z.fromLoop(p);
// m.setMode( Mesh.Mode.Triangles );
//
// List<Integer> inds = new ArrayList<>();
// List<Float> pos = new ArrayList<>();
// List<Float> norms = new ArrayList<>();
//
// Loopz.triangulate( p, true, inds, pos, norms );
//
// m.set setVe( Type.Index, Arrayz.toIntArray(inds));
geom = new Geometry(filename, m);
geom.setCullHint(CullHint.Never);
// ColorRGBA col = Jme3z.toJme( Rainbow.getColour( c++ ) );
ColorRGBA col = new ColorRGBA(color.getRed() * randy.nextFloat() / 500f + 0.1f, color.getGreen() * randy.nextFloat() / 500f + 0.1f, color.getBlue() * randy.nextFloat() / 500f + 0.1f, 1f);
Material mat = new Material(tweed.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
mat.setColor("Diffuse", col);
mat.setColor("Ambient", col);
mat.setBoolean("UseMaterialColors", true);
geom.setUserData(ClickMe.class.getSimpleName(), new Object[] { new ClickMe() {
@Override
public void clicked(Object data) {
polyClicked(callbackI);
}
} });
geom.setUserData(Gen.class.getSimpleName(), new Object[] { this });
geom.setMaterial(mat);
geom.setLocalTranslation(0, 0, 0);
if (TweedSettings.settings.LOD) {
LodGenerator lod = new LodGenerator(geom);
lod.bakeLods(LodGenerator.TriangleReductionMethod.COLLAPSE_COST, 10, 100);
GISLodControl lc = new GISLodControl();
lc.setTrisPerPixel(0.000001f);
geom.addControl(lc);
}
gNode.attachChild(geom);
}
}
gNode.updateModelBound();
super.calculate();
}
Aggregations