graph density style example (based on the mandel gallery example)
[PyX.git] / pyx / mesh.py
blobc93f6f2e1d9c2c9449c6100c737f1d42236293cf
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2006-2011 André Wobst <wobsta@users.sourceforge.net>
6 # This file is part of PyX (http://pyx.sourceforge.net/).
8 # PyX is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # PyX is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with PyX; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 # Just a quick'n'dirty ascii art (I'll do a nice PyX plot later on):
26 # node1 *
27 # | \
28 # | \ neighbor2
29 # | \
30 # | \
31 # neighbor3 |element * node3
32 # | /
33 # | /
34 # | / neighbor1
35 # | /
36 # node2 *
39 import struct, binascii, zlib, os, tempfile
40 import bbox, canvasitem, color, pdfwriter, unit
43 class node_pt:
45 def __init__(self, coords_pt, value):
46 self.coords_pt = coords_pt
47 self.value = value
50 class node(node_pt):
52 def __init__(self, coords, value):
53 node_pt.__init__(self, [unit.topt(coord) for coord in coords], value)
56 class element:
58 def __init__(self, nodes, neighbors=None):
59 self.nodes = nodes
60 self.neighbors = neighbors
63 def coords24bit_pt(coords_pt, min_pt, max_pt):
64 return struct.pack(">I", int((coords_pt-min_pt)*16777215.0/(max_pt-min_pt)))[1:]
67 class PDFGenericResource(pdfwriter.PDFobject):
69 def __init__(self, type, name, content):
70 pdfwriter.PDFobject.__init__(self, type, name)
71 self.content = content
73 def write(self, file, writer, registry):
74 file.write(self.content)
77 class mesh(canvasitem.canvasitem):
79 def __init__(self, elements, check=1):
80 self.elements = elements
81 if check:
82 colorspacestring = ""
83 for element in elements:
84 if len(element.nodes) != 3:
85 raise ValueError("triangular mesh expected")
86 try:
87 for node in element.nodes:
88 if not colorspacestring:
89 colorspacestring = node.value.colorspacestring()
90 elif node.value.colorspacestring() != colorspacestring:
91 raise ValueError("color space mismatch")
92 except AttributeError:
93 raise ValueError("gray, rgb or cmyk color values expected")
94 for node in element.nodes:
95 if len(node.coords_pt) != 2:
96 raise ValueError("two dimensional coordinates expected")
98 def bbox(self):
99 return bbox.bbox_pt(min([node.coords_pt[0] for element in self.elements for node in element.nodes]),
100 min([node.coords_pt[1] for element in self.elements for node in element.nodes]),
101 max([node.coords_pt[0] for element in self.elements for node in element.nodes]),
102 max([node.coords_pt[1] for element in self.elements for node in element.nodes]))
104 def data(self, bbox):
105 return "".join(["\000%s%s%s" % (coords24bit_pt(node.coords_pt[0], bbox.llx_pt, bbox.urx_pt),
106 coords24bit_pt(node.coords_pt[1], bbox.lly_pt, bbox.ury_pt),
107 node.value.to8bitstring())
108 for element in self.elements for node in element.nodes])
110 def processPS(self, file, writer, context, registry, bbox):
111 if writer.mesh_as_bitmap:
112 from pyx import bitmap, canvas
113 import Image
114 c = canvas.canvas()
115 c.insert(self)
116 fd, fname = tempfile.mkstemp()
117 f = os.fdopen(fd, "wb")
118 f.close()
119 c.pipeGS(fname, device="pngalpha", resolution=writer.mesh_as_bitmap_resolution)
120 i = Image.open(fname)
121 os.unlink(fname)
122 b = bitmap.bitmap_pt(self.bbox().llx_pt, self.bbox().lly_pt, i)
123 # we slightly shift the bitmap to re-center it, as the bitmap might contain some additional border
124 # unfortunately we need to construct another bitmap instance for that ...
125 b = bitmap.bitmap_pt(self.bbox().llx_pt + 0.5*(self.bbox().width_pt()-b.bbox().width_pt()),
126 self.bbox().lly_pt + 0.5*(self.bbox().height_pt()-b.bbox().height_pt()), i)
127 b.processPS(file, writer, context, registry, bbox)
128 else:
129 thisbbox = self.bbox()
130 bbox += thisbbox
131 file.write("""<< /ShadingType 4
132 /ColorSpace %s
133 /BitsPerCoordinate 24
134 /BitsPerComponent 8
135 /BitsPerFlag 8
136 /Decode [%f %f %f %f %s]
137 /DataSource currentfile /ASCIIHexDecode filter /FlateDecode filter
138 >> shfill\n""" % (self.elements[0].nodes[0].value.colorspacestring(),
139 thisbbox.llx_pt, thisbbox.urx_pt, thisbbox.lly_pt, thisbbox.ury_pt,
140 " ".join(["0 1" for value in self.elements[0].nodes[0].value.to8bitstring()])))
141 file.write(binascii.b2a_hex(zlib.compress(self.data(thisbbox))))
142 file.write("\n")
144 def processPDF(self, file, writer, context, registry, bbox):
145 if writer.mesh_as_bitmap:
146 from pyx import bitmap, canvas
147 import Image
148 c = canvas.canvas()
149 c.insert(self)
150 fd, fname = tempfile.mkstemp()
151 f = os.fdopen(fd, "wb")
152 f.close()
153 c.pipeGS(fname, device="pngalpha", resolution=writer.mesh_as_bitmap_resolution)
154 i = Image.open(fname)
155 os.unlink(fname)
156 b = bitmap.bitmap_pt(self.bbox().llx_pt, self.bbox().lly_pt, i)
157 # we slightly shift the bitmap to re-center it, as the bitmap might contain some additional border
158 # unfortunately we need to construct another bitmap instance for that ...
159 b = bitmap.bitmap_pt(self.bbox().llx_pt + 0.5*(self.bbox().width_pt()-b.bbox().width_pt()),
160 self.bbox().lly_pt + 0.5*(self.bbox().height_pt()-b.bbox().height_pt()), i)
161 b.processPDF(file, writer, context, registry, bbox)
162 else:
163 thisbbox = self.bbox()
164 bbox += thisbbox
165 d = self.data(thisbbox)
166 if writer.compress:
167 filter = "/Filter /FlateDecode\n"
168 d = zlib.compress(d)
169 else:
170 filter = ""
171 name = "shading-%s" % id(self)
172 shading = PDFGenericResource("shading", name, """<<
173 /ShadingType 4
174 /ColorSpace %s
175 /BitsPerCoordinate 24
176 /BitsPerComponent 8
177 /BitsPerFlag 8
178 /Decode [%f %f %f %f %s]
179 /Length %i
180 %s>>
181 stream
183 endstream\n""" % (self.elements[0].nodes[0].value.colorspacestring(),
184 thisbbox.llx_pt, thisbbox.urx_pt, thisbbox.lly_pt, thisbbox.ury_pt,
185 " ".join(["0 1" for value in self.elements[0].nodes[0].value.to8bitstring()]),
186 len(d), filter, d))
187 registry.add(shading)
188 registry.addresource("Shading", name, shading)
189 file.write("/%s sh\n" % name)