// For users of this class: Don't fill the maps directly, call // mapAnchor + mapCurve. srecord noeq G22MeshMapping(G22Mesh mesh1, G22Mesh mesh2) { delegate Anchor, Curve to G22Mesh. // A mapping of a curve to another curve may be "flipped" // (start becomes end and end becomes start) srecord MappedCurve(Curve curve, bool flipped) { Curve get() { ret curve; } Anchor start() { ret curve.anchor(flipped); } Anchor end() { ret curve.anchor(!flipped); } } // For both maps: keys are from mesh1, values are from mesh2 BijectiveMap anchorMap = new BijectiveMap(true); // forward (mesh1 -> mesh2) and backward (mesh2 -> mesh1) new LinkedHashMap curveMap; new LinkedHashMap curveBackwardMap; // General validity check that should cover all bases void validityCheck() { if (mesh1 == mesh2) fail("Mesh1 and mesh2 must not be the same object"); // For anchors, we only need to check that they come from the // right meshes. Bijectiveness is ensured by anchorMap itself. for (a1, a2 : anchorMap) { assertAnchorInMesh(a1, +mesh1); assertAnchorInMesh(a2, +mesh2); } for (c1, c2 : curveMap) { assertTrue(c1 + " is in mesh1", mesh1.containsCurve(c1)); assertTrue(c2 + " is in mesh2", mesh2.containsCurve(c2!)); // Check compatibility with anchor mappings assertEquals("Start point", c2.start(), getAnchorMapping(c1.start)); assertEquals("End point", c2.end(), getAnchorMapping(c1.end)); } } S validityError() { try { validityCheck(); null; } catch e { ret "Validity error: " + e.getMessage(); } } bool isValid() { ret validityError() == null; } // A mapping is complete iff all anchors and curves from both // meshes are covered. bool isComplete() { ret allEq(l(anchorMap), l(mesh1.anchors()), l(mesh2.anchors())) && allEq(l(curveMap), l(mesh1.curves()), l(mesh2.curves())); } void assertAnchorInMesh(Anchor a, S meshName, G22Mesh mesh) { assertTrue(a + " is in " + meshName, mesh.containsAnchor(a)); } AutoCloseable tempMapAnchor(Anchor a1, Anchor a2) { assertAnchorInMesh(a1, +mesh1); assertAnchorInMesh(a2, +mesh2); ret tempMapPut(anchorMap, a1, a2); } void mapAnchor(Anchor a1, Anchor a2) { assertAnchorInMesh(a1, +mesh1); assertAnchorInMesh(a2, +mesh2); anchorMap.put(a1, a2); } // Note: doesn't remove the curve mappings void unmapAnchor(Anchor a1) { anchorMap.remove(a1); } AutoCloseable tempMapCurve(Curve c1, Curve c2, bool flipped) { new L closers; var forwardMapping = new MappedCurve(c2, flipped); var backwardMapping = new MappedCurve(c1, flipped); closers.add(tempMapPut(curveMap, c1, forwardMapping)); closers.add(tempMapPut(curveBackwardMap, c2, backwardMapping)); // Automatically map the anchors closers.add(tempMapAnchor(c1.start, forwardMapping.start()); closers.add(tempMapAnchor(c1.end, forwardMapping.end()); ret combineAutoCloseables(closers); } void mapCurve(Curve c1, Curve c2, bool flipped) { var forwardMapping = new MappedCurve(c2, flipped); var backwardMapping = new MappedCurve(c1, flipped); curveMap.put(c1, forwardMapping); curveBackwardMap.put(c2, backwardMapping); // Automatically map the anchors mapAnchor(c1.start, forwardMapping.start()); mapAnchor(c1.end, forwardMapping.end()); } // The following functions take anchors and curves from either mesh Anchor getAnchorMapping aka get(Anchor a) { try object anchorMap.get(a); ret anchorMap.inverseGet(a); } MappedCurve getCurveMapping aka get(Curve c) { try object curveMap.get(c); ret curveBackwardMap.get(c); } bool isMapped(Anchor a) { ret get(a) != null; } bool isMapped(Curve c) { ret get(c) != null; } L anchorMappingIndices() { var idx = mapItemsToListIndex(mesh2.anchorList()); ret map(mesh1.anchorList(), a -> idx.get(get(a))); } L curveMappingIndices() { var idx = mapItemsToListIndex(mesh2.curveList()); ret map(mesh1.curveList(), a -> { var c = get(a); ret c == null ?: idx.get(c!); }); } toString { ret "Mapped anchors: " + anchorMappingIndices() + ", mapped curves: " + curveMappingIndices(); } void drawMappedPartOfMesh1(Graphics2D g) { for (anchor : keys(anchorMap)) new G22VisualizeMeshes().drawAnchor(g, anchor.pt); for (curve : keys(curveMap)) new G22VisualizeMeshes().drawCurve(g, curve); } void drawMappedPartOfMesh2(Graphics2D g) { for (anchor : values(anchorMap)) new G22VisualizeMeshes().drawAnchor(g, anchor.pt); for (curve : values(curveMap)) { new G22VisualizeMeshes vm; if (curve.flipped) vm.curveColor(Color.green); vm.drawCurve(g, curve!); } } }