exupero's blog
RSSApps

Angle bisection in taxicab geometry

In the previous post we saw how angles in taxicab geometry vary in size depending on their direction. Given that behavior, how can one bisect angle?

In Euclidean geometry, an angle can be bisected with a compass and straightedge. If we do the same construction with taxicab geometry's circles, we can get something like this:

The smaller circles centered on the rays have an infinite number of points of intersection, rather than just two, and it's not clear where to draw the angle bisector. When the angle does allow ray-circles to have only two points of intersection, those two points max not align with the vertex of the angle, as in this example:

A line from the angle's vertex to the circle intersection closest to it clearly wouldn't produce an angle bisector, but would a line from the vertex to the far intersection? That's not so obvious.

The most reliable way I've found to get an angle bisector in taxicab geometry is to find the intersection of lines parallel to an angle's rays. Here are two parallel lines:

And here's a line from the vertex to their intersection, which is very plausibly a bisector:

As a check of whether this does produce a bisector, let's use parallel lines to find a triangle's incenter. Here's an arbitrary triangle and three interior parallel lines:

And here are the angle bisectors suggested by the intersections of the parallel lines:

The first indication that we're in luck is that all three bisectors intersect at the same point. A circle centered on that point and given the correct radius does appear to touch all three sides of the triangle, as an incircle should:

(let [n 20
      a [9 2]
      b [17 12]
      c [5 15]
      dp 3
      lab (map #(update % 0 - dp) [a b])
      lbc (map #(update % 1 - dp) [b c])
      lca (map #(update % 0 + dp) [c a])
      incenter (math/lines-intersection
                 [a (math/lines-intersection lab lca)]
                 [b (math/lines-intersection lab lbc)])]
  (grid n
    (-> {}
        (add-path [a b c a])
        (add-path (light (ray n [a (math/lines-intersection lab lca)])))
        (add-path (light (ray n [b (math/lines-intersection lab lbc)])))
        (add-path (light (ray n [c (math/lines-intersection lbc lca)])))
        (add-circle incenter (point-line-dist incenter [a b])))))
#error {
 :cause "Unable to resolve symbol: add-circle in this context"
 :via
 [{:type clojure.lang.Compiler$CompilerException
   :message "Syntax error compiling at (/Users/exupero/code/blog/src/blog/core.clj:191:9)."
   :data #:clojure.error{:phase :compile-syntax-check, :line 191, :column 9, :source "/Users/exupero/code/blog/src/blog/core.clj"}
   :at [clojure.lang.Compiler analyze "Compiler.java" 6825]}
  {:type java.lang.RuntimeException
   :message "Unable to resolve symbol: add-circle in this context"
   :at [clojure.lang.Util runtimeException "Util.java" 221]}]
 :trace
 [[clojure.lang.Util runtimeException "Util.java" 221]
  [clojure.lang.Compiler resolveIn "Compiler.java" 7431]
  [clojure.lang.Compiler resolve "Compiler.java" 7375]
  [clojure.lang.Compiler analyzeSymbol "Compiler.java" 7336]
  [clojure.lang.Compiler analyze "Compiler.java" 6785]
  [clojure.lang.Compiler analyze "Compiler.java" 6762]
  [clojure.lang.Compiler$InvokeExpr parse "Compiler.java" 3832]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7126]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7112]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler analyze "Compiler.java" 6762]
  [clojure.lang.Compiler$InvokeExpr parse "Compiler.java" 3900]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7126]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler analyze "Compiler.java" 6762]
  [clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6137]
  [clojure.lang.Compiler$LetExpr$Parser parse "Compiler.java" 6453]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7124]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler analyze "Compiler.java" 6762]
  [clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6137]
  [clojure.lang.Compiler$FnMethod parse "Compiler.java" 5479]
  [clojure.lang.Compiler$FnExpr parse "Compiler.java" 4041]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7122]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler eval "Compiler.java" 7191]
  [clojure.lang.Compiler eval "Compiler.java" 7149]
  [clojure.core$eval invokeStatic "core.clj" 3215]
  [clojure.core$eval invoke "core.clj" 3211]
  [blog.eval$eval_forms$iter__11691__11695$fn__11696$fn__11697$fn__11699 invoke "eval.clj" 31]
  [blog.eval$eval_forms$iter__11691__11695$fn__11696$fn__11697 invoke "eval.clj" 29]
  [blog.eval$eval_forms$iter__11691__11695$fn__11696 invoke "eval.clj" 25]
  [clojure.lang.LazySeq sval "LazySeq.java" 42]
  [clojure.lang.LazySeq seq "LazySeq.java" 51]
  [clojure.lang.RT seq "RT.java" 535]
  [clojure.core$seq__5467 invokeStatic "core.clj" 139]
  [clojure.core$dorun invokeStatic "core.clj" 3134]
  [clojure.core$doall invokeStatic "core.clj" 3149]
  [clojure.core$doall invoke "core.clj" 3149]
  [blog.eval$eval_forms invokeStatic "eval.clj" 24]
  [blog.eval$eval_forms invoke "eval.clj" 21]
  [blog.core$post__GT_html invokeStatic "NO_SOURCE_FILE" 39]
  [blog.core$post__GT_html invoke "NO_SOURCE_FILE" 37]
  [blog.core$build_post invokeStatic "NO_SOURCE_FILE" 56]
  [blog.core$build_post invoke "NO_SOURCE_FILE" 54]
  [blog.core$update_files$fn__17084 invoke "NO_SOURCE_FILE" 136]
  [blog.core$update_files invokeStatic "NO_SOURCE_FILE" 135]
  [blog.core$update_files invoke "NO_SOURCE_FILE" 110]
  [watchtower.core$changed_fn$fn__10389 invoke "core.clj" 63]
  [watchtower.core$watch invokeStatic "core.clj" 78]
  [watchtower.core$watch invoke "core.clj" 70]
  [blog.core$fn__76053$fn__76054 invoke "NO_SOURCE_FILE" 145]
  [clojure.core$binding_conveyor_fn$fn__5823 invoke "core.clj" 2047]
  [clojure.lang.AFn call "AFn.java" 18]
  [java.util.concurrent.FutureTask run "FutureTask.java" 317]
  [java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1144]
  [java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 642]
  [java.lang.Thread run "Thread.java" 1623]]}

This is hardly proof, however, especially given taxicab geometry's proclivity for edge cases. The triangle above is acute; let's try an obtuse triangle:

(let [n 20
      a [1 2]
      b [19 12]
      c [5 11]
      dp 3
      lab (map #(update % 1 + dp) [a b])
      lbc (map #(update % 1 - dp) [b c])
      lca (map #(update % 0 + dp) [c a])
      incenter (math/lines-intersection
                 [a (math/lines-intersection lab lca)]
                 [b (math/lines-intersection lab lbc)])]
  (grid n
    (-> {}
        (add-path [a b c a])
        (add-path (light (ray n [a (math/lines-intersection lab lca)])))
        (add-path (light (ray n [b (math/lines-intersection lab lbc)])))
        (add-path (light (ray n [c (math/lines-intersection lbc lca)])))
        (add-circle incenter (point-line-dist incenter [a b])))))
#error {
 :cause "Unable to resolve symbol: add-circle in this context"
 :via
 [{:type clojure.lang.Compiler$CompilerException
   :message "Syntax error compiling at (/Users/exupero/code/blog/src/blog/core.clj:215:9)."
   :data #:clojure.error{:phase :compile-syntax-check, :line 215, :column 9, :source "/Users/exupero/code/blog/src/blog/core.clj"}
   :at [clojure.lang.Compiler analyze "Compiler.java" 6825]}
  {:type java.lang.RuntimeException
   :message "Unable to resolve symbol: add-circle in this context"
   :at [clojure.lang.Util runtimeException "Util.java" 221]}]
 :trace
 [[clojure.lang.Util runtimeException "Util.java" 221]
  [clojure.lang.Compiler resolveIn "Compiler.java" 7431]
  [clojure.lang.Compiler resolve "Compiler.java" 7375]
  [clojure.lang.Compiler analyzeSymbol "Compiler.java" 7336]
  [clojure.lang.Compiler analyze "Compiler.java" 6785]
  [clojure.lang.Compiler analyze "Compiler.java" 6762]
  [clojure.lang.Compiler$InvokeExpr parse "Compiler.java" 3832]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7126]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7112]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler analyze "Compiler.java" 6762]
  [clojure.lang.Compiler$InvokeExpr parse "Compiler.java" 3900]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7126]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler analyze "Compiler.java" 6762]
  [clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6137]
  [clojure.lang.Compiler$LetExpr$Parser parse "Compiler.java" 6453]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7124]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler analyze "Compiler.java" 6762]
  [clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6137]
  [clojure.lang.Compiler$FnMethod parse "Compiler.java" 5479]
  [clojure.lang.Compiler$FnExpr parse "Compiler.java" 4041]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7122]
  [clojure.lang.Compiler analyze "Compiler.java" 6806]
  [clojure.lang.Compiler eval "Compiler.java" 7191]
  [clojure.lang.Compiler eval "Compiler.java" 7149]
  [clojure.core$eval invokeStatic "core.clj" 3215]
  [clojure.core$eval invoke "core.clj" 3211]
  [blog.eval$eval_forms$iter__11691__11695$fn__11696$fn__11697$fn__11699 invoke "eval.clj" 31]
  [blog.eval$eval_forms$iter__11691__11695$fn__11696$fn__11697 invoke "eval.clj" 29]
  [blog.eval$eval_forms$iter__11691__11695$fn__11696 invoke "eval.clj" 25]
  [clojure.lang.LazySeq sval "LazySeq.java" 42]
  [clojure.lang.LazySeq seq "LazySeq.java" 51]
  [clojure.lang.RT seq "RT.java" 535]
  [clojure.core$seq__5467 invokeStatic "core.clj" 139]
  [clojure.core$dorun invokeStatic "core.clj" 3134]
  [clojure.core$doall invokeStatic "core.clj" 3149]
  [clojure.core$doall invoke "core.clj" 3149]
  [blog.eval$eval_forms invokeStatic "eval.clj" 24]
  [blog.eval$eval_forms invoke "eval.clj" 21]
  [blog.core$post__GT_html invokeStatic "NO_SOURCE_FILE" 39]
  [blog.core$post__GT_html invoke "NO_SOURCE_FILE" 37]
  [blog.core$build_post invokeStatic "NO_SOURCE_FILE" 56]
  [blog.core$build_post invoke "NO_SOURCE_FILE" 54]
  [blog.core$update_files$fn__17084 invoke "NO_SOURCE_FILE" 136]
  [blog.core$update_files invokeStatic "NO_SOURCE_FILE" 135]
  [blog.core$update_files invoke "NO_SOURCE_FILE" 110]
  [watchtower.core$changed_fn$fn__10389 invoke "core.clj" 63]
  [watchtower.core$watch invokeStatic "core.clj" 78]
  [watchtower.core$watch invoke "core.clj" 70]
  [blog.core$fn__76053$fn__76054 invoke "NO_SOURCE_FILE" 145]
  [clojure.core$binding_conveyor_fn$fn__5823 invoke "core.clj" 2047]
  [clojure.lang.AFn call "AFn.java" 18]
  [java.util.concurrent.FutureTask run "FutureTask.java" 317]
  [java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1144]
  [java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 642]
  [java.lang.Thread run "Thread.java" 1623]]}

So far, so good. For my purposes, I don't need a proof, but if you have a counter-example or edge case to consider, let me know.