save comment for xcf as well
[conformal.git] / conformal.py
blob16c5bfc02079afdaa5b07831cd2e92f7fa1460d4
1 #!/usr/bin/env python
3 # conformal.py
4 # Copyright (C) 2006-2011 Michael J. Gruber <conformal@drmicha.warpmail.net>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, version 2 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 confversion = "0.3"
21 # allow access through module and without
22 import math, cmath
23 from math import *
24 from cmath import *
26 from array import array
27 from gimpfu import *
29 # try importing typical math modules
30 try:
31 from fpconst import *
32 import scipy.special
33 except ImportError:
34 pass
36 try:
37 import mpmath
38 except ImportError:
39 pass
42 def conformal_batch(width, height, code, xl, xr, yt, yb, grid, gradient, filename):
43 conformal_core(width, height, code, xl, xr, yt, yb, grid, gradient, filename)
46 def conformal(width, height, code, xl, xr, yt, yb, grid, gradient):
47 conformal_core(width, height, code, xl, xr, yt, yb, grid, gradient, None)
50 def conformal_core(width, height, code, xl, xr, yt, yb, grid, gradient, filename):
51 image = gimp.Image(width, height, RGB)
52 drawables = [ gimp.Layer(image, "Argument", width, height, RGBA_IMAGE, 100, NORMAL_MODE),
53 gimp.Layer(image, "Log. modulus", width, height, RGBA_IMAGE, 35, DARKEN_ONLY_MODE),
54 gimp.Layer(image, "Grid", width, height, RGBA_IMAGE, 10, DARKEN_ONLY_MODE)]
55 image.disable_undo()
56 l = 1
57 for drawable in drawables:
58 image.add_layer(drawable, l)
59 l = -1
61 bpp = drawables[0].bpp
63 gimp.tile_cache_ntiles(2 * (width + 63) / 64)
65 dest_rgns = [ drawable.get_pixel_rgn(0, 0, width, height, True, False) for drawable in drawables ]
66 progress = 0
67 max_progress = width * height
68 if filename is None:
69 gimp.progress_init("Conformally Mapping...")
70 sx = (width-1.0)/(xr-xl)
71 sy = (height-1.0)/(yt-yb)
72 w = complex(0.0)
73 z = complex(0.0)
74 cx, cy = 0, 0
75 ml2 = 2.0*math.log(2) # no need to do this 500*500 times...
76 compiled=compile(code, "compiled code", "exec", 0, 1)
78 for row in range(0, height):
79 args = ()
80 mods = ()
81 sqrs = ()
82 for col in range(0, width):
83 z = col/sx + xl + 1j*( yt - row/sy)
84 try:
85 exec(compiled)
86 except (OverflowError, ValueError):
87 w = 0.0
88 arg = math.atan2(w.imag, w.real)
89 if arg<0.0:
90 arg = arg + 2*math.pi
91 args = args + ( arg/(2*math.pi) ,)
92 try:
93 mod = ( math.log(w.imag**2+w.real**2)/ml2 ) % 1.0
94 except (OverflowError, ValueError):
95 mod=0.0
96 mods = mods + ( mod ,)
97 try:
98 sqr = (int)(w.imag/grid % 2.0) + (int)(w.real/grid % 2.0)
99 except (OverflowError, ValueError):
100 sqr = 0
101 sqrs = sqrs + (bpp-1)*( 255*(sqr % 2) ,) + (255, )
103 samples = gimp.gradient_get_custom_samples(gradient, args)
104 top_p = array("B", [ ((int)(255*samples[col][i]+0.5)) for col in range(0, width) for i in range(bpp) ] )
106 dest_rgns[0][0:width, row] = top_p.tostring()
108 samples = gimp.gradient_get_custom_samples("Default", mods)
109 top_p = array("B", [ ((int)(255*samples[col][i]+0.5)) for col in range(0, width) for i in range(bpp) ] )
110 dest_rgns[1][0:width, row] = top_p.tostring()
112 top_p = array("B", sqrs )
113 dest_rgns[2][0:width, row] = top_p.tostring()
115 progress = progress + width
116 if filename is None:
117 gimp.progress_update(float(progress) / max_progress)
119 for drawable in drawables:
120 drawable.flush()
121 drawable.update(0,0,width,height)
122 pdb.plug_in_edge(image,drawables[2], 10, 0, 0) # amount, WRAP, SOBEL
123 pdb.plug_in_vinvert(image,drawables[2])
124 if image.parasite_find("gimp-comment"):
125 image.parasite.detach("gimp-comment")
126 image.attach_new_parasite("gimp-comment", PARASITE_PERSISTENT, """# conformal %s
127 code = \"\"\"%s
128 \"\"\"
129 xl = %f
130 xr = %f
131 yt = %f
132 yb = %f
133 grid = %f
134 gradient = "%s"
135 width = %d
136 height = %d
137 """ % (confversion, code, xl, xr, yt, yb, grid, gradient, width, height))
138 if filename is None:
139 image.enable_undo()
140 gimp.Display(image)
141 gimp.displays_flush
142 else:
143 if filename.find('.xcf') > 0:
144 pdb.gimp_xcf_save(1, image, drawables[0], filename, filename)
145 else:
146 flat_layer = pdb.gimp_image_flatten(image)
147 pdb.gimp_file_save(image, flat_layer, filename, filename)
150 register(
151 "conformal_batch",
152 "Colour representation of a conformal map",
153 "Colour representation of a conformal map",
154 "Michael J Gruber",
155 "Michael J Gruber",
156 "2011",
160 (PF_INT, "width", "width", 512),
161 (PF_INT, "height", "height", 512),
162 (PF_TEXT, "code", "code", "w=z"),
163 (PF_FLOAT, "xl", "x left", -1.0),
164 (PF_FLOAT, "xr", "x right", 1.0),
165 (PF_FLOAT, "yt", "y top", 1.0),
166 (PF_FLOAT, "yb", "y bottom", -1.0),
167 (PF_FLOAT, "grid", "grid", 1.0),
168 (PF_GRADIENT, "gradient", "gradient", "Full saturation spectrum CCW"),
169 (PF_FILE, "file", "file", "out.xcf.bz2"),
172 conformal_batch)
174 register(
175 "conformal",
176 "Colour representation of a conformal map",
177 "Colour representation of a conformal map",
178 "Michael J Gruber",
179 "Michael J Gruber",
180 "2011",
181 "<Toolbox>/File/Create/_Conformal ...",
184 (PF_INT, "width", "width", 512),
185 (PF_INT, "height", "height", 512),
186 (PF_TEXT, "code", "code", "w=z"),
187 (PF_FLOAT, "xl", "x left", -1.0),
188 (PF_FLOAT, "xr", "x right", 1.0),
189 (PF_FLOAT, "yt", "y top", 1.0),
190 (PF_FLOAT, "yb", "y bottom", -1.0),
191 (PF_FLOAT, "grid", "grid", 1.0),
192 (PF_GRADIENT, "gradient", "gradient", "Full saturation spectrum CCW"),
195 conformal)
197 main()