def cdtFromPSLG(pslg, onlyInner = False): """cdtFromPSLG(pslg, onlyInner) -> GeoMap Return a CDT for the given planar straight line graph. `pslg` should be a GeoMap whose node positions are simple input points and edges define constraint segments. FIXME: ATM, this function may crash if the input contains duplicate points (e.g. within the same edge). This is due to a limitation of the triangle module, i.e. J. Shewchuck's code. """ # return constrainedDelaunayMap( # list(pslg.edgeIter()), pslg.imageSize(), # [node.position() for node in pslg.nodeIter() if node.isIsolated()], # onlyInner = False) points = [] segments = [] holes = [] nodes = [None] * pslg.maxNodeLabel() for node in pslg.nodeIter(): nodes[node.label()] = len(points) points.append(node.position()) for edge in pslg.edgeIter(): l = len(points) edgeSegments = [(l+i-1, l+i) for i in range(len(edge)-1)] edgeSegments[0] = (nodes[edge.startNodeLabel()], edgeSegments[0][1]) edgeSegments[-1] = (edgeSegments[-1][0], nodes[edge.endNodeLabel()]) points.extend(edge[1:-1]) # for i in range(1, len(edge)-1): # assert edge[i] not in points, "%s[%d] = ..%s,%s,%s,.. is already present!" % ( # edge, i, edge[i-1], edge[i], edge[i+1]) # points.append(edge[i]) segments.extend(edgeSegments) for face in pslg.faceIter(skipInfinite = True): if face.flag(OUTER_FACE): # FIXME: re-use available scanlines for face: holes.append(_pointInHole(contourPoly(face.contour()))) print "- performing Constrained Delaunay Triangulation..." print " (%d points, %s segments, %d holes)" % ( len(points), len(segments), len(holes)) nodePositions, edgeData = triangle.constrainedDelaunay( points, segments, onlyInner, holes) print "- storing result in a GeoMap..." result = _delaunayMapFromData(nodePositions, edgeData, pslg.imageSize()) for edge in result.edgeIter(): if edgeData[edge.label()][2]: edge.setFlag(CONTOUR_SEGMENT) result.face(0).setFlag(OUTER_FACE) for holePoint in holes: result.faceAt(holePoint).setFlag(OUTER_FACE) return result
def faceCDTMap(face, imageSize, simplifyEpsilon = None, resample = None, onlyInner = True): """USAGE: dlm = faceCDTMap(face, mapSize) `face` should be a GeoMap.Face object, and all its contours will be extracted. `mapSize` is used to initialize the GeoMap with the resulting edges. Optional keyword parameters: simplifyEpsilon If given, each contour polygon is simplified by calling simplifyPolygon with this epsilon as parameter (default None -> don't use simplifyPolygon). markContour If True(default), the point list is expected to be a sorted list, and edges between successive entries are marked as contour edges (an exception is raised if such a connection is missing). A `jumpPoints` list is used to mark multiple contours. onlyInner If True(default), all edges outside (left) of the marked contour are removed in a post-processing step. (This has no effect if `markContour` is False.)""" polygons = [contourPoly(c) for c in face.contours()] if resample: polygons = [geomap.resamplePolygon(p, resample) for p in polygons] if simplifyEpsilon != None: polygons = [geomap.simplifyPolygon(p, simplifyEpsilon) for p in polygons] if triangle: result = constrainedDelaunayMap(polygons, imageSize) else: result = fakedConstrainedDelaunayMap(polygons, imageSize) return result
def addMapFaces(self, geomap, faceMeans=None, similarity=None, returnFaces=False, container=True, **attr): """fe.addMapFaces(geomap, faceMeans, ...) Adds and returns fig.Polygons for all map faces (or -parts, see addClippedPoly). Clipping closed polygons should work nowadays, too.""" import maputils def getGray(face): faceColor = faceMeans[face.label()] return self.f.gray(int(faceColor)) def getRGB(face): faceColor = faceMeans[face.label()] return self.f.getColor(map(int, tuple(faceColor)), similarity) if not faceMeans: if not hasattr(geomap, "faceMeans"): raise ValueError( "addMapFaces: need faceMeans for proper coloring") faceMeans = geomap.faceMeans if hasattr(faceMeans, "bands"): getFaceColor = getGray if faceMeans.bands() == 3: getFaceColor = getRGB else: getFaceColor = lambda face: faceMeans[face.label()] if container == True: container = self.f attr = dict(attr) attr["lineWidth"] = attr.get("lineWidth", 0) attr["fillStyle"] = attr.get("fillStyle", fig.FillStyle.Solid) compound = fig.Compound(container) if returnFaces: result = [] else: result = compound todo = [geomap.face(0)] # +1 because the first iteration will not add any objects: currentDepth = attr.get("depth", 100) + 1 while todo: thisLayer = todo todo = [] for face in thisLayer: if face.area() > 0: color = getFaceColor(face) if color is not None: thisattr = dict(attr) thisattr["fillColor"] = getFaceColor(face) thisattr["depth"] = currentDepth parts = self.addClippedPoly(contourPoly( face.contour()), container=compound, **thisattr) if returnFaces: result.extend([(face, part) for part in parts]) for anchor in face.holeContours(): todo.extend(maputils.holeComponent(anchor)) currentDepth -= 1 return result
def addMapOverlay(fe, overlay, skipBorder=False, **attr): qtColor2figColor = figexport.qtColor2figColor # FIXME: str(type(overlay)).contains(...) instead? if isinstance(overlay, ROISelector): color = qtColor2figColor(overlay.color, fe.f) return fe.addROIRect(overlay.roi, penColor=color, **attr) elif isinstance(overlay, (MapNodes, MapEdges, MapFaces)): oldScale, oldOffset, oldROI = fe.scale, fe.offset, fe.roi if isinstance(overlay, MapFaces): zoom = overlay.edgeOverlay._zoom else: zoom = overlay._zoom extraZoom = float(zoom) / overlay.viewer.zoomFactor() fe.scale *= extraZoom fe.roi = BoundingBox(fe.roi.begin() / extraZoom, fe.roi.end() / extraZoom) map = overlay._map() if isinstance(overlay, MapNodes): radius = overlay.origRadius if not overlay.relativeRadius: radius /= float(overlay._zoom) color = qtColor2figColor(overlay.color, fe.f) result = fe.addMapNodes(map, radius, fillColor=color, lineWidth=0, **attr) elif isinstance(overlay, MapEdges): attr = dict(attr) if overlay.width: attr["lineWidth"] = overlay.width if overlay.colors: result = fig.Compound(fe.f) for edge in map.edgeIter(): edgeColor = overlay.colors[edge.label()] if edgeColor: fe.addClippedPoly(edge, penColor=qtColor2figColor( edgeColor, fe.f), container=result, **attr) elif overlay.color: result = fe.addMapEdges(map, skipBorder=skipBorder, penColor=qtColor2figColor( overlay.color, fe.f), **attr) else: result = fig.Compound(fe.f) if overlay.protectedColor: attr["penColor"] = \ qtColor2figColor(overlay.protectedColor, fe.f) attr["lineWidth"] = overlay.protectedWidth or overlay.width it = skipBorder and maputils.nonBorderEdges(map) \ or map.edgeIter() for edge in it: if edge.flag(flag_constants.ALL_PROTECTION): fe.addClippedPoly(edge, container=result, **attr) else: # isinstance(overlay, MapFaces) attr = dict(attr) if overlay.color: if overlay.width: attr["lineWidth"] = overlay.width attr["penColor"] = qtColor2figColor(overlay.color, fe.f) if overlay.fillColor: attr["fillColor"] = qtColor2figColor(overlay.fillColor, fe.f) attr["fillStyle"] = fig.FillStyle.Solid result = fig.Compound(fe.f) for face in map.faceIter(): if face.flag(overlay.flags): if face.holeCount: assert not overlay.fillColor or not overlay.color, "FIXME: cannot currently export filled+stroked polygons with holes" if not overlay.color: wholePoly = list(contourPoly(face.contour())) back = wholePoly[0] assert wholePoly[-1] == back for dart in face.holeContours(): wholePoly.extend(contourPoly(dart)) wholePoly.append(back) fe.addClippedPoly(wholePoly, container=result, **attr) else: for dart in face.contours(): fe.addClippedPoly(contourPoly(dart), container=result, **attr) fe.scale, fe.offset, fe.roi = oldScale, oldOffset, oldROI return result else: return figexport.addStandardOverlay(fe, overlay, **attr)
# which ATM leads to overlapping edges after border closing. That # violates some assumptions and would lead to errors if we actually # worked with that Map. map = GeoMap(maxima2, [], Size2D(39, 39)) maputils.addFlowLinesToMap(flowlines2, map) assert map.checkConsistency(), "graph inconsistent" map.sortEdgesEventually(stepDist = 0.2, minDist = 0.05) map.splitParallelEdges() map.initializeMap() assert map.checkConsistency(), "map inconsistent" assert maputils.checkLabelConsistency(map), "map.labelImage() inconsistent" # merge faces so that survivor has a hole: hole = map.faceAt((16,17)) assert hole.contains((16,17)) assert contourPoly(hole.contour()).contains((16,17)) dart = hole.contour() while True: while dart.startNode().hasMinDegree(3): face = maputils.removeEdge(dart.clone().prevSigma()) if dart.nextPhi() == hole.contour(): break assert face.holeCount() > 0 # should have hole for p in [(3, 24), (10, 15), (13, 21)]: # in region, but not within hole assert face.contains(p) for p in [(16, 17), (13, 17)]: # in hole assert not face.contains(p) assert hole.contains(p)
# which ATM leads to overlapping edges after border closing. That # violates some assumptions and would lead to errors if we actually # worked with that Map. map = GeoMap(maxima2, [], Size2D(39, 39)) maputils.addFlowLinesToMap(flowlines2, map) assert map.checkConsistency(), "graph inconsistent" map.sortEdgesEventually(stepDist=0.2, minDist=0.05) map.splitParallelEdges() map.initializeMap() assert map.checkConsistency(), "map inconsistent" assert maputils.checkLabelConsistency(map), "map.labelImage() inconsistent" # merge faces so that survivor has a hole: hole = map.faceAt((16, 17)) assert hole.contains((16, 17)) assert contourPoly(hole.contour()).contains((16, 17)) dart = hole.contour() while True: while dart.startNode().hasMinDegree(3): face = maputils.removeEdge(dart.clone().prevSigma()) if dart.nextPhi() == hole.contour(): break assert face.holeCount() > 0 # should have hole for p in [(3, 24), (10, 15), (13, 21)]: # in region, but not within hole assert face.contains(p) for p in [(16, 17), (13, 17)]: # in hole assert not face.contains(p) assert hole.contains(p)
def addMapOverlay(fe, overlay, skipBorder = False, **attr): qtColor2figColor = figexport.qtColor2figColor # FIXME: str(type(overlay)).contains(...) instead? if isinstance(overlay, ROISelector): color = qtColor2figColor(overlay.color, fe.f) return fe.addROIRect(overlay.roi, penColor = color, **attr) elif isinstance(overlay, (MapNodes, MapEdges, MapFaces)): oldScale, oldOffset, oldROI = fe.scale, fe.offset, fe.roi if isinstance(overlay, MapFaces): zoom = overlay.edgeOverlay._zoom else: zoom = overlay._zoom extraZoom = float(zoom) / overlay.viewer.zoomFactor() fe.scale *= extraZoom fe.roi = BoundingBox(fe.roi.begin() / extraZoom, fe.roi.end() / extraZoom) map = overlay._map() if isinstance(overlay, MapNodes): radius = overlay.origRadius if not overlay.relativeRadius: radius /= float(overlay._zoom) color = qtColor2figColor(overlay.color, fe.f) result = fe.addMapNodes(map, radius, fillColor = color, lineWidth = 0, **attr) elif isinstance(overlay, MapEdges): attr = dict(attr) if overlay.width: attr["lineWidth"] = overlay.width if overlay.colors: result = fig.Compound(fe.f) for edge in map.edgeIter(): edgeColor = overlay.colors[edge.label()] if edgeColor: fe.addClippedPoly(edge, penColor = qtColor2figColor(edgeColor, fe.f), container = result, **attr) elif overlay.color: result = fe.addMapEdges( map, skipBorder = skipBorder, penColor = qtColor2figColor(overlay.color, fe.f), **attr) else: result = fig.Compound(fe.f) if overlay.protectedColor: attr["penColor"] = \ qtColor2figColor(overlay.protectedColor, fe.f) attr["lineWidth"] = overlay.protectedWidth or overlay.width it = skipBorder and maputils.nonBorderEdges(map) \ or map.edgeIter() for edge in it: if edge.flag(flag_constants.ALL_PROTECTION): fe.addClippedPoly(edge, container = result, **attr) else: # isinstance(overlay, MapFaces) attr = dict(attr) if overlay.color: if overlay.width: attr["lineWidth"] = overlay.width attr["penColor"] = qtColor2figColor(overlay.color, fe.f) if overlay.fillColor: attr["fillColor"] = qtColor2figColor(overlay.fillColor, fe.f) attr["fillStyle"] = fig.FillStyle.Solid result = fig.Compound(fe.f) for face in map.faceIter(): if face.flag(overlay.flags): if face.holeCount: assert not overlay.fillColor or not overlay.color, "FIXME: cannot currently export filled+stroked polygons with holes" if not overlay.color: wholePoly = list(contourPoly(face.contour())) back = wholePoly[0] assert wholePoly[-1] == back for dart in face.holeContours(): wholePoly.extend(contourPoly(dart)) wholePoly.append(back) fe.addClippedPoly(wholePoly, container = result, **attr) else: for dart in face.contours(): fe.addClippedPoly(contourPoly(dart), container = result, **attr) fe.scale, fe.offset, fe.roi = oldScale, oldOffset, oldROI return result else: return figexport.addStandardOverlay(fe, overlay, **attr)
def addMapFaces(self, geomap, faceMeans = None, similarity = None, returnFaces = False, container = True, **attr): """fe.addMapFaces(geomap, faceMeans, ...) Adds and returns fig.Polygons for all map faces (or -parts, see addClippedPoly). Clipping closed polygons should work nowadays, too.""" import maputils def getGray(face): faceColor = faceMeans[face.label()] return self.f.gray(int(faceColor)) def getRGB(face): faceColor = faceMeans[face.label()] return self.f.getColor(map(int, tuple(faceColor)), similarity) if not faceMeans: if not hasattr(geomap, "faceMeans"): raise ValueError("addMapFaces: need faceMeans for proper coloring") faceMeans = geomap.faceMeans if hasattr(faceMeans, "bands"): getFaceColor = getGray if faceMeans.bands() == 3: getFaceColor = getRGB else: getFaceColor = lambda face: faceMeans[face.label()] if container == True: container = self.f attr = dict(attr) attr["lineWidth"] = attr.get("lineWidth", 0) attr["fillStyle"] = attr.get("fillStyle", fig.FillStyle.Solid) compound = fig.Compound(container) if returnFaces: result = [] else: result = compound todo = [geomap.face(0)] # +1 because the first iteration will not add any objects: currentDepth = attr.get("depth", 100) + 1 while todo: thisLayer = todo todo = [] for face in thisLayer: if face.area() > 0: color = getFaceColor(face) if color is not None: thisattr = dict(attr) thisattr["fillColor"] = getFaceColor(face) thisattr["depth"] = currentDepth parts = self.addClippedPoly( contourPoly(face.contour()), container = compound, **thisattr) if returnFaces: result.extend([(face, part) for part in parts]) for anchor in face.holeContours(): todo.extend(maputils.holeComponent(anchor)) currentDepth -= 1 return result