lib/svg_tracing.ex

defmodule SvgTracing do
  @moduledoc ~S"""
  An open source software to convert raster images (like jpg & png) into vector graphics (svg).
  It can vecterize graphics and photographs and trace the curves to output compact vector files.
  This is backed by rust (vtracer) using rustler.

  iex> SvgTracing.trace("priv/static/ikea-sofa.png", "priv/static/ikea-sofa.svg")
  {:ok, {}}
  """
  alias SvgTracing.Tracer

  @output_file_prefix "svg_tracing_"
  @output_file_random_number 100

  defstruct input_path: "",
            output_path: "",
            colormode: "color",
            hierarchical: "stacked",
            mode: "spline",
            filter_speckle: 4,
            color_precision: 6,
            layer_difference: 16,
            corner_threshold: 60,
            length_threshold: 4.0,
            max_iterations: 10,
            splice_threshold: 45,
            path_precision: 8

  def start(_ab, _args) do
    [input, output] = Burrito.Util.Args.get_arguments()
    |> get_params()
    |> preparing_log()

    trace(input, output)

    System.halt(0)
  end

  defp get_params([]), do: IO.puts("Oh no, Missing input image"); [nil, nil]
  defp get_params([input | []]), do: [input, "#{@output_file_prefix}#{:rand.uniform(@output_file_random_number)}.svg"]
  defp get_params([input, output | _]), do: [input, output]

  defp preparing_log(input) do
    IO.puts("Preparing svg from heart 💙")
    input
  end

  @doc """
  Convert images to vector graphics, this will need two paths, input image path
  and second output image path to save svg.

  iex(1)> SvgTracing.trace("priv/static/ikea-sofa.png", "priv/static/ikea-sofa.svg")
  {:ok, {}}
  """
  @spec trace(String.t(), String.t()) :: {:ok, {}} | {:error, String.t()}
  def trace(nil, nil), do: {:error, "Missing Required Argument"}
  def trace(input), do: trace(input, "#{@output_file_prefix}#{:rand.uniform(@output_file_random_number)}.svg")

  @spec trace(String.t(), String.t()) :: {:ok, {}} | {:error, String.t()}
  def trace(input_path, output_path) do
    input_path
    |> File.exists?()
    |> process_request(input_path, output_path)
  end

  defp process_request(true, input_path, output_path) do
    default_values = %SvgTracing{}

    Tracer.convert_to_svg(
      input_path,
      output_path,
      "color",
      default_values.hierarchical,
      default_values.mode,
      default_values.filter_speckle,
      default_values.color_precision,
      default_values.layer_difference,
      default_values.corner_threshold,
      default_values.length_threshold,
      default_values.max_iterations,
      default_values.splice_threshold,
      default_values.path_precision
    )

    IO.puts("Hurry, Ready at #{output_path}")
  end

  defp process_request(false, input_path, _output_path) do
    {:error, "No file found at #{input_path}"}
  end
end