Merge: Serialization: change annotation to `serialize` and intro `noserialize`
authorJean Privat <jean@pryen.org>
Sat, 30 May 2015 00:36:52 +0000 (20:36 -0400)
committerJean Privat <jean@pryen.org>
Sat, 30 May 2015 00:36:52 +0000 (20:36 -0400)
The annotation `serialize` replaces `auto_serialize` and it is also more versatile. It can annotate a module so all its class definitions are serializable. It can also annotate an attribute so only this attribute is serialized.

The `noserialize` annotation is for exceptions to `serialize`. The most common case is to mark as non-serializable an attribute such as a password or a data blob.

Pull-Request: #1389
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

29 files changed:
benchmarks/polygons/Makefile [new file with mode: 0644]
benchmarks/polygons/java/Makefile [new file with mode: 0644]
benchmarks/polygons/java/bench_polygon.sh [new file with mode: 0755]
benchmarks/polygons/java/code/AntiClockSort.java [new file with mode: 0644]
benchmarks/polygons/java/code/BenchPolygon.java [new file with mode: 0644]
benchmarks/polygons/java/code/ClockSort.java [new file with mode: 0644]
benchmarks/polygons/java/code/ConvexPolygon.java [new file with mode: 0644]
benchmarks/polygons/java/code/PointDouble.java [new file with mode: 0644]
benchmarks/polygons/java/code/PointXCompare.java [new file with mode: 0644]
benchmarks/polygons/java/code/PolygonSorter.java [new file with mode: 0644]
benchmarks/polygons/java/code/Projection.java [new file with mode: 0644]
benchmarks/polygons/nit/Makefile [new file with mode: 0644]
benchmarks/polygons/nit/bench_polygon.nit [new file with mode: 0644]
benchmarks/polygons/nit/bench_polygon.sh [new file with mode: 0755]
lib/geometry/polygon.nit [new file with mode: 0644]
share/nitdoc/css/nitdoc.css
src/doc/console_templates/console_templates.nit
src/doc/doc_base.nit
src/doc/doc_phases/doc_console.nit
src/doc/doc_phases/doc_graphs.nit
src/doc/doc_phases/doc_hierarchies.nit
src/doc/doc_phases/doc_html.nit
src/doc/doc_phases/doc_indexing.nit
src/doc/doc_phases/doc_intros_redefs.nit
src/doc/doc_phases/doc_lin.nit
src/doc/doc_phases/doc_poset.nit
src/doc/doc_phases/doc_structure.nit
src/doc/html_templates/html_model.nit
src/doc/html_templates/html_templates.nit

diff --git a/benchmarks/polygons/Makefile b/benchmarks/polygons/Makefile
new file mode 100644 (file)
index 0000000..e6bd303
--- /dev/null
@@ -0,0 +1,3 @@
+all:
+       $(MAKE) all -C nit
+       $(MAKE) all -C java
diff --git a/benchmarks/polygons/java/Makefile b/benchmarks/polygons/java/Makefile
new file mode 100644 (file)
index 0000000..e327368
--- /dev/null
@@ -0,0 +1,19 @@
+all: add_vertex_b sort_vertices_b intersection_b convex_hull_b convexity_b contain_b
+
+add_vertex_b:
+       ./bench_polygon.sh add_vertex
+
+sort_vertices_b:
+       ./bench_polygon.sh sort_vertices
+
+intersection_b:
+       ./bench_polygon.sh intersection
+
+convex_hull_b:
+       ./bench_polygon.sh convex_hull
+
+convexity_b:
+       ./bench_polygon.sh convexity
+
+contain_b:
+       ./bench_polygon.sh contain
diff --git a/benchmarks/polygons/java/bench_polygon.sh b/benchmarks/polygons/java/bench_polygon.sh
new file mode 100755 (executable)
index 0000000..46c2019
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/bash
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source ../../bench_common.sh
+source ../../bench_plot.sh
+
+# Default number of times a command must be run with bench_command
+# Can be overrided with 'the option -n'
+count=5
+points=100000
+
+function usage()
+{
+       echo "run_bench: [options]* bench_name args"
+       echo "  -n count: number of execution for each bar (default: $count)"
+       echo "  -p points: number of points used for the polygons (default: $points)"
+       echo "  -h: this help"
+       echo ""
+       echo "Benches : "
+       echo "  add_vertex: bench adding vertex in a polygon"
+       echo "    - usage : add_vertex nbpts"
+       echo "  sort_vertices: sort the vertices of the polygon"
+       echo "    - usage : sort_vertices nbpts"
+       echo "  intersection: bench the intersection between two polygons"
+       echo "    - usage : intersection nbpts"
+       echo "  convex_hull: bench creating the convex hull of a set of points"
+       echo "    - usage : convex_hull nbpts"
+       echo " convexity : bench the verification of the convexity of a polygon"
+       echo "    - usage : convexity nbpts"
+       echo " contain : bench the point in polygon check"
+       echo "    - usage : contain nbpts"
+}
+
+
+function bench_add_vertex()
+{
+       if [ -d add_vertex ]; then
+               rm add_vertex/*
+       else
+               mkdir add_vertex
+       fi
+
+       cd add_vertex
+
+       javac ../code/*.java -d .
+
+       prepare_res add_vertex.out add_vertex add_vertex
+
+       bench_command add_vertex add_vertex java BenchPolygon add_vertex $((points / 2))
+}
+
+function bench_sorting()
+{
+       if [ -d sort_vertices ]; then
+               rm sort_vertices/*
+       else
+               mkdir sort_vertices
+       fi
+       cd sort_vertices
+
+       javac ../code/*.java -d .
+
+       prepare_res sort_vertices.out sort_vertex sort_vertices
+
+       bench_command sort_vertices sort_vertices java BenchPolygon sort_vertices $((points * 20))
+
+}
+
+function bench_intersection()
+{
+       if [ -d intersection ]; then
+               rm intersection/*
+       else
+               mkdir intersection
+       fi
+       cd intersection
+
+       javac ../code/*.java -d .
+
+       prepare_res intersection.out intersection intersection
+
+       bench_command intersection intersection java BenchPolygon intersection $((points / 10))
+}
+
+function bench_convex_hull()
+{
+       echo Bench way too long, skipping it
+
+       return
+
+       if [ -d convex_hull ]; then
+               rm convex_hull/*
+       else
+               mkdir convex_hull
+       fi
+       cd convex_hull
+
+       javac ../code/*.java -d .
+
+       prepare_res convex_hull.out convex_hull convex_hull
+
+       bench_command convex_hull convex_hull java BenchPolygon convex_hull $((points * 30))
+}
+
+function bench_convexity()
+{
+       if [ -d convexity ]; then
+               rm convexity/*
+       else
+               mkdir convexity
+       fi
+       cd convexity
+
+       javac ../code/*.java -d .
+
+       prepare_res convexity.out convexity convexity
+
+       bench_command convexity convexity java BenchPolygon convexity $((points * 40))
+}
+
+
+function bench_contain()
+{
+       if [ -d contain ]; then
+               rm contain/*
+       else
+               mkdir contain
+       fi
+       cd contain
+
+       javac ../code/*.java -d .
+
+       prepare_res contain.out contain contain
+
+       bench_command contain contain java BenchPolygon contain $((points * 50))
+}
+
+stop=false
+while [ "$stop" = false ]; do
+       case "$1" in
+               -h) usage; exit;;
+               -n) count="$2"; shift; shift;;
+               -p) points="$2"; shift;shift;;
+               *) stop=true
+       esac
+done
+
+if test $# -lt 1; then
+       usage
+       exit
+fi
+
+case "$1" in
+       add_vertex) shift; bench_add_vertex $@ ;;
+       sort_vertices) shift; bench_sorting $@ ;;
+       intersection) shift; bench_intersection $@ ;;
+       convex_hull) shift; bench_convex_hull $@ ;;
+       convexity) shift; bench_convexity $@;;
+       contain) shift; bench_contain $@;;
+       *) usage; exit;;
+esac
diff --git a/benchmarks/polygons/java/code/AntiClockSort.java b/benchmarks/polygons/java/code/AntiClockSort.java
new file mode 100644 (file)
index 0000000..e698d62
--- /dev/null
@@ -0,0 +1,53 @@
+
+/**
+ *
+ * @author Johan
+ * @source
+ * http://stackoverflow.com/questions/6989100/sort-points-in-clockwise-order
+ */
+public class AntiClockSort extends PolygonSorter {
+
+    public AntiClockSort(double[][] points) {
+        super(points);
+    }
+
+    /**
+     * Compare polygon vertices in counter-clock wise order starting at six
+     * hour. If two points share the same rad, then the farest to the center is
+     * chosen.
+     *
+     * @param a: a point to compare
+     * @param b: a second point to compare
+     * @return
+     */
+    @Override
+    public int compare(PointDouble a, PointDouble b) {
+        if (a.x - center.x >= 0 && b.x - center.x < 0) {
+            return -1;
+        }
+        if (a.x - center.x < 0 && b.x - center.x >= 0) {
+            return +1;
+        }
+        if (a.x - center.x == 0 && b.x - center.x == 0) {
+            if (a.y - center.y >= 0 || b.y - center.y >= 0) {
+                return (a.y > b.y) ? -1 : +1;
+            }
+            return (b.y > a.y) ? -1 : +1;
+        }
+
+        // compute the cross product of vectors (center -> a) x (center -> b)
+        double det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y);
+        if (det < 0) {
+            return -1;
+        }
+        if (det > 0) {
+            return +1;
+        }
+
+        // points a and b are on the same line from the center
+        // check which point is closer to the center
+        double d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y);
+        double d2 = (b.x - center.x) * (b.x - center.x) + (b.y - center.y) * (b.y - center.y);
+        return (d1 > d2) ? -1 : +1;
+    }
+}
diff --git a/benchmarks/polygons/java/code/BenchPolygon.java b/benchmarks/polygons/java/code/BenchPolygon.java
new file mode 100644 (file)
index 0000000..bb7721f
--- /dev/null
@@ -0,0 +1,147 @@
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Random;
+
+/**
+ *
+ * @author Johan Kayser, Romain Chanoir
+ */
+/**
+ * Runs the benchmarks for the most important operations and generates a file
+ * with time execution results
+ */
+public class BenchPolygon {
+
+    public static void main(String[] args) throws IOException {
+           int  n = 0;
+           if(args[1] != null){
+               n = Integer.parseInt(args[1]);
+           }else {
+               n = 100000;
+           }
+       switch (args[0]){
+               case "add_vertex":
+                       testAddVertex(n);
+                       break;
+               case "sort_vertices":
+                       testSortVertices(n);
+                       break;
+               case "intersection":
+                       testIntersects(n);
+                       break;
+               case "convex_hull":
+                       testConvexHull(n);
+                       break;
+               case "convexity":
+                       testIsConvex(n);
+                       break;
+               case "contain":
+                       testContain(n);
+                       break;
+               default:
+                       break;
+       }
+    }
+
+    /**
+     * addVertex bench: adds a vertex to a polygon with the given number of
+     * vertices
+     */
+    public static void testAddVertex(int nb) throws IOException {
+        ArrayList<PointDouble> points = new ArrayList<>();
+        ArrayList<PointDouble> randomPoints = new ArrayList<>();
+        randomPoints = generatePoints(nb + 1);
+
+        for (int i = 0; i < nb; ++i) {
+            points.add(randomPoints.remove(0));
+        }
+        ConvexPolygon test = new ConvexPolygon(points);
+        test.sortVertices(new AntiClockSort(test.getVertices()));
+
+        test.addVertex(randomPoints.remove(0));
+    }
+
+    /**
+     * sortVertices bench: sorts the given number of vertices in the ArrayList
+     * of a polygon
+     */
+    public static void testSortVertices(int nb) throws IOException {
+        ArrayList<PointDouble> randomPoints = new ArrayList<>();
+        randomPoints = generatePoints(nb);
+        Collections.shuffle(randomPoints);
+
+        ConvexPolygon test = new ConvexPolygon(randomPoints);
+        test.sortVertices(new AntiClockSort(test.getVertices()));
+
+    }
+
+    /**
+     * intersects bench: tests the intersection between two polygons with the
+     * given number of vertices
+     */
+    public static void testIntersects(int nb) throws IOException {
+        ArrayList<PointDouble> points1 = new ArrayList<>();
+        ArrayList<PointDouble> points2 = new ArrayList<>();
+        points1 = generatePoints(nb);
+        points2 = generatePoints(nb);
+        ConvexPolygon test1 = new ConvexPolygon(points1);
+        ConvexPolygon test2 = new ConvexPolygon(points2);
+        test1.sortVertices(new AntiClockSort(test1.getVertices()));
+        test2.sortVertices(new AntiClockSort(test2.getVertices()));
+
+        Boolean rez = test1.intersects(test2);
+    }
+
+    /**
+     * convexHull bench: gets the convex hull of the given number of points
+     */
+    public static void testConvexHull(int nb) throws IOException {
+        ArrayList<PointDouble> randomPoints = new ArrayList<>();
+        randomPoints = generatePoints(nb);
+        Collections.shuffle(randomPoints);
+        ConvexPolygon test = new ConvexPolygon(randomPoints);
+
+        ConvexPolygon rez = test.convexHull(randomPoints);
+    }
+
+    /**
+     * isConvex bench: checks if the polygon with the given number of vertices
+     * is convex (we test the worst case -> polygon vertices are ordered)
+     */
+    public static void testIsConvex(int nb) throws IOException {
+        ArrayList<PointDouble> randomPoints = new ArrayList<>();
+        randomPoints = generatePoints(nb);
+        ConvexPolygon test = new ConvexPolygon(randomPoints);
+        test.sortVertices(new AntiClockSort(test.getVertices()));
+
+        Boolean rez = test.isConvex();
+    }
+
+    /**
+     * contain bench: checks if the polygon with the given number of vertices
+     * contains a randomly generated point
+     */
+    public static void testContain(int nb) throws IOException {
+        ArrayList<PointDouble> randomPoints = new ArrayList<>();
+        randomPoints = generatePoints(nb);
+        ConvexPolygon test = new ConvexPolygon(randomPoints);
+        test.sortVertices(new AntiClockSort(test.getVertices()));
+
+        Boolean rez = test.contain(new PointDouble(0.0, 0.0));
+    }
+
+    /**
+     * generates some points making it easier to use convex polygons
+     */
+    public static ArrayList<PointDouble> generatePoints(int nb) {
+        ArrayList<PointDouble> pts = new ArrayList<>();
+        pts = PointDouble.getNPointsOnCircle(100.0, nb);
+        return pts;
+    }
+}
diff --git a/benchmarks/polygons/java/code/ClockSort.java b/benchmarks/polygons/java/code/ClockSort.java
new file mode 100644 (file)
index 0000000..617b194
--- /dev/null
@@ -0,0 +1,50 @@
+
+/**
+ *
+ * @author Johan
+ */
+public class ClockSort extends PolygonSorter {
+
+    public ClockSort(double[][] points) {
+        super(points);
+    }
+
+    /**
+     * Compare polygon vertices in clock wise order starting at six hour. If two
+     * points share the same rad, then the farest to the center is chosen.
+     *
+     * @param a: a point to compare
+     * @param b: a second point to compare
+     * @return
+     */
+    @Override
+    public int compare(PointDouble a, PointDouble b) {
+        if (a.x - center.x >= 0 && b.x - center.x < 0) {
+            return +1;
+        }
+        if (a.x - center.x < 0 && b.x - center.x >= 0) {
+            return -1;
+        }
+        if (a.x - center.x == 0 && b.x - center.x == 0) {
+            if (a.y - center.y >= 0 || b.y - center.y >= 0) {
+                return (a.y > b.y) ? +1 : -1;
+            }
+            return (b.y > a.y) ? +1 : -1;
+        }
+
+        // compute the cross product of vectors (center -> a) x (center -> b)
+        double det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y);
+        if (det < 0.0d) {
+            return +1;
+        }
+        if (det > 0.0d) {
+            return -1;
+        }
+
+        // points a and b are on the same line from the center
+        // check which point is closer to the center
+        double d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y);
+        double d2 = (b.x - center.x) * (b.x - center.x) + (b.y - center.y) * (b.y - center.y);
+        return (d1 > d2) ? +1 : -1;
+    }
+}
diff --git a/benchmarks/polygons/java/code/ConvexPolygon.java b/benchmarks/polygons/java/code/ConvexPolygon.java
new file mode 100644 (file)
index 0000000..e02bc57
--- /dev/null
@@ -0,0 +1,280 @@
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ *
+ * @author romis_000, Johan
+ */
+public class ConvexPolygon {
+
+    private ArrayList<PointDouble> points = new ArrayList<PointDouble>();
+
+    public ConvexPolygon(ArrayList<PointDouble> points) {
+        this.points = points;
+    }
+
+    public ArrayList<PointDouble> getPointDoubles() {
+        return points;
+    }
+
+    public double[] GetXPointDoubles() {
+        double xpoints[] = new double[this.points.size()];
+        int c = 0;
+        for (PointDouble p : this.points) {
+            xpoints[c] = p.x;
+            ++c;
+        }
+        return xpoints;
+    }
+
+    public double[] GetYPointDoubles() {
+        double ypoints[] = new double[this.points.size()];
+        int c = 0;
+        for (PointDouble p : this.points) {
+            ypoints[c] = p.y;
+            ++c;
+        }
+        return ypoints;
+    }
+
+    public double[][] getVertices() {
+        double[][] vertices = new double[points.size()][2];
+        for (int i = 0; i < points.size(); ++i) {
+            vertices[i][0] = points.get(i).x;
+            vertices[i][1] = points.get(i).y;
+        }
+        return vertices;
+    }
+
+    /**
+     * Returns the axis corresponding to the edges of the polygon, used to check
+     * for detection collision
+     */
+    public PointDouble[] Getaxes() {
+        PointDouble[] axes = new PointDouble[this.points.size()];
+        for (int i = 0; i < this.points.size(); i++) {
+            // get the current vertex
+            PointDouble v1 = new PointDouble(this.points.get(i).getX(), this.points.get(i).getY());
+            // get the next vertex
+            PointDouble v2 = new PointDouble(this.points.get(i + 1 == this.points.size() ? 0 : i + 1).getX(), this.points.get(i + 1 == this.points.size() ? 0 : i + 1).getY());
+            // subtract the two to get the edge vector
+            PointDouble edge = new PointDouble((v1.x + (-v2.x)), (v1.y + (-v2.y)));
+            // get either perpendicular vector
+            PointDouble normal = new PointDouble((-edge.y), edge.x);
+            // the perp method is just (x, y) => (-y, x) or (y, -x)
+            axes[i] = normal;
+        }
+        return axes;
+    }
+
+    /**
+     * Checks if the polygon is convex
+     */
+    public boolean isConvex() {
+        PointDouble prev = points.get(points.size() - 2);
+        PointDouble curr = points.get(points.size() - 1);
+        PointDouble next = points.get(0);
+        // Are the first two selected edges making a turnleft ?
+        boolean isCCW = turnLeft(prev, curr, next);
+        // Verify if all the edges are making the same type of angle as the first two
+        for (int i = 1; i < points.size(); i++) {
+            prev = curr;
+            curr = next;
+            next = points.get(i);
+            if (turnLeft(prev, curr, next) != isCCW) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Checks if a given point is within the polygon
+     */
+    public boolean contain(PointDouble p) {
+        PointDouble prev = points.get(points.size() - 1);
+        PointDouble curr = p;
+        PointDouble next = points.get(0);
+        // Is the point left or right of the selected edge ?
+        boolean isCCW = turnLeft(prev, curr, next);
+        // Is the point the same side of every other edges of the polygon ?
+        for (int i = 1; i < points.size(); i++) {
+            prev = next;
+            next = points.get(i);
+            if (turnLeft(prev, curr, next) != isCCW) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check the sign of the angle between vectors [p1, p2] and [p2, p3]
+     * with the cross product
+     */
+    public boolean turnLeft(PointDouble p1, PointDouble p2, PointDouble p3) {
+        return ((p2.getX() - p1.getX()) * (p3.getY()
+                - p2.getY()) - (p3.getX() - p2.getX()) * (p2.getY() - p1.getY())) > 0;
+    }
+
+    /**
+     * Checks if the vertices of the polygon are in counter-clock wise order
+     * The vertices of the polygon need to be sorted for this test
+     */
+    public boolean isCCW() {
+        double min = points.get(0).getY();
+        int minIndex = 0;
+        for (int i = 1; i < points.size() - 1; i++) {
+            if (points.get(i).getY() < min) {
+                min = points.get(i).getY();
+                minIndex = i;
+            }
+        }
+        PointDouble prev = points.get((minIndex - 1 + points.size()) % points.size());
+        PointDouble next = points.get((minIndex + 1) % points.size());
+        return turnLeft(prev, points.get(minIndex), next);
+    }
+
+    /**
+     * Calculates the convex hull of list of points
+     * Using the monotone chain algorithm
+     */
+    public ConvexPolygon convexHull(ArrayList<PointDouble> points) {
+        Collections.sort(points, new PointXCompare());
+        int n = points.size();
+
+        ArrayList<PointDouble> pl1 = new ArrayList<PointDouble>();
+        ArrayList<PointDouble> pl2 = new ArrayList<PointDouble>();
+        for (int i = 0; i < n; i++) {
+            while (pl2.size() >= 2 && !(turnLeft(pl2.get(pl2.size() - 2), pl2.get(pl2.size() - 1), points.get(i)))) {
+                pl2.remove(pl2.get(pl2.size() - 1));
+            }
+            pl2.add(points.get(i));
+        }
+        for (int i = n - 1; i >= 0; i--) {
+            while (pl1.size() >= 2 && !(turnLeft(pl1.get(pl1.size() - 2), pl1.get(pl1.size() - 1), points.get(i)))) {
+                pl1.remove(pl1.get(pl1.size() - 1));
+            }
+            pl1.add(points.get(i));
+        }
+        pl1.remove(pl1.size() - 1);
+        pl2.remove(pl2.size() - 1);
+        pl2.addAll(pl1);
+        return new ConvexPolygon(pl2);
+    }
+
+    /**
+     * Translates the polygon from the given numbers
+     */
+    public void translate(double x, double y) {
+        for (PointDouble p : this.points) {
+            p.x += x;
+            p.y += y;
+        }
+    }
+
+    /**
+     * Checks for an intersection between the polygon and the second given
+     * polygon
+     */
+    public Boolean intersects(ConvexPolygon pol2) {
+        PointDouble[] axes1 = this.Getaxes();
+        PointDouble[] axes2 = pol2.Getaxes();
+        for (int i = 0; i < axes1.length; i++) {
+            PointDouble axis = axes1[i];
+            // project both shapes onto the axis
+            Projection p1 = this.project(axis);
+            Projection p2 = pol2.project(axis);
+            // do the projections overlap?
+            if (!p1.overlap(p2)) {
+                // then we can guarantee that the shapes do not overlap
+                return false;
+            }
+        }
+        for (int i = 0; i < axes2.length; i++) {
+            PointDouble axis = axes2[i];
+            Projection p1 = this.project(axis);
+            Projection p2 = pol2.project(axis);
+            if (!p1.overlap(p2)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Generates a projection of an edge of the polygon on a given axis
+     */
+    public Projection project(PointDouble axis) {
+        double min = ((axis.x * this.points.get(0).x) + (axis.y * this.points.get(0).y));
+        double max = min;
+        for (int i = 1; i < this.points.size(); i++) {
+            double p = ((axis.x * this.points.get(i).x) + (axis.y * this.points.get(i).y));
+            if (p < min) {
+                min = p;
+            } else if (p > max) {
+                max = p;
+            }
+        }
+        Projection proj = new Projection(min, max);
+        return proj;
+    }
+
+    public Boolean hasVertex(PointDouble pt) {
+        for (int i = 0; i < this.points.size(); i++) {
+            //for (int i = 0; i < this.points.size() - 1; i++) {
+            if (this.points.get(i).equals(pt)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Boolean deleteVertex(PointDouble pt) {
+        if (this.points.size() > 3) {
+            for (int i = 0; i < this.points.size(); i++) {
+                if (this.points.get(i).equals(pt)) {
+                    this.points.remove(i);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sorts the vertices of the polygon in an order specified by the sorter
+     * used
+     */
+    public void sortVertices(PolygonSorter sorter) {
+        PointDouble[] arrPointDoubles = points.toArray(new PointDouble[points.size()]);
+        Arrays.sort(arrPointDoubles, sorter);
+        this.points.clear();
+        points.addAll(Arrays.asList(arrPointDoubles));
+    }
+
+    /*
+    * Add a vertex to the polygon
+    * Return true if the vertex is added to the polygon
+    * Return false otherwise, which means that the addition
+    * of the vertex would have made it concave.
+    */
+    public Boolean addVertex(PointDouble pt) {
+        // Make a temporary list to check some properties of the new polygon
+        ArrayList<PointDouble> tempList = new ArrayList<>(Arrays.asList(this.points.toArray(new PointDouble[this.points.size()])));
+        tempList.add(pt);
+        // Create a temporary polygon
+        ConvexPolygon tempPolygon = new ConvexPolygon(tempList);
+        // Sort it
+        tempPolygon.sortVertices(new AntiClockSort(tempPolygon.getVertices()));
+        // We need the new polygon to be convex, or we can't accept to add the new vertex.
+        if (tempPolygon.isConvex()) {
+            this.points = tempPolygon.points;
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/benchmarks/polygons/java/code/PointDouble.java b/benchmarks/polygons/java/code/PointDouble.java
new file mode 100644 (file)
index 0000000..8e1b0a4
--- /dev/null
@@ -0,0 +1,60 @@
+
+import java.util.ArrayList;
+import java.util.Random;
+
+/**
+ * Represents a 2d point with double coordinates
+ *
+ * @author Johan
+ */
+public class PointDouble {
+
+    double x;
+    double y;
+
+    public PointDouble() {
+        this(0.0d, 0.0d);
+    }
+
+    public PointDouble(double x, double y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public double getX() {
+        return this.x;
+    }
+
+    public double getY() {
+        return this.y;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final PointDouble other = (PointDouble) obj;
+        if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) {
+            return false;
+        }
+        if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * returns a array of n points on a circle
+     */
+    public static ArrayList<PointDouble> getNPointsOnCircle(double radius, int n) {
+        ArrayList<PointDouble> points = new ArrayList<>();
+       Random generator = new Random(0);
+        for(int i = 0; i < n; i++){
+       double angle = generator.nextFloat() * Math.PI * 2;
+               PointDouble p = new PointDouble(Math.cos(angle) * radius, Math.sin(angle) * radius);
+       points.add(p);
+       }
+        return points;
+    }
+}
diff --git a/benchmarks/polygons/java/code/PointXCompare.java b/benchmarks/polygons/java/code/PointXCompare.java
new file mode 100644 (file)
index 0000000..34f4520
--- /dev/null
@@ -0,0 +1,21 @@
+
+import java.util.Comparator;
+
+/**
+ *
+ * @author romis_000
+ */
+class PointXCompare
+        implements Comparator<PointDouble> {
+
+    @Override
+    public int compare(final PointDouble a, final PointDouble b) {
+        if (a.x == b.x) {
+            //return (int) (a.y - b.y);
+            return (a.y > b.y) ? -1 : +1;
+        } else {
+            //return (int)(a.x - b.x);
+            return (a.x > b.x) ? -1 : +1;
+        }
+    }
+}
diff --git a/benchmarks/polygons/java/code/PolygonSorter.java b/benchmarks/polygons/java/code/PolygonSorter.java
new file mode 100644 (file)
index 0000000..e05b984
--- /dev/null
@@ -0,0 +1,33 @@
+
+import java.util.Comparator;
+
+/**
+ * An utility class to sort the polygon vertices, is extended by AntiClockSort
+ * and ClockSort
+ *
+ * @author Johan
+ */
+public abstract class PolygonSorter implements Comparator<PointDouble> {
+
+    PointDouble center;
+
+    public PolygonSorter(double[][] podoubles) {
+        this.center = calcCenter(podoubles);
+    }
+
+    /**
+     * returns the point representing the center of a polygon
+     */
+    final PointDouble calcCenter(double[][] podoubles) {
+        double sumx = 0;
+        double sumy = 0;
+        for (double[] podouble : podoubles) {
+            sumx += podouble[0];
+            sumy += podouble[1];
+        }
+        return new PointDouble(sumx / podoubles.length, sumy / podoubles.length);
+    }
+
+    @Override
+    public abstract int compare(PointDouble a, PointDouble b);
+}
diff --git a/benchmarks/polygons/java/code/Projection.java b/benchmarks/polygons/java/code/Projection.java
new file mode 100644 (file)
index 0000000..e18688e
--- /dev/null
@@ -0,0 +1,21 @@
+
+/**
+ * An utility class to store a projection of an edge on an axis, used by the
+ * intersects operation
+ *
+ * @author Johan
+ */
+public class Projection {
+
+    private double min;
+    private double max;
+
+    public Projection(double min, double max) {
+        this.min = min;
+        this.max = max;
+    }
+
+    public Boolean overlap(Projection p2) {
+        return !(this.min > p2.max || p2.min > this.max);
+    }
+}
diff --git a/benchmarks/polygons/nit/Makefile b/benchmarks/polygons/nit/Makefile
new file mode 100644 (file)
index 0000000..e327368
--- /dev/null
@@ -0,0 +1,19 @@
+all: add_vertex_b sort_vertices_b intersection_b convex_hull_b convexity_b contain_b
+
+add_vertex_b:
+       ./bench_polygon.sh add_vertex
+
+sort_vertices_b:
+       ./bench_polygon.sh sort_vertices
+
+intersection_b:
+       ./bench_polygon.sh intersection
+
+convex_hull_b:
+       ./bench_polygon.sh convex_hull
+
+convexity_b:
+       ./bench_polygon.sh convexity
+
+contain_b:
+       ./bench_polygon.sh contain
diff --git a/benchmarks/polygons/nit/bench_polygon.nit b/benchmarks/polygons/nit/bench_polygon.nit
new file mode 100644 (file)
index 0000000..695f4aa
--- /dev/null
@@ -0,0 +1,135 @@
+#This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2015 Romain Chanoir <romain.chanoir@viacesi.fr>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# a bench for the polygons
+module bench_polygon
+
+intrude import geometry::polygon
+import opts
+
+# Bench adding random vertices
+fun bench_add_vertices(n: Int) do
+       var randompoints = generate_points(n + 1)
+       var points = randompoints.clone
+       points.remove_at(points.length -1)
+       var poly = new ConvexPolygon.with_vertices(randompoints)
+       poly.sort_ccw
+       poly.add_vertex(randompoints.last)
+end
+
+# Bench the convex hull algorithm
+fun bench_convex_hull(n: Int) do
+       srand_from(50)
+       var randompoints = new Array[Point[Float]]
+       for i in [0..n] do
+               var point = new Point[Float](300.0.rand, 300.0.rand)
+               randompoints.add(point)
+       end
+       convex_hull(randompoints)
+end
+
+
+# Bench the convexity verificatioon
+fun bench_convexity(n : Int) do
+       var randompoints = generate_points(n)
+       var poly = new ConvexPolygon.with_vertices(randompoints)
+       poly.sort_ccw
+       poly.is_convex
+end
+
+# Bench the point in polygon algorithm
+fun bench_contain(n : Int) do
+       srand_from(50)
+       var randompoints = generate_points(n)
+       var poly = new ConvexPolygon.with_vertices(randompoints)
+       poly.sort_ccw
+       var point = new Point[Float](0.0, 0.0)
+       poly.contain(point)
+end
+
+# Bench the sorting of the vertices
+fun bench_sorting(n : Int) do
+       var randompoints = generate_points(n)
+       randompoints.shuffle
+       var poly = new ConvexPolygon.with_vertices(randompoints)
+       poly.sort_ccw
+
+end
+
+# Bench the intersection test between two polygons
+fun bench_intersection(n : Int) do
+       var r1 = generate_points(n)
+       var r2 = generate_points(n)
+       var poly1 = new ConvexPolygon.with_vertices(r1)
+       var poly2 = new ConvexPolygon.with_vertices(r2)
+       poly1.sort_ccw
+       poly2.sort_ccw
+       poly1.intersects(poly2)
+end
+
+# Get `n` points from a circle
+fun get_points_on_circle(radius: Float, n: Int): Array[Point[Float]] do
+       srand_from(50)
+       var points = new Array[Point[Float]]
+       for i in n.times do
+               var angle =  1000.0.rand * pi * 2.0
+               var point = new Point[Float](angle.cos * radius, angle.sin * radius)
+               points.add(point)
+       end
+       return points
+end
+
+# Helper for `get_points_on_circle`
+fun generate_points(n: Int): Array[Point[Float]] do
+       return get_points_on_circle(100.0, n)
+end
+
+var opts = new OptionContext
+var mode = new OptionEnum(["add_vertex","sort_vertices","intersection","convex_hull","is_convex","contain"], "Mode", -1, "-m")
+var nb_points = new OptionInt("number of points generated for the bench", -1, "--nbpts")
+opts.add_option(mode, nb_points)
+
+opts.parse(args)
+
+if nb_points.value == -1 then
+       opts.usage
+       exit(-1)
+end
+
+var modval = mode.value
+var n = nb_points.value
+if modval == 0 then
+       print "bench_add_vertices"
+       bench_add_vertices(n)
+else if modval == 1 then
+       print "bench_sorting"
+       bench_sorting(n)
+else if modval == 2 then
+       print "bench_intersection"
+       bench_intersection(n)
+else if modval == 3 then
+       print "bench_convex_hull"
+       bench_convex_hull(n)
+else if modval == 4 then
+       print "bench_convexity"
+       bench_convexity(n)
+else if modval == 5 then
+       print "bench_contain"
+       bench_contain(n)
+else
+       opts.usage
+       exit(-1)
+end
diff --git a/benchmarks/polygons/nit/bench_polygon.sh b/benchmarks/polygons/nit/bench_polygon.sh
new file mode 100755 (executable)
index 0000000..52895e9
--- /dev/null
@@ -0,0 +1,168 @@
+#!/bin/bash
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source ../../bench_common.sh
+source ../../bench_plot.sh
+
+# Default number of times a command must be run with bench_command
+# Can be overrided with 'the option -n'
+count=5
+points=100000
+
+function usage()
+{
+       echo "run_bench: [options]* bench_name args"
+       echo "  -n count: number of execution for each bar (default: $count)"
+       echo "  -p points: number of points used for the polygons (default: $points)"
+       echo "  -h: this help"
+       echo ""
+       echo "Benches : "
+       echo "  add_vertex: bench adding vertex in a polygon"
+       echo "    - usage : add_vertex p"
+       echo "  sort_vertices: sort the vertices of the polygon"
+       echo "    - usage : sort_vertices p"
+       echo "  intersection: bench the intersection between two polygons"
+       echo "    - usage : intersection p"
+       echo "  convex_hull: bench creating the convex hull of a set of points"
+       echo "    - usage : convex_hull p"
+       echo " convexity : bench the verification of the convexity of a polygon"
+       echo "    - usage : convexity p"
+       echo " contain : bench the point in polygon check"
+       echo "    - usage : contain p"
+}
+
+
+function bench_add_vertex()
+{
+       if [ -d add_vertex ]; then
+               rm add_vertex/*
+       else
+               mkdir add_vertex
+       fi
+       cd add_vertex
+
+       ../../../../bin/nitc --global ../bench_polygon.nit
+
+       prepare_res add_vertex.out add_vertex add_vertex
+
+       bench_command add_vertex add_vertex ./bench_polygon -m  add_vertex --nbpts $((points / 2))
+}
+
+function bench_sorting()
+{
+       if [ -d sort_vertices ]; then
+               rm sort_vertices/*
+       else
+               mkdir sort_vertices
+       fi
+       cd sort_vertices
+
+       ../../../../bin/nitc --global ../bench_polygon.nit
+
+       prepare_res sort_vertices.out sort_vertex sort_vertices
+
+       bench_command sort_vertices sort_vertices ./bench_polygon -m sort_vertices  --nbpts $((points * 20))
+}
+
+function bench_intersection()
+{
+       if [ -d intersection ]; then
+               rm intersection/*
+       else
+               mkdir intersection
+       fi
+       cd intersection
+
+       ../../../../bin/nitc --global ../bench_polygon.nit
+
+       prepare_res intersection.out intersection intersection
+
+       bench_command intersection intersection ./bench_polygon -m  intersection --nbpts $((points / 10))
+}
+
+function bench_convex_hull()
+{
+       if [ -d convex_hull ]; then
+               rm convex_hull/*
+       else
+               mkdir convex_hull
+       fi
+       cd convex_hull
+
+       ../../../../bin/nitc --global ../bench_polygon.nit
+
+       prepare_res convex_hull.out convex_hull convex_hull
+
+       bench_command convex_hull convex_hull ./bench_polygon -m convex_hull  --nbpts $((points * 30))
+}
+
+function bench_convexity()
+{
+       if [ -d convexity ]; then
+               rm convexity/*
+       else
+               mkdir convexity
+       fi
+       cd convexity
+
+       ../../../../bin/nitc --global ../bench_polygon.nit
+
+       prepare_res convexity.out convexity convexity
+
+       bench_command convexity convexity ./bench_polygon -m is_convex  --nbpts $((points * 40))
+}
+
+
+function bench_contain()
+{
+       if [ -d contain ]; then
+               rm contain/*
+       else
+               mkdir contain
+       fi
+       cd contain
+
+       ../../../../bin/nitc --global ../bench_polygon.nit
+
+       prepare_res contain.out contain contain
+
+       bench_command contain contain ./bench_polygon -m contain  --nbpts $((points * 50))
+
+}
+
+stop=false
+while [ "$stop" = false ]; do
+       case "$1" in
+               -h) usage; exit;;
+               -n) count="$2"; shift; shift;;
+               -p) points="$2"; shift;shift;;
+               *) stop=true
+       esac
+done
+
+if test $# -lt 1; then
+       usage
+       exit
+fi
+
+case "$1" in
+       add_vertex) shift; bench_add_vertex $@ ;;
+       sort_vertices) shift; bench_sorting $@ ;;
+       intersection) shift; bench_intersection $@ ;;
+       convex_hull) shift; bench_convex_hull $@ ;;
+       convexity) shift; bench_convexity $@;;
+       contain) shift; bench_contain $@;;
+       *) usage; exit;;
+esac
diff --git a/lib/geometry/polygon.nit b/lib/geometry/polygon.nit
new file mode 100644 (file)
index 0000000..8fc1969
--- /dev/null
@@ -0,0 +1,426 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2015 Romain Chanoir <romain.chanoir@viacesi.fr>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Convex Polygons manipulations
+module polygon
+
+import points_and_lines
+
+# Convex Polygon class
+class ConvexPolygon
+
+       # Vertices of this polygon
+       var points = new Array[Point[Float]]
+
+       # Init this polygon with a list of `points`
+       # REQUIRE `pts.length == 3`
+       init with_vertices(pts: Array[Point[Float]]) do
+               assert pts.length >= 3
+               points.add_all(pts)
+       end
+
+       # Get an array of the x coordinates of the vertices
+       private fun x_coordinates: Array[Float] do
+               return [for p in points do p.x]
+       end
+
+       # Get an array of the y coordinates of the vertices
+       private fun y_coordinates: Array[Float] do
+               return [for p in points do p.y]
+       end
+
+       # Get a matrice containing the coordinates of the vertices
+       private fun vertices: Array[Array[Float]] do
+               var vertices = new Array[Array[Float]]
+               for i in [0..points.length[ do
+                       var temp = new Array[Float]
+                       temp.add(points[i].x)
+                       temp.add(points[i].y)
+                       vertices.add(temp)
+               end
+               return vertices
+       end
+
+       # Returns the axes corresponding to the edges of the polygon, used for collision detection
+       private fun axes: Array[Point[Float]] do
+               var axes = new Array[Point[Float]]
+               for i in [0..points.length[ do
+                       var p1 = new Point[Float](points[i].x, points[i].y)
+                       var p2 = new Point[Float](points[(i+1) % points.length].x, points[(i+1) % points.length].y)
+                       var edge = new Point[Float](p1.x - p2.x, p1.y - p2.y)
+                       var normal = new Point[Float](-edge.y, edge.x)
+                       axes[i] = normal
+               end
+               return axes
+       end
+
+       # Sort the vertices in counter clockwise order
+       #
+       # ~~~
+       # var p1 = new Point[Float](0.0, 0.0)
+       # var p2 = new Point[Float](5.0, 0.0)
+       # var p3 = new Point[Float](0.0, 5.0)
+       # var p4 = new Point[Float](5.0, 5.0)
+       # var arr = new Array[Point[Float]].with_items(p1, p2, p3, p4)
+       # var poly = new ConvexPolygon.with_vertices(arr)
+       # poly.sort_ccw
+       # assert poly.points == [p4, p2, p1, p3]
+       # ~~~
+       fun sort_ccw do
+               var sorter = new CounterClockWiseSort.with_center(vertices)
+               sorter.sort(points)
+       end
+
+       # Sort the vertices in clockwise order
+       #
+       # ~~~
+       # var p1 = new Point[Float](0.0, 0.0)
+       # var p2 = new Point[Float](5.0, 0.0)
+       # var p3 = new Point[Float](0.0, 5.0)
+       # var p4 = new Point[Float](5.0, 5.0)
+       # var arr = new Array[Point[Float]].with_items(p1, p2, p3, p4)
+       # var poly = new ConvexPolygon.with_vertices(arr)
+       # poly.sort_cw
+       # assert poly.points == [p3, p1, p2, p4]
+       # ~~~
+       fun sort_cw do
+               var sorter = new ClockWiseSort.with_center(vertices)
+               sorter.sort(points)
+       end
+
+       # Does this polygon intersects `other` ?
+       #
+       # ~~~
+       # var p1 = new Point[Float](0.0, 0.0)
+       # var p2 = new Point[Float](5.0, 0.0)
+       # var p3 = new Point[Float](0.0, 5.0)
+       # var p4 = new Point[Float](5.0, 5.0)
+       # var arr = new Array[Point[Float]].with_items(p1, p2, p3, p4)
+       # var poly = new ConvexPolygon.with_vertices(arr)
+       # poly.sort_ccw
+       # p1 = new Point[Float](2.5, 2.5)
+       # p2 = new Point[Float](7.5, 2.5)
+       # p3 = new Point[Float](2.5, 7.5)
+       # p4 = new Point[Float](7.5, 7.5)
+       # arr = new Array[Point[Float]].with_items(p1, p2, p3, p4)
+       # var poly2 = new ConvexPolygon.with_vertices(arr)
+       # poly2.sort_ccw
+       # assert poly.intersects(poly2)
+       # ~~~
+       fun intersects(other: ConvexPolygon): Bool do
+               assert is_convex
+
+               var axes1 = axes
+               var axes2 = other.axes
+               for axis in axes1 do
+                       var project1 = project(axis)
+                       var project2 = other.project(axis)
+                       if not project1.overlap(project2) then return false
+               end
+               for axis in axes2 do
+                       var project1 = project(axis)
+                       var project2 = other.project(axis)
+                       if not project1.overlap(project2) then return false
+               end
+               return true
+       end
+
+       # Generate a projection of an edge of the polygon on a given axis
+       private fun project(axis: Point[Float]): Projection do
+               var min = axis.x * points[0].x + axis.y * points[0].y
+               var max = min
+               for i in [0..points.length[ do
+                       var p = axis.x * points[i].x + axis.y * points[i].y
+                       if p < min then min = p
+                       if p > max then max = p
+               end
+               var projection = new Projection(min, max)
+               return projection
+       end
+
+       # Is this polygon convex ?
+       #
+       # ~~~
+       # var p1 = new Point[Float](0.0, 0.0)
+       # var p2 = new Point[Float](5.0, 0.0)
+       # var p3 = new Point[Float](0.0, 5.0)
+       # var p4 = new Point[Float](5.0, 5.0)
+       # var arr = new Array[Point[Float]].with_items(p1, p2, p3, p4)
+       # var poly = new ConvexPolygon.with_vertices(arr)
+       # poly.sort_ccw
+       # assert poly.is_convex
+       # ~~~
+       fun is_convex: Bool do
+               var prev = points[points.length - 2]
+               var curr = points[points.length - 1]
+               var next = points[0]
+               var is_ccw = turn_left(prev, curr, next)
+               for i in [1..points.length[ do
+                       prev = curr
+                       curr= next
+                       next = points[i]
+                       if turn_left(prev ,curr, next) != is_ccw then return false
+               end
+               return true
+       end
+
+       # Check if `p` is in the polygon
+       #
+       # ~~~
+       # var p1 = new Point[Float](0.0, 0.0)
+       # var p2 = new Point[Float](5.0, 0.0)
+       # var p3 = new Point[Float](0.0, 5.0)
+       # var p4 = new Point[Float](5.0, 5.0)
+       # var p5 = new Point[Float](2.5, 2.5)
+       # var arr = new Array[Point[Float]].with_items(p1, p2, p3, p4)
+       # var poly = new ConvexPolygon.with_vertices(arr)
+       # poly.sort_ccw
+       # assert poly.contain(p5)
+       # ~~~
+       fun contain(p : Point[Float]): Bool do
+               var prev = points[points.length - 1]
+               var curr = p
+               var next = points[0]
+               var is_ccw = turn_left(prev, curr, next)
+               for i in [1..points.length[ do
+                       prev = next
+                       next = points[i]
+                       if turn_left(prev, curr, next) != is_ccw then return false
+               end
+               return true
+       end
+
+       # Check if the order of the points in the polygon is counter-clockwise
+       # The vertices in the polygon need to be sorted
+       #
+       # ~~~
+       # var p1 = new Point[Float](0.0, 0.0)
+       # var p2 = new Point[Float](5.0, 0.0)
+       # var p3 = new Point[Float](0.0, 5.0)
+       # var p4 = new Point[Float](5.0, 5.0)
+       # var arr = new Array[Point[Float]].with_items(p1, p2, p3, p4)
+       # var poly = new ConvexPolygon.with_vertices(arr)
+       # poly.sort_ccw
+       # assert poly.is_ccw
+       # ~~~
+       fun is_ccw: Bool do
+               var min = points[0].y
+               var min_index = 0
+               for i in [1..points.length - 1[ do
+                       if points[i].y < min then
+                               min = points[i].y
+                               min_index = i
+                       end
+               end
+               var prev = points[(min_index - 1 + points.length) % points.length]
+               var next = points[(min_index + 1) % points.length]
+               return not turn_left(prev, points[min_index], next)
+       end
+
+       # Remove  `p` from the vertices, keeping at least 3 vertices
+       fun delete_vertex(p: Point[Float]) do
+               if points.length > 3 then
+                       points.remove(p)
+               end
+       end
+
+       # Add a vertex to the polygon
+       #
+       # ~~~
+       # var p1 = new Point[Float](0.0, 0.0)
+       # var p2 = new Point[Float](5.0, 0.0)
+       # var p3 = new Point[Float](0.0, 5.0)
+       # var p4 = new Point[Float](5.0, 5.0)
+       # var arr = new Array[Point[Float]].with_items(p1, p2, p3, p4)
+       # var poly = new ConvexPolygon.with_vertices(arr)
+       # var p5 = new Point[Float](2.5, 7.5)
+       # assert poly.add_vertex(p5)
+       # ~~~
+       fun add_vertex(p: Point[Float]): Bool do
+               assert points.length >= 3
+               var temp_list = points.clone
+               temp_list.add(p)
+               var temp_polygon = new ConvexPolygon.with_vertices(temp_list)
+               temp_polygon.sort_ccw
+               if temp_polygon.is_convex then
+                       points = temp_polygon.points
+                       return true
+               else
+                       return false
+               end
+       end
+end
+
+# Projection of an edge of a `ConvexPolygon` used for intersection test
+private class Projection
+       var min: Float is writable
+       var max: Float is writable
+
+       fun overlap(other: Projection): Bool do
+               return not (min > other.max or other.min > max)
+       end
+end
+
+private class PointXCompare
+       super Comparator
+
+       redef type COMPARED: Point[Float]
+
+       redef fun compare(a,b : COMPARED): Int do
+               if a.x == b.x then
+                       if a.y == b.y then return 0
+                       if a.y > b.y then return - 1
+                       return 1
+               else
+                       if a.x > b.x then return -1
+                       return 1
+               end
+       end
+end
+
+# Sorter for polygon vertices
+private abstract class PolygonSorter
+       super Comparator
+
+       redef type COMPARED: Point[Float]
+
+       # Center of the polygon's points
+       var center: COMPARED
+
+       # init calculating the center
+       init with_center(pts : Array[Array[Float]]) do
+               center = calc_center(pts)
+       end
+
+       # Calculate the center
+       fun calc_center(pts : Array[Array[Float]]): COMPARED do
+               var sumx = 0.0
+               var sumy = 0.0
+               for ap in pts do
+                       sumx += ap[0]
+                       sumy += ap[1]
+               end
+               return new Point[Float](sumx / pts.length.to_f, sumy / pts.length.to_f)
+       end
+end
+
+# Sort the vertices of a polygon in counter clockwise order
+private class CounterClockWiseSort
+       super PolygonSorter
+
+       redef fun compare(a,b: COMPARED): Int do
+               if a.x == b.x and a.y == b.y then return 0
+               if a.x - center.x >= 0.0 and b.x - center.x < 0.0 then return -1
+               if a.x - center.x < 0.0 and b.x - center.x >= 0.0 then return 1
+               if a.x - center.x == 0.0 and b.x - center.x == 0 then
+                       if a.y - center.y >= 0.0 or b.y - center.y >= 0.0 then
+                               if a.y > b.y then return -1
+                               return 1
+                       end
+                       if b.y > a.y then return -1
+                       return 1
+               end
+
+               var det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y)
+               if det > 0.0 then return 1
+               if det < 0.0 then return -1
+
+               var d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y)
+               var d2 = (b.x - center.x) * (b.x - center.x) + (b.y - center.y) * (b.y - center.y)
+               if d1 > d2 then return -1
+               return 1
+       end
+end
+
+# Sort the vertices of a polygon in clockwise order
+private class ClockWiseSort
+       super PolygonSorter
+
+       redef fun compare(a,b: COMPARED): Int do
+               if a.x == b.x and a.y == b.y then return 0
+               if a.x - center.x >= 0.0 and b.x - center.x < 0.0 then return 1
+               if a.x - center.x < 0.0 and b.x - center.x >= 0.0 then return -1
+               if a.x - center.x == 0.0 and b.x - center.x == 0 then
+                       if a.y - center.y >= 0.0 or b.y - center.y >= 0.0 then
+                               if a.y > b.y then return 1
+                               return -1
+                       end
+                       if b.y > a.y then return 1
+                       return -1
+               end
+
+               var det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y)
+               if det > 0.0 then return -1
+               if det < 0.0 then return 1
+
+               var d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y)
+               var d2 = (b.x - center.x) * (b.x - center.x) + (b.y - center.y) * (b.y - center.y)
+               if d1 > d2 then return 1
+               return -1
+       end
+end
+
+
+# Get the convex hull of the set of `points`
+#
+# ~~~
+# var p1 = new Point[Float](0.0, 0.0)
+# var p2 = new Point[Float](5.0, 0.0)
+# var p3 = new Point[Float](0.0, 5.0)
+# var p4 = new Point[Float](5.0, 5.0)
+# var p5 = new Point[Float](2.5, 2.5)
+# var arr = new Array[Point[Float]].with_items(p1, p2, p3, p4, p5)
+# var poly = convex_hull(arr)
+# assert poly.points == [p4, p3, p1, p2]
+# ~~~
+fun convex_hull(points: Array[Point[Float]]): ConvexPolygon do
+       var sorter = new PointXCompare
+       sorter.sort(points)
+       var l = points.length
+
+       var pl1 = new Array[Point[Float]]
+       var pl2 = new Array[Point[Float]]
+       for i in [0..l[ do
+               while pl2.length >= 2 and not turn_left(pl2[pl2.length - 2], pl2[pl2.length - 1], points[i]) do
+                       pl2.remove(pl2.last)
+               end
+               pl2.add(points[i])
+       end
+       var i = l - 1
+       while i >= 0 do
+               while pl1.length >= 2 and not turn_left(pl1[pl1.length - 2], pl1[pl1.length - 1], points[i]) do
+                       pl1.remove(pl1.last)
+               end
+               pl1.add(points[i])
+               i-= 1
+       end
+       pl1.remove_at(pl1.length - 1)
+       pl2.remove_at(pl2.length -1)
+       pl2.add_all(pl1)
+       return new ConvexPolygon.with_vertices(pl2)
+end
+
+# Is the angle between [p1,p2] and [p2,p3] going left (counter clockwise) or right (clockwise) ?
+#
+# ~~~
+# var p1 = new Point[Float](0.0, 0.0)
+# var p2 = new Point[Float](5.0, 0.0)
+# var p3 = new Point[Float](0.0, 5.0)
+# assert turn_left(p1, p2, p3)
+# ~~~
+fun turn_left(p1, p2, p3: Point[Float]): Bool do
+       return ((p2.x - p1.x) * (p3.y - p2.y) - (p3.x - p2.x) * (p2.y - p1.y)) > 0.0
+end
index e47f3aa..01da471 100644 (file)
@@ -81,9 +81,6 @@ article.nospace {
        color: #666;
 }
 
-#sidebar .panel-body ul .list-labeled>li {
-}
-
 #sidebar .panel-body ul ul ul>li {
        font-size: 13px;
        color: #999;
index 45bc33e..e94a388 100644 (file)
@@ -81,11 +81,6 @@ redef class ConcernSection
        end
 end
 
-redef class ConstructorsSection
-       redef var cs_title = "Constructors"
-       redef var cs_subtitle = null
-end
-
 redef class MEntityComposite
        redef var cs_title is lazy do return mentity.cs_name
        redef var cs_subtitle is lazy do return mentity.cs_namespace
index 8f0467d..4338eee 100644 (file)
@@ -75,6 +75,15 @@ class DocPage
        var root = new DocRoot
 
        redef fun to_s do return title
+
+       # Pretty prints the content of this page.
+       fun pretty_print: Writable do
+               var res = new Template
+               res.addn "page: {title}"
+               res.addn ""
+               root.pretty_print_in(res)
+               return res
+       end
 end
 
 # `DocPage` elements that can be nested in another.
@@ -91,6 +100,19 @@ abstract class DocComposite
        # Parent element.
        var parent: nullable DocComposite = null is writable
 
+       # Element uniq id.
+       #
+       # The `id` is used as name for the generated element (if any).
+       # Because multiple elements can be generated in the same container
+       # it should be uniq.
+       #
+       # The `id` can also be used to establish links between elements
+       # (HTML links, HTML anchors, vim links, etc.).
+       var id: String is writable
+
+       # Item title if any.
+       var title: nullable String
+
        # Does `self` have a `parent`?
        fun is_root: Bool do return parent == null
 
@@ -99,8 +121,18 @@ abstract class DocComposite
        # Children are ordered, this order can be changed by the `DocPhase`.
        var children = new Array[DocComposite]
 
-       # Does `self` have `children`?
-       fun is_empty: Bool do return children.is_empty
+       # Is `self` not displayed in the page.
+       #
+       # By default, empty elements are hidden.
+       fun is_hidden: Bool do return children.is_empty
+
+       # Title used in table of content if any.
+       var toc_title: nullable String is writable, lazy do return title
+
+       # Is `self` hidden in the table of content?
+       var is_toc_hidden: Bool is writable, lazy do
+               return toc_title == null or is_hidden
+       end
 
        # Add a `child` to `self`.
        #
@@ -115,6 +147,20 @@ abstract class DocComposite
                if parent == null then return 0
                return parent.depth + 1
        end
+
+       # Pretty prints this composite recursively.
+       fun pretty_print: Writable do
+               var res = new Template
+               pretty_print_in(res)
+               return res
+       end
+
+       # Appends the Pretty print of this composite in `res`.
+       private fun pretty_print_in(res: Template) do
+               res.add "#" * depth
+               res.addn " {id}"
+               for child in children do child.pretty_print_in(res)
+       end
 end
 
 # The `DocComposite` element that contains all the other.
@@ -122,8 +168,12 @@ end
 # The root uses a specific subclass to provide different a different behavior
 # than other `DocComposite` elements.
 class DocRoot
+       noautoinit
        super DocComposite
 
+       redef var id = "<root>"
+       redef var title = "<root>"
+
        # No op for `RootSection`.
        redef fun parent=(p) do end
 end
index f927fd9..4c0c193 100644 (file)
@@ -140,7 +140,7 @@ interface NitxQuery
        # Pretty prints the results for the console.
        fun make_results(nitx: Nitx, results: Array[NitxMatch]): DocPage do
                var page = new DocPage("results", "Results")
-               page.root.add_child(new QueryResultArticle(self, results))
+               page.root.add_child(new QueryResultArticle("results.article", "Results", self, results))
                return page
        end
 
@@ -215,8 +215,8 @@ class CommentQuery
                if len == 1 then
                        var res = results.first.as(MEntityMatch)
                        var mentity = res.mentity
-                       var page = new DocPage("resultats", "Results")
-                       var article = new DefinitionArticle(mentity)
+                       var page = new DocPage("results", "Results")
+                       var article = new DefinitionArticle("results.article", "Results", mentity)
                        article.cs_title = mentity.name
                        article.cs_subtitle = mentity.cs_declaration
                        page.root.add_child article
@@ -389,7 +389,7 @@ class CodeQuery
        redef fun make_results(nitx, results) do
                var page = new DocPage("results", "Code Results")
                for res in results do
-                       page.add new CodeQueryArticle(self, res.as(CodeMatch))
+                       page.add new CodeQueryArticle("results.article", "Results", self, res.as(CodeMatch))
                end
                return page
        end
index 1339d09..50a2aab 100644 (file)
@@ -73,7 +73,7 @@ redef class MModulePage
                        end
                end
                op.append("\}\n")
-               return new GraphArticle(mentity, name, "Importation Graph", op)
+               return new GraphArticle("{mentity.nitdoc_id}.graph", "Importation Graph", name, op)
        end
 end
 
@@ -107,7 +107,7 @@ redef class MClassPage
                        end
                end
                op.append("\}\n")
-               return new GraphArticle(mentity, name, "Inheritance Graph", op)
+               return new GraphArticle("{mentity.nitdoc_id}.graph", "Inheritance Graph", name, op)
        end
 end
 
@@ -116,16 +116,14 @@ end
 # The graph is stored in dot format.
 # The final output is delayed untill rendering.
 class GraphArticle
-       super MEntityComposite
+       super DocArticle
 
        # Graph ID (used for outputing file with names).
-       var id: String
-
-       # Graph title to display.
-       var graph_title: String
+       var graph_id: String
 
        # Dot script of the graph.
        var dot: Text
 
-       redef var is_empty = false
+       redef var is_hidden = false
+       redef var is_toc_hidden = true
 end
index eecc95b..fa9de2d 100644 (file)
@@ -40,14 +40,15 @@ end
 
 redef class MModulePage
        redef fun build_inh_list(v, doc) do
-               var section = new ImportationListSection(mentity)
-               var group = new PanelGroup("List")
+               var id = mentity.nitdoc_id
+               var section = new TabbedGroup("{id}.importation", "Dependencies")
+               var group = new PanelGroup("list.group", "List")
                var imports = self.imports.to_a
                v.name_sorter.sort(imports)
-               group.add_child new HierarchyListArticle(mentity, "Imports", imports)
+               group.add_child new MEntitiesListArticle("{id}.imports", "Imports", imports)
                var clients = self.clients.to_a
                v.name_sorter.sort(clients)
-               group.add_child new HierarchyListArticle(mentity, "Clients", clients)
+               group.add_child new MEntitiesListArticle("{id}.clients", "Clients", clients)
                section.add_child group
                section.parent = root.children.first
                root.children.first.children.insert(section, 1)
@@ -56,45 +57,23 @@ end
 
 redef class MClassPage
        redef fun build_inh_list(v, doc) do
-               var section = new InheritanceListSection(mentity)
-               var group = new PanelGroup("List")
+               var id = mentity.nitdoc_id
+               var section = new TabbedGroup("{id}.inheritance", "Inheritance")
+               var group = new PanelGroup("list.group", "List")
                var parents = self.parents.to_a
                v.name_sorter.sort(parents)
-               group.add_child new HierarchyListArticle(mentity, "Parents", parents)
+               group.add_child new MEntitiesListArticle("{id}.parents", "Parents", parents)
                var ancestors = self.ancestors.to_a
                v.name_sorter.sort(ancestors)
-               group.add_child new HierarchyListArticle(mentity, "Ancestors", ancestors)
+               group.add_child new MEntitiesListArticle("{id}.ancestors", "Ancestors", ancestors)
                var children = self.children.to_a
                v.name_sorter.sort(children)
-               group.add_child new HierarchyListArticle(mentity, "Children", children)
+               group.add_child new MEntitiesListArticle("{id}.children", "Children", children)
                var descendants = self.descendants.to_a
                v.name_sorter.sort(descendants)
-               group.add_child new HierarchyListArticle(mentity, "Descendants", descendants)
+               group.add_child new MEntitiesListArticle("{id}.descendants", "Descendants", descendants)
                section.add_child group
                section.parent = root.children.first
                root.children.first.children.insert(section, 1)
        end
 end
-
-# FIXME diff hack
-class ImportationListSection
-       super TabbedGroup
-       super MEntityComposite
-end
-
-# FIXME diff hack
-class InheritanceListSection
-       super TabbedGroup
-       super MEntityComposite
-end
-
-# Dislay a hierarchical list of mentities.
-class HierarchyListArticle
-       super MEntityArticle
-
-       # Title displayed in the top of this list.
-       var list_title: String
-
-       # MEntities to display in this list.
-       var mentities: Array[MEntity]
-end
index 0c843bb..83a2cc9 100644 (file)
@@ -372,7 +372,7 @@ redef class MClassPage
                if not mprop_is_local(mprop) then
                        classes.add "inherit"
                        var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url
-                       var def_url = "{cls_url}#article:{mprop.nitdoc_id}.definition"
+                       var def_url = "{cls_url}#{mprop.nitdoc_id}.definition"
                        var lnk = new Link(def_url, mprop.html_name)
                        var mdoc = mprop.intro.mdoc_or_fallback
                        if mdoc != null then lnk.title = mdoc.short_comment
@@ -388,7 +388,7 @@ redef class MClassPage
                end
                var def = select_mpropdef(mprop)
                var anc = def.html_link_to_anchor
-               anc.href = "#article:{def.nitdoc_id}.definition"
+               anc.href = "#{def.nitdoc_id}.definition"
                var lnk = new Template
                lnk.add new DocHTMLLabel.with_classes(classes)
                lnk.add anc
@@ -471,7 +471,7 @@ redef class MEntitySection
                        title.add mentity.html_signature
                        html_title = title
                        html_subtitle = mentity.html_namespace
-                       toc_title = mentity.html_name
+                       html_toc_title = mentity.html_name
                end
                super
        end
@@ -484,16 +484,16 @@ redef class ConcernSection
                var mentity = self.mentity
                if page isa MGroupPage then
                        html_title = null
-                       toc_title = mentity.html_name
+                       html_toc_title = mentity.html_name
                        is_toc_hidden = false
                else if page.mentity isa MModule and mentity isa MModule then
                        var title = new Template
                        if mentity == page.mentity then
                                title.add "in "
-                               toc_title = "in {mentity.html_name}"
+                               html_toc_title = "in {mentity.html_name}"
                        else
                                title.add "from "
-                               toc_title = "from {mentity.html_name}"
+                               html_toc_title = "from {mentity.html_name}"
                        end
                        title.add mentity.html_namespace
                        html_title = title
@@ -503,7 +503,7 @@ redef class ConcernSection
                        title.add "in "
                        title.add mentity.html_namespace
                        html_title = title
-                       toc_title = "in {mentity.html_name}"
+                       html_toc_title = "in {mentity.html_name}"
                end
                super
        end
@@ -532,7 +532,7 @@ redef class DefinitionArticle
                        title.add mentity.html_icon
                        title.add mentity.html_namespace
                        html_title = title
-                       toc_title = mentity.html_name
+                       html_toc_title = mentity.html_name
                        if mentity isa MModule then
                                html_source_link = v.html_source_link(mentity.location)
                        end
@@ -542,7 +542,7 @@ redef class DefinitionArticle
                        title.add mentity.mmodule.html_namespace
                        html_title = mentity.html_declaration
                        html_subtitle = title
-                       toc_title = "in {mentity.html_name}"
+                       html_toc_title = "in {mentity.html_name}"
                        html_source_link = v.html_source_link(mentity.location)
                        if page isa MEntityPage and mentity.is_intro and mentity.mmodule != page.mentity then
                                is_short_comment = true
@@ -555,13 +555,13 @@ redef class DefinitionArticle
                                title.add mentity.html_declaration
                                html_title = title
                                html_subtitle = mentity.html_namespace
-                               toc_title = mentity.html_name
+                               html_toc_title = mentity.html_name
                        else
                                var title = new Template
                                title.add "in "
                                title.add mentity.mclassdef.html_link
                                html_title = title
-                               toc_title = "in {mentity.mclassdef.html_name}"
+                               html_toc_title = "in {mentity.mclassdef.html_name}"
                        end
                        html_source_link = v.html_source_link(mentity.location)
                end
@@ -576,7 +576,7 @@ redef class HomeArticle
        redef fun init_html_render(v, doc, page) do
                if v.ctx.opt_custom_title.value != null then
                        self.html_title = v.ctx.opt_custom_title.value.to_s
-                       self.toc_title = v.ctx.opt_custom_title.value.to_s
+                       self.html_toc_title = v.ctx.opt_custom_title.value.to_s
                end
                self.content = v.ctx.opt_custom_intro.value
                super
@@ -586,7 +586,7 @@ end
 redef class GraphArticle
        redef fun init_html_render(v, doc, page) do
                var output_dir = v.ctx.output_dir
-               var path = output_dir / id
+               var path = output_dir / graph_id
                var path_sh = path.escape_to_sh
                var file = new FileWriter.open("{path}.dot")
                file.write(dot)
index 72df73c..1e74138 100644 (file)
@@ -43,7 +43,7 @@ class IndexingPhase
                                if not doc.mpropdefs.has(mpropdef) then continue
                                var full_name = mpropdef.mclassdef.mclass.full_name
                                var cls_url = mpropdef.mclassdef.mclass.nitdoc_url
-                               var def_url = "{cls_url}#article:{mpropdef.nitdoc_id}.definition"
+                               var def_url = "{cls_url}#{mpropdef.nitdoc_id}.definition"
                                add_result_for(mproperty.name, full_name, def_url)
                        end
                end
index 5ed6f78..ae341f9 100644 (file)
@@ -54,52 +54,33 @@ redef class DefinitionArticle
 
        # TODO this should move to MEntity?
        private fun build_mmodule_list(v: IntroRedefListPhase, doc: DocModel, mmodule: MModule) do
-               var section = new IntrosRedefsSection(mentity)
-               var group = new PanelGroup("List")
+               var section = new TabbedGroup("{mentity.nitdoc_id}.intros_redefs")
+               section.toc_title = "Intros / Redefs"
+               var group = new PanelGroup("list.group", "List")
                var intros = mmodule.intro_mclassdefs(v.ctx.min_visibility).to_a
                doc.mainmodule.linearize_mclassdefs(intros)
-               group.add_child new IntrosRedefsListArticle(mentity, "Introduces", intros)
+               group.add_child new MEntitiesListArticle("{mentity.nitdoc_id}.intros", "Introduces", intros)
                var redefs = mmodule.redef_mclassdefs(v.ctx.min_visibility).to_a
                doc.mainmodule.linearize_mclassdefs(redefs)
-               group.add_child new IntrosRedefsListArticle(mentity, "Redefines", redefs)
+               group.add_child new MEntitiesListArticle("{mentity.nitdoc_id}.redefs", "Redefines", redefs)
                section.add_child group
                add_child(section)
        end
 
        # TODO this should move to MEntity?
        private fun build_mclassdef_list(v: IntroRedefListPhase, doc: DocModel, mclassdef: MClassDef) do
-               var section = new IntrosRedefsSection(mentity)
-               var group = new PanelGroup("List")
+               var section = new TabbedGroup("{mentity.nitdoc_id}.intros_redefs")
+               section.toc_title = "Intros / Redefs"
+               var group = new PanelGroup("list.group", "List")
                var intros = mclassdef.collect_intro_mpropdefs(v.ctx.min_visibility).to_a
                # FIXME avoid diff changes
                # v.ctx.mainmodule.linearize_mpropdefs(intros)
-               group.add_child new IntrosRedefsListArticle(mentity, "Introduces", intros)
+               group.add_child new MEntitiesListArticle("{mentity.nitdoc_id}.intros", "Introduces", intros)
                var redefs = mclassdef.collect_redef_mpropdefs(v.ctx.min_visibility).to_a
                # FIXME avoid diff changes
                # v.ctx.mainmodule.linearize_mpropdefs(redefs)
-               group.add_child new IntrosRedefsListArticle(mentity, "Redefines", redefs)
+               group.add_child new MEntitiesListArticle("{mentity.nitdoc_id}.redefs", "Redefines", redefs)
                section.add_child group
                add_child(section)
        end
-
-end
-
-# Section that contains the intros and redefs lists.
-class IntrosRedefsSection
-       super TabbedGroup
-       super MEntitySection
-end
-
-# An article that displays a list of introduced / refined mentities.
-#
-# FIXME diff hack
-# This can merged with InheritanceListArticle in a more generic class.
-class IntrosRedefsListArticle
-       super MEntityArticle
-
-       # Title displayed as header of the list.
-       var list_title: String
-
-       # Intro mentities to list.
-       var mentities: Array[MEntity]
 end
index c36756c..7864b27 100644 (file)
@@ -73,7 +73,7 @@ redef class DefinitionArticle
                var lin = all_defs.to_a
                doc.mainmodule.linearize_mpropdefs(lin)
                if lin.length > 1 then
-                       add_child new DefinitionLinArticle(mentity, lin)
+                       add_child new DefinitionLinArticle("{mentity.nitdoc_id}.lin", "Linearization", lin)
                end
        end
 
@@ -93,8 +93,11 @@ end
 
 # Display a linearized list of definitions.
 class DefinitionLinArticle
-       super MEntityArticle
+       super DocArticle
 
        # The linearized list to display.
        var mentities: Array[MEntity]
+
+       redef fun is_hidden do return mentities.is_empty
+       redef var is_toc_hidden = true
 end
index 0c610d4..556733e 100644 (file)
@@ -46,7 +46,7 @@ redef class MModulePage
        # Imported modules that should appear in the documentation.
        var imports = new HashSet[MModule]
 
-       # Clients modules that shjould appear in the documentation.
+       # Clients modules that should appear in the documentation.
        var clients = new HashSet[MModule]
 
        redef fun build_poset(v, doc) do
index 7491e4b..f9824d3 100644 (file)
@@ -47,15 +47,15 @@ end
 
 redef class OverviewPage
        redef fun apply_structure(v, doc) do
-               var article = new HomeArticle
+               var article = new HomeArticle("home.article", "Home")
                root.add_child article
                # Projects list
                var mprojects = doc.model.mprojects.to_a
                var sorter = new MConcernRankSorter
                sorter.sort mprojects
-               var section = new ProjectsSection
+               var section = new DocSection("projects.section", "Projects")
                for mproject in mprojects do
-                       section.add_child new DefinitionArticle(mproject)
+                       section.add_child new DefinitionArticle("{mproject.nitdoc_id}.definition", mproject)
                end
                article.add_child section
        end
@@ -69,18 +69,18 @@ redef class SearchPage
                v.name_sorter.sort(mclasses)
                var mprops = doc.mproperties.to_a
                v.name_sorter.sort(mprops)
-               root.add_child new IndexArticle(mmodules, mclasses, mprops)
+               root.add_child new IndexArticle("index.article", mmodules, mclasses, mprops)
        end
 end
 
 redef class MGroupPage
        redef fun apply_structure(v, doc) do
-               var section = new MEntitySection(mentity)
+               var section = new MEntitySection("{mentity.nitdoc_name}.section", mentity)
                root.add_child section
                if mentity.is_root then
-                       section.add_child new IntroArticle(mentity.mproject)
+                       section.add_child new IntroArticle("{mentity.mproject.nitdoc_id}.intro", mentity.mproject)
                else
-                       section.add_child new IntroArticle(mentity)
+                       section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", mentity)
                end
                var concerns = self.concerns
                if concerns == null or concerns.is_empty then return
@@ -90,11 +90,11 @@ redef class MGroupPage
                concerns.sort_with(v.concerns_sorter)
                mentity.mproject.booster_rank = 0
                mentity.booster_rank = 0
-               section.add_child new ConcernsArticle(mentity, concerns)
+               section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", mentity, concerns)
                for mentity in concerns do
-                       var ssection = new ConcernSection(mentity)
+                       var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", mentity)
                        if mentity isa MModule then
-                               ssection.add_child new DefinitionArticle(mentity)
+                               ssection.add_child new DefinitionArticle("{mentity.nitdoc_id}.definition", mentity)
                        end
                        section.add_child ssection
                end
@@ -103,9 +103,9 @@ end
 
 redef class MModulePage
        redef fun apply_structure(v, doc) do
-               var section = new MEntitySection(mentity)
+               var section = new MEntitySection("{mentity.nitdoc_name}.section", mentity)
                root.add_child section
-               section.add_child new IntroArticle(mentity)
+               section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", mentity)
                var concerns = self.concerns
                if concerns == null or concerns.is_empty then return
                # FIXME avoid diff
@@ -116,22 +116,25 @@ redef class MModulePage
                mentity.mgroup.mproject.booster_rank = 0
                mentity.mgroup.booster_rank = 0
                mentity.booster_rank = 0
-               section.add_child new ConcernsArticle(mentity, concerns)
+               section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", mentity, concerns)
                # reference list
                for mentity in concerns do
-                       var ssection = new ConcernSection(mentity)
+                       var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", mentity)
                        if mentity isa MModule then
                                var mclasses = mclasses_for_mmodule(mentity).to_a
                                v.name_sorter.sort(mclasses)
                                for mclass in mclasses do
-                                       var article = new DefinitionListArticle(mclass)
+                                       var article = new DefinitionListArticle(
+                                               "{mclass.intro.nitdoc_id}.definition-list", mclass)
                                        var mclassdefs = mclassdefs_for(mclass).to_a
                                        if not mclassdefs.has(mclass.intro) then
-                                               article.add_child(new DefinitionArticle(mclass.intro))
+                                               article.add_child(new DefinitionArticle(
+                                                       "{mclass.intro.nitdoc_id}.definition", mclass.intro))
                                        end
                                        doc.mainmodule.linearize_mclassdefs(mclassdefs)
                                        for mclassdef in mclassdefs do
-                                               article.add_child(new DefinitionArticle(mclassdef))
+                                               article.add_child(new DefinitionArticle(
+                                                       "{mclassdef.nitdoc_id}.definition", mclassdef))
                                        end
                                        ssection.add_child article
                                end
@@ -165,9 +168,9 @@ end
 
 redef class MClassPage
        redef fun apply_structure(v, doc) do
-               var section = new MEntitySection(mentity)
+               var section = new MEntitySection("{mentity.nitdoc_name}.section", mentity)
                root.add_child section
-               section.add_child new IntroArticle(mentity)
+               section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", mentity)
                var concerns = self.concerns
                if concerns == null or concerns.is_empty then return
                # FIXME diff hack
@@ -178,15 +181,15 @@ redef class MClassPage
                mentity.intro_mmodule.mgroup.mproject.booster_rank = 0
                mentity.intro_mmodule.mgroup.booster_rank = 0
                mentity.intro_mmodule.booster_rank = 0
-               var constructors = new ConstructorsSection(mentity)
+               var constructors = new DocSection("{mentity.nitdoc_id}.constructors", "Constructors")
                var minit = mentity.root_init
                if minit != null then
-                       constructors.add_child new DefinitionArticle(minit)
+                       constructors.add_child new DefinitionArticle("{minit.nitdoc_id}.definition", minit)
                end
                section.add_child constructors
-               section.add_child new ConcernsArticle(mentity, concerns)
+               section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", mentity, concerns)
                for mentity in concerns do
-                       var ssection = new ConcernSection(mentity)
+                       var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", mentity)
                        if mentity isa MModule then
                                var mprops = mproperties_for(mentity)
                                var by_kind = new PropertiesByKind.with_elements(mprops)
@@ -196,9 +199,11 @@ redef class MClassPage
                                                for mpropdef in mpropdefs_for(mprop, mentity) do
                                                        if mpropdef isa MMethodDef and mpropdef.mproperty.is_init then
                                                                if mpropdef == minit then continue
-                                                               constructors.add_child new DefinitionArticle(mpropdef)
+                                                               constructors.add_child new DefinitionArticle(
+                                                                       "{mpropdef.nitdoc_id}.definition", mpropdef)
                                                        else
-                                                               ssection.add_child new DefinitionArticle(mpropdef)
+                                                               ssection.add_child new DefinitionArticle(
+                                                                       "{mpropdef.nitdoc_id}.definition", mpropdef)
                                                        end
                                                end
                                        end
@@ -238,9 +243,9 @@ end
 
 redef class MPropertyPage
        redef fun apply_structure(v, doc) do
-               var section = new MEntitySection(mentity)
+               var section = new MEntitySection("{mentity.nitdoc_name}.section", mentity)
                root.add_child section
-               section.add_child new IntroArticle(mentity)
+               section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", mentity)
                var concerns = self.concerns
                if concerns == null or concerns.is_empty then return
                # FIXME diff hack
@@ -251,15 +256,16 @@ redef class MPropertyPage
                mentity.intro.mclassdef.mmodule.mgroup.mproject.booster_rank = 0
                mentity.intro.mclassdef.mmodule.mgroup.booster_rank = 0
                mentity.intro.mclassdef.mmodule.booster_rank = 0
-               section.add_child new ConcernsArticle(mentity, concerns)
+               section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", mentity, concerns)
                for mentity in concerns do
-                       var ssection = new ConcernSection(mentity)
+                       var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", mentity)
                        if mentity isa MModule then
                                # Add mproperties
                                var mpropdefs = mpropdefs_for(mentity).to_a
                                v.name_sorter.sort(mpropdefs)
                                for mpropdef in mpropdefs do
-                                       ssection.add_child new DefinitionArticle(mpropdef)
+                                       ssection.add_child new DefinitionArticle(
+                                               "{mpropdef.nitdoc_id}.definition", mpropdef)
                                end
                        end
                        section.add_child ssection
@@ -288,30 +294,26 @@ end
 # A group of sections that can be displayed together in a tab panel.
 class PanelGroup
        super DocSection
-
-       # The title of this group.
-       var group_title: String
 end
 
 # A DocComposite element about a MEntity.
 class MEntityComposite
        super DocComposite
 
+       redef fun title do return mentity.nitdoc_name
+
        # MEntity documented by this page element.
        var mentity: MEntity
 end
 
-# A list of constructors.
-class ConstructorsSection
-       super MEntitySection
-end
-
 # A Section about a Concern.
 #
 # Those sections are used to build the page summary.
 class ConcernSection
        super MEntityComposite
        super DocSection
+
+       redef fun is_toc_hidden do return is_hidden
 end
 
 # An article about a Mentity.
@@ -322,6 +324,17 @@ abstract class MEntityArticle
        super DocArticle
 end
 
+# An article that displays a list of mentities.
+class MEntitiesListArticle
+       super DocArticle
+
+       # MEntities to display.
+       var mentities: Array[MEntity]
+
+       redef fun is_hidden do return mentities.is_empty
+end
+
+
 # A section about a Mentity.
 #
 # Used to regroup content about a MEntity.
@@ -336,6 +349,9 @@ end
 class IntroArticle
        super MEntityComposite
        super DocArticle
+
+       redef var is_hidden = false
+       redef var is_toc_hidden = true
 end
 
 # An article that display a ConcernsTreee as a list.
@@ -344,9 +360,11 @@ class ConcernsArticle
 
        # Concerns to list in this article.
        var concerns: ConcernsTree
+
+       redef fun is_hidden do return concerns.is_empty
 end
 
-# An article that displaus a list of definition belonging to a MEntity.
+# An article that displays a list of definition belonging to a MEntity.
 class DefinitionListArticle
        super TabbedGroup
        super MEntityArticle
@@ -355,6 +373,8 @@ end
 # An article that display the definition text of a MEntity.
 class DefinitionArticle
        super MEntityArticle
+
+       redef var is_hidden = false
 end
 
 # The main project article.
@@ -362,11 +382,6 @@ class HomeArticle
        super DocArticle
 end
 
-# The project list.
-class ProjectsSection
-       super DocArticle
-end
-
 # An article that display an index of mmodules, mclasses and mproperties.
 class IndexArticle
        super DocArticle
@@ -379,4 +394,8 @@ class IndexArticle
 
        # List of mproperties to display.
        var mprops: Array[MProperty]
+
+       redef fun is_hidden do
+               return mmodules.is_empty and mclasses.is_empty and mprops.is_empty
+       end
 end
index bc8ccbe..46953d9 100644 (file)
@@ -686,7 +686,7 @@ redef class MConcern
        private fun html_concern_item: ListItem do
                var lnk = html_link
                var tpl = new Template
-               tpl.add new Link.with_title("#concern:{nitdoc_id}", lnk.text, lnk.title)
+               tpl.add new Link.with_title("#{nitdoc_id}.concern", lnk.text, lnk.title)
                var comment = html_short_comment
                if comment != null then
                        tpl.add ": "
index ed680f0..8849fb3 100644 (file)
@@ -247,12 +247,12 @@ redef class DocComposite
        super Template
 
        # HTML anchor id
-       var html_id: String is noinit, writable
+       var html_id: String is writable, lazy do return id
 
        # Title to display if any.
        #
        # This title can be decorated with HTML.
-       var html_title: nullable Writable is noinit, writable
+       var html_title: nullable Writable is writable, lazy do return title
 
        # Subtitle to display if any.
        var html_subtitle: nullable Writable is noinit, writable
@@ -286,26 +286,20 @@ redef class DocComposite
        # Level <hX> for HTML heading.
        private fun hlvl: Int do return depth
 
-       # Is `self` not displayed in the page.
-       #
-       # By default, empty elements are hidden.
-       fun is_hidden: Bool do return is_empty
-
        # A short, undecorated title that goes in the table of contents.
        #
        # By default, returns `html_title.to_s`, subclasses should redefine it.
-       var toc_title: String is lazy, writable do return html_title.to_s
-
-       # Is `self` hidden in the table of content?
-       var is_toc_hidden = false is writable
+       var html_toc_title: nullable String is lazy, writable do
+               if html_title == null then return toc_title
+               return html_title.write_to_string
+       end
 
        # Render this element in a table of contents.
        private fun render_toc_item(lst: UnorderedList) do
-               if is_toc_hidden then return
+               if is_toc_hidden or html_toc_title == null then return
 
                var content = new Template
-               content.add new Link("#{html_id}", toc_title)
-
+               content.add new Link("#{html_id}", html_toc_title.to_s)
                if not children.is_empty then
                        var sublst = new UnorderedList
                        sublst.css_classes.add "nav"
@@ -327,6 +321,12 @@ redef class DocComposite
        end
 end
 
+redef class DocRoot
+       redef fun rendering do
+               for child in children do addn child.write_to_string
+       end
+end
+
 redef class DocSection
        super BSComponent
 
@@ -359,21 +359,20 @@ redef class TabbedGroup
                var tabs = new DocTabs("{html_id}.tabs", "")
                for child in children do
                        if child.is_hidden then continue
-                       tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
+                       var title = child.html_toc_title or else child.toc_title or else ""
+                       tabs.add_panel new DocTabPanel(child.html_tab_id, title, child)
                end
                addn tabs
        end
 end
 
 redef class PanelGroup
-       redef var html_id is lazy do return "group:{group_title.to_lower.to_snake_case}"
        redef var html_title = null
-       redef var toc_title is lazy do return group_title
+       redef var toc_title is lazy do return title or else ""
        redef var is_toc_hidden = true
 end
 
 redef class HomeArticle
-       redef var html_id = "article:home"
        redef var html_title = "Overview"
 
        # HTML content to display on the home page.
@@ -389,11 +388,7 @@ redef class HomeArticle
 end
 
 redef class IndexArticle
-       redef var html_id = "article:index"
        redef var html_title = "Index"
-       redef fun is_empty do
-               return mmodules.is_empty and mclasses.is_empty and mprops.is_empty
-       end
 
        redef fun render_body do
                addn "<div class='container-fluid'>"
@@ -428,50 +423,21 @@ redef class IndexArticle
        end
 end
 
-redef class ProjectsSection
-       redef var html_id = "section:projects"
-       redef var html_title = "Projects"
-end
-
 redef class MEntityComposite
-       redef var html_id is lazy do return mentity.nitdoc_id
        redef var html_title is lazy do return mentity.nitdoc_name
 end
 
 redef class MEntitySection
-       redef var html_id is lazy do return "section:{mentity.nitdoc_name}"
        redef var html_title is lazy do return mentity.html_name
        redef var html_subtitle is lazy do return mentity.html_declaration
 end
 
-redef class ConstructorsSection
-       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.constructors"
-       redef var html_title = "Constructors"
-       redef var html_subtitle = null
-       redef fun is_toc_hidden do return is_empty
-end
-
 redef class ConcernSection
-       redef var html_id is lazy do return "concern:{mentity.nitdoc_id}"
        redef var html_title is lazy do return "in {mentity.nitdoc_name}"
-       redef fun is_toc_hidden do return is_empty
-end
-
-redef class ImportationListSection
-       redef var html_id is lazy do return "section:{mentity.nitdoc_id}.importation"
-       redef var html_title is lazy do return "Dependencies"
-end
-
-redef class InheritanceListSection
-       redef var html_id is lazy do return "section:{mentity.nitdoc_id}.inheritance"
-       redef var html_title is lazy do return "Inheritance"
 end
 
 redef class IntroArticle
-       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.intro"
        redef var html_title = null
-       redef var is_hidden = false
-       redef var is_toc_hidden = true
 
        # Link to source to display if any.
        var html_source_link: nullable Writable is noinit, writable
@@ -484,7 +450,8 @@ redef class IntroArticle
                end
                for child in children do
                        if child.is_hidden then continue
-                       tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
+                       var title = child.html_toc_title or else child.toc_title or else ""
+                       tabs.add_panel new DocTabPanel(child.html_tab_id, title, child)
                end
                var lnk = html_source_link
                if lnk != null then
@@ -495,15 +462,11 @@ redef class IntroArticle
 end
 
 redef class ConcernsArticle
-       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.concerns"
        redef var html_title = "Concerns"
-       redef fun is_hidden do return concerns.is_empty
        redef fun render_body do add concerns.html_list
 end
 
 redef class DefinitionListArticle
-       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.definition-list"
-
        redef var html_title is lazy do
                var title = new Template
                title.add mentity.html_icon
@@ -512,14 +475,12 @@ redef class DefinitionListArticle
        end
 
        redef var html_subtitle is lazy do return mentity.html_namespace
-       redef var toc_title is lazy do return mentity.html_name
+       redef var html_toc_title is lazy do return mentity.html_name
 end
 
 redef class DefinitionArticle
-       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.definition"
        redef var html_title is lazy do return mentity.html_name
        redef var html_subtitle is lazy do return mentity.html_declaration
-       redef var is_hidden = false
 
        # Does `self` display only it's title and no body?
        #
@@ -549,7 +510,8 @@ redef class DefinitionArticle
                end
                for child in children do
                        if child.is_hidden then continue
-                       tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
+                       var title = child.html_toc_title or else child.toc_title or else ""
+                       tabs.add_panel new DocTabPanel(child.html_tab_id, title, child)
                end
                var lnk = html_source_link
                if lnk != null then
@@ -559,52 +521,18 @@ redef class DefinitionArticle
        end
 end
 
-redef class HierarchyListArticle
-       redef var html_id is lazy do return "article:{list_title}_{mentity.nitdoc_id}.hierarchy"
-       redef var html_title is lazy do return list_title
-       redef fun is_empty do return mentities.is_empty
-       redef var is_toc_hidden = true
-
+redef class MEntitiesListArticle
        redef fun render_body do
                var lst = new UnorderedList
                lst.css_classes.add "list-unstyled list-definition"
                for mentity in mentities do
                        lst.add_li mentity.html_list_item
                end
-               addn lst
-       end
-end
-
-redef class IntrosRedefsSection
-       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.intros_redefs"
-       redef var toc_title do return "Intros / Redefs"
-       redef var html_title = null
-       redef var html_subtitle = null
-       redef var is_toc_hidden = true
-end
-
-redef class IntrosRedefsListArticle
-       redef var html_id is lazy do return "article:{list_title}_{mentity.nitdoc_id}.intros_redefs"
-       redef var html_title is lazy do return list_title
-       redef fun is_hidden do return mentities.is_empty
-       redef var is_toc_hidden = true
-
-       redef fun render_body do
-               var lst = new UnorderedList
-               lst.css_classes.add "list-unstyled list-labeled"
-               for mentity in mentities do
-                       lst.add_li mentity.html_list_item
-               end
                add lst
        end
 end
 
 redef class DefinitionLinArticle
-       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.lin"
-       redef var html_title is lazy do return "Linearization"
-       redef fun is_hidden do return mentities.is_empty
-       redef var is_toc_hidden = true
-
        redef fun render_body do
                var lst = new UnorderedList
                lst.css_classes.add "list-unstyled list-labeled"
@@ -626,11 +554,7 @@ redef class DefinitionLinArticle
 end
 
 redef class GraphArticle
-       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.graph"
        redef var html_title = null
-       redef var toc_title do return "Graph"
-       redef var is_hidden = false
-       redef var is_toc_hidden = true
 
        # HTML map used to display link.
        #
@@ -639,8 +563,8 @@ redef class GraphArticle
 
        redef fun render_body do
                addn "<div class=\"text-center\">"
-               addn " <img src='{id}.png' usemap='#{id}' style='margin:auto'"
-               addn "  alt='{graph_title}'/>"
+               addn " <img src='{graph_id}.png' usemap='#{graph_id}' style='margin:auto'"
+               addn "  alt='{title or else ""}'/>"
                add map
                addn "</div>"
        end