diff --git a/.gitignore b/.gitignore index 62c8935..e88c164 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,13 @@ -.idea/ \ No newline at end of file +.idea/ +out/ +/bin/ +.project +.classpath +.bin +.settings/ +rapport/rapport.out +rapport/rapport.aux +rapport/rapport.pdf +rapport/rapport.log +rapport/rapport.toc +rapport/rapport.synctex.gz \ No newline at end of file diff --git a/rapport/rapport.tex b/rapport/rapport.tex new file mode 100644 index 0000000..98257f9 --- /dev/null +++ b/rapport/rapport.tex @@ -0,0 +1,75 @@ +\documentclass[12pt]{article} + +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[french]{babel} +\usepackage{hyperref} +\usepackage{graphicx} +\title{Conception Logicielle - L-système} +\author{Antonin Boyon, Thomas Lalong, Quentin Legot, Arthur Page} +\date{\today} + +\begin{document} + +\maketitle +\thispagestyle{empty} +\setcounter{page}{0} +\newpage + +\tableofcontents +\newpage + +\section{Introduction} +\subsection{} +Le but de notre projet était de concevoir un générateur de flores vidéo-ludiques. Ce genre de logiciel à pour but de créer de manière procédurale des modèles végétaux qui pourront notamment être utilisés dans les jeux vidéos. Nous devions pour cela nous baser sur un L-système \ref{l-system}, un parser \ref{parser}, un moteur de réécriture\ref{rw-engine} et un moteur graphique\ref{g-engine}. Le rôle de ses différents éléments sera expliqué dans les sections suivantes. + + +\section{Le logiciel} +\subsection{Organigramme} +%inclure à la fin quand le logiciel sera terminé + +\subsection{Le l-system} +\label{l-system} +\subsubsection{Qu'est-ce qu'un L-system} +Un L-system (ou L-système en français) est un langage de réécriture permettant de modéliser l'évolution de modèles végétaux ou bactériologiques. (Wikipédia : \url{https://fr.wikipedia.org/wiki/L-Syst%C3%A8me}). +\\ +Un L-system se base sur plusieurs paramètres: +\begin{itemize} +\item L'alphabet.\ref{alpha}\\ +C'est le "langage" du L-system, il est propre à chaque L-system et c'est à nous de le définir. +\item L'axiome.\ref{axiome}\\ +C'est l'élément qui servira de base à la génération. +\item Les règles.\ref{rules}\\ +Elles servent à définir comment le modèle va évoluer en partant de l'axiome. +\item Le nombre d'itérations.\ref{nbIt}\\ +Ce nombre indique le nombre de fois que les règles peuvent être appliquées. +\end{itemize} +\subsubsection{Notre L-system} +Voici, expliqué en détail, les composants de notre L-système. +\paragraph{L'alphabet}\label{alpha} étant propre à chaque L-system, nous avons du créer le notre. +Il est constitué de 6 lettres , 10 chiffres et 6 caractères. +Les lettres comprennent 3 majuscules $(X,Y,Z)$ et 3 minuscules $(x,y,z)$. +Les trois majuscules servent à représenter un mouvement d'une unité dans le sens positif de leur axe. +Ainsi $X = $ mouvement d'une unité dans le sens positif sur l'axe $X$. +Les trois minuscules quant à elles, permettent d'effectuer une rotation de +25° sur leurs axes respectifs. +Ainsi, $x = $ rotation de 25° par rapport à l'axe des $X$. +Les chiffres permettent, avec les symboles $(.,+,-)$ de faire varier les valeurs de base des lettres de l'alphabet. +Ainsi, $-0.5X$ représentera un mouvement négatif de 0.5 unités sur l'axe $X$. +De même, $+2x$ représentera un mouvement positif de 50° sur l'axe des $X$. +Les symboles, $([,])$ permettent de différer l'exécution d'une règle, nous expliquerons leur utilité dans cette ce paragraphe \ref{rules}. Pour le dernier symbole, $=$, son utilité sera expliquée dans ce paragraphe \ref{axiome}. +\paragraph{L'axiome}\label{axiome} +\paragraph{Les règles}\label{rules} +\paragraph{Le nombre d'itérations}\label{nbIt} +\subsection{Le parser} +\label{parser} +\subsection{Le moteur de réécriture} +\label{rw-engine} +\subsection{Le moteur graphique} +\label{g-engine} + + + + + +\section{Conclusion} +\end{document} \ No newline at end of file diff --git a/src/lsystem/Main.java b/src/lsystem/Main.java index 9130184..79495d5 100644 --- a/src/lsystem/Main.java +++ b/src/lsystem/Main.java @@ -1,5 +1,8 @@ package lsystem; +import lsystem.engine.Parser; +import lsystem.engine.Rewrite; + import java.util.ArrayList; import java.util.List; import java.util.Scanner; @@ -7,14 +10,30 @@ import java.util.Scanner; public class Main { public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - System.out.println("Axiom: "); - String axiom = scanner.next(); - System.out.println("Règles (laissez vide quand vous avez fini): "); - List rules = new ArrayList<>(); - while(rules.isEmpty() || !rules.get(rules.size() - 1).equals("")) { - rules.add(scanner.next()); + final Scanner scanner = new Scanner(System.in); + String axiom = null; + Parser parser = null; + final List rules = new ArrayList<>(); + int nbIterations = 0; + while(parser == null || !parser.isCorrect()) { + if(parser != null) + System.out.println("Vos règles ou votre axiome ne sont pas correctement écrites, veuillez recommencer"); + System.out.println("Axiome: "); + axiom = scanner.next(); + System.out.println("Règles: (\"finish\" quand vous avez fini): "); + while(rules.isEmpty() || !rules.get(rules.size() - 1).equals("finish")) { + rules.add(scanner.next()); + } + rules.remove(rules.size() - 1); + System.out.println("Nombre d'itérations: "); + nbIterations = scanner.nextInt(); + parser = new Parser(axiom, rules,nbIterations); } + System.out.println("Réécriture, veuillez patientez..."); + Rewrite rewriter = new Rewrite(axiom, parser.parseRules(), nbIterations); + final String word = rewriter.rewrite(); + System.out.println(word); + scanner.close(); } } diff --git a/src/lsystem/Type.java b/src/lsystem/Type.java new file mode 100644 index 0000000..43801af --- /dev/null +++ b/src/lsystem/Type.java @@ -0,0 +1,8 @@ +package lsystem; + +public enum Type { + + AXIOM, + RULE + +} diff --git a/src/lsystem/engine/Parser.java b/src/lsystem/engine/Parser.java new file mode 100644 index 0000000..13e54e9 --- /dev/null +++ b/src/lsystem/engine/Parser.java @@ -0,0 +1,87 @@ +package lsystem.engine; + +import java.util.ArrayList; +import java.util.List; + +import lsystem.Type; +import lsystem.utils.Pair; + +public class Parser { + + private final String axiom; + private final List rules; + private final int nbIterations; + private char[] validChars = {'=',']','[','.','+','-','X','Y','Z','x','y','z','0','1','2','3','4','5','6','7','8','9',' '}; + + public Parser(String axiom, List rules,int nbIterations) { + this.axiom = axiom; + this.rules = rules; + this.nbIterations = nbIterations; + } + + + /** + * Check if axiom and rules given by user respect the syntax + * @return true if the syntax is correct + */ + public boolean isCorrect(){ + if (nbIterations < 1) { + System.out.println("Erreur, nombre d'itérations insuffisant (plus petit que 1)"); + return false; + } + boolean bl = isCorrect(axiom, Type.AXIOM); + for(String rule : this.rules){ + bl = bl && isCorrect(rule, Type.RULE); + } + return bl; + } + + + private boolean isCorrect(String stringToCheck, Type type) { + char old = ' '; + int bracket = 0; + boolean equalsSymbolFound = false; + for (int i = 0; i > stringToCheck.length(); i++){ + char temp = stringToCheck.charAt(i); + if (temp == '[') + bracket++; + if(temp == ']') + bracket--; + if(temp == '=') { + if(!equalsSymbolFound) + equalsSymbolFound = true; + else + // only one '=' allowed + return false; + } + if(old == '.'){ + for(int y = (type == Type.RULE ? 0 : 1); y < 12; y++){ + if(temp == validChars[y]) + return false; + } + } + old = temp; + for(char validChar : validChars){ + if(temp == validChar) + break; + if(validChar == ' ') + return false; + } + } + return bracket == 0; + } + + /** + * Used by {@link Rewrite} + * @return a list of rules with the left and right sides separated by a {@link lsystem.utils.Pair Pair} + */ + public List> parseRules() { + List> rules = new ArrayList<>(); + this.rules.forEach(rule -> { + String[] str = rule.split("="); + rules.add(new Pair(str[0], str[1])); + }); + return rules; + } + +} diff --git a/src/lsystem/engine/Rewrite.java b/src/lsystem/engine/Rewrite.java new file mode 100644 index 0000000..4a2c74f --- /dev/null +++ b/src/lsystem/engine/Rewrite.java @@ -0,0 +1,30 @@ +package lsystem.engine; + +import lsystem.utils.Pair; + +import java.util.List; + +public class Rewrite { + + private final String axiom; + private final List> rules; + private final int recurrences; + + public Rewrite(String axiom, List> rules, int recurrences) { + this.axiom = axiom; + this.rules = rules; + this.recurrences = recurrences; + } + + public String rewrite() { + String rewritted = axiom; + for(int i = 0; i < recurrences; ++i) { + for(int j = 0; j < rules.size(); ++j){ + Pair pair = rules.get(j); + rewritted = rewritted.replace(pair.getLeft(), pair.getRight()); + } + } + return rewritted; + } + +} diff --git a/src/lsystem/utils/Pair.java b/src/lsystem/utils/Pair.java new file mode 100644 index 0000000..20d95b5 --- /dev/null +++ b/src/lsystem/utils/Pair.java @@ -0,0 +1,51 @@ +package lsystem.utils; + +/** + * tuple containing 2 unknown type elements + * + * @param left + * @param right + */ +public class Pair { + + private final U left; + private final K right; + + public Pair(U left, K right) { + this.left = left; + this.right = right; + } + + public U getLeft() { + return left; + } + + public K getRight() { + return right; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Pair other = (Pair) obj; + return this.left.equals(other.getLeft()) && this.left.equals(other.getRight()); + } + + @Override + public int hashCode() { + return 31 + left.hashCode() * right.hashCode(); + } + + @Override + public String toString() { + return "(" + left + ", " + right + ")"; + } +}