inkscape_tools: remove `mnit` support
[nit.git] / contrib / inkscape_tools / src / svg_to_png_and_nit.nit
index f718255..f660a31 100644 (file)
@@ -1,6 +1,6 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
-# Copyright 2012-2014 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2012-2015 Alexis Laferrière <alexis.laf@xymus.net>
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This script extracts pngs from a single svg for all objects with ids
-# beginning by 0. Requires Inkscape.
+# Extract images of objects from an SVG file using Inkscape
 module svg_to_png_and_nit
 
 import opts
 import template
 
+# Image information extracted from the SVG file
 class Image
+       # Name extracted from the object ID minus the `0` prefix and Nit safe
        var name: String
+
+       # Left border
        var x: Int
+
+       # Top border
        var y: Int
+
+       # Image width
        var w: Int
+
+       # Image height
        var h: Int
+
+       # Right border
        fun right: Int do return x+w
+
+       # Bottom border
        fun bottom: Int do return y+h
 
        redef fun to_s do return name
 end
 
-# The Nit source file to retreive all images
-class ImageSetSrc
+# Document being processed, concerns both the source and the target
+class Document
+       # Name of the source file
+       var drawing_name: String
+
+       # Name of the class to generate
+       var nit_class_name: String = drawing_name.capitalized + "Images" is lazy
+
+       # Scaling to apply to the exported image
+       var scale: Float
+
+       # Source minimum X
+       var min_x: Int
+
+       # Source maximum X
+       var max_x: Int
+
+       # Source minimum Y
+       var min_y: Int
+
+       # Source maximum Y
+       var max_y: Int
+
+       # Get the coordinates for `image` as `"x, y, w, h"`
+       fun coordinates(image: Image): String
+       do
+               var x = image.x.adapt(min_x, scale)
+               var y = image.y.adapt(min_y, scale)
+               var w = (image.w.to_f*scale).to_i
+               var h = (image.h.to_f*scale).to_i
+
+               return "{x}, {y}, {w}, {h}"
+       end
+end
+
+# Nit module with a single class to retrieve to access the extracted images
+abstract class ImageSetSrc
        super Template
 
-       var name: String
+       # Target document
+       var document: Document
 
-       var attributes = new Array[String]
-       var load_exprs = new Array[String]
+       # Images found in the source document
+       var images: Array[Image]
+end
 
-       redef fun rendering
+# Nit module targeting the Gamnit framework
+#
+# Gamnit's `Texture` already manage the lazy loading, no need to do it here.
+class GamnitImageSetSrc
+       super ImageSetSrc
+
+       private fun attributes: Array[String]
        do
-               add """
-# file generated by svg_to_png, do not modify, redef instead
+               # Separate the images from the arrays of images
+               var single_images = new Array[Image]
+               var arrays_of_images = new HashMap[String, Array[Image]]
+
+               for image in images do
+                       var nit_name = image.name
+                       var last_char = nit_name.chars.last
+                       if last_char.to_s.is_numeric then
+
+                               # Is an array
+                               nit_name = nit_name.substring(0, nit_name.length-1)
+                               if not arrays_of_images.keys.has(nit_name) then
+                                       # Create a new array
+                                       var array = new Array[Image]
+                                       arrays_of_images[nit_name] = array
+                               end
+
+                               arrays_of_images[nit_name].add image
+                       else
+                               # Is a single image
+                               single_images.add image
+                       end
+               end
 
-import mnit::image_set
+               # Attributes of the class
+               var attributes = new Array[String]
+               attributes.add "\tprivate var root_texture = new Texture(\"images/{document.drawing_name}.png\")\n"
 
-class {{{name}}}
-       super ImageSet
+               # Add single images to Nit source file
+               for image in single_images do
+                       # Adapt coordinates to new top left and scale
+                       var coordinates = document.coordinates(image)
 
+                       attributes.add "\tvar {image.name}: Texture = root_texture.subtexture({coordinates})\n"
+               end
+
+               # Add array of images too
+               for name, images in arrays_of_images do
+
+                       var lines = new Array[String]
+                       for image in images do
+                               var coordinates = document.coordinates(image)
+                               lines.add "\t\troot_texture.subtexture({coordinates})"
+                       end
+
+                       attributes.add """
+       var {{{name}}} = new Array[Texture].with_items(
+{{{lines.join(",\n")}}})
 """
-               add_all attributes
-               add """
+               end
 
-       redef fun load_all(app: App)
+               return attributes
+       end
+
+       redef fun rendering
        do
+               add """
+# File generated by svg_to_png_and_nit, do not modify, redef instead
+
+import gamnit::textures
+
+class {{{document.nit_class_name}}}
+
 """
-               add_all load_exprs
+               add_all attributes
                add """
-       end
 end
 """
        end
 end
 
 redef class Int
-       fun adapt(d: Int, scale: Float): Int
+       # Magic adaption of this coordinates to the given `margin` and `scale`
+       fun adapt(margin: Int, scale: Float): Int
        do
-               var corrected = self-d
+               var corrected = self-margin
                return (corrected.to_f*scale).to_i
        end
 
+       # The first power of to equal or greater than `self`
        fun next_pow2: Int
        do
                var p = 2
@@ -82,13 +188,15 @@ redef class Int
        end
 end
 
-var opt_out_src = new OptionString("Path to output source file", "--src", "-s")
+var opt_out_src = new OptionString("Path to output source file (folder or file)", "--src", "-s")
 var opt_assets = new OptionString("Path to assert dir where to put PNG files", "--assets", "-a")
-var opt_scale = new OptionFloat("Apply scaling to exported images (defaut at 1.0 of 90dpi)", 1.0, "--scale", "-x")
+var opt_scale = new OptionFloat("Apply scaling to exported images (default at 1.0)", 1.0, "--scale", "-x")
+var opt_gamnit = new OptionBool("Target the Gamnit framework (the default, kept for compatibility)", "--gamnit", "-g")
+var opt_pow2 = new OptionBool("Round the image size to the next power of 2", "--pow2")
 var opt_help = new OptionBool("Print this help message", "--help", "-h")
 
 var opt_context = new OptionContext
-opt_context.add_option(opt_out_src, opt_assets, opt_scale, opt_help)
+opt_context.add_option(opt_out_src, opt_assets, opt_scale, opt_gamnit, opt_pow2, opt_help)
 
 opt_context.parse(args)
 var rest = opt_context.rest
@@ -107,6 +215,23 @@ if not "inkscape".program_is_in_path then
        exit 1
 end
 
+# Get the inkscape version
+var p = new ProcessReader("inkscape", "--version")
+var version_string = p.read_all
+p.wait
+p.close
+var version_re = "([0-9]+\).([0-9]+\).[0-9]+".to_re
+var match = version_string.search(version_re)
+assert match != null
+var major = match[1]
+var minor = match[2]
+assert major != null and minor != null
+
+# Set the default API using the version as heuristic
+var default_dpi = 96.0
+if major.to_s.to_i == 0 and minor.to_s.to_i < 92 then default_dpi = 90.0
+
+# Collect source files
 var drawings = rest
 for drawing in drawings do
        if not drawing.file_exists then
@@ -124,15 +249,13 @@ end
 
 var src_path = opt_out_src.value
 if src_path == null then src_path = "src"
-if not src_path.file_exists then
+if not src_path.file_exists and src_path.file_extension != "nit" then
        stderr.write "Source dir '{src_path}' does not exist (use --src)\n"
        exit 1
 end
 
 var scale = opt_scale.value
 
-var arrays_of_images = new Array[String]
-
 for drawing in drawings do
        var drawing_name = drawing.basename(".svg")
 
@@ -158,8 +281,10 @@ for drawing in drawings do
        end
        svg_file.close
 
-       assert page_width != -1
-       assert page_height != -1
+       if page_width == -1 or page_height == -1 then
+               stderr.write "Source drawing file '{drawing}' doesn't look like an SVG file\n"
+               exit 1
+       end
 
        # Query Inkscape
        var prog = "inkscape"
@@ -201,54 +326,32 @@ for drawing in drawings do
        end
        proc.close
 
-       # Nit class
-       var nit_class_name = drawing_name.chars.first.to_s.to_upper + drawing_name.substring_from(1) + "Images"
-       var nit_src = new ImageSetSrc(nit_class_name)
-       nit_src.attributes.add "\tprivate var main_image: Image is noinit\n"
-       nit_src.load_exprs.add "\t\tmain_image = app.load_image(\"images/{drawing_name}.png\")\n"
 
        # Sort images by name, it prevents Array errors and looks better
        alpha_comparator.sort(images)
 
-       # Add images to Nit source file
-       for image in images do
-               # Adapt coordinates to new top left and scale
-               var x = image.x.adapt(min_x, scale)
-               var y = image.y.adapt(min_y, scale)
-               var w = (image.w.to_f*scale).to_i
-               var h = (image.h.to_f*scale).to_i
+       var document = new Document(drawing_name, scale, min_x, max_x, min_y, max_y)
 
-               var nit_name = image.name
-               var last_char = nit_name.chars.last
-               if last_char.to_s.is_numeric then
-                       # Array of images
-                       # TODO support more than 10 images in an array
-
-                       nit_name = nit_name.substring(0, nit_name.length-1)
-                       if not arrays_of_images.has(nit_name) then
-                               # Create class attribute to store Array
-                               arrays_of_images.add(nit_name)
-                               nit_src.attributes.add "\tvar {nit_name} = new Array[Image]\n"
-                       end
-                       nit_src.load_exprs.add "\t\t{nit_name}.add(main_image.subimage({x}, {y}, {w}, {h}))\n"
-               else
-                       # Single image
-                       nit_src.attributes.add "\tvar {nit_name}: Image is noinit\n"
-                       nit_src.load_exprs.add "\t\t{nit_name} = main_image.subimage({x}, {y}, {w}, {h})\n"
-               end
+       # Nit class
+       var nit_src = new GamnitImageSetSrc(document, images)
+
+       if not src_path.file_extension == "nit" then
+               src_path = src_path/drawing_name+".nit"
        end
 
        # Output source file
-       var src_file = new FileWriter.open("{src_path}/{drawing_name}.nit")
+       var src_file = new FileWriter.open(src_path)
        nit_src.write_to(src_file)
        src_file.close
 
-       # Find closest power of 2
-       var dx = max_x - min_x
-       max_x = dx.next_pow2 + min_x
+       # Find next power of 2
+       if opt_pow2.value then
+               var dx = max_x - min_x
+               max_x = dx.next_pow2 + min_x
 
-       var dy = max_y - min_y
-       max_y = dy.next_pow2 + min_y
+               var dy = max_y - min_y
+               max_y = dy.next_pow2 + min_y
+       end
 
        # Inkscape's --export-area inverts the Y axis. It uses the lower left corner of
        # the drawing area where as queries return coordinates from the top left.
@@ -258,7 +361,7 @@ for drawing in drawings do
        # Output png file to assets
        var png_path = "{assets_path}/images/{drawing_name}.png"
        var proc2 = new Process.from_a(prog, [drawing, "--without-gui",
-               "--export-dpi={(90.0*scale).to_i}",
+               "--export-dpi={(default_dpi*scale).to_i}",
                "--export-png={png_path}",
                "--export-area={min_x}:{y0}:{max_x}:{y1}",
                "--export-background=#000000", "--export-background-opacity=0.0"])