Index: applications/editors/josm/plugins/utilsplugin2/README
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/README	(revision 27617)
+++ applications/editors/josm/plugins/utilsplugin2/README	(revision 27618)
@@ -12,4 +12,6 @@
  * Selection actions (by akks)
 
+ * Curves - Draw Arc (by olejorgenb)
+
  * Paste Relations, Replace Geometry and other actions (by Zverik)
  
Index: applications/editors/josm/plugins/utilsplugin2/images_nodist/circlearc.svg
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/images_nodist/circlearc.svg	(revision 27618)
+++ applications/editors/josm/plugins/utilsplugin2/images_nodist/circlearc.svg	(revision 27618)
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg2440"
+   sodipodi:version="0.32"
+   inkscape:version="0.48.1 r9760"
+   width="24"
+   height="24"
+   version="1.0"
+   sodipodi:docname="downloadprimitive.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="C:\Documents and Settings\matthiasj\My Documents\Eclipse\core\images\downloadprimitive.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata2445">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs2443">
+    <pattern
+       inkscape:collect="always"
+       xlink:href="#pattern4727"
+       id="pattern4735" />
+    <pattern
+       inkscape:collect="always"
+       xlink:href="#pattern4717"
+       id="pattern4723"
+       patternTransform="translate(10.03125,-11.1875)" />
+    <pattern
+       inkscape:collect="always"
+       xlink:href="#pattern3993"
+       id="pattern4717" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect3952"
+       is_visible="true"
+       pattern="m -33.426866,8.8893424 1,0"
+       copytype="single_stretched"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0"
+       normal_offset="0"
+       tang_offset="0"
+       prop_units="false"
+       vertical_pattern="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect3943"
+       is_visible="true"
+       pattern="M 0,0 1,0"
+       copytype="single_stretched"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0"
+       normal_offset="0"
+       tang_offset="0"
+       prop_units="false"
+       vertical_pattern="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="bend_path"
+       id="path-effect3941"
+       is_visible="true"
+       bendpath="M -28.798531,0.34406405 -4.8487322,-3.0353554"
+       prop_scale="1.26"
+       scale_y_rel="false"
+       vertical="false"
+       bendpath-nodetypes="cc" />
+    <inkscape:path-effect
+       effect="bend_path"
+       id="path-effect3894"
+       is_visible="true"
+       bendpath="m 0.137581,4.57711 18,0"
+       prop_scale="1"
+       scale_y_rel="false"
+       vertical="false" />
+    <inkscape:path-effect
+       effect="bend_path"
+       id="path-effect3892"
+       is_visible="true"
+       bendpath="m 2.189281,8.0143339 3.0002469,0"
+       prop_scale="1"
+       scale_y_rel="false"
+       vertical="false" />
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective2447" />
+    <inkscape:path-effect
+       effect="bend_path"
+       id="path-effect3892-4"
+       is_visible="true"
+       bendpath="m 16.078366,6.1994661 3.000245,0"
+       prop_scale="1"
+       scale_y_rel="false"
+       vertical="false" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect3952-6"
+       is_visible="true"
+       pattern="m -26.904546,13.928628 1,0"
+       copytype="single_stretched"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0"
+       normal_offset="0"
+       tang_offset="0"
+       prop_units="false"
+       vertical_pattern="false"
+       fuse_tolerance="0" />
+    <inkscape:path-effect
+       effect="skeletal"
+       id="path-effect3952-69"
+       is_visible="true"
+       pattern="m -16.105098,11.357331 1,0"
+       copytype="single_stretched"
+       prop_scale="1"
+       scale_y_rel="false"
+       spacing="0"
+       normal_offset="0"
+       tang_offset="0"
+       prop_units="false"
+       vertical_pattern="false"
+       fuse_tolerance="0" />
+    <pattern
+       patternUnits="userSpaceOnUse"
+       width="18.5"
+       height="8.90625"
+       patternTransform="translate(10.03125,-11.1875)"
+       id="pattern3993">
+      <path
+         inkscape:original-d="M 0.872237,3.5650192 11.137581,0.57711 l 7,8"
+         inkscape:path-effect="#path-effect3894"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ccc"
+         id="path3604"
+         d="M 0.137581,3.5650192 10.839725,0.57711 l 7.297856,8"
+         style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+    </pattern>
+    <pattern
+       patternUnits="userSpaceOnUse"
+       width="18.5"
+       height="8.90625"
+       patternTransform="translate(10.03125,-11.1875)"
+       id="pattern4714">
+      <rect
+         id="rect3996"
+         y="0"
+         x="0"
+         height="8.90625"
+         width="18.5"
+         style="fill:url(#pattern4717);stroke:none" />
+    </pattern>
+    <pattern
+       patternUnits="userSpaceOnUse"
+       width="10.790812"
+       height="11.192538"
+       patternTransform="translate(2.3239415,10.848514)"
+       id="pattern4727">
+      <path
+         sodipodi:open="true"
+         sodipodi:end="7.5462234"
+         sodipodi:start="3.6303654"
+         d="m 2.3239415,13.889341 a 5.73032,5.73032 0 1 1 6.795201,8.151711"
+         sodipodi:ry="5.73032"
+         sodipodi:rx="5.73032"
+         sodipodi:cy="16.579971"
+         sodipodi:cx="7.383297"
+         id="path4725"
+         style="fill:#ff0000;fill-opacity:0.04299197"
+         sodipodi:type="arc"
+         transform="translate(-2.3239415,-10.848514)" />
+    </pattern>
+    <pattern
+       patternUnits="userSpaceOnUse"
+       width="10.790812"
+       height="11.192538"
+       patternTransform="translate(2.3239415,10.848514)"
+       id="pattern4732">
+      <rect
+         id="rect4730"
+         y="0"
+         x="0"
+         height="11.192538"
+         width="10.790812"
+         style="fill:url(#pattern4735);stroke:none" />
+    </pattern>
+  </defs>
+  <sodipodi:namedview
+     inkscape:window-height="861"
+     inkscape:window-width="1438"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     guidetolerance="10.0"
+     gridtolerance="10.0"
+     objecttolerance="10.0"
+     borderopacity="1.0"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     showgrid="false"
+     inkscape:zoom="13.611806"
+     inkscape:cx="0.036905255"
+     inkscape:cy="15.620616"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:current-layer="svg2440"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:window-maximized="0" />
+  <rect
+     style="stroke:none;fill:none"
+     width="18.5"
+     height="8.90625"
+     x="10.03125"
+     y="-11.1875"
+     id="rect4719" />
+  <path
+     style="fill:none;stroke:#000000;stroke-opacity:1"
+     d="m 3.4528847,7.6171621 6.8916363,-3.4140666 7.287233,2.4590131 3.414066,6.8916364 -2.459013,7.287233"
+     id="path4802"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccccc" />
+  <rect
+     style="fill:#ffffff;fill-opacity:1;stroke:#ff0000;stroke-width:1.00036621;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+     id="rect2826-54-4-6"
+     width="2.9996338"
+     height="2.9996338"
+     x="17.160456"
+     y="18.973833" />
+  <rect
+     style="fill:#ffffff;fill-opacity:1;stroke:#ff0000;stroke-width:1.00036621;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+     id="rect2826-54-4-8"
+     width="2.9996338"
+     height="2.9996338"
+     x="19.364424"
+     y="12.214993" />
+  <rect
+     style="fill:#ffffff;fill-opacity:1;stroke:#ff0000;stroke-width:1.00036621;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+     id="rect2826-54-4-0"
+     width="2.9996338"
+     height="2.9996338"
+     x="8.7853727"
+     y="2.9583228" />
+  <rect
+     style="fill:#ffffff;fill-opacity:1;stroke:#ff0000;stroke-width:1.00036621;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+     id="rect2826-54-4"
+     width="2.9996338"
+     height="2.9996338"
+     x="2.0999994"
+     y="6.04388" />
+  <rect
+     style="fill:#ffffff;fill-opacity:1;stroke:#ff0000;stroke-width:1.00036621;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+     id="rect2826-54-4-7"
+     width="2.9996338"
+     height="2.9996338"
+     x="16.058472"
+     y="5.382689" />
+</svg>
Index: applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/UtilsPlugin2.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/UtilsPlugin2.java	(revision 27617)
+++ applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/UtilsPlugin2.java	(revision 27618)
@@ -12,4 +12,5 @@
 import utilsplugin2.selection.*;
 import utilsplugin2.dumbutils.*;
+import utilsplugin2.curves.*;
 
 import org.openstreetmap.josm.Main;
@@ -51,4 +52,6 @@
     
     JMenuItem selectURL;
+
+    JMenuItem drawArc;
     
     public UtilsPlugin2(PluginInformation info) {
@@ -89,5 +92,5 @@
         
         selectURL = MainMenu.add(toolsMenu, new ChooseURLAction());
-        
+	drawArc = MainMenu.add(toolsMenu, new CurveAction());
 
     }
@@ -122,5 +125,8 @@
         selectURL.setEnabled(enabled);
         allInside.setEnabled(enabled);
+
+        drawArc.setEnabled(enabled);
     }
+    
     @Override
     public PreferenceSetting getPreferenceSetting() {
Index: applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/curves/CircleArcMaker.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/curves/CircleArcMaker.java	(revision 27618)
+++ applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/curves/CircleArcMaker.java	(revision 27618)
@@ -0,0 +1,347 @@
+// License: GPL. Copyright 2011 by Ole Jørgen Brønner
+package utilsplugin2.curves;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+
+public class CircleArcMaker {
+    public static Collection<Command> doCircleArc(List<Node> selectedNodes, List<Way> selectedWays, int angleSeparation) {
+        Collection<Command> cmds = new LinkedList<Command>();
+
+        //// Decides which nodes to use as anchors based on selection
+        /*
+         * Rules goes like this:
+         * If there are selected ways, at least one of these are used as target ways for the arc.
+         * Selected ways override selected nodes. If nodes not part of the ways are selected they're ignored.
+         *
+         * When existing ways are reused for the arc, all ways overlapping these are transformed too.
+         *
+         * 1. Exactly 3 nodes selected:
+         *      Use these nodes.
+         *      - No way selected: create a new way.
+         * 2. Exactly 1 node selected, node part of exactly 1 way with 3 or more nodes:
+         *      Node selected used as first node, consequent nodes in the way's direction used as the rest.
+         *      - Reversed if not enough nodes in forward direction
+         *      - Selected node used as middle node its the middle node in a 3 node way
+         *      - Parent way used
+         */
+
+        //// Anchor nodes
+        Node n1 = null, n2 = null, n3 = null;
+
+        if (false) {
+            int nodeCount = selectedNodes.size();
+            int wayCount = selectedWays.size();
+
+            // TODO: filter garbage nodes based on selected ways
+
+            // Never interested in more than 3 nodes. Nodes prioritized by reverse selection order, but keep their order.
+            // TODO: replace by helper function (eg. getPostFixList(int count))
+            Node[] nodesOfInterest = new Node[3];
+            int nodesOfInterestCount = Math.min(nodeCount, 3);
+            for (int i = nodesOfInterestCount - 1; i >= 0; i--) {
+                nodesOfInterest[i] = selectedNodes.get(nodeCount - 1 - i);
+            }
+        }
+
+        Set<Way> targetWays = new HashSet<Way>();
+
+        boolean nodesHasBeenChoosen = false;
+        if (selectedNodes.size() == 3) {
+            Iterator<Node> nodeIter = selectedNodes.iterator();
+            n1 = nodeIter.next();
+            n2 = nodeIter.next();
+            n3 = nodeIter.next();
+            nodesHasBeenChoosen = true;
+            if (selectedWays.isEmpty()) { // Create a brand new way
+                Way newWay = new Way();
+                targetWays.add(newWay);
+                cmds.add(new AddCommand(newWay));
+                newWay.addNode(n1);
+                newWay.addNode(n2);
+                newWay.addNode(n3);
+            }
+        }
+        if (selectedWays.isEmpty() == false) {
+            // TODO: use only two nodes inferring the orientation from the parent way.
+
+            if (nodesHasBeenChoosen == false) {
+                // Use the three last nodes in the way as anchors. This is intended to be used with the
+                // built in draw mode
+                Way w = selectedWays.iterator().next(); //TODO: select last selected way instead
+                int nodeCount = w.getNodesCount();
+                if (nodeCount < 3)
+                    return null;
+                n3 = w.getNode(nodeCount - 1);
+                n2 = w.getNode(nodeCount - 2);
+                n1 = w.getNode(nodeCount - 3);
+                nodesHasBeenChoosen = true;
+            }
+            targetWays.addAll(OsmPrimitive.getFilteredList(n1.getReferrers(), Way.class));
+            targetWays.addAll(OsmPrimitive.getFilteredList(n2.getReferrers(), Way.class));
+            targetWays.addAll(OsmPrimitive.getFilteredList(n3.getReferrers(), Way.class));
+            //            for(Way w : selectedWays) {
+            //                targetWays.add(w);
+            //            }
+
+        }
+        if (nodesHasBeenChoosen == false) {
+            return null;
+        }
+
+
+        EastNorth p1 = n1.getEastNorth();
+        EastNorth p2 = n2.getEastNorth();
+        EastNorth p3 = n3.getEastNorth();
+        // TODO: Check that the points are distinct
+
+        // // Calculate the new points in the arc
+        ReturnValue<Integer> p2Index = new ReturnValue<Integer>();
+        List<EastNorth> points = circleArcPoints(p1, p2, p3, angleSeparation, false, p2Index);
+
+        //// Create the new arc nodes. Insert anchor nodes at correct positions.
+        List<Node> arcNodes = new ArrayList<Node>(points.size());
+        arcNodes.add(n1);
+        {
+            int i = 1;
+            for (EastNorth p : slice(points, 1, -2)) {
+                //            if (p == p2) {
+                if (i == p2Index.value) {
+                    Node n2new = new Node(n2);
+                    n2new.setEastNorth(p);
+                    arcNodes.add(n2); // add the original n2, or else we can't find it in the target ways
+                    cmds.add(new ChangeCommand(n2, n2new));
+                } else {
+                    Node n = new Node(p);
+                    arcNodes.add(n);
+                    cmds.add(new AddCommand(n));
+                }
+                i++;
+            }
+        }
+        arcNodes.add(n3);
+
+        Node[] anchorNodes = { n1, n2, n3 };
+        //// "Fuse" the arc with all target ways
+        fuseArc(anchorNodes, arcNodes, targetWays, cmds);
+
+        return cmds;
+    }
+
+    private static void fuseArc(Node[] anchorNodes, List<Node> arcNodes, Set<Way> targetWays, Collection<Command> cmds) {
+
+        for (Way originalTw : targetWays) {
+            Way tw = new Way(originalTw);
+            boolean didChangeTw = false;
+            /// Do one segment at the time (so ways only sharing one segment is fused too)
+            for (int a = 0; a < 2; a++) {
+                int anchorBi = arcNodes.indexOf(anchorNodes[a]); // TODO: optimize away
+                int anchorEi = arcNodes.indexOf(anchorNodes[a + 1]);
+                /// Find the anchor node indices in current target way
+                int bi = -1, ei = -1;
+                int i = -1;
+                // Caution: nodes might appear multiple times. For now only handle simple closed ways
+                for (Node n : tw.getNodes()) {
+                    i++;
+                    // We look for the first anchor node. The next should be directly to the left or right.
+                    // Exception when the way is closed
+                    if (n == anchorNodes[a]) {
+                        bi = i;
+                        Node otherAnchor = anchorNodes[a + 1];
+                        if (i > 0 && tw.getNode(i - 1) == otherAnchor) {
+                            ei = i - 1;
+                        } else if (i < (tw.getNodesCount() - 1) && tw.getNode(i + 1) == otherAnchor) {
+                            ei = i + 1;
+                        } else {
+                            continue; // this can happen with closed ways. Continue searching for the correct index
+                        }
+                        break;
+                    }
+                }
+                if (bi == -1 || ei == -1) {
+                    continue; // this segment is not part of the target way
+                }
+                didChangeTw = true;
+
+                /// Insert the nodes of this segment
+                // Direction of target way relative to the arc node order
+                int twDirection = ei > bi ? 1 : 0;
+                int anchorI = anchorBi + 1; // don't insert the anchor nodes again
+                int twI = bi + (twDirection == 1 ? 1 : 0); // TODO: explain
+                while (anchorI < anchorEi) {
+                    tw.addNode(twI, arcNodes.get(anchorI));
+                    anchorI++;
+                    twI += twDirection;
+                }
+            }
+            if (didChangeTw)
+                cmds.add(new ChangeCommand(originalTw, tw));
+        }
+    }
+
+    /**
+     * Return a list of coordinates lying an the circle arc determined by n1, n2 and n3.
+     * The order of the list and which of the 3 possible arcs to construct are given by the order of n1, n2, n3
+     *
+     * @param includeAnchors include the anchorpoints in the list. The original objects will be used, not copies.
+     *                       If {@code false}, p2 will be replaced by the closest arcpoint.
+     * @param anchor2Index if non-null, it's value will be set to p2's index in the returned list.
+     * @param angleSparation maximum angle separation between the arc points
+     */
+    private static List<EastNorth> circleArcPoints(EastNorth p1, EastNorth p2, EastNorth p3,
+            int angleSeparation, boolean includeAnchors, ReturnValue<Integer> anchor2Index) {
+
+        // triangle: three single nodes needed or a way with three nodes
+
+        // let's get some shorter names
+        double x1 = p1.east();
+        double y1 = p1.north();
+        double x2 = p2.east();
+        double y2 = p2.north();
+        double x3 = p3.east();
+        double y3 = p3.north();
+
+        // calculate the center (xc,yc)
+        double s = 0.5 * ((x2 - x3) * (x1 - x3) - (y2 - y3) * (y3 - y1));
+        double sUnder = (x1 - x2) * (y3 - y1) - (y2 - y1) * (x1 - x3);
+
+        assert (sUnder == 0);
+
+        s /= sUnder;
+
+        double xc = 0.5 * (x1 + x2) + s * (y2 - y1);
+        double yc = 0.5 * (y1 + y2) + s * (x1 - x2);
+
+        // calculate the radius (r)
+        double r = Math.sqrt(Math.pow(xc - x1, 2) + Math.pow(yc - y1, 2));
+
+        // The angles of the anchor points relative to the center
+        double realA1 = calcang(xc, yc, x1, y1);
+        double realA2 = calcang(xc, yc, x2, y2);
+        double realA3 = calcang(xc, yc, x3, y3);
+
+        double startAngle = realA1;
+        // Transform the angles to get a consistent starting point
+        double a1 = 0;
+        double a2 = normalizeAngle(realA2 - startAngle);
+        double a3 = normalizeAngle(realA3 - startAngle);
+        int direction = a3 > a2 ? 1 : -1;
+
+        double radialLength = 0;
+        if (direction == 1) { // counter clockwise
+            radialLength = a3;
+        } else { // clockwise
+            radialLength = Math.PI * 2 - a3;
+            // make the angles consistent with the direction.
+            a2 = (Math.PI * 2 - a2);
+            a3 = (Math.PI * 2 - a3);
+        }
+        int numberOfNodesInArc = Math.max((int) Math.ceil((radialLength / Math.PI) * 180 / angleSeparation)+1,
+                3);
+        List<EastNorth> points = new ArrayList<EastNorth>(numberOfNodesInArc);
+
+        // Calculate the circle points in order
+        double stepLength = radialLength / (numberOfNodesInArc-1);
+        // Determine closest index to p2
+
+        int indexJustBeforeP2 = (int) Math.floor(a2 / stepLength);
+        int closestIndexToP2 = indexJustBeforeP2;
+        if ((a2 - indexJustBeforeP2 * stepLength) > ((indexJustBeforeP2 + 1) * stepLength - a2)) {
+            closestIndexToP2 = indexJustBeforeP2 + 1;
+        }
+        // can't merge with end node
+        if (closestIndexToP2 == numberOfNodesInArc - 1) {
+            closestIndexToP2--;
+        } else if (closestIndexToP2 == 0) {
+            closestIndexToP2++;
+        }
+        assert (closestIndexToP2 != 0);
+
+        double a = direction * (stepLength);
+        points.add(p1);
+        if (indexJustBeforeP2 == 0 && includeAnchors) {
+            points.add(p2);
+        }
+        // i is ahead of the real index by one, since we need to be ahead in the angle calculation
+        for (int i = 2; i < numberOfNodesInArc; i++) {
+            double nextA = direction * (i * stepLength);
+            double realAngle = a + startAngle;
+            double x = xc + r * Math.cos(realAngle);
+            double y = yc + r * Math.sin(realAngle);
+
+            points.add(new EastNorth(x, y));
+            if (i - 1 == indexJustBeforeP2 && includeAnchors) {
+                points.add(p2);
+            }
+            a = nextA;
+        }
+        points.add(p3);
+        if (anchor2Index != null) {
+            anchor2Index.value = closestIndexToP2;
+        }
+        return points;
+    }
+
+    // gah... why can't java support "reverse indices"?
+    private static <T> List<T> slice(List<T> list, int from, int to) {
+        if (to < 0)
+            to += list.size() + 1;
+        return list.subList(from, to);
+    }
+
+    /**
+     * Normalizes {@code a} so it is between 0 and 2 PI
+     */
+    private static double normalizeAngle(double angle) {
+        double PI2 = Math.PI * 2;
+        if (angle < 0) {
+            angle = angle + (Math.floor(-angle / PI2) + 1) * PI2;
+        } else if (angle >= PI2) {
+            angle = angle - Math.floor(angle / PI2) * PI2;
+        }
+        return angle;
+    }
+
+    private static double calcang(double xc, double yc, double x, double y) {
+        // calculate the angle from xc|yc to x|y
+        if (xc == x && yc == y)
+            return 0; // actually invalid, but we won't have this case in this context
+        double yd = Math.abs(y - yc);
+        if (yd == 0 && xc < x)
+            return 0;
+        if (yd == 0 && xc > x)
+            return Math.PI;
+        double xd = Math.abs(x - xc);
+        double a = Math.atan2(xd, yd);
+        if (y > yc) {
+            a = Math.PI - a;
+        }
+        if (x < xc) {
+            a = -a;
+        }
+        a = 1.5 * Math.PI + a;
+        if (a < 0) {
+            a += 2 * Math.PI;
+        }
+        if (a >= 2 * Math.PI) {
+            a -= 2 * Math.PI;
+        }
+        return a;
+    }
+    public static class ReturnValue<T> {
+        public T value;
+    }
+}
Index: applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/curves/CurveAction.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/curves/CurveAction.java	(revision 27618)
+++ applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/curves/CurveAction.java	(revision 27618)
@@ -0,0 +1,81 @@
+// License: GPL. Copyright 2011 by Ole Jørgen Brønner
+package utilsplugin2.curves;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.Shortcut;
+
+// TODO: investigate splines
+
+public class CurveAction extends JosmAction {
+
+    private static final long serialVersionUID = 1L;
+
+    private int angleSeparation = -1;
+
+    public CurveAction() {
+        super(tr("Circle arc"), "circlearc", tr("Create a circle arc"),
+                Shortcut.registerShortcut("tools:createcurve", tr("Tool: {0}", tr("Create a circle arc")), KeyEvent.VK_C,
+                        Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
+        putValue("help", ht("/Action/CreateCircleArc"));
+        updatePreferences();
+    }
+
+    private void updatePreferences() {
+        // @formatter:off
+        angleSeparation = Main.pref.getInteger(prefKey("circlearc.angle-separation"), 20);
+        // @formatter:on
+    }
+
+    private String prefKey(String subKey) {
+        return "curves." + subKey;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        if (!isEnabled())
+            return;
+
+        updatePreferences();
+
+        List<Node> selectedNodes = new ArrayList<Node>(getCurrentDataSet().getSelectedNodes());
+        List<Way> selectedWays = new ArrayList<Way>(getCurrentDataSet().getSelectedWays());
+
+        // Collection<Command> cmds = doSpline(selectedNodes, selectedWays);
+        Collection<Command> cmds = CircleArcMaker.doCircleArc(selectedNodes, selectedWays, angleSeparation);
+        if (cmds != null)
+            Main.main.undoRedo.add(new SequenceCommand("Create a curve", cmds));
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        if (getCurrentDataSet() == null) {
+            setEnabled(false);
+        } else {
+            updateEnabledState(getCurrentDataSet().getSelected());
+        }
+    }
+
+    @Override
+    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
+        setEnabled(selection != null && !selection.isEmpty());
+    }
+
+    public static void main(String[] args) {
+    }
+
+}
