Version 3 of Distance from a Point to a Plane (3D)

Updated 2019-09-19 02:28:20 by kch

I used to sleep a lot in math class... For this question Calculating Distance from a Point to a Plane I can answer... Maybe there’s a faster way to calculate...

namespace eval Vector3D {

    namespace export *

    proc sub_v3v3 {v0 v1} {

    lassign $v0 v0x v0y v0z
    lassign $v1 v1x v1y v1z

    return [list [expr {$v0x - $v1x}] \
                 [expr {$v0y - $v1y}] \
                 [expr {$v0z - $v1z}]]

    }

    proc dot_v3v3 {v0 v1} {

        lassign $v0 v0x v0y v0z
        lassign $v1 v1x v1y v1z

        return [expr {($v0x * $v1x) + ($v0y * $v1y) +  ($v0z * $v1z)}]

    }

    proc cross_v3v3 {v0 v1} {

        lassign $v0 vx0 vy0 vz0
        lassign $v1 vx1 vy1 vz1

        return [list [expr {($vy0 * $vz1) - ($vy1 * $vz0)}] \
                     [expr {($vz0 * $vx1) - ($vx0 * $vz1)}] \
                     [expr {($vx0 * $vy1) - ($vy0 * $vx1)}]]

    }

    proc norm {v} {

        lassign $v vx vy vz

        return [expr {sqrt($vx**2 + $vy**2 + $vz**2)}]

    }

    proc unit {v} {

        set n [norm $v]

        if {$n == 0} {
            error "Must be greatest than 0..."
        }

        lassign $v x y z

        return [list [expr {$x / double($n)}] \
                     [expr {$y / double($n)}] \
                     [expr {$z / double($n)}]]
        
    }


}
  • Utilisation :
namespace import Vector3D::*

set plan {{-10 -10 10} {10 -10 10} {0 10 10}} ; # 3 points on plan (3d)
set point {0 0 12} ; # point (3d)

lassign $plan v0 v1 v2

set u [sub_v3v3 $v1 $v0]
set v [sub_v3v3 $v2 $v0]
set normal [unit [cross_v3v3 $u $v]] ; # normal to plane

set sub [sub_v3v3 $point $v0]
set dist [dot_v3v3 $normal $sub]

puts "Distance 3D = $dist" 
# Distance 3D = 2.0

Here is my pure Tcl implementation of the task to compute the distance of a point to a plane in 3D. The procedures are optimized for maximum performance. I did not compare to above implementation.

## Get the distance of a point p to a plane given by origin point a and normal vector u.
proc DistPoint2Plane {p a u {abskey 1}} {
  # Normalize vector u to the length of one
  set u [VectorNormalize $u]
  # Get vector from a to p
  set ap [VectorsDifference $p $a]
  # Return the distance as scalar product of u and ap
  if {$abskey} {
    return [expr {abs([VectorsDotProduct $u $ap])}]
  } else {
    return [expr {[VectorsDotProduct $u $ap]}]
  }
}
## Normalize vector a to length 1
proc VectorNormalize {a} {
  set norm [VectorNorm $a]
  if {$norm!=0} {
    return [list [expr {[lindex $a 0]/double($norm)}] [expr {[lindex $a 1]/double($norm)}] [expr {[lindex $a 2]/double($norm)}]]
  } else {
    return $a
  }
}
## Compute the length of vector a
proc VectorNorm {a} {
  lassign $a a1 a2 a3
  return [expr {sqrt(pow($a1,2)+pow($a2,2)+pow($a3,2))}]
}
## compute the difference of vector a and b (a-b)
proc VectorsDifference {a b} {
  if {[lindex $a 3]=="" || [lindex $b 3]==""} {
    return [list [expr {[lindex $a 0]-[lindex $b 0]}] [expr {[lindex $a 1]-[lindex $b 1]}] [expr {[lindex $a 2]-[lindex $b 2]}]]
  } else {
    return [list [expr {[lindex $a 0]-[lindex $b 0]}] [expr {[lindex $a 1]-[lindex $b 1]}] [expr {[lindex $a 2]-[lindex $b 2]}] [expr {[lindex $a 3]-[lindex $b 3]}] [expr {[lindex $a 4]-[lindex $b 4]}] [expr {[lindex $a 5]-[lindex $b 5]}]]
  }
}
## Compute the dot product of vectors a and b
proc VectorsDotProduct {a b} {
  lassign $a a1 a2 a3
  lassign $b b1 b2 b3
  return [expr {$a1*$b1+$a2*$b2+$a3*$b3}]
}

Usage:

DistPoint2Plane {0 0 10} {0 0 0} {0 0 1}

kch - An alternate approach. For details see [explanation

#
# This code presumes all parameters are pre-validated.
#
proc defPoint {x y z} {
  return [list $x $y $z]
}; # defPoint {}

proc defVector {Pa Pb} {
  set dx [expr {[lindex $Pb 0] - [lindex $Pa 0]}]
  set dy [expr {[lindex $Pb 1] - [lindex $Pa 1]}]
  set dz [expr {[lindex $Pb 2] - [lindex $Pa 2]}]

  return [list $dx $dy $dz]
}; # defVector {}

proc crossProduct {Va Vb} {
  set A [expr { [lindex $Va 1] * [lindex $Vb 2]
              - [lindex $Va 2] * [lindex $Vb 1]
              }]
  set B [expr { [lindex $Va 2] * [lindex $Vb 0]
              - [lindex $Va 0] * [lindex $Vb 2]
              }]
  set C [expr { [lindex $Va 0] * [lindex $Vb 1]
              - [lindex $Va 1] * [lindex $Vb 0]
              }]
  return [list $A $B $C]
}; # crossProduct {}

proc vecLen {V} {
  set accum 0
  foreach element $V {
    incr accum [expr {$element * $element}]
  }
  return [expr {sqrt($accum)}]
}; # vecLen {}

proc normalize {V} {
  set len [vecLen $V]
  set normVec {}
  foreach element $V {
    lappend normVec [expr {$element / $len}]
  }
  return $normVec
}; # normalize {}

proc defPlane {NormVec P0} {
  set D 0
  foreach vElement $NormVec pElement $P0 {
    set D [expr {$D - $vElement * $pElement}]
  }
  return [list {*}$NormVec $D]
}; # defPlane {}

proc distToPlane {plane point} {
  set D [lindex $plane 3]
  foreach vElement [lrange $plane 0 2] pElement $point {
    set D [expr {$D + $vElement * $pElement}]
  }
  return $D
}; # distToPlane {}

Usage:

 % set P0 [defPoint 1 0 0]
1 0 0
 % set P1 [defPoint 0 1 0]
0 1 0
 % set P2 [defPoint 0 0 1]
0 0 1

 % set Va [defVector $P0 $P1]
-1 1 0
 % set Vb [defVector $P0 $P2]
-1 0 1

 % set NormalA [crossProduct $Va $Vb]
1 1 1

 % set nVecA [normalize $NormalA]
0.5773502691896258 0.5773502691896258 0.5773502691896258

 % set planeA [defPlane $nVecA $P0]
0.5773502691896258 0.5773502691896258 0.5773502691896258 -0.5773502691896258

 % distToPlane $PA $P0
0.0
 % distToPlane $PA {1 1 1}
1.1547005383792517
 % distToPlane $PA {0 0 0}
-0.5773502691896258